mirror of
https://github.com/syumai/workers.git
synced 2025-03-11 09:49:12 +00:00
Merge pull request #92 from syumai/return-fetch-body-directly
return fetch body directly
This commit is contained in:
commit
4b950e8af6
@ -2,9 +2,6 @@ module github.com/syumai/workers/_examples/basic-auth-server
|
|||||||
|
|
||||||
go 1.21.3
|
go 1.21.3
|
||||||
|
|
||||||
require (
|
require github.com/syumai/workers v0.5.1
|
||||||
github.com/syumai/tinyutil v0.3.0
|
|
||||||
github.com/syumai/workers v0.5.1
|
|
||||||
)
|
|
||||||
|
|
||||||
replace github.com/syumai/workers => ../../
|
replace github.com/syumai/workers => ../../
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
github.com/syumai/tinyutil v0.3.0 h1:sgWeE8oQyequIRLNeHZgR1PddpY4mxcdkfMgx2m53IE=
|
|
||||||
github.com/syumai/tinyutil v0.3.0/go.mod h1:/owCyUs1bh6tKxH7K1Ze3M/zZtZ+vGrj3h82fgNHDFI=
|
|
@ -1,12 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/syumai/tinyutil/httputil"
|
|
||||||
"github.com/syumai/workers"
|
"github.com/syumai/workers"
|
||||||
|
"github.com/syumai/workers/cloudflare/fetch"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -33,12 +34,20 @@ func handleRequest(w http.ResponseWriter, req *http.Request) {
|
|||||||
u := *req.URL
|
u := *req.URL
|
||||||
u.Scheme = "https"
|
u.Scheme = "https"
|
||||||
u.Host = "syum.ai"
|
u.Host = "syum.ai"
|
||||||
resp, err := httputil.Get(u.String())
|
r, err := fetch.NewRequest(req.Context(), req.Method, u.String(), req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(w, http.StatusInternalServerError, "Internal Error")
|
handleError(w, http.StatusInternalServerError, "Internal Error")
|
||||||
log.Printf("failed to execute proxy request: %v\n", err)
|
log.Printf("failed to execute proxy request: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
r.Header = req.Header.Clone()
|
||||||
|
cli := fetch.NewClient()
|
||||||
|
resp, err := cli.Do(r, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
for k, values := range resp.Header {
|
for k, values := range resp.Header {
|
||||||
for _, v := range values {
|
for _, v := range values {
|
||||||
w.Header().Add(k, v)
|
w.Header().Add(k, v)
|
||||||
|
@ -66,7 +66,7 @@ func (kv *KVNamespace) GetReader(key string, opts *KVNamespaceGetOptions) (io.Re
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return jsutil.ConvertStreamReaderToReader(v.Call("getReader")), nil
|
return jsutil.ConvertReadableStreamToReadCloser(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// KVNamespaceListOptions represents Cloudflare KV namespace list options.
|
// KVNamespaceListOptions represents Cloudflare KV namespace list options.
|
||||||
|
@ -54,7 +54,7 @@ func toR2Object(v js.Value) (*R2Object, error) {
|
|||||||
bodyVal := v.Get("body")
|
bodyVal := v.Get("body")
|
||||||
var body io.Reader
|
var body io.Reader
|
||||||
if !bodyVal.IsUndefined() {
|
if !bodyVal.IsUndefined() {
|
||||||
body = jsutil.ConvertStreamReaderToReader(v.Get("body").Call("getReader"))
|
body = jsutil.ConvertReadableStreamToReadCloser(v.Get("body"))
|
||||||
}
|
}
|
||||||
return &R2Object{
|
return &R2Object{
|
||||||
instance: v,
|
instance: v,
|
||||||
|
@ -14,12 +14,13 @@ import (
|
|||||||
func newSocket(ctx context.Context, sockVal js.Value, readDeadline, writeDeadline time.Time) *Socket {
|
func newSocket(ctx context.Context, sockVal js.Value, readDeadline, writeDeadline time.Time) *Socket {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
writerVal := sockVal.Get("writable").Call("getWriter")
|
writerVal := sockVal.Get("writable").Call("getWriter")
|
||||||
readerVal := sockVal.Get("readable").Call("getReader")
|
readerVal := sockVal.Get("readable")
|
||||||
|
readCloser := jsutil.ConvertReadableStreamToReadCloser(readerVal)
|
||||||
return &Socket{
|
return &Socket{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
|
|
||||||
reader: jsutil.ConvertStreamReaderToReader(readerVal),
|
reader: readCloser,
|
||||||
writerVal: writerVal,
|
writerVal: writerVal,
|
||||||
|
|
||||||
readDeadline: readDeadline,
|
readDeadline: readDeadline,
|
||||||
@ -27,7 +28,7 @@ func newSocket(ctx context.Context, sockVal js.Value, readDeadline, writeDeadlin
|
|||||||
|
|
||||||
startTLS: func() js.Value { return sockVal.Call("startTls") },
|
startTLS: func() js.Value { return sockVal.Call("startTls") },
|
||||||
close: func() { sockVal.Call("close") },
|
close: func() { sockVal.Call("close") },
|
||||||
closeRead: func() { readerVal.Call("close") },
|
closeRead: func() { readCloser.Close() },
|
||||||
closeWrite: func() { writerVal.Call("close") },
|
closeWrite: func() { writerVal.Call("close") },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,7 @@ func ToBody(streamOrNull js.Value) io.ReadCloser {
|
|||||||
if streamOrNull.IsNull() {
|
if streamOrNull.IsNull() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sr := streamOrNull.Call("getReader")
|
return jsutil.ConvertReadableStreamToReadCloser(streamOrNull)
|
||||||
return io.NopCloser(jsutil.ConvertStreamReaderToReader(sr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToRequest converts JavaScript sides Request to *http.Request.
|
// ToRequest converts JavaScript sides Request to *http.Request.
|
||||||
|
@ -25,19 +25,19 @@ func ToResponse(res js.Value) (*http.Response, error) {
|
|||||||
Status: strconv.Itoa(status) + " " + res.Get("statusText").String(),
|
Status: strconv.Itoa(status) + " " + res.Get("statusText").String(),
|
||||||
StatusCode: status,
|
StatusCode: status,
|
||||||
Header: header,
|
Header: header,
|
||||||
Body: io.NopCloser(jsutil.ConvertStreamReaderToReader(blob.Call("stream").Call("getReader"))),
|
Body: jsutil.ConvertReadableStreamToReadCloser(blob.Call("stream")),
|
||||||
ContentLength: contentLength,
|
ContentLength: contentLength,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToJSResponse converts *http.Response to JavaScript sides Response class object.
|
// ToJSResponse converts *http.Response to JavaScript sides Response class object.
|
||||||
func ToJSResponse(res *http.Response) js.Value {
|
func ToJSResponse(res *http.Response) js.Value {
|
||||||
return newJSResponse(res.StatusCode, res.Header, res.Body)
|
return newJSResponse(res.StatusCode, res.Header, res.Body, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newJSResponse creates JavaScript sides Response class object.
|
// newJSResponse creates JavaScript sides Response class object.
|
||||||
// - Response: https://developer.mozilla.org/docs/Web/API/Response
|
// - Response: https://developer.mozilla.org/docs/Web/API/Response
|
||||||
func newJSResponse(statusCode int, headers http.Header, body io.ReadCloser) js.Value {
|
func newJSResponse(statusCode int, headers http.Header, body io.ReadCloser, rawBody *js.Value) js.Value {
|
||||||
status := statusCode
|
status := statusCode
|
||||||
if status == 0 {
|
if status == 0 {
|
||||||
status = http.StatusOK
|
status = http.StatusOK
|
||||||
@ -52,6 +52,11 @@ func newJSResponse(statusCode int, headers http.Header, body io.ReadCloser) js.V
|
|||||||
status == http.StatusNotModified {
|
status == http.StatusNotModified {
|
||||||
return jsutil.ResponseClass.New(jsutil.Null, respInit)
|
return jsutil.ResponseClass.New(jsutil.Null, respInit)
|
||||||
}
|
}
|
||||||
readableStream := jsutil.ConvertReaderToReadableStream(body)
|
var readableStream js.Value
|
||||||
|
if rawBody != nil {
|
||||||
|
readableStream = *rawBody
|
||||||
|
} else {
|
||||||
|
readableStream = jsutil.ConvertReaderToReadableStream(body)
|
||||||
|
}
|
||||||
return jsutil.ResponseClass.New(readableStream, respInit)
|
return jsutil.ResponseClass.New(readableStream, respInit)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResponseWriter struct {
|
type ResponseWriter struct {
|
||||||
@ -14,9 +16,13 @@ type ResponseWriter struct {
|
|||||||
Writer *io.PipeWriter
|
Writer *io.PipeWriter
|
||||||
ReadyCh chan struct{}
|
ReadyCh chan struct{}
|
||||||
Once sync.Once
|
Once sync.Once
|
||||||
|
RawJSBody *js.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ http.ResponseWriter = &ResponseWriter{}
|
var (
|
||||||
|
_ http.ResponseWriter = (*ResponseWriter)(nil)
|
||||||
|
_ jsutil.RawJSBodyWriter = (*ResponseWriter)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// Ready indicates that ResponseWriter is ready to be converted to Response.
|
// Ready indicates that ResponseWriter is ready to be converted to Response.
|
||||||
func (w *ResponseWriter) Ready() {
|
func (w *ResponseWriter) Ready() {
|
||||||
@ -38,8 +44,12 @@ func (w *ResponseWriter) WriteHeader(statusCode int) {
|
|||||||
w.StatusCode = statusCode
|
w.StatusCode = statusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *ResponseWriter) WriteRawJSBody(body js.Value) {
|
||||||
|
w.RawJSBody = &body
|
||||||
|
}
|
||||||
|
|
||||||
// ToJSResponse converts *ResponseWriter to JavaScript sides Response.
|
// ToJSResponse converts *ResponseWriter to JavaScript sides Response.
|
||||||
// - Response: https://developer.mozilla.org/docs/Web/API/Response
|
// - Response: https://developer.mozilla.org/docs/Web/API/Response
|
||||||
func (w *ResponseWriter) ToJSResponse() js.Value {
|
func (w *ResponseWriter) ToJSResponse() js.Value {
|
||||||
return newJSResponse(w.StatusCode, w.HeaderValue, w.Reader)
|
return newJSResponse(w.StatusCode, w.HeaderValue, w.Reader, w.RawJSBody)
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,30 @@ import (
|
|||||||
"syscall/js"
|
"syscall/js"
|
||||||
)
|
)
|
||||||
|
|
||||||
// streamReaderToReader implements io.Reader sourced from ReadableStreamDefaultReader.
|
type RawJSBodyWriter interface {
|
||||||
// - ReadableStreamDefaultReader: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader
|
WriteRawJSBody(body js.Value)
|
||||||
// - This implementation is based on: https://deno.land/std@0.139.0/streams/conversion.ts#L76
|
|
||||||
type streamReaderToReader struct {
|
|
||||||
buf bytes.Buffer
|
|
||||||
streamReader js.Value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readableStreamToReadCloser implements io.Reader sourced from ReadableStreamDefaultReader.
|
||||||
|
// - ReadableStreamDefaultReader: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader
|
||||||
|
// - This implementation is based on: https://deno.land/std@0.139.0/streams/conversion.ts#L76
|
||||||
|
type readableStreamToReadCloser struct {
|
||||||
|
buf bytes.Buffer
|
||||||
|
stream js.Value
|
||||||
|
streamReader *js.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ io.ReadCloser = (*readableStreamToReadCloser)(nil)
|
||||||
|
_ io.WriterTo = (*readableStreamToReadCloser)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// Read reads bytes from ReadableStreamDefaultReader.
|
// Read reads bytes from ReadableStreamDefaultReader.
|
||||||
func (sr *streamReaderToReader) Read(p []byte) (n int, err error) {
|
func (sr *readableStreamToReadCloser) Read(p []byte) (n int, err error) {
|
||||||
|
if sr.streamReader == nil {
|
||||||
|
r := sr.stream.Call("getReader")
|
||||||
|
sr.streamReader = &r
|
||||||
|
}
|
||||||
if sr.buf.Len() == 0 {
|
if sr.buf.Len() == 0 {
|
||||||
promise := sr.streamReader.Call("read")
|
promise := sr.streamReader.Call("read")
|
||||||
resultCh := make(chan js.Value)
|
resultCh := make(chan js.Value)
|
||||||
@ -56,10 +70,31 @@ func (sr *streamReaderToReader) Read(p []byte) (n int, err error) {
|
|||||||
return sr.buf.Read(p)
|
return sr.buf.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertStreamReaderToReader converts ReadableStreamDefaultReader to io.Reader.
|
func (sr *readableStreamToReadCloser) Close() error {
|
||||||
func ConvertStreamReaderToReader(sr js.Value) io.Reader {
|
if sr.streamReader == nil {
|
||||||
return &streamReaderToReader{
|
return nil
|
||||||
streamReader: sr,
|
}
|
||||||
|
sr.streamReader.Call("close")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readerWrapper is wrapper to disable readableStreamToReadCloser's WriteTo method.
|
||||||
|
type readerWrapper struct {
|
||||||
|
io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr *readableStreamToReadCloser) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
if w, ok := w.(RawJSBodyWriter); ok {
|
||||||
|
w.WriteRawJSBody(sr.stream)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return io.Copy(w, &readerWrapper{sr})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertReadableStreamToReadCloser converts ReadableStream to io.ReadCloser.
|
||||||
|
func ConvertReadableStreamToReadCloser(stream js.Value) io.ReadCloser {
|
||||||
|
return &readableStreamToReadCloser{
|
||||||
|
stream: stream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user