1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
|
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
// Package httprequest provides functionality for unmarshaling
// HTTP request parameters into a struct type.
package httprequest
import (
"fmt"
"net/http"
"reflect"
"sort"
"strings"
"sync"
"github.com/julienschmidt/httprouter"
"golang.org/x/net/context"
"gopkg.in/errgo.v1"
)
// TODO include field name and source in error messages.
var (
typeMutex sync.RWMutex
typeMap = make(map[reflect.Type]*requestType)
)
// Route is the type of a field that specifies a routing
// path and HTTP method. See Marshal and Unmarshal
// for details.
type Route struct{}
// Params holds the parameters provided to an HTTP request.
type Params struct {
Response http.ResponseWriter
Request *http.Request
PathVar httprouter.Params
// PathPattern holds the path pattern matched by httprouter.
// It is only set where httprequest has the information;
// that is where the call was made by Server.Handler
// or Server.Handlers.
PathPattern string
// Context holds a context for the request. In Go 1.7 and later,
// this should be used in preference to Request.Context.
Context context.Context
}
// resultMaker is provided to the unmarshal functions.
// When called with the value passed to the unmarshaler,
// it returns the field value to be assigned to,
// creating it if necessary.
type resultMaker func(reflect.Value) reflect.Value
// unmarshaler unmarshals some value from params into
// the given value. The value should not be assigned to directly,
// but passed to makeResult and then updated.
type unmarshaler func(v reflect.Value, p Params, makeResult resultMaker) error
// marshaler marshals the specified value into params.
// The value is always the value type, even if the field type
// is a pointer.
type marshaler func(reflect.Value, *Params) error
// requestType holds information derived from a request
// type, preprocessed so that it's quick to marshal or unmarshal.
type requestType struct {
method string
path string
fields []field
}
// field holds preprocessed information on an individual field
// in the request.
type field struct {
name string
// index holds the index slice of the field.
index []int
// unmarshal is used to unmarshal the value into
// the given field. The value passed as its first
// argument is not a pointer type, but is addressable.
unmarshal unmarshaler
// marshal is used to marshal the value into the
// give filed. The value passed as its first argument is not
// a pointer type, but it is addressable.
marshal marshaler
// makeResult is the resultMaker that will be
// passed into the unmarshaler.
makeResult resultMaker
// isPointer is true if the field is pointer to the underlying type.
isPointer bool
}
// getRequestType is like parseRequestType except that
// it returns the cached requestType when possible,
// adding the type to the cache otherwise.
func getRequestType(t reflect.Type) (*requestType, error) {
typeMutex.RLock()
pt := typeMap[t]
typeMutex.RUnlock()
if pt != nil {
return pt, nil
}
typeMutex.Lock()
defer typeMutex.Unlock()
if pt = typeMap[t]; pt != nil {
// The type has been parsed after we dropped
// the read lock, so use it.
return pt, nil
}
pt, err := parseRequestType(t)
if err != nil {
return nil, errgo.Mask(err)
}
typeMap[t] = pt
return pt, nil
}
// parseRequestType preprocesses the given type
// into a form that can be efficiently interpreted
// by Unmarshal.
func parseRequestType(t reflect.Type) (*requestType, error) {
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
return nil, fmt.Errorf("type is not pointer to struct")
}
hasBody := false
var pt requestType
foundRoute := false
// taggedFieldIndex holds the index of most recent anonymous
// tagged field - we will skip any fields inside that.
// It is nil when we're not inside an anonymous tagged field.
var taggedFieldIndex []int
for _, f := range fields(t.Elem()) {
if f.PkgPath != "" && !f.Anonymous {
// Ignore non-anonymous unexported fields.
continue
}
if taggedFieldIndex != nil && withinIndex(f.Index, taggedFieldIndex) {
// Ignore fields within tagged anonymous fields.
continue
}
taggedFieldIndex = nil
if !foundRoute && f.Anonymous && f.Type == reflect.TypeOf(Route{}) {
var err error
pt.method, pt.path, err = parseRouteTag(f.Tag)
if err != nil {
return nil, errgo.Notef(err, "bad route tag %q", f.Tag)
}
foundRoute = true
continue
}
tag, err := parseTag(f.Tag, f.Name)
if err != nil {
return nil, errgo.Notef(err, "bad tag %q in field %s", f.Tag, f.Name)
}
if tag.source == sourceBody {
if hasBody {
return nil, errgo.New("more than one body field specified")
}
hasBody = true
}
field := field{
index: f.Index,
name: f.Name,
}
if f.Type.Kind() == reflect.Ptr {
// The field is a pointer, so when the value is set,
// we need to create a new pointer to put
// it into.
field.makeResult = makePointerResult
field.isPointer = true
f.Type = f.Type.Elem()
} else {
field.makeResult = makeValueResult
field.isPointer = false
}
field.unmarshal, err = getUnmarshaler(tag, f.Type)
if err != nil {
return nil, errgo.Mask(err)
}
field.marshal, err = getMarshaler(tag, f.Type)
if err != nil {
return nil, errgo.Mask(err)
}
if f.Anonymous && tag.source != sourceNone {
taggedFieldIndex = f.Index
}
pt.fields = append(pt.fields, field)
}
return &pt, nil
}
// withinIndex reports whether the field with index i0 should be
// considered to be within the field with index i1.
func withinIndex(i0, i1 []int) bool {
// The index of a field within an anonymous field is formed by
// appending its field offset to the anonymous field's index, so
// it is sufficient that we check that i0 is prefixed by i1.
if len(i0) < len(i1) {
return false
}
for i := range i1 {
if i0[i] != i1[i] {
return false
}
}
return true
}
// Note: we deliberately omit HEAD and OPTIONS
// from this list. HEAD will be routed through GET handlers
// and OPTIONS is handled separately.
var validMethod = map[string]bool{
"PUT": true,
"POST": true,
"DELETE": true,
"GET": true,
"PATCH": true,
}
func parseRouteTag(tag reflect.StructTag) (method, path string, err error) {
tagStr := tag.Get("httprequest")
if tagStr == "" {
return "", "", errgo.New("no httprequest tag")
}
f := strings.Fields(tagStr)
switch len(f) {
case 2:
path = f[1]
fallthrough
case 1:
method = f[0]
default:
return "", "", errgo.New("wrong field count")
}
if !validMethod[method] {
return "", "", errgo.Newf("invalid method")
}
// TODO check that path looks valid
return method, path, nil
}
func makePointerResult(v reflect.Value) reflect.Value {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
return v.Elem()
}
func makeValueResult(v reflect.Value) reflect.Value {
return v
}
type tagSource uint8
const (
sourceNone = iota
sourcePath
sourceForm
sourceBody
sourceHeader
)
type tag struct {
name string
source tagSource
omitempty bool
}
// parseTag parses the given struct tag attached to the given
// field name into a tag structure.
func parseTag(rtag reflect.StructTag, fieldName string) (tag, error) {
t := tag{
name: fieldName,
}
tagStr := rtag.Get("httprequest")
if tagStr == "" {
return t, nil
}
fields := strings.Split(tagStr, ",")
if fields[0] != "" {
t.name = fields[0]
}
for _, f := range fields[1:] {
switch f {
case "path":
t.source = sourcePath
case "form":
t.source = sourceForm
case "body":
t.source = sourceBody
case "header":
t.source = sourceHeader
case "omitempty":
t.omitempty = true
default:
return tag{}, fmt.Errorf("unknown tag flag %q", f)
}
}
if t.omitempty && t.source != sourceForm && t.source != sourceHeader {
return tag{}, fmt.Errorf("can only use omitempty with form or header fields")
}
return t, nil
}
// fields returns all the fields in the given struct type
// including fields inside anonymous struct members.
// The fields are ordered with top level fields first
// followed by the members of those fields
// for anonymous fields.
func fields(t reflect.Type) []reflect.StructField {
byName := make(map[string]reflect.StructField)
addFields(t, byName, nil)
fields := make(fieldsByIndex, 0, len(byName))
for _, f := range byName {
if f.Name != "" {
fields = append(fields, f)
}
}
sort.Sort(fields)
return fields
}
func addFields(t reflect.Type, byName map[string]reflect.StructField, index []int) {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
index := append(index, i)
var add bool
old, ok := byName[f.Name]
switch {
case ok && len(old.Index) == len(index):
// Fields with the same name at the same depth
// cancel one another out. Set the field name
// to empty to signify that has happened.
old.Name = ""
byName[f.Name] = old
add = false
case ok:
// Fields at less depth win.
add = len(index) < len(old.Index)
default:
// The field did not previously exist.
add = true
}
if add {
// copy the index so that it's not overwritten
// by the other appends.
f.Index = append([]int(nil), index...)
byName[f.Name] = f
}
if f.Anonymous {
if f.Type.Kind() == reflect.Ptr {
f.Type = f.Type.Elem()
}
if f.Type.Kind() == reflect.Struct {
addFields(f.Type, byName, index)
}
}
}
}
type fieldsByIndex []reflect.StructField
func (f fieldsByIndex) Len() int {
return len(f)
}
func (f fieldsByIndex) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
func (f fieldsByIndex) Less(i, j int) bool {
indexi, indexj := f[i].Index, f[j].Index
for len(indexi) != 0 && len(indexj) != 0 {
ii, ij := indexi[0], indexj[0]
if ii != ij {
return ii < ij
}
indexi, indexj = indexi[1:], indexj[1:]
}
return len(indexi) < len(indexj)
}
|