"refactor(common): 重构序列化工具包,将serialize重命名为utils并添加bitset组件"
This commit is contained in:
2
common/utils/go-jsonrpc/httpio/README
Normal file
2
common/utils/go-jsonrpc/httpio/README
Normal file
@@ -0,0 +1,2 @@
|
||||
This package provides param encoders / decoders for `io.Reader` which proxy
|
||||
data over temporary http endpoints
|
||||
142
common/utils/go-jsonrpc/httpio/reader.go
Normal file
142
common/utils/go-jsonrpc/httpio/reader.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package httpio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
)
|
||||
|
||||
var log = logging.Logger("rpc")
|
||||
|
||||
func ReaderParamEncoder(addr string) jsonrpc.Option {
|
||||
return jsonrpc.WithParamEncoder(new(io.Reader), func(value reflect.Value) (reflect.Value, error) {
|
||||
r := value.Interface().(io.Reader)
|
||||
|
||||
reqID := uuid.New()
|
||||
u, _ := url.Parse(addr)
|
||||
u.Path = path.Join(u.Path, reqID.String())
|
||||
|
||||
go func() {
|
||||
// TODO: figure out errors here
|
||||
|
||||
resp, err := http.Post(u.String(), "application/octet-stream", r)
|
||||
if err != nil {
|
||||
log.Errorf("sending reader param: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
log.Errorf("sending reader param: non-200 status: ", resp.Status)
|
||||
return
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
return reflect.ValueOf(reqID), nil
|
||||
})
|
||||
}
|
||||
|
||||
type waitReadCloser struct {
|
||||
io.ReadCloser
|
||||
wait chan struct{}
|
||||
}
|
||||
|
||||
func (w *waitReadCloser) Read(p []byte) (int, error) {
|
||||
n, err := w.ReadCloser.Read(p)
|
||||
if err != nil {
|
||||
close(w.wait)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (w *waitReadCloser) Close() error {
|
||||
close(w.wait)
|
||||
return w.ReadCloser.Close()
|
||||
}
|
||||
|
||||
func ReaderParamDecoder() (http.HandlerFunc, jsonrpc.ServerOption) {
|
||||
var readersLk sync.Mutex
|
||||
readers := map[uuid.UUID]chan *waitReadCloser{}
|
||||
|
||||
hnd := func(resp http.ResponseWriter, req *http.Request) {
|
||||
strId := path.Base(req.URL.Path)
|
||||
u, err := uuid.Parse(strId)
|
||||
if err != nil {
|
||||
http.Error(resp, fmt.Sprintf("parsing reader uuid: %s", err), 400)
|
||||
}
|
||||
|
||||
readersLk.Lock()
|
||||
ch, found := readers[u]
|
||||
if !found {
|
||||
ch = make(chan *waitReadCloser)
|
||||
readers[u] = ch
|
||||
}
|
||||
readersLk.Unlock()
|
||||
|
||||
wr := &waitReadCloser{
|
||||
ReadCloser: req.Body,
|
||||
wait: make(chan struct{}),
|
||||
}
|
||||
|
||||
select {
|
||||
case ch <- wr:
|
||||
case <-req.Context().Done():
|
||||
log.Error("context error in reader stream handler (1): %v", req.Context().Err())
|
||||
resp.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-wr.wait:
|
||||
case <-req.Context().Done():
|
||||
log.Error("context error in reader stream handler (2): %v", req.Context().Err())
|
||||
resp.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
}
|
||||
|
||||
dec := jsonrpc.WithParamDecoder(new(io.Reader), func(ctx context.Context, b []byte) (reflect.Value, error) {
|
||||
var strId string
|
||||
if err := json.Unmarshal(b, &strId); err != nil {
|
||||
return reflect.Value{}, xerrors.Errorf("unmarshaling reader id: %w", err)
|
||||
}
|
||||
|
||||
u, err := uuid.Parse(strId)
|
||||
if err != nil {
|
||||
return reflect.Value{}, xerrors.Errorf("parsing reader UUDD: %w", err)
|
||||
}
|
||||
|
||||
readersLk.Lock()
|
||||
ch, found := readers[u]
|
||||
if !found {
|
||||
ch = make(chan *waitReadCloser)
|
||||
readers[u] = ch
|
||||
}
|
||||
readersLk.Unlock()
|
||||
|
||||
select {
|
||||
case wr := <-ch:
|
||||
return reflect.ValueOf(wr), nil
|
||||
case <-ctx.Done():
|
||||
return reflect.Value{}, ctx.Err()
|
||||
}
|
||||
})
|
||||
|
||||
return hnd, dec
|
||||
}
|
||||
54
common/utils/go-jsonrpc/httpio/reader_test.go
Normal file
54
common/utils/go-jsonrpc/httpio/reader_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package httpio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
)
|
||||
|
||||
type ReaderHandler struct {
|
||||
}
|
||||
|
||||
func (h *ReaderHandler) ReadAll(ctx context.Context, r io.Reader) ([]byte, error) {
|
||||
return io.ReadAll(r)
|
||||
}
|
||||
|
||||
func (h *ReaderHandler) ReadUrl(ctx context.Context, u string) (string, error) {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func TestReaderProxy(t *testing.T) {
|
||||
var client struct {
|
||||
ReadAll func(ctx context.Context, r io.Reader) ([]byte, error)
|
||||
}
|
||||
|
||||
serverHandler := &ReaderHandler{}
|
||||
|
||||
readerHandler, readerServerOpt := ReaderParamDecoder()
|
||||
rpcServer := jsonrpc.NewServer(readerServerOpt)
|
||||
rpcServer.Register("ReaderHandler", serverHandler)
|
||||
|
||||
mux := mux.NewRouter()
|
||||
mux.Handle("/rpc/v0", rpcServer)
|
||||
mux.Handle("/rpc/streams/v0/push/{uuid}", readerHandler)
|
||||
|
||||
testServ := httptest.NewServer(mux)
|
||||
defer testServ.Close()
|
||||
|
||||
re := ReaderParamEncoder("http://" + testServ.Listener.Addr().String() + "/rpc/streams/v0/push")
|
||||
closer, err := jsonrpc.NewMergeClient(context.Background(), "ws://"+testServ.Listener.Addr().String()+"/rpc/v0", "ReaderHandler", []interface{}{&client}, nil, re)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer closer()
|
||||
|
||||
read, err := client.ReadAll(context.TODO(), strings.NewReader("pooooootato"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "pooooootato", string(read), "potatos weren't equal")
|
||||
}
|
||||
Reference in New Issue
Block a user