mirror of
https://github.com/nadoo/glider.git
synced 2025-04-22 12:12:09 +08:00
Compare commits
No commits in common. "8e81e09a8f79198a832a4eb53e58b7b9a746e129" and "2f154678a9aa7e4cd6bdc9a15d39220bbc247a54" have entirely different histories.
8e81e09a8f
...
2f154678a9
@ -38,10 +38,10 @@ archives:
|
|||||||
builds:
|
builds:
|
||||||
- default
|
- default
|
||||||
wrap_in_directory: true
|
wrap_in_directory: true
|
||||||
formats: tar.gz
|
format: tar.gz
|
||||||
format_overrides:
|
format_overrides:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
formats: zip
|
format: zip
|
||||||
files:
|
files:
|
||||||
- LICENSE
|
- LICENSE
|
||||||
- README.md
|
- README.md
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM golang:1.24-alpine AS build-env
|
FROM golang:1.23-alpine AS build-env
|
||||||
ADD . /src
|
ADD . /src
|
||||||
RUN apk --no-cache add git \
|
RUN apk --no-cache add git \
|
||||||
&& cd /src && go build -v -ldflags "-s -w"
|
&& cd /src && go build -v -ldflags "-s -w"
|
||||||
|
@ -210,11 +210,10 @@ func (h *Header) SetAncount(ancount int) {
|
|||||||
h.ANCOUNT = uint16(ancount)
|
h.ANCOUNT = uint16(ancount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not used now, but keep it for future use.
|
func (h *Header) setFlag(QR uint16, Opcode uint16, AA uint16,
|
||||||
// func (h *Header) setFlag(QR uint16, Opcode uint16, AA uint16,
|
TC uint16, RD uint16, RA uint16, RCODE uint16) {
|
||||||
// TC uint16, RD uint16, RA uint16, RCODE uint16) {
|
h.Bits = QR<<15 + Opcode<<11 + AA<<10 + TC<<9 + RD<<8 + RA<<7 + RCODE
|
||||||
// h.Bits = QR<<15 + Opcode<<11 + AA<<10 + TC<<9 + RD<<8 + RA<<7 + RCODE
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// MarshalTo marshals header struct to []byte and write to w.
|
// MarshalTo marshals header struct to []byte and write to w.
|
||||||
func (h *Header) MarshalTo(w io.Writer) (int, error) {
|
func (h *Header) MarshalTo(w io.Writer) (int, error) {
|
||||||
|
12
go.mod
12
go.mod
@ -1,23 +1,23 @@
|
|||||||
module github.com/nadoo/glider
|
module github.com/nadoo/glider
|
||||||
|
|
||||||
go 1.24
|
go 1.23
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
|
||||||
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
|
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
|
||||||
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
|
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
|
||||||
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
|
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
|
github.com/insomniacslk/dhcp v0.0.0-20241219180459-a662cc47d412
|
||||||
github.com/nadoo/conflag v0.3.1
|
github.com/nadoo/conflag v0.3.1
|
||||||
github.com/nadoo/ipset v0.5.0
|
github.com/nadoo/ipset v0.5.0
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.18
|
github.com/xtaci/kcp-go/v5 v5.6.18
|
||||||
golang.org/x/crypto v0.35.0
|
golang.org/x/crypto v0.31.0
|
||||||
golang.org/x/sys v0.30.0
|
golang.org/x/sys v0.28.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
|
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.12.4 // indirect
|
github.com/klauspost/reedsolomon v1.12.4 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
@ -25,5 +25,5 @@ require (
|
|||||||
github.com/templexxx/xorsimd v0.4.3 // indirect
|
github.com/templexxx/xorsimd v0.4.3 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||||
golang.org/x/net v0.35.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
24
go.sum
24
go.sum
@ -33,12 +33,12 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
github.com/insomniacslk/dhcp v0.0.0-20241219180459-a662cc47d412 h1:Gpj5alZpJhmJYx8Gljb+SxScp5+smvPA9SmajG4RenY=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
github.com/insomniacslk/dhcp v0.0.0-20241219180459-a662cc47d412/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||||
github.com/klauspost/reedsolomon v1.12.4 h1:5aDr3ZGoJbgu/8+j45KtUJxzYm8k08JGtB9Wx1VQ4OA=
|
github.com/klauspost/reedsolomon v1.12.4 h1:5aDr3ZGoJbgu/8+j45KtUJxzYm8k08JGtB9Wx1VQ4OA=
|
||||||
github.com/klauspost/reedsolomon v1.12.4/go.mod h1:d3CzOMOt0JXGIFZm1StgkyF14EYr3xneR2rNWo7NcMU=
|
github.com/klauspost/reedsolomon v1.12.4/go.mod h1:d3CzOMOt0JXGIFZm1StgkyF14EYr3xneR2rNWo7NcMU=
|
||||||
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
|
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
|
||||||
@ -73,8 +73,8 @@ github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@ -85,8 +85,8 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -97,10 +97,10 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -3,7 +3,6 @@ package pool
|
|||||||
import (
|
import (
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -18,12 +17,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
for i := range num {
|
for i := 0; i < num; i++ {
|
||||||
size := 1 << i
|
size := 1 << i
|
||||||
sizes[i] = size
|
sizes[i] = size
|
||||||
pools[i].New = func() any {
|
pools[i].New = func() any {
|
||||||
buf := make([]byte, size)
|
return make([]byte, size)
|
||||||
return unsafe.SliceData(buf)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,10 +30,11 @@ func init() {
|
|||||||
// otherwise, this function will call make([]byte, size) directly.
|
// otherwise, this function will call make([]byte, size) directly.
|
||||||
func GetBuffer(size int) []byte {
|
func GetBuffer(size int) []byte {
|
||||||
if size >= 1 && size <= maxsize {
|
if size >= 1 && size <= maxsize {
|
||||||
i := bits.Len32(uint32(size - 1))
|
i := bits.Len32(uint32(size)) - 1
|
||||||
if p := pools[i].Get().(*byte); p != nil {
|
if sizes[i] < size {
|
||||||
return unsafe.Slice(p, 1<<i)[:size]
|
i += 1
|
||||||
}
|
}
|
||||||
|
return pools[i].Get().([]byte)[:size]
|
||||||
}
|
}
|
||||||
return make([]byte, size)
|
return make([]byte, size)
|
||||||
}
|
}
|
||||||
@ -43,9 +42,9 @@ func GetBuffer(size int) []byte {
|
|||||||
// PutBuffer puts a buffer into pool.
|
// PutBuffer puts a buffer into pool.
|
||||||
func PutBuffer(buf []byte) {
|
func PutBuffer(buf []byte) {
|
||||||
if size := cap(buf); size >= 1 && size <= maxsize {
|
if size := cap(buf); size >= 1 && size <= maxsize {
|
||||||
i := bits.Len32(uint32(size - 1))
|
i := bits.Len32(uint32(size)) - 1
|
||||||
if sizes[i] == size {
|
if sizes[i] == size {
|
||||||
pools[i].Put(unsafe.SliceData(buf))
|
pools[i].Put(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
pkg/smux/LICENSE
Normal file
21
pkg/smux/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016-2017 Daniel Fu
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -1,25 +1,3 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -60,18 +38,16 @@ const (
|
|||||||
|
|
||||||
// Frame defines a packet from or to be multiplexed into a single connection
|
// Frame defines a packet from or to be multiplexed into a single connection
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
ver byte // version
|
ver byte
|
||||||
cmd byte // command
|
cmd byte
|
||||||
sid uint32 // stream id
|
sid uint32
|
||||||
data []byte // payload
|
data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFrame creates a new frame with given version, command and stream id
|
|
||||||
func newFrame(version byte, cmd byte, sid uint32) Frame {
|
func newFrame(version byte, cmd byte, sid uint32) Frame {
|
||||||
return Frame{ver: version, cmd: cmd, sid: sid}
|
return Frame{ver: version, cmd: cmd, sid: sid}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rawHeader is a byte array representation of Frame header
|
|
||||||
type rawHeader [headerSize]byte
|
type rawHeader [headerSize]byte
|
||||||
|
|
||||||
func (h rawHeader) Version() byte {
|
func (h rawHeader) Version() byte {
|
||||||
@ -95,7 +71,6 @@ func (h rawHeader) String() string {
|
|||||||
h.Version(), h.Cmd(), h.StreamID(), h.Length())
|
h.Version(), h.Cmd(), h.StreamID(), h.Length())
|
||||||
}
|
}
|
||||||
|
|
||||||
// updHeader is a byte array representation of cmdUPD
|
|
||||||
type updHeader [szCmdUPD]byte
|
type updHeader [szCmdUPD]byte
|
||||||
|
|
||||||
func (h updHeader) Consumed() uint32 {
|
func (h updHeader) Consumed() uint32 {
|
||||||
|
@ -1,25 +1,7 @@
|
|||||||
// MIT License
|
// Package smux is a multiplexing library for Golang.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2016-2017 xtaci
|
// It relies on an underlying connection to provide reliability and ordering, such as TCP or KCP,
|
||||||
//
|
// and provides stream-oriented multiplexing over a single channel.
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
86
pkg/smux/mux_test.go
Normal file
86
pkg/smux/mux_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package smux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type buffer struct {
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *buffer) Close() error {
|
||||||
|
b.Buffer.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig(t *testing.T) {
|
||||||
|
VerifyConfig(DefaultConfig())
|
||||||
|
|
||||||
|
config := DefaultConfig()
|
||||||
|
config.KeepAliveInterval = 0
|
||||||
|
err := VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.KeepAliveInterval = 10
|
||||||
|
config.KeepAliveTimeout = 5
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxFrameSize = 0
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxFrameSize = 65536
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxReceiveBuffer = 0
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxStreamBuffer = 0
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxStreamBuffer = 100
|
||||||
|
config.MaxReceiveBuffer = 99
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bts buffer
|
||||||
|
if _, err := Server(&bts, config); err == nil {
|
||||||
|
t.Fatal("server started with wrong config")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client(&bts, config); err == nil {
|
||||||
|
t.Fatal("client started with wrong config")
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,3 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -28,7 +6,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -39,38 +16,25 @@ import (
|
|||||||
const (
|
const (
|
||||||
defaultAcceptBacklog = 1024
|
defaultAcceptBacklog = 1024
|
||||||
maxShaperSize = 1024
|
maxShaperSize = 1024
|
||||||
openCloseTimeout = 30 * time.Second // Timeout for opening/closing streams
|
openCloseTimeout = 30 * time.Second // stream open/close timeout
|
||||||
)
|
)
|
||||||
|
|
||||||
// CLASSID represents the class of a frame
|
// define frame class
|
||||||
type CLASSID int
|
type CLASSID int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CLSCTRL CLASSID = iota // prioritized control signal
|
CLSCTRL CLASSID = iota
|
||||||
CLSDATA
|
CLSDATA
|
||||||
)
|
)
|
||||||
|
|
||||||
// timeoutError representing timeouts for operations such as accept, read and write
|
|
||||||
//
|
|
||||||
// To better cooperate with the standard library, timeoutError should implement the standard library's `net.Error`.
|
|
||||||
//
|
|
||||||
// For example, using smux to implement net.Listener and work with http.Server, the keep-alive connection (*smux.Stream) will be unexpectedly closed.
|
|
||||||
// For more details, see https://github.com/xtaci/smux/pull/99.
|
|
||||||
type timeoutError struct{}
|
|
||||||
|
|
||||||
func (timeoutError) Error() string { return "timeout" }
|
|
||||||
func (timeoutError) Temporary() bool { return true }
|
|
||||||
func (timeoutError) Timeout() bool { return true }
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidProtocol = errors.New("invalid protocol")
|
ErrInvalidProtocol = errors.New("invalid protocol")
|
||||||
ErrConsumed = errors.New("peer consumed more than sent")
|
ErrConsumed = errors.New("peer consumed more than sent")
|
||||||
ErrGoAway = errors.New("stream id overflows, should start a new connection")
|
ErrGoAway = errors.New("stream id overflows, should start a new connection")
|
||||||
ErrTimeout net.Error = &timeoutError{}
|
ErrTimeout = errors.New("timeout")
|
||||||
ErrWouldBlock = errors.New("operation would block on IO")
|
ErrWouldBlock = errors.New("operation would block on IO")
|
||||||
)
|
)
|
||||||
|
|
||||||
// writeRequest represents a request to write a frame
|
|
||||||
type writeRequest struct {
|
type writeRequest struct {
|
||||||
class CLASSID
|
class CLASSID
|
||||||
frame Frame
|
frame Frame
|
||||||
@ -78,7 +42,6 @@ type writeRequest struct {
|
|||||||
result chan writeResult
|
result chan writeResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeResult represents the result of a write request
|
|
||||||
type writeResult struct {
|
type writeResult struct {
|
||||||
n int
|
n int
|
||||||
err error
|
err error
|
||||||
@ -95,7 +58,7 @@ type Session struct {
|
|||||||
bucket int32 // token bucket
|
bucket int32 // token bucket
|
||||||
bucketNotify chan struct{} // used for waiting for tokens
|
bucketNotify chan struct{} // used for waiting for tokens
|
||||||
|
|
||||||
streams map[uint32]*stream // all streams in this session
|
streams map[uint32]*Stream // all streams in this session
|
||||||
streamLock sync.Mutex // locks streams
|
streamLock sync.Mutex // locks streams
|
||||||
|
|
||||||
die chan struct{} // flag session has died
|
die chan struct{} // flag session has died
|
||||||
@ -114,7 +77,7 @@ type Session struct {
|
|||||||
chProtoError chan struct{}
|
chProtoError chan struct{}
|
||||||
protoErrorOnce sync.Once
|
protoErrorOnce sync.Once
|
||||||
|
|
||||||
chAccepts chan *stream
|
chAccepts chan *Stream
|
||||||
|
|
||||||
dataReady int32 // flag data has arrived
|
dataReady int32 // flag data has arrived
|
||||||
|
|
||||||
@ -122,7 +85,7 @@ type Session struct {
|
|||||||
|
|
||||||
deadline atomic.Value
|
deadline atomic.Value
|
||||||
|
|
||||||
requestID uint32 // Monotonic increasing write request ID
|
requestID uint32 // write request monotonic increasing
|
||||||
shaper chan writeRequest // a shaper for writing
|
shaper chan writeRequest // a shaper for writing
|
||||||
writes chan writeRequest
|
writes chan writeRequest
|
||||||
}
|
}
|
||||||
@ -132,8 +95,8 @@ func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
|
|||||||
s.die = make(chan struct{})
|
s.die = make(chan struct{})
|
||||||
s.conn = conn
|
s.conn = conn
|
||||||
s.config = config
|
s.config = config
|
||||||
s.streams = make(map[uint32]*stream)
|
s.streams = make(map[uint32]*Stream)
|
||||||
s.chAccepts = make(chan *stream, defaultAcceptBacklog)
|
s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
|
||||||
s.bucket = int32(config.MaxReceiveBuffer)
|
s.bucket = int32(config.MaxReceiveBuffer)
|
||||||
s.bucketNotify = make(chan struct{}, 1)
|
s.bucketNotify = make(chan struct{}, 1)
|
||||||
s.shaper = make(chan writeRequest)
|
s.shaper = make(chan writeRequest)
|
||||||
@ -181,7 +144,7 @@ func (s *Session) OpenStream() (*Stream, error) {
|
|||||||
|
|
||||||
stream := newStream(sid, s.config.MaxFrameSize, s)
|
stream := newStream(sid, s.config.MaxFrameSize, s)
|
||||||
|
|
||||||
if _, err := s.writeControlFrame(newFrame(byte(s.config.Version), cmdSYN, sid)); err != nil {
|
if _, err := s.writeFrame(newFrame(byte(s.config.Version), cmdSYN, sid)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,14 +159,7 @@ func (s *Session) OpenStream() (*Stream, error) {
|
|||||||
return nil, io.ErrClosedPipe
|
return nil, io.ErrClosedPipe
|
||||||
default:
|
default:
|
||||||
s.streams[sid] = stream
|
s.streams[sid] = stream
|
||||||
wrapper := &Stream{stream: stream}
|
return stream, nil
|
||||||
// NOTE(x): disabled finalizer for issue #997
|
|
||||||
/*
|
|
||||||
runtime.SetFinalizer(wrapper, func(s *Stream) {
|
|
||||||
s.Close()
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
return wrapper, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,11 +180,7 @@ func (s *Session) AcceptStream() (*Stream, error) {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case stream := <-s.chAccepts:
|
case stream := <-s.chAccepts:
|
||||||
wrapper := &Stream{stream: stream}
|
return stream, nil
|
||||||
runtime.SetFinalizer(wrapper, func(s *Stream) {
|
|
||||||
s.Close()
|
|
||||||
})
|
|
||||||
return wrapper, nil
|
|
||||||
case <-deadline:
|
case <-deadline:
|
||||||
return nil, ErrTimeout
|
return nil, ErrTimeout
|
||||||
case <-s.chSocketReadError:
|
case <-s.chSocketReadError:
|
||||||
@ -350,15 +302,12 @@ func (s *Session) RemoteAddr() net.Addr {
|
|||||||
// notify the session that a stream has closed
|
// notify the session that a stream has closed
|
||||||
func (s *Session) streamClosed(sid uint32) {
|
func (s *Session) streamClosed(sid uint32) {
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
|
||||||
n := stream.recycleTokens()
|
|
||||||
if n > 0 { // return remaining tokens to the bucket
|
|
||||||
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
|
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
|
||||||
s.notifyBucket()
|
s.notifyBucket()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(s.streams, sid)
|
delete(s.streams, sid)
|
||||||
}
|
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,7 +342,7 @@ func (s *Session) recvLoop() {
|
|||||||
sid := hdr.StreamID()
|
sid := hdr.StreamID()
|
||||||
switch hdr.Cmd() {
|
switch hdr.Cmd() {
|
||||||
case cmdNOP:
|
case cmdNOP:
|
||||||
case cmdSYN: // stream opening
|
case cmdSYN:
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if _, ok := s.streams[sid]; !ok {
|
if _, ok := s.streams[sid]; !ok {
|
||||||
stream := newStream(sid, s.config.MaxFrameSize, s)
|
stream := newStream(sid, s.config.MaxFrameSize, s)
|
||||||
@ -404,26 +353,22 @@ func (s *Session) recvLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
case cmdFIN: // stream closing
|
case cmdFIN:
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if stream, ok := s.streams[sid]; ok {
|
||||||
stream.fin()
|
stream.fin()
|
||||||
stream.notifyReadEvent()
|
stream.notifyReadEvent()
|
||||||
}
|
}
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
case cmdPSH: // data frame
|
case cmdPSH:
|
||||||
if hdr.Length() > 0 {
|
if hdr.Length() > 0 {
|
||||||
newbuf := pool.GetBuffer(int(hdr.Length()))
|
newbuf := pool.GetBuffer(int(hdr.Length()))
|
||||||
if written, err := io.ReadFull(s.conn, newbuf); err == nil {
|
if written, err := io.ReadFull(s.conn, newbuf); err == nil {
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if stream, ok := s.streams[sid]; ok {
|
||||||
stream.pushBytes(newbuf)
|
stream.pushBytes(newbuf)
|
||||||
// a stream used some token
|
|
||||||
atomic.AddInt32(&s.bucket, -int32(written))
|
atomic.AddInt32(&s.bucket, -int32(written))
|
||||||
stream.notifyReadEvent()
|
stream.notifyReadEvent()
|
||||||
} else {
|
|
||||||
// data directed to a missing/closed stream, recycle the buffer immediately.
|
|
||||||
pool.PutBuffer(newbuf)
|
|
||||||
}
|
}
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
@ -431,7 +376,7 @@ func (s *Session) recvLoop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case cmdUPD: // a window update signal
|
case cmdUPD:
|
||||||
if _, err := io.ReadFull(s.conn, updHdr[:]); err == nil {
|
if _, err := io.ReadFull(s.conn, updHdr[:]); err == nil {
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if stream, ok := s.streams[sid]; ok {
|
||||||
@ -453,7 +398,6 @@ func (s *Session) recvLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keepalive sends NOP frame to peer to keep the connection alive, and detect dead peers
|
|
||||||
func (s *Session) keepalive() {
|
func (s *Session) keepalive() {
|
||||||
tickerPing := time.NewTicker(s.config.KeepAliveInterval)
|
tickerPing := time.NewTicker(s.config.KeepAliveInterval)
|
||||||
tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
|
tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
|
||||||
@ -479,8 +423,7 @@ func (s *Session) keepalive() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shaperLoop implements a priority queue for write requests,
|
// shaper shapes the sending sequence among streams
|
||||||
// some control messages are prioritized over data messages
|
|
||||||
func (s *Session) shaperLoop() {
|
func (s *Session) shaperLoop() {
|
||||||
var reqs shaperHeap
|
var reqs shaperHeap
|
||||||
var next writeRequest
|
var next writeRequest
|
||||||
@ -521,7 +464,6 @@ func (s *Session) shaperLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendLoop sends frames to the underlying connection
|
|
||||||
func (s *Session) sendLoop() {
|
func (s *Session) sendLoop() {
|
||||||
var buf []byte
|
var buf []byte
|
||||||
var n int
|
var n int
|
||||||
@ -549,7 +491,6 @@ func (s *Session) sendLoop() {
|
|||||||
binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
|
binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
|
||||||
binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)
|
binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)
|
||||||
|
|
||||||
// support for scatter-gather I/O
|
|
||||||
if len(vec) > 0 {
|
if len(vec) > 0 {
|
||||||
vec[0] = buf[:headerSize]
|
vec[0] = buf[:headerSize]
|
||||||
vec[1] = request.frame.data
|
vec[1] = request.frame.data
|
||||||
@ -581,13 +522,10 @@ func (s *Session) sendLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeControlFrame writes the control frame to the underlying connection
|
// writeFrame writes the frame to the underlying connection
|
||||||
// and returns the number of bytes written if successful
|
// and returns the number of bytes written if successful
|
||||||
func (s *Session) writeControlFrame(f Frame) (n int, err error) {
|
func (s *Session) writeFrame(f Frame) (n int, err error) {
|
||||||
timer := time.NewTimer(openCloseTimeout)
|
return s.writeFrameInternal(f, time.After(openCloseTimeout), CLSCTRL)
|
||||||
defer timer.Stop()
|
|
||||||
|
|
||||||
return s.writeFrameInternal(f, timer.C, CLSCTRL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal writeFrame version to support deadline used in keepalive
|
// internal writeFrame version to support deadline used in keepalive
|
||||||
|
1090
pkg/smux/session_test.go
Normal file
1090
pkg/smux/session_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,42 +1,12 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
// _itimediff returns the time difference between two uint32 values.
|
|
||||||
// The result is a signed 32-bit integer representing the difference between 'later' and 'earlier'.
|
|
||||||
func _itimediff(later, earlier uint32) int32 {
|
func _itimediff(later, earlier uint32) int32 {
|
||||||
return (int32)(later - earlier)
|
return (int32)(later - earlier)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shaperHeap is a min-heap of writeRequest.
|
|
||||||
// It orders writeRequests by class first, then by sequence number within the same class.
|
|
||||||
type shaperHeap []writeRequest
|
type shaperHeap []writeRequest
|
||||||
|
|
||||||
func (h shaperHeap) Len() int { return len(h) }
|
func (h shaperHeap) Len() int { return len(h) }
|
||||||
|
|
||||||
// Less determines the ordering of elements in the heap.
|
|
||||||
// Requests are ordered by their class first. If two requests have the same class,
|
|
||||||
// they are ordered by their sequence numbers.
|
|
||||||
func (h shaperHeap) Less(i, j int) bool {
|
func (h shaperHeap) Less(i, j int) bool {
|
||||||
if h[i].class != h[j].class {
|
if h[i].class != h[j].class {
|
||||||
return h[i].class < h[j].class
|
return h[i].class < h[j].class
|
||||||
|
50
pkg/smux/shaper_test.go
Normal file
50
pkg/smux/shaper_test.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package smux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShaper(t *testing.T) {
|
||||||
|
w1 := writeRequest{seq: 1}
|
||||||
|
w2 := writeRequest{seq: 2}
|
||||||
|
w3 := writeRequest{seq: 3}
|
||||||
|
w4 := writeRequest{seq: 4}
|
||||||
|
w5 := writeRequest{seq: 5}
|
||||||
|
|
||||||
|
var reqs shaperHeap
|
||||||
|
heap.Push(&reqs, w5)
|
||||||
|
heap.Push(&reqs, w4)
|
||||||
|
heap.Push(&reqs, w3)
|
||||||
|
heap.Push(&reqs, w2)
|
||||||
|
heap.Push(&reqs, w1)
|
||||||
|
|
||||||
|
for len(reqs) > 0 {
|
||||||
|
w := heap.Pop(&reqs).(writeRequest)
|
||||||
|
t.Log("sid:", w.frame.sid, "seq:", w.seq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShaper2(t *testing.T) {
|
||||||
|
w1 := writeRequest{class: CLSDATA, seq: 1} // stream 0
|
||||||
|
w2 := writeRequest{class: CLSDATA, seq: 2}
|
||||||
|
w3 := writeRequest{class: CLSDATA, seq: 3}
|
||||||
|
w4 := writeRequest{class: CLSDATA, seq: 4}
|
||||||
|
w5 := writeRequest{class: CLSDATA, seq: 5}
|
||||||
|
w6 := writeRequest{class: CLSCTRL, seq: 6, frame: Frame{sid: 10}} // ctrl 1
|
||||||
|
w7 := writeRequest{class: CLSCTRL, seq: 7, frame: Frame{sid: 11}} // ctrl 2
|
||||||
|
|
||||||
|
var reqs shaperHeap
|
||||||
|
heap.Push(&reqs, w6)
|
||||||
|
heap.Push(&reqs, w5)
|
||||||
|
heap.Push(&reqs, w4)
|
||||||
|
heap.Push(&reqs, w3)
|
||||||
|
heap.Push(&reqs, w2)
|
||||||
|
heap.Push(&reqs, w1)
|
||||||
|
heap.Push(&reqs, w7)
|
||||||
|
|
||||||
|
for len(reqs) > 0 {
|
||||||
|
w := heap.Pop(&reqs).(writeRequest)
|
||||||
|
t.Log("sid:", w.frame.sid, "seq:", w.seq)
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,3 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -33,41 +11,36 @@ import (
|
|||||||
"github.com/nadoo/glider/pkg/pool"
|
"github.com/nadoo/glider/pkg/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// wrapper for GC
|
|
||||||
type Stream struct {
|
|
||||||
*stream
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream implements net.Conn
|
// Stream implements net.Conn
|
||||||
type stream struct {
|
type Stream struct {
|
||||||
id uint32 // Stream identifier
|
id uint32
|
||||||
sess *Session
|
sess *Session
|
||||||
|
|
||||||
buffers [][]byte // the sequential buffers of stream
|
buffers [][]byte
|
||||||
heads [][]byte // slice heads of the buffers above, kept for recycle
|
heads [][]byte // slice heads kept for recycle
|
||||||
|
|
||||||
bufferLock sync.Mutex // Mutex to protect access to buffers
|
bufferLock sync.Mutex
|
||||||
frameSize int // Maximum frame size for the stream
|
frameSize int
|
||||||
|
|
||||||
// notify a read event
|
// notify a read event
|
||||||
chReadEvent chan struct{}
|
chReadEvent chan struct{}
|
||||||
|
|
||||||
// flag the stream has closed
|
// flag the stream has closed
|
||||||
die chan struct{}
|
die chan struct{}
|
||||||
dieOnce sync.Once // Ensures die channel is closed only once
|
dieOnce sync.Once
|
||||||
|
|
||||||
// FIN command
|
// FIN command
|
||||||
chFinEvent chan struct{}
|
chFinEvent chan struct{}
|
||||||
finEventOnce sync.Once // Ensures chFinEvent is closed only once
|
finEventOnce sync.Once
|
||||||
|
|
||||||
// deadlines
|
// deadlines
|
||||||
readDeadline atomic.Value
|
readDeadline atomic.Value
|
||||||
writeDeadline atomic.Value
|
writeDeadline atomic.Value
|
||||||
|
|
||||||
// per stream sliding window control
|
// per stream sliding window control
|
||||||
numRead uint32 // count num of bytes read
|
numRead uint32 // number of consumed bytes
|
||||||
numWritten uint32 // count num of bytes written
|
numWritten uint32 // count num of bytes written
|
||||||
incr uint32 // bytes sent since last window update
|
incr uint32 // counting for sending
|
||||||
|
|
||||||
// UPD command
|
// UPD command
|
||||||
peerConsumed uint32 // num of bytes the peer has consumed
|
peerConsumed uint32 // num of bytes the peer has consumed
|
||||||
@ -75,9 +48,9 @@ type stream struct {
|
|||||||
chUpdate chan struct{} // notify of remote data consuming and window update
|
chUpdate chan struct{} // notify of remote data consuming and window update
|
||||||
}
|
}
|
||||||
|
|
||||||
// newStream initializes and returns a new Stream.
|
// newStream initiates a Stream struct
|
||||||
func newStream(id uint32, frameSize int, sess *Session) *stream {
|
func newStream(id uint32, frameSize int, sess *Session) *Stream {
|
||||||
s := new(stream)
|
s := new(Stream)
|
||||||
s.id = id
|
s.id = id
|
||||||
s.chReadEvent = make(chan struct{}, 1)
|
s.chReadEvent = make(chan struct{}, 1)
|
||||||
s.chUpdate = make(chan struct{}, 1)
|
s.chUpdate = make(chan struct{}, 1)
|
||||||
@ -86,17 +59,16 @@ func newStream(id uint32, frameSize int, sess *Session) *stream {
|
|||||||
s.die = make(chan struct{})
|
s.die = make(chan struct{})
|
||||||
s.chFinEvent = make(chan struct{})
|
s.chFinEvent = make(chan struct{})
|
||||||
s.peerWindow = initialPeerWindow // set to initial window size
|
s.peerWindow = initialPeerWindow // set to initial window size
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns the stream's unique identifier.
|
// ID returns the unique stream ID.
|
||||||
func (s *stream) ID() uint32 {
|
func (s *Stream) ID() uint32 {
|
||||||
return s.id
|
return s.id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reads data from the stream into the provided buffer.
|
// Read implements net.Conn
|
||||||
func (s *stream) Read(b []byte) (n int, err error) {
|
func (s *Stream) Read(b []byte) (n int, err error) {
|
||||||
for {
|
for {
|
||||||
n, err = s.tryRead(b)
|
n, err = s.tryRead(b)
|
||||||
if err == ErrWouldBlock {
|
if err == ErrWouldBlock {
|
||||||
@ -109,8 +81,8 @@ func (s *stream) Read(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryRead attempts to read data from the stream without blocking.
|
// tryRead is the nonblocking version of Read
|
||||||
func (s *stream) tryRead(b []byte) (n int, err error) {
|
func (s *Stream) tryRead(b []byte) (n int, err error) {
|
||||||
if s.sess.config.Version == 2 {
|
if s.sess.config.Version == 2 {
|
||||||
return s.tryReadv2(b)
|
return s.tryReadv2(b)
|
||||||
}
|
}
|
||||||
@ -119,7 +91,6 @@ func (s *stream) tryRead(b []byte) (n int, err error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A critical section to copy data from buffers to
|
|
||||||
s.bufferLock.Lock()
|
s.bufferLock.Lock()
|
||||||
if len(s.buffers) > 0 {
|
if len(s.buffers) > 0 {
|
||||||
n = copy(b, s.buffers[0])
|
n = copy(b, s.buffers[0])
|
||||||
@ -147,8 +118,7 @@ func (s *stream) tryRead(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryReadv2 is the non-blocking version of Read for version 2 streams.
|
func (s *Stream) tryReadv2(b []byte) (n int, err error) {
|
||||||
func (s *stream) tryReadv2(b []byte) (n int, err error) {
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@ -169,25 +139,20 @@ func (s *stream) tryReadv2(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// in an ideal environment:
|
// in an ideal environment:
|
||||||
// if more than half of buffer has consumed, send read ack to peer
|
// if more than half of buffer has consumed, send read ack to peer
|
||||||
// based on round-trip time of ACK, continous flowing data
|
// based on round-trip time of ACK, continuous flowing data
|
||||||
// won't slow down due to waiting for ACK, as long as the
|
// won't slow down because of waiting for ACK, as long as the
|
||||||
// consumer keeps on reading data.
|
// consumer keeps on reading data
|
||||||
//
|
// s.numRead == n also notify window at the first read
|
||||||
// s.numRead == n implies that it's the initial reading
|
|
||||||
s.numRead += uint32(n)
|
s.numRead += uint32(n)
|
||||||
s.incr += uint32(n)
|
s.incr += uint32(n)
|
||||||
|
|
||||||
// for initial reading, send window update
|
|
||||||
if s.incr >= uint32(s.sess.config.MaxStreamBuffer/2) || s.numRead == uint32(n) {
|
if s.incr >= uint32(s.sess.config.MaxStreamBuffer/2) || s.numRead == uint32(n) {
|
||||||
notifyConsumed = s.numRead
|
notifyConsumed = s.numRead
|
||||||
s.incr = 0 // reset couting for next window update
|
s.incr = 0
|
||||||
}
|
}
|
||||||
s.bufferLock.Unlock()
|
s.bufferLock.Unlock()
|
||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
s.sess.returnTokens(n)
|
s.sess.returnTokens(n)
|
||||||
|
|
||||||
// send window update if necessary
|
|
||||||
if notifyConsumed > 0 {
|
if notifyConsumed > 0 {
|
||||||
err := s.sendWindowUpdate(notifyConsumed)
|
err := s.sendWindowUpdate(notifyConsumed)
|
||||||
return n, err
|
return n, err
|
||||||
@ -205,12 +170,7 @@ func (s *stream) tryReadv2(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteTo implements io.WriteTo
|
// WriteTo implements io.WriteTo
|
||||||
// WriteTo writes data to w until there's no more data to write or when an error occurs.
|
func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
// The return value n is the number of bytes written. Any error encountered during the write is also returned.
|
|
||||||
// WriteTo calls Write in a loop until there is no more data to write or when an error occurs.
|
|
||||||
// If the underlying stream is a v2 stream, it will send window update to peer when necessary.
|
|
||||||
// If the underlying stream is a v1 stream, it will not send window update to peer.
|
|
||||||
func (s *stream) WriteTo(w io.Writer) (n int64, err error) {
|
|
||||||
if s.sess.config.Version == 2 {
|
if s.sess.config.Version == 2 {
|
||||||
return s.writeTov2(w)
|
return s.writeTov2(w)
|
||||||
}
|
}
|
||||||
@ -227,7 +187,6 @@ func (s *stream) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
|
|
||||||
if buf != nil {
|
if buf != nil {
|
||||||
nw, ew := w.Write(buf)
|
nw, ew := w.Write(buf)
|
||||||
// NOTE: WriteTo is a reader, so we need to return tokens here
|
|
||||||
s.sess.returnTokens(len(buf))
|
s.sess.returnTokens(len(buf))
|
||||||
pool.PutBuffer(buf)
|
pool.PutBuffer(buf)
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
@ -243,8 +202,7 @@ func (s *stream) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check comments in WriteTo
|
func (s *Stream) writeTov2(w io.Writer) (n int64, err error) {
|
||||||
func (s *stream) writeTov2(w io.Writer) (n int64, err error) {
|
|
||||||
for {
|
for {
|
||||||
var notifyConsumed uint32
|
var notifyConsumed uint32
|
||||||
var buf []byte
|
var buf []byte
|
||||||
@ -264,7 +222,6 @@ func (s *stream) writeTov2(w io.Writer) (n int64, err error) {
|
|||||||
|
|
||||||
if buf != nil {
|
if buf != nil {
|
||||||
nw, ew := w.Write(buf)
|
nw, ew := w.Write(buf)
|
||||||
// NOTE: WriteTo is a reader, so we need to return tokens here
|
|
||||||
s.sess.returnTokens(len(buf))
|
s.sess.returnTokens(len(buf))
|
||||||
pool.PutBuffer(buf)
|
pool.PutBuffer(buf)
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
@ -286,8 +243,7 @@ func (s *stream) writeTov2(w io.Writer) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendWindowUpdate sends a window update frame to the peer.
|
func (s *Stream) sendWindowUpdate(consumed uint32) error {
|
||||||
func (s *stream) sendWindowUpdate(consumed uint32) error {
|
|
||||||
var timer *time.Timer
|
var timer *time.Timer
|
||||||
var deadline <-chan time.Time
|
var deadline <-chan time.Time
|
||||||
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
||||||
@ -301,12 +257,11 @@ func (s *stream) sendWindowUpdate(consumed uint32) error {
|
|||||||
binary.LittleEndian.PutUint32(hdr[:], consumed)
|
binary.LittleEndian.PutUint32(hdr[:], consumed)
|
||||||
binary.LittleEndian.PutUint32(hdr[4:], uint32(s.sess.config.MaxStreamBuffer))
|
binary.LittleEndian.PutUint32(hdr[4:], uint32(s.sess.config.MaxStreamBuffer))
|
||||||
frame.data = hdr[:]
|
frame.data = hdr[:]
|
||||||
_, err := s.sess.writeFrameInternal(frame, deadline, CLSCTRL)
|
_, err := s.sess.writeFrameInternal(frame, deadline, CLSDATA)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitRead blocks until a read event occurs or a deadline is reached.
|
func (s *Stream) waitRead() error {
|
||||||
func (s *stream) waitRead() error {
|
|
||||||
var timer *time.Timer
|
var timer *time.Timer
|
||||||
var deadline <-chan time.Time
|
var deadline <-chan time.Time
|
||||||
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
||||||
@ -316,10 +271,10 @@ func (s *stream) waitRead() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.chReadEvent: // notify some data has arrived, or closed
|
case <-s.chReadEvent:
|
||||||
return nil
|
return nil
|
||||||
case <-s.chFinEvent:
|
case <-s.chFinEvent:
|
||||||
// BUGFIX(xtaci): Fix for https://github.com/xtaci/smux/issues/82
|
// BUG(xtaci): Fix for https://github.com/xtaci/smux/issues/82
|
||||||
s.bufferLock.Lock()
|
s.bufferLock.Lock()
|
||||||
defer s.bufferLock.Unlock()
|
defer s.bufferLock.Unlock()
|
||||||
if len(s.buffers) > 0 {
|
if len(s.buffers) > 0 {
|
||||||
@ -342,7 +297,7 @@ func (s *stream) waitRead() error {
|
|||||||
//
|
//
|
||||||
// Note that the behavior when multiple goroutines write concurrently is not deterministic,
|
// Note that the behavior when multiple goroutines write concurrently is not deterministic,
|
||||||
// frames may interleave in random way.
|
// frames may interleave in random way.
|
||||||
func (s *stream) Write(b []byte) (n int, err error) {
|
func (s *Stream) Write(b []byte) (n int, err error) {
|
||||||
if s.sess.config.Version == 2 {
|
if s.sess.config.Version == 2 {
|
||||||
return s.writeV2(b)
|
return s.writeV2(b)
|
||||||
}
|
}
|
||||||
@ -356,8 +311,6 @@ func (s *stream) Write(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// check if stream has closed
|
// check if stream has closed
|
||||||
select {
|
select {
|
||||||
case <-s.chFinEvent: // passive closing
|
|
||||||
return 0, io.EOF
|
|
||||||
case <-s.die:
|
case <-s.die:
|
||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
default:
|
default:
|
||||||
@ -385,8 +338,7 @@ func (s *stream) Write(b []byte) (n int, err error) {
|
|||||||
return sent, nil
|
return sent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeV2 writes data to the stream for version 2 streams.
|
func (s *Stream) writeV2(b []byte) (n int, err error) {
|
||||||
func (s *stream) writeV2(b []byte) (n int, err error) {
|
|
||||||
// check empty input
|
// check empty input
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
@ -394,8 +346,6 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// check if stream has closed
|
// check if stream has closed
|
||||||
select {
|
select {
|
||||||
case <-s.chFinEvent:
|
|
||||||
return 0, io.EOF
|
|
||||||
case <-s.die:
|
case <-s.die:
|
||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
default:
|
default:
|
||||||
@ -422,18 +372,14 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
// even if uint32 overflow, this math still works:
|
// even if uint32 overflow, this math still works:
|
||||||
// eg1: uint32(0) - uint32(math.MaxUint32) = 1
|
// eg1: uint32(0) - uint32(math.MaxUint32) = 1
|
||||||
// eg2: int32(uint32(0) - uint32(1)) = -1
|
// eg2: int32(uint32(0) - uint32(1)) = -1
|
||||||
//
|
// security check for misbehavior
|
||||||
// basicially, you can take it as a MODULAR ARITHMETIC
|
|
||||||
inflight := int32(atomic.LoadUint32(&s.numWritten) - atomic.LoadUint32(&s.peerConsumed))
|
inflight := int32(atomic.LoadUint32(&s.numWritten) - atomic.LoadUint32(&s.peerConsumed))
|
||||||
if inflight < 0 { // security check for malformed data
|
if inflight < 0 {
|
||||||
return 0, ErrConsumed
|
return 0, ErrConsumed
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure you understand 'win' is calculated in modular arithmetic(2^32(4GB))
|
|
||||||
win := int32(atomic.LoadUint32(&s.peerWindow)) - inflight
|
win := int32(atomic.LoadUint32(&s.peerWindow)) - inflight
|
||||||
|
|
||||||
if win > 0 {
|
if win > 0 {
|
||||||
// determine how many bytes to send
|
|
||||||
if win > int32(len(b)) {
|
if win > int32(len(b)) {
|
||||||
bts = b
|
bts = b
|
||||||
b = nil
|
b = nil
|
||||||
@ -442,17 +388,13 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
b = b[win:]
|
b = b[win:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// frame split and transmit
|
|
||||||
for len(bts) > 0 {
|
for len(bts) > 0 {
|
||||||
// splitting frame
|
|
||||||
sz := len(bts)
|
sz := len(bts)
|
||||||
if sz > s.frameSize {
|
if sz > s.frameSize {
|
||||||
sz = s.frameSize
|
sz = s.frameSize
|
||||||
}
|
}
|
||||||
frame.data = bts[:sz]
|
frame.data = bts[:sz]
|
||||||
bts = bts[sz:]
|
bts = bts[sz:]
|
||||||
|
|
||||||
// transmit of frame
|
|
||||||
n, err := s.sess.writeFrameInternal(frame, deadline, CLSDATA)
|
n, err := s.sess.writeFrameInternal(frame, deadline, CLSDATA)
|
||||||
atomic.AddUint32(&s.numWritten, uint32(sz))
|
atomic.AddUint32(&s.numWritten, uint32(sz))
|
||||||
sent += n
|
sent += n
|
||||||
@ -462,12 +404,12 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is any data left to be sent,
|
// if there is any data remaining to be sent
|
||||||
// wait until stream closes, window changes or deadline reached
|
// wait until stream closes, window changes or deadline reached
|
||||||
// this blocking behavior will back propagate flow control to upper layer.
|
// this blocking behavior will inform upper layer to do flow control
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
select {
|
select {
|
||||||
case <-s.chFinEvent:
|
case <-s.chFinEvent: // if fin arrived, future window update is impossible
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
case <-s.die:
|
case <-s.die:
|
||||||
return sent, io.ErrClosedPipe
|
return sent, io.ErrClosedPipe
|
||||||
@ -475,7 +417,7 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
return sent, ErrTimeout
|
return sent, ErrTimeout
|
||||||
case <-s.sess.chSocketWriteError:
|
case <-s.sess.chSocketWriteError:
|
||||||
return sent, s.sess.socketWriteError.Load().(error)
|
return sent, s.sess.socketWriteError.Load().(error)
|
||||||
case <-s.chUpdate: // notify of remote data consuming and window update
|
case <-s.chUpdate:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -485,7 +427,7 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close implements net.Conn
|
// Close implements net.Conn
|
||||||
func (s *stream) Close() error {
|
func (s *Stream) Close() error {
|
||||||
var once bool
|
var once bool
|
||||||
var err error
|
var err error
|
||||||
s.dieOnce.Do(func() {
|
s.dieOnce.Do(func() {
|
||||||
@ -494,13 +436,7 @@ func (s *stream) Close() error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if once {
|
if once {
|
||||||
// send FIN in order
|
_, err = s.sess.writeFrame(newFrame(byte(s.sess.config.Version), cmdFIN, s.id))
|
||||||
f := newFrame(byte(s.sess.config.Version), cmdFIN, s.id)
|
|
||||||
|
|
||||||
timer := time.NewTimer(openCloseTimeout)
|
|
||||||
defer timer.Stop()
|
|
||||||
|
|
||||||
_, err = s.sess.writeFrameInternal(f, timer.C, CLSDATA)
|
|
||||||
s.sess.streamClosed(s.id)
|
s.sess.streamClosed(s.id)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
@ -510,14 +446,14 @@ func (s *stream) Close() error {
|
|||||||
|
|
||||||
// GetDieCh returns a readonly chan which can be readable
|
// GetDieCh returns a readonly chan which can be readable
|
||||||
// when the stream is to be closed.
|
// when the stream is to be closed.
|
||||||
func (s *stream) GetDieCh() <-chan struct{} {
|
func (s *Stream) GetDieCh() <-chan struct{} {
|
||||||
return s.die
|
return s.die
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetReadDeadline sets the read deadline as defined by
|
// SetReadDeadline sets the read deadline as defined by
|
||||||
// net.Conn.SetReadDeadline.
|
// net.Conn.SetReadDeadline.
|
||||||
// A zero time value disables the deadline.
|
// A zero time value disables the deadline.
|
||||||
func (s *stream) SetReadDeadline(t time.Time) error {
|
func (s *Stream) SetReadDeadline(t time.Time) error {
|
||||||
s.readDeadline.Store(t)
|
s.readDeadline.Store(t)
|
||||||
s.notifyReadEvent()
|
s.notifyReadEvent()
|
||||||
return nil
|
return nil
|
||||||
@ -526,7 +462,7 @@ func (s *stream) SetReadDeadline(t time.Time) error {
|
|||||||
// SetWriteDeadline sets the write deadline as defined by
|
// SetWriteDeadline sets the write deadline as defined by
|
||||||
// net.Conn.SetWriteDeadline.
|
// net.Conn.SetWriteDeadline.
|
||||||
// A zero time value disables the deadline.
|
// A zero time value disables the deadline.
|
||||||
func (s *stream) SetWriteDeadline(t time.Time) error {
|
func (s *Stream) SetWriteDeadline(t time.Time) error {
|
||||||
s.writeDeadline.Store(t)
|
s.writeDeadline.Store(t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -534,7 +470,7 @@ func (s *stream) SetWriteDeadline(t time.Time) error {
|
|||||||
// SetDeadline sets both read and write deadlines as defined by
|
// SetDeadline sets both read and write deadlines as defined by
|
||||||
// net.Conn.SetDeadline.
|
// net.Conn.SetDeadline.
|
||||||
// A zero time value disables the deadlines.
|
// A zero time value disables the deadlines.
|
||||||
func (s *stream) SetDeadline(t time.Time) error {
|
func (s *Stream) SetDeadline(t time.Time) error {
|
||||||
if err := s.SetReadDeadline(t); err != nil {
|
if err := s.SetReadDeadline(t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -545,10 +481,10 @@ func (s *stream) SetDeadline(t time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// session closes
|
// session closes
|
||||||
func (s *stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }
|
func (s *Stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }
|
||||||
|
|
||||||
// LocalAddr satisfies net.Conn interface
|
// LocalAddr satisfies net.Conn interface
|
||||||
func (s *stream) LocalAddr() net.Addr {
|
func (s *Stream) LocalAddr() net.Addr {
|
||||||
if ts, ok := s.sess.conn.(interface {
|
if ts, ok := s.sess.conn.(interface {
|
||||||
LocalAddr() net.Addr
|
LocalAddr() net.Addr
|
||||||
}); ok {
|
}); ok {
|
||||||
@ -558,7 +494,7 @@ func (s *stream) LocalAddr() net.Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoteAddr satisfies net.Conn interface
|
// RemoteAddr satisfies net.Conn interface
|
||||||
func (s *stream) RemoteAddr() net.Addr {
|
func (s *Stream) RemoteAddr() net.Addr {
|
||||||
if ts, ok := s.sess.conn.(interface {
|
if ts, ok := s.sess.conn.(interface {
|
||||||
RemoteAddr() net.Addr
|
RemoteAddr() net.Addr
|
||||||
}); ok {
|
}); ok {
|
||||||
@ -568,7 +504,7 @@ func (s *stream) RemoteAddr() net.Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pushBytes append buf to buffers
|
// pushBytes append buf to buffers
|
||||||
func (s *stream) pushBytes(buf []byte) (written int, err error) {
|
func (s *Stream) pushBytes(buf []byte) (written int, err error) {
|
||||||
s.bufferLock.Lock()
|
s.bufferLock.Lock()
|
||||||
s.buffers = append(s.buffers, buf)
|
s.buffers = append(s.buffers, buf)
|
||||||
s.heads = append(s.heads, buf)
|
s.heads = append(s.heads, buf)
|
||||||
@ -577,7 +513,7 @@ func (s *stream) pushBytes(buf []byte) (written int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// recycleTokens transform remaining bytes to tokens(will truncate buffer)
|
// recycleTokens transform remaining bytes to tokens(will truncate buffer)
|
||||||
func (s *stream) recycleTokens() (n int) {
|
func (s *Stream) recycleTokens() (n int) {
|
||||||
s.bufferLock.Lock()
|
s.bufferLock.Lock()
|
||||||
for k := range s.buffers {
|
for k := range s.buffers {
|
||||||
n += len(s.buffers[k])
|
n += len(s.buffers[k])
|
||||||
@ -590,7 +526,7 @@ func (s *stream) recycleTokens() (n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// notify read event
|
// notify read event
|
||||||
func (s *stream) notifyReadEvent() {
|
func (s *Stream) notifyReadEvent() {
|
||||||
select {
|
select {
|
||||||
case s.chReadEvent <- struct{}{}:
|
case s.chReadEvent <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
@ -598,7 +534,7 @@ func (s *stream) notifyReadEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update command
|
// update command
|
||||||
func (s *stream) update(consumed uint32, window uint32) {
|
func (s *Stream) update(consumed uint32, window uint32) {
|
||||||
atomic.StoreUint32(&s.peerConsumed, consumed)
|
atomic.StoreUint32(&s.peerConsumed, consumed)
|
||||||
atomic.StoreUint32(&s.peerWindow, window)
|
atomic.StoreUint32(&s.peerWindow, window)
|
||||||
select {
|
select {
|
||||||
@ -608,7 +544,7 @@ func (s *stream) update(consumed uint32, window uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mark this stream has been closed in protocol
|
// mark this stream has been closed in protocol
|
||||||
func (s *stream) fin() {
|
func (s *Stream) fin() {
|
||||||
s.finEventOnce.Do(func() {
|
s.finEventOnce.Do(func() {
|
||||||
close(s.chFinEvent)
|
close(s.chFinEvent)
|
||||||
})
|
})
|
||||||
|
@ -33,8 +33,6 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf := pool.GetBytesBuffer()
|
buf := pool.GetBytesBuffer()
|
||||||
defer pool.PutBytesBuffer(buf)
|
|
||||||
|
|
||||||
buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n")
|
buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n")
|
||||||
buf.WriteString("Host: " + addr + "\r\n")
|
buf.WriteString("Host: " + addr + "\r\n")
|
||||||
buf.WriteString("Proxy-Connection: Keep-Alive\r\n")
|
buf.WriteString("Proxy-Connection: Keep-Alive\r\n")
|
||||||
@ -47,6 +45,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
|||||||
// header ended
|
// header ended
|
||||||
buf.WriteString("\r\n")
|
buf.WriteString("\r\n")
|
||||||
_, err = rc.Write(buf.Bytes())
|
_, err = rc.Write(buf.Bytes())
|
||||||
|
pool.PutBytesBuffer(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
// Source code from:
|
// Source code from:
|
||||||
// https://github.com/linuxkit/virtsock/tree/master/pkg/vsock
|
// https://github.com/linuxkit/virtsock/tree/master/pkg/vsock
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
Loading…
Reference in New Issue
Block a user