Search notes:

A Simple Webserver in C++ for Windows

Transferred from adp-gmbh.ch 2018-01-15
This c++ source files are supposed to demonstrate the basic mechanisms of the HTTP Protocol and a webserver for educational purposes.
Thus, it is far from being finished… I welcome everybody for further help.

The webserver::http_request struct

The webserver::http_request (in webserver.h) is arguably on of the most important structures for the webserver.
For each HTTP request, path_ is filled with the request's path. params_ contains name values pairs if the GET request passes some form variables.
After filling these fields, this structure is passed to the request handler function who is responsible of filling answer_.

main.cpp

main.cpp demonstrates how the web server can be used.
The web server's constructor is basically waiting for new connection request on the port passed as argument to the constructor.
As soon as a request is detected, a new thread is created (_beginthreadex) which starts in the static method Request. This method fills the http_request structure and passes it to the supplied http handler function.
Request then reads the first line from the socket (ReceiveLine) to determine the Request method.
#include "webserver.h"
#include "Socket.h"

void Request_Handler(webserver::http_request* r) {
  Socket s = *(r->s_);

  std::string title;
  std::string body;
  std::string bgcolor="#ffffff";
  std::string links =
      "<p><a href='/red'>red</a> "
      "<br><a href='/blue'>blue</a> "
      "<br><a href='/form'>form</a> "
      "<br><a href='/auth'>authentication example</a> [use <b>rene</b> as username and <b>secretGarden</b> as password"
      "<br><a href='/header'>show some HTTP header details</a> "
      ;

  if(r->path_ == "/") {
    title = "Web Server Example";
    body  = "<h1>Welcome to Rene's Web Server</h1>"
            "I wonder what you're going to click"  + links;
  }
  else if (r->path_ == "/red") {
    bgcolor = "#ff4444";
    title   = "You chose red";
    body    = "<h1>Red</h1>" + links;
  }
  else if (r->path_ == "/blue") {
    bgcolor = "#4444ff";
    title   = "You chose blue";
    body    = "<h1>Blue</h1>" + links;
  }
  else if (r->path_ == "/form") {
    title   = "Fill a form";

    body    = "<h1>Fill a form</h1>";
    body   += "<form action='/form'>"
              "<table>"
              "<tr><td>Field 1</td><td><input name=field_1></td></tr>"
              "<tr><td>Field 2</td><td><input name=field_2></td></tr>"
              "<tr><td>Field 3</td><td><input name=field_3></td></tr>"
              "</table>"
              "<input type=submit></form>";


    for (std::map<std::string, std::string>::const_iterator i = r->params_.begin();
         i != r->params_.end();
         i++) {

      body += "<br>" + i->first + " = " + i->second;
    }


    body += "<hr>" + links;

  }
  else if (r->path_ == "/auth") {
    if (r->authentication_given_) {
      if (r->username_ == "rene" && r->password_ == "secretGarden") {
         body = "<h1>Successfully authenticated</h1>" + links;
      }
      else {
         body = "<h1>Wrong username or password</h1>" + links;
         r->auth_realm_ = "Private Stuff";
      }
    }
    else {
      r->auth_realm_ = "Private Stuff";
    }
  }
  else if (r->path_ == "/header") {
    title   = "some HTTP header details";
    body    = std::string ("<table>")                                   +
              "<tr><td>Accept:</td><td>"          + r->accept_          + "</td></tr>" +
              "<tr><td>Accept-Encoding:</td><td>" + r->accept_encoding_ + "</td></tr>" +
              "<tr><td>Accept-Language:</td><td>" + r->accept_language_ + "</td></tr>" +
              "<tr><td>User-Agent:</td><td>"      + r->user_agent_      + "</td></tr>" +
              "</table>"                                                +
              links;
  }
  else {
    r->status_ = "404 Not Found";
    title      = "Wrong URL";
    body       = "<h1>Wrong URL</h1>";
    body      += "Path is : &gt;" + r->path_ + "&lt;"; 
  }

  r->answer_  = "<html><head><title>";
  r->answer_ += title;
  r->answer_ += "</title></head><body bgcolor='" + bgcolor + "'>";
  r->answer_ += body;
  r->answer_ += "</body></html>";
}

int main() {
  webserver(8080, Request_Handler);
}
Github repository cpp-webserver, path: /main.cpp

Source code

The source code is hosted on github.
It can be cloned as follows
git clone https://github.com/ReneNyffenegger/cpp-webserver webserver
cd webserver
git submodule update --init --recursive
The sources come with a compile.mingw.bat file that should compile the sources into a WebServer.exe executable (GCC/MinGW).
If the executable could be successfully built and run, it listens, by default, on port 8080, so you can connect to it on http://localhost:8080/.

Dependencies

The webserver requires this base 64 c++ class (for authentication) and this c++ socket class.

Thanks

Cybrax Cyberspace pointed out that VS 2008 doesn't compile the code with a error C2065: 'EAGAIN': undeclared identifier error unless a #include <errno.h> is added in the source. Many thanks for the feedback!
Kaarlo Räihä has created a Visual Studio 6 project for the webserver which can be downloaded here. (But note, that the sources in this project are not up-to-date anymore). Thank you very much.
Thanks also to Tom Lynn and Frank M. Hoffmann who each pointed out an error in webserver.cpp.
2020-03-03: Janis Jansen aka Carnageous has provided a pull request which lifted a limitation of HTTP request methods: only PUT and GET were allowed.
With this fix, all requests (POST, DELETE, …) are now possible.

TODO

The page that shows the headers should at least also print the Auhtorization HTTP-header.
A Makefile or (possibly more preferrable) MSBuild. (A pull request already has a Visual Studio .sln file, but I am unable merge the pull request)

Custom value for Content-Type

The value of the Content-Type HTTP header is hardcoded.
It should be possible to change this header.

Add Access-Control-Allow-Origin header

The Access-Control-Allow-Origin HTTP Header should probably be implemented.
This is possible by adding the following lines to webserver.cpp.
s.SendLine("Access-Control-Allow-Credentials: true");
s.SendLine("Access-Control-Allow-Origin: *");
s.SendLine("Access-Control-Allow-Methods: GET,POST");

Cookies

It would be nice if cookies were possible.

See also

webserver

Index