The best way to learn the minutue of how web requests are made is to write a web server in as low a level as practical. It's not actually that hard to use the raw socket libraries and build something simple in C or C++. This file handles HTTP 1.0 and returns a simple static doc for every request URI. It also prints the raw HTTP request made by the client to STDOUT which can be useful sometimes when investigating the behavior of esoteric HTTP clients.

    1 #include <ctime>
    2 #include <iostream>
    3 #include <sstream>
    4 #include <string>
    5 #include <cstdio>
    6 #include <unistd.h>
    7 
    8 // Network related includes
    9 #include <netinet/in.h> // sockaddr_in
   10 #include <sys/socket.h>
   11 #include <sys/types.h>
   12 
   13 using namespace std;
   14 
   15 // Returns UTC time in the particular format required by HTTP.
   16 // e.g. Date: Thu, 03 Jun 2010 05:34:52 UTC
   17 string getHeaderTime() {
   18   time_t now = time(NULL);
   19   struct tm* timeinfo;
   20   timeinfo = gmtime(&now);
   21 
   22   char buff[80];
   23   memset(buff, 0, sizeof(buff));
   24   strftime(buff, 80, "Date: %a, %d %b %Y %X %Z", timeinfo);
   25   string s(buff);
   26   return s;
   27 }
   28 
   29 string get200Header() {
   30   stringstream head;
   31   head.clear();
   32   head << "HTTP/1.0 200 OK" << endl
   33        << getHeaderTime() << endl
   34        << "Expires: -1" << endl
   35        << "Cache-Control: private, max-age=0" << endl
   36        << "Content-Type: text/html; charset=ISO-8859-1\r\n\n";
   37   return head.str();
   38 }
   39 
   40 string getSimpleDoc() {
   41   stringstream resp;
   42   resp.clear();
   43   resp << "<html><head><title>47 Ways to Fun</title></head>"
   44        << "<body>Sample Document</body></html>"
   45        << "\r\n";
   46   return resp.str();
   47 }
   48 
   49 int main(int argc, char* argv[]) {
   50   static const int kBacklog = 50;
   51   static const int kPort = 8000;
   52   const string CRLF = "\r\n";
   53 
   54   int listenfd = socket(AF_INET, SOCK_STREAM, 0);
   55 
   56   struct sockaddr_in servaddr;
   57   memset(&servaddr, '\0', sizeof(servaddr));
   58   servaddr.sin_family = AF_INET;
   59   servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
   60   servaddr.sin_port = htons(kPort);
   61 
   62   if (bind(listenfd, (sockaddr *) &servaddr, sizeof(servaddr)) == -1) {
   63     cerr << "Error binding to port: " << kPort << endl;
   64     perror("bind error");
   65     exit(1);
   66   }
   67   listen(listenfd, kBacklog);
   68 
   69   int connfd;
   70   char buff[1024*8];
   71   while (true) {
   72     connfd = accept(listenfd, (struct sockaddr *) NULL, NULL);
   73     memset(buff, 0, sizeof(buff));
   74     recv(connfd, buff, 1024*8, 0);
   75     cout << "========================================================" << endl;
   76     cout << buff << endl;
   77 
   78     string head = get200Header();
   79     write(connfd, head.c_str(), head.length());
   80 
   81     string doc = getSimpleDoc();
   82     write(connfd, doc.c_str(), doc.length());
   83 
   84     close(connfd);
   85   }
   86   return 0;
   87 }