| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package prpc | 5 package prpc |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "encoding/base64" | 9 "encoding/base64" |
| 10 "fmt" | 10 "fmt" |
| 11 "io/ioutil" | 11 "io/ioutil" |
| 12 "mime" | 12 "mime" |
| 13 "net/http" | 13 "net/http" |
| 14 "strings" | 14 "strings" |
| 15 | 15 |
| 16 "github.com/golang/protobuf/jsonpb" | 16 "github.com/golang/protobuf/jsonpb" |
| 17 "github.com/golang/protobuf/proto" | 17 "github.com/golang/protobuf/proto" |
| 18 "golang.org/x/net/context" | 18 "golang.org/x/net/context" |
| 19 "google.golang.org/grpc/metadata" | 19 "google.golang.org/grpc/metadata" |
| 20 | 20 |
| 21 "github.com/luci/luci-go/common/clock" | 21 "github.com/luci/luci-go/common/clock" |
| 22 prpccommon "github.com/luci/luci-go/common/prpc" |
| 22 ) | 23 ) |
| 23 | 24 |
| 24 // This file implements decoding of HTTP requests to RPC parameters. | 25 // This file implements decoding of HTTP requests to RPC parameters. |
| 25 | 26 |
| 26 const ( | 27 const ( |
| 27 // headerSuffixBinary is a suffix of an HTTP header that specifies that | 28 // headerSuffixBinary is a suffix of an HTTP header that specifies that |
| 28 // the header value is encoded in std base64. | 29 // the header value is encoded in std base64. |
| 29 // After decoding, a handler must process the header without the suffix. | 30 // After decoding, a handler must process the header without the suffix. |
| 30 headerSuffixBinary = "-Bin" | 31 headerSuffixBinary = "-Bin" |
| 31 headerContentType = "Content-Type" | 32 headerContentType = "Content-Type" |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 default: | 88 default: |
| 88 err = fmt.Errorf("unknown message format: %d", format) | 89 err = fmt.Errorf("unknown message format: %d", format) |
| 89 } | 90 } |
| 90 if err != nil { | 91 if err != nil { |
| 91 return errorf(http.StatusBadRequest, "could not decode body: %s"
, err) | 92 return errorf(http.StatusBadRequest, "could not decode body: %s"
, err) |
| 92 } | 93 } |
| 93 return nil | 94 return nil |
| 94 } | 95 } |
| 95 | 96 |
| 96 // parseHeader parses HTTP headers and derives a new context. | 97 // parseHeader parses HTTP headers and derives a new context. |
| 97 // Supports headerTimeout. | 98 // Supports HeaderTimeout. |
| 98 // Ignores "Accept" and "Content-Type" headers. | 99 // Ignores "Accept" and "Content-Type" headers. |
| 99 // | 100 // |
| 100 // If there are unrecognized HTTP headers, with or without headerSuffixBinary, | 101 // If there are unrecognized HTTP headers, with or without headerSuffixBinary, |
| 101 // they are added to a metadata.MD and a new context is derived. | 102 // they are added to a metadata.MD and a new context is derived. |
| 102 // If c already has metadata, the latter is copied. | 103 // If c already has metadata, the latter is copied. |
| 103 // | 104 // |
| 104 // In case of an error, returns c unmodified. | 105 // In case of an error, returns c unmodified. |
| 105 func parseHeader(c context.Context, header http.Header) (context.Context, error)
{ | 106 func parseHeader(c context.Context, header http.Header) (context.Context, error)
{ |
| 106 origC := c | 107 origC := c |
| 107 | 108 |
| 108 md, ok := metadata.FromContext(c) | 109 md, ok := metadata.FromContext(c) |
| 109 if ok { | 110 if ok { |
| 110 md = md.Copy() | 111 md = md.Copy() |
| 111 } else { | 112 } else { |
| 112 md = metadata.MD{} | 113 md = metadata.MD{} |
| 113 } | 114 } |
| 114 | 115 |
| 115 addedMeta := false | 116 addedMeta := false |
| 116 for name, values := range header { | 117 for name, values := range header { |
| 117 if len(values) == 0 { | 118 if len(values) == 0 { |
| 118 continue | 119 continue |
| 119 } | 120 } |
| 120 name = http.CanonicalHeaderKey(name) | 121 name = http.CanonicalHeaderKey(name) |
| 121 switch name { | 122 switch name { |
| 122 | 123 |
| 123 » » case headerTimeout: | 124 » » case prpccommon.HeaderTimeout: |
| 124 // Decode only first value, ignore the rest | 125 // Decode only first value, ignore the rest |
| 125 // to be consistent with http.Header.Get. | 126 // to be consistent with http.Header.Get. |
| 126 » » » timeout, err := decodeTimeout(values[0]) | 127 » » » timeout, err := prpccommon.DecodeTimeout(values[0]) |
| 127 if err != nil { | 128 if err != nil { |
| 128 » » » » return origC, fmt.Errorf("%s header: %s", header
Timeout, err) | 129 » » » » return origC, fmt.Errorf("%s header: %s", prpcco
mmon.HeaderTimeout, err) |
| 129 } | 130 } |
| 130 c, _ = clock.WithTimeout(c, timeout) | 131 c, _ = clock.WithTimeout(c, timeout) |
| 131 | 132 |
| 132 case headerAccept, headerContentType: | 133 case headerAccept, headerContentType: |
| 133 // readMessage and writeMessage handle these headers. | 134 // readMessage and writeMessage handle these headers. |
| 134 | 135 |
| 135 default: | 136 default: |
| 136 addedMeta = true | 137 addedMeta = true |
| 137 if !strings.HasSuffix(name, headerSuffixBinary) { | 138 if !strings.HasSuffix(name, headerSuffixBinary) { |
| 138 md[name] = append(md[name], values...) | 139 md[name] = append(md[name], values...) |
| 139 break // switch name | 140 break // switch name |
| 140 } | 141 } |
| 141 trimmedName := strings.TrimSuffix(name, headerSuffixBina
ry) | 142 trimmedName := strings.TrimSuffix(name, headerSuffixBina
ry) |
| 142 for _, v := range values { | 143 for _, v := range values { |
| 143 decoded, err := base64.StdEncoding.DecodeString(
v) | 144 decoded, err := base64.StdEncoding.DecodeString(
v) |
| 144 if err != nil { | 145 if err != nil { |
| 145 return origC, fmt.Errorf("%s header: %s"
, name, err) | 146 return origC, fmt.Errorf("%s header: %s"
, name, err) |
| 146 } | 147 } |
| 147 md[trimmedName] = append(md[trimmedName], string
(decoded)) | 148 md[trimmedName] = append(md[trimmedName], string
(decoded)) |
| 148 } | 149 } |
| 149 } | 150 } |
| 150 } | 151 } |
| 151 if addedMeta { | 152 if addedMeta { |
| 152 c = metadata.NewContext(c, md) | 153 c = metadata.NewContext(c, md) |
| 153 } | 154 } |
| 154 return c, nil | 155 return c, nil |
| 155 } | 156 } |
| OLD | NEW |