mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
124 lines
2.5 KiB
Go
124 lines
2.5 KiB
Go
|
package http
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/demodesk/neko/pkg/types"
|
||
|
"github.com/demodesk/neko/pkg/utils"
|
||
|
)
|
||
|
|
||
|
type BatchRequest struct {
|
||
|
Path string `json:"path"`
|
||
|
Method string `json:"method"`
|
||
|
Body json.RawMessage `json:"body,omitempty"`
|
||
|
}
|
||
|
|
||
|
type BatchResponse struct {
|
||
|
Path string `json:"path"`
|
||
|
Method string `json:"method"`
|
||
|
Body json.RawMessage `json:"body,omitempty"`
|
||
|
Status int `json:"status"`
|
||
|
}
|
||
|
|
||
|
func (b *BatchResponse) Error(httpErr *utils.HTTPError) (err error) {
|
||
|
b.Body, err = json.Marshal(httpErr)
|
||
|
b.Status = httpErr.Code
|
||
|
return
|
||
|
}
|
||
|
|
||
|
type batchHandler struct {
|
||
|
Router types.Router
|
||
|
PathPrefix string
|
||
|
Excluded []string
|
||
|
}
|
||
|
|
||
|
func (b *batchHandler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||
|
var requests []BatchRequest
|
||
|
if err := json.NewDecoder(r.Body).Decode(&requests); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
responses := make([]BatchResponse, len(requests))
|
||
|
for i, request := range requests {
|
||
|
res := BatchResponse{
|
||
|
Path: request.Path,
|
||
|
Method: request.Method,
|
||
|
}
|
||
|
|
||
|
if !strings.HasPrefix(request.Path, b.PathPrefix) {
|
||
|
res.Error(utils.HttpBadRequest("this path is not allowed in batch requests"))
|
||
|
responses[i] = res
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if exists, _ := utils.ArrayIn(request.Path, b.Excluded); exists {
|
||
|
res.Error(utils.HttpBadRequest("this path is excluded from batch requests"))
|
||
|
responses[i] = res
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// prepare request
|
||
|
req, err := http.NewRequest(request.Method, request.Path, bytes.NewBuffer(request.Body))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// copy headers
|
||
|
for k, vv := range r.Header {
|
||
|
for _, v := range vv {
|
||
|
req.Header.Add(k, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// execute request
|
||
|
rr := newResponseRecorder()
|
||
|
b.Router.ServeHTTP(rr, req)
|
||
|
|
||
|
// read response
|
||
|
body, err := io.ReadAll(rr.Body)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// write response
|
||
|
responses[i] = BatchResponse{
|
||
|
Path: request.Path,
|
||
|
Method: request.Method,
|
||
|
Body: body,
|
||
|
Status: rr.Code,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return utils.HttpSuccess(w, responses)
|
||
|
}
|
||
|
|
||
|
type responseRecorder struct {
|
||
|
Code int
|
||
|
HeaderMap http.Header
|
||
|
Body *bytes.Buffer
|
||
|
}
|
||
|
|
||
|
func newResponseRecorder() *responseRecorder {
|
||
|
return &responseRecorder{
|
||
|
Code: http.StatusOK,
|
||
|
HeaderMap: make(http.Header),
|
||
|
Body: new(bytes.Buffer),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (w *responseRecorder) Header() http.Header {
|
||
|
return w.HeaderMap
|
||
|
}
|
||
|
|
||
|
func (w *responseRecorder) Write(b []byte) (int, error) {
|
||
|
return w.Body.Write(b)
|
||
|
}
|
||
|
|
||
|
func (w *responseRecorder) WriteHeader(code int) {
|
||
|
w.Code = code
|
||
|
}
|