Lighttpd uses modular design. In programs like web servers, that uses many data structures for both optimization and ease of develop, common choice is to define this data structures like utilities. Apache uses Apache portable runtime (APR), Nginx defines them in src/core subdirectory and lighttpd also defines them. As the source code of lighttpd is not complicated as other web servers, source files of this type are reside in src beside other sources.

Basic types in lighttpd are composed as:

  1. Utility types
  • array. Defined in array.h, as a simple array to contain same type objects.
  • buffer, Defined in buffer.h, a controlled container of mainly text data.
  1. Core types
  • request
  • response
  • server
  • server_config
  • connection

Buffer

Buffer is a key type in lighttpd, it contains string, data and represents some meaning about them. it's definition is as folow:

/* generic string + binary data container; contains a terminating 0 in both
 * cases
 *
 * used == 0 indicates a special "empty" state (unset config values);
 * ptr might be NULL, too.
 *
 * copy/append functions will ensure used >= 1
 * (i.e. never leave it in the special empty state)
 */
typedef struct {
   char *ptr;
   /* "used" includes a terminating 0 */
   uint32_t used;
   /* size of allocated buffer at *ptr */
   uint32_t size;
} buffer;

As the comment, buffer is a generic data container for both string and data, and a terminating NULL. field ptr points to the real data in memory and if the buffer is empty, it may be NULL. Empty state has different meaning in different situations. The comment says it is used to indicate unset, may be for configuration file's options. By the way, the standard way to check empty state is to use used field that saves the realy used amount of memory. size field indicates the total memory allocated for this buffer, so the used memory may be lower than allocation.

Buffer operation includes many functions like init, free, move, copy, append, extend and many other. String operations are implemented into buffer itself, but data operations are mainly implemented in array type as array uses buffer as it's back. Read the buffer.h and it's comment to understand supported operations.

Array

Array uses buffer as it's underlying memory. This let's array to use buffers abstraction, buffer is just a countineous chunk of memory, anyway.

implementation of array is based on 3 auxilary types: struct data_unset, struct data_method and enum data_type_t. In fact, array of every special type, has it's own structure, controlled by DATA_UNSET macro's fields:

struct data_unset; /* declaration */
 
struct data_methods {
    struct data_unset *(*copy)(const struct data_unset *src); \
    void (*free)(struct data_unset *p); \
    void (*insert_dup)(struct data_unset *dst, struct data_unset *src);
};
 
typedef enum { TYPE_STRING, TYPE_ARRAY, TYPE_INTEGER, TYPE_CONFIG, TYPE_OTHER } data_type_t;
#define DATA_UNSET \
    buffer key; \
    const struct data_methods *fn; /* function table */ \
    data_type_t type

Every array struct, has DATA_UNSET as it's first line, that adds a buffer named key as backend, a method table named fn and a field to distinguish type of objects stored in array. fn is pointer to some data-specific methods to handle copy, free and duplication of data.

WIP

Chunck

chunk provides a linked list of source of data. In general, data sources are files and memory. It is called chunck as it is not a countineous area of memory or file data, but a sequence of scattered buffers to form a particualr data ready to be sent (using scatter output) or received (using gatter input).

typedef struct chunk_file_view {
    char    *mptr; /* base pointer of mmap'ed area */
    off_t    mlen; /* length of mmap'ed area */
    off_t    foff; /* offset from the start of the file */
    int    refcnt;
} chunk_file_view;

typedef struct chunk {
    struct chunk *next;
    enum { MEM_CHUNK, FILE_CHUNK } type;

    buffer *mem; /* either the storage of the mem-chunk or the name of the file */

    /* the size of the chunk is either:
     * - mem-chunk: buffer_string_length(chunk::mem) - c->offset
     * - file-chunk: chunk::file.length - c->offset
     */
    off_t offset;

    struct {
        /* filechunk */
        off_t  length; /* end pos + 1 in file (octets to send: file.length - c->offset) */

        int    fd;
        uint8_t is_temp; /* file is temporary and will be deleted if on cleanup */
        uint8_t busy;    /* file chunk not in page cache; reading might block */
        uint8_t flagmask;/* (internal; used with preadv2() RWF_NOWAIT) */
      #if defined(HAVE_MMAP) || defined(_WIN32) /*(see local sys-mmap.h)*/
        chunk_file_view *view;
      #endif
        void *ref;
        void(*refchg)(void *, int);
    } file;
} chunk;

As you may get, memroy-backed chunck is just a buffer. If the chunck is baked by a file, data can be read by file field.

Now, look at next field. It forms a single-linkd list (point to next). This mechanism let's us to store data in chuncks.

an other type that is related to chunck is chunckqueue. [!WIP]


Core types

Core types are higher level types that forms the server's operations. Almost in any web based program (client-server), this types are present.

server

Most high level type in lighttpd is server. It contains information about a (virtual) server.

struct server {
    void *plugin_slots;
    array *config_context;
    int config_captures;

    struct fdevents *ev;
    int (* network_backend_write)(int fd, chunkqueue *cq, off_t max_bytes, log_error_st *errh);
    handler_t (* request_env)(request_st *r);

    ...

    uint32_t lim_conns;
    connection *conns;
    connection *conns_pool;

    log_error_st *errh;

    ...

    /* members used at start-up or rarely used */

    handler_t (* plugins_request_reset)(request_st *r);/*(for cgi.local-redir)*/

    server_config srvconf;
    void *config_data_base;

    server_socket_array srv_sockets;
    server_socket_array srv_sockets_inherited;
    struct { void *ptr; uint32_t used; } plugins;

    ...

    const buffer *default_server_tag;
    char **argv;
    
    ...
};