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