Skip to content

Commit

Permalink
Merge pull request #17 from JadeMatrix/v0.8.0
Browse files Browse the repository at this point in the history
v0.8.0
  • Loading branch information
JadeMatrix authored Dec 26, 2017
2 parents 2286aad + a9b7ea6 commit 6ec5adf
Show file tree
Hide file tree
Showing 19 changed files with 532 additions and 510 deletions.
19 changes: 4 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,10 @@

[![stable version](https://img.shields.io/github/release/JadeMatrix/SHOW.svg?label=stable)](https://github.com/JadeMatrix/SHOW/releases/latest)
[![latest version](https://img.shields.io/github/release/JadeMatrix/SHOW/all.svg?label=latest)](https://github.com/JadeMatrix/SHOW/releases)
[![Documentation Status](https://readthedocs.org/projects/show-cpp/badge/?version=v0.8.0)](http://show-cpp.readthedocs.io/en/v0.8.0/?badge=v0.8.0)

The goal of SHOW is to make an idiomatic library for standalone webserver applications written for modern C++. SHOW is simple in the same way the standard library is simple — it doesn't make any design decisions for the programmer, instead giving them a set of primitives for building an HTTP web application.
SHOW is an idiomatic library for standalone webserver applications written for modern C++. SHOW is simple in the same way the standard library is simple — it doesn't make any design decisions for the programmer, instead offering a set of primitives for building an HTTP web application. Everything — when to serve, what requests to accept, even whether to send a response at all — is completely up to the programmer.

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.
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.0 and HTTP/1.1 requests.

SHOW uses the [`zlib` license](LICENSE). C++11 support and a POSIX operating system are required.

## How to use

You can find SHOW's documentation, including a tutorial, on [ReadTheDocs.io](http://show-cpp.readthedocs.io/).

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.

There are a few important points to keep in mind, however:

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.

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!
You can find SHOW's documentation, including a tutorial, on [ReadTheDocs.io](http://show-cpp.readthedocs.io/). SHOW uses the [`zlib` license](LICENSE). A C++11 compiler and a POSIX operating system are required.
38 changes: 15 additions & 23 deletions doc/Classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ Types
.. cpp:member:: std::string description

.. cpp:class:: query_args_t
.. cpp:class:: query_args_type
An alias for :cpp:class:`std::map\< std::string, std::vector\< std::string > >`, and can be statically initialized like one::
show::query_args_t args = {
show::query_args_type args = {
{ "tag", { "foo", "bar" } },
{ "page", { "3" } }
};
Expand All @@ -67,15 +67,15 @@ Types

* :cpp:type:`std::vector` on `cppreference.com <http://en.cppreference.com/w/cpp/container/vector>`_

.. cpp:class:: headers_t
.. cpp:class:: headers_type
An alias for :cpp:class:`std::map\< std::string, std::vector\< std::string >, show::_less_ignore_case_ASCII >`, where :cpp:class:`show::_less_ignore_case_ASCII` is a case-insensitive `compare <http://en.cppreference.com/w/cpp/container/map>`_ for :cpp:class:`std::map`.

While HTTP header names are typically given in ``Dashed-Title-Case``, they are technically case-insensitive. Additionally, in general a given header name may appear more than once in a request or response. This type satisfies both these constraints.

Headers can be statically initialized::
show::headers_t headers = {
show::headers_type headers = {
{ "Content-Type", { "text/plain" } },
{ "Set-Cookie", {
"cookie1=foobar",
Expand All @@ -97,7 +97,11 @@ Not all of these strictly represent an error state when throw; some signal commo
Connection interruptions
------------------------

.. cpp:class:: connection_timeout
.. cpp:class:: connection_interrupted
A common base class for both types of connection interruptions

.. cpp:class:: connection_timeout : public connection_interrupted
An object of this type will be thrown in two general situations:

Expand All @@ -106,44 +110,32 @@ Connection interruptions

In the first situation, generally the application will simply loop and start waiting again. In the second case, the application may want to close the connection or continue waiting with either the same timoute or some kind of falloff. Either way the action will be application-specific.

.. cpp:class:: client_disconnected
.. cpp:class:: client_disconnected : public connection_interrupted
This is thrown when SHOW detects that a client has broken connection with the server and no further communication can occur.

Exceptions
----------

.. cpp:class:: exception : std::exception
A common base class for all of SHOW's exceptions
.. seealso::

.. seealso::

* :cpp:type:`std::exception` on `cppreference.com <http://en.cppreference.com/w/cpp/error/exception>`_
* :cpp:type:`std::runtime_error` on `cppreference.com <http://en.cppreference.com/w/cpp/error/runtime_error/runtime_error>`_

.. cpp:class:: socket_error : exception
.. cpp:class:: socket_error : std::runtime_error
An unrecoverable, low-level error occurred inside SHOW. If thrown while handling a connection, the connection will no longer be valid but the server should be fine. If thrown while creating or working with a server, the server object itself is in an unrecoverable state and can no longer serve.

The nature of this error when thrown by a server typically implies trying again will not work. If the application is designed to serve on a single IP/port, you will most likely want to exit the program with an error.

.. cpp:class:: request_parse_error : exception
.. cpp:class:: request_parse_error : std::runtime_error
Thrown when creating a request object from a connection and SHOW encounters something it can't manage to interpret into a :cpp:class:`request`.

As parsing the offending request almost certainly failed midway, garbage data will likely in the connection's buffer. Currently, the only safe way to handle this exception is to close the connection.

.. cpp:class:: url_decode_error : exception
.. cpp:class:: url_decode_error : std::runtime_error
Thrown by :cpp:func:`url_decode()` when the input is not a valid `URL- or percent-encoded <https://en.wikipedia.org/wiki/Percent-encoding>`_ string.

.. note::
:cpp:func:`url_encode()` shouldn't throw an exception, as any string can be converted to percent-encoding.

.. cpp:class:: base64_decode_error : exception
Thrown by :cpp:func:`base64_decode()` when the input is not valid `base-64 <https://en.wikipedia.org/wiki/Base64>`_.

.. note::
:cpp:func:`base64_encode()` shouldn't throw an exception, as any string can be converted to base-64.

6 changes: 3 additions & 3 deletions doc/Connection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ Connection
Destructor for a connection, which closes it; any requests or responses created on this connection can no longer be read from or written to

.. cpp:member:: const std::string& client_address
.. cpp:function:: const std::string& client_address() const
The IP address of the connected client

.. cpp:member:: const unsigned int& client_port
.. cpp:function:: unsigned int client_port() const
The port of the connected client

.. cpp:function:: int timeout()
.. cpp:function:: int timeout() const
Get the current timeout of this connection, initially inherited from the server the connection is created from

Expand Down
52 changes: 20 additions & 32 deletions doc/Constants.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,33 @@
Constants
=========

.. cpp:namespace-push:: show

All constants are ``const``-qualified.

.. cpp:namespace-push:: show
Version
=======

.. cpp:class:: version
An anonymous ``struct`` containing information about the current SHOW version. Has the following members:

.. cpp:member:: std::string name
The proper name of SHOW as it should appear referenced in headers, log messages, etc.

.. cpp:member:: int major
The major SHOW version (``X.0.0``)

.. cpp:member:: int minor
The minor SHOW version (``0.X.0``)

.. cpp:member:: int revision
The SHOW version revision (``0.0.X``)

.. cpp:member:: std::string string
A string representing the major, minor, and revision version numbers
The ``version`` sub-namespace contains information about the current SHOW version. It has the following members:

.. cpp:namespace-push:: version

.. cpp:var:: char* base64_chars_standard
.. cpp:var:: std::string name
The standard set of `base-64 characters`_ for use with :cpp:func:`base64_encode()` and :cpp:func:`base64_decode()`
The proper name of SHOW as it should appear referenced in headers, log messages, etc.

.. cpp:var:: int major
.. _base-64 characters: https://en.wikipedia.org/wiki/Base64
The major SHOW version (``X.0.0``)

.. cpp:var:: char* base64_chars_urlsafe
.. cpp:var:: int minor
The URL_safe set of `base-64 characters`_ for use with :cpp:func:`base64_encode()` and :cpp:func:`base64_decode()`, making the following replacements:
The minor SHOW version (``0.X.0``)

.. cpp:var:: int revision
.. _base-64 characters: https://en.wikipedia.org/wiki/Base64
The SHOW version revision (``0.0.X``)

.. cpp:var:: std::string string
* ``+`` → ``-``
* ``/`` → ``_``
A string representing the major, minor, and revision version numbers
20 changes: 0 additions & 20 deletions doc/Functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,3 @@ Functions
.. cpp:function:: std::string url_decode( const std::string& )
Decode a `URL- or percent-encoded <https://en.wikipedia.org/wiki/Percent-encoding>`_ string. Throws :cpp:class:`url_decode_error` if the input string is not validly encoded.

.. cpp:function:: string show::base64_encode( const std::string& o, const char* chars = base64_chars_standard )
`Base-64 <https://en.wikipedia.org/wiki/Base64>`_ a string ``o`` using the character set ``chars``, which must point to a ``char`` array of length 64.

.. seealso::

* :cpp:var:`base64_chars_standard`

* :cpp:var:`base64_chars_urlsafe`

.. cpp:function:: std::string base64_decode( const std::string& o, const char* chars = base64_chars_standard )
Decode a `base-64 <https://en.wikipedia.org/wiki/Base64>`_ encoded string ``o`` using the character set ``chars``, which must point to a ``char`` array of length 64. Throws a :cpp:class:`base64_decode_error` if the input is not encoded against ``chars`` or has incorrect padding.

.. seealso::

* :cpp:var:`base64_chars_standard`

* :cpp:var:`base64_chars_urlsafe`
38 changes: 21 additions & 17 deletions doc/Request.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Request

* :cpp:type:`std::istream` on `cppreference.com <http://en.cppreference.com/w/cpp/io/basic_istream>`_

.. cpp:enum:: content_length_flag_type
.. cpp:enum:: content_length_flag
A utility type for :cpp:member:`unknown_content_length()` with the values:
A utility type for :cpp:func:`unknown_content_length()` with the values:

+-----------+--------------+
| Value | Evaluates to |
Expand All @@ -28,11 +28,11 @@ Request
| ``MAYBE`` | ``true`` |
+-----------+--------------+

.. cpp:member:: const std::string& client_address
.. cpp:function:: const std::string& client_address() const
The IP address of the client that sent the request

.. cpp:member:: const unsigned int& client_port
.. cpp:function:: const unsigned int client_port() const
The port of the client that sent the request

Expand All @@ -42,7 +42,7 @@ Request

.. seealso::

* :cpp:member:`unknown_content_length`
* :cpp:func:`unknown_content_length`

.. cpp:function:: request( connection& )
Expand All @@ -60,15 +60,19 @@ Request

.. _move constructor: http://en.cppreference.com/w/cpp/language/move_constructor

.. cpp:member:: const http_protocol& protocol
.. cpp:function:: void flush()
The HTTP protocol used by the request. If ``NONE``, it's usually safe to assume HTTP/1.0. If ``UNKNOWN``, typically either a *400 Bad Request* should be returned, just assume HTTP/1.0 to be permissive, or try to interpret something from :cpp:member:`protocol_string`.
Flushes the request contents from the buffer, putting it in a state where the next request can be extracted. It is only safe to call this function if :cpp:func:`unknown_content_length()` evaluates to ``false``.

.. cpp:member:: const std::string& protocol_string
.. cpp:function:: http_protocol protocol() const
The raw protocol string sent in the request, useful if :cpp:member:`protocol` is ``UNKNOWN``
The HTTP protocol used by the request. If ``NONE``, it's usually safe to assume HTTP/1.0. If ``UNKNOWN``, typically either a *400 Bad Request* should be returned, just assume HTTP/1.0 to be permissive, or try to interpret something from :cpp:func:`protocol_string`.

.. cpp:member:: const std::string& method
.. cpp:function:: const std::string& protocol_string() const
The raw protocol string sent in the request, useful if :cpp:func:`protocol` is ``UNKNOWN``

.. cpp:function:: const std::string& method() const
The request method as a capitalized ASCII string. While the HTTP protocol technically does not restrict the available methods, typically this will be one of the following:

Expand Down Expand Up @@ -96,7 +100,7 @@ Request

* `List of common HTTP methods on Wikipedia <https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods>`_ for descriptions of the methods

.. cpp:member:: const std::vector< std::string >& path
.. cpp:function:: const std::vector< std::string >& path() const
The request path separated into its elements, each of which has been URL- or percent-decoded. For example::
Expand All @@ -111,7 +115,7 @@ Request
"こんにちは"
}
.. cpp:member:: const query_args_t& query_args
.. cpp:function:: const query_args_type& query_args() const
The request query arguments. SHOW is very permissive in how it parses query arguments:

Expand All @@ -127,25 +131,25 @@ Request
| ``?foo&bar=1&bar=2`` | ``{ { "foo", { "" } }, { "bar", { "1", "2" } } }`` |
+----------------------+----------------------------------------------------+

.. cpp:member:: const headers_t& headers
.. cpp:function:: const headers_type& headers() const
The request headers

.. seealso::

* `List of common HTTP headers on Wikipedia <https://en.wikipedia.org/wiki/List_of_HTTP_header_fields>`_

.. cpp:member:: const content_length_flag_type& unknown_content_length
.. cpp:function:: content_length_flag unknown_content_length() const
Whether the content length of the request could be interpreted

This member may be a bit confusing because it is "*un*-known" rather than "know". It's convenient for :cpp:type:`content_length_flag_type` to evaluate to a boolean value, but there are two possible reasons the content length would be unknown. Either
This member may be a bit confusing because it is "*un*-known" rather than "know". It's convenient for :cpp:type:`content_length_flag` to evaluate to a boolean value, but there are two possible reasons the content length would be unknown. Either

1. the request did not send a *Content-Length* header, or
2. the value supplied is not an integer or multiple *Content-Length* headers were sent.

In many languages (including C++), 0 is ``false`` and any other value is ``true``; so the boolean value needs to be ``false`` for a known content length and ``true`` for anything else.

.. cpp:member:: unsigned long long content_length
.. cpp:function:: unsigned long long content_length() const
The number of bytes in the request content; only holds a meaningful value if :cpp:member:`unknown_content_length` is ``YES``/``true``
The number of bytes in the request content; only holds a meaningful value if :cpp:func:`unknown_content_length` is ``YES``/``true``
4 changes: 2 additions & 2 deletions doc/Response.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Response

* :cpp:type:`std::ostream` on `cppreference.com <http://en.cppreference.com/w/cpp/io/basic_ostream>`_

.. cpp:function:: response( request&, http_protocol, const response_code&, const headers_t& )
.. cpp:function:: response( connection&, http_protocol, const response_code&, const headers_t& )
Constructs a new response in response to a request. The protocols, response code, and headers are immediately buffered and cannot be changed after the response is created, so they have to be passed to the constructor.
Constructs a new response to the client who made a connection. The protocols, response code, and headers are immediately buffered and cannot be changed after the response is created, so they have to be passed to the constructor.

.. cpp:function:: ~response()
Expand Down
Loading

0 comments on commit 6ec5adf

Please sign in to comment.