/*
* Run with an http: or https: url on the command line. The program will
* fetch the indicated document with HTTP 1.0 and print the entire response
* on standard output, including headers. The URL parsing is far from
* complete, so keep it simple.
*/
#include <stdlib.h>
#include <iostream>
#include <string>
#include <stdexcept>
#include <cleansocks.h>
#include <cleanip.h>
#include <cleantlsc.h>
using std::string;
using std::cout;
using std::endl;
using std::runtime_error;
using namespace cleansocks;
/*
* This performs the bulk of the operation. It receives the URL from the
* command line, parses it, builds and sends the HTTP request, and
* echos the response to the command line. Throws if something goes wrong.
*/
void doit(string url)
{
string original(url);
// Remove the protocol.
int loc = url.find(":");
if(loc == string::npos)
throw runtime_error
(original + ": Can't find colon marking protocol");
string proto = url.substr(0,loc);
url.erase(0,loc);
// Must be http: or https:
if(proto != "http" && proto != "https")
throw runtime_error(original + ": Not http or https");
// Next part has to be ://.
if(url.substr(0,3) != "://")
throw runtime_error(original + ": Too complicated for me!");
url.erase(0,3);
// Simplest case, the remainder of the URL is the host name, and
// the path is implied /.
string hostname = url;
string path = "/";
// But if there's a slash, it's the start of the path.
int hend = url.find("/");
if(hend != string::npos) {
hostname = url.substr(0,hend);
path = url.substr(hend);
}
// See if we need TLS.
bool secure = (proto == "https");
// Show what we found out.
cout << "proto = " << proto
<< ", host = " << hostname
<< ", path = " << path << (secure ? " [secure]" : "")
<< "\n" << endl;
// Look up the host and try to connect.
IPaddress a = lookup_host(hostname);
IPport p = lookup_service(proto);
TCPsocket cs;
connect(cs,IPendpoint(a,p));
// Create a TLS socket, but only enable TLS if needed.
client_tls_socket s(cs,hostname,secure);
// Send a minimal HTTP request to the server.
send(s, "GET "+path+" HTTP/1.0\r\n");
send(s, "Host: "+hostname+"\r\n");
send(s, string("User-Agent: URLtest\r\n\r\n"));
// Read and print the response.
char buf[1024];
int n;
while((n = recv(s, buf, sizeof buf)) != 0)
cout.write(buf, n);
close(s);
}
int main(int argc, char **argv)
{
// Check that we got exactly one argument, or whine.
if(argc != 2) {
std::cerr << "Provide exactly one URL." << endl;
exit(2);
}
// Perform the operation, and catch the pieces.
try {
doit(argv[1]);
} catch(socket_db_error &e) {
cout << "Socket db error: " << e.what() << endl;
}
catch(socket_tls_error &e) {
cout << "TLS error: " << e.what() << endl;
socket_tls_error::error_stack();
}
catch(socket_error &e) {
cout << "Socket error: " << e.what() << endl;
}
catch(runtime_error &e) {
cout << "Runtime error: " << e.what() << endl;
}
}