Add support for dsd formats in HTTP

This commit is contained in:
Daniel 2021-11-23 11:24:52 +01:00
parent 560b96a825
commit 40b25a0af7

132
formats/dsd/http.go Normal file
View file

@ -0,0 +1,132 @@
package dsd
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
)
var (
ErrMissingBody = errors.New("dsd: missing http body")
ErrMissingContentType = errors.New("dsd: missing http content type")
)
const (
httpHeaderContentType = "Content-Type"
)
func LoadFromHTTPRequest(r *http.Request, t interface{}) (format uint8, err error) {
if r.Body == nil {
return 0, ErrMissingBody
}
defer r.Body.Close()
return loadFromHTTP(r.Body, r.Header.Get(httpHeaderContentType), t)
}
func LoadFromHTTPResponse(resp *http.Response, t interface{}) (format uint8, err error) {
if resp.Body == nil {
return 0, ErrMissingBody
}
defer resp.Body.Close()
return loadFromHTTP(resp.Body, resp.Header.Get(httpHeaderContentType), t)
}
func loadFromHTTP(body io.Reader, mimeType string, t interface{}) (format uint8, err error) {
// Read full body.
data, err := ioutil.ReadAll(body)
if err != nil {
return 0, fmt.Errorf("dsd: failed to read http body: %w", err)
}
// Get mime type from header, then check, clean and verify it.
if mimeType == "" {
return 0, ErrMissingContentType
}
if strings.Contains(mimeType, ";") {
mimeType = strings.SplitN(mimeType, ";", 2)[0]
}
format, ok := MimeTypeToFormat[mimeType]
if !ok {
return 0, ErrIncompatibleFormat
}
// Parse data..
return format, LoadAsFormat(data, format, t)
}
func RequestHTTPResponseFormat(r *http.Request, format uint8) (mimeType string, err error) {
// Get mime type.
mimeType, ok := FormatToMimeType[format]
if !ok {
return "", ErrIncompatibleFormat
}
// Request response format.
r.Header.Set("Accept", mimeType)
return mimeType, nil
}
func DumpToHTTPRequest(r *http.Request, t interface{}, format uint8) error {
mimeType, err := RequestHTTPResponseFormat(r, format)
if err != nil {
return err
}
// Serialize data.
data, err := Dump(t, format)
if err != nil {
return fmt.Errorf("dsd: failed to serialize: %w", err)
}
// Set body.
r.Header.Set("Content-Type", mimeType)
r.Body = ioutil.NopCloser(bytes.NewReader(data))
return nil
}
func DumpToHTTPResponse(w http.ResponseWriter, r *http.Request, t interface{}, fallbackFormat uint8) error {
// Get format from Accept header.
format, ok := MimeTypeToFormat[r.Header.Get("Accept")]
if !ok {
format = fallbackFormat
}
mimeType, ok := FormatToMimeType[format]
if !ok {
return ErrIncompatibleFormat
}
// Serialize data.
data, err := Dump(t, format)
if err != nil {
return fmt.Errorf("dsd: failed to serialize: %w", err)
}
// Write data to response
w.Header().Set("Content-Type", mimeType)
_, err = w.Write(data)
if err != nil {
return fmt.Errorf("dsd: failed to write response: %w", err)
}
return nil
}
var (
FormatToMimeType = map[uint8]string{
JSON: "application/json; charset=utf-8",
CBOR: "application/cbor",
MsgPack: "application/msgpack",
}
MimeTypeToFormat = map[string]uint8{
"application/json": JSON,
"application/cbor": CBOR,
"application/msgpack": MsgPack,
}
)