mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 18:16:50 +08:00
Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4c82ef8a1b | ||
![]() |
30af792777 | ||
![]() |
33daa0c94b | ||
![]() |
70383c50cc | ||
![]() |
521d8ef6a1 | ||
![]() |
4531a7e228 | ||
![]() |
a342db3e28 | ||
![]() |
60553a6c26 | ||
![]() |
59f6685774 | ||
![]() |
4cb2a128db | ||
![]() |
8a4217fdf5 | ||
![]() |
7cf5ee8afd | ||
![]() |
2becdd6414 | ||
![]() |
edae38c620 | ||
![]() |
36f427f22b | ||
![]() |
c27d652d80 | ||
![]() |
0f65aa8ed8 | ||
![]() |
22535d8643 | ||
![]() |
529f206d33 | ||
![]() |
964859b4bc | ||
![]() |
8deb953aec | ||
![]() |
a0040f13dd | ||
![]() |
d8994b7603 |
28
.github/docker/Dockerfile
vendored
28
.github/docker/Dockerfile
vendored
@@ -2,21 +2,27 @@
|
|||||||
FROM --platform=$BUILDPLATFORM golang:alpine AS build
|
FROM --platform=$BUILDPLATFORM golang:alpine AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
ARG TARGETOS TARGETARCH
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||||
|
ADD https://github.com/v2fly/geoip/releases/latest/download/geoip.dat /v2fly/geoip.dat
|
||||||
|
ADD https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat /v2fly/geosite.dat
|
||||||
|
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat /loyalsoldier/geoip.dat
|
||||||
|
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat /loyalsoldier/geosite.dat
|
||||||
|
|
||||||
FROM --platform=${TARGETPLATFORM} alpine:latest
|
# chainguard/static contains only tzdata and ca-certificates, can be built with multiarch static binaries.
|
||||||
WORKDIR /root
|
FROM --platform=linux/amd64 chainguard/static:latest
|
||||||
|
WORKDIR /var/log/xray
|
||||||
COPY .github/docker/files/config.json /etc/xray/config.json
|
COPY .github/docker/files/config.json /etc/xray/config.json
|
||||||
COPY --from=build /src/xray /usr/bin/xray
|
COPY --from=build --chmod=755 /src/xray /usr/bin/xray
|
||||||
RUN set -ex \
|
|
||||||
&& apk add --no-cache tzdata ca-certificates \
|
|
||||||
&& mkdir -p /var/log/xray /usr/share/xray \
|
|
||||||
&& chmod +x /usr/bin/xray \
|
|
||||||
&& wget -O /usr/share/xray/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat \
|
|
||||||
&& wget -O /usr/share/xray/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
|
||||||
|
|
||||||
|
USER root
|
||||||
|
WORKDIR /root
|
||||||
VOLUME /etc/xray
|
VOLUME /etc/xray
|
||||||
ENV TZ=Asia/Shanghai
|
ARG TZ=Asia/Shanghai
|
||||||
|
ENV TZ=$TZ
|
||||||
ENTRYPOINT [ "/usr/bin/xray" ]
|
ENTRYPOINT [ "/usr/bin/xray" ]
|
||||||
CMD [ "-config", "/etc/xray/config.json" ]
|
CMD [ "-config", "/etc/xray/config.json" ]
|
||||||
|
|
||||||
|
ARG flavor=v2fly
|
||||||
|
COPY --from=build /$flavor /usr/share/xray
|
||||||
|
42
.github/workflows/docker.yml
vendored
42
.github/workflows/docker.yml
vendored
@@ -19,7 +19,20 @@ jobs:
|
|||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
||||||
flavor: latest=true
|
flavor: latest=auto
|
||||||
|
tags: |
|
||||||
|
type=sha
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
- name: Docker metadata Loyalsoldier flavor
|
||||||
|
id: loyalsoldier
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
||||||
|
flavor: |
|
||||||
|
latest=auto
|
||||||
|
suffix=-ls,onlatest=true
|
||||||
tags: |
|
tags: |
|
||||||
type=sha
|
type=sha
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
@@ -31,18 +44,33 @@ jobs:
|
|||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- # Add support for more platforms with QEMU (optional)
|
|
||||||
# https://github.com/docker/setup-qemu-action
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: |
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64
|
||||||
|
linux/loong64
|
||||||
|
linux/riscv64
|
||||||
|
provenance: false
|
||||||
file: .github/docker/Dockerfile
|
file: .github/docker/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
- name: Build and push Loyalsoldier flavor
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: |
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64
|
||||||
|
linux/loong64
|
||||||
|
linux/riscv64
|
||||||
|
provenance: false
|
||||||
|
file: .github/docker/Dockerfile
|
||||||
|
build-args: flavor=loyalsoldier
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ steps.loyalsoldier.outputs.tags }}
|
||||||
|
@@ -149,10 +149,6 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) {
|
|||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(template)
|
|
||||||
}
|
|
||||||
|
|
||||||
parentCert := template
|
parentCert := template
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
pCert, err := x509.ParseCertificate(parent.Certificate)
|
pCert, err := x509.ParseCertificate(parent.Certificate)
|
||||||
@@ -162,6 +158,17 @@ func Generate(parent *Certificate, opts ...Option) (*Certificate, error) {
|
|||||||
parentCert = pCert
|
parentCert = pCert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if parentCert.NotAfter.Before(template.NotAfter) {
|
||||||
|
template.NotAfter = parentCert.NotAfter
|
||||||
|
}
|
||||||
|
if parentCert.NotBefore.After(template.NotBefore) {
|
||||||
|
template.NotBefore = parentCert.NotBefore
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(template)
|
||||||
|
}
|
||||||
|
|
||||||
derBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, publicKey(selfKey), parentKey)
|
derBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, publicKey(selfKey), parentKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to create certificate").Base(err)
|
return nil, errors.New("failed to create certificate").Base(err)
|
||||||
|
@@ -21,7 +21,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
Version_x byte = 1
|
Version_x byte = 1
|
||||||
Version_y byte = 8
|
Version_y byte = 8
|
||||||
Version_z byte = 19
|
Version_z byte = 23
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -2,6 +2,7 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -242,3 +243,33 @@ func (v *User) Build() *protocol.User {
|
|||||||
Level: uint32(v.LevelByte),
|
Level: uint32(v.LevelByte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Int32Range deserializes from "1-2" or 1, so can deserialize from both int and number.
|
||||||
|
// Negative integers can be passed as sentinel values, but do not parse as ranges.
|
||||||
|
type Int32Range struct {
|
||||||
|
From int32
|
||||||
|
To int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Int32Range) UnmarshalJSON(data []byte) error {
|
||||||
|
var stringrange string
|
||||||
|
var rawint int32
|
||||||
|
if err := json.Unmarshal(data, &stringrange); err == nil {
|
||||||
|
pair := strings.SplitN(stringrange, "-", 2)
|
||||||
|
if len(pair) == 2 {
|
||||||
|
from, err := strconv.Atoi(pair[0])
|
||||||
|
to, err2 := strconv.Atoi(pair[1])
|
||||||
|
if err == nil && err2 == nil {
|
||||||
|
v.From = int32(from)
|
||||||
|
v.To = int32(to)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if err := json.Unmarshal(data, &rawint); err == nil {
|
||||||
|
v.From = rawint
|
||||||
|
v.To = rawint
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("Invalid integer range, expected either string of form \"1-2\" or plain integer.")
|
||||||
|
}
|
||||||
|
@@ -229,8 +229,10 @@ type SplitHTTPConfig struct {
|
|||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Headers map[string]string `json:"headers"`
|
Headers map[string]string `json:"headers"`
|
||||||
MaxConcurrentUploads int32 `json:"maxConcurrentUploads"`
|
ScMaxConcurrentPosts Int32Range `json:"scMaxConcurrentPosts"`
|
||||||
MaxUploadSize int32 `json:"maxUploadSize"`
|
ScMaxEachPostBytes Int32Range `json:"scMaxEachPostBytes"`
|
||||||
|
ScMinPostsIntervalMs Int32Range `json:"scMinPostsIntervalMs"`
|
||||||
|
NoSSEHeader bool `json:"noSSEHeader"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -244,11 +246,22 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
|||||||
c.Host = c.Headers["Host"]
|
c.Host = c.Headers["Host"]
|
||||||
}
|
}
|
||||||
config := &splithttp.Config{
|
config := &splithttp.Config{
|
||||||
Path: c.Path,
|
Path: c.Path,
|
||||||
Host: c.Host,
|
Host: c.Host,
|
||||||
Header: c.Headers,
|
Header: c.Headers,
|
||||||
MaxConcurrentUploads: c.MaxConcurrentUploads,
|
ScMaxConcurrentPosts: &splithttp.RandRangeConfig{
|
||||||
MaxUploadSize: c.MaxUploadSize,
|
From: c.ScMaxConcurrentPosts.From,
|
||||||
|
To: c.ScMaxConcurrentPosts.To,
|
||||||
|
},
|
||||||
|
ScMaxEachPostBytes: &splithttp.RandRangeConfig{
|
||||||
|
From: c.ScMaxEachPostBytes.From,
|
||||||
|
To: c.ScMaxEachPostBytes.To,
|
||||||
|
},
|
||||||
|
ScMinPostsIntervalMs: &splithttp.RandRangeConfig{
|
||||||
|
From: c.ScMinPostsIntervalMs.From,
|
||||||
|
To: c.ScMinPostsIntervalMs.To,
|
||||||
|
},
|
||||||
|
NoSSEHeader: c.NoSSEHeader,
|
||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
@@ -372,6 +385,7 @@ type TLSCertConfig struct {
|
|||||||
Usage string `json:"usage"`
|
Usage string `json:"usage"`
|
||||||
OcspStapling uint64 `json:"ocspStapling"`
|
OcspStapling uint64 `json:"ocspStapling"`
|
||||||
OneTimeLoading bool `json:"oneTimeLoading"`
|
OneTimeLoading bool `json:"oneTimeLoading"`
|
||||||
|
BuildChain bool `json:"buildChain"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -410,6 +424,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
|||||||
certificate.OneTimeLoading = c.OneTimeLoading
|
certificate.OneTimeLoading = c.OneTimeLoading
|
||||||
}
|
}
|
||||||
certificate.OcspStapling = c.OcspStapling
|
certificate.OcspStapling = c.OcspStapling
|
||||||
|
certificate.BuildChain = c.BuildChain
|
||||||
|
|
||||||
return certificate, nil
|
return certificate, nil
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
var useSplice bool
|
var useSplice bool
|
||||||
@@ -225,9 +226,16 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
writeConn = inbound.Conn
|
writeConn = inbound.Conn
|
||||||
inTimer = inbound.Timer
|
inTimer = inbound.Timer
|
||||||
}
|
}
|
||||||
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
|
if !isTLSConn(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
|
||||||
|
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var reader buf.Reader
|
||||||
|
if destination.Network == net.Network_TCP {
|
||||||
|
reader = buf.NewReader(conn)
|
||||||
|
} else {
|
||||||
|
reader = NewPacketReader(conn, UDPOverride)
|
||||||
}
|
}
|
||||||
reader := NewPacketReader(conn, UDPOverride)
|
|
||||||
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
||||||
return errors.New("failed to process response").Base(err)
|
return errors.New("failed to process response").Base(err)
|
||||||
}
|
}
|
||||||
@@ -245,6 +253,19 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isTLSConn(conn stat.Connection) bool {
|
||||||
|
if conn != nil {
|
||||||
|
statConn, ok := conn.(*stat.CounterConnection)
|
||||||
|
if ok {
|
||||||
|
conn = statConn.Connection
|
||||||
|
}
|
||||||
|
if _, ok := conn.(*tls.Conn); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
|
func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
|
||||||
iConn := conn
|
iConn := conn
|
||||||
statConn, ok := iConn.(*stat.CounterConnection)
|
statConn, ok := iConn.(*stat.CounterConnection)
|
||||||
|
@@ -100,7 +100,7 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer
|
// bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer
|
||||||
bind := &netBindClient{
|
h.bind = &netBindClient{
|
||||||
netBind: netBind{
|
netBind: netBind{
|
||||||
dns: h.dns,
|
dns: h.dns,
|
||||||
dnsOption: dns.IPOption{
|
dnsOption: dns.IPOption{
|
||||||
@@ -115,15 +115,14 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = bind.Close()
|
_ = h.bind.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
h.net, err = h.makeVirtualTun(bind)
|
h.net, err = h.makeVirtualTun()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("failed to create virtual tun interface").Base(err)
|
return errors.New("failed to create virtual tun interface").Base(err)
|
||||||
}
|
}
|
||||||
h.bind = bind
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,16 +237,16 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// creates a tun interface on netstack given a configuration
|
// creates a tun interface on netstack given a configuration
|
||||||
func (h *Handler) makeVirtualTun(bind *netBindClient) (Tunnel, error) {
|
func (h *Handler) makeVirtualTun() (Tunnel, error) {
|
||||||
t, err := h.conf.createTun()(h.endpoints, int(h.conf.Mtu), nil)
|
t, err := h.conf.createTun()(h.endpoints, int(h.conf.Mtu), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bind.dnsOption.IPv4Enable = h.hasIPv4
|
h.bind.dnsOption.IPv4Enable = h.hasIPv4
|
||||||
bind.dnsOption.IPv6Enable = h.hasIPv6
|
h.bind.dnsOption.IPv6Enable = h.hasIPv6
|
||||||
|
|
||||||
if err = t.BuildDevice(h.createIPCRequest(bind, h.conf), bind); err != nil {
|
if err = t.BuildDevice(h.createIPCRequest(), h.bind); err != nil {
|
||||||
_ = t.Close()
|
_ = t.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -255,17 +254,17 @@ func (h *Handler) makeVirtualTun(bind *netBindClient) (Tunnel, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// serialize the config into an IPC request
|
// serialize the config into an IPC request
|
||||||
func (h *Handler) createIPCRequest(bind *netBindClient, conf *DeviceConfig) string {
|
func (h *Handler) createIPCRequest() string {
|
||||||
var request strings.Builder
|
var request strings.Builder
|
||||||
|
|
||||||
request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey))
|
request.WriteString(fmt.Sprintf("private_key=%s\n", h.conf.SecretKey))
|
||||||
|
|
||||||
if !conf.IsClient {
|
if !h.conf.IsClient {
|
||||||
// placeholder, we'll handle actual port listening on Xray
|
// placeholder, we'll handle actual port listening on Xray
|
||||||
request.WriteString("listen_port=1337\n")
|
request.WriteString("listen_port=1337\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, peer := range conf.Peers {
|
for _, peer := range h.conf.Peers {
|
||||||
if peer.PublicKey != "" {
|
if peer.PublicKey != "" {
|
||||||
request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey))
|
request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey))
|
||||||
}
|
}
|
||||||
@@ -280,7 +279,7 @@ func (h *Handler) createIPCRequest(bind *netBindClient, conf *DeviceConfig) stri
|
|||||||
}
|
}
|
||||||
addr := net.ParseAddress(address)
|
addr := net.ParseAddress(address)
|
||||||
if addr.Family().IsDomain() {
|
if addr.Family().IsDomain() {
|
||||||
dialerIp := bind.dialer.DestIpAddress()
|
dialerIp := h.bind.dialer.DestIpAddress()
|
||||||
if dialerIp != nil {
|
if dialerIp != nil {
|
||||||
addr = net.ParseAddress(dialerIp.String())
|
addr = net.ParseAddress(dialerIp.String())
|
||||||
errors.LogInfo(h.bind.ctx, "createIPCRequest use dialer dest ip: ", addr)
|
errors.LogInfo(h.bind.ctx, "createIPCRequest use dialer dest ip: ", addr)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.34.2
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v5.27.2
|
// protoc v5.27.0
|
||||||
// source: transport/internet/config.proto
|
// source: transport/internet/config.proto
|
||||||
|
|
||||||
package internet
|
package internet
|
||||||
@@ -863,7 +863,7 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
|
|||||||
|
|
||||||
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||||
var file_transport_internet_config_proto_goTypes = []any{
|
var file_transport_internet_config_proto_goTypes = []interface{}{
|
||||||
(TransportProtocol)(0), // 0: xray.transport.internet.TransportProtocol
|
(TransportProtocol)(0), // 0: xray.transport.internet.TransportProtocol
|
||||||
(DomainStrategy)(0), // 1: xray.transport.internet.DomainStrategy
|
(DomainStrategy)(0), // 1: xray.transport.internet.DomainStrategy
|
||||||
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
||||||
@@ -897,7 +897,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
if !protoimpl.UnsafeEnabled {
|
||||||
file_transport_internet_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
file_transport_internet_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*TransportConfig); i {
|
switch v := v.(*TransportConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -909,7 +909,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_transport_internet_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
file_transport_internet_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*StreamConfig); i {
|
switch v := v.(*StreamConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -921,7 +921,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_transport_internet_config_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
file_transport_internet_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*ProxyConfig); i {
|
switch v := v.(*ProxyConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -933,7 +933,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_transport_internet_config_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
file_transport_internet_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*CustomSockopt); i {
|
switch v := v.(*CustomSockopt); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -945,7 +945,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_transport_internet_config_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
file_transport_internet_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*SocketConfig); i {
|
switch v := v.(*SocketConfig); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@@ -58,7 +58,6 @@ func Test_listenHTTPUpgradeAndDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
common.Must(conn.Close())
|
common.Must(conn.Close())
|
||||||
<-time.After(time.Second * 5)
|
|
||||||
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
_, err = conn.Write([]byte("Test connection 2"))
|
_, err = conn.Write([]byte("Test connection 2"))
|
||||||
@@ -118,7 +117,6 @@ func Test_listenHTTPUpgradeAndDialWithHeaders(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
common.Must(conn.Close())
|
common.Must(conn.Close())
|
||||||
<-time.After(time.Second * 5)
|
|
||||||
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
_, err = conn.Write([]byte("Test connection 2"))
|
_, err = conn.Write([]byte("Test connection 2"))
|
||||||
|
@@ -94,6 +94,10 @@ func (c *DefaultDialerClient) OpenDownload(ctx context.Context, baseURL string)
|
|||||||
gotDownResponse.Close()
|
gotDownResponse.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if c.isH3 {
|
||||||
|
gotConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// we want to block Dial until we know the remote address of the server,
|
// we want to block Dial until we know the remote address of the server,
|
||||||
// for logging purposes
|
// for logging purposes
|
||||||
<-gotConn.Wait()
|
<-gotConn.Wait()
|
||||||
@@ -113,10 +117,10 @@ func (c *DefaultDialerClient) OpenDownload(ctx context.Context, baseURL string)
|
|||||||
|
|
||||||
func (c *DefaultDialerClient) SendUploadRequest(ctx context.Context, url string, payload io.ReadWriteCloser, contentLength int64) error {
|
func (c *DefaultDialerClient) SendUploadRequest(ctx context.Context, url string, payload io.ReadWriteCloser, contentLength int64) error {
|
||||||
req, err := http.NewRequest("POST", url, payload)
|
req, err := http.NewRequest("POST", url, payload)
|
||||||
req.ContentLength = contentLength
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
req.ContentLength = contentLength
|
||||||
req.Header = c.transportConfig.GetRequestHeader()
|
req.Header = c.transportConfig.GetRequestHeader()
|
||||||
|
|
||||||
if c.isH2 || c.isH3 {
|
if c.isH2 || c.isH3 {
|
||||||
|
@@ -1,24 +1,31 @@
|
|||||||
package splithttp
|
package splithttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Config) GetNormalizedPath() string {
|
func (c *Config) GetNormalizedPath(addPath string, addQuery bool) string {
|
||||||
path := c.Path
|
pathAndQuery := strings.SplitN(c.Path, "?", 2)
|
||||||
if path == "" {
|
path := pathAndQuery[0]
|
||||||
path = "/"
|
query := ""
|
||||||
|
if len(pathAndQuery) > 1 && addQuery {
|
||||||
|
query = "?" + pathAndQuery[1]
|
||||||
}
|
}
|
||||||
if path[0] != '/' {
|
|
||||||
|
if path == "" || path[0] != '/' {
|
||||||
path = "/" + path
|
path = "/" + path
|
||||||
}
|
}
|
||||||
if path[len(path)-1] != '/' {
|
if path[len(path)-1] != '/' {
|
||||||
path = path + "/"
|
path = path + "/"
|
||||||
}
|
}
|
||||||
return path
|
|
||||||
|
return path + addPath + query
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetRequestHeader() http.Header {
|
func (c *Config) GetRequestHeader() http.Header {
|
||||||
@@ -29,20 +36,37 @@ func (c *Config) GetRequestHeader() http.Header {
|
|||||||
return header
|
return header
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetNormalizedMaxConcurrentUploads() int32 {
|
func (c *Config) GetNormalizedScMaxConcurrentPosts() RandRangeConfig {
|
||||||
if c.MaxConcurrentUploads == 0 {
|
if c.ScMaxConcurrentPosts == nil || c.ScMaxConcurrentPosts.To == 0 {
|
||||||
return 10
|
return RandRangeConfig{
|
||||||
|
From: 100,
|
||||||
|
To: 100,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.MaxConcurrentUploads
|
return *c.ScMaxConcurrentPosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetNormalizedMaxUploadSize() int32 {
|
func (c *Config) GetNormalizedScMaxEachPostBytes() RandRangeConfig {
|
||||||
if c.MaxUploadSize == 0 {
|
if c.ScMaxEachPostBytes == nil || c.ScMaxEachPostBytes.To == 0 {
|
||||||
return 1000000
|
return RandRangeConfig{
|
||||||
|
From: 1000000,
|
||||||
|
To: 1000000,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.MaxUploadSize
|
return *c.ScMaxEachPostBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetNormalizedScMinPostsIntervalMs() RandRangeConfig {
|
||||||
|
if c.ScMinPostsIntervalMs == nil || c.ScMinPostsIntervalMs.To == 0 {
|
||||||
|
return RandRangeConfig{
|
||||||
|
From: 30,
|
||||||
|
To: 30,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *c.ScMinPostsIntervalMs
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -50,3 +74,11 @@ func init() {
|
|||||||
return new(Config)
|
return new(Config)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c RandRangeConfig) roll() int32 {
|
||||||
|
if c.From == c.To {
|
||||||
|
return c.From
|
||||||
|
}
|
||||||
|
bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(c.To-c.From)))
|
||||||
|
return c.From + int32(bigInt.Int64())
|
||||||
|
}
|
||||||
|
@@ -28,8 +28,10 @@ type Config struct {
|
|||||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||||
Header map[string]string `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
Header map[string]string `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
MaxConcurrentUploads int32 `protobuf:"varint,4,opt,name=maxConcurrentUploads,proto3" json:"maxConcurrentUploads,omitempty"`
|
ScMaxConcurrentPosts *RandRangeConfig `protobuf:"bytes,4,opt,name=scMaxConcurrentPosts,proto3" json:"scMaxConcurrentPosts,omitempty"`
|
||||||
MaxUploadSize int32 `protobuf:"varint,5,opt,name=maxUploadSize,proto3" json:"maxUploadSize,omitempty"`
|
ScMaxEachPostBytes *RandRangeConfig `protobuf:"bytes,5,opt,name=scMaxEachPostBytes,proto3" json:"scMaxEachPostBytes,omitempty"`
|
||||||
|
ScMinPostsIntervalMs *RandRangeConfig `protobuf:"bytes,6,opt,name=scMinPostsIntervalMs,proto3" json:"scMinPostsIntervalMs,omitempty"`
|
||||||
|
NoSSEHeader bool `protobuf:"varint,7,opt,name=noSSEHeader,proto3" json:"noSSEHeader,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -85,16 +87,85 @@ func (x *Config) GetHeader() map[string]string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetMaxConcurrentUploads() int32 {
|
func (x *Config) GetScMaxConcurrentPosts() *RandRangeConfig {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.MaxConcurrentUploads
|
return x.ScMaxConcurrentPosts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetScMaxEachPostBytes() *RandRangeConfig {
|
||||||
|
if x != nil {
|
||||||
|
return x.ScMaxEachPostBytes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetScMinPostsIntervalMs() *RandRangeConfig {
|
||||||
|
if x != nil {
|
||||||
|
return x.ScMinPostsIntervalMs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetNoSSEHeader() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.NoSSEHeader
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type RandRangeConfig struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
From int32 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"`
|
||||||
|
To int32 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RandRangeConfig) Reset() {
|
||||||
|
*x = RandRangeConfig{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_transport_internet_splithttp_config_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RandRangeConfig) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*RandRangeConfig) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *RandRangeConfig) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_splithttp_config_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use RandRangeConfig.ProtoReflect.Descriptor instead.
|
||||||
|
func (*RandRangeConfig) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_splithttp_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RandRangeConfig) GetFrom() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.From
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetMaxUploadSize() int32 {
|
func (x *RandRangeConfig) GetTo() int32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.MaxUploadSize
|
return x.To
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -106,8 +177,8 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
|
|||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63,
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61,
|
||||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x22, 0x94,
|
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x22, 0x86,
|
||||||
0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73,
|
0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73,
|
||||||
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a,
|
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a,
|
||||||
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||||
0x68, 0x12, 0x4d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28,
|
0x68, 0x12, 0x4d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28,
|
||||||
@@ -115,25 +186,43 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
|
|||||||
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69,
|
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69,
|
||||||
0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61,
|
0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61,
|
||||||
0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
|
0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
|
||||||
0x12, 0x32, 0x0a, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
|
0x12, 0x66, 0x0a, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
|
||||||
0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14,
|
0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32,
|
||||||
0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x6c,
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
|
||||||
0x6f, 0x61, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f, 0x61,
|
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
|
||||||
0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78,
|
0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x39, 0x0a, 0x0b, 0x48, 0x65,
|
0x69, 0x67, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
|
||||||
0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x5a, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x55,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
|
0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
0x32, 0x32, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||||
0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x85, 0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74,
|
||||||
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
|
0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f,
|
||||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50,
|
0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42,
|
||||||
0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
0x79, 0x74, 0x65, 0x73, 0x12, 0x64, 0x0a, 0x13, 0x6d, 0x69, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61,
|
||||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61,
|
0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f,
|
0x0b, 0x32, 0x32, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
||||||
0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79,
|
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69,
|
||||||
0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72,
|
0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43,
|
||||||
0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x6f,
|
||||||
|
0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
|
0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x39, 0x0a, 0x0b,
|
||||||
|
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||||
|
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
|
||||||
|
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
|
||||||
|
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x35, 0x0a, 0x0f, 0x52, 0x61, 0x6e, 0x64, 0x52,
|
||||||
|
0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72,
|
||||||
|
0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e,
|
||||||
|
0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x74, 0x6f, 0x42, 0x85,
|
||||||
|
0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||||
|
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73,
|
||||||
|
0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68,
|
||||||
|
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||||
|
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
||||||
|
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
|
||||||
|
0x74, 0x70, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
|
||||||
|
0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c,
|
||||||
|
0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -148,18 +237,22 @@ func file_transport_internet_splithttp_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_transport_internet_splithttp_config_proto_rawDescData
|
return file_transport_internet_splithttp_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_transport_internet_splithttp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
var file_transport_internet_splithttp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||||
var file_transport_internet_splithttp_config_proto_goTypes = []interface{}{
|
var file_transport_internet_splithttp_config_proto_goTypes = []interface{}{
|
||||||
(*Config)(nil), // 0: xray.transport.internet.splithttp.Config
|
(*Config)(nil), // 0: xray.transport.internet.splithttp.Config
|
||||||
nil, // 1: xray.transport.internet.splithttp.Config.HeaderEntry
|
(*RandRangeConfig)(nil), // 1: xray.transport.internet.splithttp.RandRangeConfig
|
||||||
|
nil, // 2: xray.transport.internet.splithttp.Config.HeaderEntry
|
||||||
}
|
}
|
||||||
var file_transport_internet_splithttp_config_proto_depIdxs = []int32{
|
var file_transport_internet_splithttp_config_proto_depIdxs = []int32{
|
||||||
1, // 0: xray.transport.internet.splithttp.Config.header:type_name -> xray.transport.internet.splithttp.Config.HeaderEntry
|
2, // 0: xray.transport.internet.splithttp.Config.header:type_name -> xray.transport.internet.splithttp.Config.HeaderEntry
|
||||||
1, // [1:1] is the sub-list for method output_type
|
1, // 1: xray.transport.internet.splithttp.Config.scMaxConcurrentPosts:type_name -> xray.transport.internet.splithttp.RandRangeConfig
|
||||||
1, // [1:1] is the sub-list for method input_type
|
1, // 2: xray.transport.internet.splithttp.Config.scMaxEachPostBytes:type_name -> xray.transport.internet.splithttp.RandRangeConfig
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
1, // 3: xray.transport.internet.splithttp.Config.scMinPostsIntervalMs:type_name -> xray.transport.internet.splithttp.RandRangeConfig
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
4, // [4:4] is the sub-list for method output_type
|
||||||
0, // [0:1] is the sub-list for field type_name
|
4, // [4:4] is the sub-list for method input_type
|
||||||
|
4, // [4:4] is the sub-list for extension type_name
|
||||||
|
4, // [4:4] is the sub-list for extension extendee
|
||||||
|
0, // [0:4] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_transport_internet_splithttp_config_proto_init() }
|
func init() { file_transport_internet_splithttp_config_proto_init() }
|
||||||
@@ -180,6 +273,18 @@ func file_transport_internet_splithttp_config_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file_transport_internet_splithttp_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*RandRangeConfig); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
@@ -187,7 +292,7 @@ func file_transport_internet_splithttp_config_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_transport_internet_splithttp_config_proto_rawDesc,
|
RawDescriptor: file_transport_internet_splithttp_config_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 2,
|
NumMessages: 3,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
@@ -10,6 +10,13 @@ message Config {
|
|||||||
string host = 1;
|
string host = 1;
|
||||||
string path = 2;
|
string path = 2;
|
||||||
map<string, string> header = 3;
|
map<string, string> header = 3;
|
||||||
int32 maxConcurrentUploads = 4;
|
RandRangeConfig scMaxConcurrentPosts = 4;
|
||||||
int32 maxUploadSize = 5;
|
RandRangeConfig scMaxEachPostBytes = 5;
|
||||||
|
RandRangeConfig scMinPostsIntervalMs = 6;
|
||||||
|
bool noSSEHeader = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RandRangeConfig {
|
||||||
|
int32 from = 1;
|
||||||
|
int32 to = 2;
|
||||||
}
|
}
|
||||||
|
51
transport/internet/splithttp/config_test.go
Normal file
51
transport/internet/splithttp/config_test.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package splithttp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_GetNormalizedPath(t *testing.T) {
|
||||||
|
c := Config{
|
||||||
|
Path: "/?world",
|
||||||
|
}
|
||||||
|
|
||||||
|
path := c.GetNormalizedPath("hello", true)
|
||||||
|
if path != "/hello?world" {
|
||||||
|
t.Error("Unexpected: ", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_GetNormalizedPath2(t *testing.T) {
|
||||||
|
c := Config{
|
||||||
|
Path: "?world",
|
||||||
|
}
|
||||||
|
|
||||||
|
path := c.GetNormalizedPath("hello", true)
|
||||||
|
if path != "/hello?world" {
|
||||||
|
t.Error("Unexpected: ", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_GetNormalizedPath3(t *testing.T) {
|
||||||
|
c := Config{
|
||||||
|
Path: "hello?world",
|
||||||
|
}
|
||||||
|
|
||||||
|
path := c.GetNormalizedPath("", true)
|
||||||
|
if path != "/hello/?world" {
|
||||||
|
t.Error("Unexpected: ", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_GetNormalizedPath4(t *testing.T) {
|
||||||
|
c := Config{
|
||||||
|
Path: "hello?world",
|
||||||
|
}
|
||||||
|
|
||||||
|
path := c.GetNormalizedPath("", false)
|
||||||
|
if path != "/hello/" {
|
||||||
|
t.Error("Unexpected: ", path)
|
||||||
|
}
|
||||||
|
}
|
@@ -41,6 +41,10 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
return &BrowserDialerClient{}
|
return &BrowserDialerClient{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
|
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
|
||||||
|
isH3 := tlsConfig != nil && (len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3")
|
||||||
|
|
||||||
globalDialerAccess.Lock()
|
globalDialerAccess.Lock()
|
||||||
defer globalDialerAccess.Unlock()
|
defer globalDialerAccess.Unlock()
|
||||||
|
|
||||||
@@ -48,14 +52,13 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
globalDialerMap = make(map[dialerConf]DialerClient)
|
globalDialerMap = make(map[dialerConf]DialerClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isH3 {
|
||||||
|
dest.Network = net.Network_UDP
|
||||||
|
}
|
||||||
if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found {
|
if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found {
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
|
||||||
isH2 := tlsConfig != nil && !(len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "http/1.1")
|
|
||||||
isH3 := tlsConfig != nil && (len(tlsConfig.NextProtocol) == 1 && tlsConfig.NextProtocol[0] == "h3")
|
|
||||||
|
|
||||||
var gotlsConfig *gotls.Config
|
var gotlsConfig *gotls.Config
|
||||||
|
|
||||||
if tlsConfig != nil {
|
if tlsConfig != nil {
|
||||||
@@ -86,26 +89,39 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
var uploadTransport http.RoundTripper
|
var uploadTransport http.RoundTripper
|
||||||
|
|
||||||
if isH3 {
|
if isH3 {
|
||||||
dest.Network = net.Network_UDP
|
|
||||||
quicConfig := &quic.Config{
|
|
||||||
HandshakeIdleTimeout: 10 * time.Second,
|
|
||||||
MaxIdleTimeout: 90 * time.Second,
|
|
||||||
KeepAlivePeriod: 3 * time.Second,
|
|
||||||
Allow0RTT: true,
|
|
||||||
}
|
|
||||||
roundTripper := &http3.RoundTripper{
|
roundTripper := &http3.RoundTripper{
|
||||||
TLSClientConfig: gotlsConfig,
|
TLSClientConfig: gotlsConfig,
|
||||||
QUICConfig: quicConfig,
|
|
||||||
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||||
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
|
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp", conn.RemoteAddr().String())
|
|
||||||
if err != nil {
|
var udpConn *net.UDPConn
|
||||||
return nil, err
|
var udpAddr *net.UDPAddr
|
||||||
|
|
||||||
|
switch c := conn.(type) {
|
||||||
|
case *internet.PacketConnWrapper:
|
||||||
|
var ok bool
|
||||||
|
udpConn, ok = c.Conn.(*net.UDPConn)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("PacketConnWrapper does not contain a UDP connection")
|
||||||
|
}
|
||||||
|
udpAddr, err = net.ResolveUDPAddr("udp", c.Dest.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case *net.UDPConn:
|
||||||
|
udpConn = c
|
||||||
|
udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported connection type: %T", conn)
|
||||||
}
|
}
|
||||||
return quic.DialEarly(ctx, conn.(*internet.PacketConnWrapper).Conn.(*net.UDPConn), udpAddr, tlsCfg, cfg)
|
|
||||||
|
return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
downloadTransport = roundTripper
|
downloadTransport = roundTripper
|
||||||
@@ -165,8 +181,9 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
|
transportConfiguration := streamSettings.ProtocolSettings.(*Config)
|
||||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||||
|
|
||||||
maxConcurrentUploads := transportConfiguration.GetNormalizedMaxConcurrentUploads()
|
scMaxConcurrentPosts := transportConfiguration.GetNormalizedScMaxConcurrentPosts()
|
||||||
maxUploadSize := transportConfiguration.GetNormalizedMaxUploadSize()
|
scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
|
||||||
|
scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()
|
||||||
|
|
||||||
if tlsConfig != nil {
|
if tlsConfig != nil {
|
||||||
requestURL.Scheme = "https"
|
requestURL.Scheme = "https"
|
||||||
@@ -177,20 +194,21 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
if requestURL.Host == "" {
|
if requestURL.Host == "" {
|
||||||
requestURL.Host = dest.NetAddr()
|
requestURL.Host = dest.NetAddr()
|
||||||
}
|
}
|
||||||
requestURL.Path = transportConfiguration.GetNormalizedPath()
|
|
||||||
|
sessionIdUuid := uuid.New()
|
||||||
|
requestURL.Path = transportConfiguration.GetNormalizedPath(sessionIdUuid.String(), true)
|
||||||
|
baseURL := requestURL.String()
|
||||||
|
|
||||||
httpClient := getHTTPClient(ctx, dest, streamSettings)
|
httpClient := getHTTPClient(ctx, dest, streamSettings)
|
||||||
|
|
||||||
sessionIdUuid := uuid.New()
|
uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(scMaxEachPostBytes.roll()))
|
||||||
sessionId := sessionIdUuid.String()
|
|
||||||
baseURL := requestURL.String() + sessionId
|
|
||||||
|
|
||||||
uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize))
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
requestsLimiter := semaphore.New(int(maxConcurrentUploads))
|
requestsLimiter := semaphore.New(int(scMaxConcurrentPosts.roll()))
|
||||||
var requestCounter int64
|
var requestCounter int64
|
||||||
|
|
||||||
|
lastWrite := time.Now()
|
||||||
|
|
||||||
// by offloading the uploads into a buffered pipe, multiple conn.Write
|
// by offloading the uploads into a buffered pipe, multiple conn.Write
|
||||||
// calls get automatically batched together into larger POST requests.
|
// calls get automatically batched together into larger POST requests.
|
||||||
// without batching, bandwidth is extremely limited.
|
// without batching, bandwidth is extremely limited.
|
||||||
@@ -221,6 +239,14 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if scMinPostsIntervalMs.From > 0 {
|
||||||
|
roll := time.Duration(scMinPostsIntervalMs.roll()) * time.Millisecond
|
||||||
|
if time.Since(lastWrite) < roll {
|
||||||
|
time.Sleep(roll)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWrite = time.Now()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@@ -11,6 +11,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
"github.com/quic-go/quic-go/http3"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -24,6 +26,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type requestHandler struct {
|
type requestHandler struct {
|
||||||
|
config *Config
|
||||||
host string
|
host string
|
||||||
path string
|
path string
|
||||||
ln *Listener
|
ln *Listener
|
||||||
@@ -73,7 +76,7 @@ func (h *requestHandler) upsertSession(sessionId string) *httpSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s := &httpSession{
|
s := &httpSession{
|
||||||
uploadQueue: NewUploadQueue(int(2 * h.ln.config.GetNormalizedMaxConcurrentUploads())),
|
uploadQueue: NewUploadQueue(int(h.ln.config.GetNormalizedScMaxConcurrentPosts().To)),
|
||||||
isFullyConnected: done.New(),
|
isFullyConnected: done.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +123,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentSession := h.upsertSession(sessionId)
|
currentSession := h.upsertSession(sessionId)
|
||||||
|
scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To)
|
||||||
|
|
||||||
if request.Method == "POST" {
|
if request.Method == "POST" {
|
||||||
seq := ""
|
seq := ""
|
||||||
@@ -134,6 +138,13 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload, err := io.ReadAll(request.Body)
|
payload, err := io.ReadAll(request.Body)
|
||||||
|
|
||||||
|
if len(payload) > scMaxEachPostBytes {
|
||||||
|
errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request had size ", len(payload), ". Adjust scMaxEachPostBytes on the server to be at least as large as client.")
|
||||||
|
writer.WriteHeader(http.StatusRequestEntityTooLarge)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogInfoInner(context.Background(), err, "failed to upload")
|
errors.LogInfoInner(context.Background(), err, "failed to upload")
|
||||||
writer.WriteHeader(http.StatusInternalServerError)
|
writer.WriteHeader(http.StatusInternalServerError)
|
||||||
@@ -172,8 +183,10 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
|||||||
|
|
||||||
// magic header instructs nginx + apache to not buffer response body
|
// magic header instructs nginx + apache to not buffer response body
|
||||||
writer.Header().Set("X-Accel-Buffering", "no")
|
writer.Header().Set("X-Accel-Buffering", "no")
|
||||||
// magic header to make the HTTP middle box consider this as SSE to disable buffer
|
if !h.config.NoSSEHeader {
|
||||||
writer.Header().Set("Content-Type", "text/event-stream")
|
// magic header to make the HTTP middle box consider this as SSE to disable buffer
|
||||||
|
writer.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
}
|
||||||
|
|
||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
// send a chunk immediately to enable CDN streaming.
|
// send a chunk immediately to enable CDN streaming.
|
||||||
@@ -233,10 +246,13 @@ func (c *httpResponseBodyWriter) Close() error {
|
|||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
server http.Server
|
server http.Server
|
||||||
listener net.Listener
|
h3server *http3.Server
|
||||||
config *Config
|
listener net.Listener
|
||||||
addConn internet.ConnHandler
|
h3listener *quic.EarlyListener
|
||||||
|
config *Config
|
||||||
|
addConn internet.ConnHandler
|
||||||
|
isH3 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
|
func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
|
||||||
@@ -253,6 +269,17 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
var err error
|
var err error
|
||||||
var localAddr = gonet.TCPAddr{}
|
var localAddr = gonet.TCPAddr{}
|
||||||
|
handler := &requestHandler{
|
||||||
|
config: shSettings,
|
||||||
|
host: shSettings.Host,
|
||||||
|
path: shSettings.GetNormalizedPath("", false),
|
||||||
|
ln: l,
|
||||||
|
sessionMu: &sync.Mutex{},
|
||||||
|
sessions: sync.Map{},
|
||||||
|
localAddr: localAddr,
|
||||||
|
}
|
||||||
|
tlsConfig := getTLSConfig(streamSettings)
|
||||||
|
l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
|
||||||
|
|
||||||
if port == net.Port(0) { // unix
|
if port == net.Port(0) { // unix
|
||||||
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
||||||
@@ -263,6 +290,29 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
return nil, errors.New("failed to listen unix domain socket(for SH) on ", address).Base(err)
|
return nil, errors.New("failed to listen unix domain socket(for SH) on ", address).Base(err)
|
||||||
}
|
}
|
||||||
errors.LogInfo(ctx, "listening unix domain socket(for SH) on ", address)
|
errors.LogInfo(ctx, "listening unix domain socket(for SH) on ", address)
|
||||||
|
} else if l.isH3 { // quic
|
||||||
|
Conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
|
||||||
|
IP: address.IP(),
|
||||||
|
Port: int(port),
|
||||||
|
}, streamSettings.SocketSettings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to listen UDP(for SH3) on ", address, ":", port).Base(err)
|
||||||
|
}
|
||||||
|
h3listener, err := quic.ListenEarly(Conn, tlsConfig, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to listen QUIC(for SH3) on ", address, ":", port).Base(err)
|
||||||
|
}
|
||||||
|
l.h3listener = h3listener
|
||||||
|
errors.LogInfo(ctx, "listening QUIC(for SH3) on ", address, ":", port)
|
||||||
|
|
||||||
|
l.h3server = &http3.Server{
|
||||||
|
Handler: handler,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := l.h3server.ServeListener(l.h3listener); err != nil {
|
||||||
|
errors.LogWarningInner(ctx, err, "failed to serve http3 for splithttp")
|
||||||
|
}
|
||||||
|
}()
|
||||||
} else { // tcp
|
} else { // tcp
|
||||||
localAddr = gonet.TCPAddr{
|
localAddr = gonet.TCPAddr{
|
||||||
IP: address.IP(),
|
IP: address.IP(),
|
||||||
@@ -278,37 +328,29 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
|
|||||||
errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port)
|
errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
// tcp/unix (h1/h2)
|
||||||
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
|
if listener != nil {
|
||||||
listener = tls.NewListener(listener, tlsConfig)
|
if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||||
|
if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
|
||||||
|
listener = tls.NewListener(listener, tlsConfig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
handler := &requestHandler{
|
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
||||||
host: shSettings.Host,
|
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
||||||
path: shSettings.GetNormalizedPath(),
|
l.listener = listener
|
||||||
ln: l,
|
l.server = http.Server{
|
||||||
sessionMu: &sync.Mutex{},
|
Handler: h2cHandler,
|
||||||
sessions: sync.Map{},
|
ReadHeaderTimeout: time.Second * 4,
|
||||||
localAddr: localAddr,
|
MaxHeaderBytes: 8192,
|
||||||
}
|
|
||||||
|
|
||||||
// h2cHandler can handle both plaintext HTTP/1.1 and h2c
|
|
||||||
h2cHandler := h2c.NewHandler(handler, &http2.Server{})
|
|
||||||
|
|
||||||
l.listener = listener
|
|
||||||
|
|
||||||
l.server = http.Server{
|
|
||||||
Handler: h2cHandler,
|
|
||||||
ReadHeaderTimeout: time.Second * 4,
|
|
||||||
MaxHeaderBytes: 8192,
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := l.server.Serve(l.listener); err != nil {
|
|
||||||
errors.LogWarningInner(ctx, err, "failed to serve http for splithttp")
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
go func() {
|
||||||
|
if err := l.server.Serve(l.listener); err != nil {
|
||||||
|
errors.LogWarningInner(ctx, err, "failed to serve http for splithttp")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
return l, err
|
return l, err
|
||||||
}
|
}
|
||||||
@@ -320,9 +362,22 @@ func (ln *Listener) Addr() net.Addr {
|
|||||||
|
|
||||||
// Close implements net.Listener.Close().
|
// Close implements net.Listener.Close().
|
||||||
func (ln *Listener) Close() error {
|
func (ln *Listener) Close() error {
|
||||||
return ln.listener.Close()
|
if ln.h3server != nil {
|
||||||
|
if err := ln.h3server.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if ln.listener != nil {
|
||||||
|
return ln.listener.Close()
|
||||||
|
}
|
||||||
|
return errors.New("listener does not have an HTTP/3 server or a net.listener")
|
||||||
|
}
|
||||||
|
func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *tls.Config {
|
||||||
|
config := v2tls.ConfigFromStreamSettings(streamSettings)
|
||||||
|
if config == nil {
|
||||||
|
return &tls.Config{}
|
||||||
|
}
|
||||||
|
return config.GetTLSConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
|
common.Must(internet.RegisterTransportListener(protocolName, ListenSH))
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package splithttp_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
gonet "net"
|
gonet "net"
|
||||||
@@ -10,10 +11,13 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||||
"github.com/xtls/xray-core/testing/servers/tcp"
|
"github.com/xtls/xray-core/testing/servers/tcp"
|
||||||
|
"github.com/xtls/xray-core/testing/servers/udp"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
@@ -142,7 +146,16 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||||
go func() {
|
go func() {
|
||||||
_ = conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
var b [1024]byte
|
||||||
|
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
_, err := conn.Read(b[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Must2(conn.Write([]byte("Response")))
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
@@ -150,7 +163,15 @@ func Test_listenSHAndDial_TLS(t *testing.T) {
|
|||||||
|
|
||||||
conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
_ = conn.Close()
|
|
||||||
|
_, err = conn.Write([]byte("Test connection 1"))
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
var b [1024]byte
|
||||||
|
n, _ := conn.Read(b[:])
|
||||||
|
if string(b[:n]) != "Response" {
|
||||||
|
t.Error("response: ", string(b[:n]))
|
||||||
|
}
|
||||||
|
|
||||||
end := time.Now()
|
end := time.Now()
|
||||||
if !end.Before(start.Add(time.Second * 5)) {
|
if !end.Before(start.Add(time.Second * 5)) {
|
||||||
@@ -204,3 +225,184 @@ func Test_listenSHAndDial_H2C(t *testing.T) {
|
|||||||
t.Error("Expected h2 but got:", resp.ProtoMajor)
|
t.Error("Expected h2 but got:", resp.ProtoMajor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_listenSHAndDial_QUIC(t *testing.T) {
|
||||||
|
if runtime.GOARCH == "arm64" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
listenPort := udp.PickPort()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
streamSettings := &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
ProtocolSettings: &Config{
|
||||||
|
Path: "shs",
|
||||||
|
},
|
||||||
|
SecurityType: "tls",
|
||||||
|
SecuritySettings: &tls.Config{
|
||||||
|
AllowInsecure: true,
|
||||||
|
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
|
||||||
|
NextProtocol: []string{"h3"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||||
|
go func() {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
b := buf.New()
|
||||||
|
defer b.Release()
|
||||||
|
|
||||||
|
for {
|
||||||
|
b.Clear()
|
||||||
|
if _, err := b.ReadFrom(conn); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Must2(conn.Write(b.Bytes()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
defer listen.Close()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
conn, err := Dial(context.Background(), net.UDPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
|
common.Must(err)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
const N = 1024
|
||||||
|
b1 := make([]byte, N)
|
||||||
|
common.Must2(rand.Read(b1))
|
||||||
|
b2 := buf.New()
|
||||||
|
|
||||||
|
common.Must2(conn.Write(b1))
|
||||||
|
|
||||||
|
b2.Clear()
|
||||||
|
common.Must2(b2.ReadFullFrom(conn, N))
|
||||||
|
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
|
||||||
|
t.Error(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Must2(conn.Write(b1))
|
||||||
|
|
||||||
|
b2.Clear()
|
||||||
|
common.Must2(b2.ReadFullFrom(conn, N))
|
||||||
|
if r := cmp.Diff(b2.Bytes(), b1); r != "" {
|
||||||
|
t.Error(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
end := time.Now()
|
||||||
|
if !end.Before(start.Add(time.Second * 5)) {
|
||||||
|
t.Error("end: ", end, " start: ", start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_listenSHAndDial_Unix(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
tempSocket := tempDir + "/server.sock"
|
||||||
|
|
||||||
|
listen, err := ListenSH(context.Background(), net.DomainAddress(tempSocket), 0, &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
ProtocolSettings: &Config{
|
||||||
|
Path: "/sh",
|
||||||
|
},
|
||||||
|
}, func(conn stat.Connection) {
|
||||||
|
go func(c stat.Connection) {
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
var b [1024]byte
|
||||||
|
c.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
_, err := c.Read(b[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Must2(c.Write([]byte("Response")))
|
||||||
|
}(conn)
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
ctx := context.Background()
|
||||||
|
streamSettings := &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
ProtocolSettings: &Config{
|
||||||
|
Host: "example.com",
|
||||||
|
Path: "sh",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
conn, err := Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)), streamSettings)
|
||||||
|
|
||||||
|
common.Must(err)
|
||||||
|
_, err = conn.Write([]byte("Test connection 1"))
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
var b [1024]byte
|
||||||
|
fmt.Println("test2")
|
||||||
|
n, _ := conn.Read(b[:])
|
||||||
|
fmt.Println("string is", n)
|
||||||
|
if string(b[:n]) != "Response" {
|
||||||
|
t.Error("response: ", string(b[:n]))
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Must(conn.Close())
|
||||||
|
conn, err = Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)), streamSettings)
|
||||||
|
|
||||||
|
common.Must(err)
|
||||||
|
_, err = conn.Write([]byte("Test connection 2"))
|
||||||
|
common.Must(err)
|
||||||
|
n, _ = conn.Read(b[:])
|
||||||
|
common.Must(err)
|
||||||
|
if string(b[:n]) != "Response" {
|
||||||
|
t.Error("response: ", string(b[:n]))
|
||||||
|
}
|
||||||
|
common.Must(conn.Close())
|
||||||
|
|
||||||
|
common.Must(listen.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_queryString(t *testing.T) {
|
||||||
|
listenPort := tcp.PickPort()
|
||||||
|
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
ProtocolSettings: &Config{
|
||||||
|
// this querystring does not have any effect, but sometimes people blindly copy it from websocket config. make sure the outbound doesn't break
|
||||||
|
Path: "/sh?ed=2048",
|
||||||
|
},
|
||||||
|
}, func(conn stat.Connection) {
|
||||||
|
go func(c stat.Connection) {
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
var b [1024]byte
|
||||||
|
c.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
|
_, err := c.Read(b[:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Must2(c.Write([]byte("Response")))
|
||||||
|
}(conn)
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
ctx := context.Background()
|
||||||
|
streamSettings := &internet.MemoryStreamConfig{
|
||||||
|
ProtocolName: "splithttp",
|
||||||
|
ProtocolSettings: &Config{Path: "sh"},
|
||||||
|
}
|
||||||
|
conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
|
|
||||||
|
common.Must(err)
|
||||||
|
_, err = conn.Write([]byte("Test connection 1"))
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
var b [1024]byte
|
||||||
|
fmt.Println("test2")
|
||||||
|
n, _ := conn.Read(b[:])
|
||||||
|
fmt.Println("string is", n)
|
||||||
|
if string(b[:n]) != "Response" {
|
||||||
|
t.Error("response: ", string(b[:n]))
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Must(conn.Close())
|
||||||
|
common.Must(listen.Close())
|
||||||
|
}
|
||||||
|
@@ -6,6 +6,7 @@ package splithttp
|
|||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
)
|
)
|
||||||
@@ -16,11 +17,12 @@ type Packet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type uploadQueue struct {
|
type uploadQueue struct {
|
||||||
pushedPackets chan Packet
|
pushedPackets chan Packet
|
||||||
heap uploadHeap
|
writeCloseMutex sync.Mutex
|
||||||
nextSeq uint64
|
heap uploadHeap
|
||||||
closed bool
|
nextSeq uint64
|
||||||
maxPackets int
|
closed bool
|
||||||
|
maxPackets int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUploadQueue(maxPackets int) *uploadQueue {
|
func NewUploadQueue(maxPackets int) *uploadQueue {
|
||||||
@@ -34,6 +36,9 @@ func NewUploadQueue(maxPackets int) *uploadQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *uploadQueue) Push(p Packet) error {
|
func (h *uploadQueue) Push(p Packet) error {
|
||||||
|
h.writeCloseMutex.Lock()
|
||||||
|
defer h.writeCloseMutex.Unlock()
|
||||||
|
|
||||||
if h.closed {
|
if h.closed {
|
||||||
return errors.New("splithttp packet queue closed")
|
return errors.New("splithttp packet queue closed")
|
||||||
}
|
}
|
||||||
@@ -43,6 +48,9 @@ func (h *uploadQueue) Push(p Packet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *uploadQueue) Close() error {
|
func (h *uploadQueue) Close() error {
|
||||||
|
h.writeCloseMutex.Lock()
|
||||||
|
defer h.writeCloseMutex.Unlock()
|
||||||
|
|
||||||
h.closed = true
|
h.closed = true
|
||||||
close(h.pushedPackets)
|
close(h.pushedPackets)
|
||||||
return nil
|
return nil
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -50,72 +51,84 @@ func (c *Config) BuildCertificates() []*tls.Certificate {
|
|||||||
if entry.Usage != Certificate_ENCIPHERMENT {
|
if entry.Usage != Certificate_ENCIPHERMENT {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
|
getX509KeyPair := func() *tls.Certificate {
|
||||||
if err != nil {
|
keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
|
||||||
errors.LogWarningInner(context.Background(), err, "ignoring invalid X509 key pair")
|
if err != nil {
|
||||||
continue
|
errors.LogWarningInner(context.Background(), err, "ignoring invalid X509 key pair")
|
||||||
}
|
return nil
|
||||||
keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
|
|
||||||
if err != nil {
|
|
||||||
errors.LogWarningInner(context.Background(), err, "ignoring invalid certificate")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
certs = append(certs, &keyPair)
|
|
||||||
if !entry.OneTimeLoading {
|
|
||||||
var isOcspstapling bool
|
|
||||||
hotReloadCertInterval := uint64(3600)
|
|
||||||
if entry.OcspStapling != 0 {
|
|
||||||
hotReloadCertInterval = entry.OcspStapling
|
|
||||||
isOcspstapling = true
|
|
||||||
}
|
}
|
||||||
index := len(certs) - 1
|
keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
|
||||||
go func(entry *Certificate, cert *tls.Certificate, index int) {
|
if err != nil {
|
||||||
t := time.NewTicker(time.Duration(hotReloadCertInterval) * time.Second)
|
errors.LogWarningInner(context.Background(), err, "ignoring invalid certificate")
|
||||||
for {
|
return nil
|
||||||
if entry.CertificatePath != "" && entry.KeyPath != "" {
|
}
|
||||||
newCert, err := filesystem.ReadFile(entry.CertificatePath)
|
return &keyPair
|
||||||
if err != nil {
|
|
||||||
errors.LogErrorInner(context.Background(), err, "failed to parse certificate")
|
|
||||||
<-t.C
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newKey, err := filesystem.ReadFile(entry.KeyPath)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogErrorInner(context.Background(), err, "failed to parse key")
|
|
||||||
<-t.C
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if string(newCert) != string(entry.Certificate) && string(newKey) != string(entry.Key) {
|
|
||||||
newKeyPair, err := tls.X509KeyPair(newCert, newKey)
|
|
||||||
if err != nil {
|
|
||||||
errors.LogErrorInner(context.Background(), err, "ignoring invalid X509 key pair")
|
|
||||||
<-t.C
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if newKeyPair.Leaf, err = x509.ParseCertificate(newKeyPair.Certificate[0]); err != nil {
|
|
||||||
errors.LogErrorInner(context.Background(), err, "ignoring invalid certificate")
|
|
||||||
<-t.C
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cert = &newKeyPair
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isOcspstapling {
|
|
||||||
if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
|
|
||||||
errors.LogWarningInner(context.Background(), err, "ignoring invalid OCSP")
|
|
||||||
} else if string(newOCSPData) != string(cert.OCSPStaple) {
|
|
||||||
cert.OCSPStaple = newOCSPData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
certs[index] = cert
|
|
||||||
<-t.C
|
|
||||||
}
|
|
||||||
}(entry, certs[index], index)
|
|
||||||
}
|
}
|
||||||
|
if keyPair := getX509KeyPair(); keyPair != nil {
|
||||||
|
certs = append(certs, keyPair)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
index := len(certs) - 1
|
||||||
|
setupOcspTicker(entry, func(isReloaded, isOcspstapling bool){
|
||||||
|
cert := certs[index]
|
||||||
|
if isReloaded {
|
||||||
|
if newKeyPair := getX509KeyPair(); newKeyPair != nil {
|
||||||
|
cert = newKeyPair
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isOcspstapling {
|
||||||
|
if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
|
||||||
|
errors.LogWarningInner(context.Background(), err, "ignoring invalid OCSP")
|
||||||
|
} else if string(newOCSPData) != string(cert.OCSPStaple) {
|
||||||
|
cert.OCSPStaple = newOCSPData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
certs[index] = cert
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return certs
|
return certs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupOcspTicker(entry *Certificate, callback func(isReloaded, isOcspstapling bool)) {
|
||||||
|
go func() {
|
||||||
|
if entry.OneTimeLoading {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var isOcspstapling bool
|
||||||
|
hotReloadCertInterval := uint64(3600)
|
||||||
|
if entry.OcspStapling != 0 {
|
||||||
|
hotReloadCertInterval = entry.OcspStapling
|
||||||
|
isOcspstapling = true
|
||||||
|
}
|
||||||
|
t := time.NewTicker(time.Duration(hotReloadCertInterval) * time.Second)
|
||||||
|
for {
|
||||||
|
var isReloaded bool
|
||||||
|
if entry.CertificatePath != "" && entry.KeyPath != "" {
|
||||||
|
newCert, err := filesystem.ReadFile(entry.CertificatePath)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(context.Background(), err, "failed to parse certificate")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newKey, err := filesystem.ReadFile(entry.KeyPath)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogErrorInner(context.Background(), err, "failed to parse key")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(newCert) != string(entry.Certificate) || string(newKey) != string(entry.Key) {
|
||||||
|
entry.Certificate = newCert
|
||||||
|
entry.Key = newKey
|
||||||
|
isReloaded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(isReloaded, isOcspstapling)
|
||||||
|
<-t.C
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
func isCertificateExpired(c *tls.Certificate) bool {
|
func isCertificateExpired(c *tls.Certificate) bool {
|
||||||
if c.Leaf == nil && len(c.Certificate) > 0 {
|
if c.Leaf == nil && len(c.Certificate) > 0 {
|
||||||
if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
|
if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
|
||||||
@@ -137,6 +150,9 @@ func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, erro
|
|||||||
return nil, errors.New("failed to generate new certificate for ", domain).Base(err)
|
return nil, errors.New("failed to generate new certificate for ", domain).Base(err)
|
||||||
}
|
}
|
||||||
newCertPEM, newKeyPEM := newCert.ToPEM()
|
newCertPEM, newKeyPEM := newCert.ToPEM()
|
||||||
|
if rawCA.BuildChain {
|
||||||
|
newCertPEM = bytes.Join([][]byte{newCertPEM, rawCA.Certificate}, []byte("\n"))
|
||||||
|
}
|
||||||
cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
|
cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
|
||||||
return &cert, err
|
return &cert, err
|
||||||
}
|
}
|
||||||
@@ -146,6 +162,7 @@ func (c *Config) getCustomCA() []*Certificate {
|
|||||||
for _, certificate := range c.Certificate {
|
for _, certificate := range c.Certificate {
|
||||||
if certificate.Usage == Certificate_AUTHORITY_ISSUE {
|
if certificate.Usage == Certificate_AUTHORITY_ISSUE {
|
||||||
certs = append(certs, certificate)
|
certs = append(certs, certificate)
|
||||||
|
setupOcspTicker(certificate, func(isReloaded, isOcspstapling bool){ })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return certs
|
return certs
|
||||||
|
@@ -86,6 +86,7 @@ type Certificate struct {
|
|||||||
KeyPath string `protobuf:"bytes,6,opt,name=key_path,json=keyPath,proto3" json:"key_path,omitempty"`
|
KeyPath string `protobuf:"bytes,6,opt,name=key_path,json=keyPath,proto3" json:"key_path,omitempty"`
|
||||||
// If true, one-Time Loading
|
// If true, one-Time Loading
|
||||||
OneTimeLoading bool `protobuf:"varint,7,opt,name=One_time_loading,json=OneTimeLoading,proto3" json:"One_time_loading,omitempty"`
|
OneTimeLoading bool `protobuf:"varint,7,opt,name=One_time_loading,json=OneTimeLoading,proto3" json:"One_time_loading,omitempty"`
|
||||||
|
BuildChain bool `protobuf:"varint,8,opt,name=build_chain,json=buildChain,proto3" json:"build_chain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Certificate) Reset() {
|
func (x *Certificate) Reset() {
|
||||||
@@ -169,6 +170,13 @@ func (x *Certificate) GetOneTimeLoading() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Certificate) GetBuildChain() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.BuildChain
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -359,7 +367,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
|
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
|
||||||
0x6c, 0x73, 0x22, 0xe2, 0x02, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
0x6c, 0x73, 0x22, 0x83, 0x03, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||||
0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
||||||
0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
@@ -377,66 +385,68 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x5f, 0x74,
|
0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x5f, 0x74,
|
||||||
0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28,
|
0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||||
0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e,
|
0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e,
|
||||||
0x67, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x4e,
|
0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e,
|
||||||
0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10,
|
0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x68, 0x61,
|
||||||
0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59,
|
0x69, 0x6e, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45,
|
||||||
0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f,
|
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
||||||
0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xf6, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
|
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
||||||
0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65,
|
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
||||||
0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f,
|
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xf6, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
||||||
0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, 0x72,
|
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
||||||
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,
|
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
|
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72,
|
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
|
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
|
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65,
|
||||||
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76,
|
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69,
|
||||||
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70,
|
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6e,
|
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72,
|
||||||
0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19, 0x65,
|
0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f,
|
||||||
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c,
|
||||||
0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17,
|
0x6e, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19,
|
||||||
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
|
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72,
|
||||||
0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62,
|
0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06,
|
0x17, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
|
||||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79, 0x73,
|
0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61,
|
||||||
0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x76,
|
0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18,
|
||||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x69,
|
0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79,
|
||||||
0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f,
|
0x73, 0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f,
|
||||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
|
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
|
||||||
0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69, 0x70,
|
0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78,
|
||||||
0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
|
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
|
||||||
0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x41,
|
0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69,
|
||||||
0x0a, 0x1b, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
|
0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28,
|
||||||
0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20,
|
0x09, 0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12,
|
||||||
0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x53,
|
0x41, 0x0a, 0x1b, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65,
|
0x5f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x0a,
|
||||||
0x73, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74,
|
0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72,
|
||||||
0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72,
|
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74,
|
||||||
0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e,
|
0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e,
|
||||||
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
|
||||||
0x10, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e,
|
0x72, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75,
|
||||||
0x69, 0x12, 0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72,
|
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08,
|
||||||
0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61,
|
0x52, 0x10, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53,
|
||||||
0x69, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52,
|
0x6e, 0x69, 0x12, 0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65,
|
||||||
0x20, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69,
|
0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68,
|
||||||
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35,
|
0x61, 0x69, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c,
|
||||||
0x36, 0x12, 0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72,
|
0x52, 0x20, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74,
|
||||||
0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62,
|
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32,
|
||||||
0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e,
|
0x35, 0x36, 0x12, 0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65,
|
||||||
0x20, 0x03, 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72,
|
0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75,
|
||||||
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69,
|
0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18,
|
||||||
0x63, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61,
|
0x0e, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65,
|
||||||
0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20, 0x01,
|
0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c,
|
||||||
0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67,
|
0x69, 0x63, 0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d,
|
||||||
0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20,
|
||||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f,
|
||||||
0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
0x67, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
|
||||||
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||||
0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||||
0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54,
|
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
|
||||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||||
0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e,
|
||||||
|
0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||||
|
0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -31,6 +31,8 @@ message Certificate {
|
|||||||
|
|
||||||
// If true, one-Time Loading
|
// If true, one-Time Loading
|
||||||
bool One_time_loading = 7;
|
bool One_time_loading = 7;
|
||||||
|
|
||||||
|
bool build_chain = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
|
@@ -58,7 +58,6 @@ func Test_listenWSAndDial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
common.Must(conn.Close())
|
common.Must(conn.Close())
|
||||||
<-time.After(time.Second * 5)
|
|
||||||
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
_, err = conn.Write([]byte("Test connection 2"))
|
_, err = conn.Write([]byte("Test connection 2"))
|
||||||
|
Reference in New Issue
Block a user