Index: third_party/grpc/tools/http2_interop/frameheader.go |
diff --git a/third_party/grpc/tools/http2_interop/frameheader.go b/third_party/grpc/tools/http2_interop/frameheader.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..84f6fa5c55822277ff51d100679ab0a07042f5a6 |
--- /dev/null |
+++ b/third_party/grpc/tools/http2_interop/frameheader.go |
@@ -0,0 +1,120 @@ |
+package http2interop |
+ |
+import ( |
+ "encoding/binary" |
+ "fmt" |
+ "io" |
+) |
+ |
+type FrameHeader struct { |
+ Length int |
+ Type FrameType |
+ Flags byte |
+ Reserved Reserved |
+ StreamID |
+} |
+ |
+type Reserved bool |
+ |
+func (r Reserved) String() string { |
+ if r { |
+ return "R" |
+ } |
+ return "" |
+} |
+ |
+func (fh *FrameHeader) Parse(r io.Reader) error { |
+ buf := make([]byte, 9) |
+ if _, err := io.ReadFull(r, buf); err != nil { |
+ return err |
+ } |
+ return fh.UnmarshalBinary(buf) |
+} |
+ |
+func (fh *FrameHeader) UnmarshalBinary(b []byte) error { |
+ if len(b) != 9 { |
+ return fmt.Errorf("Invalid frame header length %d", len(b)) |
+ } |
+ *fh = FrameHeader{ |
+ Length: int(b[0])<<16 | int(b[1])<<8 | int(b[2]), |
+ Type: FrameType(b[3]), |
+ Flags: b[4], |
+ Reserved: Reserved(b[5]>>7 == 1), |
+ StreamID: StreamID(binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff), |
+ } |
+ return nil |
+} |
+ |
+func (fh *FrameHeader) MarshalBinary() ([]byte, error) { |
+ buf := make([]byte, 9, 9+fh.Length) |
+ |
+ if fh.Length > 0xFFFFFF || fh.Length < 0 { |
+ return nil, fmt.Errorf("Invalid frame header length: %d", fh.Length) |
+ } |
+ if fh.StreamID < 0 { |
+ return nil, fmt.Errorf("Invalid Stream ID: %v", fh.StreamID) |
+ } |
+ |
+ buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length) |
+ buf[3] = byte(fh.Type) |
+ buf[4] = fh.Flags |
+ var res uint32 |
+ if fh.Reserved { |
+ res = 0x80000000 |
+ } |
+ binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)|res) |
+ |
+ return buf, nil |
+} |
+ |
+type StreamID int32 |
+ |
+type FrameType byte |
+ |
+func (ft FrameType) String() string { |
+ switch ft { |
+ case DataFrameType: |
+ return "DATA" |
+ case HeadersFrameType: |
+ return "HEADERS" |
+ case PriorityFrameType: |
+ return "PRIORITY" |
+ case ResetStreamFrameType: |
+ return "RST_STREAM" |
+ case SettingsFrameType: |
+ return "SETTINGS" |
+ case PushPromiseFrameType: |
+ return "PUSH_PROMISE" |
+ case PingFrameType: |
+ return "PING" |
+ case GoAwayFrameType: |
+ return "GOAWAY" |
+ case WindowUpdateFrameType: |
+ return "WINDOW_UPDATE" |
+ case ContinuationFrameType: |
+ return "CONTINUATION" |
+ case HTTP1FrameType: |
+ return "HTTP/1.? (Bad)" |
+ default: |
+ return fmt.Sprintf("UNKNOWN(%d)", byte(ft)) |
+ } |
+} |
+ |
+// Types |
+const ( |
+ DataFrameType FrameType = 0 |
+ HeadersFrameType FrameType = 1 |
+ PriorityFrameType FrameType = 2 |
+ ResetStreamFrameType FrameType = 3 |
+ SettingsFrameType FrameType = 4 |
+ PushPromiseFrameType FrameType = 5 |
+ PingFrameType FrameType = 6 |
+ GoAwayFrameType FrameType = 7 |
+ WindowUpdateFrameType FrameType = 8 |
+ ContinuationFrameType FrameType = 9 |
+ |
+ // HTTP1FrameType is not a real type, but rather a convenient way to check if the response |
+ // is an http response. The type of a frame header is the 4th byte, which in an http1 |
+ // response will be "HTTP/1.1 200 OK" or something like that. The character for "P" is 80. |
+ HTTP1FrameType FrameType = 80 |
+) |