Commit 0f5ec227 authored by Sietse Ringers's avatar Sietse Ringers

feat: switch to binary protocol in revocation HTTP traffic

parent 669bd187
......@@ -384,41 +384,41 @@ func (s *Server) handleRevocationMessage(
) (int, []byte) {
if (noun == "updatefrom" || noun == "updatelatest") && method == http.MethodGet {
if len(args) != 2 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "GET "+noun+" expects 2 url arguments"))
return server.GobResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "GET "+noun+" expects 2 url arguments"))
}
i, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
return server.GobResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
}
cred := irma.NewCredentialTypeIdentifier(args[0])
if noun == "updatefrom" {
return server.JsonResponse(s.handleGetUpdateFrom(cred, i))
return server.GobResponse(s.handleGetUpdateFrom(cred, i))
} else {
return server.JsonResponse(s.handleGetUpdateLatest(cred, i))
return server.GobResponse(s.handleGetUpdateLatest(cred, i))
}
}
if noun == "update" && method == http.MethodPost {
if len(args) != 1 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST update expects 1 url argument"))
return server.GobResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST update expects 1 url argument"))
}
cred := irma.NewCredentialTypeIdentifier(args[0])
var update *revocation.Update
if err := json.Unmarshal(message, &update); err != nil {
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
if err := irma.UnmarshalBinary(message, update); err != nil {
return server.GobResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
}
return server.JsonResponse(s.handlePostUpdate(cred, update))
return server.GobResponse(s.handlePostUpdate(cred, update))
}
if noun == "issuancerecord" && method == http.MethodPost {
if len(args) != 2 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST issuancercord expects 2 url arguments"))
return server.GobResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST issuancercord expects 2 url arguments"))
}
cred := irma.NewCredentialTypeIdentifier(args[0])
counter, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
return server.GobResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
}
return server.JsonResponse(s.handlePostIssuanceRecord(cred, counter, message))
return server.GobResponse(s.handlePostIssuanceRecord(cred, counter, message))
}
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, ""))
return server.GobResponse(nil, server.RemoteError(server.ErrorInvalidRequest, ""))
}
......@@ -2,6 +2,8 @@ package irma
import (
"bytes"
"encoding"
"encoding/gob"
"encoding/json"
"net/url"
"strconv"
......@@ -131,6 +133,44 @@ func UnmarshalValidate(data []byte, dest interface{}) error {
return nil
}
func UnmarshalValidateBinary(data []byte, dest interface{}) error {
if err := UnmarshalBinary(data, dest); err != nil {
return err
}
if v, ok := dest.(Validator); ok {
return v.Validate()
}
return nil
}
func MarshalBinary(message interface{}) ([]byte, error) {
var bts []byte
var err error
if m, ok := message.(encoding.BinaryMarshaler); ok {
bts, err = m.MarshalBinary()
if err != nil {
return nil, err
}
} else {
var buf bytes.Buffer
if err = gob.NewEncoder(&buf).Encode(message); err != nil {
return nil, err
}
bts = buf.Bytes()
}
return bts, nil
}
func UnmarshalBinary(data []byte, dst interface{}) error {
if u, ok := dst.(encoding.BinaryUnmarshaler); ok {
return u.UnmarshalBinary(data)
}
if err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(dst); err != nil {
return err
}
return nil
}
func (err *RemoteError) Error() string {
var msg string
if err.Message != "" {
......
......@@ -509,6 +509,7 @@ func (rs *RevocationStorage) getSettings(typ CredentialTypeIdentifier) *Revocati
func (client RevocationClient) PostUpdate(typ CredentialTypeIdentifier, urls []string, update *revocation.Update) {
transport := NewHTTPTransport("")
transport.Binary = true
for _, url := range urls {
err := transport.Post(fmt.Sprintf("%s/revocation/update/%s", url, typ.String()), nil, update)
if err != nil {
......@@ -537,15 +538,18 @@ func (client RevocationClient) FetchUpdateLatest(typ CredentialTypeIdentifier, c
}
func (client RevocationClient) fetchUpdate(typ CredentialTypeIdentifier, u string, i uint64) (*revocation.Update, error) {
records := &revocation.Update{}
var err error
var errs multierror.Error
transport := NewHTTPTransport("")
var (
err error
errs multierror.Error
update = &revocation.Update{}
transport = NewHTTPTransport("")
)
transport.Binary = true
for _, url := range client.Conf.CredentialTypes[typ].RevocationServers {
transport.Server = url
err = transport.Get(fmt.Sprintf("revocation/%s/%s/%d", u, typ, i), &records)
err = transport.Get(fmt.Sprintf("revocation/%s/%s/%d", u, typ, i), &update)
if err == nil {
return records, nil
return update, nil
} else {
errs.Errors = append(errs.Errors, err)
}
......
package server
import (
"bytes"
"encoding/gob"
"encoding/json"
"fmt"
"io/ioutil"
......@@ -104,15 +102,7 @@ func JsonResponse(v interface{}, err *irma.RemoteError) (int, []byte) {
}
func GobResponse(v interface{}, err *irma.RemoteError) (int, []byte) {
return encodeValOrError(v, err, gobMarshal)
}
func gobMarshal(v interface{}) ([]byte, error) {
var b bytes.Buffer
if err := gob.NewEncoder(&b).Encode(v); err != nil {
return nil, err
}
return b.Bytes(), nil
return encodeValOrError(v, err, irma.MarshalBinary)
}
func encodeValOrError(v interface{}, err *irma.RemoteError, encoder func(interface{}) ([]byte, error)) (int, []byte) {
......
......@@ -26,6 +26,7 @@ import (
// HTTPTransport sends and receives JSON messages to a HTTP server.
type HTTPTransport struct {
Server string
Binary bool
client *retryablehttp.Client
headers map[string]string
}
......@@ -91,6 +92,49 @@ func NewHTTPTransport(serverURL string) *HTTPTransport {
}
}
func (transport *HTTPTransport) marshal(o interface{}) ([]byte, error) {
if transport.Binary {
return MarshalBinary(o)
}
return json.Marshal(o)
}
func (transport *HTTPTransport) unmarshal(data []byte, dst interface{}) error {
if transport.Binary {
return UnmarshalBinary(data, dst)
}
return json.Unmarshal(data, dst)
}
func (transport *HTTPTransport) unmarshalValidate(data []byte, dst interface{}) error {
if transport.Binary {
return UnmarshalValidateBinary(data, dst)
}
return UnmarshalValidate(data, dst)
}
func (transport *HTTPTransport) log(prefix string, message interface{}, binary bool) {
if !Logger.IsLevelEnabled(logrus.TraceLevel) {
return // do nothing if nothing would be printed anyway
}
var str string
switch s := message.(type) {
case []byte:
str = string(s)
case string:
str = s
default:
tmp, _ := json.Marshal(message)
str = string(tmp)
binary = false
}
if !binary {
Logger.Tracef("transport: %s: %s", prefix, str)
} else {
Logger.Tracef("transport: %s (base64): %s", prefix, base64.RawStdEncoding.EncodeToString([]byte(str)))
}
}
// SetHeader sets a header to be sent in requests.
func (transport *HTTPTransport) SetHeader(name, val string) {
transport.headers[name] = val
......@@ -133,19 +177,19 @@ func (transport *HTTPTransport) jsonRequest(url string, method string, result in
if object != nil {
switch o := object.(type) {
case []byte:
Logger.Trace("transport: body (base64): ", base64.StdEncoding.EncodeToString(o))
transport.log("body", o, true)
contenttype = "application/octet-stream"
reader = bytes.NewBuffer(o)
case string:
Logger.Trace("transport: body: ", o)
transport.log("body", o, false)
contenttype = "text/plain; charset=UTF-8"
reader = bytes.NewBuffer([]byte(o))
default:
marshaled, err := json.Marshal(object)
marshaled, err := transport.marshal(object)
if err != nil {
return &SessionError{ErrorType: ErrorSerialization, Err: err}
}
Logger.Trace("transport: body: ", string(marshaled))
transport.log("body", string(marshaled), transport.Binary)
contenttype = "application/json; charset=UTF-8"
reader = bytes.NewBuffer(marshaled)
}
......@@ -165,22 +209,22 @@ func (transport *HTTPTransport) jsonRequest(url string, method string, result in
}
if res.StatusCode != 200 {
apierr := &RemoteError{}
err = json.Unmarshal(body, apierr)
err = transport.unmarshal(body, apierr)
if err != nil || apierr.ErrorName == "" { // Not an ApiErrorMessage
return &SessionError{ErrorType: ErrorServerResponse, RemoteStatus: res.StatusCode}
}
Logger.Tracef("transport: error: %+v", apierr)
transport.log("error", apierr, false)
return &SessionError{ErrorType: ErrorApi, RemoteStatus: res.StatusCode, RemoteError: apierr}
}
Logger.Tracef("transport: response: %s", string(body))
transport.log("response", body, transport.Binary)
if result == nil { // caller doesn't care about server response
return nil
}
if _, resultstr := result.(*string); resultstr {
*result.(*string) = string(body)
} else {
err = UnmarshalValidate(body, result)
err = transport.unmarshalValidate(body, result)
if err != nil {
return &SessionError{ErrorType: ErrorServerResponse, Err: err, RemoteStatus: res.StatusCode}
}
......@@ -202,6 +246,7 @@ func (transport *HTTPTransport) GetBytes(url string) ([]byte, error) {
if err != nil {
return nil, &SessionError{ErrorType: ErrorServerResponse, Err: err, RemoteStatus: res.StatusCode}
}
transport.log("response", b, true)
return b, nil
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment