SourcePro : Secure Communication Module User’s Guide : PART III HTTPS Package : Callbacks : Using the HTTP CONNECT Method with the HTTPS Package
Using the HTTP CONNECT Method with the HTTPS Package
The HTTP CONNECT method creates an end-to-end tunnel between two machines through a proxy/firewall machine.
NOTE: The HTTP CONNECT method can be used only with the client class (in our case RWHttpsSecureSocketClient).
If you want to access secure content on a machine behind a proxy, your application must:
1. Connect to the proxy insecurely.
Normally, when using RWHttpsSecureSocketClient, the connection is secure from the time of connection. To start out as a nonsecure HTTP client so that the application can connect to the proxy, pass the enumeration RW_HTTPS_START_INSECURE as the second parameter to the static make() function of RWHttpsSecureSocketClient. At this point, the class behaves like class RWHttpSocketClient in the HTTP package.
For more information about connecting to proxies, see the chapter on the HTTP protocol in the Internet Protocols Module User’s Guide.
2. Ask the proxy to create a tunnel to the secure machine.
After step 1, your application can use standard HTTP commands to connect to the proxy and send the CONNECT request for a tunnel to the remote (secure) server.
3. Direct the HTTPS package to begin using security. Do this by calling the secureUpgrade() member function on RWHttpsSecureSocketClient. The function returns true if a secure connection is established.
After the CONNECT request returns from the proxy successfully, your channel to the secure server is established, as if you had connected to the server directly.
From this point on, you can use standard HTTP client commands to write your application.
Example 14 – HTTPS CONNECT Example
// File: examples/https/tutorial/SecureProxyRequestDocument.cpp
 
#include <stdlib.h>
 
#include <rw/network/RWInetAddr.h>
#include <rw/network/RWWinSockInfo.h>
#include <rw/internet/RWURL.h>
#include <rw/http/RWHttpRequest.h>
 
#include <rw/secsock/RWSecureSocketPackageInit.h>
#include <rw/https/RWHttpsSecureSocketClient.h>
 
#include <iostream>
#include "args.h"
 
using std::cout;
using std::cerr;
using std::endl;
 
// Wait at most 5 seconds for each call to connect, submit, or getReply
// on RWHttpClient to complete.
const unsigned long networkMaxWait = 20000;
 
// This data is used to seed the RNG. Extremely Insecure!
const unsigned char seed[] = "123456789012345678901234567890"
"123456789012345678901234567890"
"123456789012345678901234567890"
"123456789012345678901234567890"
"123456789012345678901234567890";
 
int main(int argc, char** argv)
{
// Initialize the WinSock library (if necessary), and the
// Secure Sockets Package
RWWinSockInfo winsock;
RWSecureSocketPackageInit secsock;
 
// Seed the RNG. Seeding from a const string is not
// secure and not recommended for a real-world
// application, however for the purposes of this
// example is sufficient.
RWSecureSocketPackageInit::seedRNGFromMemory(seed, sizeof(seed));
 
// Parse commandline args
RWTValOrderedVector<RWCString> args = processArgs(argc, argv);
 
if (args.entries() < 3) {
cout << "Usage: " << argv[0] << " <proxy host> <proxy port> <url>"
<< endl;
return 1;
}
 
// Create the context and pass in the location of the trusted certificates
RWSecureSocketContext context;
 
// In a secure application, some level of authentication is
// usually performed to verify either the server or client
// matches the host we connected to. For simplicity in this
// example, we will ignore this step.
//
// context.prepareToAuthenticate("<certificate file>");
 
RWCString proxyHost = args[0];
int proxyPort = atoi(args[1]);
 
RWURL url(args[2]);
 
RWHttpsSecureSocketClient client;
client = RWHttpsSecureSocketClient::make(context, RW_HTTPS_START_INSECURE); //1
 
RWCString actualServer(url.getHost());
actualServer.append(':');
actualServer.append(url.getPort());
 
RWHttpRequest makeTunnelRequest(RWHttpRequest::Connect, actualServer); //2
 
// Create InetAddr to encapsulate host and port
RWInetAddr proxyAddr(proxyPort, proxyHost);
 
try {
client.connect(proxyAddr, networkMaxWait); //3
client.submit(makeTunnelRequest, RW_HTTP_FORBID_PIPELINING, networkMaxWait); //4
 
RWHttpReply reply = client.getReply(networkMaxWait);
 
if (!reply.is2XX()) { //5
cerr << "Failed to establish tunnel through proxy" << endl;
return 1;
}
 
// All requests/replies beyond this point will be sent through the tunnel
// to the server behind the proxy
 
if (!client.secureUpgrade()) { //6
cerr << "Failed to upgrade to secure client" << endl;
return 1;
}
 
RWCString requestStr('/');
requestStr.append(url.getPath());
RWHttpRequest actualRequest(RWHttpRequest::Get, requestStr);
client.submit(actualRequest, RW_HTTP_FORBID_PIPELINING, networkMaxWait); //7
 
reply = client.getReply(networkMaxWait);
cout << reply.asString() << endl;
 
if (!reply.is2XX()) { //8
cerr << "Invalid server reply code" << endl;
return 1;
}
 
cout << reply.getBody() << endl;
}
catch (const RWxmsg& msg) {
cerr << "Error: " << msg.why() << endl;
return 1;
}
catch (...) {
cerr << "Error: Unexpected exception." << endl;
return 1;
}
 
return 0;
}
//1 Starts the HTTPS client insecurely (using a standard TCP/IP socket) by passing RW_HTTPS_START_INSECURE as the second parameter to make().
//2 Creates the CONNECT request with the name of the “actual” server (the server behind the proxy).
//3 Connects our client to the proxy machine.
//4 Submits our CONNECT request, which asks the proxy to set up a tunnel to the real machine.
//5 If a successful (200 series) reply is returned from the server, the tunnel has been established; otherwise, an error message is generated and the example exits with a return code of 1.
//6 Calls secureUpgrade() to start using SSL; again, failure results in exit and a return code of 1.
//7 Submits our request to the actual server (behind the proxy).
//8 Exits in the case of failure; otherwise, falls through to the next line, which displays the page received from the server.