Skip to content

Instantly share code, notes, and snippets.

@mwf
Created May 22, 2019 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwf/ee7bed621f00f7aabb9a6821d39e48a0 to your computer and use it in GitHub Desktop.
Save mwf/ee7bed621f00f7aabb9a6821d39e48a0 to your computer and use it in GitHub Desktop.
github.com/utrack/clay "application/x-www-form-urlencoded" marshaller
syntax = "proto3";
import "google/api/annotations.proto";
service Foo {
rpc ParseForm (FormData) returns (FormData) {
option (google.api.http) = {
post: "/form"
body: "*"
};
};
}
message FormData {
string b = 1;
int32 a = 2;
};
package strings
import (
"fmt"
"io"
"io/ioutil"
"net/url"
"reflect"
"github.com/gogo/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"github.com/pkg/errors"
"github.com/utrack/clay/v2/transport/httpruntime"
)
const (
maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
)
type MarshalerFormUrlencoded struct{}
var _ httpruntime.Marshaler = MarshalerFormUrlencoded{}
func (MarshalerFormUrlencoded) ContentType() string {
return "application/x-www-form-urlencoded"
}
func (m MarshalerFormUrlencoded) Unmarshal(reader io.Reader, dst interface{}) error {
var msg proto.Message
var ok bool
// We need this dirty hack because `dst` contains double pointer to real struct.
// And we can't extract an interface from a double pointer without some reflection
v := reflect.ValueOf(dst)
for {
if kind := v.Kind(); kind != reflect.Ptr && kind != reflect.Interface {
break
}
vv := v.Interface()
msg, ok = vv.(proto.Message)
if ok {
break
}
v = v.Elem()
}
if msg == nil {
return fmt.Errorf("Form unmarshaller supports only `proto.Message` request type, got %T", dst)
}
reader = io.LimitReader(reader, maxFormSize+1)
b, err := ioutil.ReadAll(reader)
if err != nil {
return err
}
if int64(len(b)) > maxFormSize {
return errors.New("http: POST too large")
}
vs, err := url.ParseQuery(string(b))
if err != nil {
return err
}
filter := &utilities.DoubleArray{}
err = runtime.PopulateQueryParameters(msg, vs, filter)
if err != nil {
return err
}
return nil
}
func (m MarshalerFormUrlencoded) Marshal(w io.Writer, src interface{}) error {
return errors.New("not implemented")
}
// Code generated by protoc-gen-goclay, but your can (must) modify it.
// source: strings.proto
package strings
import (
"context"
"github.com/utrack/clay/v2/transport/httpruntime"
desc "<path to pb package>"
)
func init() {
httpruntime.OverrideMarshaler("application/x-www-form-urlencoded", MarshalerFormUrlencoded{})
}
func (i *Implementation) ParseForm(ctx context.Context, req *desc.FormData) (*desc.FormData, error) {
return req, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment