0
mirror of https://github.com/XTLS/Xray-core.git synced 2025-06-15 04:47:16 +03:00

XHTTP: Add "stream-one" mode for client & server (#4071)

""Breaking"": Client uses "stream-one" mode by default when using **REALITY** ("stream-up" if "downloadSettings" exists)
This commit is contained in:
RPRX
2024-11-27 20:19:18 +00:00
committed by GitHub
parent d8934cf839
commit f7bd98b13c
5 changed files with 141 additions and 25 deletions

View File

@ -29,6 +29,10 @@ type DialerClient interface {
// (ctx, baseURL) -> uploadWriter
// baseURL already contains sessionId
OpenUpload(context.Context, string) io.WriteCloser
// (ctx, pureURL) -> (uploadWriter, downloadReader)
// pureURL can not contain sessionId
Open(context.Context, string) (io.WriteCloser, io.ReadCloser)
}
// implements splithttp.DialerClient in terms of direct network connections
@ -42,6 +46,30 @@ type DefaultDialerClient struct {
dialUploadConn func(ctxInner context.Context) (net.Conn, error)
}
func (c *DefaultDialerClient) Open(ctx context.Context, pureURL string) (io.WriteCloser, io.ReadCloser) {
reader, writer := io.Pipe()
req, _ := http.NewRequestWithContext(ctx, "POST", pureURL, reader)
req.Header = c.transportConfig.GetRequestHeader()
if !c.transportConfig.NoGRPCHeader {
req.Header.Set("Content-Type", "application/grpc")
}
wrc := &WaitReadCloser{Wait: make(chan struct{})}
go func() {
response, err := c.client.Do(req)
if err != nil || response.StatusCode != 200 {
if err != nil {
errors.LogInfoInner(ctx, err, "failed to open ", pureURL)
} else {
errors.LogInfo(ctx, "unexpected status ", response.StatusCode)
}
wrc.Close()
return
}
wrc.Set(response.Body)
}()
return writer, wrc
}
func (c *DefaultDialerClient) OpenUpload(ctx context.Context, baseURL string) io.WriteCloser {
reader, writer := io.Pipe()
req, _ := http.NewRequestWithContext(ctx, "POST", baseURL, reader)
@ -226,3 +254,40 @@ func (c downloadBody) Close() error {
c.cancel()
return nil
}
type WaitReadCloser struct {
Wait chan struct{}
io.ReadCloser
}
func (w *WaitReadCloser) Set(rc io.ReadCloser) {
w.ReadCloser = rc
defer func() {
if recover() != nil {
rc.Close()
}
}()
close(w.Wait)
}
func (w *WaitReadCloser) Read(b []byte) (int, error) {
if w.ReadCloser == nil {
if <-w.Wait; w.ReadCloser == nil {
return 0, io.ErrClosedPipe
}
}
return w.ReadCloser.Read(b)
}
func (w *WaitReadCloser) Close() error {
if w.ReadCloser != nil {
return w.ReadCloser.Close()
}
defer func() {
if recover() != nil && w.ReadCloser != nil {
w.ReadCloser.Close()
}
}()
close(w.Wait)
return nil
}