blob: 214af7fdc6c5196f679b07b5ac3e5143eeeff6b3 [file] [log] [blame]
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package reader
import (
"bufio"
"fmt"
"net/http"
"net/textproto"
"net/url"
"strings"
"github.com/apache/skywalking-rover/pkg/profiling/task/network/analyze/buffer"
"github.com/apache/skywalking-rover/pkg/profiling/task/network/analyze/enums"
)
type Request struct {
*MessageOpt
original *http.Request
headerBuffer *buffer.Buffer
bodyBuffer *buffer.Buffer
}
func (r *Request) Headers() http.Header {
return r.original.Header
}
func (r *Request) HeaderBuffer() *buffer.Buffer {
return r.headerBuffer
}
func (r *Request) BodyBuffer() *buffer.Buffer {
return r.bodyBuffer
}
func (r *Request) MinDataID() int {
return int(r.headerBuffer.FirstSocketBuffer().DataID())
}
func (r *Request) RequestURI() string {
return r.original.RequestURI
}
//nolint
func ReadRequest(buf *buffer.Buffer, readBody bool) (*Request, enums.ParseResult, error) {
bufReader := bufio.NewReader(buf)
tp := textproto.NewReader(bufReader)
req := &http.Request{}
result := &Request{original: req}
result.MessageOpt = &MessageOpt{result}
headerStartPosition := buf.Position()
line, err := tp.ReadLine()
if err != nil {
return nil, enums.ParseResultSkipPackage, fmt.Errorf("read request first lint failure: %v", err)
}
method, rest, ok1 := strings.Cut(line, " ")
requestURI, proto, ok2 := strings.Cut(rest, " ")
if !ok1 || !ok2 {
return nil, enums.ParseResultSkipPackage, fmt.Errorf("the first line is not request: %s", line)
}
isRequest := false
for _, m := range requestMethods {
if method == m {
isRequest = true
break
}
}
if !isRequest {
return nil, enums.ParseResultSkipPackage, fmt.Errorf("is not request: %s", method)
}
major, minor, ok := http.ParseHTTPVersion(proto)
if !ok {
return nil, enums.ParseResultSkipPackage, fmt.Errorf("the protocol version cannot be identity: %s", proto)
}
justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(requestURI, "/")
if justAuthority {
requestURI = "http://" + requestURI
}
uri, err := url.ParseRequestURI(requestURI)
if err != nil {
return nil, enums.ParseResultSkipPackage, err
}
req.Method, req.URL, req.RequestURI = method, uri, requestURI
req.Proto, req.ProtoMajor, req.ProtoMinor = proto, major, minor
// header reader
mimeHeader, err := tp.ReadMIMEHeader()
if err != nil {
return nil, enums.ParseResultSkipPackage, err
}
req.Header = http.Header(mimeHeader)
req.Host = req.URL.Host
if req.Host == "" {
req.Host = req.Header.Get("Host")
}
result.buildHeaderBuffer(headerStartPosition, buf, bufReader)
if readBody {
if b, r, err := result.readFullBody(bufReader, buf); err != nil {
return nil, enums.ParseResultSkipPackage, err
} else if r != enums.ParseResultSuccess {
return nil, r, nil
} else {
result.bodyBuffer = b
}
}
return result, enums.ParseResultSuccess, nil
}
func (r *Request) buildHeaderBuffer(start *buffer.Position, buf *buffer.Buffer, bufReader *bufio.Reader) {
endPosition := buf.OffsetPosition(-bufReader.Buffered())
r.headerBuffer = buf.Slice(true, start, endPosition)
}
func (r *Request) readFullBody(bodyReader *bufio.Reader, original *buffer.Buffer) (*buffer.Buffer, enums.ParseResult, error) {
length, err := r.appointedLength()
if err != nil {
return nil, enums.ParseResultSkipPackage, err
} else if length > 0 {
return r.checkBodyWithSize(original, bodyReader, length, true)
}
if r.isChunked() {
return r.checkChunkedBody(original, bodyReader)
}
return r.readBodyUntilCurrentPackageFinished(original, bodyReader)
}