![]() |
![]() |
![]() |
libsoup Reference Manual | ![]() |
---|
Soup Server BasicsSoup Server Basics — Server-side tutorial |
As with the client API, there is a single object that will encapsulate most of your interactions with libsoup. In this case, SoupServer.
Note that SoupServer isn't as polished as SoupSession, and thus not as stable, and the APIs will likely change in incompatible (but not difficult-to-port-to) ways in the future to make things nicer. We apologize in advance for the inconvenience.
You create the server with soup_server_new
,
and as with the SoupSession constructor, you can specify
various additional options:
The TCP port to listen on. If |
|
A SoupAddress,
specifying the IP address of the network interface to run
the server on. If |
|
Points to a file containing an SSL certificate to use. If this is set, then the server will speak HTTPS; otherwise it will speak HTTP. |
|
Points to a file containing the private key for the
|
|
A GMainContext which the server will use for asynchronous operations. This can be set if you want to use a SoupServer in a thread other than the main thread. |
By default, SoupServer
returns "404 Not Found" in response to all requests (except ones that
it can't parse, which get "400 Bad Request"). To override this
behavior, call soup_server_add_handler
to set a callback to handle certain URI paths.
soup_server_add_handler (server, "/foo", NULL, server_callback, unregister_callback, data);
The "/foo"
indicates the base path for this
handler. When a request comes in, if there is a handler registered for
exactly the path in the request's Request-URI
, then
that handler will be called. Otherwise
libsoup will strip path components one by
one until it finds a matching handler. So for example, a request of
the form
"GET /foo/bar/baz.html?a=1&b=2 HTTP/1.1
"
would look for handlers for "/foo/bar/baz.html
",
"/foo/bar
", and "/foo
". If a
handler has been registered with a NULL
base path,
then it is used as the default handler for any request that doesn't
match any other handler.
A handler callback looks something like this:
static void server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data) { ... }
msg
is the request that has been received.
data
is the same data that was passed to soup_server_add_handler
.
The context argument contains some additional information
related to the request.
By default, libsoup assumes that you have
completely finished processing the message when you return from the
callback, and that it can therefore begin sending the response. If you
are not ready to send a response immediately (eg, you have to contact
another server, or wait for data from a database), you must call soup_message_io_pause
on the message before returning from the callback. This will delay
sending a response until you call soup_message_io_unpause
.
(You must also connect to the finished signal on the message
in this case, so that you can break off processing if the client
unexpectedly disconnects before you start sending the data.)
To set the response status, call soup_message_set_status
or soup_message_set_status_full
.
If the response requires a body, the callback must call soup_server_message_set_encoding
to indicate whether it will provide the response all at once with
Content-Length
encoding, or in pieces with
chunked
encoding.
Content-Length
EncodingThis is the simpler way to set a response body, if you have all of the data available at once.
static void server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data) { MyServerData *server_data = data; SoupUri *uri = soup_message_get_uri (msg); const char *mime_type; GByteArray *body; if (context->method_id != SOUP_METHOD_ID_GET) { soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); return; } body = g_hash_table_lookup (server_data->bodies, uri->path); mime_type = g_hash_table_lookup (server_data->mime_types, uri->path); if (!body || !mime_type) { soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); return; } soup_message_set_status (msg, SOUP_STATUS_OK); soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg), SOUP_TRANSFER_CONTENT_LENGTH); soup_message_set_response (msg, mime_type, SOUP_BUFFER_USER_OWNED, body->data, body->len); }
chunked
Encoding
If you want to supply the response body in chunks as it becomes
available, use chunked
encoding instead. In this
case, call soup_message_add_chunk
with
each chunk of the response body as it becomes available, and call
soup_message_add_final_chunk
when the response is complete. After each of these calls, you must
also call soup_message_io_unpause
to
cause the chunk to be sent. (You do not normally need to call
soup_message_io_pause
,
because I/O is automatically paused when doing a
chunked
transfer if no chunks are available.)
When using chunked encoding, you must also connect to the finished signal on the message, so that you will be notified if the client disconnects between two chunks; SoupServer will unref the message if that happens, so you must stop adding new chunks to the response at that point.
The simple-proxy
example in the tests/
directory gives an example of
using chunked
encoding.
To have SoupServer
handle HTTP authentication for you, pass a SoupAuthContext to soup_server_add_handler
:
SoupServerAuthContext auth_ctx; auth_ctx.types = SOUP_AUTH_TYPE_BASIC; auth_ctx.callback = auth_callback; auth_ctx.user_data = data; auth_ctx.basic_info.realm = "My Realm"; soup_server_add_handler (server, "/bar", &auth_ctx, server_callback, unregister_callback, data);
Then, every request that matches that handler will be passed to the
auth_callback
first before being passed to the
server_callback
:
static gboolean auth_callback (SoupServerAuthContext *auth_ctx, SoupServerAuth *auth, SoupMessage *msg, gpointer user_data) { MyServerData *server_data = user_data; const char *username, *password; if (!auth) return FALSE; username = soup_server_auth_get_user (auth); password = g_hash_table_lookup (server_data->passwords, username); if (!password) return FALSE; return soup_server_auth_check_passwd (auth, password); }
The auth
parameter indicates the authentication
information passed by the client. If no
WWW-Authenticate
header was present, this will be
NULL
, so we return FALSE
from
the callback to indicate that the server should return a 401
Unauthorized
response. If it is non-NULL
,
we extract the username from it, and compare it against our stored
password. Assuming it matches, we return TRUE
, and
the server callback is then invoked normally.