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