@@ -14,9 +14,9 @@ import (
14
14
15
15
"github.com/gin-gonic/gin"
16
16
17
- "github.com/smrz2001 /go-mirror /common/config"
18
- "github.com/smrz2001 /go-mirror /common/logging"
19
- "github.com/smrz2001 /go-mirror /common/metric"
17
+ "github.com/3box /go-proxy /common/config"
18
+ "github.com/3box /go-proxy /common/logging"
19
+ "github.com/3box /go-proxy /common/metric"
20
20
)
21
21
22
22
type ProxyController interface {
@@ -36,6 +36,23 @@ type proxyController struct {
36
36
transport * http.Transport
37
37
}
38
38
39
+ type requestType string
40
+
41
+ const (
42
+ proxyRequest requestType = "proxy"
43
+ mirrorRequest = "mirror"
44
+ )
45
+
46
+ // Create a struct to hold request context
47
+ type requestContext struct {
48
+ reqType requestType
49
+ ginContext * gin.Context
50
+ request * http.Request
51
+ bodyBytes []byte
52
+ startTime time.Time
53
+ targetURL * url.URL
54
+ }
55
+
39
56
func NewProxyController (
40
57
ctx context.Context ,
41
58
cfg * config.Config ,
@@ -57,15 +74,10 @@ func NewProxyController(
57
74
transport := & http.Transport {
58
75
Proxy : http .ProxyFromEnvironment ,
59
76
DialContext : (& net.Dialer {
60
- Timeout : 30 * time .Second ,
61
- KeepAlive : 30 * time .Second ,
77
+ Timeout : cfg .Proxy .DialTimeout ,
62
78
}).DialContext ,
63
- ForceAttemptHTTP2 : true ,
64
- MaxIdleConns : 100 ,
65
- MaxIdleConnsPerHost : 10 ,
66
- IdleConnTimeout : 90 * time .Second ,
67
- TLSHandshakeTimeout : 10 * time .Second ,
68
- ExpectContinueTimeout : 1 * time .Second ,
79
+ ForceAttemptHTTP2 : true ,
80
+ IdleConnTimeout : cfg .Proxy .IdleTimeout ,
69
81
}
70
82
71
83
return & proxyController {
@@ -87,149 +99,111 @@ func (_this proxyController) proxyRequest(c *gin.Context) {
87
99
c .JSON (http .StatusInternalServerError , gin.H {"error" : "failed to read request" })
88
100
return
89
101
}
102
+ // Ignore error since we are closing the body anyway
103
+ _ = c .Request .Body .Close ()
90
104
91
- // Close the original body
92
- c .Request .Body .Close ()
105
+ // Handle proxy request
106
+ _this .handleRequest (requestContext {
107
+ reqType : proxyRequest ,
108
+ ginContext : c ,
109
+ request : c .Request ,
110
+ bodyBytes : bodyBytes ,
111
+ startTime : time .Now (),
112
+ targetURL : _this .target ,
113
+ })
93
114
94
- start := time .Now ()
115
+ // Handle mirror request if configured
116
+ if _this .mirror != nil {
117
+ go func () {
118
+ ctx , cancel := context .WithTimeout (_this .ctx , _this .cfg .Proxy .MirrorTimeout )
119
+ defer cancel ()
120
+
121
+ _this .handleRequest (requestContext {
122
+ reqType : mirrorRequest ,
123
+ request : c .Request .Clone (ctx ),
124
+ bodyBytes : bodyBytes ,
125
+ startTime : time .Now (),
126
+ targetURL : _this .mirror ,
127
+ })
128
+ }()
129
+ }
130
+ }
95
131
96
- // Create the proxy request
97
- proxyReq := c .Request .Clone (c .Request .Context ())
98
- proxyReq .URL .Scheme = _this .target .Scheme
99
- proxyReq .URL .Host = _this .target .Host
100
- proxyReq .Host = _this .target .Host
101
- proxyReq .Body = io .NopCloser (bytes .NewBuffer (bodyBytes ))
132
+ func (_this proxyController ) handleRequest (reqCtx requestContext ) {
133
+ // Prepare the request
134
+ req := reqCtx .request .Clone (reqCtx .request .Context ())
135
+ req .URL .Scheme = reqCtx .targetURL .Scheme
136
+ req .URL .Host = reqCtx .targetURL .Host
137
+ req .Host = reqCtx .targetURL .Host
138
+ req .Body = io .NopCloser (bytes .NewBuffer (reqCtx .bodyBytes ))
102
139
103
140
// Log outbound request
104
- _this .logger .Debugw ("outbound request" ,
105
- "method" , proxyReq .Method ,
106
- "url" , proxyReq .URL .String (),
107
- "headers" , proxyReq .Header ,
141
+ _this .logger .Debugw (fmt . Sprintf ( "%s request", reqCtx . reqType ) ,
142
+ "method" , req .Method ,
143
+ "url" , req .URL .String (),
144
+ "headers" , req .Header ,
108
145
)
109
146
110
- // Record request with normalized path
111
- err = _this . metrics . RecordRequest ( _this . ctx , metric .MetricProxyRequest , proxyReq . Method , proxyReq . URL . Path )
112
- if err != nil {
113
- _this . logger . Errorw ( "failed to record proxy request metric" , "error" , err )
147
+ // Record metrics
148
+ metricName := metric .MetricProxyRequest
149
+ if reqCtx . reqType == mirrorRequest {
150
+ metricName = metric . MetricMirrorRequest
114
151
}
115
152
116
- // Make the proxy request
117
- resp , err := _this .transport .RoundTrip (proxyReq )
153
+ if err := _this .metrics .RecordRequest (_this .ctx , metricName , req .Method , req .URL .Path ); err != nil {
154
+ _this .logger .Errorw ("failed to record request metric" , "error" , err )
155
+ }
156
+
157
+ // Make the request
158
+ resp , err := _this .transport .RoundTrip (req )
118
159
if err != nil {
119
- _this .logger .Errorw ("proxy error" , "error" , err )
120
- c .JSON (http .StatusBadGateway , gin.H {"error" : "proxy error" })
160
+ _this .logger .Errorw (fmt .Sprintf ("%s error" , reqCtx .reqType ), "error" , err )
161
+ if reqCtx .reqType == proxyRequest {
162
+ reqCtx .ginContext .JSON (http .StatusBadGateway , gin.H {"error" : "proxy error" })
163
+ }
121
164
return
122
165
}
123
- defer resp .Body .Close ()
166
+ // Ignore error since we are closing the body anyway
167
+ defer func () { _ = resp .Body .Close () }()
124
168
125
- // Read the response body
169
+ // Process response
126
170
respBody , err := io .ReadAll (resp .Body )
127
171
if err != nil {
128
- _this .logger .Errorw ("failed to read proxy response body" , "error" , err )
129
- c .JSON (http .StatusInternalServerError , gin.H {"error" : "failed to read response" })
172
+ _this .logger .Errorw (fmt .Sprintf ("failed to read %s response" , reqCtx .reqType ), "error" , err )
173
+ if reqCtx .reqType == proxyRequest {
174
+ reqCtx .ginContext .JSON (http .StatusInternalServerError , gin.H {"error" : "failed to read response" })
175
+ }
130
176
return
131
177
}
132
178
133
- // Log response details
134
- _this .logger .Debugw ("received proxy response" ,
179
+ // Log response
180
+ _this .logger .Debugw (fmt . Sprintf ( "%s response", reqCtx . reqType ) ,
135
181
"status" , resp .StatusCode ,
136
- "content_length" , len (respBody ),
137
- "content_type" , resp .Header .Get ("Content-Type" ),
182
+ "content length" , len (respBody ),
183
+ "headers" , resp .Header ,
184
+ "latency" , time .Since (reqCtx .startTime ),
138
185
)
139
186
187
+ // Record metrics
140
188
attrs := []attribute.KeyValue {
141
- attribute .String ("method" , proxyReq .Method ),
142
- attribute .String ("path" , proxyReq .URL .Path ),
189
+ attribute .String ("method" , req .Method ),
190
+ attribute .String ("path" , req .URL .Path ),
143
191
attribute .Int ("status_code" , resp .StatusCode ),
144
- // Group status codes into categories
145
192
attribute .String ("status_class" , fmt .Sprintf ("%dxx" , resp .StatusCode / 100 )),
146
193
}
147
194
148
- err = _this .metrics .RecordDuration (_this .ctx , metric .MetricProxyRequest , time .Since (start ), attrs ... )
149
- if err != nil {
150
- _this .logger .Errorw ("failed to record proxy request duration metric" , "error" , err )
151
- }
152
-
153
- // Send mirror request if configured
154
- if _this .mirror != nil {
155
- // Create a new context for the mirror request
156
- mirrorReq := c .Request .Clone (_this .ctx )
157
- go _this .mirrorRequest (mirrorReq , bodyBytes )
195
+ if err := _this .metrics .RecordDuration (_this .ctx , metricName , time .Since (reqCtx .startTime ), attrs ... ); err != nil {
196
+ _this .logger .Errorw ("failed to record duration metric" , "error" , err )
158
197
}
159
198
160
- // Copy response headers
161
- for k , vv := range resp .Header {
162
- for _ , v := range vv {
163
- c .Header (k , v )
199
+ // Write response for proxy requests only
200
+ if reqCtx .reqType == proxyRequest {
201
+ for k , vv := range resp .Header {
202
+ for _ , v := range vv {
203
+ reqCtx .ginContext .Header (k , v )
204
+ }
164
205
}
165
- }
166
-
167
- // Write the response
168
- c .Data (resp .StatusCode , resp .Header .Get ("Content-Type" ), respBody )
169
- }
170
-
171
- func (_this proxyController ) mirrorRequest (mirrorReq * http.Request , bodyBytes []byte ) {
172
- // Create a context with timeout for the mirror request
173
- ctx , cancel := context .WithTimeout (_this .ctx , 30 * time .Second )
174
- defer cancel ()
175
-
176
- // Use the new context
177
- mirrorReq = mirrorReq .WithContext (ctx )
178
-
179
- start := time .Now ()
180
-
181
- // Create the mirror request
182
- mirrorReq .URL .Scheme = _this .mirror .Scheme
183
- mirrorReq .URL .Host = _this .mirror .Host
184
- mirrorReq .Host = _this .mirror .Host
185
- mirrorReq .Body = io .NopCloser (bytes .NewBuffer (bodyBytes ))
186
-
187
- // Log mirror request
188
- _this .logger .Debugw ("mirror request" ,
189
- "method" , mirrorReq .Method ,
190
- "url" , mirrorReq .URL .String (),
191
- "headers" , mirrorReq .Header ,
192
- )
193
-
194
- // Record request with normalized path
195
- err := _this .metrics .RecordRequest (_this .ctx , metric .MetricMirrorRequest , mirrorReq .Method , mirrorReq .URL .Path )
196
- if err != nil {
197
- _this .logger .Errorw ("failed to record mirror request metric" , "error" , err )
198
- }
199
-
200
- // Make the mirror request
201
- resp , err := _this .transport .RoundTrip (mirrorReq )
202
- if err != nil {
203
- _this .logger .Errorw ("mirror error" , "error" , err )
204
- return
205
- }
206
- defer resp .Body .Close ()
207
-
208
- // Read and log the mirror response
209
- body , err := io .ReadAll (resp .Body )
210
- if err != nil {
211
- _this .logger .Errorw ("failed to read mirror response" , "error" , err )
212
- return
213
- }
214
-
215
- // Log mirror response
216
- _this .logger .Debugw ("mirror response" ,
217
- "status" , resp .StatusCode ,
218
- "content_length" , len (body ),
219
- "content_type" , resp .Header .Get ("Content-Type" ),
220
- )
221
-
222
- attrs := []attribute.KeyValue {
223
- attribute .String ("method" , mirrorReq .Method ),
224
- attribute .String ("path" , mirrorReq .URL .Path ),
225
- attribute .Int ("status_code" , resp .StatusCode ),
226
- // Group status codes into categories
227
- attribute .String ("status_class" , fmt .Sprintf ("%dxx" , resp .StatusCode / 100 )),
228
- }
229
-
230
- err = _this .metrics .RecordDuration (_this .ctx , metric .MetricMirrorRequest , time .Since (start ), attrs ... )
231
- if err != nil {
232
- _this .logger .Errorw ("failed to record proxy request duration metric" , "error" , err )
206
+ reqCtx .ginContext .Data (resp .StatusCode , resp .Header .Get ("Content-Type" ), respBody )
233
207
}
234
208
}
235
209
0 commit comments