diff options
Diffstat (limited to 'libsoup/server')
-rw-r--r-- | libsoup/server/soup-auth-domain-basic.c | 27 | ||||
-rw-r--r-- | libsoup/server/soup-auth-domain-basic.h | 10 | ||||
-rw-r--r-- | libsoup/server/soup-auth-domain-digest.c | 31 | ||||
-rw-r--r-- | libsoup/server/soup-auth-domain-digest.h | 8 | ||||
-rw-r--r-- | libsoup/server/soup-auth-domain.c | 48 | ||||
-rw-r--r-- | libsoup/server/soup-auth-domain.h | 46 | ||||
-rw-r--r-- | libsoup/server/soup-server-io.c | 473 | ||||
-rw-r--r-- | libsoup/server/soup-server-message-private.h | 50 | ||||
-rw-r--r-- | libsoup/server/soup-server-message.c | 881 | ||||
-rw-r--r-- | libsoup/server/soup-server-message.h | 79 | ||||
-rw-r--r-- | libsoup/server/soup-server-private.h | 23 | ||||
-rw-r--r-- | libsoup/server/soup-server.c | 625 | ||||
-rw-r--r-- | libsoup/server/soup-server.h | 54 |
13 files changed, 1590 insertions, 765 deletions
diff --git a/libsoup/server/soup-auth-domain-basic.c b/libsoup/server/soup-auth-domain-basic.c index 9a850050..0cba37d2 100644 --- a/libsoup/server/soup-auth-domain-basic.c +++ b/libsoup/server/soup-auth-domain-basic.c @@ -204,8 +204,9 @@ pw_free (char *pw) } static gboolean -parse_basic (SoupMessage *msg, const char *header, - char **username, char **password) +parse_basic (const char *header, + char **username, + char **password) { char *decoded, *colon; gsize len, plen; @@ -232,15 +233,16 @@ parse_basic (SoupMessage *msg, const char *header, } static char * -soup_auth_domain_basic_accepts (SoupAuthDomain *domain, SoupMessage *msg, - const char *header) +soup_auth_domain_basic_accepts (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *header) { SoupAuthDomainBasicPrivate *priv = soup_auth_domain_basic_get_instance_private (SOUP_AUTH_DOMAIN_BASIC (domain)); char *username, *password; gboolean ok = FALSE; - if (!parse_basic (msg, header, &username, &password)) + if (!parse_basic (header, &username, &password)) return NULL; if (priv->auth_callback) { @@ -262,7 +264,8 @@ soup_auth_domain_basic_accepts (SoupAuthDomain *domain, SoupMessage *msg, } static char * -soup_auth_domain_basic_challenge (SoupAuthDomain *domain, SoupMessage *msg) +soup_auth_domain_basic_challenge (SoupAuthDomain *domain, + SoupServerMessage *msg) { GString *challenge; @@ -272,18 +275,18 @@ soup_auth_domain_basic_challenge (SoupAuthDomain *domain, SoupMessage *msg) } static gboolean -soup_auth_domain_basic_check_password (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username, - const char *password) +soup_auth_domain_basic_check_password (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + const char *password) { const char *header; char *msg_username, *msg_password; gboolean ok; - header = soup_message_headers_get_one (msg->request_headers, + header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg), "Authorization"); - if (!parse_basic (msg, header, &msg_username, &msg_password)) + if (!parse_basic (header, &msg_username, &msg_password)) return FALSE; ok = (!strcmp (username, msg_username) && diff --git a/libsoup/server/soup-auth-domain-basic.h b/libsoup/server/soup-auth-domain-basic.h index a260e891..d0397051 100644 --- a/libsoup/server/soup-auth-domain-basic.h +++ b/libsoup/server/soup-auth-domain-basic.h @@ -20,11 +20,11 @@ SOUP_AVAILABLE_IN_2_4 SoupAuthDomain *soup_auth_domain_basic_new (const char *optname1, ...) G_GNUC_NULL_TERMINATED; -typedef gboolean (*SoupAuthDomainBasicAuthCallback) (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username, - const char *password, - gpointer user_data); +typedef gboolean (*SoupAuthDomainBasicAuthCallback) (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + const char *password, + gpointer user_data); SOUP_AVAILABLE_IN_2_4 void soup_auth_domain_basic_set_auth_callback (SoupAuthDomain *domain, diff --git a/libsoup/server/soup-auth-domain-digest.c b/libsoup/server/soup-auth-domain-digest.c index ae9fa266..1ad34a91 100644 --- a/libsoup/server/soup-auth-domain-digest.c +++ b/libsoup/server/soup-auth-domain-digest.c @@ -191,9 +191,11 @@ soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain, } static gboolean -check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg, - GHashTable *params, const char *username, - const char *hex_urp) +check_hex_urp (SoupAuthDomain *domain, + SoupServerMessage *msg, + GHashTable *params, + const char *username, + const char *hex_urp) { const char *uri, *qop, *realm, *msg_username; const char *nonce, *nc, *cnonce, *response; @@ -210,7 +212,7 @@ check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg, if (!uri) return FALSE; - req_uri = soup_message_get_uri (msg); + req_uri = soup_server_message_get_uri (msg); dig_uri = soup_uri_new (uri); if (dig_uri) { if (!soup_uri_equal (dig_uri, req_uri)) { @@ -263,7 +265,8 @@ check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg, soup_auth_digest_compute_hex_a1 (hex_urp, SOUP_AUTH_DIGEST_ALGORITHM_MD5, nonce, cnonce, hex_a1); - soup_auth_digest_compute_response (msg->method, uri, + soup_auth_digest_compute_response (soup_server_message_get_method (msg), + uri, hex_a1, SOUP_AUTH_DIGEST_QOP_AUTH, nonce, cnonce, nonce_count, @@ -272,8 +275,9 @@ check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg, } static char * -soup_auth_domain_digest_accepts (SoupAuthDomain *domain, SoupMessage *msg, - const char *header) +soup_auth_domain_digest_accepts (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *header) { SoupAuthDomainDigestPrivate *priv = soup_auth_domain_digest_get_instance_private (SOUP_AUTH_DOMAIN_DIGEST (domain)); @@ -317,7 +321,8 @@ soup_auth_domain_digest_accepts (SoupAuthDomain *domain, SoupMessage *msg, } static char * -soup_auth_domain_digest_challenge (SoupAuthDomain *domain, SoupMessage *msg) +soup_auth_domain_digest_challenge (SoupAuthDomain *domain, + SoupServerMessage *msg) { GString *str; @@ -366,10 +371,10 @@ soup_auth_domain_digest_encode_password (const char *username, } static gboolean -soup_auth_domain_digest_check_password (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username, - const char *password) +soup_auth_domain_digest_check_password (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + const char *password) { const char *header; GHashTable *params; @@ -377,7 +382,7 @@ soup_auth_domain_digest_check_password (SoupAuthDomain *domain, char hex_urp[33]; gboolean accept; - header = soup_message_headers_get_one (msg->request_headers, + header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg), "Authorization"); if (!header || (strncmp (header, "Digest ", 7) != 0)) return FALSE; diff --git a/libsoup/server/soup-auth-domain-digest.h b/libsoup/server/soup-auth-domain-digest.h index 523497ce..c5630470 100644 --- a/libsoup/server/soup-auth-domain-digest.h +++ b/libsoup/server/soup-auth-domain-digest.h @@ -20,10 +20,10 @@ SOUP_AVAILABLE_IN_2_4 SoupAuthDomain *soup_auth_domain_digest_new (const char *optname1, ...) G_GNUC_NULL_TERMINATED; -typedef char * (*SoupAuthDomainDigestAuthCallback) (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username, - gpointer user_data); +typedef char * (*SoupAuthDomainDigestAuthCallback) (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + gpointer user_data); SOUP_AVAILABLE_IN_2_4 void soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain, diff --git a/libsoup/server/soup-auth-domain.c b/libsoup/server/soup-auth-domain.c index 8785c474..f22cd9ea 100644 --- a/libsoup/server/soup-auth-domain.c +++ b/libsoup/server/soup-auth-domain.c @@ -364,7 +364,7 @@ soup_auth_domain_remove_path (SoupAuthDomain *domain, const char *path) /** * SoupAuthDomainFilter: * @domain: a #SoupAuthDomain - * @msg: a #SoupMessage + * @msg: a #SoupServerMessage * @user_data: the data passed to soup_auth_domain_set_filter() * * The prototype for a #SoupAuthDomain filter; see @@ -444,7 +444,7 @@ soup_auth_domain_get_realm (SoupAuthDomain *domain) /** * SoupAuthDomainGenericAuthCallback: * @domain: a #SoupAuthDomain - * @msg: the #SoupMessage being authenticated + * @msg: the #SoupServerMessage being authenticated * @username: the username from @msg * @user_data: the data passed to * soup_auth_domain_set_generic_auth_callback() @@ -504,9 +504,9 @@ soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain, } gboolean -soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username) +soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username) { SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain); @@ -519,7 +519,7 @@ soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, /** * soup_auth_domain_check_password: * @domain: a #SoupAuthDomain - * @msg: a #SoupMessage + * @msg: a #SoupServerMessage * @username: a username * @password: a password * @@ -530,10 +530,10 @@ soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, * Return value: whether or not the message is authenticated **/ gboolean -soup_auth_domain_check_password (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username, - const char *password) +soup_auth_domain_check_password (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + const char *password) { return SOUP_AUTH_DOMAIN_GET_CLASS (domain)->check_password (domain, msg, username, @@ -543,7 +543,7 @@ soup_auth_domain_check_password (SoupAuthDomain *domain, /** * soup_auth_domain_covers: * @domain: a #SoupAuthDomain - * @msg: a #SoupMessage + * @msg: a #SoupServerMessage * * Checks if @domain requires @msg to be authenticated (according to * its paths and filter function). This does not actually look at @@ -556,13 +556,14 @@ soup_auth_domain_check_password (SoupAuthDomain *domain, * Return value: %TRUE if @domain requires @msg to be authenticated **/ gboolean -soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg) +soup_auth_domain_covers (SoupAuthDomain *domain, + SoupServerMessage *msg) { SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain); const char *path; if (!priv->proxy) { - path = soup_message_get_uri (msg)->path; + path = soup_server_message_get_uri (msg)->path; if (!soup_path_map_lookup (priv->paths, path)) return FALSE; } @@ -576,7 +577,7 @@ soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg) /** * soup_auth_domain_accepts: * @domain: a #SoupAuthDomain - * @msg: a #SoupMessage + * @msg: a #SoupServerMessage * * Checks if @msg contains appropriate authorization for @domain to * accept it. Mirroring soup_auth_domain_covers(), this does not check @@ -590,12 +591,13 @@ soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg) * as, if in fact it has authenticated. %NULL otherwise. **/ char * -soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg) +soup_auth_domain_accepts (SoupAuthDomain *domain, + SoupServerMessage *msg) { SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain); const char *header; - header = soup_message_headers_get_one (msg->request_headers, + header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg), priv->proxy ? "Proxy-Authorization" : "Authorization"); @@ -607,7 +609,7 @@ soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg) /** * soup_auth_domain_challenge: (virtual challenge) * @domain: a #SoupAuthDomain - * @msg: a #SoupMessage + * @msg: a #SoupServerMessage * * Adds a "WWW-Authenticate" or "Proxy-Authenticate" header to @msg, * requesting that the client authenticate, and sets @msg's status @@ -617,16 +619,18 @@ soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg) * anyone else. **/ void -soup_auth_domain_challenge (SoupAuthDomain *domain, SoupMessage *msg) +soup_auth_domain_challenge (SoupAuthDomain *domain, + SoupServerMessage *msg) { SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain); char *challenge; challenge = SOUP_AUTH_DOMAIN_GET_CLASS (domain)->challenge (domain, msg); - soup_message_set_status (msg, priv->proxy ? - SOUP_STATUS_PROXY_UNAUTHORIZED : - SOUP_STATUS_UNAUTHORIZED); - soup_message_headers_append (msg->response_headers, + soup_server_message_set_status (msg, priv->proxy ? + SOUP_STATUS_PROXY_UNAUTHORIZED : + SOUP_STATUS_UNAUTHORIZED, + NULL); + soup_message_headers_append (soup_server_message_get_response_headers (msg), priv->proxy ? "Proxy-Authenticate" : "WWW-Authenticate", diff --git a/libsoup/server/soup-auth-domain.h b/libsoup/server/soup-auth-domain.h index 397dc53b..c904d01b 100644 --- a/libsoup/server/soup-auth-domain.h +++ b/libsoup/server/soup-auth-domain.h @@ -16,15 +16,15 @@ G_DECLARE_DERIVABLE_TYPE (SoupAuthDomain, soup_auth_domain, SOUP, AUTH_DOMAIN, G struct _SoupAuthDomainClass { GObjectClass parent_class; - char * (*accepts) (SoupAuthDomain *domain, - SoupMessage *msg, - const char *header); - char * (*challenge) (SoupAuthDomain *domain, - SoupMessage *msg); - gboolean (*check_password) (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username, - const char *password); + char * (*accepts) (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *header); + char * (*challenge) (SoupAuthDomain *domain, + SoupServerMessage *msg); + gboolean (*check_password) (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + const char *password); gpointer padding[6]; }; @@ -37,14 +37,14 @@ struct _SoupAuthDomainClass { #define SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK "generic-auth-callback" #define SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA "generic-auth-data" -typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain *domain, - SoupMessage *msg, - gpointer user_data); +typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain *domain, + SoupServerMessage *msg, + gpointer user_data); -typedef gboolean (*SoupAuthDomainGenericAuthCallback) (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username, - gpointer user_data); +typedef gboolean (*SoupAuthDomainGenericAuthCallback) (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username, + gpointer user_data); SOUP_AVAILABLE_IN_2_4 void soup_auth_domain_add_path (SoupAuthDomain *domain, @@ -69,24 +69,24 @@ void soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain, GDestroyNotify dnotify); SOUP_AVAILABLE_IN_2_4 gboolean soup_auth_domain_check_password (SoupAuthDomain *domain, - SoupMessage *msg, + SoupServerMessage *msg, const char *username, const char *password); SOUP_AVAILABLE_IN_2_4 gboolean soup_auth_domain_covers (SoupAuthDomain *domain, - SoupMessage *msg); + SoupServerMessage *msg); SOUP_AVAILABLE_IN_2_4 char *soup_auth_domain_accepts (SoupAuthDomain *domain, - SoupMessage *msg); + SoupServerMessage *msg); SOUP_AVAILABLE_IN_2_4 void soup_auth_domain_challenge (SoupAuthDomain *domain, - SoupMessage *msg); + SoupServerMessage *msg); /* protected */ SOUP_AVAILABLE_IN_2_4 -gboolean soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, - SoupMessage *msg, - const char *username); +gboolean soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username); G_END_DECLS diff --git a/libsoup/server/soup-server-io.c b/libsoup/server/soup-server-io.c index 4556635f..e94a366e 100644 --- a/libsoup/server/soup-server-io.c +++ b/libsoup/server/soup-server-io.c @@ -15,34 +15,115 @@ #include "soup-body-input-stream.h" #include "soup-body-output-stream.h" #include "soup-filter-input-stream.h" -#include "soup-message-private.h" +#include "soup-server-message-private.h" #include "soup-misc.h" #include "soup-socket-private.h" +struct _SoupServerMessageIOData { + SoupMessageIOData base; + + GBytes *write_chunk; + goffset write_body_offset; + + GSource *unpause_source; +}; + #define RESPONSE_BLOCK_SIZE 8192 #define HEADER_SIZE_LIMIT (64 * 1024) +void +soup_server_message_io_data_free (SoupServerMessageIOData *io) +{ + if (!io) + return; + + soup_message_io_data_cleanup (&io->base); + + if (io->unpause_source) { + g_source_destroy (io->unpause_source); + g_source_unref (io->unpause_source); + io->unpause_source = NULL; + } + + g_clear_pointer (&io->write_chunk, g_bytes_unref); + + g_slice_free (SoupServerMessageIOData, io); +} + +void +soup_server_message_io_finished (SoupServerMessage *msg) +{ + SoupServerMessageIOData *io; + SoupMessageIOCompletionFn completion_cb; + gpointer completion_data; + SoupMessageIOCompletion completion; + + io = soup_server_message_get_io_data (msg); + if (!io) + return; + + completion_cb = io->base.completion_cb; + completion_data = io->base.completion_data; + + if ((io->base.read_state >= SOUP_MESSAGE_IO_STATE_FINISHING && + io->base.write_state >= SOUP_MESSAGE_IO_STATE_FINISHING)) + completion = SOUP_MESSAGE_IO_COMPLETE; + else + completion = SOUP_MESSAGE_IO_INTERRUPTED; + + g_object_ref (msg); + soup_server_message_set_io_data (msg, NULL); + if (completion_cb) + completion_cb (G_OBJECT (msg), completion, completion_data); + g_object_unref (msg); +} + +GIOStream * +soup_server_message_io_steal (SoupServerMessage *msg) +{ + SoupServerMessageIOData *io; + SoupMessageIOCompletionFn completion_cb; + gpointer completion_data; + GIOStream *iostream; + + io = soup_server_message_get_io_data (msg); + if (!io || !io->base.iostream) + return NULL; + + iostream = g_object_ref (io->base.iostream); + completion_cb = io->base.completion_cb; + completion_data = io->base.completion_data; + + g_object_ref (msg); + soup_server_message_set_io_data (msg, NULL); + if (completion_cb) + completion_cb (G_OBJECT (msg), SOUP_MESSAGE_IO_STOLEN, completion_data); + g_object_unref (msg); + + return iostream; +} + static void closed_async (GObject *source, GAsyncResult *result, gpointer user_data) { GOutputStream *body_ostream = G_OUTPUT_STREAM (source); - SoupMessage *msg = user_data; - SoupMessageIOData *io; + SoupServerMessage *msg = user_data; + SoupServerMessageIOData *io; GCancellable *async_wait; - io = soup_message_get_io_data (msg); - if (!io || !io->async_wait || io->body_ostream != body_ostream) { + io = soup_server_message_get_io_data (msg); + if (!io || !io->base.async_wait || io->base.body_ostream != body_ostream) { g_object_unref (msg); return; } - g_output_stream_close_finish (body_ostream, result, &io->async_error); - g_clear_object (&io->body_ostream); + g_output_stream_close_finish (body_ostream, result, &io->base.async_error); + g_clear_object (&io->base.body_ostream); - async_wait = io->async_wait; - io->async_wait = NULL; + async_wait = io->base.async_wait; + io->base.async_wait = NULL; g_cancellable_cancel (async_wait); g_object_unref (async_wait); @@ -76,12 +157,19 @@ closed_async (GObject *source, */ static void -handle_partial_get (SoupMessage *msg) +handle_partial_get (SoupServerMessage *msg) { SoupRange *ranges; int nranges; GBytes *full_response; guint status; + SoupMessageHeaders *request_headers; + SoupMessageHeaders *response_headers; + SoupMessageBody *response_body; + + request_headers = soup_server_message_get_request_headers (msg); + response_headers = soup_server_message_get_response_headers (msg); + response_body = soup_server_message_get_response_body (msg); /* Make sure the message is set up right for us to return a * partial response; it has to be a GET, the status must be @@ -89,50 +177,50 @@ handle_partial_get (SoupMessage *msg) * Content), and the SoupServer must have already filled in * the response body */ - if (msg->method != SOUP_METHOD_GET || - msg->status_code != SOUP_STATUS_OK || - soup_message_headers_get_encoding (msg->response_headers) != + if (soup_server_message_get_method (msg) != SOUP_METHOD_GET || + soup_server_message_get_status (msg, NULL) != SOUP_STATUS_OK || + soup_message_headers_get_encoding (response_headers) != SOUP_ENCODING_CONTENT_LENGTH || - msg->response_body->length == 0 || - !soup_message_body_get_accumulate (msg->response_body)) + response_body->length == 0 || + !soup_message_body_get_accumulate (response_body)) return; /* Oh, and there has to have been a valid Range header on the * request, of course. */ - status = soup_message_headers_get_ranges_internal (msg->request_headers, - msg->response_body->length, + status = soup_message_headers_get_ranges_internal (request_headers, + response_body->length, TRUE, &ranges, &nranges); if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) { - soup_message_set_status (msg, status); - soup_message_body_truncate (msg->response_body); + soup_server_message_set_status (msg, status, NULL); + soup_message_body_truncate (response_body); return; } else if (status != SOUP_STATUS_PARTIAL_CONTENT) return; - full_response = soup_message_body_flatten (msg->response_body); + full_response = soup_message_body_flatten (response_body); if (!full_response) { - soup_message_headers_free_ranges (msg->request_headers, ranges); + soup_message_headers_free_ranges (request_headers, ranges); return; } - soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT); - soup_message_body_truncate (msg->response_body); + soup_server_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT, NULL); + soup_message_body_truncate (response_body); if (nranges == 1) { GBytes *range_buf; /* Single range, so just set Content-Range and fix the body. */ - soup_message_headers_set_content_range (msg->response_headers, + soup_message_headers_set_content_range (response_headers, ranges[0].start, ranges[0].end, g_bytes_get_size (full_response)); range_buf = g_bytes_new_from_bytes (full_response, ranges[0].start, ranges[0].end - ranges[0].start + 1); - soup_message_body_append_bytes (msg->response_body, range_buf); + soup_message_body_append_bytes (response_body, range_buf); g_bytes_unref (range_buf); } else { SoupMultipart *multipart; @@ -147,7 +235,7 @@ handle_partial_get (SoupMessage *msg) */ multipart = soup_multipart_new ("multipart/byteranges"); - content_type = soup_message_headers_get_one (msg->response_headers, + content_type = soup_message_headers_get_one (response_headers, "Content-Type"); for (i = 0; i < nranges; i++) { part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); @@ -169,52 +257,62 @@ handle_partial_get (SoupMessage *msg) g_bytes_unref (part_body); } - soup_multipart_to_message (multipart, msg->response_headers, &body); - soup_message_body_append_bytes (msg->response_body, body); + soup_multipart_to_message (multipart, response_headers, &body); + soup_message_body_append_bytes (response_body, body); g_bytes_unref (body); soup_multipart_free (multipart); } g_bytes_unref (full_response); - soup_message_headers_free_ranges (msg->request_headers, ranges); + soup_message_headers_free_ranges (request_headers, ranges); } static void -write_headers (SoupMessage *msg, - GString *headers, - SoupEncoding *encoding) +write_headers (SoupServerMessage *msg, + GString *headers, + SoupEncoding *encoding) { SoupEncoding claimed_encoding; SoupMessageHeadersIter iter; const char *name, *value; + guint status_code; + const char *reason_phrase; + const char *method; + SoupMessageHeaders *response_headers; + SoupMessageBody *response_body; - if (msg->status_code == 0) - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + if (soup_server_message_get_status (msg, NULL) == 0) + soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); handle_partial_get (msg); + status_code = soup_server_message_get_status (msg, &reason_phrase); + g_string_append_printf (headers, "HTTP/1.%c %d %s\r\n", - soup_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1', - msg->status_code, msg->reason_phrase); - - claimed_encoding = soup_message_headers_get_encoding (msg->response_headers); - if ((msg->method == SOUP_METHOD_HEAD || - msg->status_code == SOUP_STATUS_NO_CONTENT || - msg->status_code == SOUP_STATUS_NOT_MODIFIED || - SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) || - (msg->method == SOUP_METHOD_CONNECT && - SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))) + soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1', + status_code, reason_phrase); + + method = soup_server_message_get_method (msg); + response_headers = soup_server_message_get_response_headers (msg); + claimed_encoding = soup_message_headers_get_encoding (response_headers); + if ((method == SOUP_METHOD_HEAD || + status_code == SOUP_STATUS_NO_CONTENT || + status_code == SOUP_STATUS_NOT_MODIFIED || + SOUP_STATUS_IS_INFORMATIONAL (status_code)) || + (method == SOUP_METHOD_CONNECT && + SOUP_STATUS_IS_SUCCESSFUL (status_code))) *encoding = SOUP_ENCODING_NONE; else *encoding = claimed_encoding; + response_body = soup_server_message_get_response_body (msg); if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH && - !soup_message_headers_get_content_length (msg->response_headers)) { - soup_message_headers_set_content_length (msg->response_headers, - msg->response_body->length); + !soup_message_headers_get_content_length (response_headers)) { + soup_message_headers_set_content_length (response_headers, + response_body->length); } - soup_message_headers_iter_init (&iter, msg->response_headers); + soup_message_headers_iter_init (&iter, response_headers); while (soup_message_headers_iter_next (&iter, &name, &value)) g_string_append_printf (headers, "%s: %s\r\n", name, value); g_string_append (headers, "\r\n"); @@ -227,13 +325,15 @@ write_headers (SoupMessage *msg, * socket not writable, write is complete, etc). */ static gboolean -io_write (SoupMessage *msg, - GCancellable *cancellable, - GError **error) +io_write (SoupServerMessage *msg, + GCancellable *cancellable, + GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); + SoupMessageIOData *io = &server_io->base; GBytes *chunk; gssize nwrote; + guint status_code; if (io->async_error) { g_propagate_error (error, io->async_error); @@ -248,11 +348,12 @@ io_write (SoupMessage *msg, switch (io->write_state) { case SOUP_MESSAGE_IO_STATE_HEADERS: - if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING && msg->status_code == 0) { + status_code = soup_server_message_get_status (msg, NULL); + if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING && status_code == 0) { /* Client requested "Expect: 100-continue", and * server did not set an error. */ - soup_message_set_status (msg, SOUP_STATUS_CONTINUE); + soup_server_message_set_status (msg, SOUP_STATUS_CONTINUE, NULL); } if (!io->write_buf->len) @@ -272,8 +373,9 @@ io_write (SoupMessage *msg, io->written = 0; g_string_truncate (io->write_buf, 0); - if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { - if (msg->status_code == SOUP_STATUS_CONTINUE) { + status_code = soup_server_message_get_status (msg, NULL); + if (SOUP_STATUS_IS_INFORMATIONAL (status_code)) { + if (status_code == SOUP_STATUS_CONTINUE) { /* Stop and wait for the body now */ io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING; @@ -288,20 +390,20 @@ io_write (SoupMessage *msg, */ } - soup_message_wrote_informational (msg); + soup_server_message_wrote_informational (msg); /* If this was "101 Switching Protocols", then * the server probably stole the connection... */ - if (io != soup_message_get_io_data (msg)) + if (server_io != soup_server_message_get_io_data (msg)) return FALSE; - soup_message_cleanup_response (msg); + soup_server_message_cleanup_response (msg); break; } if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) - io->write_length = soup_message_headers_get_content_length (msg->response_headers); + io->write_length = soup_message_headers_get_content_length (soup_server_message_get_response_headers (msg)); io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START; /* If the client was waiting for a Continue @@ -311,7 +413,7 @@ io_write (SoupMessage *msg, if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING) io->read_state = SOUP_MESSAGE_IO_STATE_DONE; - soup_message_wrote_headers (msg); + soup_server_message_wrote_headers (msg); break; case SOUP_MESSAGE_IO_STATE_BODY_START: @@ -329,51 +431,53 @@ io_write (SoupMessage *msg, break; } - if (!io->write_chunk) { - io->write_chunk = soup_message_body_get_chunk (msg->response_body, io->write_body_offset); - if (!io->write_chunk) { - soup_message_io_pause (msg); + if (!server_io->write_chunk) { + server_io->write_chunk = soup_message_body_get_chunk (soup_server_message_get_response_body (msg), + server_io->write_body_offset); + if (!server_io->write_chunk) { + soup_server_message_io_pause (msg); return FALSE; } - if (!g_bytes_get_size (io->write_chunk)) { + if (!g_bytes_get_size (server_io->write_chunk)) { io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; break; } } nwrote = g_pollable_stream_write (io->body_ostream, - (guchar*)g_bytes_get_data (io->write_chunk, NULL) + io->written, - g_bytes_get_size (io->write_chunk) - io->written, + (guchar*)g_bytes_get_data (server_io->write_chunk, NULL) + io->written, + g_bytes_get_size (server_io->write_chunk) - io->written, FALSE, cancellable, error); if (nwrote == -1) return FALSE; - chunk = g_bytes_new_from_bytes (io->write_chunk, io->written, nwrote); + chunk = g_bytes_new_from_bytes (server_io->write_chunk, io->written, nwrote); io->written += nwrote; if (io->write_length) io->write_length -= nwrote; - if (io->written == g_bytes_get_size (io->write_chunk)) + if (io->written == g_bytes_get_size (server_io->write_chunk)) io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DATA; - soup_message_wrote_body_data (msg, chunk); + soup_server_message_wrote_body_data (msg, g_bytes_get_size (chunk)); g_bytes_unref (chunk); break; case SOUP_MESSAGE_IO_STATE_BODY_DATA: io->written = 0; - if (g_bytes_get_size (io->write_chunk) == 0) { + if (g_bytes_get_size (server_io->write_chunk) == 0) { io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; break; } - soup_message_body_wrote_chunk (msg->response_body, io->write_chunk); - io->write_body_offset += g_bytes_get_size (io->write_chunk); - g_clear_pointer (&io->write_chunk, g_bytes_unref); + soup_message_body_wrote_chunk (soup_server_message_get_response_body (msg), + server_io->write_chunk); + server_io->write_body_offset += g_bytes_get_size (server_io->write_chunk); + g_clear_pointer (&server_io->write_chunk, g_bytes_unref); io->write_state = SOUP_MESSAGE_IO_STATE_BODY; - soup_message_wrote_chunk (msg); + soup_server_message_wrote_chunk (msg); break; case SOUP_MESSAGE_IO_STATE_BODY_FLUSH: @@ -397,7 +501,7 @@ io_write (SoupMessage *msg, case SOUP_MESSAGE_IO_STATE_BODY_DONE: io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; - soup_message_wrote_body (msg); + soup_server_message_wrote_body (msg); break; case SOUP_MESSAGE_IO_STATE_FINISHING: @@ -435,55 +539,52 @@ parse_connect_authority (const char *req_path) } static guint -parse_headers (SoupMessage *msg, - char *headers, - guint headers_len, - SoupEncoding *encoding, - SoupSocket *sock, - GError **error) +parse_headers (SoupServerMessage *msg, + char *headers, + guint headers_len, + SoupEncoding *encoding, + GError **error) { char *req_method, *req_path, *url; SoupHTTPVersion version; + SoupSocket *sock; const char *req_host; guint status; SoupURI *uri; + SoupMessageHeaders *request_headers; + + request_headers = soup_server_message_get_request_headers (msg); status = soup_headers_parse_request (headers, headers_len, - msg->request_headers, + request_headers, &req_method, &req_path, &version); - if (!SOUP_STATUS_IS_SUCCESSFUL (status)) { - if (status == SOUP_STATUS_MALFORMED) { - g_set_error_literal (error, SOUP_REQUEST_ERROR, - SOUP_REQUEST_ERROR_PARSING, - _("Could not parse HTTP request")); - } + if (!SOUP_STATUS_IS_SUCCESSFUL (status)) return status; - } - g_object_set (G_OBJECT (msg), - SOUP_MESSAGE_METHOD, req_method, - SOUP_MESSAGE_HTTP_VERSION, version, - NULL); + soup_server_message_set_method (msg, req_method); + soup_server_message_set_http_version (msg, version); g_free (req_method); /* Handle request body encoding */ - *encoding = soup_message_headers_get_encoding (msg->request_headers); + *encoding = soup_message_headers_get_encoding (request_headers); if (*encoding == SOUP_ENCODING_UNRECOGNIZED) { - if (soup_message_headers_get_list (msg->request_headers, "Transfer-Encoding")) + if (soup_message_headers_get_list (request_headers, "Transfer-Encoding")) return SOUP_STATUS_NOT_IMPLEMENTED; else return SOUP_STATUS_BAD_REQUEST; } /* Generate correct context for request */ - req_host = soup_message_headers_get_one (msg->request_headers, "Host"); + req_host = soup_message_headers_get_one (request_headers, "Host"); if (req_host && strchr (req_host, '/')) { g_free (req_path); return SOUP_STATUS_BAD_REQUEST; } + sock = soup_server_message_get_soup_socket (msg); + if (!strcmp (req_path, "*") && req_host) { /* Eg, "OPTIONS * HTTP/1.1" */ url = g_strdup_printf ("%s://%s", @@ -493,7 +594,7 @@ parse_headers (SoupMessage *msg, if (uri) soup_uri_set_path (uri, "*"); g_free (url); - } else if (msg->method == SOUP_METHOD_CONNECT) { + } else if (soup_server_message_get_method (msg) == SOUP_METHOD_CONNECT) { /* Authority */ uri = parse_connect_authority (req_path); } else if (*req_path != '/') { @@ -505,7 +606,7 @@ parse_headers (SoupMessage *msg, req_host, req_path); uri = soup_uri_new (url); g_free (url); - } else if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) { + } else if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) { /* No Host header, no AbsoluteUri */ GInetSocketAddress *addr = soup_socket_get_local_address (sock); GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); @@ -530,7 +631,7 @@ parse_headers (SoupMessage *msg, return SOUP_STATUS_BAD_REQUEST; } - soup_message_set_uri (msg, uri); + soup_server_message_set_uri (msg, uri); soup_uri_free (uri); return SOUP_STATUS_OK; @@ -543,27 +644,33 @@ parse_headers (SoupMessage *msg, * socket not readable, read is complete, etc). */ static gboolean -io_read (SoupMessage *msg, - GCancellable *cancellable, - GError **error) +io_read (SoupServerMessage *msg, + GCancellable *cancellable, + GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); + SoupMessageIOData *io = &server_io->base; gssize nread; guint status; + SoupMessageHeaders *request_headers; switch (io->read_state) { case SOUP_MESSAGE_IO_STATE_HEADERS: - if (!soup_message_io_read_headers (msg, io->istream, io->read_header_buf, FALSE, cancellable, error)) + if (!soup_message_io_data_read_headers (io, FALSE, cancellable, error)) { + if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT)) + soup_server_message_set_status (msg, SOUP_STATUS_MALFORMED, NULL); return FALSE; + } status = parse_headers (msg, (char *)io->read_header_buf->data, io->read_header_buf->len, &io->read_encoding, - io->sock, error); g_byte_array_set_size (io->read_header_buf, 0); + request_headers = soup_server_message_get_request_headers (msg); + if (status != SOUP_STATUS_OK) { /* Either we couldn't parse the headers, or they * indicated something that would mean we wouldn't @@ -572,14 +679,13 @@ io_read (SoupMessage *msg, * reading, and make sure the connection gets * closed when we're done. */ - soup_message_set_status (msg, status); - soup_message_headers_append (msg->request_headers, - "Connection", "close"); + soup_server_message_set_status (msg, status, NULL); + soup_message_headers_append (request_headers, "Connection", "close"); io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; break; } - if (soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) { + if (soup_message_headers_get_expectations (request_headers) & SOUP_EXPECTATION_CONTINUE) { /* We must return a status code and response * headers to the client; either an error to * be set by a got-headers handler below, or @@ -591,11 +697,11 @@ io_read (SoupMessage *msg, io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) - io->read_length = soup_message_headers_get_content_length (msg->request_headers); + io->read_length = soup_message_headers_get_content_length (request_headers); else io->read_length = -1; - soup_message_got_headers (msg); + soup_server_message_got_headers (msg); break; case SOUP_MESSAGE_IO_STATE_BODY_START: @@ -618,10 +724,13 @@ io_read (SoupMessage *msg, FALSE, cancellable, error); if (nread > 0) { - if (msg->request_body) { + SoupMessageBody *request_body; + + request_body = soup_server_message_get_request_body (msg); + if (request_body) { GBytes *bytes = g_bytes_new (buf, nread); - soup_message_body_got_chunk (msg->request_body, bytes); - soup_message_got_chunk (msg, bytes); + soup_message_body_got_chunk (request_body, bytes); + soup_server_message_got_chunk (msg, bytes); g_bytes_unref (bytes); } break; @@ -637,7 +746,7 @@ io_read (SoupMessage *msg, case SOUP_MESSAGE_IO_STATE_BODY_DONE: io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; - soup_message_got_body (msg); + soup_server_message_got_body (msg); break; case SOUP_MESSAGE_IO_STATE_FINISHING: @@ -653,13 +762,14 @@ io_read (SoupMessage *msg, } static gboolean -io_run_until (SoupMessage *msg, +io_run_until (SoupServerMessage *msg, SoupMessageIOState read_state, SoupMessageIOState write_state, GCancellable *cancellable, GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); + SoupMessageIOData *io = &server_io->base; gboolean progress = TRUE, done; GError *my_error = NULL; @@ -674,7 +784,7 @@ io_run_until (SoupMessage *msg, g_object_ref (msg); - while (progress && soup_message_get_io_data (msg) == io && !io->paused && !io->async_wait && + while (progress && soup_server_message_get_io_data (msg) == server_io && !io->paused && !io->async_wait && (io->read_state < read_state || io->write_state < write_state)) { if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state)) @@ -689,7 +799,7 @@ io_run_until (SoupMessage *msg, g_propagate_error (error, my_error); g_object_unref (msg); return FALSE; - } else if (soup_message_get_io_data (msg) != io) { + } else if (soup_server_message_get_io_data (msg) != server_io) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled")); @@ -716,22 +826,22 @@ io_run_until (SoupMessage *msg, return done; } -static void io_run (SoupMessage *msg); +static void io_run (SoupServerMessage *msg); static gboolean -io_run_ready (SoupMessage *msg, - gpointer user_data) +io_run_ready (SoupServerMessage *msg, + gpointer user_data) { io_run (msg); return FALSE; } static void -io_run (SoupMessage *msg) +io_run (SoupServerMessage *msg) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); + SoupMessageIOData *io = &server_io->base; GError *error = NULL; - GCancellable *cancellable; if (io->io_source) { g_source_destroy (io->io_source); @@ -740,58 +850,109 @@ io_run (SoupMessage *msg) } g_object_ref (msg); - cancellable = io->cancellable ? g_object_ref (io->cancellable) : NULL; - if (io_run_until (msg, SOUP_MESSAGE_IO_STATE_DONE, SOUP_MESSAGE_IO_STATE_DONE, - cancellable, &error)) { - soup_message_io_finished (msg); + NULL, &error)) { + soup_server_message_io_finished (msg); } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_clear_error (&error); - io->io_source = soup_message_io_get_source (msg, NULL, io_run_ready, msg); + io->io_source = soup_message_io_data_get_source (io, G_OBJECT (msg), NULL, + (SoupMessageIOSourceFunc)io_run_ready, + NULL); g_source_attach (io->io_source, io->async_context); } else { - if (soup_message_get_io_data (msg) == io) { - if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code) && + if (soup_server_message_get_io_data (msg) == server_io) { + if (!SOUP_STATUS_IS_TRANSPORT_ERROR (soup_server_message_get_status (msg, NULL)) && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - soup_message_set_status (msg, SOUP_STATUS_IO_ERROR); + soup_server_message_set_status (msg, SOUP_STATUS_IO_ERROR, NULL); } - soup_message_io_finished (msg); + soup_server_message_io_finished (msg); } g_error_free (error); } - g_object_unref (msg); - g_clear_object (&cancellable); } void -soup_message_read_request (SoupMessage *msg, - SoupSocket *sock, - SoupMessageCompletionFn completion_cb, - gpointer user_data) +soup_server_message_read_request (SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data) { - SoupMessageIOData *io; + SoupServerMessageIOData *io; + SoupSocket *sock; - io = g_slice_new0 (SoupMessageIOData); - io->completion_cb = completion_cb; - io->completion_data = user_data; + io = g_slice_new0 (SoupServerMessageIOData); + io->base.completion_cb = completion_cb; + io->base.completion_data = user_data; - io->sock = sock; - io->iostream = g_object_ref (soup_socket_get_iostream (io->sock)); - io->istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (io->iostream)); - io->ostream = g_io_stream_get_output_stream (io->iostream); - io->async_context = g_main_context_ref_thread_default (); + sock = soup_server_message_get_soup_socket (msg); + io->base.iostream = g_object_ref (soup_socket_get_iostream (sock)); + io->base.istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (io->base.iostream)); + io->base.ostream = g_io_stream_get_output_stream (io->base.iostream); + io->base.async_context = g_main_context_ref_thread_default (); - io->read_header_buf = g_byte_array_new (); - io->write_buf = g_string_new (NULL); + io->base.read_header_buf = g_byte_array_new (); + io->base.write_buf = g_string_new (NULL); - io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; - io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED; + io->base.read_state = SOUP_MESSAGE_IO_STATE_HEADERS; + io->base.write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED; - soup_message_set_io_data (msg, io); + soup_server_message_set_io_data (msg, io); io_run (msg); } + +void +soup_server_message_io_pause (SoupServerMessage *msg) +{ + SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); + + g_return_if_fail (io != NULL); + + if (io->unpause_source) { + g_source_destroy (io->unpause_source); + g_source_unref (io->unpause_source); + io->unpause_source = NULL; + } + + soup_message_io_data_pause (&io->base); +} + +static gboolean +io_unpause_internal (gpointer msg) +{ + SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); + + g_return_val_if_fail (io != NULL, FALSE); + + g_clear_pointer (&io->unpause_source, g_source_unref); + soup_message_io_data_unpause (&io->base); + if (io->base.io_source) + return FALSE; + + io_run (msg); + return FALSE; +} + +void +soup_server_message_io_unpause (SoupServerMessage *msg) +{ + SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); + + g_return_if_fail (io != NULL); + + if (!io->unpause_source) { + io->unpause_source = soup_add_completion_reffed (io->base.async_context, + io_unpause_internal, msg, NULL); + } +} + +gboolean +soup_server_message_is_io_paused (SoupServerMessage *msg) +{ + SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); + + return io && io->base.paused; +} diff --git a/libsoup/server/soup-server-message-private.h b/libsoup/server/soup-server-message-private.h new file mode 100644 index 00000000..057b058d --- /dev/null +++ b/libsoup/server/soup-server-message-private.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 Igalia S.L. + */ + +#ifndef __SOUP_SERVER_MESSAGE_PRIVATE_H__ +#define __SOUP_SERVER_MESSAGE_PRIVATE_H__ 1 + +#include "soup-server-message.h" +#include "soup-auth-domain.h" +#include "soup-message-io-data.h" +#include "soup-socket.h" + +SoupServerMessage *soup_server_message_new (SoupSocket *sock); +void soup_server_message_set_uri (SoupServerMessage *msg, + SoupURI *uri); +void soup_server_message_set_method (SoupServerMessage *msg, + const char *method); +SoupSocket *soup_server_message_get_soup_socket (SoupServerMessage *msg); +void soup_server_message_set_auth (SoupServerMessage *msg, + SoupAuthDomain *domain, + char *user); +gboolean soup_server_message_is_keepalive (SoupServerMessage *msg); +GIOStream *soup_server_message_io_steal (SoupServerMessage *msg); +void soup_server_message_io_pause (SoupServerMessage *msg); +void soup_server_message_io_unpause (SoupServerMessage *msg); +gboolean soup_server_message_is_io_paused (SoupServerMessage *msg); +void soup_server_message_io_finished (SoupServerMessage *msg); +void soup_server_message_cleanup_response (SoupServerMessage *msg); +void soup_server_message_wrote_informational (SoupServerMessage *msg); +void soup_server_message_wrote_headers (SoupServerMessage *msg); +void soup_server_message_wrote_chunk (SoupServerMessage *msg); +void soup_server_message_wrote_body_data (SoupServerMessage *msg, + gsize chunk_size); +void soup_server_message_wrote_body (SoupServerMessage *msg); +void soup_server_message_got_headers (SoupServerMessage *msg); +void soup_server_message_got_chunk (SoupServerMessage *msg, + GBytes *chunk); +void soup_server_message_got_body (SoupServerMessage *msg); +void soup_server_message_finished (SoupServerMessage *msg); +void soup_server_message_read_request (SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data); + +typedef struct _SoupServerMessageIOData SoupServerMessageIOData; +void soup_server_message_io_data_free (SoupServerMessageIOData *io); +void soup_server_message_set_io_data (SoupServerMessage *msg, + SoupServerMessageIOData *io); +SoupServerMessageIOData *soup_server_message_get_io_data (SoupServerMessage *msg); + +#endif /* __SOUP_SERVER_MESSAGE_PRIVATE_H__ */ diff --git a/libsoup/server/soup-server-message.c b/libsoup/server/soup-server-message.c new file mode 100644 index 00000000..1f2e8515 --- /dev/null +++ b/libsoup/server/soup-server-message.c @@ -0,0 +1,881 @@ +/* + * soup-server-message.c: HTTP server request/response + * + * Copyright (C) 2020 Igalia S.L. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "soup-server-message.h" +#include "soup.h" +#include "soup-connection.h" +#include "soup-server-message-private.h" +#include "soup-socket-private.h" + +/** + * SECTION:soup-server-message + * @short_description: An HTTP server request and response. + * @see_also: #SoupMessageHeaders, #SoupMessageBody + * + * A SoupServerMessage represents an HTTP message that is being sent or + * received on a #SoupServer + * + * #SoupServer will create #SoupServerMessage<!-- -->s automatically for + * incoming requests, which your application will receive via handlers. + * + * Note that libsoup's terminology here does not quite match the HTTP + * specification: in RFC 2616, an "HTTP-message" is + * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a + * Response. In libsoup, a #SoupServerMessage combines both the request and + * the response. + **/ + +struct _SoupServerMessage { + GObject parent; + + SoupSocket *sock; + GSocket *gsock; + SoupAuthDomain *auth_domain; + char *auth_user; + + GSocketAddress *remote_addr; + char *remote_ip; + GSocketAddress *local_addr; + + const char *method; + SoupHTTPVersion http_version; + SoupHTTPVersion orig_http_version; + + guint status_code; + char *reason_phrase; + + SoupURI *uri; + + SoupMessageBody *request_body; + SoupMessageHeaders *request_headers; + + SoupMessageBody *response_body; + SoupMessageHeaders *response_headers; + + SoupServerMessageIOData *io_data; +}; + +struct _SoupServerMessageClass { + GObjectClass parent_class; +}; + +G_DEFINE_TYPE (SoupServerMessage, soup_server_message, G_TYPE_OBJECT) + +enum { + WROTE_INFORMATIONAL, + WROTE_HEADERS, + WROTE_CHUNK, + WROTE_BODY_DATA, + WROTE_BODY, + + GOT_HEADERS, + GOT_CHUNK, + GOT_BODY, + + DISCONNECTED, + FINISHED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +soup_server_message_init (SoupServerMessage *msg) +{ + msg->request_body = soup_message_body_new (); + msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST); + msg->response_body = soup_message_body_new (); + msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); + soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CONTENT_LENGTH); +} + +static void +soup_server_message_finalize (GObject *object) +{ + SoupServerMessage *msg = SOUP_SERVER_MESSAGE (object); + + soup_server_message_io_data_free (msg->io_data); + + g_clear_object (&msg->auth_domain); + g_clear_pointer (&msg->auth_user, g_free); + g_clear_object (&msg->remote_addr); + g_clear_object (&msg->local_addr); + + if (msg->sock) { + g_signal_handlers_disconnect_by_data (msg->sock, msg); + g_object_unref (msg->sock); + } + g_clear_object (&msg->gsock); + g_clear_pointer (&msg->remote_ip, g_free); + + g_clear_pointer (&msg->uri, soup_uri_free); + g_free (msg->reason_phrase); + + soup_message_body_free (msg->request_body); + soup_message_headers_free (msg->request_headers); + soup_message_body_free (msg->response_body); + soup_message_headers_free (msg->response_headers); + + G_OBJECT_CLASS (soup_server_message_parent_class)->finalize (object); +} + +static void +soup_server_message_class_init (SoupServerMessageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = soup_server_message_finalize; + + /** + * SoupServerMessage::wrote-informational: + * @msg: the message + * + * Emitted immediately after writing a 1xx (Informational) response. + */ + signals[WROTE_INFORMATIONAL] = + g_signal_new ("wrote-informational", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerMessage::wrote-headers: + * @msg: the message + * + * Emitted immediately after writing the response headers for a + * message. + */ + signals[WROTE_HEADERS] = + g_signal_new ("wrote-headers", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerMessage::wrote-chunk: + * @msg: the message + * + * Emitted immediately after writing a body chunk for a message. + * + * Note that this signal is not parallel to + * #SoupServerMessage::got-chunk; it is emitted only when a complete + * chunk (added with soup_message_body_append() or + * soup_message_body_append_bytes()) has been written. To get + * more useful continuous progress information, use + * #SoupServerMessage::wrote-body-data. + */ + signals[WROTE_CHUNK] = + g_signal_new ("wrote-chunk", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerMessage::wrote-body-data: + * @msg: the message + * @chunk_size: the number of bytes written + * + * Emitted immediately after writing a portion of the message + * body to the network. + */ + signals[WROTE_BODY_DATA] = + g_signal_new ("wrote-body-data", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + /** + * SoupServerMessage::wrote-body: + * @msg: the message + * + * Emitted immediately after writing the complete response body for a + * message. + */ + signals[WROTE_BODY] = + g_signal_new ("wrote-body", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerMessage::got-headers: + * @msg: the message + * + * Emitted after receiving the Request-Line and request headers. + */ + signals[GOT_HEADERS] = + g_signal_new ("got-headers", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerMessage::got-chunk: + * @msg: the message + * @chunk: the just-read chunk + * + * Emitted after receiving a chunk of a message body. Note + * that "chunk" in this context means any subpiece of the + * body, not necessarily the specific HTTP 1.1 chunks sent by + * the other side. + */ + signals[GOT_CHUNK] = + g_signal_new ("got-chunk", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + G_TYPE_BYTES); + + /** + * SoupServerMessage::got-body: + * @msg: the message + * + * Emitted after receiving the complete request body. + */ + signals[GOT_BODY] = + g_signal_new ("got-body", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerMessage::finished: + * @msg: the message + * + * Emitted when all HTTP processing is finished for a message. + * (After #SoupServerMessage::wrote-body). + */ + signals[FINISHED] = + g_signal_new ("finished", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerMessage::disconnected: + * @msg: the message + * + * Emitted when the @msg's socket is disconnected. + */ + signals[DISCONNECTED] = + g_signal_new ("disconnected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); +} + +static void +socket_disconnected (SoupServerMessage *msg) +{ + g_signal_emit (msg, signals[DISCONNECTED], 0); +} + +SoupServerMessage * +soup_server_message_new (SoupSocket *sock) +{ + SoupServerMessage *msg; + + msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL); + msg->sock = g_object_ref (sock); + msg->gsock = soup_socket_get_gsocket (sock); + if (msg->gsock) + g_object_ref (msg->gsock); + + g_signal_connect_object (sock, "disconnected", + G_CALLBACK (socket_disconnected), + msg, G_CONNECT_SWAPPED); + + return msg; +} + +void +soup_server_message_set_uri (SoupServerMessage *msg, + SoupURI *uri) +{ + if (msg->uri) + soup_uri_free (msg->uri); + msg->uri = soup_uri_copy (uri); +} + +SoupSocket * +soup_server_message_get_soup_socket (SoupServerMessage *msg) +{ + return msg->sock; +} + +void +soup_server_message_set_auth (SoupServerMessage *msg, + SoupAuthDomain *domain, + char *user) +{ + if (msg->auth_domain) + g_object_unref (msg->auth_domain); + msg->auth_domain = domain; + + if (msg->auth_user) + g_free (msg->auth_user); + msg->auth_user = user; +} + +gboolean +soup_server_message_is_keepalive (SoupServerMessage *msg) +{ + if (msg->status_code == SOUP_STATUS_OK && msg->method == SOUP_METHOD_CONNECT) + return TRUE; + + /* Not persistent if the server sent a terminate-by-EOF response */ + if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF) + return FALSE; + + if (msg->http_version == SOUP_HTTP_1_0) { + /* In theory, HTTP/1.0 connections are only persistent + * if the client requests it, and the server agrees. + * But some servers do keep-alive even if the client + * doesn't request it. So ignore c_conn. + */ + + if (!soup_message_headers_header_contains (msg->response_headers, + "Connection", "Keep-Alive")) + return FALSE; + } else { + /* Normally persistent unless either side requested otherwise */ + if (soup_message_headers_header_contains (msg->request_headers, + "Connection", "close") || + soup_message_headers_header_contains (msg->response_headers, + "Connection", "close")) + return FALSE; + + return TRUE; + } + + return TRUE; +} + +void +soup_server_message_set_io_data (SoupServerMessage *msg, + SoupServerMessageIOData *io) +{ + soup_server_message_io_data_free (msg->io_data); + msg->io_data = io; +} + +SoupServerMessageIOData * +soup_server_message_get_io_data (SoupServerMessage *msg) +{ + return msg->io_data; +} + +void +soup_server_message_cleanup_response (SoupServerMessage *msg) +{ + soup_message_body_truncate (msg->response_body); + soup_message_headers_clear (msg->response_headers); + soup_message_headers_set_encoding (msg->response_headers, + SOUP_ENCODING_CONTENT_LENGTH); + msg->status_code = SOUP_STATUS_NONE; + g_clear_pointer (&msg->reason_phrase, g_free); + msg->http_version = msg->orig_http_version; +} + +void +soup_server_message_wrote_informational (SoupServerMessage *msg) +{ + g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0); +} + +void +soup_server_message_wrote_headers (SoupServerMessage *msg) +{ + g_signal_emit (msg, signals[WROTE_HEADERS], 0); +} + +void +soup_server_message_wrote_chunk (SoupServerMessage *msg) +{ + g_signal_emit (msg, signals[WROTE_CHUNK], 0); +} + +void +soup_server_message_wrote_body_data (SoupServerMessage *msg, + gsize chunk_size) +{ + g_signal_emit (msg, signals[WROTE_BODY_DATA], 0, chunk_size); +} + +void +soup_server_message_wrote_body (SoupServerMessage *msg) +{ + g_signal_emit (msg, signals[WROTE_BODY], 0); +} + +void +soup_server_message_got_headers (SoupServerMessage *msg) +{ + g_signal_emit (msg, signals[GOT_HEADERS], 0); +} + +void +soup_server_message_got_chunk (SoupServerMessage *msg, + GBytes *chunk) +{ + g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk); +} + +void +soup_server_message_got_body (SoupServerMessage *msg) +{ + if (soup_message_body_get_accumulate (msg->request_body)) + g_bytes_unref (soup_message_body_flatten (msg->request_body)); + g_signal_emit (msg, signals[GOT_BODY], 0); +} + +void +soup_server_message_finished (SoupServerMessage *msg) +{ + g_signal_emit (msg, signals[FINISHED], 0); +} + +/** + * soup_server_message_get_request_headers: + * @msg: a #SoupServerMessage + * + * Get the request headers of @msg. + * + * Returns: (transfer none): a #SoupMessageHeaders with the request headers. + */ +SoupMessageHeaders * +soup_server_message_get_request_headers (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->request_headers; +} + +/** + * soup_server_message_get_response_headers: + * @msg: a #SoupServerMessage + * + * Get the response headers of @msg. + * + * Returns: (transfer none): a #SoupMessageHeaders with the response headers. + */ +SoupMessageHeaders * +soup_server_message_get_response_headers (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->response_headers; +} + +/** + * soup_server_message_get_request_body: + * @msg: a #SoupServerMessage + * + * Get the request body of @msg. + * + * Returns: (transfer none): a #SoupMessageBody. + */ +SoupMessageBody * +soup_server_message_get_request_body (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->request_body; +} + +/** + * soup_server_message_get_response_body: + * @msg: a #SoupServerMessage + * + * Get the response body of @msg. + * + * Returns: (transfer none): a #SoupMessageBody. + */ +SoupMessageBody * +soup_server_message_get_response_body (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->response_body; +} + +/** + * soup_server_message_get_method: + * @msg: a #SoupServerMessage + * + * Get the HTTP method of @msg. + * + * Returns: the HTTP method. + */ +const char * +soup_server_message_get_method (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->method; +} + +void +soup_server_message_set_method (SoupServerMessage *msg, + const char *method) +{ + msg->method = g_intern_string (method); +} + +/** + * soup_server_message_get_http_version: + * @msg: a #SoupServerMessage + * + * Get the HTTP version of @msg. + * + * Returns: a #SoupHTTPVersion. + */ +SoupHTTPVersion +soup_server_message_get_http_version (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), SOUP_HTTP_1_1); + + return msg->http_version; +} + +/** + * soup_server_message_set_http_version: + * @msg: a #SoupServerMessage + * @version: a #SoupHTTPVersion + * + * Set the HTTP version of @msg. + */ +void +soup_server_message_set_http_version (SoupServerMessage *msg, + SoupHTTPVersion version) +{ + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); + + msg->http_version = version; + if (msg->status_code == SOUP_STATUS_NONE) + msg->orig_http_version = version; +} + +/** + * soup_server_message_get_status: + * @msg: a #SoupServerMessage + * @reason_phrase: (out) (nullable) (transfer none): a location to store the reason phrase or %NULL + * + * Get the HTTP status code of @msg and optionally the reason phrase if @reason_phrase is not %NULL. + * + * Returns: the HTTP status code. + */ +guint +soup_server_message_get_status (SoupServerMessage *msg, + const char **reason_phrase) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), 0); + + if (reason_phrase) + *reason_phrase = msg->reason_phrase; + + return msg->status_code; +} + +/** + * soup_server_message_set_status: + * @msg: a #SoupServerMessage + * @status_code: an HTTP status code + * @reason_phrase: (nullable): a reason phrase + * + * Sets @msg's status code to @status_code. If @status_code is a + * known value and @reason_phrase is %NULL, the reason_phrase will + * be set automatically. + **/ +void +soup_server_message_set_status (SoupServerMessage *msg, + guint status_code, + const char *reason_phrase) +{ + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); + g_return_if_fail (status_code != 0); + + g_free (msg->reason_phrase); + + msg->status_code = status_code; + msg->reason_phrase = g_strdup (reason_phrase ? reason_phrase : soup_status_get_phrase (status_code)); +} + +/** + * soup_server_message_get_uri: + * @msg: a #SoupServerMessage + * + * Get @msg's URI. + * + * Returns: (transfer none): a #SoupURI + */ +SoupURI * +soup_server_message_get_uri (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->uri; +} + +/** + * soup_server_message_set_response: + * @msg: the message + * @content_type: (allow-none): MIME Content-Type of the body + * @resp_use: a #SoupMemoryUse describing how to handle @resp_body + * @resp_body: (allow-none) (array length=resp_length) (element-type guint8): + * a data buffer containing the body of the message response. + * @resp_length: the byte length of @resp_body. + * + * Convenience function to set the response body of a #SoupServerMessage. If + * @content_type is %NULL, the response body must be empty as well. + */ +void +soup_server_message_set_response (SoupServerMessage *msg, + const char *content_type, + SoupMemoryUse resp_use, + const char *resp_body, + gsize resp_length) +{ + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); + g_return_if_fail (content_type != NULL || resp_length == 0); + + if (content_type) { + g_warn_if_fail (strchr (content_type, '/') != NULL); + + soup_message_headers_replace (msg->response_headers, + "Content-Type", content_type); + soup_message_body_append (msg->response_body, resp_use, + resp_body, resp_length); + } else { + soup_message_headers_remove (msg->response_headers, + "Content-Type"); + soup_message_body_truncate (msg->response_body); + } +} + +/** + * soup_server_message_set_redirect: + * @msg: a #SoupServerMessage + * @status_code: a 3xx status code + * @redirect_uri: the URI to redirect @msg to + * + * Sets @msg's status_code to @status_code and adds a Location header + * pointing to @redirect_uri. Use this from a #SoupServer when you + * want to redirect the client to another URI. + * + * @redirect_uri can be a relative URI, in which case it is + * interpreted relative to @msg's current URI. In particular, if + * @redirect_uri is just a path, it will replace the path + * <emphasis>and query</emphasis> of @msg's URI. + */ +void +soup_server_message_set_redirect (SoupServerMessage *msg, + guint status_code, + const char *redirect_uri) +{ + SoupURI *location; + char *location_str; + + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); + + location = soup_uri_new_with_base (soup_server_message_get_uri (msg), redirect_uri); + g_return_if_fail (location != NULL); + + soup_server_message_set_status (msg, status_code, NULL); + location_str = soup_uri_to_string (location, FALSE); + soup_message_headers_replace (msg->response_headers, "Location", + location_str); + g_free (location_str); + soup_uri_free (location); +} + +/** + * soup_server_message_get_socket: + * @msg: a #SoupServerMessage + * + * Retrieves the #GSocket that @msg is associated with. + * + * If you are using this method to observe when multiple requests are + * made on the same persistent HTTP connection (eg, as the ntlm-test + * test program does), you will need to pay attention to socket + * destruction as well (eg, by using weak references), so that you do + * not get fooled when the allocator reuses the memory address of a + * previously-destroyed socket to represent a new socket. + * + * Return value: (nullable) (transfer none): the #GSocket that @msg is + * associated with, %NULL if you used soup_server_accept_iostream(). + */ +GSocket * +soup_server_message_get_socket (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->gsock; +} + +/** + * soup_server_message_get_remote_address: + * @msg: a #SoupServerMessage + * + * Retrieves the #GSocketAddress associated with the remote end + * of a connection. + * + * Return value: (nullable) (transfer none): the #GSocketAddress + * associated with the remote end of a connection, it may be + * %NULL if you used soup_server_accept_iostream(). + */ +GSocketAddress * +soup_server_message_get_remote_address (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + if (msg->remote_addr) + return msg->remote_addr; + + msg->remote_addr = msg->gsock ? + g_socket_get_remote_address (msg->gsock, NULL) : + G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_remote_address (msg->sock))); + + return msg->remote_addr; +} + +/** + * soup_server_message_get_local_address: + * @msg: a #SoupServerMessage + * + * Retrieves the #GSocketAddress associated with the local end + * of a connection. + * + * Return value: (nullable) (transfer none): the #GSocketAddress + * associated with the local end of a connection, it may be + * %NULL if you used soup_server_accept_iostream(). + */ +GSocketAddress * +soup_server_message_get_local_address (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + if (msg->local_addr) + return msg->local_addr; + + msg->local_addr = msg->gsock ? + g_socket_get_local_address (msg->gsock, NULL) : + G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_local_address (msg->sock))); + + return msg->local_addr; +} + +/** + * soup_server_message_get_remote_host: + * @msg: a #SoupServerMessage + * + * Retrieves the IP address associated with the remote end of a + * connection. + * + * Return value: (nullable): the IP address associated with the remote + * end of a connection, it may be %NULL if you used + * soup_server_accept_iostream(). + */ +const char * +soup_server_message_get_remote_host (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + if (msg->remote_ip) + return msg->remote_ip; + + if (msg->gsock) { + GSocketAddress *addr = soup_server_message_get_remote_address (msg); + GInetAddress *iaddr; + + if (!addr || !G_IS_INET_SOCKET_ADDRESS (addr)) + return NULL; + iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); + msg->remote_ip = g_inet_address_to_string (iaddr); + } else { + GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_socket_get_remote_address (msg->sock)); + GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); + msg->remote_ip = g_inet_address_to_string (inet_addr); + } + + return msg->remote_ip; +} + +/** + * soup_server_message_steal_connection: + * @msg: a #SoupServerMessage + * + * "Steals" the HTTP connection associated with @msg from its + * #SoupServer. This happens immediately, regardless of the current + * state of the connection; if the response to @msg has not yet finished + * being sent, then it will be discarded; you can steal the connection from a + * #SoupServerMessage:wrote-informational or #SoupServerMessage:wrote-body signal + * handler if you need to wait for part or all of the response to be sent. + * + * Note that when calling this function from C, @msg will most + * likely be freed as a side effect. + * + * Return value: (transfer full): the #GIOStream formerly associated + * with @msg (or %NULL if @msg was no longer associated with a + * connection). No guarantees are made about what kind of #GIOStream + * is returned. + */ +GIOStream * +soup_server_message_steal_connection (SoupServerMessage *msg) +{ + GIOStream *stream; + + g_object_ref (msg); + stream = soup_server_message_io_steal (msg); + if (stream) { + g_object_set_data_full (G_OBJECT (stream), "GSocket", + soup_socket_steal_gsocket (msg->sock), + g_object_unref); + } + + socket_disconnected (msg); + g_object_unref (msg); + + return stream; +} diff --git a/libsoup/server/soup-server-message.h b/libsoup/server/soup-server-message.h new file mode 100644 index 00000000..5102648e --- /dev/null +++ b/libsoup/server/soup-server-message.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 Igalia S.L. + */ + +#ifndef __SOUP_SERVER_MESSAGE_H__ +#define __SOUP_SERVER_MESSAGE_H__ 1 + +#include "soup-types.h" +#include "soup-message-body.h" +#include "soup-message-headers.h" +#include "soup-method.h" + +G_BEGIN_DECLS + +#define SOUP_TYPE_SERVER_MESSAGE (soup_server_message_get_type ()) +SOUP_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SoupServerMessage, soup_server_message, SOUP, SERVER_MESSAGE, GObject) + +SOUP_AVAILABLE_IN_ALL +SoupMessageHeaders *soup_server_message_get_request_headers (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +SoupMessageHeaders *soup_server_message_get_response_headers (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +SoupMessageBody *soup_server_message_get_request_body (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +SoupMessageBody *soup_server_message_get_response_body (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +const char *soup_server_message_get_method (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +SoupHTTPVersion soup_server_message_get_http_version (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +void soup_server_message_set_http_version (SoupServerMessage *msg, + SoupHTTPVersion version); + +SOUP_AVAILABLE_IN_ALL +guint soup_server_message_get_status (SoupServerMessage *msg, + const char **reason_phrase); +SOUP_AVAILABLE_IN_ALL +void soup_server_message_set_status (SoupServerMessage *msg, + guint status_code, + const char *reason_phrase); +SOUP_AVAILABLE_IN_ALL +SoupURI *soup_server_message_get_uri (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +void soup_server_message_set_response (SoupServerMessage *msg, + const char *content_type, + SoupMemoryUse resp_use, + const char *resp_body, + gsize resp_length); +SOUP_AVAILABLE_IN_ALL +void soup_server_message_set_redirect (SoupServerMessage *msg, + guint status_code, + const char *redirect_uri); + +SOUP_AVAILABLE_IN_ALL +GSocket *soup_server_message_get_socket (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +GSocketAddress *soup_server_message_get_local_address (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +GSocketAddress *soup_server_message_get_remote_address (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +const char *soup_server_message_get_remote_host (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_ALL +GIOStream *soup_server_message_steal_connection (SoupServerMessage *msg); + +G_END_DECLS + +#endif /* __SOUP_SERVER_MESSAGE_H__ */ diff --git a/libsoup/server/soup-server-private.h b/libsoup/server/soup-server-private.h deleted file mode 100644 index 15430e73..00000000 --- a/libsoup/server/soup-server-private.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2020 Igalia S.L. - * - * This file is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#pragma once - -#include "soup-server.h" - -SoupSocket *soup_client_context_get_soup_socket (SoupClientContext *client); diff --git a/libsoup/server/soup-server.c b/libsoup/server/soup-server.c index aa5f2203..2802f66a 100644 --- a/libsoup/server/soup-server.c +++ b/libsoup/server/soup-server.c @@ -13,9 +13,10 @@ #include <glib/gi18n-lib.h> -#include "soup-server-private.h" +#include "soup-server.h" +#include "soup-server-message-private.h" #include "soup.h" -#include "soup-message-private.h" +#include "soup-server-message-private.h" #include "soup-misc.h" #include "soup-path-map.h" #include "soup-socket-private.h" @@ -129,21 +130,6 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -struct _SoupClientContext { - SoupServer *server; - SoupSocket *sock; - GSocket *gsock; - SoupMessage *msg; - SoupAuthDomain *auth_domain; - char *auth_user; - - GSocketAddress *remote_addr; - char *remote_ip; - GSocketAddress *local_addr; - - int ref_count; -}; - typedef struct { char *path; @@ -207,9 +193,8 @@ enum { G_DEFINE_TYPE_WITH_PRIVATE (SoupServer, soup_server, G_TYPE_OBJECT) -static SoupClientContext *soup_client_context_ref (SoupClientContext *client); -static void soup_client_context_unref (SoupClientContext *client); - +static void start_request (SoupServer *server, + SoupServerMessage *msg); static void free_handler (SoupServerHandler *handler) { @@ -398,7 +383,6 @@ soup_server_class_init (SoupServerClass *server_class) * SoupServer::request-started: * @server: the server * @message: the new message - * @client: the client context * * Emitted when the server has started reading a new request. * @message will be completely blank; not even the @@ -419,15 +403,13 @@ soup_server_class_init (SoupServerClass *server_class) G_STRUCT_OFFSET (SoupServerClass, request_started), NULL, NULL, NULL, - G_TYPE_NONE, 2, - SOUP_TYPE_MESSAGE, - SOUP_TYPE_CLIENT_CONTEXT); + G_TYPE_NONE, 1, + SOUP_TYPE_SERVER_MESSAGE); /** * SoupServer::request-read: * @server: the server * @message: the message - * @client: the client context * * Emitted when the server has successfully read a request. * @message will have all of its request-side information @@ -444,15 +426,13 @@ soup_server_class_init (SoupServerClass *server_class) G_STRUCT_OFFSET (SoupServerClass, request_read), NULL, NULL, NULL, - G_TYPE_NONE, 2, - SOUP_TYPE_MESSAGE, - SOUP_TYPE_CLIENT_CONTEXT); + G_TYPE_NONE, 1, + SOUP_TYPE_SERVER_MESSAGE); /** * SoupServer::request-finished: * @server: the server * @message: the message - * @client: the client context * * Emitted when the server has finished writing a response to * a request. @@ -464,15 +444,13 @@ soup_server_class_init (SoupServerClass *server_class) G_STRUCT_OFFSET (SoupServerClass, request_finished), NULL, NULL, NULL, - G_TYPE_NONE, 2, - SOUP_TYPE_MESSAGE, - SOUP_TYPE_CLIENT_CONTEXT); + G_TYPE_NONE, 1, + SOUP_TYPE_SERVER_MESSAGE); /** * SoupServer::request-aborted: * @server: the server * @message: the message - * @client: the client context * * Emitted when processing has failed for a message; this * could mean either that it could not be read (if @@ -493,9 +471,8 @@ soup_server_class_init (SoupServerClass *server_class) G_STRUCT_OFFSET (SoupServerClass, request_aborted), NULL, NULL, NULL, - G_TYPE_NONE, 2, - SOUP_TYPE_MESSAGE, - SOUP_TYPE_CLIENT_CONTEXT); + G_TYPE_NONE, 1, + SOUP_TYPE_SERVER_MESSAGE); /* properties */ /** @@ -818,97 +795,6 @@ soup_server_get_listeners (SoupServer *server) return listeners; } -static void start_request (SoupServer *, SoupClientContext *); -static void socket_disconnected (SoupSocket *sock, SoupClientContext *client); - -static SoupClientContext * -soup_client_context_new (SoupServer *server, SoupSocket *sock) -{ - SoupClientContext *client = g_slice_new0 (SoupClientContext); - - client->server = server; - client->sock = g_object_ref (sock); - client->gsock = soup_socket_get_gsocket (sock); - if (client->gsock) - g_object_ref (client->gsock); - g_signal_connect (sock, "disconnected", - G_CALLBACK (socket_disconnected), client); - client->ref_count = 1; - - return client; -} - -static void -soup_client_context_cleanup (SoupClientContext *client) -{ - g_clear_object (&client->auth_domain); - g_clear_pointer (&client->auth_user, g_free); - g_clear_object (&client->remote_addr); - g_clear_object (&client->local_addr); - - client->msg = NULL; -} - -static SoupClientContext * -soup_client_context_ref (SoupClientContext *client) -{ - g_atomic_int_inc (&client->ref_count); - return client; -} - -static void -soup_client_context_unref (SoupClientContext *client) -{ - if (!g_atomic_int_dec_and_test (&client->ref_count)) - return; - - soup_client_context_cleanup (client); - - g_signal_handlers_disconnect_by_func (client->sock, socket_disconnected, client); - g_object_unref (client->sock); - g_clear_object (&client->gsock); - g_clear_pointer (&client->remote_ip, g_free); - g_slice_free (SoupClientContext, client); -} - -static void -request_finished (SoupMessage *msg, SoupMessageIOCompletion completion, gpointer user_data) -{ - SoupClientContext *client = user_data; - SoupServer *server = client->server; - SoupServerPrivate *priv = soup_server_get_instance_private (server); - SoupSocket *sock = client->sock; - gboolean failed; - - if (completion == SOUP_MESSAGE_IO_STOLEN) { - soup_client_context_unref (client); - g_object_unref (msg); - return; - } - - /* Complete the message, assuming it actually really started. */ - if (msg->method) { - soup_message_finished (msg); - - failed = (completion == SOUP_MESSAGE_IO_INTERRUPTED || - msg->status_code == SOUP_STATUS_IO_ERROR); - g_signal_emit (server, - failed ? signals[REQUEST_ABORTED] : signals[REQUEST_FINISHED], - 0, msg, client); - } - - if (completion == SOUP_MESSAGE_IO_COMPLETE && - soup_socket_is_connected (sock) && - soup_message_is_keepalive (msg) && - priv->listeners) { - start_request (server, client); - } else { - soup_socket_disconnect (client->sock); - soup_client_context_unref (client); - } - g_object_unref (msg); -} - /* "" was never documented as meaning the same thing as "/", but it * effectively was. We have to special case it now or otherwise it * would match "*" too. @@ -916,19 +802,21 @@ request_finished (SoupMessage *msg, SoupMessageIOCompletion completion, gpointer #define NORMALIZED_PATH(path) ((path) && *(path) ? (path) : "/") static SoupServerHandler * -get_handler (SoupServer *server, SoupMessage *msg) +get_handler (SoupServer *server, + SoupServerMessage *msg) { SoupServerPrivate *priv = soup_server_get_instance_private (server); SoupURI *uri; - uri = soup_message_get_uri (msg); + uri = soup_server_message_get_uri (msg); return soup_path_map_lookup (priv->handlers, NORMALIZED_PATH (uri->path)); } static void -call_handler (SoupServer *server, SoupServerHandler *handler, - SoupClientContext *client, SoupMessage *msg, - gboolean early) +call_handler (SoupServer *server, + SoupServerHandler *handler, + SoupServerMessage *msg, + gboolean early) { GHashTable *form_data_set; SoupURI *uri; @@ -938,10 +826,10 @@ call_handler (SoupServer *server, SoupServerHandler *handler, else if (!early && !handler->callback) return; - if (msg->status_code != 0) + if (soup_server_message_get_status (msg, NULL) != 0) return; - uri = soup_message_get_uri (msg); + uri = soup_server_message_get_uri (msg); if (uri->query) form_data_set = soup_form_decode (uri->query); else @@ -950,11 +838,11 @@ call_handler (SoupServer *server, SoupServerHandler *handler, if (early) { (*handler->early_callback) (server, msg, uri->path, form_data_set, - client, handler->early_user_data); + handler->early_user_data); } else { (*handler->callback) (server, msg, uri->path, form_data_set, - client, handler->user_data); + handler->user_data); } if (form_data_set) @@ -962,9 +850,9 @@ call_handler (SoupServer *server, SoupServerHandler *handler, } static void -got_headers (SoupMessage *msg, SoupClientContext *client) +got_headers (SoupServer *server, + SoupServerMessage *msg) { - SoupServer *server = client->server; SoupServerPrivate *priv = soup_server_get_instance_private (server); SoupServerHandler *handler; SoupURI *uri; @@ -974,22 +862,26 @@ got_headers (SoupMessage *msg, SoupClientContext *client) GSList *iter; gboolean rejected = FALSE; char *auth_user; + SoupMessageHeaders *headers; + SoupSocket *sock; /* Add required response headers */ + headers = soup_server_message_get_response_headers (msg); + date = g_date_time_new_now_utc (); date_string = soup_date_time_to_string (date, SOUP_DATE_HTTP); - soup_message_headers_replace (msg->response_headers, "Date", - date_string); + soup_message_headers_replace (headers, "Date", date_string); g_free (date_string); g_date_time_unref (date); - if (msg->status_code != 0) + if (soup_server_message_get_status (msg, NULL) != 0) return; - uri = soup_message_get_uri (msg); - if ((soup_socket_is_ssl (client->sock) && !soup_uri_is_https (uri, priv->https_aliases)) || - (!soup_socket_is_ssl (client->sock) && !soup_uri_is_http (uri, priv->http_aliases))) { - soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); + sock = soup_server_message_get_soup_socket (msg); + uri = soup_server_message_get_uri (msg); + if ((soup_socket_is_ssl (sock) && !soup_uri_is_https (uri, priv->https_aliases)) || + (!soup_socket_is_ssl (sock) && !soup_uri_is_http (uri, priv->http_aliases))) { + soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); return; } @@ -1010,7 +902,7 @@ got_headers (SoupMessage *msg, SoupClientContext *client) ) { /* Introducing new ".." segments is not allowed */ g_free (decoded_path); - soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); + soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); return; } @@ -1029,8 +921,7 @@ got_headers (SoupMessage *msg, SoupClientContext *client) if (soup_auth_domain_covers (domain, msg)) { auth_user = soup_auth_domain_accepts (domain, msg); if (auth_user) { - client->auth_domain = g_object_ref (domain); - client->auth_user = auth_user; + soup_server_message_set_auth (msg, g_object_ref (domain), auth_user); return; } @@ -1052,15 +943,14 @@ got_headers (SoupMessage *msg, SoupClientContext *client) /* Otherwise, call the early handlers. */ handler = get_handler (server, msg); if (handler) - call_handler (server, handler, client, msg, TRUE); + call_handler (server, handler, msg, TRUE); } static void -complete_websocket_upgrade (SoupMessage *msg, gpointer user_data) +complete_websocket_upgrade (SoupServer *server, + SoupServerMessage *msg) { - SoupClientContext *client = user_data; - SoupServer *server = client->server; - SoupURI *uri = soup_message_get_uri (msg); + SoupURI *uri = soup_server_message_get_uri (msg); SoupServerHandler *handler; GIOStream *stream; SoupWebsocketConnection *conn; @@ -1069,42 +959,41 @@ complete_websocket_upgrade (SoupMessage *msg, gpointer user_data) if (!handler || !handler->websocket_callback) return; - soup_client_context_ref (client); - stream = soup_client_context_steal_connection (client); + g_object_ref (msg); + stream = soup_server_message_steal_connection (msg); conn = soup_websocket_connection_new_with_extensions (stream, uri, SOUP_WEBSOCKET_CONNECTION_SERVER, - soup_message_headers_get_one (msg->request_headers, "Origin"), - soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"), + soup_message_headers_get_one (soup_server_message_get_request_headers (msg), "Origin"), + soup_message_headers_get_one (soup_server_message_get_response_headers (msg), "Sec-WebSocket-Protocol"), handler->websocket_extensions); handler->websocket_extensions = NULL; g_object_unref (stream); - soup_client_context_unref (client); - (*handler->websocket_callback) (server, conn, uri->path, client, + (*handler->websocket_callback) (server, msg, uri->path, conn, handler->websocket_user_data); g_object_unref (conn); - soup_client_context_unref (client); + g_object_unref (msg); } static void -got_body (SoupMessage *msg, SoupClientContext *client) +got_body (SoupServer *server, + SoupServerMessage *msg) { - SoupServer *server = client->server; SoupServerHandler *handler; - g_signal_emit (server, signals[REQUEST_READ], 0, msg, client); + g_signal_emit (server, signals[REQUEST_READ], 0, msg); - if (msg->status_code != 0) + if (soup_server_message_get_status (msg, NULL) != 0) return; handler = get_handler (server, msg); if (!handler) { - soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); + soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL); return; } - call_handler (server, handler, client, msg, FALSE); - if (msg->status_code != 0) + call_handler (server, handler, msg, FALSE); + if (soup_server_message_get_status (msg, NULL) != 0) return; if (handler->websocket_callback) { @@ -1116,65 +1005,110 @@ got_body (SoupMessage *msg, SoupClientContext *client) handler->websocket_protocols, priv->websocket_extension_types, &handler->websocket_extensions)) { - g_signal_connect (msg, "wrote-informational", - G_CALLBACK (complete_websocket_upgrade), - soup_client_context_ref (client)); + g_signal_connect_object (msg, "wrote-informational", + G_CALLBACK (complete_websocket_upgrade), + server, G_CONNECT_SWAPPED); } } } static void -start_request (SoupServer *server, SoupClientContext *client) +client_disconnected (SoupServer *server, + SoupServerMessage *msg) { SoupServerPrivate *priv = soup_server_get_instance_private (server); - SoupMessage *msg; - - soup_client_context_cleanup (client); - /* Listen for another request on this connection */ - msg = g_object_new (SOUP_TYPE_MESSAGE, - SOUP_MESSAGE_SERVER_SIDE, TRUE, - NULL); - client->msg = msg; + priv->clients = g_slist_remove (priv->clients, msg); - if (priv->server_header) { - soup_message_headers_append (msg->response_headers, "Server", - priv->server_header); + if (soup_server_message_get_status (msg, NULL) != 0) { + soup_server_message_set_status (msg, SOUP_STATUS_IO_ERROR, NULL); + soup_server_message_io_finished (msg); } +} - g_signal_connect (msg, "got_headers", G_CALLBACK (got_headers), client); - g_signal_connect (msg, "got_body", G_CALLBACK (got_body), client); - - g_signal_emit (server, signals[REQUEST_STARTED], 0, - msg, client); - - soup_message_read_request (msg, client->sock, - request_finished, client); +static void +soup_server_accept_socket (SoupServer *server, + SoupSocket *sock) +{ + SoupServerPrivate *priv = soup_server_get_instance_private (server); + SoupServerMessage *msg; + + msg = soup_server_message_new (sock); + g_signal_connect_object (msg, "disconnected", + G_CALLBACK (client_disconnected), + server, G_CONNECT_SWAPPED); + priv->clients = g_slist_prepend (priv->clients, msg); + start_request (server, msg); } static void -socket_disconnected (SoupSocket *sock, SoupClientContext *client) +request_finished (SoupServerMessage *msg, + SoupMessageIOCompletion completion, + SoupServer *server) { - SoupServerPrivate *priv = soup_server_get_instance_private (client->server); + SoupServerPrivate *priv = soup_server_get_instance_private (server); + SoupSocket *sock = soup_server_message_get_soup_socket (msg); + gboolean failed; + + if (completion == SOUP_MESSAGE_IO_STOLEN) { + g_object_unref (msg); + return; + } - priv->clients = g_slist_remove (priv->clients, client); + /* Complete the message, assuming it actually really started. */ + if (soup_server_message_get_method (msg)) { + soup_server_message_finished (msg); + + failed = (completion == SOUP_MESSAGE_IO_INTERRUPTED || + soup_server_message_get_status (msg, NULL) == SOUP_STATUS_IO_ERROR); + g_signal_emit (server, + failed ? signals[REQUEST_ABORTED] : signals[REQUEST_FINISHED], + 0, msg); + } - if (client->msg) { - soup_message_set_status (client->msg, SOUP_STATUS_IO_ERROR); - soup_message_io_finished (client->msg); + if (completion == SOUP_MESSAGE_IO_COMPLETE && + soup_socket_is_connected (sock) && + soup_server_message_is_keepalive (msg) && + priv->listeners) { + g_object_ref (sock); + priv->clients = g_slist_remove (priv->clients, msg); + g_object_unref (msg); + + soup_server_accept_socket (server, sock); + g_object_unref (sock); + return; } + + soup_socket_disconnect (sock); + g_object_unref (msg); } static void -soup_server_accept_socket (SoupServer *server, - SoupSocket *sock) +start_request (SoupServer *server, + SoupServerMessage *msg) { SoupServerPrivate *priv = soup_server_get_instance_private (server); - SoupClientContext *client; - client = soup_client_context_new (server, sock); - priv->clients = g_slist_prepend (priv->clients, client); - start_request (server, client); + if (priv->server_header) { + SoupMessageHeaders *headers; + + headers = soup_server_message_get_response_headers (msg); + soup_message_headers_append (headers, "Server", + priv->server_header); + } + + g_signal_connect_object (msg, "got-headers", + G_CALLBACK (got_headers), + server, G_CONNECT_SWAPPED); + g_signal_connect_object (msg, "got-body", + G_CALLBACK (got_body), + server, G_CONNECT_SWAPPED); + + g_signal_emit (server, signals[REQUEST_STARTED], 0, msg); + + soup_server_message_read_request (msg, + (SoupMessageIOCompletionFn)request_finished, + server); } /** @@ -1194,11 +1128,11 @@ soup_server_accept_socket (SoupServer *server, * Since: 2.50 **/ gboolean -soup_server_accept_iostream (SoupServer *server, - GIOStream *stream, - GSocketAddress *local_addr, - GSocketAddress *remote_addr, - GError **error) +soup_server_accept_iostream (SoupServer *server, + GIOStream *stream, + GSocketAddress *local_addr, + GSocketAddress *remote_addr, + GError **error) { SoupSocket *sock; @@ -1244,7 +1178,6 @@ soup_server_disconnect (SoupServer *server) SoupServerPrivate *priv; GSList *listeners, *clients, *iter; SoupSocket *listener; - SoupClientContext *client; g_return_if_fail (SOUP_IS_SERVER (server)); priv = soup_server_get_instance_private (server); @@ -1255,8 +1188,9 @@ soup_server_disconnect (SoupServer *server) priv->listeners = NULL; for (iter = clients; iter; iter = iter->next) { - client = iter->data; - soup_socket_disconnect (client->sock); + SoupServerMessage *msg = iter->data; + + soup_socket_disconnect (soup_server_message_get_soup_socket (msg)); } g_slist_free (clients); @@ -1679,257 +1613,12 @@ soup_server_get_uris (SoupServer *server) } /** - * SoupClientContext: - * - * A #SoupClientContext provides additional information about the - * client making a particular request. In particular, you can use - * soup_client_context_get_auth_domain() and - * soup_client_context_get_auth_user() to determine if HTTP - * authentication was used successfully. - * - * soup_client_context_get_remote_address() and/or - * soup_client_context_get_host() can be used to get information for - * logging or debugging purposes. soup_client_context_get_socket() may - * also be of use in some situations (eg, tracking when multiple - * requests are made on the same connection). - **/ -G_DEFINE_BOXED_TYPE (SoupClientContext, soup_client_context, soup_client_context_ref, soup_client_context_unref) - -/** - * soup_client_context_get_soup_socket: - * @client: a #SoupClientContext - * - * Retrieves the #SoupSocket that @client is associated with. - * - * If you are using this method to observe when multiple requests are - * made on the same persistent HTTP connection (eg, as the ntlm-test - * test program does), you will need to pay attention to socket - * destruction as well (either by using weak references, or by - * connecting to the #SoupSocket::disconnected signal), so that you do - * not get fooled when the allocator reuses the memory address of a - * previously-destroyed socket to represent a new socket. - * - * Return value: (transfer none): the #SoupSocket that @client is - * associated with. - **/ -SoupSocket * -soup_client_context_get_soup_socket (SoupClientContext *client) -{ - g_return_val_if_fail (client != NULL, NULL); - - return client->sock; -} - -/** - * soup_client_context_get_socket: - * @client: a #SoupClientContext - * - * Retrieves the #GSocket that @client is associated with. - * - * If you are using this method to observe when multiple requests are - * made on the same persistent HTTP connection (eg, as the ntlm-test - * test program does), you will need to pay attention to socket - * destruction as well (eg, by using weak references), so that you do - * not get fooled when the allocator reuses the memory address of a - * previously-destroyed socket to represent a new socket. - * - * Return value: (nullable) (transfer none): the #GSocket that @client is - * associated with, %NULL if you used soup_server_accept_iostream(). - * - * Since: 2.48 - **/ -GSocket * -soup_client_context_get_socket (SoupClientContext *client) -{ - g_return_val_if_fail (client != NULL, NULL); - - return client->gsock; -} - -/** - * soup_client_context_get_remote_address: - * @client: a #SoupClientContext - * - * Retrieves the #GSocketAddress associated with the remote end - * of a connection. - * - * Return value: (nullable) (transfer none): the #GSocketAddress - * associated with the remote end of a connection, it may be - * %NULL if you used soup_server_accept_iostream(). - * - * Since: 2.48 - **/ -GSocketAddress * -soup_client_context_get_remote_address (SoupClientContext *client) -{ - g_return_val_if_fail (client != NULL, NULL); - - if (client->remote_addr) - return client->remote_addr; - - client->remote_addr = client->gsock ? - g_socket_get_remote_address (client->gsock, NULL) : - G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_remote_address (client->sock))); - - return client->remote_addr; -} - -/** - * soup_client_context_get_local_address: - * @client: a #SoupClientContext - * - * Retrieves the #GSocketAddress associated with the local end - * of a connection. - * - * Return value: (nullable) (transfer none): the #GSocketAddress - * associated with the local end of a connection, it may be - * %NULL if you used soup_server_accept_iostream(). - * - * Since: 2.48 - **/ -GSocketAddress * -soup_client_context_get_local_address (SoupClientContext *client) -{ - g_return_val_if_fail (client != NULL, NULL); - - if (client->local_addr) - return client->local_addr; - - client->local_addr = client->gsock ? - g_socket_get_local_address (client->gsock, NULL) : - G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_local_address (client->sock))); - - return client->local_addr; -} - -/** - * soup_client_context_get_host: - * @client: a #SoupClientContext - * - * Retrieves the IP address associated with the remote end of a - * connection. - * - * Return value: (nullable): the IP address associated with the remote - * end of a connection, it may be %NULL if you used - * soup_server_accept_iostream(). - **/ -const char * -soup_client_context_get_host (SoupClientContext *client) -{ - g_return_val_if_fail (client != NULL, NULL); - - if (client->remote_ip) - return client->remote_ip; - - if (client->gsock) { - GSocketAddress *addr = soup_client_context_get_remote_address (client); - GInetAddress *iaddr; - - if (!addr || !G_IS_INET_SOCKET_ADDRESS (addr)) - return NULL; - iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); - client->remote_ip = g_inet_address_to_string (iaddr); - } else { - GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_socket_get_remote_address (client->sock)); - GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); - client->remote_ip = g_inet_address_to_string (inet_addr); - } - - return client->remote_ip; -} - -/** - * soup_client_context_get_auth_domain: - * @client: a #SoupClientContext - * - * Checks whether the request associated with @client has been - * authenticated, and if so returns the #SoupAuthDomain that - * authenticated it. - * - * Return value: (transfer none) (nullable): a #SoupAuthDomain, or - * %NULL if the request was not authenticated. - **/ -SoupAuthDomain * -soup_client_context_get_auth_domain (SoupClientContext *client) -{ - g_return_val_if_fail (client != NULL, NULL); - - return client->auth_domain; -} - -/** - * soup_client_context_get_auth_user: - * @client: a #SoupClientContext - * - * Checks whether the request associated with @client has been - * authenticated, and if so returns the username that the client - * authenticated as. - * - * Return value: (nullable): the authenticated-as user, or %NULL if - * the request was not authenticated. - **/ -const char * -soup_client_context_get_auth_user (SoupClientContext *client) -{ - g_return_val_if_fail (client != NULL, NULL); - - return client->auth_user; -} - -/** - * soup_client_context_steal_connection: - * @client: a #SoupClientContext - * - * "Steals" the HTTP connection associated with @client from its - * #SoupServer. This happens immediately, regardless of the current - * state of the connection; if the response to the current - * #SoupMessage has not yet finished being sent, then it will be - * discarded; you can steal the connection from a - * #SoupMessage::wrote-informational or #SoupMessage::wrote-body signal - * handler if you need to wait for part or all of the response to be - * sent. - * - * Note that when calling this function from C, @client will most - * likely be freed as a side effect. - * - * Return value: (transfer full): the #GIOStream formerly associated - * with @client (or %NULL if @client was no longer associated with a - * connection). No guarantees are made about what kind of #GIOStream - * is returned. - * - * Since: 2.50 - **/ -GIOStream * -soup_client_context_steal_connection (SoupClientContext *client) -{ - GIOStream *stream; - - g_return_val_if_fail (client != NULL, NULL); - - soup_client_context_ref (client); - - stream = soup_message_io_steal (client->msg); - if (stream) { - g_object_set_data_full (G_OBJECT (stream), "GSocket", - soup_socket_steal_gsocket (client->sock), - g_object_unref); - } - - socket_disconnected (client->sock, client); - soup_client_context_unref (client); - - return stream; -} - - -/** * SoupServerCallback: * @server: the #SoupServer * @msg: the message being processed * @path: the path component of @msg's Request-URI * @query: (element-type utf8 utf8) (allow-none): the parsed query * component of @msg's Request-URI - * @client: additional contextual information about the client * @user_data: the data passed to soup_server_add_handler() or * soup_server_add_early_handler(). * @@ -2108,7 +1797,7 @@ soup_server_add_early_handler (SoupServer *server, * @server: the #SoupServer * @path: the path component of @msg's Request-URI * @connection: the newly created WebSocket connection - * @client: additional contextual information about the client + * @msg: the #SoupServerMessage * @user_data: the data passed to @soup_server_add_handler * * A callback used to handle WebSocket requests to a #SoupServer. The @@ -2249,30 +1938,30 @@ soup_server_remove_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain) /** * soup_server_pause_message: * @server: a #SoupServer - * @msg: a #SoupMessage associated with @server. + * @msg: a #SoupServerMessage associated with @server. * * Pauses I/O on @msg. This can be used when you need to return from * the server handler without having the full response ready yet. Use * soup_server_unpause_message() to resume I/O. * - * This must only be called on a #SoupMessage which was created by the + * This must only be called on a #SoupServerMessage which was created by the * #SoupServer and are currently doing I/O, such as those passed into a * #SoupServerCallback or emitted in a #SoupServer::request-read signal. **/ void -soup_server_pause_message (SoupServer *server, - SoupMessage *msg) +soup_server_pause_message (SoupServer *server, + SoupServerMessage *msg) { g_return_if_fail (SOUP_IS_SERVER (server)); - g_return_if_fail (SOUP_IS_MESSAGE (msg)); + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); - soup_message_io_pause (msg); + soup_server_message_io_pause (msg); } /** * soup_server_unpause_message: * @server: a #SoupServer - * @msg: a #SoupMessage associated with @server. + * @msg: a #SoupServerMessage associated with @server. * * Resumes I/O on @msg. Use this to resume after calling * soup_server_pause_message(), or after adding a new chunk to a @@ -2280,18 +1969,18 @@ soup_server_pause_message (SoupServer *server, * * I/O won't actually resume until you return to the main loop. * - * This must only be called on a #SoupMessage which was created by the + * This must only be called on a #SoupServerMessage which was created by the * #SoupServer and are currently doing I/O, such as those passed into a * #SoupServerCallback or emitted in a #SoupServer::request-read signal. **/ void -soup_server_unpause_message (SoupServer *server, - SoupMessage *msg) +soup_server_unpause_message (SoupServer *server, + SoupServerMessage *msg) { g_return_if_fail (SOUP_IS_SERVER (server)); - g_return_if_fail (SOUP_IS_MESSAGE (msg)); + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); - soup_message_io_unpause (msg); + soup_server_message_io_unpause (msg); } /** diff --git a/libsoup/server/soup-server.h b/libsoup/server/soup-server.h index eb9a13b7..b5225207 100644 --- a/libsoup/server/soup-server.h +++ b/libsoup/server/soup-server.h @@ -15,11 +15,6 @@ G_BEGIN_DECLS SOUP_AVAILABLE_IN_2_4 G_DECLARE_DERIVABLE_TYPE (SoupServer, soup_server, SOUP, SERVER, GObject) -typedef struct _SoupClientContext SoupClientContext; -SOUP_AVAILABLE_IN_2_4 -GType soup_client_context_get_type (void); -#define SOUP_TYPE_CLIENT_CONTEXT (soup_client_context_get_type ()) - typedef enum { SOUP_SERVER_LISTEN_HTTPS = (1 << 0), SOUP_SERVER_LISTEN_IPV4_ONLY = (1 << 1), @@ -30,14 +25,14 @@ struct _SoupServerClass { GObjectClass parent_class; /* signals */ - void (*request_started) (SoupServer *server, SoupMessage *msg, - SoupClientContext *client); - void (*request_read) (SoupServer *server, SoupMessage *msg, - SoupClientContext *client); - void (*request_finished) (SoupServer *server, SoupMessage *msg, - SoupClientContext *client); - void (*request_aborted) (SoupServer *server, SoupMessage *msg, - SoupClientContext *client); + void (*request_started) (SoupServer *server, + SoupServerMessage *msg); + void (*request_read) (SoupServer *server, + SoupServerMessage *msg); + void (*request_finished) (SoupServer *server, + SoupServerMessage *msg); + void (*request_aborted) (SoupServer *server, + SoupServerMessage *msg); gpointer padding[6]; }; @@ -98,10 +93,9 @@ gboolean soup_server_accept_iostream (SoupServer *server /* Handlers and auth */ typedef void (*SoupServerCallback) (SoupServer *server, - SoupMessage *msg, + SoupServerMessage *msg, const char *path, GHashTable *query, - SoupClientContext *client, gpointer user_data); SOUP_AVAILABLE_IN_2_4 @@ -121,9 +115,9 @@ void soup_server_add_early_handler (SoupServer *server, #define SOUP_SERVER_REMOVE_WEBSOCKET_EXTENSION "remove-websocket-extension" typedef void (*SoupServerWebsocketCallback) (SoupServer *server, - SoupWebsocketConnection *connection, + SoupServerMessage *msg, const char *path, - SoupClientContext *client, + SoupWebsocketConnection *connection, gpointer user_data); SOUP_AVAILABLE_IN_2_50 void soup_server_add_websocket_handler (SoupServer *server, @@ -153,28 +147,10 @@ void soup_server_remove_auth_domain (SoupServer *server, /* I/O */ SOUP_AVAILABLE_IN_2_4 -void soup_server_pause_message (SoupServer *server, - SoupMessage *msg); -SOUP_AVAILABLE_IN_2_4 -void soup_server_unpause_message (SoupServer *server, - SoupMessage *msg); - -/* Client context */ - -SOUP_AVAILABLE_IN_2_48 -GSocket *soup_client_context_get_socket (SoupClientContext *client); -SOUP_AVAILABLE_IN_2_48 -GSocketAddress *soup_client_context_get_local_address (SoupClientContext *client); -SOUP_AVAILABLE_IN_2_48 -GSocketAddress *soup_client_context_get_remote_address (SoupClientContext *client); -SOUP_AVAILABLE_IN_2_4 -const char *soup_client_context_get_host (SoupClientContext *client); +void soup_server_pause_message (SoupServer *server, + SoupServerMessage *msg); SOUP_AVAILABLE_IN_2_4 -SoupAuthDomain *soup_client_context_get_auth_domain (SoupClientContext *client); -SOUP_AVAILABLE_IN_2_4 -const char *soup_client_context_get_auth_user (SoupClientContext *client); - -SOUP_AVAILABLE_IN_2_50 -GIOStream *soup_client_context_steal_connection (SoupClientContext *client); +void soup_server_unpause_message (SoupServer *server, + SoupServerMessage *msg); G_END_DECLS |