]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/net/http/roundtrip_js.go
net/http: remove Content-Encoding header in roundtrip_js
[gostls13.git] / src / net / http / roundtrip_js.go
index 323372584fcaeab2feebb88f6a3193f28916a8a8..cbf978af182d5fb473f52de93ced64058293e1d2 100644 (file)
@@ -10,6 +10,7 @@ import (
        "errors"
        "fmt"
        "io"
+       "net/http/internal/ascii"
        "strconv"
        "strings"
        "syscall/js"
@@ -55,38 +56,6 @@ var jsFetchMissing = js.Global().Get("fetch").IsUndefined()
 var jsFetchDisabled = js.Global().Get("process").Type() == js.TypeObject &&
        strings.HasPrefix(js.Global().Get("process").Get("argv0").String(), "node")
 
-// Determine whether the JS runtime supports streaming request bodies.
-// Courtesy: https://developer.chrome.com/articles/fetch-streaming-requests/#feature-detection
-func supportsPostRequestStreams() bool {
-       requestOpt := js.Global().Get("Object").New()
-       requestBody := js.Global().Get("ReadableStream").New()
-
-       requestOpt.Set("method", "POST")
-       requestOpt.Set("body", requestBody)
-
-       // There is quite a dance required to define a getter if you do not have the { get property() { ... } }
-       // syntax available. However, it is possible:
-       // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#defining_a_getter_on_existing_objects_using_defineproperty
-       duplexCalled := false
-       duplexGetterObj := js.Global().Get("Object").New()
-       duplexGetterFunc := js.FuncOf(func(this js.Value, args []js.Value) any {
-               duplexCalled = true
-               return "half"
-       })
-       defer duplexGetterFunc.Release()
-       duplexGetterObj.Set("get", duplexGetterFunc)
-       js.Global().Get("Object").Call("defineProperty", requestOpt, "duplex", duplexGetterObj)
-
-       // Slight difference here between the aforementioned example: Non-browser-based runtimes
-       // do not have a non-empty API Base URL (https://html.spec.whatwg.org/multipage/webappapis.html#api-base-url)
-       // so we have to supply a valid URL here.
-       requestObject := js.Global().Get("Request").New("https://www.example.org", requestOpt)
-
-       hasContentTypeHeader := requestObject.Get("headers").Call("has", "Content-Type").Bool()
-
-       return duplexCalled && !hasContentTypeHeader
-}
-
 // RoundTrip implements the RoundTripper interface using the WHATWG Fetch API.
 func (t *Transport) RoundTrip(req *Request) (*Response, error) {
        // The Transport has a documented contract that states that if the DialContext or
@@ -135,60 +104,25 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
        }
        opt.Set("headers", headers)
 
-       var readableStreamStart, readableStreamPull, readableStreamCancel js.Func
        if req.Body != nil {
-               if !supportsPostRequestStreams() {
-                       body, err := io.ReadAll(req.Body)
-                       if err != nil {
-                               req.Body.Close() // RoundTrip must always close the body, including on errors.
-                               return nil, err
-                       }
-                       if len(body) != 0 {
-                               buf := uint8Array.New(len(body))
-                               js.CopyBytesToJS(buf, body)
-                               opt.Set("body", buf)
-                       }
-               } else {
-                       readableStreamCtorArg := js.Global().Get("Object").New()
-                       readableStreamCtorArg.Set("type", "bytes")
-                       readableStreamCtorArg.Set("autoAllocateChunkSize", t.writeBufferSize())
-
-                       readableStreamPull = js.FuncOf(func(this js.Value, args []js.Value) any {
-                               controller := args[0]
-                               byobRequest := controller.Get("byobRequest")
-                               if byobRequest.IsNull() {
-                                       controller.Call("close")
-                               }
-
-                               byobRequestView := byobRequest.Get("view")
-
-                               bodyBuf := make([]byte, byobRequestView.Get("byteLength").Int())
-                               readBytes, readErr := io.ReadFull(req.Body, bodyBuf)
-                               if readBytes > 0 {
-                                       buf := uint8Array.New(byobRequestView.Get("buffer"))
-                                       js.CopyBytesToJS(buf, bodyBuf)
-                                       byobRequest.Call("respond", readBytes)
-                               }
-
-                               if readErr == io.EOF || readErr == io.ErrUnexpectedEOF {
-                                       controller.Call("close")
-                               } else if readErr != nil {
-                                       readErrCauseObject := js.Global().Get("Object").New()
-                                       readErrCauseObject.Set("cause", readErr.Error())
-                                       readErr := js.Global().Get("Error").New("io.ReadFull failed while streaming POST body", readErrCauseObject)
-                                       controller.Call("error", readErr)
-                               }
-                               // Note: This a return from the pull callback of the controller and *not* RoundTrip().
-                               return nil
-                       })
-                       readableStreamCtorArg.Set("pull", readableStreamPull)
-
-                       opt.Set("body", js.Global().Get("ReadableStream").New(readableStreamCtorArg))
-                       // There is a requirement from the WHATWG fetch standard that the duplex property of
-                       // the object given as the options argument to the fetch call be set to 'half'
-                       // when the body property of the same options object is a ReadableStream:
-                       // https://fetch.spec.whatwg.org/#dom-requestinit-duplex
-                       opt.Set("duplex", "half")
+               // TODO(johanbrandhorst): Stream request body when possible.
+               // See https://bugs.chromium.org/p/chromium/issues/detail?id=688906 for Blink issue.
+               // See https://bugzilla.mozilla.org/show_bug.cgi?id=1387483 for Firefox issue.
+               // See https://github.com/web-platform-tests/wpt/issues/7693 for WHATWG tests issue.
+               // See https://developer.mozilla.org/en-US/docs/Web/API/Streams_API for more details on the Streams API
+               // and browser support.
+               // NOTE(haruyama480): Ensure HTTP/1 fallback exists.
+               // See https://go.dev/issue/61889 for discussion.
+               body, err := io.ReadAll(req.Body)
+               if err != nil {
+                       req.Body.Close() // RoundTrip must always close the body, including on errors.
+                       return nil, err
+               }
+               req.Body.Close()
+               if len(body) != 0 {
+                       buf := uint8Array.New(len(body))
+                       js.CopyBytesToJS(buf, body)
+                       opt.Set("body", buf)
                }
        }
 
@@ -201,11 +135,6 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
        success = js.FuncOf(func(this js.Value, args []js.Value) any {
                success.Release()
                failure.Release()
-               readableStreamCancel.Release()
-               readableStreamPull.Release()
-               readableStreamStart.Release()
-
-               req.Body.Close()
 
                result := args[0]
                header := Header{}
@@ -256,11 +185,22 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
                }
 
                code := result.Get("status").Int()
+
+               uncompressed := false
+               if ascii.EqualFold(header.Get("Content-Encoding"), "gzip") {
+                       // The fetch api will decode the gzip, but Content-Encoding not be deleted.
+                       header.Del("Content-Encoding")
+                       header.Del("Content-Length")
+                       contentLength = -1
+                       uncompressed = true
+               }
+
                respCh <- &Response{
                        Status:        fmt.Sprintf("%d %s", code, StatusText(code)),
                        StatusCode:    code,
                        Header:        header,
                        ContentLength: contentLength,
+                       Uncompressed:  uncompressed,
                        Body:          body,
                        Request:       req,
                }
@@ -270,11 +210,6 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
        failure = js.FuncOf(func(this js.Value, args []js.Value) any {
                success.Release()
                failure.Release()
-               readableStreamCancel.Release()
-               readableStreamPull.Release()
-               readableStreamStart.Release()
-
-               req.Body.Close()
 
                err := args[0]
                // The error is a JS Error type