blob: f2de84d43a810d7447af49e7b46014ad525f1b8e [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2 *
3 * Copyright 2016 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19// This file is the implementation of a gRPC server using HTTP/2 which
20// uses the standard Go http2 Server implementation (via the
21// http.Handler interface), rather than speaking low-level HTTP/2
22// frames itself. It is the implementation of *grpc.Server.ServeHTTP.
23
24package transport
25
26import (
27 "context"
28 "errors"
29 "fmt"
30 "io"
31 "net"
32 "net/http"
33 "strings"
34 "sync"
35 "time"
36
37 "github.com/golang/protobuf/proto"
38 "golang.org/x/net/http2"
39 "google.golang.org/grpc/codes"
40 "google.golang.org/grpc/credentials"
41 "google.golang.org/grpc/metadata"
42 "google.golang.org/grpc/peer"
43 "google.golang.org/grpc/stats"
44 "google.golang.org/grpc/status"
45)
46
47// NewServerHandlerTransport returns a ServerTransport handling gRPC
48// from inside an http.Handler. It requires that the http Server
49// supports HTTP/2.
50func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats stats.Handler) (ServerTransport, error) {
51 if r.ProtoMajor != 2 {
52 return nil, errors.New("gRPC requires HTTP/2")
53 }
54 if r.Method != "POST" {
55 return nil, errors.New("invalid gRPC request method")
56 }
57 contentType := r.Header.Get("Content-Type")
58 // TODO: do we assume contentType is lowercase? we did before
59 contentSubtype, validContentType := contentSubtype(contentType)
60 if !validContentType {
61 return nil, errors.New("invalid gRPC request content-type")
62 }
63 if _, ok := w.(http.Flusher); !ok {
64 return nil, errors.New("gRPC requires a ResponseWriter supporting http.Flusher")
65 }
66
67 st := &serverHandlerTransport{
68 rw: w,
69 req: r,
70 closedCh: make(chan struct{}),
71 writes: make(chan func()),
72 contentType: contentType,
73 contentSubtype: contentSubtype,
74 stats: stats,
75 }
76
77 if v := r.Header.Get("grpc-timeout"); v != "" {
78 to, err := decodeTimeout(v)
79 if err != nil {
80 return nil, status.Errorf(codes.Internal, "malformed time-out: %v", err)
81 }
82 st.timeoutSet = true
83 st.timeout = to
84 }
85
86 metakv := []string{"content-type", contentType}
87 if r.Host != "" {
88 metakv = append(metakv, ":authority", r.Host)
89 }
90 for k, vv := range r.Header {
91 k = strings.ToLower(k)
92 if isReservedHeader(k) && !isWhitelistedHeader(k) {
93 continue
94 }
95 for _, v := range vv {
96 v, err := decodeMetadataHeader(k, v)
97 if err != nil {
98 return nil, status.Errorf(codes.Internal, "malformed binary metadata: %v", err)
99 }
100 metakv = append(metakv, k, v)
101 }
102 }
103 st.headerMD = metadata.Pairs(metakv...)
104
105 return st, nil
106}
107
108// serverHandlerTransport is an implementation of ServerTransport
109// which replies to exactly one gRPC request (exactly one HTTP request),
110// using the net/http.Handler interface. This http.Handler is guaranteed
111// at this point to be speaking over HTTP/2, so it's able to speak valid
112// gRPC.
113type serverHandlerTransport struct {
114 rw http.ResponseWriter
115 req *http.Request
116 timeoutSet bool
117 timeout time.Duration
118 didCommonHeaders bool
119
120 headerMD metadata.MD
121
122 closeOnce sync.Once
123 closedCh chan struct{} // closed on Close
124
125 // writes is a channel of code to run serialized in the
126 // ServeHTTP (HandleStreams) goroutine. The channel is closed
127 // when WriteStatus is called.
128 writes chan func()
129
130 // block concurrent WriteStatus calls
131 // e.g. grpc/(*serverStream).SendMsg/RecvMsg
132 writeStatusMu sync.Mutex
133
134 // we just mirror the request content-type
135 contentType string
136 // we store both contentType and contentSubtype so we don't keep recreating them
137 // TODO make sure this is consistent across handler_server and http2_server
138 contentSubtype string
139
140 stats stats.Handler
141}
142
143func (ht *serverHandlerTransport) Close() error {
144 ht.closeOnce.Do(ht.closeCloseChanOnce)
145 return nil
146}
147
148func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
149
150func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) }
151
152// strAddr is a net.Addr backed by either a TCP "ip:port" string, or
153// the empty string if unknown.
154type strAddr string
155
156func (a strAddr) Network() string {
157 if a != "" {
158 // Per the documentation on net/http.Request.RemoteAddr, if this is
159 // set, it's set to the IP:port of the peer (hence, TCP):
160 // https://golang.org/pkg/net/http/#Request
161 //
162 // If we want to support Unix sockets later, we can
163 // add our own grpc-specific convention within the
164 // grpc codebase to set RemoteAddr to a different
165 // format, or probably better: we can attach it to the
166 // context and use that from serverHandlerTransport.RemoteAddr.
167 return "tcp"
168 }
169 return ""
170}
171
172func (a strAddr) String() string { return string(a) }
173
174// do runs fn in the ServeHTTP goroutine.
175func (ht *serverHandlerTransport) do(fn func()) error {
176 select {
177 case <-ht.closedCh:
178 return ErrConnClosing
179 case ht.writes <- fn:
180 return nil
181 }
182}
183
184func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) error {
185 ht.writeStatusMu.Lock()
186 defer ht.writeStatusMu.Unlock()
187
188 err := ht.do(func() {
189 ht.writeCommonHeaders(s)
190
191 // And flush, in case no header or body has been sent yet.
192 // This forces a separation of headers and trailers if this is the
193 // first call (for example, in end2end tests's TestNoService).
194 ht.rw.(http.Flusher).Flush()
195
196 h := ht.rw.Header()
197 h.Set("Grpc-Status", fmt.Sprintf("%d", st.Code()))
198 if m := st.Message(); m != "" {
199 h.Set("Grpc-Message", encodeGrpcMessage(m))
200 }
201
202 if p := st.Proto(); p != nil && len(p.Details) > 0 {
203 stBytes, err := proto.Marshal(p)
204 if err != nil {
205 // TODO: return error instead, when callers are able to handle it.
206 panic(err)
207 }
208
209 h.Set("Grpc-Status-Details-Bin", encodeBinHeader(stBytes))
210 }
211
212 if md := s.Trailer(); len(md) > 0 {
213 for k, vv := range md {
214 // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
215 if isReservedHeader(k) {
216 continue
217 }
218 for _, v := range vv {
219 // http2 ResponseWriter mechanism to send undeclared Trailers after
220 // the headers have possibly been written.
221 h.Add(http2.TrailerPrefix+k, encodeMetadataHeader(k, v))
222 }
223 }
224 }
225 })
226
227 if err == nil { // transport has not been closed
228 if ht.stats != nil {
229 ht.stats.HandleRPC(s.Context(), &stats.OutTrailer{})
230 }
231 }
232 ht.Close()
233 return err
234}
235
236// writeCommonHeaders sets common headers on the first write
237// call (Write, WriteHeader, or WriteStatus).
238func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
239 if ht.didCommonHeaders {
240 return
241 }
242 ht.didCommonHeaders = true
243
244 h := ht.rw.Header()
245 h["Date"] = nil // suppress Date to make tests happy; TODO: restore
246 h.Set("Content-Type", ht.contentType)
247
248 // Predeclare trailers we'll set later in WriteStatus (after the body).
249 // This is a SHOULD in the HTTP RFC, and the way you add (known)
250 // Trailers per the net/http.ResponseWriter contract.
251 // See https://golang.org/pkg/net/http/#ResponseWriter
252 // and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
253 h.Add("Trailer", "Grpc-Status")
254 h.Add("Trailer", "Grpc-Message")
255 h.Add("Trailer", "Grpc-Status-Details-Bin")
256
257 if s.sendCompress != "" {
258 h.Set("Grpc-Encoding", s.sendCompress)
259 }
260}
261
262func (ht *serverHandlerTransport) Write(s *Stream, hdr []byte, data []byte, opts *Options) error {
263 return ht.do(func() {
264 ht.writeCommonHeaders(s)
265 ht.rw.Write(hdr)
266 ht.rw.Write(data)
267 ht.rw.(http.Flusher).Flush()
268 })
269}
270
271func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
272 err := ht.do(func() {
273 ht.writeCommonHeaders(s)
274 h := ht.rw.Header()
275 for k, vv := range md {
276 // Clients don't tolerate reading restricted headers after some non restricted ones were sent.
277 if isReservedHeader(k) {
278 continue
279 }
280 for _, v := range vv {
281 v = encodeMetadataHeader(k, v)
282 h.Add(k, v)
283 }
284 }
285 ht.rw.WriteHeader(200)
286 ht.rw.(http.Flusher).Flush()
287 })
288
289 if err == nil {
290 if ht.stats != nil {
291 ht.stats.HandleRPC(s.Context(), &stats.OutHeader{})
292 }
293 }
294 return err
295}
296
297func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) {
298 // With this transport type there will be exactly 1 stream: this HTTP request.
299
300 ctx := ht.req.Context()
301 var cancel context.CancelFunc
302 if ht.timeoutSet {
303 ctx, cancel = context.WithTimeout(ctx, ht.timeout)
304 } else {
305 ctx, cancel = context.WithCancel(ctx)
306 }
307
308 // requestOver is closed when the status has been written via WriteStatus.
309 requestOver := make(chan struct{})
310 go func() {
311 select {
312 case <-requestOver:
313 case <-ht.closedCh:
314 case <-ht.req.Context().Done():
315 }
316 cancel()
317 ht.Close()
318 }()
319
320 req := ht.req
321
322 s := &Stream{
323 id: 0, // irrelevant
324 requestRead: func(int) {},
325 cancel: cancel,
326 buf: newRecvBuffer(),
327 st: ht,
328 method: req.URL.Path,
329 recvCompress: req.Header.Get("grpc-encoding"),
330 contentSubtype: ht.contentSubtype,
331 }
332 pr := &peer.Peer{
333 Addr: ht.RemoteAddr(),
334 }
335 if req.TLS != nil {
336 pr.AuthInfo = credentials.TLSInfo{State: *req.TLS}
337 }
338 ctx = metadata.NewIncomingContext(ctx, ht.headerMD)
339 s.ctx = peer.NewContext(ctx, pr)
340 if ht.stats != nil {
341 s.ctx = ht.stats.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method})
342 inHeader := &stats.InHeader{
343 FullMethod: s.method,
344 RemoteAddr: ht.RemoteAddr(),
345 Compression: s.recvCompress,
346 }
347 ht.stats.HandleRPC(s.ctx, inHeader)
348 }
349 s.trReader = &transportReader{
350 reader: &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf},
351 windowHandler: func(int) {},
352 }
353
354 // readerDone is closed when the Body.Read-ing goroutine exits.
355 readerDone := make(chan struct{})
356 go func() {
357 defer close(readerDone)
358
359 // TODO: minimize garbage, optimize recvBuffer code/ownership
360 const readSize = 8196
361 for buf := make([]byte, readSize); ; {
362 n, err := req.Body.Read(buf)
363 if n > 0 {
364 s.buf.put(recvMsg{data: buf[:n:n]})
365 buf = buf[n:]
366 }
367 if err != nil {
368 s.buf.put(recvMsg{err: mapRecvMsgError(err)})
369 return
370 }
371 if len(buf) == 0 {
372 buf = make([]byte, readSize)
373 }
374 }
375 }()
376
377 // startStream is provided by the *grpc.Server's serveStreams.
378 // It starts a goroutine serving s and exits immediately.
379 // The goroutine that is started is the one that then calls
380 // into ht, calling WriteHeader, Write, WriteStatus, Close, etc.
381 startStream(s)
382
383 ht.runStream()
384 close(requestOver)
385
386 // Wait for reading goroutine to finish.
387 req.Body.Close()
388 <-readerDone
389}
390
391func (ht *serverHandlerTransport) runStream() {
392 for {
393 select {
394 case fn := <-ht.writes:
395 fn()
396 case <-ht.closedCh:
397 return
398 }
399 }
400}
401
402func (ht *serverHandlerTransport) IncrMsgSent() {}
403
404func (ht *serverHandlerTransport) IncrMsgRecv() {}
405
406func (ht *serverHandlerTransport) Drain() {
407 panic("Drain() is not implemented")
408}
409
410// mapRecvMsgError returns the non-nil err into the appropriate
411// error value as expected by callers of *grpc.parser.recvMsg.
412// In particular, in can only be:
413// * io.EOF
414// * io.ErrUnexpectedEOF
415// * of type transport.ConnectionError
416// * an error from the status package
417func mapRecvMsgError(err error) error {
418 if err == io.EOF || err == io.ErrUnexpectedEOF {
419 return err
420 }
421 if se, ok := err.(http2.StreamError); ok {
422 if code, ok := http2ErrConvTab[se.Code]; ok {
423 return status.Error(code, se.Error())
424 }
425 }
426 if strings.Contains(err.Error(), "body closed by handler") {
427 return status.Error(codes.Canceled, err.Error())
428 }
429 return connectionErrorf(true, err, err.Error())
430}