-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtunnel.go
132 lines (111 loc) · 3.19 KB
/
tunnel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package httptunnel
import (
"bufio"
"context"
"io"
"net/http"
"time"
"github.com/mediabuyerbot/httpclient"
"github.com/mediabuyerbot/go-wirenet"
)
const (
DefaultReTryCount = 10
DefaultHTTPTimeout = 120 * time.Second
)
// ReadRequest reads and parses an incoming request from b.
type ReadRequest func(*bufio.Reader) (*http.Request, error)
// ReadResponse reads and returns an HTTP response from r.
type ReadResponse func(*bufio.Reader, *http.Request) (*http.Response, error)
// RequestFunc
type RequestFunc func(r *http.Request)
// ResponseFunc
type ResponseFunc func(r *http.Response)
type Tunnel struct {
client httpclient.Client
errorHandler ErrorHandler
readRequest ReadRequest
before []RequestFunc
after []ResponseFunc
}
func New(
options ...TunnelOption,
) *Tunnel {
defaultClient, _ := httpclient.New(
httpclient.WithRetryCount(DefaultReTryCount),
httpclient.WithTimeout(DefaultHTTPTimeout),
)
tunnel := &Tunnel{
readRequest: http.ReadRequest,
client: defaultClient,
errorHandler: NewLogErrorHandler(),
before: []RequestFunc{},
after: []ResponseFunc{},
}
for _, option := range options {
if option == nil {
continue
}
option(tunnel)
}
return tunnel
}
// TunnelOption sets an optional parameter for tunnel instance.
type TunnelOption func(tunnel *Tunnel)
// SetTunnelErrorHandler is used to handle non-terminal errors. By default,
// non-terminal errors are ignored. This is intended as a diagnostic measure.
func SetTunnelErrorHandler(errorHandler ErrorHandler) TunnelOption {
return func(tunnel *Tunnel) { tunnel.errorHandler = errorHandler }
}
// SetClient sets the HTTP client.
func SetClient(client httpclient.Client) TunnelOption {
return func(tunnel *Tunnel) { tunnel.client = client }
}
// SetReadRequest sets the parser for incoming request.
func SetReadRequest(r ReadRequest) TunnelOption {
return func(tunnel *Tunnel) { tunnel.readRequest = r }
}
// RequestHook functions are executed on the HTTP request object before the
// request is send.
func RequestHook(before ...RequestFunc) TunnelOption {
return func(tunnel *Tunnel) { tunnel.before = append(tunnel.before, before...) }
}
// ResponseHook functions are executed on the HTTP response writer after the
// endpoint is invoked, but before anything is written to the client.
func ResponseHook(after ...ResponseFunc) TunnelOption {
return func(tunnel *Tunnel) { tunnel.after = append(tunnel.after, after...) }
}
// Handle implements the Handler interface.
func (tunnel Tunnel) Handle(ctx context.Context, stream wirenet.Stream) {
defer stream.Close()
reader := stream.Reader()
writer := stream.Writer()
for !stream.IsClosed() {
req, err := tunnel.readRequest(bufio.NewReader(reader))
if err != nil {
if err == io.EOF {
break
}
tunnel.errorHandler.Handle(ctx, err)
break
}
reader.Close()
for _, f := range tunnel.before {
f(req)
}
req.RequestURI = ""
resp, err := tunnel.client.Do(req)
if err != nil {
tunnel.errorHandler.Handle(ctx, err)
break
}
for _, f := range tunnel.after {
f(resp)
}
if err := resp.Write(writer); err != nil {
tunnel.errorHandler.Handle(ctx, err)
break
}
resp.Body.Close()
writer.Close()
}
}