You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The goal of SHOW is to make an idiomatic library for tiny, standalone webserverlets written for modern C++. Currently requires C++11 or higher and a POSIX operating system.
6
+
The goal of SHOW is to make an idiomatic library for standalone webserver applications written for modern C++. Currently requires C++11 or higher and a POSIX operating system.
7
7
8
-
SHOW uses the `zlib` license.
8
+
Both HTTP/1.0 and HTTP/1.1 are supported. SHOW assumes a modern approach to application hosting, and is intended to be run behind a full reverse proxy such as [nginx](https://nginx.org/). As such, SHOW will not support HTTP/2 or TLS (HTTPS). Instead, you should write your applications to serve local HTTP/1.x requests.
9
9
10
-
### Feature status
11
-
12
-
| Support for | Status |
13
-
| --- | --- |
14
-
| Linux | working on CentOS 7 |
15
-
| OS X / macOS | working, tested on 10.12.6 |
16
-
|~~Windows~~||
17
-
| IPv4 | working |
18
-
| IPv6 | working |
19
-
| HTTP/1.0 | working |
20
-
|~~HTTP/1.1~~||
21
-
|~~HTTP/2.0~~||
10
+
SHOW uses the [`zlib` license](LICENSE).
22
11
23
12
## How to use
24
13
25
-
SHOW is designed to give the programmer full control over how and where HTTP requests are handled.
14
+
*This shows the basic usage of SHOW; see the [examples](examples) for more advanced stuff*
15
+
16
+
SHOW is designed to give the programmer full control over how and where HTTP requests are handled. Any number of servers can be created which will then accept connections. From each connection, one or more requests can be extracted, each of which can then be responded to separately. Everything — when to serve, what requests to accept, even whether to send a response at all — is completely up to the application.
17
+
18
+
There are a few important points to keep in mind, however:
19
+
20
+
HTTP/1.1 — and HTTP/1.0 with an extension — allow multiple requests to be pipelined on the same TCP connection. Don't try to extract another request from a connection before you've finished handling the last one (the `response` object has been destroyed)! If you don't want to even send a response for a given pipelined request, you must close the connection, otherwise the client will think you're responding to the first unanswered request. SHOW can't know with certainty where on the connection one request ends and another starts — it's just the nature of pipelined HTTP. Sure, the *Content-Length* header could be used, and [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding) has well-established semantics, but if neither are used it is up to your application to figure out the end of the request's content. In general, you should reject requests whose length you can't readily figure out, but SHOW leaves that decision up to the programmer.
21
+
22
+
Another thing SHOW doesn't prevent you from doing is creating multiple responses to the same request. This is mainly for simplicity and because it's very unlikely to happen in a well-structured program. Doing this may throw an exception in the future; for now, just don't do it!
26
23
27
24
### Including
28
25
@@ -34,6 +31,12 @@ SHOW is entirely contained in a single file, so all you have to do is include it
34
31
35
32
in your source files as needed.
36
33
34
+
Adding header search paths will depend on the compiler, but for GCC and Clang simply add an `-I` option:
To start serving requests, first create a server object:
@@ -42,44 +45,53 @@ To start serving requests, first create a server object:
42
45
show::server my_server(
43
46
"0.0.0.0", // IP address on which to serve
44
47
9090, // Port on which to serve
45
-
2 // Listen timeout, in seconds
48
+
10 // Listen timeout, in seconds
46
49
);
47
50
```
48
51
49
-
For each call of `my_server.serve()` a single `show::request` object will be returned or a `show::connection_timeout` thrown. You may want to use something like this:
52
+
That's it, you've made a server that sits there and hangs on the first connection it gets (as long as it arrives within 10 seconds). Not terribly useful, but that's easy to fix.
53
+
54
+
### Connections
55
+
56
+
For each call of `my_server.serve()` a single `show::connection` object will be returned or a `show::connection_timeout` thrown. You may want to use something like this:
50
57
51
58
```cpp
52
-
while( /* serve condition */ )
59
+
while( true )
53
60
try
54
61
{
55
-
show::request request( test_server.serve() );
56
-
// handle request here
62
+
show::connection connection( my_server.serve() );
63
+
// handle request(s) here
57
64
}
58
65
catch( show::connection_timeout& ct )
59
66
{
60
-
std::cout << "timeout exceeded! looping...\n";
67
+
std::cout
68
+
<< "timed out waiting for a connection, looping..."
69
+
<< std::endl
70
+
;
61
71
continue;
62
72
}
63
73
```
64
74
65
-
The server listen timeout can be a positive number, `0`, or `-1`. If it is `-1`, the server will continue listening until interrupted by a signal; if `0`, the `serve()` throw a `show::connection_timeout` immediately unless connections are available.
75
+
The server listen timeout can be a positive number, `0`, or `-1`. If it is `-1`, the server will continue listening until interrupted by a signal; if `0`, `serve()` will throw a `show::connection_timeout` immediately unless connections are available.
76
+
77
+
The connection is now independent from the server. You can adjust the connection's timeout independently using `connection::timeout(int)`. You can also pass it off to a worker thread for processing so your server can continue accepting other connections; this is usually how you'd implement a real web application.
66
78
67
79
### Requests
68
80
69
-
`show::request` objects have a large number of fields containing the HTTP request's metadata:
81
+
`show::request` objects have a number of `const` fields containing the HTTP request's metadata:
70
82
71
83
| Field | Description |
72
84
| --- | --- |
73
-
| `protocol` | An enum value, either `HTTP_1_0`, `HTTP_1_1`, `HTTP_2_0`, `NONE`, or `UNKNOWN` |
85
+
| `protocol` | An enum value, either `HTTP_1_0`, `HTTP_1_1`, `NONE`, or `UNKNOWN` |
74
86
| `protocol_string` | The raw protocol string, useful if `protocol` is `UNKNOWN` |
75
-
| `method` | A capitalized `std::string` containing the [request method](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods) (`GET`, `POST`, etc.) |
87
+
| `method` | An uppercase `std::string` containing the [request method](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods) (`GET`, `POST`, etc.) |
76
88
| `path` | The request path split into a `std::vector` of `std::string`s, with [URL-decoding](https://en.wikipedia.org/wiki/Percent-encoding) already handled |
77
89
| `query_args` | The request query arguments split into a `std::map` of `std::vector`s of `std::string`s, with URL-decoding already handled |
78
90
| `headers` | The request headers split into a `std::map` of `std::vector`s of `std::string`s |
79
91
| `unknown_content_length` | An enum value, either `NO` (length is given), `YES` (length is missing), or `MAYBE` (length is indeterminate or corrupt), with `YES` and `MAYBE` evaluating to `true` |
80
92
| `content_length` | The raw content length string, useful if `unknown_content_length` is `MAYBE` |
81
93
82
-
`query_args` and `headers` are somewhat complicated, as each key can appear multiple times and so have multiple values. [`std::map< std::string, std::vector< std::string > >`](http://en.cppreference.com/w/cpp/container/map) was chosen over than [`std::multimap< std::string, std::string >`](http://en.cppreference.com/w/cpp/container/multimap) as the former is easier for read operations (request objects are read-only).
94
+
`query_args` and `headers` are somewhat complicated, as each key can appear multiple times and so have multiple values. [`std::map< std::string, std::vector< std::string > >`](http://en.cppreference.com/w/cpp/container/map) was chosen over than [`std::multimap< std::string, std::string >`](http://en.cppreference.com/w/cpp/container/multimap) as the former is easier for read operations (`request`s are generally read-only).
83
95
84
96
Note that the above fields do not include the request content, if any. This is because HTTP allows the request content to be streamed to the server. In other words, the server can interpret the headers then wait for the client to send data over a period of time. For this purpose, `show::request` inherits from [`std::streambuf`](http://en.cppreference.com/w/cpp/io/basic_streambuf), implementing the read/get functionality. You can use the raw `std::streambuf` methods to read the incoming data, or create a [`std::istream`](http://en.cppreference.com/w/cpp/io/basic_istream) from the request object for `std::cin`-like behavior.
85
97
@@ -102,7 +114,8 @@ Also note that individual request operations may timeout, so the entire serve co
There are two easy ways to build the examples in this directory. If you have [`cmake`](https://cmake.org/) installed, there are build instructions in the supplied *CMakeLists.txt*. To use it, first run
2
+
3
+
```sh
4
+
mkdir -p make
5
+
cd make
6
+
cmake ..
7
+
```
8
+
9
+
then either run `make` to build all examples, or `make $NAME` to build a specific example. The other way to build any of the examples is manually with Clang (`clang++`) or GCC (`g++`). Assuming you're running this in the "make" directory from above:
Each of these servers can be tested from a second terminal window.
16
+
17
+
# `hello_world`
18
+
19
+
The most basic server possible — returns *200 OK* with a plaintext "Hello World" message for every request. You can test this server either with `curl`:
20
+
21
+
```sh
22
+
curl -i 0.0.0.0:9090
23
+
```
24
+
25
+
or by navigating to `http://0.0.0.0:9090/` in your browser.
26
+
27
+
# `echo`
28
+
29
+
Echoes back the contents of any *POST* request. Very simple and unsafe — see [`streaming_echo`](#streaming_echo) for a more thorough implementation. You can test this server with
Demonstrates how to integrate SHOW's HTTP/1.1 support into your application. The easiest way to test this server is with [Netcat](https://en.wikipedia.org/wiki/Netcat); after starting the server, connect with
38
+
39
+
```sh
40
+
nc 0.0.0.0 9090
41
+
```
42
+
43
+
Then, before 10 seconds have passed (that's the connection timeout hard-coded into this server), paste at least one HTTP request; you can wait up to 10 seconds between pastes. Here are two simple requests (note that the two trailing newlines are required for the first one!):
44
+
45
+
```http
46
+
GET / HTTP/1.1
47
+
Content-Length: 0
48
+
49
+
50
+
```
51
+
52
+
```http
53
+
POST /foo/bar HTTP/1.1
54
+
Content-Length: 11
55
+
Content-Type: text/plain
56
+
57
+
Hello World
58
+
```
59
+
60
+
# `streaming_echo`
61
+
62
+
A more advanced echo server that streams large requests using `std::istream` and responds using `std::ostream`. Run & test it like `http_1_1`, but use a longer request you can enter in parts. For example, first paste this into Netcat:
The server should echo back each of the three lines as soon as you paste them in. Note that there is a newline following each of those data chunks. This might not show up with some Markdown renderers, so try opening this file in a plaintext editor and copying from there.
88
+
89
+
# `fileserve`
90
+
91
+
A basic file server that serves a single directory and guesses [MIME types](https://en.wikipedia.org/wiki/Media_type#mime.types). The code contains conditional sections that use [C++17's `filesystem`](http://en.cppreference.com/w/cpp/filesystem) library if it is available; otherwise it uses the POSIX `dir`/`dirent`.
92
+
93
+
To try this example, start it with a directory:
94
+
95
+
```sh
96
+
./fileserve path/to/directory
97
+
```
98
+
99
+
then navigate to `http://0.0.0.0:9090/` in your browser.
100
+
101
+
# `multiple_clients`
102
+
103
+
A multi-threaded server that handles one connection per thread, as a real application should. This server doesn't do much except return *501 Not Implemented* for every request.
0 commit comments