Skip to content

Commit ce6c0dc

Browse files
authored
XHTTP XMUX: Abandon client if client.Do(req) failed (#4253)
51769fd
1 parent aeb12d9 commit ce6c0dc

4 files changed

Lines changed: 36 additions & 14 deletions

File tree

transport/internet/splithttp/client.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
5555
},
5656
})
5757

58-
method := "GET"
58+
method := "GET" // stream-down
5959
if body != nil {
60-
method = "POST"
60+
method = "POST" // stream-up/one
6161
}
62-
req, _ := http.NewRequestWithContext(ctx, method, url, body)
62+
req, _ := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body)
6363
req.Header = c.transportConfig.GetRequestHeader()
6464
if method == "POST" && !c.transportConfig.NoGRPCHeader {
6565
req.Header.Set("Content-Type", "application/grpc")
@@ -69,17 +69,20 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
6969
go func() {
7070
resp, err := c.client.Do(req)
7171
if err != nil {
72+
if !uploadOnly {
73+
c.closed = true
74+
}
7275
errors.LogInfoInner(ctx, err, "failed to "+method+" "+url)
7376
gotConn.Close()
7477
wrc.Close()
7578
return
7679
}
7780
if resp.StatusCode != 200 && !uploadOnly {
78-
// c.closed = true
7981
errors.LogInfo(ctx, "unexpected status ", resp.StatusCode)
8082
}
81-
if resp.StatusCode != 200 || uploadOnly {
82-
resp.Body.Close()
83+
if resp.StatusCode != 200 || uploadOnly { // stream-up
84+
io.Copy(io.Discard, resp.Body)
85+
resp.Body.Close() // if it is called immediately, the upload will be interrupted also
8386
wrc.Close()
8487
return
8588
}
@@ -91,7 +94,7 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
9194
}
9295

9396
func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body io.Reader, contentLength int64) error {
94-
req, err := http.NewRequestWithContext(ctx, "POST", url, body)
97+
req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), "POST", url, body)
9598
if err != nil {
9699
return err
97100
}
@@ -101,13 +104,14 @@ func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body i
101104
if c.httpVersion != "1.1" {
102105
resp, err := c.client.Do(req)
103106
if err != nil {
107+
c.closed = true
104108
return err
105109
}
106110

111+
io.Copy(io.Discard, resp.Body)
107112
defer resp.Body.Close()
108113

109114
if resp.StatusCode != 200 {
110-
// c.closed = true
111115
return errors.New("bad status code:", resp.Status)
112116
}
113117
} else {
@@ -139,11 +143,12 @@ func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body i
139143
if h1UploadConn.UnreadedResponsesCount > 0 {
140144
resp, err := http.ReadResponse(h1UploadConn.RespBufReader, req)
141145
if err != nil {
146+
c.closed = true
142147
return fmt.Errorf("error while reading response: %s", err.Error())
143148
}
149+
io.Copy(io.Discard, resp.Body)
150+
defer resp.Body.Close()
144151
if resp.StatusCode != 200 {
145-
// c.closed = true
146-
// resp.Body.Close() // I'm not sure
147152
return fmt.Errorf("got non-200 error response code: %d", resp.StatusCode)
148153
}
149154
}

transport/internet/splithttp/config.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/xtls/xray-core/common"
10+
"github.com/xtls/xray-core/core"
1011
"github.com/xtls/xray-core/transport/internet"
1112
)
1213

@@ -36,10 +37,11 @@ func (c *Config) GetNormalizedQuery() string {
3637
if query != "" {
3738
query += "&"
3839
}
40+
query += "x_version=" + core.Version()
3941

4042
paddingLen := c.GetNormalizedXPaddingBytes().rand()
4143
if paddingLen > 0 {
42-
query += "x_padding=" + strings.Repeat("0", int(paddingLen))
44+
query += "&x_padding=" + strings.Repeat("0", int(paddingLen))
4345
}
4446

4547
return query
@@ -58,6 +60,7 @@ func (c *Config) WriteResponseHeader(writer http.ResponseWriter) {
5860
// CORS headers for the browser dialer
5961
writer.Header().Set("Access-Control-Allow-Origin", "*")
6062
writer.Header().Set("Access-Control-Allow-Methods", "GET, POST")
63+
writer.Header().Set("X-Version", core.Version())
6164
paddingLen := c.GetNormalizedXPaddingBytes().rand()
6265
if paddingLen > 0 {
6366
writer.Header().Set("X-Padding", strings.Repeat("0", int(paddingLen)))

transport/internet/splithttp/dialer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,14 +372,14 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
372372
if xmuxClient != nil {
373373
xmuxClient.LeftRequests.Add(-1)
374374
}
375-
conn.reader, conn.remoteAddr, conn.localAddr, _ = httpClient.OpenStream(context.WithoutCancel(ctx), requestURL.String(), reader, false)
375+
conn.reader, conn.remoteAddr, conn.localAddr, _ = httpClient.OpenStream(ctx, requestURL.String(), reader, false)
376376
return stat.Connection(&conn), nil
377377
} else { // stream-down
378378
var err error
379379
if xmuxClient2 != nil {
380380
xmuxClient2.LeftRequests.Add(-1)
381381
}
382-
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(context.WithoutCancel(ctx), requestURL2.String(), nil, false)
382+
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(ctx, requestURL2.String(), nil, false)
383383
if err != nil { // browser dialer only
384384
return nil, err
385385
}
@@ -454,7 +454,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
454454

455455
go func() {
456456
err := httpClient.PostPacket(
457-
context.WithoutCancel(ctx),
457+
ctx,
458458
url.String(),
459459
&buf.MultiBufferContainer{MultiBuffer: chunk},
460460
int64(chunk.Len()),

transport/internet/splithttp/hub.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package splithttp
22

33
import (
4+
"bytes"
45
"context"
56
"crypto/tls"
67
"io"
@@ -102,6 +103,12 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
102103

103104
h.config.WriteResponseHeader(writer)
104105

106+
clientVer := []int{0, 0, 0}
107+
x_version := strings.Split(request.URL.Query().Get("x_version"), ".")
108+
for j := 0; j < 3 && len(x_version) > j; j++ {
109+
clientVer[j], _ = strconv.Atoi(x_version[j])
110+
}
111+
105112
validRange := h.config.GetNormalizedXPaddingBytes()
106113
x_padding := int32(len(request.URL.Query().Get("x_padding")))
107114
if validRange.To > 0 && (x_padding < validRange.From || x_padding > validRange.To) {
@@ -160,6 +167,13 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
160167
writer.WriteHeader(http.StatusConflict)
161168
} else {
162169
writer.WriteHeader(http.StatusOK)
170+
if request.ProtoMajor != 1 && len(clientVer) > 0 && clientVer[0] >= 25 {
171+
paddingLen := h.config.GetNormalizedXPaddingBytes().rand()
172+
if paddingLen > 0 {
173+
writer.Write(bytes.Repeat([]byte{'0'}, int(paddingLen)))
174+
}
175+
writer.(http.Flusher).Flush()
176+
}
163177
<-request.Context().Done()
164178
}
165179
return

0 commit comments

Comments
 (0)