The Appweb HTTP web server is the fastest little web server. It is a high-performance yet compact embeddable webserver that has a modular, secure core. It supports an extensive feature set including: HTTP/1, HTTP/1.1, HTTP/2, WebSockets, TLS/SSL, CGI, PHP, ESP, Virtual Hosts, Digest Authentication, Apache style configuration, and logging and security sandboxing. Yet Appweb remains very small (from 1MB) and exceptionally fast (> 7,000 requests per second).
Appweb supports dynamic web application frameworks like ESP, and PHP. It also supports older web creation strategies like CGI.
Appweb is an event-driven, multi-threaded web server and is able to support many thousands of requests per second but it does not try to scale out like an enterprise class server for top-tier web sites. Rather, it aims to be robust, secure and blazing fast with low and predictable memory utilization.
Appweb can be deployed in several ways:
- As a stand-alone web server
- Embedded in applications and devices
- Proxied behind front-end web servers
Stand-Alone Web Server
The Appweb distribution contains a robust, secure, stand-alone web server. The binary installation will install this web server to be automatically started as a system daemon/service. It is supervised by a manager process which acts as a guardian to start, monitor and restart Appweb if required.
Embedded in Applications and Devices
Appweb provides an embeddable server and client library so that any application can be web-enabled. When embedded, the application can provide data and respond to requests over HTTP. For devices, Appweb can be configured with the minimal required set of features for minimal memory and security footprint.
Appweb is an ideal application web server when running behind a reverse proxy. The reverse proxy dispatches incoming requests to a set of Appweb instances which can effectively host ESP, or PHP applications.
Appweb typically runs one, multi-threaded, event driven process. Appweb uses a non-blocking event driven architecture and is capable of running many requests simultaneously.
Event Driven and Multi-threaded
While Appweb is multi-threaded, it does not consume a thread per request. A high performance thread pool is used to temporarily loan worker threads as required to service incoming requests. While waiting for incoming and outgoing I/O, the worker thread is returned to the pool for use by other requests. This architecture minimizes memory usage by reusing threads and provides excellent performance due to the thread pool. Further, it scales well, as one thread can service many requests.
Embeddable web servers have several competing goals:
- Minimize memory footprint
- Minimize CPU consumption
- Maximize security
- Provide a strong and standard feature set
To meet all these goals in one server is difficult. Appweb uses loadable modules so that the core Appweb HTTP server may be compact, secure, and fast while still having the ability to grow functionality as required.
An Appweb loadable module is a discrete unit of functionality that may be linked into Appweb to enhance the core HTTP server. A module may be a request handler, filter, network connector, or simply a "lump" of code.
Appweb has a modular architecture where modules are dynamically loaded at runtime depending on the desired configuration. The key components of Appweb are:
|Multithreaded Portable Runtime||Cross-platform, multi-threaded portable runtime. Includes services for memory allocation, dynamic module loading, safe string handling, lists, hashing, command execution, socket communications, threads, thread synchronization, thread-pool, events, timers, debug trace and logging.|
|Secure Sockets Layer (SSL)||Secure Socket Layer protocol stack. This is a virtual interface that can selectively support a variety of SSL providers including: the MbedTLS and OpenSSL stacks.|
|HTTP/1||Traditional HTTP/1 text based HTTP protocol.|
|HTTP/2||HTTP/2 binary multiplexing HTTP protocol.|
|WebSockets||Binary full-duplex communication protocol.|
|Web Framework Pipeline||Core HTTP server. Includes services for initialization, HTTP protocol handling, logging, virtual hosts, directory and location blocks, request processing pipeline, and module loading and management.|
|File Handler||The File handler serves static content such as HTML pages, images and PDF files.|
|ESP Handler||The Embedded Server Pages (ESP) web framework is an advanced, Model,View,Controller "C" language web framework.|
|PHP Handler||In-memory handler module for the PHP web environment.|
|CGI Handler||Common Gateway Interface handler.|
|Chunk Filter||The Chunk applies Transfer Chunk Encoding to outgoing data. Chunk encoding enables the HTTP connection to be reused for subsequent requests without having to establish a new TCP/IP connection. This can significantly improve network and application performance.|
|Range Filter||The Range filter selects the requested I/O range by the client.|
Appweb HTTP Server Core
The core Appweb HTTP server is one of the relatively smaller components of the Appweb product when compared to the dynamic modules that run atop it. The core server provides a set of services for the handlers, filters and connectors to use when serving content to clients. The goal is to centralize any facilities for the modules so it will not be replicated.
The core services include: the main HTTP processing and socket communications, initialization and parsing the Appweb configuration file, server, virtual host and directory authorization management, request pipeline management and request processing.
The core server also configures and enforces any sandbox resource limits that have been requested in the configuration file, for example: thread limits and request URI and body size limitations. This enables Appweb to be deterministic in its use of system resources and to be a good system citizen. The core Appweb server can be configured to execute single or multi-threaded. With appropriate sandbox limits, it can serve thousands of requests per second if required.
Appweb uses an Apache style configuration file. This configuration file is read when Appweb starts and it manages every aspect of Appweb's configuration including what ports and addresses to listen to, what modules to load, where to find the web pages, and how to log requests.
The configuration file supports a wide variety of directives such as virtual hosting, directory authentication, port binding, aliasing, logging, and request pipeline configuration. It has conditional declarations and supports the inclusion of other configuration files. Appweb uses a one-pass traversal of the configuration file which means that the order of directives matters.
The Appweb HTTP core manages the processing of requests and the construction of the request processing pipeline. When a new HTTP request arrives, Appweb will examine the network interface on which the request arrived, and if it is assigned to an IP based virtual host, Appweb will route the request to be handled by that virtual host. If name based virtual hosting is configured, the determination of which virtual host will process the request must be deferred until the HTTP header has been parsed and the default server is used to initially parse the request. See the Virtual Hosts for more information.
HTTP Header Parsing
The first line of the HTTP request specifies the HTTP operation method to use, the URI to access and the variant of the HTTP protocol to use. This typically looks like:
GET /index.html HTTP/1.1
This example is asking for the /index.html document via the GET method using the HTTP/1.1 protocol.
After the request line, follow a set of HTTP headers. Typically there are 5-15 headers which specify further information to help the server process the request. Headers are of the format:
Some typical headers are:
|Authorization||Authorization details including user name, realm, password digest and other authorization parameters to implement Basic and Digest authentication.|
|Connection||Describe how the TCP/IP connection should be managed when the request completes.|
|Content-Length||Length of any addition data with a POST request.|
|Content-Type||Mime types the client prefers to accept in response to this request.|
|Cookie||Cookie associated with the URI in the clients cookie cache.|
|Host||Name to the target host to serve the request. This specifies the host name when using virtual hosting.|
|If-Modified-Since||Only return the content if it has been modified since the date specified. Clients who have cached copies of a document (or graphics) use this header to allow the server to skip copying the document if it has not changed.|
|Keep-Alive||Request the server to keep the connection alive so that subsequent requests can reuse the connection.|
Appweb parses the HTTP headers and stores their values in a hash table for access by the request handlers and pipeline stages. When all the headers have been processed Appweb proceeds to do handler matching. This will occur before any associated POST data has been read from the client. POST data may be form data submitted by the client or it may be a file upload using the PUT method.
Appweb has a powerful request routing engine that processes client HTTP requests. The engine is configured with a set of routes from the Appweb configuration file. When a request is received, it tests various routes and selects the best route to handle the request. In the process, routes may redirect or rewrite the request as required.
An Appweb configuration will typically have many routes. The configured routes are tested in order by matching the route regular expression pattern against the request URI. A route may require that further preconditions be met before it is suitable to process the request. If the required conditions are not met, the next route in the configuration will be tested. There is always a catch-all route that will process the request if all prior routes fail to qualify.
To define a route, use the Route directive. For example:
<Route /projects/video> SetHandler videoHandler </Route>
This will cause the videoHandler to respond to any URI that begins with "/projects/video" after the http://site portion of the URI.
To associate a handler to a given extension, use the AddHandler directive. For example:
AddHandler espHandler myDoc
This will cause the ejsHandler to respond to any URI that has a ".myDoc" extension.
Multiple routes may match a URI. In this case they are applied in the order they are specified in the Appweb configuration file.
Once the route has been selected, the request pipeline is constructed and the request handler is started to create the response for the client.
The Appweb core uses a bidirectional pipeline to process requests and generate responses. This consists of a mechanism of queues, packets, buffering and event scheduling. The pipeline architecture is highly optimized and supports the efficient transmission of data without copying. It uses vectored, scatter/gather writes to the network to avoid the costly aggregation of data and headers in a single buffer before writing to the network.
The request pipeline is comprised of an incoming and outgoing stream. Each stream is in turn comprised of stages. Each stage has a data queue with two service routines, one to accept data without queuing and one for delayed servicing of previously queued data. Pipeline stages support flow-control and can queue data for delayed servicing if the downstream stage cannot currently accept data.
A typical pipeline consists of the following stages.
- One handler
- Zero or more filters
- One network connector
The Appweb HTTP core is responsible for parsing the incoming HTTP request. The HTTP request first line specifies the request URI resource and the request headers provide additional context. The HTTP core stores the parsed headers for use by the routing engine and creates a new request pipeline to process the request. The incoming and outgoing pipelines are constructed separately as each direction may require a different set of filters.
The routing engine matches the request against the configured request routes and selects the best route for the request. A route specifies the handler to manage the request and other context such as where to find documents to serve. The routing engine may rewrite the request and then reroute. The engine also manages HTTP authorization.
Depending on the request URI and headers, the appropriate handler will be selected. There are handlers for ESP, PHP, CGI and static file data.
The handler is responsible for receiving the request and creating a response. It generates response content based on the HTTP request URI, headers, any associated body data, and potential application session state. The output data flows through the output pipeline before transmission by the network connector to the client.
There is one handler for each request. The Appweb core selects that handler based on a request matching algorithm. Handlers are configured in the Appweb configure file via the AddHandler and the SetHandler directives.
The filters job is to permute the data in some way and pass it on to the next stage in the pipeline. Filters connect one to another and multiple filters may be used in both the incoming and outgoing pipeline directions.
The order of filters typically does matter. For example, compressing data before encrypting is not the same as encrypting then compressing. Appweb provides the AddInputFilter and AddOutputFilter directives to assist in constructing filter pipelines.
Appweb uses filters to implement Transfer Authorization, Chunk Encoding and HTTP Ranged requests. By using filters to implement these features, they automatically become available to all handlers and all content types.
The connector is responsible for transmitting the outgoing data to the client. It receives a set of packets representing the data to send to the client. The connector is responsible for requesting the Appweb HTTP core to create a packet with the HTTP response headers. It is at this stage that Appweb can determine if the TCP/IP connection can be kept alive for subsequent requests on the same connection.
Connectors will receive an empty header packet which must first be filled with the HTTP response headers. By delaying the generation of the HTTP headers until the last possible moment, Appweb maximizes every opportunity to optimize the response and use the HTTP/1.1 Keep-Alive feature even for dynamically generated data.
The Net connector is a generalized network connector that can transmit content from any handler to the client. It supports vectored writes to avoid copying data when interleaving with HTTP, range or chunk headers.
Appweb supports loadable modules for the ESP, and PHP web frameworks.
Embedded Server Pages Web Framework
The ESP web framework is a high performance, Model/View/Controller web framework. It uses the "C" language embedded in HTML web pages and for Controllers. It provides an application generator, templating and layout engine, a Model/View/Controller framework, extensive caching, and a library of Ajax view controls. It is blazing fast via the use of "C" and caching of compiled web pages and rendered HTML.
PHP is a widely-used general-purpose scripting language that is especially suited for Web development and can be embedded into HTML. Appweb provides a fast, in-memory handler for the PHP web framework.
Appweb provides a suite of measures designed to increase the security of your web server:
- Secure Sockets Layer
- Authorization directives
- Sandbox directives
- MPR — secure portable runtime
- Manager monitoring process
Appweb supports the MbedTLS and OpenSSL stacks. You can configure server and/or client certificate-based authentication. SSL and TLS are supported.
Appweb provides the Digest authentication mechanism and supports both file and PAM-based authentication backends.
Sandboxing is the term applied to running Appweb in a confined environment. Appweb has a set of configuration file directives that allow you to define a sandbox which limits the resources that Appweb may used for a given request. By using well defined sandbox directives, you can help ensure that your application and system will not be compromised by malicious requests.
Multithreaded Portable Runtime (MPR)
Appweb is built upon a portable runtime layer called the MPR. This layer insulates the rest of the product from the underlying platform and allows it to be highly portable to new operating systems or hardware.
The MPR provides a suite of services that facilitate the creation of high performance, multi-threaded management and server applications, including: thread management, dynamic module loading, networking, safe string handling, timers, XML parsing and logging.
The MPR also provides a safer environment in which to program as it replaces 'C' APIs that are prone to buffer overflows and other similar security exploits. The MPR includes a high performance, safe string library that supports a secure programming style.
The MPR event processing mechanism can be easily integrated into existing applications. It supports single and multi-threaded architectures, polled or async event notification, POSIX select waiting and Windows message loops. When coupled with C and C++ APIs, API web can be easily integrated into most C/C++ applications.
The MPR includes a high performance memory allocator and generational garbage collector. The allocator is a fast, coalescing allocator that exhibits very low fragmentation. It is optimized for frequent allocations of small blocks and uses a background collector for freeing unused memory.
The garbage collector is somewhat unusual in a C program. However, garbage collection is especially well suited for long running applications like a web server as it virtually eliminates memory leaks. Unlike traditional memory allocation where free must be called, Appweb 4 uses the opposite approach where memory that must be retained, needs to be actively managed to prevent garbage collection. This means that a managed reference must be held for all active memory. See Memory Allocation for more details.
Appweb fully supports both IPv4 and IPv6 for both server and client operations. You can configure Appweb to listen on both IPv4 and IPv6 addresses.
The Appweb HTTP server library can be embedded in an application to web-enable that application. Appweb provides two levels of embedding:
One Line Embedding
Appweb can be embedded in one C statement. This will create a web server instance based on the given configuration file.:
Appweb also provides a detailed API so you can create an embedded web server instance and control virtual hosts, port binding, directory and location blocks and every other part of Appweb. See the Appweb Native API for full details.