diff --git a/models/CfhttpHttpClient.cfc b/models/CfhttpHttpClient.cfc index 94d338b..dce0261 100644 --- a/models/CfhttpHttpClient.cfc +++ b/models/CfhttpHttpClient.cfc @@ -89,6 +89,19 @@ component implements="HyperHttpClientInterface" { attrCollection[ "clientCertPassword" ] = req.getClientCertPassword(); } + if ( len( req.getProxyServer() ) ) { + attrCollection[ "proxyServer" ] = req.getProxyServer(); + attrCollection[ "proxyPort" ] = req.getProxyPort(); + + if ( len( req.getProxyUser() ) ) { + attrCollection[ "proxyUser" ] = req.getProxyUser(); + } + + if ( len( req.getProxyPassword() ) ) { + attrCollection[ "proxyPassword" ] = req.getProxyPassword(); + } + } + var cfhttpHeaders = []; var headers = req.getHeaders(); for ( var name in headers ) { @@ -235,6 +248,19 @@ component implements="HyperHttpClientInterface" { attrCollection[ "clientCertPassword" ] = req.getClientCertPassword(); } + if ( len( req.getProxyServer() ) ) { + attrCollection[ "proxyServer" ] = req.getProxyServer(); + attrCollection[ "proxyPort" ] = req.getProxyPort(); + + if ( len( req.getProxyUser() ) ) { + attrCollection[ "proxyUser" ] = req.getProxyUser(); + } + + if ( len( req.getProxyPassword() ) ) { + attrCollection[ "proxyPassword" ] = req.getProxyPassword(); + } + } + cfhttp( attributeCollection = attrCollection ) { var headers = req.getHeaders(); for ( var name in headers ) { diff --git a/models/HyperRequest.cfc b/models/HyperRequest.cfc index 58cb7fd..686e21e 100644 --- a/models/HyperRequest.cfc +++ b/models/HyperRequest.cfc @@ -186,6 +186,26 @@ component accessors="true" { */ property name="preventStrayRequests" type="boolean"; + /** + * The proxy server for the request. + */ + property name="proxyServer" default=""; + + /** + * The proxy port for the request. + */ + property name="proxyPort" default="80"; + + /** + * The proxy user for the request. + */ + property name="proxyUser" default=""; + + /** + * The proxy password for the request. + */ + property name="proxyPassword" default=""; + /** * A reference to the HyperBuilder that created this request, if any. */ @@ -840,6 +860,29 @@ component accessors="true" { return this; } + /** + * Sets the proxy settings for the request. + * + * @proxyHost The proxy server host or IP address. + * @proxyPort The proxy server port. Defaults to 80. + * @proxyUser The username for proxy authentication. Defaults to empty string. + * @proxyPassword The password for proxy authentication. Defaults to empty string. + * + * @returns The HyperRequest instance. + */ + function throughProxy( + required string proxyHost, + numeric proxyPort = 80, + string proxyUser = "", + string proxyPassword = "" + ) { + setProxyServer( arguments.proxyHost ); + setProxyPort( arguments.proxyPort ); + setProxyUser( arguments.proxyUser ); + setProxyPassword( arguments.proxyPassword ); + return this; + } + /** * Schedules a callback to be ran when executing the request. * @@ -1110,7 +1153,10 @@ component accessors="true" { callback( res ); } - param variables.useAnnounceMethodForInterceptorService = structKeyExists( variables.interceptorService, "announce" ); + param variables.useAnnounceMethodForInterceptorService = structKeyExists( + variables.interceptorService, + "announce" + ); if ( variables.useAnnounceMethodForInterceptorService ) { variables.interceptorService.announce( "onHyperResponse", @@ -1311,6 +1357,10 @@ component accessors="true" { req.setDomain( variables.domain ); req.setWorkstation( variables.workstation ); req.setAuthType( variables.authType ); + req.setProxyServer( variables.proxyServer ); + req.setProxyPort( variables.proxyPort ); + req.setProxyUser( variables.proxyUser ); + req.setProxyPassword( variables.proxyPassword ); req.setRequestCallbacks( duplicate( variables.requestCallbacks ) ); req.setResponseCallbacks( duplicate( variables.responseCallbacks ) ); req.setRetries( duplicate( getRetries() ) ); @@ -1429,28 +1479,34 @@ component accessors="true" { public struct function getMemento( array excludes = [] ) { return structFilter( { - "requestID" : getRequestID(), - "baseUrl" : getBaseUrl(), - "url" : getUrl(), - "fullUrl" : getFullUrl(), - "method" : getMethod(), - "queryParams" : getQueryParams(), - "headers" : getHeaders(), - "cookies" : getCookies(), - "files" : getFiles(), - "bodyFormat" : getBodyFormat(), - "body" : getBody(), - "referrerId" : isNull( variables.referrer ) ? "" : variables.referrer.getResponseID(), - "throwOnError" : getThrowOnError(), - "timeout" : getTimeout(), - "maximumRedirects" : getMaximumRedirects(), - "authType" : getAuthType(), - "username" : getUsername(), - "password" : getPassword(), - "clientCert" : isNull( variables.clientCert ) ? "" : variables.clientCert, - "clientCertPassword" : isNull( variables.clientCertPassword ) ? "" : variables.clientCertPassword, - "domain" : getDomain(), - "workstation" : getWorkstation(), + "requestID" : getRequestID(), + "baseUrl" : getBaseUrl(), + "url" : getUrl(), + "fullUrl" : getFullUrl(), + "method" : getMethod(), + "queryParams" : getQueryParams(), + "headers" : getHeaders(), + "cookies" : getCookies(), + "files" : getFiles(), + "bodyFormat" : getBodyFormat(), + "body" : getBody(), + "referrerId" : isNull( variables.referrer ) ? "" : variables.referrer.getResponseID(), + "throwOnError" : getThrowOnError(), + "timeout" : getTimeout(), + "maximumRedirects" : getMaximumRedirects(), + "authType" : getAuthType(), + "username" : getUsername(), + "password" : getPassword(), + "clientCert" : isNull( variables.clientCert ) ? "" : variables.clientCert, + "clientCertPassword" : isNull( variables.clientCertPassword ) ? "" : variables.clientCertPassword, + "domain" : getDomain(), + "workstation" : getWorkstation(), + "proxy" : { + "proxyServer" : getProxyServer(), + "proxyPort" : getProxyPort(), + "proxyUser" : getProxyUser(), + "proxyPassword" : getProxyPassword() + }, "resolveUrls" : getResolveUrls(), "encodeUrl" : getEncodeUrl(), "retries" : getRetries(), diff --git a/tests/specs/integration/DebugSpec.cfc b/tests/specs/integration/DebugSpec.cfc index dd62d49..ccf511f 100644 --- a/tests/specs/integration/DebugSpec.cfc +++ b/tests/specs/integration/DebugSpec.cfc @@ -79,6 +79,30 @@ component extends="tests.resources.ModuleIntegrationSpec" appMapping="/app" { expect( headers[ 4 ] ).toHaveKey( "value" ); expect( headers[ 4 ].value ).toBe( "HyperCFML/#req.getHyperVersion()#" ); } ); + + it( "includes proxy settings in debug output", function() { + var req = hyper + .setUrl( "https://example.com" ) + .throughProxy( + proxyHost = "proxy.example.com", + proxyUser = "proxyuser", + proxyPassword = "proxypass", + proxyPort = 8080 + ); + + var debugReq = req.debug(); + + expect( debugReq ).toBeStruct(); + expect( debugReq ).toHaveKey( "attributes" ); + expect( debugReq.attributes ).toHaveKey( "proxyServer" ); + expect( debugReq.attributes.proxyServer ).toBe( "proxy.example.com" ); + expect( debugReq.attributes ).toHaveKey( "proxyPort" ); + expect( debugReq.attributes.proxyPort ).toBe( 8080 ); + expect( debugReq.attributes ).toHaveKey( "proxyUser" ); + expect( debugReq.attributes.proxyUser ).toBe( "proxyuser" ); + expect( debugReq.attributes ).toHaveKey( "proxyPassword" ); + expect( debugReq.attributes.proxyPassword ).toBe( "proxypass" ); + } ); } ); } diff --git a/tests/specs/unit/HyperRequestSpec.cfc b/tests/specs/unit/HyperRequestSpec.cfc index c17d04a..789d043 100644 --- a/tests/specs/unit/HyperRequestSpec.cfc +++ b/tests/specs/unit/HyperRequestSpec.cfc @@ -14,28 +14,34 @@ component extends="testbox.system.BaseSpec" { it( "can serialize to a memento", function() { expect( variables.req.getMemento() ).toBe( { - "requestID" : variables.req.getRequestID(), - "baseUrl" : variables.req.getBaseUrl(), - "url" : variables.req.getUrl(), - "fullUrl" : variables.req.getFullUrl(), - "method" : variables.req.getMethod(), - "queryParams" : variables.req.getQueryParams(), - "headers" : variables.req.getHeaders(), - "cookies" : variables.req.getCookies(), - "files" : variables.req.getFiles(), - "bodyFormat" : variables.req.getBodyFormat(), - "body" : variables.req.getBody(), - "referrerId" : "", - "throwOnError" : variables.req.getThrowOnError(), - "timeout" : variables.req.getTimeout(), - "maximumRedirects" : variables.req.getMaximumRedirects(), - "authType" : variables.req.getAuthType(), - "username" : variables.req.getUsername(), - "password" : variables.req.getPassword(), - "clientCert" : "", - "clientCertPassword" : "", - "domain" : variables.req.getDomain(), - "workstation" : variables.req.getWorkstation(), + "requestID" : variables.req.getRequestID(), + "baseUrl" : variables.req.getBaseUrl(), + "url" : variables.req.getUrl(), + "fullUrl" : variables.req.getFullUrl(), + "method" : variables.req.getMethod(), + "queryParams" : variables.req.getQueryParams(), + "headers" : variables.req.getHeaders(), + "cookies" : variables.req.getCookies(), + "files" : variables.req.getFiles(), + "bodyFormat" : variables.req.getBodyFormat(), + "body" : variables.req.getBody(), + "referrerId" : "", + "throwOnError" : variables.req.getThrowOnError(), + "timeout" : variables.req.getTimeout(), + "maximumRedirects" : variables.req.getMaximumRedirects(), + "authType" : variables.req.getAuthType(), + "username" : variables.req.getUsername(), + "password" : variables.req.getPassword(), + "clientCert" : "", + "clientCertPassword" : "", + "domain" : variables.req.getDomain(), + "workstation" : variables.req.getWorkstation(), + "proxy" : { + "proxyServer" : variables.req.getProxyServer(), + "proxyPort" : variables.req.getProxyPort(), + "proxyUser" : variables.req.getProxyUser(), + "proxyPassword" : variables.req.getProxyPassword() + }, "resolveUrls" : variables.req.getResolveUrls(), "encodeUrl" : variables.req.getEncodeUrl(), "retries" : variables.req.getRetries(), @@ -166,6 +172,83 @@ component extends="testbox.system.BaseSpec" { expect( req.getClientCertPassword() ).toBe( "mypassword" ); } ); + it( "can set proxy settings via throughProxy method", function() { + expect( req.getProxyServer() ).toBe( "" ); + expect( req.getProxyPort() ).toBe( 80 ); + expect( req.getProxyUser() ).toBe( "" ); + expect( req.getProxyPassword() ).toBe( "" ); + req.throughProxy( + proxyHost = "proxy.example.com", + proxyUser = "proxyuser", + proxyPassword = "proxypass", + proxyPort = 8080 + ); + expect( req.getProxyServer() ).toBe( "proxy.example.com" ); + expect( req.getProxyPort() ).toBe( 8080 ); + expect( req.getProxyUser() ).toBe( "proxyuser" ); + expect( req.getProxyPassword() ).toBe( "proxypass" ); + } ); + + it( "can set proxy settings with default port", function() { + expect( req.getProxyPort() ).toBe( 80 ); + req.throughProxy( + proxyHost = "proxy.example.com", + proxyUser = "proxyuser", + proxyPassword = "proxypass" + ); + expect( req.getProxyServer() ).toBe( "proxy.example.com" ); + expect( req.getProxyPort() ).toBe( 80 ); + expect( req.getProxyUser() ).toBe( "proxyuser" ); + expect( req.getProxyPassword() ).toBe( "proxypass" ); + } ); + + it( "can set proxy settings without authentication", function() { + req.throughProxy( proxyHost = "proxy.example.com" ); + expect( req.getProxyServer() ).toBe( "proxy.example.com" ); + expect( req.getProxyPort() ).toBe( 80 ); + expect( req.getProxyUser() ).toBe( "" ); + expect( req.getProxyPassword() ).toBe( "" ); + } ); + + it( "can set proxy settings with custom port but no authentication", function() { + req.throughProxy( proxyHost = "proxy.example.com", proxyPort = 8080 ); + expect( req.getProxyServer() ).toBe( "proxy.example.com" ); + expect( req.getProxyPort() ).toBe( 8080 ); + expect( req.getProxyUser() ).toBe( "" ); + expect( req.getProxyPassword() ).toBe( "" ); + } ); + + it( "includes proxy settings in memento", function() { + req.throughProxy( + proxyHost = "proxy.example.com", + proxyUser = "proxyuser", + proxyPassword = "proxypass", + proxyPort = 8080 + ); + var memento = req.getMemento(); + expect( memento ).toHaveKey( "proxy" ); + expect( memento.proxy ).toBe( { + "proxyServer" : "proxy.example.com", + "proxyPort" : 8080, + "proxyUser" : "proxyuser", + "proxyPassword" : "proxypass" + } ); + } ); + + it( "clones proxy settings to new request", function() { + req.throughProxy( + proxyHost = "proxy.example.com", + proxyUser = "proxyuser", + proxyPassword = "proxypass", + proxyPort = 8080 + ); + var clonedReq = req.clone(); + expect( clonedReq.getProxyServer() ).toBe( "proxy.example.com" ); + expect( clonedReq.getProxyPort() ).toBe( 8080 ); + expect( clonedReq.getProxyUser() ).toBe( "proxyuser" ); + expect( clonedReq.getProxyPassword() ).toBe( "proxypass" ); + } ); + it( "can define onRequest callback hooks", function() { var method = "not set yet"; var headers = {};