Skip to content

Commit 37bec4c

Browse files
authored
feat(HyperRequest): Add proxy support via throughProxy()
1 parent 7620533 commit 37bec4c

4 files changed

Lines changed: 234 additions & 45 deletions

File tree

models/CfhttpHttpClient.cfc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ component implements="HyperHttpClientInterface" {
8989
attrCollection[ "clientCertPassword" ] = req.getClientCertPassword();
9090
}
9191

92+
if ( len( req.getProxyServer() ) ) {
93+
attrCollection[ "proxyServer" ] = req.getProxyServer();
94+
attrCollection[ "proxyPort" ] = req.getProxyPort();
95+
96+
if ( len( req.getProxyUser() ) ) {
97+
attrCollection[ "proxyUser" ] = req.getProxyUser();
98+
}
99+
100+
if ( len( req.getProxyPassword() ) ) {
101+
attrCollection[ "proxyPassword" ] = req.getProxyPassword();
102+
}
103+
}
104+
92105
var cfhttpHeaders = [];
93106
var headers = req.getHeaders();
94107
for ( var name in headers ) {
@@ -235,6 +248,19 @@ component implements="HyperHttpClientInterface" {
235248
attrCollection[ "clientCertPassword" ] = req.getClientCertPassword();
236249
}
237250

251+
if ( len( req.getProxyServer() ) ) {
252+
attrCollection[ "proxyServer" ] = req.getProxyServer();
253+
attrCollection[ "proxyPort" ] = req.getProxyPort();
254+
255+
if ( len( req.getProxyUser() ) ) {
256+
attrCollection[ "proxyUser" ] = req.getProxyUser();
257+
}
258+
259+
if ( len( req.getProxyPassword() ) ) {
260+
attrCollection[ "proxyPassword" ] = req.getProxyPassword();
261+
}
262+
}
263+
238264
cfhttp( attributeCollection = attrCollection ) {
239265
var headers = req.getHeaders();
240266
for ( var name in headers ) {

models/HyperRequest.cfc

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,26 @@ component accessors="true" {
186186
*/
187187
property name="preventStrayRequests" type="boolean";
188188

189+
/**
190+
* The proxy server for the request.
191+
*/
192+
property name="proxyServer" default="";
193+
194+
/**
195+
* The proxy port for the request.
196+
*/
197+
property name="proxyPort" default="80";
198+
199+
/**
200+
* The proxy user for the request.
201+
*/
202+
property name="proxyUser" default="";
203+
204+
/**
205+
* The proxy password for the request.
206+
*/
207+
property name="proxyPassword" default="";
208+
189209
/**
190210
* A reference to the HyperBuilder that created this request, if any.
191211
*/
@@ -840,6 +860,29 @@ component accessors="true" {
840860
return this;
841861
}
842862

863+
/**
864+
* Sets the proxy settings for the request.
865+
*
866+
* @proxyHost The proxy server host or IP address.
867+
* @proxyPort The proxy server port. Defaults to 80.
868+
* @proxyUser The username for proxy authentication. Defaults to empty string.
869+
* @proxyPassword The password for proxy authentication. Defaults to empty string.
870+
*
871+
* @returns The HyperRequest instance.
872+
*/
873+
function throughProxy(
874+
required string proxyHost,
875+
numeric proxyPort = 80,
876+
string proxyUser = "",
877+
string proxyPassword = ""
878+
) {
879+
setProxyServer( arguments.proxyHost );
880+
setProxyPort( arguments.proxyPort );
881+
setProxyUser( arguments.proxyUser );
882+
setProxyPassword( arguments.proxyPassword );
883+
return this;
884+
}
885+
843886
/**
844887
* Schedules a callback to be ran when executing the request.
845888
*
@@ -1110,7 +1153,10 @@ component accessors="true" {
11101153
callback( res );
11111154
}
11121155

1113-
param variables.useAnnounceMethodForInterceptorService = structKeyExists( variables.interceptorService, "announce" );
1156+
param variables.useAnnounceMethodForInterceptorService = structKeyExists(
1157+
variables.interceptorService,
1158+
"announce"
1159+
);
11141160
if ( variables.useAnnounceMethodForInterceptorService ) {
11151161
variables.interceptorService.announce(
11161162
"onHyperResponse",
@@ -1311,6 +1357,10 @@ component accessors="true" {
13111357
req.setDomain( variables.domain );
13121358
req.setWorkstation( variables.workstation );
13131359
req.setAuthType( variables.authType );
1360+
req.setProxyServer( variables.proxyServer );
1361+
req.setProxyPort( variables.proxyPort );
1362+
req.setProxyUser( variables.proxyUser );
1363+
req.setProxyPassword( variables.proxyPassword );
13141364
req.setRequestCallbacks( duplicate( variables.requestCallbacks ) );
13151365
req.setResponseCallbacks( duplicate( variables.responseCallbacks ) );
13161366
req.setRetries( duplicate( getRetries() ) );
@@ -1429,28 +1479,34 @@ component accessors="true" {
14291479
public struct function getMemento( array excludes = [] ) {
14301480
return structFilter(
14311481
{
1432-
"requestID" : getRequestID(),
1433-
"baseUrl" : getBaseUrl(),
1434-
"url" : getUrl(),
1435-
"fullUrl" : getFullUrl(),
1436-
"method" : getMethod(),
1437-
"queryParams" : getQueryParams(),
1438-
"headers" : getHeaders(),
1439-
"cookies" : getCookies(),
1440-
"files" : getFiles(),
1441-
"bodyFormat" : getBodyFormat(),
1442-
"body" : getBody(),
1443-
"referrerId" : isNull( variables.referrer ) ? "" : variables.referrer.getResponseID(),
1444-
"throwOnError" : getThrowOnError(),
1445-
"timeout" : getTimeout(),
1446-
"maximumRedirects" : getMaximumRedirects(),
1447-
"authType" : getAuthType(),
1448-
"username" : getUsername(),
1449-
"password" : getPassword(),
1450-
"clientCert" : isNull( variables.clientCert ) ? "" : variables.clientCert,
1451-
"clientCertPassword" : isNull( variables.clientCertPassword ) ? "" : variables.clientCertPassword,
1452-
"domain" : getDomain(),
1453-
"workstation" : getWorkstation(),
1482+
"requestID" : getRequestID(),
1483+
"baseUrl" : getBaseUrl(),
1484+
"url" : getUrl(),
1485+
"fullUrl" : getFullUrl(),
1486+
"method" : getMethod(),
1487+
"queryParams" : getQueryParams(),
1488+
"headers" : getHeaders(),
1489+
"cookies" : getCookies(),
1490+
"files" : getFiles(),
1491+
"bodyFormat" : getBodyFormat(),
1492+
"body" : getBody(),
1493+
"referrerId" : isNull( variables.referrer ) ? "" : variables.referrer.getResponseID(),
1494+
"throwOnError" : getThrowOnError(),
1495+
"timeout" : getTimeout(),
1496+
"maximumRedirects" : getMaximumRedirects(),
1497+
"authType" : getAuthType(),
1498+
"username" : getUsername(),
1499+
"password" : getPassword(),
1500+
"clientCert" : isNull( variables.clientCert ) ? "" : variables.clientCert,
1501+
"clientCertPassword" : isNull( variables.clientCertPassword ) ? "" : variables.clientCertPassword,
1502+
"domain" : getDomain(),
1503+
"workstation" : getWorkstation(),
1504+
"proxy" : {
1505+
"proxyServer" : getProxyServer(),
1506+
"proxyPort" : getProxyPort(),
1507+
"proxyUser" : getProxyUser(),
1508+
"proxyPassword" : getProxyPassword()
1509+
},
14541510
"resolveUrls" : getResolveUrls(),
14551511
"encodeUrl" : getEncodeUrl(),
14561512
"retries" : getRetries(),

tests/specs/integration/DebugSpec.cfc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,30 @@ component extends="tests.resources.ModuleIntegrationSpec" appMapping="/app" {
7979
expect( headers[ 4 ] ).toHaveKey( "value" );
8080
expect( headers[ 4 ].value ).toBe( "HyperCFML/#req.getHyperVersion()#" );
8181
} );
82+
83+
it( "includes proxy settings in debug output", function() {
84+
var req = hyper
85+
.setUrl( "https://example.com" )
86+
.throughProxy(
87+
proxyHost = "proxy.example.com",
88+
proxyUser = "proxyuser",
89+
proxyPassword = "proxypass",
90+
proxyPort = 8080
91+
);
92+
93+
var debugReq = req.debug();
94+
95+
expect( debugReq ).toBeStruct();
96+
expect( debugReq ).toHaveKey( "attributes" );
97+
expect( debugReq.attributes ).toHaveKey( "proxyServer" );
98+
expect( debugReq.attributes.proxyServer ).toBe( "proxy.example.com" );
99+
expect( debugReq.attributes ).toHaveKey( "proxyPort" );
100+
expect( debugReq.attributes.proxyPort ).toBe( 8080 );
101+
expect( debugReq.attributes ).toHaveKey( "proxyUser" );
102+
expect( debugReq.attributes.proxyUser ).toBe( "proxyuser" );
103+
expect( debugReq.attributes ).toHaveKey( "proxyPassword" );
104+
expect( debugReq.attributes.proxyPassword ).toBe( "proxypass" );
105+
} );
82106
} );
83107
}
84108

tests/specs/unit/HyperRequestSpec.cfc

Lines changed: 105 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,34 @@ component extends="testbox.system.BaseSpec" {
1414

1515
it( "can serialize to a memento", function() {
1616
expect( variables.req.getMemento() ).toBe( {
17-
"requestID" : variables.req.getRequestID(),
18-
"baseUrl" : variables.req.getBaseUrl(),
19-
"url" : variables.req.getUrl(),
20-
"fullUrl" : variables.req.getFullUrl(),
21-
"method" : variables.req.getMethod(),
22-
"queryParams" : variables.req.getQueryParams(),
23-
"headers" : variables.req.getHeaders(),
24-
"cookies" : variables.req.getCookies(),
25-
"files" : variables.req.getFiles(),
26-
"bodyFormat" : variables.req.getBodyFormat(),
27-
"body" : variables.req.getBody(),
28-
"referrerId" : "",
29-
"throwOnError" : variables.req.getThrowOnError(),
30-
"timeout" : variables.req.getTimeout(),
31-
"maximumRedirects" : variables.req.getMaximumRedirects(),
32-
"authType" : variables.req.getAuthType(),
33-
"username" : variables.req.getUsername(),
34-
"password" : variables.req.getPassword(),
35-
"clientCert" : "",
36-
"clientCertPassword" : "",
37-
"domain" : variables.req.getDomain(),
38-
"workstation" : variables.req.getWorkstation(),
17+
"requestID" : variables.req.getRequestID(),
18+
"baseUrl" : variables.req.getBaseUrl(),
19+
"url" : variables.req.getUrl(),
20+
"fullUrl" : variables.req.getFullUrl(),
21+
"method" : variables.req.getMethod(),
22+
"queryParams" : variables.req.getQueryParams(),
23+
"headers" : variables.req.getHeaders(),
24+
"cookies" : variables.req.getCookies(),
25+
"files" : variables.req.getFiles(),
26+
"bodyFormat" : variables.req.getBodyFormat(),
27+
"body" : variables.req.getBody(),
28+
"referrerId" : "",
29+
"throwOnError" : variables.req.getThrowOnError(),
30+
"timeout" : variables.req.getTimeout(),
31+
"maximumRedirects" : variables.req.getMaximumRedirects(),
32+
"authType" : variables.req.getAuthType(),
33+
"username" : variables.req.getUsername(),
34+
"password" : variables.req.getPassword(),
35+
"clientCert" : "",
36+
"clientCertPassword" : "",
37+
"domain" : variables.req.getDomain(),
38+
"workstation" : variables.req.getWorkstation(),
39+
"proxy" : {
40+
"proxyServer" : variables.req.getProxyServer(),
41+
"proxyPort" : variables.req.getProxyPort(),
42+
"proxyUser" : variables.req.getProxyUser(),
43+
"proxyPassword" : variables.req.getProxyPassword()
44+
},
3945
"resolveUrls" : variables.req.getResolveUrls(),
4046
"encodeUrl" : variables.req.getEncodeUrl(),
4147
"retries" : variables.req.getRetries(),
@@ -166,6 +172,83 @@ component extends="testbox.system.BaseSpec" {
166172
expect( req.getClientCertPassword() ).toBe( "mypassword" );
167173
} );
168174

175+
it( "can set proxy settings via throughProxy method", function() {
176+
expect( req.getProxyServer() ).toBe( "" );
177+
expect( req.getProxyPort() ).toBe( 80 );
178+
expect( req.getProxyUser() ).toBe( "" );
179+
expect( req.getProxyPassword() ).toBe( "" );
180+
req.throughProxy(
181+
proxyHost = "proxy.example.com",
182+
proxyUser = "proxyuser",
183+
proxyPassword = "proxypass",
184+
proxyPort = 8080
185+
);
186+
expect( req.getProxyServer() ).toBe( "proxy.example.com" );
187+
expect( req.getProxyPort() ).toBe( 8080 );
188+
expect( req.getProxyUser() ).toBe( "proxyuser" );
189+
expect( req.getProxyPassword() ).toBe( "proxypass" );
190+
} );
191+
192+
it( "can set proxy settings with default port", function() {
193+
expect( req.getProxyPort() ).toBe( 80 );
194+
req.throughProxy(
195+
proxyHost = "proxy.example.com",
196+
proxyUser = "proxyuser",
197+
proxyPassword = "proxypass"
198+
);
199+
expect( req.getProxyServer() ).toBe( "proxy.example.com" );
200+
expect( req.getProxyPort() ).toBe( 80 );
201+
expect( req.getProxyUser() ).toBe( "proxyuser" );
202+
expect( req.getProxyPassword() ).toBe( "proxypass" );
203+
} );
204+
205+
it( "can set proxy settings without authentication", function() {
206+
req.throughProxy( proxyHost = "proxy.example.com" );
207+
expect( req.getProxyServer() ).toBe( "proxy.example.com" );
208+
expect( req.getProxyPort() ).toBe( 80 );
209+
expect( req.getProxyUser() ).toBe( "" );
210+
expect( req.getProxyPassword() ).toBe( "" );
211+
} );
212+
213+
it( "can set proxy settings with custom port but no authentication", function() {
214+
req.throughProxy( proxyHost = "proxy.example.com", proxyPort = 8080 );
215+
expect( req.getProxyServer() ).toBe( "proxy.example.com" );
216+
expect( req.getProxyPort() ).toBe( 8080 );
217+
expect( req.getProxyUser() ).toBe( "" );
218+
expect( req.getProxyPassword() ).toBe( "" );
219+
} );
220+
221+
it( "includes proxy settings in memento", function() {
222+
req.throughProxy(
223+
proxyHost = "proxy.example.com",
224+
proxyUser = "proxyuser",
225+
proxyPassword = "proxypass",
226+
proxyPort = 8080
227+
);
228+
var memento = req.getMemento();
229+
expect( memento ).toHaveKey( "proxy" );
230+
expect( memento.proxy ).toBe( {
231+
"proxyServer" : "proxy.example.com",
232+
"proxyPort" : 8080,
233+
"proxyUser" : "proxyuser",
234+
"proxyPassword" : "proxypass"
235+
} );
236+
} );
237+
238+
it( "clones proxy settings to new request", function() {
239+
req.throughProxy(
240+
proxyHost = "proxy.example.com",
241+
proxyUser = "proxyuser",
242+
proxyPassword = "proxypass",
243+
proxyPort = 8080
244+
);
245+
var clonedReq = req.clone();
246+
expect( clonedReq.getProxyServer() ).toBe( "proxy.example.com" );
247+
expect( clonedReq.getProxyPort() ).toBe( 8080 );
248+
expect( clonedReq.getProxyUser() ).toBe( "proxyuser" );
249+
expect( clonedReq.getProxyPassword() ).toBe( "proxypass" );
250+
} );
251+
169252
it( "can define onRequest callback hooks", function() {
170253
var method = "not set yet";
171254
var headers = {};

0 commit comments

Comments
 (0)