httpLayer

package
v1.2.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 28, 2022 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

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文档里提及的 代码实现, 我们不予支持。

Index

Constants

View Source
const (
	FallBack_default byte = 1 << iota //default 其实也是path,只不过path是通配符。

	Fallback_path
	Fallback_alpn
	Fallback_sni
)
View Source
const (
	Fail_tooShort = iota + 1
	Fail_not_for_h2c
	Fail_method_len_wrong
	Fail_unexpected_proxy
	Fail_space_index_wrong
	Fail_is_11proxy_butNot_11
	Fail_no_slash
	Fail_earlyLinefeed
	Fail_firstline_less_than_10
)
View Source
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

	XForwardStr = "X-Forwarded-For"
)
View Source
const (

	//符合 nginx返回的时间格式,且符合 golang对时间格式字符串的 "123456"的约定 的字符串。
	Nginx_timeFormatStr = "02 Jan 2006 15:04:05 MST"

	Nginx400_html = "" /* 171-byte string literal not displayed */

	// real nginx response,to generate it,  echo xx | nc 127.0.0.1 80 > response
	Err400response_nginx = "HTTP/1.1 400 Bad Request\r\nServer: nginx/1.21.5\r\nDate: Sat, 02 Jan 2006 15:04:05 MST\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n" + Nginx400_html

	// real nginx response,to generate it,  curl -iv --raw 127.0.0.1/not_exist_path > response
	Err404response_nginx = "" /* 406-byte string literal not displayed */

	Nginx403_html = "" /* 185-byte string literal not displayed */

	/* real nginx response, to generate it,  set nginx config like:
	location / {
		return 403;
	}
	*/
	Err403response_nginx = "HTTP/1.1 403 Forbidden\r\nServer: nginx/1.21.5\r\nDate: Sat, 02 Jan 2006 15:04:05 MST\r\nContent-Type: text/html\r\nContent-Length: 169\r\nConnection: keep-alive\r\n\r\n" + Nginx403_html
)
View Source
const Fail_no_endMark = -12
View Source
const (
	Fallback_none = 0
)
View Source
const MaxParseUrlLen = 3000

也许有 ws 的 earlydata放在 query请求里的情况; 虽然本作不支持这种earlydata, 但是也要认定这是合法的请求。

Variables

View Source
var (
	HeaderENDING_bytes = []byte(HeaderENDING)

	ErrNotHTTP_Request = utils.InvalidDataErr("not http request")

	Err400response_golang string
)
View Source
var ErrShouldFallback = errors.New("will fallback")

Functions

func AllHeadersIn added in v1.2.0

func AllHeadersIn(template map[string][]string, realh http.Header) (ok bool, firstNotMatchKey string)

all values in template is given by real

func CanonicalizeHeaderKey added in v1.2.1

func CanonicalizeHeaderKey(k []byte)

Algorithm below is like standard textproto/CanonicalMIMEHeaderKey, except that it operates with slice of bytes and modifies it inplace without copying. copied from gobwas/ws

func GetNginx400Response added in v1.2.4

func GetNginx400Response() string

Get real a 400 response that looks like it comes from nginx.

func GetNginx403Response added in v1.2.4

func GetNginx403Response() string

Get real a 403 response that looks like it comes from nginx.

func GetNginx404Response added in v1.2.4

func GetNginx404Response() string

Get real a 404 response that looks like it comes from nginx.

func GetNginxResponse added in v1.2.4

func GetNginxResponse(template string) string

Get real a response that looks like it comes from nginx.

func GetNginxWeekdayStr added in v1.2.4

func GetNginxWeekdayStr(t *time.Time) string

func HasFallbackType

func HasFallbackType(ftype, b byte) bool

判断 Fallback.SupportType 返回的 数值 是否具有特定的Fallback类型

func SetDefaultNginxHeader added in v1.2.4

func SetDefaultNginxHeader(rw http.ResponseWriter)

func SetNginx400Response added in v1.2.4

func SetNginx400Response(rw http.ResponseWriter)

mimic GetNginx400Response()

func SetNginx403Response added in v1.2.4

func SetNginx403Response(rw http.ResponseWriter)

func TrimHeaders added in v1.2.0

func TrimHeaders(m map[string][]string) (result map[string][]string)

return a clone of m with headers trimmed to one value

Types

type ClassicFallback

type ClassicFallback struct {
	Default *FallbackResult

	Map map[string]map[FallbackConditionSet]*FallbackResult //第一层key为 inTag,若为 "" 则表示 来自所有inServer 的 都会被匹配
	// contains filtered or unexported fields
}

