Skip to content
This repository has been archived by the owner on Aug 5, 2024. It is now read-only.

Commit

Permalink
Merge branch 'XTLS:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
MHSanaei authored Jun 24, 2024
2 parents 3886014 + 7acd5a6 commit d2d32d3
Show file tree
Hide file tree
Showing 11 changed files with 279 additions and 89 deletions.
4 changes: 3 additions & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ body:
options:
- label: I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values.
required: true
- label: I provided the complete config and logs, rather than just providing the truncated parts based on my own judgment.
required: true
- label: I searched issues and did not find any similar issues.
required: true
- type: input
Expand Down Expand Up @@ -40,7 +42,7 @@ body:
Don't just paste a big exported config file here. Eliminate useless inbound/outbound, rules, options, this can help determine the problem, if you really want to get help.
### For logs
Please set the log level to debug first.
Please set the log level to debug and dnsLog to true first.
Restart Xray-core, then operate according to the reproduction method, try to reduce the irrelevant part in the log.
Remember to delete parts with personal information (such as UUID and IP).
Provide the log of Xray-core, not the log output by the panel or other things.
Expand Down
4 changes: 3 additions & 1 deletion .github/ISSUE_TEMPLATE/bug_report_zh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ body:
options:
- label: 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
required: true
- label: 我提供了完整的配置文件和日志,而不是出于自己的判断只给出截取的部分。
required: true
- label: 我搜索了issues,没有发现已提出的类似问题。
required: true
- type: input
Expand Down Expand Up @@ -40,7 +42,7 @@ body:
不要直接在这里黏贴一大段导出的 config 文件。去掉无用的出入站、规则、选项,这可以帮助确定问题,如果你真的想得到帮助。
### 对于日志
请先将日志等级设置为 debug.
请先将日志等级设置为 debug, dnsLog 设置为true.
重启 Xray-core ,再按复现方式操作,尽量减少日志中的无关部分。
记得删除有关个人信息(如UUID与IP)的部分。
提供 Xray-core 的日志,而不是面板或者别的东西输出的日志。
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Build docker image

on:
release:
types: [published]
push:
branches:
- main
Expand All @@ -19,6 +21,7 @@ jobs:
images: ghcr.io/${{ github.repository_owner }}/xray-core
flavor: latest=true
tags: |
type=sha
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
Expand Down
2 changes: 1 addition & 1 deletion core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
var (
Version_x byte = 1
Version_y byte = 8
Version_z byte = 15
Version_z byte = 16
)

var (
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/miekg/dns v1.1.61
github.com/pelletier/go-toml v1.9.5
github.com/pires/go-proxyproto v0.7.0
github.com/quic-go/quic-go v0.45.0
github.com/quic-go/quic-go v0.45.1
github.com/refraction-networking/utls v1.6.6
github.com/sagernet/sing v0.4.1
github.com/sagernet/sing-shadowsocks v0.2.6
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=
github.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA=
github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig=
github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
Expand Down
142 changes: 81 additions & 61 deletions transport/internet/splithttp/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/common/signal/semaphore"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/transport/internet"
Expand Down Expand Up @@ -44,18 +45,6 @@ var (
globalDialerAccess sync.Mutex
)

func destroyHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) {
globalDialerAccess.Lock()
defer globalDialerAccess.Unlock()

if globalDialerMap == nil {
globalDialerMap = make(map[dialerConf]reusedClient)
}

delete(globalDialerMap, dialerConf{dest, streamSettings})

}

func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) reusedClient {
globalDialerAccess.Lock()
defer globalDialerAccess.Unlock()
Expand All @@ -69,6 +58,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
}

tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")

var gotlsConfig *gotls.Config

Expand All @@ -77,15 +67,15 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
}

