Lighttpd Source Code Review - Basic types
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:
- 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.
- 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;
...
};