github.com/cloudwego/kitex@v0.9.0/pkg/remote/trans/nphttp2/client_conn.go (about) 1 /* 2 * Copyright 2021 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package nphttp2 18 19 import ( 20 "context" 21 "encoding/binary" 22 "io" 23 "net" 24 "net/url" 25 "strings" 26 "time" 27 28 "github.com/cloudwego/kitex/pkg/remote" 29 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/codes" 30 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/grpc" 31 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/metadata" 32 "github.com/cloudwego/kitex/pkg/rpcinfo" 33 "github.com/cloudwego/kitex/pkg/serviceinfo" 34 ) 35 36 const ( 37 contentSubTypeThrift = "thrift" 38 contentSubTypeProtobuf = "protobuf" 39 ) 40 41 type streamDesc struct { 42 isStreaming bool 43 } 44 45 type clientConn struct { 46 tr grpc.ClientTransport 47 s *grpc.Stream 48 desc *streamDesc 49 } 50 51 var _ GRPCConn = (*clientConn)(nil) 52 53 func (c *clientConn) ReadFrame() (hdr, data []byte, err error) { 54 hdr = make([]byte, 5) 55 _, err = c.Read(hdr) 56 if err != nil { 57 return nil, nil, err 58 } 59 dLen := int(binary.BigEndian.Uint32(hdr[1:])) 60 data = make([]byte, dLen) 61 _, err = c.Read(data) 62 if err != nil { 63 return nil, nil, err 64 } 65 return hdr, data, nil 66 } 67 68 func getContentSubType(codec serviceinfo.PayloadCodec) string { 69 switch codec { 70 case serviceinfo.Thrift: 71 return contentSubTypeThrift 72 case serviceinfo.Protobuf: 73 fallthrough 74 default: 75 return "" // default is protobuf, keep for backward compatibility 76 } 77 } 78 79 func newClientConn(ctx context.Context, tr grpc.ClientTransport, addr string) (*clientConn, error) { 80 ri := rpcinfo.GetRPCInfo(ctx) 81 host := ri.To().ServiceName() 82 if rawURL, ok := ri.To().Tag(rpcinfo.HTTPURL); ok { 83 u, err := url.Parse(rawURL) 84 if err != nil { 85 return nil, err 86 } 87 host = u.Host 88 } 89 isStreaming := ri.Config().InteractionMode() == rpcinfo.Streaming 90 invocation := ri.Invocation() 91 callHdr := &grpc.CallHdr{ 92 Host: host, 93 // grpc method format /package.Service/Method 94 Method: fullMethodName(invocation.PackageName(), invocation.ServiceName(), invocation.MethodName()), 95 SendCompress: remote.GetSendCompressor(ri), 96 ContentSubtype: getContentSubType(ri.Config().PayloadCodec()), 97 } 98 99 s, err := tr.NewStream(ctx, callHdr) 100 if err != nil { 101 return nil, err 102 } 103 return &clientConn{ 104 tr: tr, 105 s: s, 106 desc: &streamDesc{isStreaming: isStreaming}, 107 }, nil 108 } 109 110 // fullMethodName returns in the format of "/[$pkg.]$svc/$methodName". 111 func fullMethodName(pkg, svc, method string) string { 112 sb := strings.Builder{} 113 if pkg == "" { 114 sb.Grow(len(svc) + len(method) + 2) 115 } else { 116 sb.Grow(len(pkg) + len(svc) + len(method) + 3) 117 } 118 sb.WriteByte('/') 119 if pkg != "" { 120 sb.WriteString(pkg) 121 sb.WriteByte('.') 122 } 123 sb.WriteString(svc) 124 sb.WriteByte('/') 125 sb.WriteString(method) 126 return sb.String() 127 } 128 129 // impl net.Conn 130 func (c *clientConn) Read(b []byte) (n int, err error) { 131 n, err = c.s.Read(b) 132 if err == io.EOF { 133 if status := c.s.Status(); status.Code() != codes.OK { 134 if bizStatusErr := c.s.BizStatusErr(); bizStatusErr != nil { 135 err = bizStatusErr 136 } else { 137 err = status.Err() 138 } 139 } 140 } 141 return n, err 142 } 143 144 func (c *clientConn) Write(b []byte) (n int, err error) { 145 if len(b) < 5 { 146 return 0, io.ErrShortWrite 147 } 148 return c.WriteFrame(b[:5], b[5:]) 149 } 150 151 func (c *clientConn) WriteFrame(hdr, data []byte) (n int, err error) { 152 grpcConnOpt := &grpc.Options{Last: !c.desc.isStreaming} 153 err = c.tr.Write(c.s, hdr, data, grpcConnOpt) 154 return len(hdr) + len(data), err 155 } 156 157 func (c *clientConn) LocalAddr() net.Addr { return c.tr.LocalAddr() } 158 func (c *clientConn) RemoteAddr() net.Addr { return c.tr.RemoteAddr() } 159 func (c *clientConn) SetDeadline(t time.Time) error { return nil } 160 func (c *clientConn) SetReadDeadline(t time.Time) error { return nil } 161 func (c *clientConn) SetWriteDeadline(t time.Time) error { return nil } 162 func (c *clientConn) Close() error { 163 c.tr.Write(c.s, nil, nil, &grpc.Options{Last: true}) 164 // Always return nil; io.EOF is the only error that might make sense 165 // instead, but there is no need to signal the client to call Read 166 // as the only use left for the stream after Close is to call 167 // Read. This also matches historical behavior. 168 return nil 169 } 170 171 func (c *clientConn) Header() (metadata.MD, error) { return c.s.Header() } 172 func (c *clientConn) Trailer() metadata.MD { return c.s.Trailer() } 173 func (c *clientConn) GetRecvCompress() string { return c.s.RecvCompress() } 174 175 type hasGetRecvCompress interface { 176 GetRecvCompress() string 177 } 178 179 type hasHeader interface { 180 Header() (metadata.MD, error) 181 } 182 183 type hasTrailer interface { 184 Trailer() metadata.MD 185 }