move Reqeust / Response / ResponseWriterBuffer to jsutil

This commit is contained in:
syumai 2023-02-22 19:10:47 +09:00
parent 8bb44582c5
commit cd154341ff
6 changed files with 82 additions and 86 deletions

View File

@ -47,14 +47,14 @@ func handleRequest(reqObj js.Value, runtimeCtxObj js.Value) (js.Value, error) {
if httpHandler == nil {
return js.Value{}, fmt.Errorf("Serve must be called before handleRequest.")
}
req, err := toRequest(reqObj)
req, err := jsutil.ToRequest(reqObj)
if err != nil {
panic(err)
}
ctx := runtimecontext.New(context.Background(), runtimeCtxObj)
req = req.WithContext(ctx)
reader, writer := io.Pipe()
w := &responseWriterBuffer{
w := &jsutil.ResponseWriterBuffer{
header: http.Header{},
statusCode: http.StatusOK,
reader: reader,
@ -66,7 +66,7 @@ func handleRequest(reqObj js.Value, runtimeCtxObj js.Value) (js.Value, error) {
defer writer.Close()
httpHandler.ServeHTTP(w, req)
}()
return toJSResponse(w)
return jsutil.ToJSResponse(w)
}
// Server serves http.Handler on Cloudflare Workers.

View File

@ -1,4 +1,4 @@
package workers
package jsutil
import (
"io"
@ -7,24 +7,22 @@ import (
"strconv"
"strings"
"syscall/js"
"github.com/syumai/workers/internal/jsutil"
)
// toBody converts JavaScripts sides ReadableStream (can be null) to io.ReadCloser.
// ToBody converts JavaScripts sides ReadableStream (can be null) to io.ReadCloser.
// - ReadableStream: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
func toBody(streamOrNull js.Value) io.ReadCloser {
func ToBody(streamOrNull js.Value) io.ReadCloser {
if streamOrNull.IsNull() {
return nil
}
sr := streamOrNull.Call("getReader")
return io.NopCloser(jsutil.ConvertStreamReaderToReader(sr))
return io.NopCloser(ConvertStreamReaderToReader(sr))
}
// toHeader converts JavaScript sides Headers to http.Header.
// ToHeader converts JavaScript sides Headers to http.Header.
// - Headers: https://developer.mozilla.org/ja/docs/Web/API/Headers
func toHeader(headers js.Value) http.Header {
entries := jsutil.ArrayFrom(headers.Call("entries"))
func ToHeader(headers js.Value) http.Header {
entries := ArrayFrom(headers.Call("entries"))
headerLen := entries.Length()
h := http.Header{}
for i := 0; i < headerLen; i++ {
@ -38,14 +36,14 @@ func toHeader(headers js.Value) http.Header {
return h
}
// toRequest converts JavaScript sides Request to *http.Request.
// ToRequest converts JavaScript sides Request to *http.Request.
// - Request: https://developer.mozilla.org/ja/docs/Web/API/Request
func toRequest(req js.Value) (*http.Request, error) {
func ToRequest(req js.Value) (*http.Request, error) {
reqUrl, err := url.Parse(req.Get("url").String())
if err != nil {
return nil, err
}
header := toHeader(req.Get("headers"))
header := ToHeader(req.Get("headers"))
// ignore err
contentLength, _ := strconv.ParseInt(header.Get("Content-Length"), 10, 64)
@ -53,7 +51,7 @@ func toRequest(req js.Value) (*http.Request, error) {
Method: req.Get("method").String(),
URL: reqUrl,
Header: header,
Body: toBody(req.Get("body")),
Body: ToBody(req.Get("body")),
ContentLength: contentLength,
TransferEncoding: strings.Split(header.Get("Transfer-Encoding"), ","),
Host: header.Get("Host"),

View File

@ -0,0 +1,30 @@
package jsutil
import (
"net/http"
"syscall/js"
)
func ToJSHeader(header http.Header) js.Value {
h := HeadersClass.New()
for key, values := range header {
for _, value := range values {
h.Call("append", key, value)
}
}
return h
}
func ToJSResponse(w *ResponseWriterBuffer) (js.Value, error) {
<-w.readyCh // wait until ready
status := w.statusCode
if status == 0 {
status = http.StatusOK
}
respInit := NewObject()
respInit.Set("status", status)
respInit.Set("statusText", http.StatusText(status))
respInit.Set("headers", ToJSHeader(w.Header()))
readableStream := ConvertReaderToReadableStream(w.reader)
return ResponseClass.New(readableStream, respInit), nil
}

View File

@ -0,0 +1,38 @@
package jsutil
import (
"io"
"net/http"
"sync"
)
type ResponseWriterBuffer struct {
header http.Header
statusCode int
reader *io.PipeReader
writer *io.PipeWriter
readyCh chan struct{}
once sync.Once
}
var _ http.ResponseWriter = &ResponseWriterBuffer{}
// ready indicates that ResponseWriterBuffer is ready to be converted to Response.
func (w *ResponseWriterBuffer) ready() {
w.once.Do(func() {
close(w.readyCh)
})
}
func (w *ResponseWriterBuffer) Write(data []byte) (n int, err error) {
w.ready()
return w.writer.Write(data)
}
func (w *ResponseWriterBuffer) Header() http.Header {
return w.header
}
func (w *ResponseWriterBuffer) WriteHeader(statusCode int) {
w.statusCode = statusCode
}

View File

@ -1,32 +0,0 @@
package workers
import (
"net/http"
"syscall/js"
"github.com/syumai/workers/internal/jsutil"
)
func toJSHeader(header http.Header) js.Value {
h := jsutil.HeadersClass.New()
for key, values := range header {
for _, value := range values {
h.Call("append", key, value)
}
}
return h
}
func toJSResponse(w *responseWriterBuffer) (js.Value, error) {
<-w.readyCh // wait until ready
status := w.statusCode
if status == 0 {
status = http.StatusOK
}
respInit := jsutil.NewObject()
respInit.Set("status", status)
respInit.Set("statusText", http.StatusText(status))
respInit.Set("headers", toJSHeader(w.Header()))
readableStream := jsutil.ConvertReaderToReadableStream(w.reader)
return jsutil.ResponseClass.New(readableStream, respInit), nil
}

View File

@ -1,38 +0,0 @@
package workers
import (
"io"
"net/http"
"sync"
)
type responseWriterBuffer struct {
header http.Header
statusCode int
reader *io.PipeReader
writer *io.PipeWriter
readyCh chan struct{}
once sync.Once
}
var _ http.ResponseWriter = &responseWriterBuffer{}
// ready indicates that responseWriterBuffer is ready to be converted to Response.
func (w *responseWriterBuffer) ready() {
w.once.Do(func() {
close(w.readyCh)
})
}
func (w *responseWriterBuffer) Write(data []byte) (n int, err error) {
w.ready()
return w.writer.Write(data)
}
func (w *responseWriterBuffer) Header() http.Header {
return w.header
}
func (w *responseWriterBuffer) WriteHeader(statusCode int) {
w.statusCode = statusCode
}