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;
}