In this article, I will show how to use the
libevent library to write a simple Web server that will issue jpeg image files at the request of clients.
The
libevent library provides programmers with access to the cross-platform asynchronous network API. Based on this library, you can create high-performance network applications. For example,
libevent is used in such well-known applications as
Memcached (distributed caching system) and
TOR (distributed anonymous network).
')
Download server code
here .
Let's start the code review with the main () function:75:int main (int argc, char *argv[])
76:{
77: struct event_base *ev_base;
78: struct evhttp *ev_http;
79:
80: if (argc != 3) {
81: printf ("Usage: %s host port\n", argv[0]);
82: exit (1);
83: }
84:
85: ev_base = event_init ();
86:
87: ev_http = evhttp_new (ev_base);
88: if (evhttp_bind_socket (ev_http, argv[1], (u_short)atoi (argv[2]))) {
89: printf ("Failed to bind %s:%s\n", argv[1], argv[2]);
90: exit (1);
91: }
92: evhttp_set_cb (ev_http, "/img", http_img_cb, NULL);
93:
94: event_base_dispatch (ev_base);
95:
96: return 0;
97:}
Our Web server will take two parameters from the command line: the IP address and port that the server will listen on. Lines 80-83 check the number of parameters passed from the command line. Do not forget that the first element of the
argv [] array is always the name of the application itself.
Line 85 initializes the
libevent library, returns a pointer to the "
struct event_base " data structure. In all further calls to the library functions of the
libevent , this variable will have to be passed.
From 87 to 92 lines, an HTTP server is created, and a function is called that initializes the tapping on the address transmitted on the command line. If suddenly this address is already being listened by some other application or we do not have rights to occupy this address (forget that ports up to 1024 can only be occupied by an application running as root), the program displays a message on the screen and terminates. Next, we set the callback function for the "/ img" URI.
The entire library of
libevent is built on the basis of the so-called "callback" functions (callback functions), they work as follows: suppose we want that when an event occurs ("event" is English. "Event", hence the name of the library ) our function was called. To do this, we use the
libevent API, register our function for a specific event (this could be a timer event, socket readiness to receive data, request a URI, etc.). Later, upon the occurrence of this event, our function is called, which receives all the necessary parameters.
Line 94 starts the event loop. In our example, the cycle will be executed an infinite number of times, since we have not provided for the completion of the HTTP server. In order to exit this cycle and shut down the server, you will need to press CTRL + C in the console.
Now consider the processing of the "/ img" HTTP request:13:static void http_img_cb (struct evhttp_request *request, void *ctx)
14:{
15: struct evbuffer *evb;
16: int fd;
17: const char *fname;
18: struct stat stbuf;
19: int total_read_bytes, read_bytes;
20: struct evkeyvalq uri_params;
21:
22: evb = evbuffer_new ();
23:
24: printf ("Request from: %s:%d URI: %s\n", request->remote_host, request->remote_port, request->uri);
25:
26: evhttp_parse_query (request->uri, &uri_params);
27: fname = evhttp_find_header (&uri_params, "name");
28:
29: if (!fname) {
30: evbuffer_add_printf (evb, "Bad request");
31: evhttp_send_reply (request, HTTP_BADREQUEST, "Bad request", evb);
32: evhttp_clear_headers (&uri_params);
33: evbuffer_free (evb);
34: return;
35: }
Stork 13 declares the "callback" function
http_img_cb (). When the function is called, it, as an argument, is passed a pointer to a
struct evhttp_request structure, which contains all the necessary information about the HTTP request and a pointer to user data. In our example, the variable "
ctx " is not used.
Line 22 initializes a variable of type "
struct evbuffer *".
In
libevent, the “
struct evbuffer ” structure is the main type for working with Input / Output data (“I / O”). The API for working with “
struct evbuffer ” allows you to efficiently read, write and search data.
Line 26 calls the
libevent function “
evhttp_parse_query ()”, which takes the string URI and returns a list with the values “key” → “value” from the URI parameters. For example, if the request “http: // serverIP: port / img? Aa = bb & cc = dd”
occurred and called the function “
evhttp_parse_query (request-> uri, & uri_params) ”, then “uri_params” will contain “aa” → → pairs bb "," cc "→" dd ". The
libevent API contains several functions for working with the “
struct evkeyvalq ” type.
Line 27 calls one of these functions, which takes a pointer to a
struct evkeyvalq structure and a key string. The function returns the key value or "
NULL " if such a key is not found. In our case, we will accept the key "name" in the URI parameters, which will contain the name of the required file.
Lines 29-35 verify that the “name” key is specified in the URI parameters. If it is not specified, we, using the “
evhttp_send_reply ()” function,
send the response to the client with the HTTP code 400 and the message for the user “Bad request”. Next, we call the function “
evhttp_clear_headers ()” to clear the list “key” → “value” and the function “
evbuffer_free ()” to release the memory occupied by the structure “
struct evbuffer ” and exit the function.
The function “
evhttp_send_reply ()” is the main way of sending HTTP messages to clients, in parameters it accepts a pointer to the structure “
struct evhttp_request ”, HTTP code (there are several predefined constants with HTTP codes in
libevent ), a string with a short text message for the browser and a pointer to structure "
struct evbuffer " - this data will be directly displayed to the client.
37: if ((fd = open (fname, O_RDONLY)) < 0) {
38: evbuffer_add_printf (evb, " File %s not found", fname);
39: evhttp_send_reply (request, HTTP_NOTFOUND, "File not found", evb);
40: evhttp_clear_headers (&uri_params);
41: evbuffer_free (evb);
42: return;
43: }
44: if (fstat (fd, &stbuf) < 0) {
45: evbuffer_add_printf (evb, "File %s not found", fname);
46: evhttp_send_reply (request, HTTP_NOTFOUND, "File not found", evb);
47: evhttp_clear_headers (&uri_params);
48: evbuffer_free (evb);
49: close (fd);
50: return;
51: }
Lines 37-51 open the file for reading. The file name is taken from the parameter "name". Next, the function “
fstat ()” is called, which returns various system information about the file. If an error occurred in any of the functions, then we send the response to the client with the HTTP code 404 and the message “File not found” for the user, release the memory and exit the function.
53: total_read_bytes = 0;
54: while (total_read_bytes < stbuf.st_size) {
55: read_bytes = evbuffer_read (evb, fd, stbuf.st_size);
56: if (read_bytes < 0) {
57: evbuffer_add_printf (evb, "Error reading file %s", fname);
58: evhttp_send_reply (request, HTTP_NOTFOUND, "File not found", evb);
59: evhttp_clear_headers (&uri_params);
60: evbuffer_free (evb);
61: close (fd);
62: return;
63: }
64: total_read_bytes += read_bytes;
65: }
Lines 53-65 read data from an open file into a
struct evbuffer structure. The function “
evbuffer_read ()” takes in the parameters a pointer to the structure “
struct evbuffer ”, the descriptor of the open file, and how many bytes to read. The function returns the number of bytes read from the file. It is possible that the file is too large and cannot be considered as a single call of “
evbuffer_read ()”, then we cycle check the number of bytes already read with the file size obtained from the call of “
fstat ()” and, if necessary, repeat reading.
66: evhttp_add_header (request->output_headers, "Content-Type", "image/jpeg");
67: evhttp_send_reply (request, HTTP_OK, "HTTP_OK", evb);
68:
69: evhttp_clear_headers (&uri_params);
70: evbuffer_free (evb);
71: close (fd);
72:}
Lines 66-72 send the contents of the file to the client with the HTTP code 200. Pre-adding the header “Content-Type: image / jpeg” to the HTTP response, this is necessary so that the client's browser correctly displays the contents of the picture. After that, we release the memory, close the file and exit the function.
Build and test.Copy to the current directory a couple of jpeg images and remember the file names.
To compile this code, you need to install the
libevent version 1.4.XX library on your computer (replace XX with the latest available version). On some Linux distributions, you will need to install the “
libevent-dev ” package. Examples:
for Gentoo:
emerge libevent
for Ubuntu:
aptitude install libevent-dev
After the library is installed, save the program code to the main.c file and you can try to compile:
gcc main.c -o web_server -levent
If there were no errors, then the web_server file will appear in your current directory. Run it with the parameters IP address and port, for example:
./web_server 127.0.0.1 8090
Now, on your machine, open any browser and enter the following URL:
http://127.0.0.1:8090/img?name=_jpeg_
Should seem a picture. Hooray, you just created a web server!
A little about security.The web server in this form is not suitable for use on the Internet, since there is no check for which file is being accessed. Suppose you can send such a request: "
http://serverIP:port/img?name=/etc/passwd
http://serverIP:port/img?name=/etc/passwd
"and the file with the list of users of the system will be sent to the client. I deliberately omitted safety considerations, as this is the topic of a whole separate article. I can only advise that it is completely safe to run such a Web server in a “
chroot ” environment, after having compiled it with the “
-static ” flag.
What's next.Perhaps, I will soon write a sequel about the development of a simple Web-server, where I will show how you can cache image files. Just recently I started writing an article where I want to tell you how to organize the interaction between the server and the Flash client using the binary protocol and the
libevent API.
There are many different interesting things that can be done using the
libevent API. The main thing that distinguishes the development of programs using
libevent is the simplicity and at the same time the efficiency and speed of the execution of the programs obtained. Be sure to read the book
Learning Libevent . Successful coding!
PS Code light for some reason is not displayed; /