Files
v2ray_simple/httpLayer/httpLayer.go
2022-11-30 17:02:29 +08:00

187 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
Package httpLayer provides methods for parsing and sending http request and response.
Fallback 由 本包 处理. 因为回落的目标只可能是http服务器.
http头 格式 可以参考:
https://datatracker.ietf.org/doc/html/rfc2616#section-4
https://datatracker.ietf.org/doc/html/rfc2616#section-5
V2ray 兼容性
http头我们希望能够完全兼容 v2ray的行为。
观察v2ray的实现在没有header时还会添加一个 Date 这个v2ray的文档里没提
v2ray文档: https://www.v2fly.org/config/transport/tcp.html#noneheaderobject
相关 v2ray代码: https://github.com/v2fly/v2ray-core/tree/master/transport/internet/headers/http/http.go
我们虽然宣称兼容 v2ray但在对于这种 不在 v2ray文档里提及的 代码实现, 我们不予支持。
*/
package httpLayer
import (
"bytes"
"io"
"net"
"strings"
"time"
"github.com/e1732a364fed/v2ray_simple/utils"
"net/http"
"net/http/httptest"
)
const (
Err403response = "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nCache-Control: max-age=3600, public\r\nContent-Length: 0\r\n\r\n"
H11_Str = "http/1.1"
H2_Str = "h2"
CRLF = "\r\n"
//参考 https://datatracker.ietf.org/doc/html/rfc2616#section-4.1
//
//http头的尾部. 每个header末尾都有一个 crlf, 整个头部结尾还有一个crlf, 所以是两个.
HeaderENDING = CRLF + CRLF
//我们不使用v2ray的8k的最大header长度限制因为这反倒会探测出特殊性。http标准是最大1MB。
XForwardStr = "X-Forwarded-For"
)
var (
HeaderENDING_bytes = []byte(HeaderENDING)
ErrNotHTTP_Request = utils.InvalidDataErr("not http request")
Err400response_golang string
)
func init() {
//使用 httptest 包 来完美重现 golang的http包的 notfound, 可以随golang具体实现而同步变化
req := httptest.NewRequest("GET", "http://exam.com/", nil)
w := httptest.NewRecorder()
http.NotFound(w, req)
buf := &bytes.Buffer{}
w.Result().Write(buf)
Err400response_golang = buf.String()
}
type RequestErr struct {
Path string
Method string
}
func (e *RequestErr) Is(err error) bool {
if err == nil {
return false
}
if re, ok := err.(*RequestErr); ok && re != nil {
return (e.Path == re.Path && e.Method == re.Path)
}
if err == utils.ErrInvalidData {
return true
}
return false
}
func (e *RequestErr) Error() string {
var sb strings.Builder
sb.WriteString("InvaidRequest ")
sb.WriteString(e.Method)
sb.WriteString(",")
sb.WriteString(e.Path)
return sb.String()
}
// H1RequestParser被用于 预读一个链接判断该连接是否是有效的http请求,
// 并将VersionPathMethod 记录在结构中.
//
// 只能过滤 http 0.9, 1.0 和 1.1. 无法过滤h2和h3.
type H1RequestParser struct {
Version string
Path string
Method string
WholeRequestBuf *bytes.Buffer
Failreason int //为0表示没错误
Headers []RawHeader
}
// 尝试读取数据并解析HTTP请求, 解析的 数据会存入 RequestParser 结构中.
//如果读取错误,会返回该错误; 如果读到的不是HTTP请求返回 的err 的 errors.Is(err,ErrNotHTTP_Request) == true;
//
// 该函数有个小问题,就是如果一次Read是短Read可能是网络原因没读完整个http头部则仍然会认为是错误
func (rhr *H1RequestParser) ReadAndParse(r io.Reader) error {
bs := utils.GetPacket()
n, e := r.Read(bs)
if e != nil {
return e
}
data := bs[:n]
buf := bytes.NewBuffer(data)
rhr.WholeRequestBuf = buf
rhr.Version, rhr.Method, rhr.Path, rhr.Headers, rhr.Failreason = ParseH1Request(data, false)
if rhr.Failreason != 0 {
return utils.ErrInErr{ErrDesc: "httpLayer ReadAndParse failed", ErrDetail: ErrNotHTTP_Request, Data: rhr.Failreason}
}
return nil
}
//第一次没读完后,再读一遍
func (rhr *H1RequestParser) ReadAndParse_2(r net.Conn) error {
bs := utils.GetPacket()
n, e := r.Read(bs)
if e != nil {
return e
}
data := bs[:n]
buf := bytes.NewBuffer(data)
rhr.WholeRequestBuf = buf
rhr.Version, rhr.Method, rhr.Path, rhr.Headers, rhr.Failreason = ParseH1Request(data, false)
if rhr.Failreason == Fail_no_endMark {
r.SetReadDeadline(time.Now().Add(time.Millisecond * 200))
n2, e2 := r.Read(bs[n:])
r.SetReadDeadline(time.Time{})
if e2 != nil && n2 <= 0 {
return utils.ErrInErr{ErrDesc: "httpLayer ReadAndParse failed", ErrDetail: ErrNotHTTP_Request, Data: []interface{}{rhr.Failreason, e2}}
} else {
data2 := bs[:n+rhr.WholeRequestBuf.Len()]
buf2 := bytes.NewBuffer(data2)
rhr.WholeRequestBuf = buf2
rhr.Version, rhr.Method, rhr.Path, rhr.Headers, rhr.Failreason = ParseH1Request(buf2.Bytes(), false)
if rhr.Failreason != 0 {
return utils.ErrInErr{ErrDesc: "httpLayer ReadAndParse failed", ErrDetail: ErrNotHTTP_Request, Data: rhr.Failreason}
}
}
} else if rhr.Failreason != 0 {
return utils.ErrInErr{ErrDesc: "httpLayer ReadAndParse failed", ErrDetail: ErrNotHTTP_Request, Data: rhr.Failreason}
}
return nil
}