实现 Fallback,支持 path, alpn, sni 分流。

内部 map 我们使用通用的集合办法, 而不是多层map嵌套;

虽然目前就三个fallback类型,但是谁知道以后会加几个?所以这样更通用.

目前3种fallback性能是没问题的,不过如果 fallback继续增加的话,
最差情况下集合的子集总数会急剧上升,导致最差情况下性能不如多重 map;不过一般没人会给出那种配置.

TODO: 预先计算所有的子集,这样就不用每次匹配都算一遍

func NewClassicFallback

func NewClassicFallback() *ClassicFallback

func NewClassicFallbackFromConfList

func NewClassicFallbackFromConfList(fcl []*FallbackConf) *ClassicFallback

func (*ClassicFallback) GetFallback

func (cfb *ClassicFallback) GetFallback(fromServerTag string, ftype byte, ss ...string) *FallbackResult

GetFallback 使用给出的 ftype mask 和 对应参数 来试图匹配到 回落地址. ss 必须按 FallBack_* 类型 从小到大顺序排列

func (*ClassicFallback) InsertFallbackConditionSet

func (cfb *ClassicFallback) InsertFallbackConditionSet(condition FallbackConditionSet, forServerTags []string, addr netLayer.Addr, xver int)

func (*ClassicFallback) SupportType

func (cfb *ClassicFallback) SupportType() byte

type Fallback

type Fallback interface {
	GetFallback(ftype byte, params ...string) *FallbackResult
	SupportType() byte //参考Fallback_开头的常量。如果支持多个,则返回它们 按位与 的结果
}

实现 Fallback. 这里的fallback只与http协议有关,所以只能按path,alpn 和 sni 进行分类

type FallbackConditionSet

type FallbackConditionSet struct {
	Path, Sni string
	AlpnMask  byte
}

func (*FallbackConditionSet) GetAllSubSets

func (fcs *FallbackConditionSet) GetAllSubSets() (rs []FallbackConditionSet)

返回不包括自己的所有子集

func (*FallbackConditionSet) GetSub

func (fcs *FallbackConditionSet) GetSub(subType byte) (r FallbackConditionSet)

func (*FallbackConditionSet) GetType

func (fcs *FallbackConditionSet) GetType() (r byte)

func (FallbackConditionSet) TestAllSubSets

func (fcs FallbackConditionSet) TestAllSubSets(allsupportedTypeMask byte, theMap map[FallbackConditionSet]*FallbackResult) *FallbackResult

TestAllSubSets 传入一个map, 对fcs自己以及其所有子集依次测试, 看是否有匹配的。 对比 GetAllSubSets 内存占用较大, 而本方法开销则小很多, 因为1是复用内存, 2是匹配到就会返回,一般不会到遍历全部子集.

type FallbackConf

type FallbackConf struct {
	//可选
	FromTag []string `toml:"from" json:"from"` //which inServer triggered this fallback

	Xver int `toml:"xver" json:"xver"` //use PROXY protocol or not, and which version

	//必填。
	//see netLayer.NewAddrFromAny for details about "any" addr.
	//
	// 约定,如果该项是字符串 且 开头为@,则我们认为它给出的是 tag 名称,要将其替换为 实际 该tag的 listen  的地址。
	Dest any `toml:"dest" json:"dest"`

	Path string   `toml:"path" json:"path"`
	Sni  string   `toml:"sni" json:"sni"`
	Alpn []string `toml:"alpn" json:"alpn"`
}

type FallbackMeta added in v1.2.0

type FallbackMeta struct {
	net.Conn
	H1RequestBuf *bytes.Buffer
	Path         string
	Method       string
	IsH2         bool
	Reason       string

	H2Request *http.Request
	H2RW      http.ResponseWriter
	XFF       net.Addr
}

http level fallback metadata

type FallbackResult added in v1.2.0

type FallbackResult struct {
	Addr netLayer.Addr
	Xver int
}

func (*FallbackResult) GetFallback added in v1.2.0

func (r *FallbackResult) GetFallback(ftype byte, _ ...string) *FallbackResult

func (FallbackResult) SupportType added in v1.2.0

func (FallbackResult) SupportType() byte

type H1RequestParser added in v1.2.0

type H1RequestParser struct {
	Version         string
	Path            string
	Method          string
	WholeRequestBuf *bytes.Buffer
	Failreason      int //为0表示没错误
	Headers         []RawHeader
}

H1RequestParser被用于 预读一个链接,判断该连接是否是有效的http请求, 并将Version,Path,Method 记录在结构中.

