Files
fn-serverless/api/agent/protocol/json.go
Denis Makogon 7dd9b5a4cd We still can write JSON request object in parts
except just copying content from request body to STDIN
  we need to write encoded data,
  so we're using STDIN JSON stream encoder.
2017-10-07 00:43:09 +03:00

134 lines
2.9 KiB
Go

package protocol
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
// This is sent into the function
// All HTTP request headers should be set in env
type JSONIO struct {
Headers http.Header `json:"headers,omitempty"`
Body string `json:"body"`
StatusCode int `json:"status_code,omitempty"`
}
// JSONProtocol converts stdin/stdout streams from HTTP into JSON format.
type JSONProtocol struct {
in io.Writer
out io.Reader
}
func (p *JSONProtocol) IsStreamable() bool {
return true
}
type RequestEncoder struct {
*json.Encoder
}
func (h *JSONProtocol) DumpJSON(w io.Writer, req *http.Request) error {
stdin := json.NewEncoder(h.in)
_, err := io.WriteString(h.in, `{`)
if err != nil {
// this shouldn't happen
return err
}
if req.Body != nil {
_, err := io.WriteString(h.in, `"body":"`)
if err != nil {
// this shouldn't happen
return err
}
bb := new(bytes.Buffer)
_, err = bb.ReadFrom(req.Body)
if err != nil {
return err
}
err = stdin.Encode(bb.String())
if err != nil {
return err
}
_, err = io.WriteString(h.in, `",`)
if err != nil {
// this shouldn't happen
return err
}
defer bb.Reset()
defer req.Body.Close()
}
_, err = io.WriteString(h.in, `"headers:"`)
if err != nil {
// this shouldn't happen
return err
}
err = stdin.Encode(req.Header)
if err != nil {
// this shouldn't happen
return err
}
_, err = io.WriteString(h.in, `"}`)
if err != nil {
// this shouldn't happen
return err
}
return nil
}
func (h *JSONProtocol) Dispatch(w io.Writer, req *http.Request) error {
err := h.DumpJSON(w, req)
if err != nil {
return respondWithError(
w, fmt.Errorf("unable to write JSON into STDIN: %s", err.Error()))
}
jout := new(JSONIO)
dec := json.NewDecoder(h.out)
if err := dec.Decode(jout); err != nil {
return respondWithError(
w, fmt.Errorf("unable to decode JSON response object: %s", err.Error()))
}
if rw, ok := w.(http.ResponseWriter); ok {
// this has to be done for pulling out:
// - status code
// - body
// - headers
for k, vs := range jout.Headers {
for _, v := range vs {
rw.Header().Add(k, v) // on top of any specified on the route
}
}
rw.WriteHeader(jout.StatusCode)
_, err = rw.Write([]byte(jout.Body)) // TODO timeout
if err != nil {
return respondWithError(
w, fmt.Errorf("unable to write JSON response object: %s", err.Error()))
}
} else {
// logs can just copy the full thing in there, headers and all.
err = json.NewEncoder(w).Encode(jout)
if err != nil {
return respondWithError(
w, fmt.Errorf("error writing function response: %s", err.Error()))
}
}
return nil
}
func respondWithError(w io.Writer, err error) error {
errMsg := []byte(err.Error())
statusCode := http.StatusInternalServerError
if rw, ok := w.(http.ResponseWriter); ok {
rw.WriteHeader(statusCode)
rw.Write(errMsg)
} else {
// logs can just copy the full thing in there, headers and all.
w.Write(errMsg)
}
return err
}