dialContext := func(ctxInner context.Context) (net.Conn, error) {
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
conn, err := internet.DialSystem(ctxInner, dest, streamSettings.SocketSettings)
if err != nil {
return nil, err
}

if gotlsConfig != nil {
if fingerprint := tls.GetFingerprint(tlsConfig.Fingerprint); fingerprint != nil {
conn = tls.UClient(conn, gotlsConfig, fingerprint)
if err := conn.(*tls.UConn).HandshakeContext(ctx); err != nil {
if err := conn.(*tls.UConn).HandshakeContext(ctxInner); err != nil {
return nil, err
}
} else {
Expand All @@ -99,7 +89,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
var uploadTransport http.RoundTripper
var downloadTransport http.RoundTripper

if tlsConfig != nil {
if isH2 {
downloadTransport = &http2.Transport{
DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
return dialContext(ctxInner)
Expand Down Expand Up @@ -132,7 +122,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
upload: &http.Client{
Transport: uploadTransport,
},
isH2: tlsConfig != nil,
isH2: isH2,
uploadRawPool: &sync.Pool{},
dialUploadConn: dialContext,
}
Expand Down Expand Up @@ -171,49 +161,73 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me

var remoteAddr gonet.Addr
var localAddr gonet.Addr
// this is done when the TCP/UDP connection to the server was established,
// and we can unblock the Dial function and print correct net addresses in
// logs
gotConn := done.New()

trace := &httptrace.ClientTrace{
GotConn: func(connInfo httptrace.GotConnInfo) {
remoteAddr = connInfo.Conn.RemoteAddr()
localAddr = connInfo.Conn.LocalAddr()
},
}
var downResponse io.ReadCloser
gotDownResponse := done.New()

sessionIdUuid := uuid.New()
sessionId := sessionIdUuid.String()

req, err := http.NewRequestWithContext(
httptrace.WithClientTrace(ctx, trace),
"GET",
requestURL.String()+"?session="+sessionId,
nil,
)
if err != nil {
return nil, err
}
go func() {
trace := &httptrace.ClientTrace{
GotConn: func(connInfo httptrace.GotConnInfo) {
remoteAddr = connInfo.Conn.RemoteAddr()
localAddr = connInfo.Conn.LocalAddr()
gotConn.Close()
},
}

req.Header = transportConfiguration.GetRequestHeader()

downResponse, err := httpClient.download.Do(req)
if err != nil {
// workaround for various connection pool related issues, mostly around
// HTTP/1.1. if the http client ever fails to send a request, we simply
// delete it entirely.
// in HTTP/1.1, it was observed that pool connections would immediately
// fail with "context canceled" if the previous http response body was
// not explicitly BOTH drained and closed. at the same time, sometimes
// the draining itself takes forever and causes more problems.
// see also https://github.com/golang/go/issues/60240
destroyHTTPClient(ctx, dest, streamSettings)
return nil, newError("failed to send download http request, destroying client").Base(err)
}
// in case we hit an error, we want to unblock this part
defer gotConn.Close()

if downResponse.StatusCode != 200 {
downResponse.Body.Close()
return nil, newError("invalid status code on download:", downResponse.Status)
}
req, err := http.NewRequestWithContext(
httptrace.WithClientTrace(context.WithoutCancel(ctx), trace),
"GET",
requestURL.String()+sessionId,
nil,
)
if err != nil {
newError("failed to construct download http request").Base(err).WriteToLog()
gotDownResponse.Close()
return
}

req.Header = transportConfiguration.GetRequestHeader()

response, err := httpClient.download.Do(req)
gotConn.Close()
if err != nil {
newError("failed to send download http request").Base(err).WriteToLog()
gotDownResponse.Close()
return
}

if response.StatusCode != 200 {
response.Body.Close()
newError("invalid status code on download:", response.Status).WriteToLog()
gotDownResponse.Close()
return
}

// skip "ok" response
trashHeader := []byte{0, 0}
_, err = io.ReadFull(response.Body, trashHeader)
if err != nil {
response.Body.Close()
newError("failed to read initial response").Base(err).WriteToLog()
gotDownResponse.Close()
return
}

uploadUrl := requestURL.String() + "?session=" + sessionId + "&seq="
downResponse = response.Body
gotDownResponse.Close()
}()

uploadUrl := requestURL.String() + sessionId + "/"

uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize))

Expand Down Expand Up @@ -263,10 +277,10 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
} else {
var err error
var uploadConn any
for _ = range 5 {
for i := 0; i < 5; i++ {
uploadConn = httpClient.uploadRawPool.Get()
if uploadConn == nil {
uploadConn, err = httpClient.dialUploadConn(ctx)
uploadConn, err = httpClient.dialUploadConn(context.WithoutCancel(ctx))
if err != nil {
newError("failed to connect upload").Base(err).WriteToLog()
uploadPipeReader.Interrupt()
Expand All @@ -293,21 +307,27 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
}
}()

// skip "ok" response
trashHeader := []byte{0, 0}
_, err = io.ReadFull(downResponse.Body, trashHeader)
if err != nil {
downResponse.Body.Close()
return nil, newError("failed to read initial response")
}
// we want to block Dial until we know the remote address of the server,
// for logging purposes
<-gotConn.Wait()

// necessary in order to send larger chunks in upload
bufferedUploadPipeWriter := buf.NewBufferedWriter(uploadPipeWriter)
bufferedUploadPipeWriter.SetBuffered(false)

lazyDownload := &LazyReader{
CreateReader: func() (io.ReadCloser, error) {
<-gotDownResponse.Wait()
if downResponse == nil {
return nil, newError("downResponse failed")
}
return downResponse, nil
},
}

conn := splitConn{
writer: bufferedUploadPipeWriter,
reader: downResponse.Body,
reader: lazyDownload,
remoteAddr: remoteAddr,
localAddr: localAddr,
}
Expand Down
Loading

0 comments on commit d2d32d3

Please sign in to comment.