mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
151 lines
5.3 KiB
Go
151 lines
5.3 KiB
Go
package xhttp
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/sagernet/sing-box/common/xray/buf"
|
|
"github.com/sagernet/sing-box/common/xray/utils"
|
|
"github.com/sagernet/sing-box/option"
|
|
)
|
|
|
|
func FillStreamRequest(request *http.Request, sessionId string, seqStr string, options *option.V2RayXHTTPBaseOptions) {
|
|
request.Header = options.GetRequestHeader()
|
|
length := int(options.GetNormalizedXPaddingBytes().Rand())
|
|
config := XPaddingConfig{Length: length}
|
|
if options.XPaddingObfsMode {
|
|
config.Placement = XPaddingPlacement{
|
|
Placement: options.XPaddingPlacement,
|
|
Key: options.XPaddingKey,
|
|
Header: options.XPaddingHeader,
|
|
RawURL: request.URL.String(),
|
|
}
|
|
config.Method = PaddingMethod(options.XPaddingMethod)
|
|
} else {
|
|
config.Placement = XPaddingPlacement{
|
|
Placement: option.PlacementQueryInHeader,
|
|
Key: "x_padding",
|
|
Header: "Referer",
|
|
RawURL: request.URL.String(),
|
|
}
|
|
}
|
|
ApplyXPaddingToRequest(request, config)
|
|
ApplyMetaToRequest(options, request, sessionId, "")
|
|
if request.Body != nil && !options.NoGRPCHeader { // stream-up/one
|
|
request.Header.Set("Content-Type", "application/grpc")
|
|
}
|
|
}
|
|
|
|
func FillPacketRequest(request *http.Request, sessionId string, seqStr string, payload buf.MultiBuffer, options *option.V2RayXHTTPBaseOptions) error {
|
|
dataPlacement := options.GetNormalizedUplinkDataPlacement()
|
|
if dataPlacement == option.PlacementBody || dataPlacement == option.PlacementAuto {
|
|
request.Header = options.GetRequestHeader()
|
|
request.Body = io.NopCloser(&buf.MultiBufferContainer{MultiBuffer: payload})
|
|
request.ContentLength = int64(payload.Len())
|
|
} else {
|
|
data := make([]byte, payload.Len())
|
|
payload.Copy(data)
|
|
buf.ReleaseMulti(payload)
|
|
switch dataPlacement {
|
|
case option.PlacementHeader:
|
|
request.Header = GetRequestHeaderWithPayload(data, options)
|
|
case option.PlacementCookie:
|
|
request.Header = options.GetRequestHeader()
|
|
for _, cookie := range GetRequestCookiesWithPayload(data, options) {
|
|
request.AddCookie(cookie)
|
|
}
|
|
}
|
|
}
|
|
length := int(options.GetNormalizedXPaddingBytes().Rand())
|
|
config := XPaddingConfig{Length: length}
|
|
if options.XPaddingObfsMode {
|
|
config.Placement = XPaddingPlacement{
|
|
Placement: options.XPaddingPlacement,
|
|
Key: options.XPaddingKey,
|
|
Header: options.XPaddingHeader,
|
|
RawURL: request.URL.String(),
|
|
}
|
|
config.Method = PaddingMethod(options.XPaddingMethod)
|
|
} else {
|
|
config.Placement = XPaddingPlacement{
|
|
Placement: option.PlacementQueryInHeader,
|
|
Key: "x_padding",
|
|
Header: "Referer",
|
|
RawURL: request.URL.String(),
|
|
}
|
|
}
|
|
ApplyXPaddingToRequest(request, config)
|
|
ApplyMetaToRequest(options, request, sessionId, seqStr)
|
|
return nil
|
|
}
|
|
|
|
func WriteResponseHeader(writer http.ResponseWriter, requestMethod string, requestHeader http.Header, options *option.V2RayXHTTPOptions) {
|
|
// CORS headers for the browser dialer
|
|
if origin := requestHeader.Get("Origin"); origin == "" {
|
|
writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
} else {
|
|
// Chrome says: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
|
|
writer.Header().Set("Access-Control-Allow-Origin", origin)
|
|
}
|
|
if options.GetNormalizedSessionPlacement() == option.PlacementCookie ||
|
|
options.GetNormalizedSeqPlacement() == option.PlacementCookie ||
|
|
options.XPaddingPlacement == option.PlacementCookie ||
|
|
options.GetNormalizedUplinkDataPlacement() == option.PlacementCookie {
|
|
writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
|
}
|
|
if requestMethod == "OPTIONS" {
|
|
requestedMethod := requestHeader.Get("Access-Control-Request-Method")
|
|
if requestedMethod != "" {
|
|
writer.Header().Set("Access-Control-Allow-Methods", requestedMethod)
|
|
} else {
|
|
writer.Header().Set("Access-Control-Allow-Methods", "*")
|
|
}
|
|
requestedHeaders := requestHeader.Get("Access-Control-Request-Headers")
|
|
if requestedHeaders == "" {
|
|
writer.Header().Set("Access-Control-Allow-Headers", "*")
|
|
} else {
|
|
writer.Header().Set("Access-Control-Allow-Headers", requestedHeaders)
|
|
}
|
|
}
|
|
}
|
|
|
|
func GetRequestHeader(options *option.V2RayXHTTPBaseOptions) http.Header {
|
|
header := http.Header{}
|
|
for k, v := range options.Headers {
|
|
header.Add(k, v)
|
|
}
|
|
utils.TryDefaultHeadersWith(header, "fetch")
|
|
return header
|
|
}
|
|
|
|
func GetRequestHeaderWithPayload(payload []byte, options *option.V2RayXHTTPBaseOptions) http.Header {
|
|
header := GetRequestHeader(options)
|
|
key := options.UplinkDataKey
|
|
encodedData := base64.RawURLEncoding.EncodeToString(payload)
|
|
for i := 0; len(encodedData) > 0; i++ {
|
|
chunkSize := min(int(options.GetNormalizedUplinkChunkSize().Rand()), len(encodedData))
|
|
chunk := encodedData[:chunkSize]
|
|
encodedData = encodedData[chunkSize:]
|
|
headerKey := fmt.Sprintf("%s-%d", key, i)
|
|
header.Set(headerKey, chunk)
|
|
}
|
|
|
|
return header
|
|
}
|
|
|
|
func GetRequestCookiesWithPayload(payload []byte, options *option.V2RayXHTTPBaseOptions) []*http.Cookie {
|
|
cookies := []*http.Cookie{}
|
|
key := options.UplinkDataKey
|
|
encodedData := base64.RawURLEncoding.EncodeToString(payload)
|
|
for i := 0; len(encodedData) > 0; i++ {
|
|
chunkSize := min(int(options.GetNormalizedUplinkChunkSize().Rand()), len(encodedData))
|
|
chunk := encodedData[:chunkSize]
|
|
encodedData = encodedData[chunkSize:]
|
|
cookieName := fmt.Sprintf("%s_%d", key, i)
|
|
cookies = append(cookies, &http.Cookie{Name: cookieName, Value: chunk})
|
|
}
|
|
return cookies
|
|
}
|