Source file
src/net/http/roundtrip_js.go
1
2
3
4
5
6
7 package http
8
9 import (
10 "errors"
11 "fmt"
12 "io"
13 "strconv"
14 "syscall/js"
15 )
16
17 var uint8Array = js.Global().Get("Uint8Array")
18
19
20
21
22
23
24
25 const jsFetchMode = "js.fetch:mode"
26
27
28
29
30
31
32
33 const jsFetchCreds = "js.fetch:credentials"
34
35
36
37
38
39
40
41 const jsFetchRedirect = "js.fetch:redirect"
42
43
44
45 var jsFetchMissing = js.Global().Get("fetch").IsUndefined()
46
47
48 func (t *Transport) RoundTrip(req *Request) (*Response, error) {
49
50
51
52
53
54
55 if t.Dial != nil || t.DialContext != nil || t.DialTLS != nil || t.DialTLSContext != nil || jsFetchMissing {
56 return t.roundTrip(req)
57 }
58
59 ac := js.Global().Get("AbortController")
60 if !ac.IsUndefined() {
61
62
63
64 ac = ac.New()
65 }
66
67 opt := js.Global().Get("Object").New()
68
69
70 opt.Set("method", req.Method)
71 opt.Set("credentials", "same-origin")
72 if h := req.Header.Get(jsFetchCreds); h != "" {
73 opt.Set("credentials", h)
74 req.Header.Del(jsFetchCreds)
75 }
76 if h := req.Header.Get(jsFetchMode); h != "" {
77 opt.Set("mode", h)
78 req.Header.Del(jsFetchMode)
79 }
80 if h := req.Header.Get(jsFetchRedirect); h != "" {
81 opt.Set("redirect", h)
82 req.Header.Del(jsFetchRedirect)
83 }
84 if !ac.IsUndefined() {
85 opt.Set("signal", ac.Get("signal"))
86 }
87 headers := js.Global().Get("Headers").New()
88 for key, values := range req.Header {
89 for _, value := range values {
90 headers.Call("append", key, value)
91 }
92 }
93 opt.Set("headers", headers)
94
95 if req.Body != nil {
96
97
98
99
100
101
102 body, err := io.ReadAll(req.Body)
103 if err != nil {
104 req.Body.Close()
105 return nil, err
106 }
107 req.Body.Close()
108 if len(body) != 0 {
109 buf := uint8Array.New(len(body))
110 js.CopyBytesToJS(buf, body)
111 opt.Set("body", buf)
112 }
113 }
114
115 fetchPromise := js.Global().Call("fetch", req.URL.String(), opt)
116 var (
117 respCh = make(chan *Response, 1)
118 errCh = make(chan error, 1)
119 success, failure js.Func
120 )
121 success = js.FuncOf(func(this js.Value, args []js.Value) any {
122 success.Release()
123 failure.Release()
124
125 result := args[0]
126 header := Header{}
127
128 headersIt := result.Get("headers").Call("entries")
129 for {
130 n := headersIt.Call("next")
131 if n.Get("done").Bool() {
132 break
133 }
134 pair := n.Get("value")
135 key, value := pair.Index(0).String(), pair.Index(1).String()
136 ck := CanonicalHeaderKey(key)
137 header[ck] = append(header[ck], value)
138 }
139
140 contentLength := int64(0)
141 clHeader := header.Get("Content-Length")
142 switch {
143 case clHeader != "":
144 cl, err := strconv.ParseInt(clHeader, 10, 64)
145 if err != nil {
146 errCh <- fmt.Errorf("net/http: ill-formed Content-Length header: %v", err)
147 return nil
148 }
149 if cl < 0 {
150
151
152 errCh <- fmt.Errorf("net/http: invalid Content-Length header: %q", clHeader)
153 return nil
154 }
155 contentLength = cl
156 default:
157
158 contentLength = -1
159 }
160
161 b := result.Get("body")
162 var body io.ReadCloser
163
164
165 if !b.IsUndefined() && !b.IsNull() {
166 body = &streamReader{stream: b.Call("getReader")}
167 } else {
168
169
170 body = &arrayReader{arrayPromise: result.Call("arrayBuffer")}
171 }
172
173 code := result.Get("status").Int()
174 respCh <- &Response{
175 Status: fmt.Sprintf("%d %s", code, StatusText(code)),
176 StatusCode: code,
177 Header: header,
178 ContentLength: contentLength,
179 Body: body,
180 Request: req,
181 }
182
183 return nil
184 })
185 failure = js.FuncOf(func(this js.Value, args []js.Value) any {
186 success.Release()
187 failure.Release()
188 errCh <- fmt.Errorf("net/http: fetch() failed: %s", args[0].Get("message").String())
189 return nil
190 })
191
192 fetchPromise.Call("then", success, failure)
193 select {
194 case <-req.Context().Done():
195 if !ac.IsUndefined() {
196
197 ac.Call("abort")
198 }
199 return nil, req.Context().Err()
200 case resp := <-respCh:
201 return resp, nil
202 case err := <-errCh:
203 return nil, err
204 }
205 }
206
207 var errClosed = errors.New("net/http: reader is closed")
208
209
210
211 type streamReader struct {
212 pending []byte
213 stream js.Value
214 err error
215 }
216
217 func (r *streamReader) Read(p []byte) (n int, err error) {
218 if r.err != nil {
219 return 0, r.err
220 }
221 if len(r.pending) == 0 {
222 var (
223 bCh = make(chan []byte, 1)
224 errCh = make(chan error, 1)
225 )
226 success := js.FuncOf(func(this js.Value, args []js.Value) any {
227 result := args[0]
228 if result.Get("done").Bool() {
229 errCh <- io.EOF
230 return nil
231 }
232 value := make([]byte, result.Get("value").Get("byteLength").Int())
233 js.CopyBytesToGo(value, result.Get("value"))
234 bCh <- value
235 return nil
236 })
237 defer success.Release()
238 failure := js.FuncOf(func(this js.Value, args []js.Value) any {
239
240
241
242
243
244 errCh <- errors.New(args[0].Get("message").String())
245 return nil
246 })
247 defer failure.Release()
248 r.stream.Call("read").Call("then", success, failure)
249 select {
250 case b := <-bCh:
251 r.pending = b
252 case err := <-errCh:
253 r.err = err
254 return 0, err
255 }
256 }
257 n = copy(p, r.pending)
258 r.pending = r.pending[n:]
259 return n, nil
260 }
261
262 func (r *streamReader) Close() error {
263
264
265
266 r.stream.Call("cancel")
267 if r.err == nil {
268 r.err = errClosed
269 }
270 return nil
271 }
272
273
274
275 type arrayReader struct {
276 arrayPromise js.Value
277 pending []byte
278 read bool
279 err error
280 }
281
282 func (r *arrayReader) Read(p []byte) (n int, err error) {
283 if r.err != nil {
284 return 0, r.err
285 }
286 if !r.read {
287 r.read = true
288 var (
289 bCh = make(chan []byte, 1)
290 errCh = make(chan error, 1)
291 )
292 success := js.FuncOf(func(this js.Value, args []js.Value) any {
293
294 uint8arrayWrapper := uint8Array.New(args[0])
295 value := make([]byte, uint8arrayWrapper.Get("byteLength").Int())
296 js.CopyBytesToGo(value, uint8arrayWrapper)
297 bCh <- value
298 return nil
299 })
300 defer success.Release()
301 failure := js.FuncOf(func(this js.Value, args []js.Value) any {
302
303
304
305
306 errCh <- errors.New(args[0].Get("message").String())
307 return nil
308 })
309 defer failure.Release()
310 r.arrayPromise.Call("then", success, failure)
311 select {
312 case b := <-bCh:
313 r.pending = b
314 case err := <-errCh:
315 return 0, err
316 }
317 }
318 if len(r.pending) == 0 {
319 return 0, io.EOF
320 }
321 n = copy(p, r.pending)
322 r.pending = r.pending[n:]
323 return n, nil
324 }
325
326 func (r *arrayReader) Close() error {
327 if r.err == nil {
328 r.err = errClosed
329 }
330 return nil
331 }
332
View as plain text