只能过滤 http 0.9, 1.0 和 1.1. 无法过滤h2和h3.

func (*H1RequestParser) ReadAndParse added in v1.2.0

func (rhr *H1RequestParser) ReadAndParse(r io.Reader) error

尝试读取数据并解析HTTP请求, 解析的 数据会存入 RequestParser 结构中. 如果读取错误,会返回该错误; 如果读到的不是HTTP请求,返回 的err 的 errors.Is(err,ErrNotHTTP_Request) == true;

该函数有个小问题,就是如果一次Read是短Read(可能是网络原因),没读完整个http头部,则仍然会认为是错误

func (*H1RequestParser) ReadAndParse_2 added in v1.2.4

func (rhr *H1RequestParser) ReadAndParse_2(r net.Conn) error

第一次没读完后,再读一遍

type HeaderConn

type HeaderConn struct {
	net.Conn
	H           *HeaderPreset
	IsServerEnd bool

	ReadOkCallback func(*bytes.Buffer)
	// contains filtered or unexported fields
}

func (*HeaderConn) Read

func (c *HeaderConn) Read(p []byte) (n int, err error)

func (*HeaderConn) Write

func (c *HeaderConn) Write(p []byte) (n int, err error)

type HeaderPreset

type HeaderPreset struct {
	Request  *RequestHeader  `toml:"request"`
	Response *ResponseHeader `toml:"response"`

	Strict                              bool `toml:"strict"`
	NoResponseHeaderWhenGotHttpResponse bool `toml:"no_resp_h_c"` //用于读取 回落到 真实http服务器的情况,此时我们就不用自定义的响应,而是用 真实服务器的响应。no_resp_h_c 意思是 no response header conditional
}

http 头 预设, 分客户端的request 和 服务端的 response这两部分.

func (*HeaderPreset) AssignDefaultValue

func (h *HeaderPreset) AssignDefaultValue()

默认值保持与v2ray的配置相同

func (*HeaderPreset) Prepare added in v1.2.0

func (h *HeaderPreset) Prepare()

将Header改为首字母大写

func (*HeaderPreset) ReadRequest

func (h *HeaderPreset) ReadRequest(underlay io.Reader) (rp H1RequestParser, leftBuf *bytes.Buffer, err error)

func (*HeaderPreset) ReadResponse

func (h *HeaderPreset) ReadResponse(underlay io.Reader) (leftBuf *bytes.Buffer, err error)

func (*HeaderPreset) WriteRequest

func (h *HeaderPreset) WriteRequest(underlay io.Writer, payload []byte) error

func (*HeaderPreset) WriteResponse

func (h *HeaderPreset) WriteResponse(underlay io.Writer, payload []byte) error

type RawHeader added in v1.2.1

type RawHeader struct {
	Head  []byte
	Value []byte
}

func ParseH1Request added in v1.2.1

func ParseH1Request(bs []byte, isproxy bool) (version, method, path string, headers []RawHeader, failreason int)

从数据中试图获取 http1.1, http1.0或 http0.9 请求的 version, path, method 和 headers. failreason!=0 表示获取失败, 即表示不是合法的h1请求.

如果是http代理的话,判断方式会有变化,所以需要 isproxy 参数。

type RejectConn added in v1.2.4

type RejectConn struct {
	http.ResponseWriter
}

implements netLayer.RejectConn

func (RejectConn) Reject added in v1.2.4

func (rc RejectConn) Reject()

call SetNginx403Response

func (RejectConn) RejectBehaviorDefined added in v1.2.4

func (RejectConn) RejectBehaviorDefined() bool

type RequestErr

type RequestErr struct {
	Path   string
	Method string
}

func (*RequestErr) Error

func (e *RequestErr) Error() string

func (*RequestErr) Is added in v1.2.0

func (e *RequestErr) Is(err error) bool

type RequestHeader

type RequestHeader struct {
	Version string              `toml:"version"` //默认值为 "1.1"
	Method  string              `toml:"method"`  //默认值为 "GET"。
	Path    []string            `toml:"path"`    //默认值为 ["/"]。当有多个值时,每次请求随机选择一个值。
	Headers map[string][]string `toml:"headers"` //一个键值对,每个键表示一个 HTTP 头的名称,对应的值是一个数组。每次请求会附上所有的键,并随机选择一个对应的值。
}

type ResponseHeader

type ResponseHeader struct {
	Version    string              `toml:"version"` // 1.1
	StatusCode string              `toml:"status"`  // 200
	Reason     string              `toml:"reason"`  // OK
	Headers    map[string][]string `toml:"headers"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL