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 |