summaryrefslogtreecommitdiff
path: root/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/github.com/gopherjs/gopherjs/compiler/natives/src/net/http/fetch.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/github.com/gopherjs/gopherjs/compiler/natives/src/net/http/fetch.go')
-rw-r--r--src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/github.com/gopherjs/gopherjs/compiler/natives/src/net/http/fetch.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/github.com/gopherjs/gopherjs/compiler/natives/src/net/http/fetch.go b/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/github.com/gopherjs/gopherjs/compiler/natives/src/net/http/fetch.go
new file mode 100644
index 00000000000..f9123d09063
--- /dev/null
+++ b/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/github.com/gopherjs/gopherjs/compiler/natives/src/net/http/fetch.go
@@ -0,0 +1,134 @@
+// +build js
+
+package http
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "strconv"
+
+ "github.com/gopherjs/gopherjs/js"
+)
+
+// streamReader implements an io.ReadCloser wrapper for ReadableStream of https://fetch.spec.whatwg.org/.
+type streamReader struct {
+ pending []byte
+ stream *js.Object
+}
+
+func (r *streamReader) Read(p []byte) (n int, err error) {
+ if len(r.pending) == 0 {
+ var (
+ bCh = make(chan []byte)
+ errCh = make(chan error)
+ )
+ r.stream.Call("read").Call("then",
+ func(result *js.Object) {
+ if result.Get("done").Bool() {
+ errCh <- io.EOF
+ return
+ }
+ bCh <- result.Get("value").Interface().([]byte)
+ },
+ func(reason *js.Object) {
+ // Assumes it's a DOMException.
+ errCh <- errors.New(reason.Get("message").String())
+ },
+ )
+ select {
+ case b := <-bCh:
+ r.pending = b
+ case err := <-errCh:
+ return 0, err
+ }
+ }
+ n = copy(p, r.pending)
+ r.pending = r.pending[n:]
+ return n, nil
+}
+
+func (r *streamReader) Close() error {
+ // This ignores any error returned from cancel method. So far, I did not encounter any concrete
+ // situation where reporting the error is meaningful. Most users ignore error from resp.Body.Close().
+ // If there's a need to report error here, it can be implemented and tested when that need comes up.
+ r.stream.Call("cancel")
+ return nil
+}
+
+// fetchTransport is a RoundTripper that is implemented using Fetch API. It supports streaming
+// response bodies.
+type fetchTransport struct{}
+
+func (t *fetchTransport) RoundTrip(req *Request) (*Response, error) {
+ headers := js.Global.Get("Headers").New()
+ for key, values := range req.Header {
+ for _, value := range values {
+ headers.Call("append", key, value)
+ }
+ }
+ opt := map[string]interface{}{
+ "method": req.Method,
+ "headers": headers,
+ "credentials": "same-origin",
+ }
+ if req.Body != nil {
+ // TODO: Find out if request body can be streamed into the fetch request rather than in advance here.
+ // See BufferSource at https://fetch.spec.whatwg.org/#body-mixin.
+ body, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ req.Body.Close() // RoundTrip must always close the body, including on errors.
+ return nil, err
+ }
+ req.Body.Close()
+ opt["body"] = body
+ }
+ respPromise := js.Global.Call("fetch", req.URL.String(), opt)
+
+ var (
+ respCh = make(chan *Response)
+ errCh = make(chan error)
+ )
+ respPromise.Call("then",
+ func(result *js.Object) {
+ header := Header{}
+ result.Get("headers").Call("forEach", func(value, key *js.Object) {
+ ck := CanonicalHeaderKey(key.String())
+ header[ck] = append(header[ck], value.String())
+ })
+
+ contentLength := int64(-1)
+ if cl, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
+ contentLength = cl
+ }
+
+ select {
+ case respCh <- &Response{
+ Status: result.Get("status").String() + " " + StatusText(result.Get("status").Int()),
+ StatusCode: result.Get("status").Int(),
+ Header: header,
+ ContentLength: contentLength,
+ Body: &streamReader{stream: result.Get("body").Call("getReader")},
+ Request: req,
+ }:
+ case <-req.Context().Done():
+ }
+ },
+ func(reason *js.Object) {
+ select {
+ case errCh <- fmt.Errorf("net/http: fetch() failed: %s", reason.String()):
+ case <-req.Context().Done():
+ }
+ },
+ )
+ select {
+ case <-req.Context().Done():
+ // TODO: Abort request if possible using Fetch API.
+ return nil, errors.New("net/http: request canceled")
+ case resp := <-respCh:
+ return resp, nil
+ case err := <-errCh:
+ return nil, err
+ }
+}