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