Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

685 строки
19 KiB

  1. /*
  2. *
  3. * Copyright 2014 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. package transport
  19. import (
  20. "bufio"
  21. "bytes"
  22. "encoding/base64"
  23. "fmt"
  24. "io"
  25. "math"
  26. "net"
  27. "net/http"
  28. "strconv"
  29. "strings"
  30. "time"
  31. "unicode/utf8"
  32. "github.com/golang/protobuf/proto"
  33. "golang.org/x/net/http2"
  34. "golang.org/x/net/http2/hpack"
  35. spb "google.golang.org/genproto/googleapis/rpc/status"
  36. "google.golang.org/grpc/codes"
  37. "google.golang.org/grpc/status"
  38. )
  39. const (
  40. // http2MaxFrameLen specifies the max length of a HTTP2 frame.
  41. http2MaxFrameLen = 16384 // 16KB frame
  42. // http://http2.github.io/http2-spec/#SettingValues
  43. http2InitHeaderTableSize = 4096
  44. // baseContentType is the base content-type for gRPC. This is a valid
  45. // content-type on it's own, but can also include a content-subtype such as
  46. // "proto" as a suffix after "+" or ";". See
  47. // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
  48. // for more details.
  49. baseContentType = "application/grpc"
  50. )
  51. var (
  52. clientPreface = []byte(http2.ClientPreface)
  53. http2ErrConvTab = map[http2.ErrCode]codes.Code{
  54. http2.ErrCodeNo: codes.Internal,
  55. http2.ErrCodeProtocol: codes.Internal,
  56. http2.ErrCodeInternal: codes.Internal,
  57. http2.ErrCodeFlowControl: codes.ResourceExhausted,
  58. http2.ErrCodeSettingsTimeout: codes.Internal,
  59. http2.ErrCodeStreamClosed: codes.Internal,
  60. http2.ErrCodeFrameSize: codes.Internal,
  61. http2.ErrCodeRefusedStream: codes.Unavailable,
  62. http2.ErrCodeCancel: codes.Canceled,
  63. http2.ErrCodeCompression: codes.Internal,
  64. http2.ErrCodeConnect: codes.Internal,
  65. http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
  66. http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
  67. http2.ErrCodeHTTP11Required: codes.Internal,
  68. }
  69. statusCodeConvTab = map[codes.Code]http2.ErrCode{
  70. codes.Internal: http2.ErrCodeInternal,
  71. codes.Canceled: http2.ErrCodeCancel,
  72. codes.Unavailable: http2.ErrCodeRefusedStream,
  73. codes.ResourceExhausted: http2.ErrCodeEnhanceYourCalm,
  74. codes.PermissionDenied: http2.ErrCodeInadequateSecurity,
  75. }
  76. // HTTPStatusConvTab is the HTTP status code to gRPC error code conversion table.
  77. HTTPStatusConvTab = map[int]codes.Code{
  78. // 400 Bad Request - INTERNAL.
  79. http.StatusBadRequest: codes.Internal,
  80. // 401 Unauthorized - UNAUTHENTICATED.
  81. http.StatusUnauthorized: codes.Unauthenticated,
  82. // 403 Forbidden - PERMISSION_DENIED.
  83. http.StatusForbidden: codes.PermissionDenied,
  84. // 404 Not Found - UNIMPLEMENTED.
  85. http.StatusNotFound: codes.Unimplemented,
  86. // 429 Too Many Requests - UNAVAILABLE.
  87. http.StatusTooManyRequests: codes.Unavailable,
  88. // 502 Bad Gateway - UNAVAILABLE.
  89. http.StatusBadGateway: codes.Unavailable,
  90. // 503 Service Unavailable - UNAVAILABLE.
  91. http.StatusServiceUnavailable: codes.Unavailable,
  92. // 504 Gateway timeout - UNAVAILABLE.
  93. http.StatusGatewayTimeout: codes.Unavailable,
  94. }
  95. )
  96. type parsedHeaderData struct {
  97. encoding string
  98. // statusGen caches the stream status received from the trailer the server
  99. // sent. Client side only. Do not access directly. After all trailers are
  100. // parsed, use the status method to retrieve the status.
  101. statusGen *status.Status
  102. // rawStatusCode and rawStatusMsg are set from the raw trailer fields and are not
  103. // intended for direct access outside of parsing.
  104. rawStatusCode *int
  105. rawStatusMsg string
  106. httpStatus *int
  107. // Server side only fields.
  108. timeoutSet bool
  109. timeout time.Duration
  110. method string
  111. // key-value metadata map from the peer.
  112. mdata map[string][]string
  113. statsTags []byte
  114. statsTrace []byte
  115. contentSubtype string
  116. // isGRPC field indicates whether the peer is speaking gRPC (otherwise HTTP).
  117. //
  118. // We are in gRPC mode (peer speaking gRPC) if:
  119. // * We are client side and have already received a HEADER frame that indicates gRPC peer.
  120. // * The header contains valid a content-type, i.e. a string starts with "application/grpc"
  121. // And we should handle error specific to gRPC.
  122. //
  123. // Otherwise (i.e. a content-type string starts without "application/grpc", or does not exist), we
  124. // are in HTTP fallback mode, and should handle error specific to HTTP.
  125. isGRPC bool
  126. grpcErr error
  127. httpErr error
  128. contentTypeErr string
  129. }
  130. // decodeState configures decoding criteria and records the decoded data.
  131. type decodeState struct {
  132. // whether decoding on server side or not
  133. serverSide bool
  134. // ignoreContentType indicates whether when processing the HEADERS frame, ignoring checking the
  135. // content-type is grpc or not.
  136. //
  137. // Trailers (after headers) should not have a content-type. And thus we will ignore checking the
  138. // content-type.
  139. //
  140. // For server, this field is always false.
  141. ignoreContentType bool
  142. // Records the states during HPACK decoding. It will be filled with info parsed from HTTP HEADERS
  143. // frame once decodeHeader function has been invoked and returned.
  144. data parsedHeaderData
  145. }
  146. // isReservedHeader checks whether hdr belongs to HTTP2 headers
  147. // reserved by gRPC protocol. Any other headers are classified as the
  148. // user-specified metadata.
  149. func isReservedHeader(hdr string) bool {
  150. if hdr != "" && hdr[0] == ':' {
  151. return true
  152. }
  153. switch hdr {
  154. case "content-type",
  155. "user-agent",
  156. "grpc-message-type",
  157. "grpc-encoding",
  158. "grpc-message",
  159. "grpc-status",
  160. "grpc-timeout",
  161. "grpc-status-details-bin",
  162. // Intentionally exclude grpc-previous-rpc-attempts and
  163. // grpc-retry-pushback-ms, which are "reserved", but their API
  164. // intentionally works via metadata.
  165. "te":
  166. return true
  167. default:
  168. return false
  169. }
  170. }
  171. // isWhitelistedHeader checks whether hdr should be propagated into metadata
  172. // visible to users, even though it is classified as "reserved", above.
  173. func isWhitelistedHeader(hdr string) bool {
  174. switch hdr {
  175. case ":authority", "user-agent":
  176. return true
  177. default:
  178. return false
  179. }
  180. }
  181. // contentSubtype returns the content-subtype for the given content-type. The
  182. // given content-type must be a valid content-type that starts with
  183. // "application/grpc". A content-subtype will follow "application/grpc" after a
  184. // "+" or ";". See
  185. // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
  186. // more details.
  187. //
  188. // If contentType is not a valid content-type for gRPC, the boolean
  189. // will be false, otherwise true. If content-type == "application/grpc",
  190. // "application/grpc+", or "application/grpc;", the boolean will be true,
  191. // but no content-subtype will be returned.
  192. //
  193. // contentType is assumed to be lowercase already.
  194. func contentSubtype(contentType string) (string, bool) {
  195. if contentType == baseContentType {
  196. return "", true
  197. }
  198. if !strings.HasPrefix(contentType, baseContentType) {
  199. return "", false
  200. }
  201. // guaranteed since != baseContentType and has baseContentType prefix
  202. switch contentType[len(baseContentType)] {
  203. case '+', ';':
  204. // this will return true for "application/grpc+" or "application/grpc;"
  205. // which the previous validContentType function tested to be valid, so we
  206. // just say that no content-subtype is specified in this case
  207. return contentType[len(baseContentType)+1:], true
  208. default:
  209. return "", false
  210. }
  211. }
  212. // contentSubtype is assumed to be lowercase
  213. func contentType(contentSubtype string) string {
  214. if contentSubtype == "" {
  215. return baseContentType
  216. }
  217. return baseContentType + "+" + contentSubtype
  218. }
  219. func (d *decodeState) status() *status.Status {
  220. if d.data.statusGen == nil {
  221. // No status-details were provided; generate status using code/msg.
  222. d.data.statusGen = status.New(codes.Code(int32(*(d.data.rawStatusCode))), d.data.rawStatusMsg)
  223. }
  224. return d.data.statusGen
  225. }
  226. const binHdrSuffix = "-bin"
  227. func encodeBinHeader(v []byte) string {
  228. return base64.RawStdEncoding.EncodeToString(v)
  229. }
  230. func decodeBinHeader(v string) ([]byte, error) {
  231. if len(v)%4 == 0 {
  232. // Input was padded, or padding was not necessary.
  233. return base64.StdEncoding.DecodeString(v)
  234. }
  235. return base64.RawStdEncoding.DecodeString(v)
  236. }
  237. func encodeMetadataHeader(k, v string) string {
  238. if strings.HasSuffix(k, binHdrSuffix) {
  239. return encodeBinHeader(([]byte)(v))
  240. }
  241. return v
  242. }
  243. func decodeMetadataHeader(k, v string) (string, error) {
  244. if strings.HasSuffix(k, binHdrSuffix) {
  245. b, err := decodeBinHeader(v)
  246. return string(b), err
  247. }
  248. return v, nil
  249. }
  250. func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
  251. // frame.Truncated is set to true when framer detects that the current header
  252. // list size hits MaxHeaderListSize limit.
  253. if frame.Truncated {
  254. return status.Error(codes.Internal, "peer header list size exceeded limit")
  255. }
  256. for _, hf := range frame.Fields {
  257. d.processHeaderField(hf)
  258. }
  259. if d.data.isGRPC {
  260. if d.data.grpcErr != nil {
  261. return d.data.grpcErr
  262. }
  263. if d.serverSide {
  264. return nil
  265. }
  266. if d.data.rawStatusCode == nil && d.data.statusGen == nil {
  267. // gRPC status doesn't exist.
  268. // Set rawStatusCode to be unknown and return nil error.
  269. // So that, if the stream has ended this Unknown status
  270. // will be propagated to the user.
  271. // Otherwise, it will be ignored. In which case, status from
  272. // a later trailer, that has StreamEnded flag set, is propagated.
  273. code := int(codes.Unknown)
  274. d.data.rawStatusCode = &code
  275. }
  276. return nil
  277. }
  278. // HTTP fallback mode
  279. if d.data.httpErr != nil {
  280. return d.data.httpErr
  281. }
  282. var (
  283. code = codes.Internal // when header does not include HTTP status, return INTERNAL
  284. ok bool
  285. )
  286. if d.data.httpStatus != nil {
  287. code, ok = HTTPStatusConvTab[*(d.data.httpStatus)]
  288. if !ok {
  289. code = codes.Unknown
  290. }
  291. }
  292. return status.Error(code, d.constructHTTPErrMsg())
  293. }
  294. // constructErrMsg constructs error message to be returned in HTTP fallback mode.
  295. // Format: HTTP status code and its corresponding message + content-type error message.
  296. func (d *decodeState) constructHTTPErrMsg() string {
  297. var errMsgs []string
  298. if d.data.httpStatus == nil {
  299. errMsgs = append(errMsgs, "malformed header: missing HTTP status")
  300. } else {
  301. errMsgs = append(errMsgs, fmt.Sprintf("%s: HTTP status code %d", http.StatusText(*(d.data.httpStatus)), *d.data.httpStatus))
  302. }
  303. if d.data.contentTypeErr == "" {
  304. errMsgs = append(errMsgs, "transport: missing content-type field")
  305. } else {
  306. errMsgs = append(errMsgs, d.data.contentTypeErr)
  307. }
  308. return strings.Join(errMsgs, "; ")
  309. }
  310. func (d *decodeState) addMetadata(k, v string) {
  311. if d.data.mdata == nil {
  312. d.data.mdata = make(map[string][]string)
  313. }
  314. d.data.mdata[k] = append(d.data.mdata[k], v)
  315. }
  316. func (d *decodeState) processHeaderField(f hpack.HeaderField) {
  317. switch f.Name {
  318. case "content-type":
  319. contentSubtype, validContentType := contentSubtype(f.Value)
  320. if !validContentType {
  321. d.data.contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", f.Value)
  322. return
  323. }
  324. d.data.contentSubtype = contentSubtype
  325. // TODO: do we want to propagate the whole content-type in the metadata,
  326. // or come up with a way to just propagate the content-subtype if it was set?
  327. // ie {"content-type": "application/grpc+proto"} or {"content-subtype": "proto"}
  328. // in the metadata?
  329. d.addMetadata(f.Name, f.Value)
  330. d.data.isGRPC = true
  331. case "grpc-encoding":
  332. d.data.encoding = f.Value
  333. case "grpc-status":
  334. code, err := strconv.Atoi(f.Value)
  335. if err != nil {
  336. d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status: %v", err)
  337. return
  338. }
  339. d.data.rawStatusCode = &code
  340. case "grpc-message":
  341. d.data.rawStatusMsg = decodeGrpcMessage(f.Value)
  342. case "grpc-status-details-bin":
  343. v, err := decodeBinHeader(f.Value)
  344. if err != nil {
  345. d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
  346. return
  347. }
  348. s := &spb.Status{}
  349. if err := proto.Unmarshal(v, s); err != nil {
  350. d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
  351. return
  352. }
  353. d.data.statusGen = status.FromProto(s)
  354. case "grpc-timeout":
  355. d.data.timeoutSet = true
  356. var err error
  357. if d.data.timeout, err = decodeTimeout(f.Value); err != nil {
  358. d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed time-out: %v", err)
  359. }
  360. case ":path":
  361. d.data.method = f.Value
  362. case ":status":
  363. code, err := strconv.Atoi(f.Value)
  364. if err != nil {
  365. d.data.httpErr = status.Errorf(codes.Internal, "transport: malformed http-status: %v", err)
  366. return
  367. }
  368. d.data.httpStatus = &code
  369. case "grpc-tags-bin":
  370. v, err := decodeBinHeader(f.Value)
  371. if err != nil {
  372. d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-tags-bin: %v", err)
  373. return
  374. }
  375. d.data.statsTags = v
  376. d.addMetadata(f.Name, string(v))
  377. case "grpc-trace-bin":
  378. v, err := decodeBinHeader(f.Value)
  379. if err != nil {
  380. d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-trace-bin: %v", err)
  381. return
  382. }
  383. d.data.statsTrace = v
  384. d.addMetadata(f.Name, string(v))
  385. default:
  386. if isReservedHeader(f.Name) && !isWhitelistedHeader(f.Name) {
  387. break
  388. }
  389. v, err := decodeMetadataHeader(f.Name, f.Value)
  390. if err != nil {
  391. errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
  392. return
  393. }
  394. d.addMetadata(f.Name, v)
  395. }
  396. }
  397. type timeoutUnit uint8
  398. const (
  399. hour timeoutUnit = 'H'
  400. minute timeoutUnit = 'M'
  401. second timeoutUnit = 'S'
  402. millisecond timeoutUnit = 'm'
  403. microsecond timeoutUnit = 'u'
  404. nanosecond timeoutUnit = 'n'
  405. )
  406. func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) {
  407. switch u {
  408. case hour:
  409. return time.Hour, true
  410. case minute:
  411. return time.Minute, true
  412. case second:
  413. return time.Second, true
  414. case millisecond:
  415. return time.Millisecond, true
  416. case microsecond:
  417. return time.Microsecond, true
  418. case nanosecond:
  419. return time.Nanosecond, true
  420. default:
  421. }
  422. return
  423. }
  424. const maxTimeoutValue int64 = 100000000 - 1
  425. // div does integer division and round-up the result. Note that this is
  426. // equivalent to (d+r-1)/r but has less chance to overflow.
  427. func div(d, r time.Duration) int64 {
  428. if m := d % r; m > 0 {
  429. return int64(d/r + 1)
  430. }
  431. return int64(d / r)
  432. }
  433. // TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it.
  434. func encodeTimeout(t time.Duration) string {
  435. if t <= 0 {
  436. return "0n"
  437. }
  438. if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
  439. return strconv.FormatInt(d, 10) + "n"
  440. }
  441. if d := div(t, time.Microsecond); d <= maxTimeoutValue {
  442. return strconv.FormatInt(d, 10) + "u"
  443. }
  444. if d := div(t, time.Millisecond); d <= maxTimeoutValue {
  445. return strconv.FormatInt(d, 10) + "m"
  446. }
  447. if d := div(t, time.Second); d <= maxTimeoutValue {
  448. return strconv.FormatInt(d, 10) + "S"
  449. }
  450. if d := div(t, time.Minute); d <= maxTimeoutValue {
  451. return strconv.FormatInt(d, 10) + "M"
  452. }
  453. // Note that maxTimeoutValue * time.Hour > MaxInt64.
  454. return strconv.FormatInt(div(t, time.Hour), 10) + "H"
  455. }
  456. func decodeTimeout(s string) (time.Duration, error) {
  457. size := len(s)
  458. if size < 2 {
  459. return 0, fmt.Errorf("transport: timeout string is too short: %q", s)
  460. }
  461. if size > 9 {
  462. // Spec allows for 8 digits plus the unit.
  463. return 0, fmt.Errorf("transport: timeout string is too long: %q", s)
  464. }
  465. unit := timeoutUnit(s[size-1])
  466. d, ok := timeoutUnitToDuration(unit)
  467. if !ok {
  468. return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s)
  469. }
  470. t, err := strconv.ParseInt(s[:size-1], 10, 64)
  471. if err != nil {
  472. return 0, err
  473. }
  474. const maxHours = math.MaxInt64 / int64(time.Hour)
  475. if d == time.Hour && t > maxHours {
  476. // This timeout would overflow math.MaxInt64; clamp it.
  477. return time.Duration(math.MaxInt64), nil
  478. }
  479. return d * time.Duration(t), nil
  480. }
  481. const (
  482. spaceByte = ' '
  483. tildeByte = '~'
  484. percentByte = '%'
  485. )
  486. // encodeGrpcMessage is used to encode status code in header field
  487. // "grpc-message". It does percent encoding and also replaces invalid utf-8
  488. // characters with Unicode replacement character.
  489. //
  490. // It checks to see if each individual byte in msg is an allowable byte, and
  491. // then either percent encoding or passing it through. When percent encoding,
  492. // the byte is converted into hexadecimal notation with a '%' prepended.
  493. func encodeGrpcMessage(msg string) string {
  494. if msg == "" {
  495. return ""
  496. }
  497. lenMsg := len(msg)
  498. for i := 0; i < lenMsg; i++ {
  499. c := msg[i]
  500. if !(c >= spaceByte && c <= tildeByte && c != percentByte) {
  501. return encodeGrpcMessageUnchecked(msg)
  502. }
  503. }
  504. return msg
  505. }
  506. func encodeGrpcMessageUnchecked(msg string) string {
  507. var buf bytes.Buffer
  508. for len(msg) > 0 {
  509. r, size := utf8.DecodeRuneInString(msg)
  510. for _, b := range []byte(string(r)) {
  511. if size > 1 {
  512. // If size > 1, r is not ascii. Always do percent encoding.
  513. buf.WriteString(fmt.Sprintf("%%%02X", b))
  514. continue
  515. }
  516. // The for loop is necessary even if size == 1. r could be
  517. // utf8.RuneError.
  518. //
  519. // fmt.Sprintf("%%%02X", utf8.RuneError) gives "%FFFD".
  520. if b >= spaceByte && b <= tildeByte && b != percentByte {
  521. buf.WriteByte(b)
  522. } else {
  523. buf.WriteString(fmt.Sprintf("%%%02X", b))
  524. }
  525. }
  526. msg = msg[size:]
  527. }
  528. return buf.String()
  529. }
  530. // decodeGrpcMessage decodes the msg encoded by encodeGrpcMessage.
  531. func decodeGrpcMessage(msg string) string {
  532. if msg == "" {
  533. return ""
  534. }
  535. lenMsg := len(msg)
  536. for i := 0; i < lenMsg; i++ {
  537. if msg[i] == percentByte && i+2 < lenMsg {
  538. return decodeGrpcMessageUnchecked(msg)
  539. }
  540. }
  541. return msg
  542. }
  543. func decodeGrpcMessageUnchecked(msg string) string {
  544. var buf bytes.Buffer
  545. lenMsg := len(msg)
  546. for i := 0; i < lenMsg; i++ {
  547. c := msg[i]
  548. if c == percentByte && i+2 < lenMsg {
  549. parsed, err := strconv.ParseUint(msg[i+1:i+3], 16, 8)
  550. if err != nil {
  551. buf.WriteByte(c)
  552. } else {
  553. buf.WriteByte(byte(parsed))
  554. i += 2
  555. }
  556. } else {
  557. buf.WriteByte(c)
  558. }
  559. }
  560. return buf.String()
  561. }
  562. type bufWriter struct {
  563. buf []byte
  564. offset int
  565. batchSize int
  566. conn net.Conn
  567. err error
  568. onFlush func()
  569. }
  570. func newBufWriter(conn net.Conn, batchSize int) *bufWriter {
  571. return &bufWriter{
  572. buf: make([]byte, batchSize*2),
  573. batchSize: batchSize,
  574. conn: conn,
  575. }
  576. }
  577. func (w *bufWriter) Write(b []byte) (n int, err error) {
  578. if w.err != nil {
  579. return 0, w.err
  580. }
  581. if w.batchSize == 0 { // Buffer has been disabled.
  582. return w.conn.Write(b)
  583. }
  584. for len(b) > 0 {
  585. nn := copy(w.buf[w.offset:], b)
  586. b = b[nn:]
  587. w.offset += nn
  588. n += nn
  589. if w.offset >= w.batchSize {
  590. err = w.Flush()
  591. }
  592. }
  593. return n, err
  594. }
  595. func (w *bufWriter) Flush() error {
  596. if w.err != nil {
  597. return w.err
  598. }
  599. if w.offset == 0 {
  600. return nil
  601. }
  602. if w.onFlush != nil {
  603. w.onFlush()
  604. }
  605. _, w.err = w.conn.Write(w.buf[:w.offset])
  606. w.offset = 0
  607. return w.err
  608. }
  609. type framer struct {
  610. writer *bufWriter
  611. fr *http2.Framer
  612. }
  613. func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, maxHeaderListSize uint32) *framer {
  614. if writeBufferSize < 0 {
  615. writeBufferSize = 0
  616. }
  617. var r io.Reader = conn
  618. if readBufferSize > 0 {
  619. r = bufio.NewReaderSize(r, readBufferSize)
  620. }
  621. w := newBufWriter(conn, writeBufferSize)
  622. f := &framer{
  623. writer: w,
  624. fr: http2.NewFramer(w, r),
  625. }
  626. // Opt-in to Frame reuse API on framer to reduce garbage.
  627. // Frames aren't safe to read from after a subsequent call to ReadFrame.
  628. f.fr.SetReuseFrames()
  629. f.fr.MaxHeaderListSize = maxHeaderListSize
  630. f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
  631. return f
  632. }