From bdd54f02e78a020cf9a717b8acb60a20e4b7272f Mon Sep 17 00:00:00 2001 From: Ben Krieger Date: Wed, 30 Oct 2024 20:24:31 -0400 Subject: [PATCH] Carry over non-zero Content-Length to Response --- internal/jshttp/response.go | 6 ++++-- internal/jshttp/responsewriter.go | 4 +++- internal/jsutil/jsutil.go | 27 ++++++++++++++------------- internal/jsutil/stream.go | 23 +++++++++++++++++++++++ 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/internal/jshttp/response.go b/internal/jshttp/response.go index 8a27bb0..93a33d9 100644 --- a/internal/jshttp/response.go +++ b/internal/jshttp/response.go @@ -32,12 +32,12 @@ func ToResponse(res js.Value) (*http.Response, error) { // ToJSResponse converts *http.Response to JavaScript sides Response class object. func ToJSResponse(res *http.Response) js.Value { - return newJSResponse(res.StatusCode, res.Header, res.Body, nil) + return newJSResponse(res.StatusCode, res.Header, res.ContentLength, res.Body, nil) } // newJSResponse creates JavaScript sides Response class object. // - Response: https://developer.mozilla.org/docs/Web/API/Response -func newJSResponse(statusCode int, headers http.Header, body io.ReadCloser, rawBody *js.Value) js.Value { +func newJSResponse(statusCode int, headers http.Header, contentLength int64, body io.ReadCloser, rawBody *js.Value) js.Value { status := statusCode if status == 0 { status = http.StatusOK @@ -55,6 +55,8 @@ func newJSResponse(statusCode int, headers http.Header, body io.ReadCloser, rawB var readableStream js.Value if rawBody != nil { readableStream = *rawBody + } else if contentLength > 0 { + readableStream = jsutil.ConvertReaderToFixedLengthStream(body, contentLength) } else { readableStream = jsutil.ConvertReaderToReadableStream(body) } diff --git a/internal/jshttp/responsewriter.go b/internal/jshttp/responsewriter.go index 51f4843..8849537 100644 --- a/internal/jshttp/responsewriter.go +++ b/internal/jshttp/responsewriter.go @@ -3,6 +3,7 @@ package jshttp import ( "io" "net/http" + "strconv" "sync" "syscall/js" @@ -51,5 +52,6 @@ func (w *ResponseWriter) WriteRawJSBody(body js.Value) { // ToJSResponse converts *ResponseWriter to JavaScript sides Response. // - Response: https://developer.mozilla.org/docs/Web/API/Response func (w *ResponseWriter) ToJSResponse() js.Value { - return newJSResponse(w.StatusCode, w.HeaderValue, w.Reader, w.RawJSBody) + contentLength, _ := strconv.ParseInt(w.HeaderValue.Get("Content-Length"), 10, 64) + return newJSResponse(w.StatusCode, w.HeaderValue, contentLength, w.Reader, w.RawJSBody) } diff --git a/internal/jsutil/jsutil.go b/internal/jsutil/jsutil.go index 5210d86..56ad95a 100644 --- a/internal/jsutil/jsutil.go +++ b/internal/jsutil/jsutil.go @@ -7,19 +7,20 @@ import ( ) var ( - RuntimeContext = js.Global().Get("context") - Binding = js.Global().Get("context").Get("binding") - ObjectClass = js.Global().Get("Object") - PromiseClass = js.Global().Get("Promise") - RequestClass = js.Global().Get("Request") - ResponseClass = js.Global().Get("Response") - HeadersClass = js.Global().Get("Headers") - ArrayClass = js.Global().Get("Array") - Uint8ArrayClass = js.Global().Get("Uint8Array") - ErrorClass = js.Global().Get("Error") - ReadableStreamClass = js.Global().Get("ReadableStream") - DateClass = js.Global().Get("Date") - Null = js.ValueOf(nil) + RuntimeContext = js.Global().Get("context") + Binding = js.Global().Get("context").Get("binding") + ObjectClass = js.Global().Get("Object") + PromiseClass = js.Global().Get("Promise") + RequestClass = js.Global().Get("Request") + ResponseClass = js.Global().Get("Response") + HeadersClass = js.Global().Get("Headers") + ArrayClass = js.Global().Get("Array") + Uint8ArrayClass = js.Global().Get("Uint8Array") + ErrorClass = js.Global().Get("Error") + ReadableStreamClass = js.Global().Get("ReadableStream") + FixedLengthStreamClass = js.Global().Get("FixedLengthStream") + DateClass = js.Global().Get("Date") + Null = js.ValueOf(nil) ) func NewObject() js.Value { diff --git a/internal/jsutil/stream.go b/internal/jsutil/stream.go index 79f5e51..dc8afc9 100644 --- a/internal/jsutil/stream.go +++ b/internal/jsutil/stream.go @@ -198,3 +198,26 @@ func ConvertReaderToReadableStream(reader io.ReadCloser) js.Value { })) return ReadableStreamClass.New(rsInit) } + +// ConvertReaderToFixedLengthStream converts io.ReadCloser to TransformStream. +func ConvertReaderToFixedLengthStream(rc io.ReadCloser, size int64) js.Value { + stream := FixedLengthStreamClass.New(js.ValueOf(size)) + go func(writer js.Value) { + defer rc.Close() + + chunk := make([]byte, min(size, defaultChunkSize)) + for { + n, err := rc.Read(chunk) + if n > 0 { + b := Uint8ArrayClass.New(n) + js.CopyBytesToJS(b, chunk[:n]) + writer.Call("write", b) + } + if err != nil { + writer.Call("close") + return + } + } + }(stream.Get("writable").Call("getWriter")) + return stream.Get("readable") +}