@@ -12,7 +12,6 @@ import (
12
12
"io"
13
13
"strconv"
14
14
"strings"
15
- "sync"
16
15
"syscall/js"
17
16
)
18
17
@@ -56,38 +55,6 @@ var jsFetchMissing = js.Global().Get("fetch").IsUndefined()
56
55
var jsFetchDisabled = js .Global ().Get ("process" ).Type () == js .TypeObject &&
57
56
strings .HasPrefix (js .Global ().Get ("process" ).Get ("argv0" ).String (), "node" )
58
57
59
- // Determine whether the JS runtime supports streaming request bodies.
60
- // Courtesy: https://developer.chrome.com/articles/fetch-streaming-requests/#feature-detection
61
- var supportsPostRequestStreams = sync .OnceValue (func () bool {
62
- requestOpt := js .Global ().Get ("Object" ).New ()
63
- requestBody := js .Global ().Get ("ReadableStream" ).New ()
64
-
65
- requestOpt .Set ("method" , "POST" )
66
- requestOpt .Set ("body" , requestBody )
67
-
68
- // There is quite a dance required to define a getter if you do not have the { get property() { ... } }
69
- // syntax available. However, it is possible:
70
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#defining_a_getter_on_existing_objects_using_defineproperty
71
- duplexCalled := false
72
- duplexGetterObj := js .Global ().Get ("Object" ).New ()
73
- duplexGetterFunc := js .FuncOf (func (this js.Value , args []js.Value ) any {
74
- duplexCalled = true
75
- return "half"
76
- })
77
- defer duplexGetterFunc .Release ()
78
- duplexGetterObj .Set ("get" , duplexGetterFunc )
79
- js .Global ().Get ("Object" ).Call ("defineProperty" , requestOpt , "duplex" , duplexGetterObj )
80
-
81
- // Slight difference here between the aforementioned example: Non-browser-based runtimes
82
- // do not have a non-empty API Base URL (https://html.spec.whatwg.org/multipage/webappapis.html#api-base-url)
83
- // so we have to supply a valid URL here.
84
- requestObject := js .Global ().Get ("Request" ).New ("https://www.example.org" , requestOpt )
85
-
86
- hasContentTypeHeader := requestObject .Get ("headers" ).Call ("has" , "Content-Type" ).Bool ()
87
-
88
- return duplexCalled && ! hasContentTypeHeader
89
- })
90
-
91
58
// RoundTrip implements the RoundTripper interface using the WHATWG Fetch API.
92
59
func (t * Transport ) RoundTrip (req * Request ) (* Response , error ) {
93
60
// The Transport has a documented contract that states that if the DialContext or
@@ -137,63 +104,24 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
137
104
opt .Set ("headers" , headers )
138
105
139
106
if req .Body != nil {
140
- if ! supportsPostRequestStreams () {
141
- body , err := io .ReadAll (req .Body )
142
- if err != nil {
143
- req .Body .Close () // RoundTrip must always close the body, including on errors.
144
- return nil , err
145
- }
146
- req .Body .Close ()
147
- if len (body ) != 0 {
148
- buf := uint8Array .New (len (body ))
149
- js .CopyBytesToJS (buf , body )
150
- opt .Set ("body" , buf )
151
- }
152
- } else {
153
- readableStreamCtorArg := js .Global ().Get ("Object" ).New ()
154
- readableStreamCtorArg .Set ("type" , "bytes" )
155
- readableStreamCtorArg .Set ("autoAllocateChunkSize" , t .writeBufferSize ())
156
-
157
- readableStreamPull := js .FuncOf (func (this js.Value , args []js.Value ) any {
158
- controller := args [0 ]
159
- byobRequest := controller .Get ("byobRequest" )
160
- if byobRequest .IsNull () {
161
- controller .Call ("close" )
162
- }
163
-
164
- byobRequestView := byobRequest .Get ("view" )
165
-
166
- bodyBuf := make ([]byte , byobRequestView .Get ("byteLength" ).Int ())
167
- readBytes , readErr := io .ReadFull (req .Body , bodyBuf )
168
- if readBytes > 0 {
169
- buf := uint8Array .New (byobRequestView .Get ("buffer" ))
170
- js .CopyBytesToJS (buf , bodyBuf )
171
- byobRequest .Call ("respond" , readBytes )
172
- }
173
-
174
- if readErr == io .EOF || readErr == io .ErrUnexpectedEOF {
175
- controller .Call ("close" )
176
- } else if readErr != nil {
177
- readErrCauseObject := js .Global ().Get ("Object" ).New ()
178
- readErrCauseObject .Set ("cause" , readErr .Error ())
179
- readErr := js .Global ().Get ("Error" ).New ("io.ReadFull failed while streaming POST body" , readErrCauseObject )
180
- controller .Call ("error" , readErr )
181
- }
182
- // Note: This a return from the pull callback of the controller and *not* RoundTrip().
183
- return nil
184
- })
185
- defer func () {
186
- readableStreamPull .Release ()
187
- req .Body .Close ()
188
- }()
189
- readableStreamCtorArg .Set ("pull" , readableStreamPull )
190
-
191
- opt .Set ("body" , js .Global ().Get ("ReadableStream" ).New (readableStreamCtorArg ))
192
- // There is a requirement from the WHATWG fetch standard that the duplex property of
193
- // the object given as the options argument to the fetch call be set to 'half'
194
- // when the body property of the same options object is a ReadableStream:
195
- // https://fetch.spec.whatwg.org/#dom-requestinit-duplex
196
- opt .Set ("duplex" , "half" )
107
+ // TODO(johanbrandhorst): Stream request body when possible.
108
+ // See https://bugs.chromium.org/p/chromium/issues/detail?id=688906 for Blink issue.
109
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1387483 for Firefox issue.
110
+ // See https://github.com/web-platform-tests/wpt/issues/7693 for WHATWG tests issue.
111
+ // See https://developer.mozilla.org/en-US/docs/Web/API/Streams_API for more details on the Streams API
112
+ // and browser support.
113
+ // NOTE(haruyama480): Ensure HTTP/1 fallback exists.
114
+ // See https://go.dev/issue/61889 for discussion.
115
+ body , err := io .ReadAll (req .Body )
116
+ if err != nil {
117
+ req .Body .Close () // RoundTrip must always close the body, including on errors.
118
+ return nil , err
119
+ }
120
+ req .Body .Close ()
121
+ if len (body ) != 0 {
122
+ buf := uint8Array .New (len (body ))
123
+ js .CopyBytesToJS (buf , body )
124
+ opt .Set ("body" , buf )
197
125
}
198
126
}
199
127
0 commit comments