From b31af7214b94fe871b5d89db4733adfc9f89d653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Mert=20Y=C4=B1ld=C4=B1ran?= Date: Thu, 20 Jan 2022 11:02:21 +0300 Subject: [PATCH] TRA-4140 Fix HTTP/1.0 is recognized as HTTP/1.1 (#666) --- README.md | 2 +- tap/extensions/http/handlers.go | 8 +++---- tap/extensions/http/http2_assembler.go | 4 ++-- tap/extensions/http/main.go | 32 ++++++++++++++++++-------- tap/extensions/http/matcher.go | 14 +++++++---- ui/src/components/UI/StatusCode.tsx | 2 +- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b071f6390..508e93304 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Think TCPDump and Wireshark re-invented for Kubernetes. - Simple and powerful CLI - Monitoring network traffic in real-time. Supported protocols: - - [HTTP/1.1](https://datatracker.ietf.org/doc/html/rfc2616) (REST, etc.) + - [HTTP/1.x](https://datatracker.ietf.org/doc/html/rfc2616) (REST, GraphQL, SOAP, etc.) - [HTTP/2](https://datatracker.ietf.org/doc/html/rfc7540) (gRPC) - [AMQP](https://www.rabbitmq.com/amqp-0-9-1-reference.html) (RabbitMQ, Apache Qpid, etc.) - [Apache Kafka](https://kafka.apache.org/protocol) diff --git a/tap/extensions/http/handlers.go b/tap/extensions/http/handlers.go index 9b34d63f1..3c701e530 100644 --- a/tap/extensions/http/handlers.go +++ b/tap/extensions/http/handlers.go @@ -66,7 +66,7 @@ func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTi streamID, "HTTP2", ) - item = reqResMatcher.registerRequest(ident, &messageHTTP1, superTimer.CaptureTime) + item = reqResMatcher.registerRequest(ident, &messageHTTP1, superTimer.CaptureTime, messageHTTP1.ProtoMinor) if item != nil { item.ConnectionInfo = &api.ConnectionInfo{ ClientIP: tcpID.SrcIP, @@ -86,7 +86,7 @@ func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTi streamID, "HTTP2", ) - item = reqResMatcher.registerResponse(ident, &messageHTTP1, superTimer.CaptureTime) + item = reqResMatcher.registerResponse(ident, &messageHTTP1, superTimer.CaptureTime, messageHTTP1.ProtoMinor) if item != nil { item.ConnectionInfo = &api.ConnectionInfo{ ClientIP: tcpID.DstIP, @@ -135,7 +135,7 @@ func handleHTTP1ClientStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api counterPair.Request, "HTTP1", ) - item := reqResMatcher.registerRequest(ident, req, superTimer.CaptureTime) + item := reqResMatcher.registerRequest(ident, req, superTimer.CaptureTime, req.ProtoMinor) if item != nil { item.ConnectionInfo = &api.ConnectionInfo{ ClientIP: tcpID.SrcIP, @@ -175,7 +175,7 @@ func handleHTTP1ServerStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api counterPair.Response, "HTTP1", ) - item := reqResMatcher.registerResponse(ident, res, superTimer.CaptureTime) + item := reqResMatcher.registerResponse(ident, res, superTimer.CaptureTime, res.ProtoMinor) if item != nil { item.ConnectionInfo = &api.ConnectionInfo{ ClientIP: tcpID.DstIP, diff --git a/tap/extensions/http/http2_assembler.go b/tap/extensions/http/http2_assembler.go index 0563aacb9..eeec5a273 100644 --- a/tap/extensions/http/http2_assembler.go +++ b/tap/extensions/http/http2_assembler.go @@ -235,8 +235,8 @@ func checkIsHTTP2ServerStream(b *bufio.Reader) (bool, error) { return false, err } - // If response starts with this text, it is HTTP/1.x - if bytes.Compare(buf, []byte("HTTP/1.0 ")) == 0 || bytes.Compare(buf, []byte("HTTP/1.1 ")) == 0 { + // If response starts with HTTP/1. then it's not HTTP/2 + if bytes.HasPrefix(buf, []byte("HTTP/1.")) { return false, nil } diff --git a/tap/extensions/http/main.go b/tap/extensions/http/main.go index cf6a905f5..df9432bc5 100644 --- a/tap/extensions/http/main.go +++ b/tap/extensions/http/main.go @@ -15,7 +15,21 @@ import ( "github.com/up9inc/mizu/tap/api" ) -var protocol api.Protocol = api.Protocol{ +var http10protocol api.Protocol = api.Protocol{ + Name: "http", + LongName: "Hypertext Transfer Protocol -- HTTP/1.0", + Abbreviation: "HTTP", + Macro: "http", + Version: "1.0", + BackgroundColor: "#205cf5", + ForegroundColor: "#ffffff", + FontSize: 12, + ReferenceLink: "https://datatracker.ietf.org/doc/html/rfc1945", + Ports: []string{"80", "443", "8080"}, + Priority: 0, +} + +var http11protocol api.Protocol = api.Protocol{ Name: "http", LongName: "Hypertext Transfer Protocol -- HTTP/1.1", Abbreviation: "HTTP", @@ -69,12 +83,12 @@ func init() { type dissecting string func (d dissecting) Register(extension *api.Extension) { - extension.Protocol = &protocol + extension.Protocol = &http11protocol extension.MatcherMap = reqResMatcher.openMessagesMap } func (d dissecting) Ping() { - log.Printf("pong %s", protocol.Name) + log.Printf("pong %s", http11protocol.Name) } func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions) error { @@ -96,7 +110,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co http2Assembler = createHTTP2Assembler(b) } - if superIdentifier.Protocol != nil && superIdentifier.Protocol != &protocol { + if superIdentifier.Protocol != nil && superIdentifier.Protocol != &http11protocol { return errors.New("Identified by another protocol") } @@ -128,7 +142,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co tcpID.DstPort, "HTTP2", ) - item := reqResMatcher.registerRequest(ident, req, superTimer.CaptureTime) + item := reqResMatcher.registerRequest(ident, req, superTimer.CaptureTime, req.ProtoMinor) if item != nil { item.ConnectionInfo = &api.ConnectionInfo{ ClientIP: tcpID.SrcIP, @@ -154,7 +168,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co if !dissected { return err } - superIdentifier.Protocol = &protocol + superIdentifier.Protocol = &http11protocol return nil } @@ -442,9 +456,9 @@ func (d dissecting) Represent(request map[string]interface{}, response map[strin func (d dissecting) Macros() map[string]string { return map[string]string{ - `http`: fmt.Sprintf(`proto.name == "%s" and proto.version == "%s"`, protocol.Name, protocol.Version), - `http2`: fmt.Sprintf(`proto.name == "%s" and proto.version == "%s"`, protocol.Name, http2Protocol.Version), - `grpc`: fmt.Sprintf(`proto.name == "%s" and proto.version == "%s" and proto.macro == "%s"`, protocol.Name, grpcProtocol.Version, grpcProtocol.Macro), + `http`: fmt.Sprintf(`proto.name == "%s" and proto.version.startsWith("%c")`, http11protocol.Name, http11protocol.Version[0]), + `http2`: fmt.Sprintf(`proto.name == "%s" and proto.version == "%s"`, http11protocol.Name, http2Protocol.Version), + `grpc`: fmt.Sprintf(`proto.name == "%s" and proto.version == "%s" and proto.macro == "%s"`, http11protocol.Name, grpcProtocol.Version, grpcProtocol.Macro), } } diff --git a/tap/extensions/http/matcher.go b/tap/extensions/http/matcher.go index 01048fa21..047cde06c 100644 --- a/tap/extensions/http/matcher.go +++ b/tap/extensions/http/matcher.go @@ -22,7 +22,7 @@ func createResponseRequestMatcher() requestResponseMatcher { return *newMatcher } -func (matcher *requestResponseMatcher) registerRequest(ident string, request *http.Request, captureTime time.Time) *api.OutputChannelItem { +func (matcher *requestResponseMatcher) registerRequest(ident string, request *http.Request, captureTime time.Time, protoMinor int) *api.OutputChannelItem { split := splitIdent(ident) key := genKey(split) @@ -41,14 +41,14 @@ func (matcher *requestResponseMatcher) registerRequest(ident string, request *ht if responseHTTPMessage.IsRequest { return nil } - return matcher.preparePair(&requestHTTPMessage, responseHTTPMessage) + return matcher.preparePair(&requestHTTPMessage, responseHTTPMessage, protoMinor) } matcher.openMessagesMap.Store(key, &requestHTTPMessage) return nil } -func (matcher *requestResponseMatcher) registerResponse(ident string, response *http.Response, captureTime time.Time) *api.OutputChannelItem { +func (matcher *requestResponseMatcher) registerResponse(ident string, response *http.Response, captureTime time.Time, protoMinor int) *api.OutputChannelItem { split := splitIdent(ident) key := genKey(split) @@ -67,14 +67,18 @@ func (matcher *requestResponseMatcher) registerResponse(ident string, response * if !requestHTTPMessage.IsRequest { return nil } - return matcher.preparePair(requestHTTPMessage, &responseHTTPMessage) + return matcher.preparePair(requestHTTPMessage, &responseHTTPMessage, protoMinor) } matcher.openMessagesMap.Store(key, &responseHTTPMessage) return nil } -func (matcher *requestResponseMatcher) preparePair(requestHTTPMessage *api.GenericMessage, responseHTTPMessage *api.GenericMessage) *api.OutputChannelItem { +func (matcher *requestResponseMatcher) preparePair(requestHTTPMessage *api.GenericMessage, responseHTTPMessage *api.GenericMessage, protoMinor int) *api.OutputChannelItem { + protocol := http11protocol + if protoMinor == 0 { + protocol = http10protocol + } return &api.OutputChannelItem{ Protocol: protocol, Timestamp: requestHTTPMessage.CaptureTime.UnixNano() / int64(time.Millisecond), diff --git a/ui/src/components/UI/StatusCode.tsx b/ui/src/components/UI/StatusCode.tsx index 88fd04325..574a8bb07 100644 --- a/ui/src/components/UI/StatusCode.tsx +++ b/ui/src/components/UI/StatusCode.tsx @@ -35,7 +35,7 @@ export function getClassification(statusCode: number): string { let classification = StatusCodeClassification.NEUTRAL; // 1 - 16 HTTP/2 (gRPC) status codes - // 2xx - 5xx HTTP/1.1 status codes + // 2xx - 5xx HTTP/1.x status codes if ((statusCode >= 200 && statusCode <= 399) || statusCode === 0) { classification = StatusCodeClassification.SUCCESS; } else if (statusCode >= 400 || (statusCode >= 1 && statusCode <= 16)) {