mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-08-13 14:17:54 +00:00
Fix gRPC crash, display gRPC as base64, display gRPC URL and status code (#27)
* Added Method (POST) and URL (emtpy) to gRPC requests. * Removed quickfix that skips writing HTTP/2 to HAR. * Use HTTP/2 body to fill out http.Request and htt.Response. * Make sure that in HARs request.postData.mimeType and response.content.mimeType are application/grpc in case of grpc. * Comment. * Add URL and status code for gRPC. * Don't assume http scheme. * Use http.Header.Set instead of manually acccessing the underlaying map.
This commit is contained in:
parent
377fc79315
commit
4d6528771a
@ -6,8 +6,11 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
@ -62,6 +65,7 @@ func (fbs *fragmentsByStream) pop(streamID uint32) ([]hpack.HeaderField, []byte)
|
|||||||
headers := (*fbs)[streamID].headers
|
headers := (*fbs)[streamID].headers
|
||||||
data := (*fbs)[streamID].data
|
data := (*fbs)[streamID].data
|
||||||
delete((*fbs), streamID)
|
delete((*fbs), streamID)
|
||||||
|
|
||||||
return headers, data
|
return headers, data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,9 +104,11 @@ func (ga *GrpcAssembler) readMessage() (uint32, interface{}, string, error) {
|
|||||||
|
|
||||||
headers, data := ga.fragmentsByStream.pop(streamID)
|
headers, data := ga.fragmentsByStream.pop(streamID)
|
||||||
|
|
||||||
|
// Note: header keys are converted by http.Header.Set to canonical names, e.g. content-type -> Content-Type.
|
||||||
|
// By converting the keys we violate the HTTP/2 specification, which state that all headers must be lowercase.
|
||||||
headersHTTP1 := make(http.Header)
|
headersHTTP1 := make(http.Header)
|
||||||
for _, header := range headers {
|
for _, header := range headers {
|
||||||
headersHTTP1[header.Name] = []string{header.Value}
|
headersHTTP1.Add(header.Name, header.Value)
|
||||||
}
|
}
|
||||||
dataString := base64.StdEncoding.EncodeToString(data)
|
dataString := base64.StdEncoding.EncodeToString(data)
|
||||||
|
|
||||||
@ -112,10 +118,14 @@ func (ga *GrpcAssembler) readMessage() (uint32, interface{}, string, error) {
|
|||||||
var messageHTTP1 interface{}
|
var messageHTTP1 interface{}
|
||||||
if _, ok := headersHTTP1[":method"]; ok {
|
if _, ok := headersHTTP1[":method"]; ok {
|
||||||
messageHTTP1 = http.Request{
|
messageHTTP1 = http.Request{
|
||||||
|
URL: &url.URL{},
|
||||||
|
Method: "POST",
|
||||||
Header: headersHTTP1,
|
Header: headersHTTP1,
|
||||||
Proto: protoHTTP2,
|
Proto: protoHTTP2,
|
||||||
ProtoMajor: protoMajorHTTP2,
|
ProtoMajor: protoMajorHTTP2,
|
||||||
ProtoMinor: protoMinorHTTP2,
|
ProtoMinor: protoMinorHTTP2,
|
||||||
|
Body: io.NopCloser(strings.NewReader(dataString)),
|
||||||
|
ContentLength: int64(len(dataString)),
|
||||||
}
|
}
|
||||||
} else if _, ok := headersHTTP1[":status"]; ok {
|
} else if _, ok := headersHTTP1[":status"]; ok {
|
||||||
messageHTTP1 = http.Response{
|
messageHTTP1 = http.Response{
|
||||||
@ -123,6 +133,8 @@ func (ga *GrpcAssembler) readMessage() (uint32, interface{}, string, error) {
|
|||||||
Proto: protoHTTP2,
|
Proto: protoHTTP2,
|
||||||
ProtoMajor: protoMajorHTTP2,
|
ProtoMajor: protoMajorHTTP2,
|
||||||
ProtoMinor: protoMinorHTTP2,
|
ProtoMinor: protoMinorHTTP2,
|
||||||
|
Body: io.NopCloser(strings.NewReader(dataString)),
|
||||||
|
ContentLength: int64(len(dataString)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0, nil, "", errors.New("Failed to assemble stream: neither a request nor a message")
|
return 0, nil, "", errors.New("Failed to assemble stream: neither a request nor a message")
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/martian/har"
|
"github.com/google/martian/har"
|
||||||
@ -40,26 +42,41 @@ type HarFile struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewEntry(request *http.Request, requestTime time.Time, response *http.Response, responseTime time.Time) (*har.Entry, error) {
|
func NewEntry(request *http.Request, requestTime time.Time, response *http.Response, responseTime time.Time) (*har.Entry, error) {
|
||||||
// TODO: quick fix until TRA-3212 is implemented
|
|
||||||
if request.URL == nil || request.Method == "" {
|
|
||||||
return nil, errors.New("Invalid request")
|
|
||||||
}
|
|
||||||
harRequest, err := har.NewRequest(request, true)
|
harRequest, err := har.NewRequest(request, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SilentError("convert-request-to-har", "Failed converting request to HAR %s (%v,%+v)\n", err, err, err)
|
SilentError("convert-request-to-har", "Failed converting request to HAR %s (%v,%+v)\n", err, err, err)
|
||||||
return nil, errors.New("Failed converting request to HAR")
|
return nil, errors.New("Failed converting request to HAR")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Martian copies http.Request.URL.String() to har.Request.URL.
|
|
||||||
// According to the spec, the URL field needs to be the absolute URL.
|
|
||||||
harRequest.URL = fmt.Sprintf("http://%s%s", request.Host, request.URL)
|
|
||||||
|
|
||||||
harResponse, err := har.NewResponse(response, true)
|
harResponse, err := har.NewResponse(response, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SilentError("convert-response-to-har", "Failed converting response to HAR %s (%v,%+v)\n", err, err, err)
|
SilentError("convert-response-to-har", "Failed converting response to HAR %s (%v,%+v)\n", err, err, err)
|
||||||
return nil, errors.New("Failed converting response to HAR")
|
return nil, errors.New("Failed converting response to HAR")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if harRequest.PostData != nil && strings.HasPrefix(harRequest.PostData.MimeType, "application/grpc") {
|
||||||
|
// Force HTTP/2 gRPC into HAR template
|
||||||
|
|
||||||
|
harRequest.URL = fmt.Sprintf("%s://%s%s", request.Header.Get(":scheme"), request.Header.Get(":authority"), request.Header.Get(":path"))
|
||||||
|
|
||||||
|
status, err := strconv.Atoi(response.Header.Get(":status"))
|
||||||
|
if err != nil {
|
||||||
|
SilentError("convert-response-status-for-har", "Failed converting status to int %s (%v,%+v)\n", err, err, err)
|
||||||
|
return nil, errors.New("Failed converting response status to int for HAR")
|
||||||
|
}
|
||||||
|
harResponse.Status = status
|
||||||
|
} else {
|
||||||
|
// Martian copies http.Request.URL.String() to har.Request.URL, which usually contains the path.
|
||||||
|
// However, according to the HAR spec, the URL field needs to be the absolute URL.
|
||||||
|
var scheme string
|
||||||
|
if request.URL.Scheme != "" {
|
||||||
|
scheme = request.URL.Scheme
|
||||||
|
} else {
|
||||||
|
scheme = "http"
|
||||||
|
}
|
||||||
|
harRequest.URL = fmt.Sprintf("%s://%s%s", scheme, request.Host, request.URL)
|
||||||
|
}
|
||||||
|
|
||||||
totalTime := responseTime.Sub(requestTime).Round(time.Millisecond).Milliseconds()
|
totalTime := responseTime.Sub(requestTime).Round(time.Millisecond).Milliseconds()
|
||||||
if totalTime < 1 {
|
if totalTime < 1 {
|
||||||
totalTime = 1
|
totalTime = 1
|
||||||
|
Loading…
Reference in New Issue
Block a user