Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
G
gost
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
gost
Commits
c02bc32c
Commit
c02bc32c
authored
Aug 03, 2017
by
rui.zheng
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add HTTP2 proxy client support
parent
36b61e05
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
90 additions
and
181 deletions
+90
-181
gost/examples/bench/cli.go
gost/examples/bench/cli.go
+9
-10
gost/examples/bench/srv.go
gost/examples/bench/srv.go
+1
-2
gost/http2.go
gost/http2.go
+78
-169
gost/log.go
gost/log.go
+2
-0
No files found.
gost/examples/bench/cli.go
View file @
c02bc32c
...
...
@@ -11,6 +11,7 @@ import (
"time"
"github.com/ginuerzh/gost/gost"
"golang.org/x/net/http2"
)
var
(
...
...
@@ -25,6 +26,7 @@ func init() {
flag
.
IntVar
(
&
requests
,
"n"
,
1
,
"Number of requests to perform"
)
flag
.
IntVar
(
&
concurrency
,
"c"
,
1
,
"Number of multiple requests to make at a time"
)
flag
.
BoolVar
(
&
quiet
,
"q"
,
false
,
"quiet mode"
)
flag
.
BoolVar
(
&
http2
.
VerboseLogs
,
"v"
,
false
,
"HTTP2 verbose logs"
)
flag
.
BoolVar
(
&
gost
.
Debug
,
"d"
,
false
,
"debug mode"
)
flag
.
Parse
()
...
...
@@ -103,17 +105,13 @@ func main() {
*/
/*
// http2
+tls, http2+tcp
// http2
gost.Node{
Addr: "127.0.0.1:1443",
Client: gost.NewClient(
gost.HTTP2Connector(url.UserPassword("admin", "123456")),
gost.HTTP2Transporter(
nil,
&tls.Config{InsecureSkipVerify: true}, // or nil, will use h2c mode (http2+tcp).
time.Second*1,
),
),
Client: &gost.Client{
Connector: gost.HTTP2Connector(url.UserPassword("admin", "123456")),
Transporter: gost.HTTP2Transporter(nil),
},
},
*/
...
...
@@ -149,13 +147,14 @@ func main() {
},
},
*/
// socks5+h2
gost
.
Node
{
Addr
:
"localhost:8443"
,
Client
:
&
gost
.
Client
{
// Connector: gost.HTTPConnector(url.UserPassword("admin", "123456")),
Connector
:
gost
.
SOCKS5Connector
(
url
.
UserPassword
(
"admin"
,
"123456"
)),
// Transporter: gost.H2CTransporter(), // HTTP2 h2c mode
Transporter
:
gost
.
H2Transporter
(),
// HTTP2 h2
Transporter
:
gost
.
H2Transporter
(
nil
),
// HTTP2 h2
},
},
)
...
...
gost/examples/bench/srv.go
View file @
c02bc32c
...
...
@@ -222,8 +222,7 @@ func http2Server() {
// http2.VerboseLogs = true
s
:=
&
gost
.
Server
{}
ln
,
err
:=
gost
.
TLSListener
(
":1443"
,
tlsConfig
())
// HTTP2 h2 mode
// ln, err := gost.TCPListener(":1443") // HTTP2 h2c mode
ln
,
err
:=
gost
.
HTTP2Listener
(
":1443"
,
tlsConfig
())
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
...
...
gost/http2.go
View file @
c02bc32c
...
...
@@ -2,12 +2,10 @@ package gost
import
(
"bufio"
"context"
"crypto/tls"
"encoding/base64"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httputil"
...
...
@@ -31,37 +29,36 @@ func HTTP2Connector(user *url.Userinfo) Connector {
}
func
(
c
*
http2Connector
)
Connect
(
conn
net
.
Conn
,
addr
string
)
(
net
.
Conn
,
error
)
{
cc
,
ok
:=
conn
.
(
*
http2
Dummy
Conn
)
cc
,
ok
:=
conn
.
(
*
http2
Client
Conn
)
if
!
ok
{
return
nil
,
errors
.
New
(
"wrong connection type"
)
}
pr
,
pw
:=
io
.
Pipe
()
u
:=
&
url
.
URL
{
Host
:
addr
,
}
req
,
err
:=
http
.
NewRequest
(
http
.
MethodConnect
,
u
.
String
(),
ioutil
.
NopCloser
(
pr
))
if
err
!=
nil
{
log
.
Logf
(
"[http2] %s - %s : %s"
,
cc
.
raddr
,
addr
,
err
)
return
nil
,
err
req
:=
&
http
.
Request
{
Method
:
http
.
MethodConnect
,
URL
:
&
url
.
URL
{
Scheme
:
"https"
,
Host
:
addr
},
Header
:
make
(
http
.
Header
),
Proto
:
"HTTP/2.0"
,
ProtoMajor
:
2
,
ProtoMinor
:
0
,
Body
:
pr
,
Host
:
addr
,
ContentLength
:
-
1
,
}
// req.Header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
if
c
.
User
!=
nil
{
req
.
Header
.
Set
(
"Proxy-Authorization"
,
"Basic "
+
base64
.
StdEncoding
.
EncodeToString
([]
byte
(
c
.
User
.
String
())))
}
req
.
ProtoMajor
=
2
req
.
ProtoMinor
=
0
if
Debug
{
dump
,
_
:=
httputil
.
DumpRequest
(
req
,
false
)
log
.
Log
(
"[http2]"
,
string
(
dump
))
}
resp
,
err
:=
cc
.
c
onn
.
RoundTrip
(
req
)
resp
,
err
:=
cc
.
c
lient
.
Do
(
req
)
if
err
!=
nil
{
log
.
Logf
(
"[http2] %s - %s : %s"
,
cc
.
raddr
,
addr
,
err
)
return
nil
,
err
}
if
Debug
{
dump
,
_
:=
httputil
.
DumpResponse
(
resp
,
false
)
log
.
Log
(
"[http2]"
,
string
(
dump
))
...
...
@@ -71,72 +68,64 @@ func (c *http2Connector) Connect(conn net.Conn, addr string) (net.Conn, error) {
resp
.
Body
.
Close
()
return
nil
,
errors
.
New
(
resp
.
Status
)
}
hc
:=
&
http2Conn
{
r
:
resp
.
Body
,
w
:
pw
}
hc
.
remoteAddr
,
_
=
net
.
ResolveTCPAddr
(
"tcp"
,
cc
.
raddr
)
hc
:=
&
http2Conn
{
r
:
resp
.
Body
,
w
:
pw
,
closed
:
make
(
chan
struct
{}),
}
hc
.
remoteAddr
,
_
=
net
.
ResolveTCPAddr
(
"tcp"
,
addr
)
hc
.
localAddr
,
_
=
net
.
ResolveTCPAddr
(
"tcp"
,
cc
.
addr
)
return
hc
,
nil
}
type
http2Transporter
struct
{
tlsConfig
*
tls
.
Config
tr
*
http2
.
Transport
chain
*
Chain
sessions
map
[
string
]
*
http2Session
sessionMutex
sync
.
Mutex
pingInterval
time
.
Duration
}
// HTTP2Transporter creates a Transporter that is used by HTTP2 proxy client.
//
// Optional chain is a proxy chain that can be used to establish a connection with the HTTP2 server.
//
// Optional config is a TLS config for TLS handshake, if is nil, will use h2c mode.
//
// Optional ping is the ping interval, if is zero, ping will not be enabled.
func
HTTP2Transporter
(
chain
*
Chain
,
config
*
tls
.
Config
,
ping
time
.
Duration
)
Transporter
{
clients
map
[
string
]
*
http
.
Client
clientMutex
sync
.
Mutex
tlsConfig
*
tls
.
Config
}
// HTTP2Transporter creates a Transporter that is used by HTTP2 h2 proxy client.
func
HTTP2Transporter
(
config
*
tls
.
Config
)
Transporter
{
if
config
==
nil
{
config
=
&
tls
.
Config
{
InsecureSkipVerify
:
true
}
}
return
&
http2Transporter
{
tlsConfig
:
config
,
tr
:
new
(
http2
.
Transport
),
chain
:
chain
,
pingInterval
:
ping
,
sessions
:
make
(
map
[
string
]
*
http2Session
),
clients
:
make
(
map
[
string
]
*
http
.
Client
),
tlsConfig
:
config
,
}
}
func
(
tr
*
http2Transporter
)
Dial
(
addr
string
,
options
...
DialOption
)
(
net
.
Conn
,
error
)
{
tr
.
sessionMutex
.
Lock
()
defer
tr
.
sessionMutex
.
Unlock
()
opts
:=
&
DialOptions
{}
for
_
,
option
:=
range
options
{
option
(
opts
)
}
session
,
ok
:=
tr
.
sessions
[
addr
]
tr
.
clientMutex
.
Lock
()
client
,
ok
:=
tr
.
clients
[
addr
]
if
!
ok
{
conn
,
err
:=
tr
.
chain
.
Dial
(
addr
)
if
err
!=
nil
{
return
nil
,
err
}
if
tr
.
tlsConfig
!=
nil
{
tc
:=
tls
.
Client
(
conn
,
tr
.
tlsConfig
)
if
err
:=
tc
.
Handshake
();
err
!=
nil
{
return
nil
,
err
}
conn
=
tc
transport
:=
http2
.
Transport
{
TLSClientConfig
:
tr
.
tlsConfig
,
DialTLS
:
func
(
network
,
adr
string
,
cfg
*
tls
.
Config
)
(
net
.
Conn
,
error
)
{
conn
,
err
:=
opts
.
Chain
.
Dial
(
addr
)
if
err
!=
nil
{
return
nil
,
err
}
return
wrapTLSClient
(
conn
,
cfg
)
},
}
c
c
,
err
:=
tr
.
tr
.
NewClientConn
(
conn
)
if
err
!=
nil
{
return
nil
,
err
c
lient
=
&
http
.
Client
{
Transport
:
&
transport
,
Timeout
:
opts
.
Timeout
,
}
session
=
newHTTP2Session
(
conn
,
cc
,
tr
.
pingInterval
)
tr
.
sessions
[
addr
]
=
session
}
if
!
session
.
Healthy
()
{
session
.
Close
()
delete
(
tr
.
sessions
,
addr
)
// TODO: we could re-connect to the addr automatically.
return
nil
,
errors
.
New
(
"connection is dead"
)
tr
.
clients
[
addr
]
=
client
}
tr
.
clientMutex
.
Unlock
()
return
&
http2
Dummy
Conn
{
raddr
:
addr
,
c
onn
:
session
.
clientConn
,
return
&
http2
Client
Conn
{
addr
:
addr
,
c
lient
:
client
,
},
nil
}
...
...
@@ -154,13 +143,18 @@ type h2Transporter struct {
tlsConfig
*
tls
.
Config
}
func
H2Transporter
()
Transporter
{
// H2Transporter creates a Transporter that is used by HTTP2 h2 tunnel client.
func
H2Transporter
(
config
*
tls
.
Config
)
Transporter
{
if
config
==
nil
{
config
=
&
tls
.
Config
{
InsecureSkipVerify
:
true
}
}
return
&
h2Transporter
{
clients
:
make
(
map
[
string
]
*
http
.
Client
),
tlsConfig
:
&
tls
.
Config
{
InsecureSkipVerify
:
true
}
,
tlsConfig
:
config
,
}
}
// H2CTransporter creates a Transporter that is used by HTTP2 h2c tunnel client.
func
H2CTransporter
()
Transporter
{
return
&
h2Transporter
{
clients
:
make
(
map
[
string
]
*
http
.
Client
),
...
...
@@ -186,7 +180,7 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err
if
tr
.
tlsConfig
==
nil
{
return
conn
,
nil
}
return
wrapTLSClient
(
conn
,
tr
.
tlsConfi
g
)
return
wrapTLSClient
(
conn
,
cf
g
)
},
}
client
=
&
http
.
Client
{
...
...
@@ -277,11 +271,10 @@ func (h *http2Handler) Handle(conn net.Conn) {
}
func
(
h
*
http2Handler
)
roundTrip
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
target
:=
r
.
Header
.
Get
(
"Gost-Target"
)
// compitable with old version
target
:=
r
.
Header
.
Get
(
"Gost-Target"
)
if
target
==
""
{
target
=
r
.
Host
}
// target := r.Host
if
!
strings
.
Contains
(
target
,
":"
)
{
target
+=
":80"
}
...
...
@@ -391,6 +384,7 @@ type http2Listener struct {
errChan
chan
error
}
// HTTP2Listener creates a Listener for HTTP2 proxy server.
func
HTTP2Listener
(
addr
string
,
config
*
tls
.
Config
)
(
Listener
,
error
)
{
l
:=
&
http2Listener
{
connChan
:
make
(
chan
*
http2ServerConn
,
1024
),
...
...
@@ -596,91 +590,6 @@ func (l *h2Listener) Accept() (conn net.Conn, err error) {
return
}
type
http2Session
struct
{
conn
net
.
Conn
clientConn
*
http2
.
ClientConn
closeChan
chan
struct
{}
pingChan
chan
struct
{}
}
func
newHTTP2Session
(
conn
net
.
Conn
,
clientConn
*
http2
.
ClientConn
,
interval
time
.
Duration
)
*
http2Session
{
session
:=
&
http2Session
{
conn
:
conn
,
clientConn
:
clientConn
,
closeChan
:
make
(
chan
struct
{}),
}
if
interval
>
0
{
session
.
pingChan
=
make
(
chan
struct
{})
go
session
.
Ping
(
interval
)
}
return
session
}
func
(
s
*
http2Session
)
Ping
(
interval
time
.
Duration
)
{
if
interval
<=
0
{
return
}
defer
close
(
s
.
pingChan
)
log
.
Log
(
"[http2] ping is enabled, interval:"
,
interval
)
baseCtx
:=
context
.
Background
()
t
:=
time
.
NewTicker
(
interval
)
retries
:=
PingRetries
for
{
select
{
case
<-
t
.
C
:
if
Debug
{
log
.
Log
(
"[http2] sending ping"
)
}
if
!
s
.
clientConn
.
CanTakeNewRequest
()
{
log
.
Logf
(
"[http2] connection is dead"
)
return
}
ctx
,
cancel
:=
context
.
WithTimeout
(
baseCtx
,
PingTimeout
)
if
err
:=
s
.
clientConn
.
Ping
(
ctx
);
err
!=
nil
{
log
.
Logf
(
"[http2] ping: %s"
,
err
)
if
retries
>
0
{
retries
--
log
.
Log
(
"[http2] retry ping"
)
cancel
()
continue
}
cancel
()
return
}
if
Debug
{
log
.
Log
(
"[http2] ping OK"
)
}
cancel
()
retries
=
PingRetries
case
<-
s
.
closeChan
:
return
}
}
}
func
(
s
*
http2Session
)
Healthy
()
bool
{
select
{
case
<-
s
.
pingChan
:
return
false
default
:
}
return
s
.
clientConn
.
CanTakeNewRequest
()
}
func
(
s
*
http2Session
)
Close
()
error
{
select
{
case
<-
s
.
closeChan
:
default
:
close
(
s
.
closeChan
)
}
return
nil
}
// HTTP2 connection, wrapped up just like a net.Conn
type
http2Conn
struct
{
r
io
.
Reader
...
...
@@ -780,41 +689,41 @@ func (c *http2ServerConn) SetWriteDeadline(t time.Time) error {
return
&
net
.
OpError
{
Op
:
"set"
,
Net
:
"http2"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"deadline not supported"
)}
}
//
Dummy HTTP2 connection.
type
http2
Dummy
Conn
struct
{
raddr
string
c
onn
*
http2
.
ClientConn
//
a dummy HTTP2 client conn used by HTTP2 client connector
type
http2
Client
Conn
struct
{
addr
string
c
lient
*
http
.
Client
}
func
(
c
*
http2
Dummy
Conn
)
Read
(
b
[]
byte
)
(
n
int
,
err
error
)
{
func
(
c
*
http2
Client
Conn
)
Read
(
b
[]
byte
)
(
n
int
,
err
error
)
{
return
0
,
&
net
.
OpError
{
Op
:
"read"
,
Net
:
"http2"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"read not supported"
)}
}
func
(
c
*
http2
Dummy
Conn
)
Write
(
b
[]
byte
)
(
n
int
,
err
error
)
{
func
(
c
*
http2
Client
Conn
)
Write
(
b
[]
byte
)
(
n
int
,
err
error
)
{
return
0
,
&
net
.
OpError
{
Op
:
"write"
,
Net
:
"http2"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"write not supported"
)}
}
func
(
c
*
http2
Dummy
Conn
)
Close
()
error
{
func
(
c
*
http2
Client
Conn
)
Close
()
error
{
return
nil
}
func
(
c
*
http2
Dummy
Conn
)
LocalAddr
()
net
.
Addr
{
func
(
c
*
http2
Client
Conn
)
LocalAddr
()
net
.
Addr
{
return
nil
}
func
(
c
*
http2
Dummy
Conn
)
RemoteAddr
()
net
.
Addr
{
func
(
c
*
http2
Client
Conn
)
RemoteAddr
()
net
.
Addr
{
return
nil
}
func
(
c
*
http2
Dummy
Conn
)
SetDeadline
(
t
time
.
Time
)
error
{
func
(
c
*
http2
Client
Conn
)
SetDeadline
(
t
time
.
Time
)
error
{
return
&
net
.
OpError
{
Op
:
"set"
,
Net
:
"http2"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"deadline not supported"
)}
}
func
(
c
*
http2
Dummy
Conn
)
SetReadDeadline
(
t
time
.
Time
)
error
{
func
(
c
*
http2
Client
Conn
)
SetReadDeadline
(
t
time
.
Time
)
error
{
return
&
net
.
OpError
{
Op
:
"set"
,
Net
:
"http2"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"deadline not supported"
)}
}
func
(
c
*
http2
Dummy
Conn
)
SetWriteDeadline
(
t
time
.
Time
)
error
{
func
(
c
*
http2
Client
Conn
)
SetWriteDeadline
(
t
time
.
Time
)
error
{
return
&
net
.
OpError
{
Op
:
"set"
,
Net
:
"http2"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"deadline not supported"
)}
}
...
...
gost/log.go
View file @
c02bc32c
...
...
@@ -9,6 +9,7 @@ func init() {
log
.
SetFlags
(
log
.
LstdFlags
|
log
.
Lshortfile
)
}
// LogLogger uses the standard log package as the logger
type
LogLogger
struct
{
}
...
...
@@ -20,6 +21,7 @@ func (l *LogLogger) Logf(format string, v ...interface{}) {
log
.
Output
(
3
,
fmt
.
Sprintf
(
format
,
v
...
))
}
// NopLogger is a null logger that discards the log outputs
type
NopLogger
struct
{
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment