diff options
Diffstat (limited to 'libsoup')
34 files changed, 2240 insertions, 1800 deletions
diff --git a/libsoup/meson.build b/libsoup/meson.build index c3a691d4..3a700870 100644 --- a/libsoup/meson.build +++ b/libsoup/meson.build @@ -44,6 +44,7 @@ soup_sources = [ 'server/soup-path-map.c', 'server/soup-server.c', 'server/soup-server-io.c', + 'server/soup-server-message.c', 'websocket/soup-websocket.c', 'websocket/soup-websocket-connection.c', @@ -67,6 +68,7 @@ soup_sources = [ 'soup-message-body.c', 'soup-message-headers.c', 'soup-message-io.c', + 'soup-message-io-data.c', 'soup-message-queue.c', 'soup-method.c', 'soup-misc.c', @@ -117,6 +119,7 @@ soup_introspection_headers = [ 'server/soup-auth-domain-basic.h', 'server/soup-auth-domain-digest.h', 'server/soup-server.h', + 'server/soup-server-message.h', 'websocket/soup-websocket.h', 'websocket/soup-websocket-connection.h', 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 diff --git a/libsoup/soup-client-input-stream.c b/libsoup/soup-client-input-stream.c index 16abb411..8fa28c0b 100644 --- a/libsoup/soup-client-input-stream.c +++ b/libsoup/soup-client-input-stream.c @@ -193,8 +193,9 @@ soup_client_input_stream_close_async (GInputStream *stream, g_task_set_priority (task, priority); if (close_async_ready (cistream->priv->msg, task) == G_SOURCE_CONTINUE) { - source = soup_message_io_get_source (cistream->priv->msg, - cancellable, NULL, NULL); + source = soup_message_io_data_get_source ((SoupMessageIOData *)soup_message_get_io_data (cistream->priv->msg), + G_OBJECT (cistream->priv->msg), + cancellable, NULL, NULL); g_task_attach_source (task, source, (GSourceFunc) close_async_ready); g_source_unref (source); diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index df2cdb59..6e39bc7c 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -730,10 +730,10 @@ soup_connection_get_ever_used (SoupConnection *conn) } void -soup_connection_send_request (SoupConnection *conn, - SoupMessageQueueItem *item, - SoupMessageCompletionFn completion_cb, - gpointer user_data) +soup_connection_send_request (SoupConnection *conn, + SoupMessageQueueItem *item, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data) { SoupConnectionPrivate *priv; diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h index 19888f1f..00c030cf 100644 --- a/libsoup/soup-connection.h +++ b/libsoup/soup-connection.h @@ -71,10 +71,10 @@ void soup_connection_set_state (SoupConnection *conn, gboolean soup_connection_get_ever_used (SoupConnection *conn); -void soup_connection_send_request (SoupConnection *conn, - SoupMessageQueueItem *item, - SoupMessageCompletionFn completion_cb, - gpointer user_data); +void soup_connection_send_request (SoupConnection *conn, + SoupMessageQueueItem *item, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data); G_END_DECLS diff --git a/libsoup/soup-form.c b/libsoup/soup-form.c index 32e7de3c..e73cca93 100644 --- a/libsoup/soup-form.c +++ b/libsoup/soup-form.c @@ -112,13 +112,13 @@ soup_form_decode (const char *encoded_form) /** * soup_form_decode_multipart: - * @msg: a #SoupMessage containing a "multipart/form-data" request body + * @multipart: a #SoupMultipart * @file_control_name: (allow-none): the name of the HTML file upload control, or %NULL * @filename: (out) (allow-none): return location for the name of the uploaded file, or %NULL * @content_type: (out) (allow-none): return location for the MIME type of the uploaded file, or %NULL * @file: (out) (allow-none): return location for the uploaded file data, or %NULL * - * Decodes the "multipart/form-data" request in @msg; this is a + * Decodes the "multipart/form-data" request in @multipart; this is a * convenience method for the case when you have a single file upload * control in a form. (Or when you don't have any file upload * controls, but are still using "multipart/form-data" anyway.) Pass @@ -142,29 +142,21 @@ soup_form_decode (const char *encoded_form) * a hash table containing the name/value pairs (other than * @file_control_name) from @msg, which you can free with * g_hash_table_destroy(). On error, it will return %NULL. - * - * Since: 2.26 - **/ + */ GHashTable * -soup_form_decode_multipart (SoupMessage *msg, const char *file_control_name, - char **filename, char **content_type, - GBytes **file) +soup_form_decode_multipart (SoupMultipart *multipart, + const char *file_control_name, + char **filename, + char **content_type, + GBytes **file) { - SoupMultipart *multipart; GHashTable *form_data_set, *params; SoupMessageHeaders *part_headers; - GBytes *body; GBytes *part_body; char *disposition, *name; int i; - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL); - - body = soup_message_body_flatten (msg->request_body); - multipart = soup_multipart_new_from_message (msg->request_headers, body); - g_bytes_unref (body); - if (!multipart) - return NULL; + g_return_val_if_fail (multipart != NULL, NULL); if (filename) *filename = NULL; diff --git a/libsoup/soup-form.h b/libsoup/soup-form.h index 4880e7be..27ca8aa1 100644 --- a/libsoup/soup-form.h +++ b/libsoup/soup-form.h @@ -17,11 +17,11 @@ G_BEGIN_DECLS SOUP_AVAILABLE_IN_2_4 GHashTable *soup_form_decode (const char *encoded_form); SOUP_AVAILABLE_IN_2_26 -GHashTable *soup_form_decode_multipart (SoupMessage *msg, - const char *file_control_name, - char **filename, - char **content_type, - GBytes **file); +GHashTable *soup_form_decode_multipart (SoupMultipart *multipart, + const char *file_control_name, + char **filename, + char **content_type, + GBytes **file); SOUP_AVAILABLE_IN_2_4 char *soup_form_encode (const char *first_field, diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c index fbe1e986..5a54b012 100644 --- a/libsoup/soup-headers.c +++ b/libsoup/soup-headers.c @@ -195,7 +195,7 @@ soup_headers_parse_request (const char *str, unsigned long major_version, minor_version; char *p; - g_return_val_if_fail (str != NULL, SOUP_STATUS_MALFORMED); + g_return_val_if_fail (str != NULL, SOUP_STATUS_BAD_REQUEST); /* RFC 2616 4.1 "servers SHOULD ignore any empty line(s) * received where a Request-Line is expected." diff --git a/libsoup/soup-logger.c b/libsoup/soup-logger.c index d76701a2..402f4f57 100644 --- a/libsoup/soup-logger.c +++ b/libsoup/soup-logger.c @@ -597,22 +597,6 @@ print_request (SoupLogger *logger, SoupMessage *msg, "%s: %s", name, value); } } - if (log_level == SOUP_LOGGER_LOG_HEADERS) - return; - - if (msg->request_body->length && - soup_message_body_get_accumulate (msg->request_body)) { - GBytes *request; - - request = soup_message_body_flatten (msg->request_body); - g_return_if_fail (request != NULL); - g_bytes_unref (request); - - if (soup_message_headers_get_expectations (msg->request_headers) != SOUP_EXPECTATION_CONTINUE) { - soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>', - "\n%s", msg->request_body->data); - } - } } static void @@ -653,13 +637,6 @@ print_response (SoupLogger *logger, SoupMessage *msg) soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '<', "%s: %s", name, value); } - if (log_level == SOUP_LOGGER_LOG_HEADERS) - return; - - if (msg->response_body->data) { - soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '<', - "\n%s", msg->response_body->data); - } } static void @@ -688,23 +665,9 @@ got_informational (SoupMessage *msg, gpointer user_data) print_response (logger, msg); soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n"); - if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body->data) { - SoupLoggerLogLevel log_level; - + if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body_stream) { soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>', "[Now sending request body...]"); - - if (priv->request_filter) { - log_level = priv->request_filter (logger, msg, - priv->request_filter_data); - } else - log_level = priv->level; - - if (log_level == SOUP_LOGGER_LOG_BODY) { - soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>', - "%s", msg->request_body->data); - } - soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n"); } diff --git a/libsoup/soup-message-io-data.c b/libsoup/soup-message-io-data.c new file mode 100644 index 00000000..8af99467 --- /dev/null +++ b/libsoup/soup-message-io-data.c @@ -0,0 +1,266 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-message-io-data.c: HTTP message I/O data + * + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib/gi18n-lib.h> + +#include "soup-message-io-data.h" +#include "soup-message-private.h" +#include "soup-server-message-private.h" +#include "soup.h" + +#define RESPONSE_BLOCK_SIZE 8192 +#define HEADER_SIZE_LIMIT (64 * 1024) + +void +soup_message_io_data_cleanup (SoupMessageIOData *io) +{ + if (io->io_source) { + g_source_destroy (io->io_source); + g_source_unref (io->io_source); + io->io_source = NULL; + } + + if (io->iostream) + g_object_unref (io->iostream); + if (io->body_istream) + g_object_unref (io->body_istream); + if (io->body_ostream) + g_object_unref (io->body_ostream); + if (io->async_context) + g_main_context_unref (io->async_context); + + g_byte_array_free (io->read_header_buf, TRUE); + + g_string_free (io->write_buf, TRUE); + + if (io->async_wait) { + g_cancellable_cancel (io->async_wait); + g_clear_object (&io->async_wait); + } + g_clear_error (&io->async_error); +} + +gboolean +soup_message_io_data_read_headers (SoupMessageIOData *io, + gboolean blocking, + GCancellable *cancellable, + GError **error) +{ + gssize nread, old_len; + gboolean got_lf; + + while (1) { + old_len = io->read_header_buf->len; + g_byte_array_set_size (io->read_header_buf, old_len + RESPONSE_BLOCK_SIZE); + nread = soup_filter_input_stream_read_line (io->istream, + io->read_header_buf->data + old_len, + RESPONSE_BLOCK_SIZE, + blocking, + &got_lf, + cancellable, error); + io->read_header_buf->len = old_len + MAX (nread, 0); + if (nread == 0) { + if (io->read_header_buf->len > 0) + break; + + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + _("Connection terminated unexpectedly")); + } + if (nread <= 0) + return FALSE; + + if (got_lf) { + if (nread == 1 && old_len >= 2 && + !strncmp ((char *)io->read_header_buf->data + + io->read_header_buf->len - 2, + "\n\n", 2)) { + io->read_header_buf->len--; + break; + } else if (nread == 2 && old_len >= 3 && + !strncmp ((char *)io->read_header_buf->data + + io->read_header_buf->len - 3, + "\n\r\n", 3)) { + io->read_header_buf->len -= 2; + break; + } + } + + if (io->read_header_buf->len > HEADER_SIZE_LIMIT) { + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + _("Header too big")); + return FALSE; + } + } + + io->read_header_buf->data[io->read_header_buf->len] = '\0'; + return TRUE; +} + +static gboolean +message_io_is_paused (GObject *msg) +{ + if (SOUP_IS_MESSAGE (msg)) + return soup_message_is_io_paused (SOUP_MESSAGE (msg)); + + if (SOUP_IS_SERVER_MESSAGE (msg)) + return soup_server_message_is_io_paused (SOUP_SERVER_MESSAGE (msg)); + + return FALSE; +} + +typedef struct { + GSource source; + GObject *msg; + gboolean paused; +} SoupMessageIOSource; + +static gboolean +message_io_source_check (GSource *source) +{ + SoupMessageIOSource *message_source = (SoupMessageIOSource *)source; + + if (message_source->paused) { + if (message_io_is_paused (message_source->msg)) + return FALSE; + return TRUE; + } else + return FALSE; +} + +static gboolean +message_io_source_prepare (GSource *source, + gint *timeout) +{ + *timeout = -1; + return message_io_source_check (source); +} + +static gboolean +message_io_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + SoupMessageIOSourceFunc func = (SoupMessageIOSourceFunc)callback; + SoupMessageIOSource *message_source = (SoupMessageIOSource *)source; + + return (*func) (message_source->msg, user_data); +} + +static void +message_io_source_finalize (GSource *source) +{ + SoupMessageIOSource *message_source = (SoupMessageIOSource *)source; + + g_object_unref (message_source->msg); +} + +static gboolean +message_io_source_closure_callback (GObject *msg, + gpointer data) +{ + GClosure *closure = data; + GValue param = G_VALUE_INIT; + GValue result_value = G_VALUE_INIT; + gboolean result; + + g_value_init (&result_value, G_TYPE_BOOLEAN); + + g_value_init (¶m, G_TYPE_OBJECT); + g_value_set_object (¶m, msg); + + g_closure_invoke (closure, &result_value, 1, ¶m, NULL); + + result = g_value_get_boolean (&result_value); + g_value_unset (&result_value); + g_value_unset (¶m); + + return result; +} + +static GSourceFuncs message_io_source_funcs = +{ + message_io_source_prepare, + message_io_source_check, + message_io_source_dispatch, + message_io_source_finalize, + (GSourceFunc)message_io_source_closure_callback, + (GSourceDummyMarshal)g_cclosure_marshal_generic, +}; + +GSource * +soup_message_io_data_get_source (SoupMessageIOData *io, + GObject *msg, + GCancellable *cancellable, + SoupMessageIOSourceFunc callback, + gpointer user_data) +{ + GSource *base_source, *source; + SoupMessageIOSource *message_source; + + if (!io) { + base_source = g_timeout_source_new (0); + } else if (io->paused) { + base_source = NULL; + } else if (io->async_wait) { + base_source = g_cancellable_source_new (io->async_wait); + } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) { + GPollableInputStream *istream; + + if (io->body_istream) + istream = G_POLLABLE_INPUT_STREAM (io->body_istream); + else + istream = G_POLLABLE_INPUT_STREAM (io->istream); + base_source = g_pollable_input_stream_create_source (istream, cancellable); + } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state)) { + GPollableOutputStream *ostream; + + if (io->body_ostream) + ostream = G_POLLABLE_OUTPUT_STREAM (io->body_ostream); + else + ostream = G_POLLABLE_OUTPUT_STREAM (io->ostream); + base_source = g_pollable_output_stream_create_source (ostream, cancellable); + } else + base_source = g_timeout_source_new (0); + + source = g_source_new (&message_io_source_funcs, sizeof (SoupMessageIOSource)); + g_source_set_name (source, "SoupMessageIOSource"); + message_source = (SoupMessageIOSource *)source; + message_source->msg = g_object_ref (msg); + message_source->paused = io && io->paused; + + if (base_source) { + g_source_set_dummy_callback (base_source); + g_source_add_child_source (source, base_source); + g_source_unref (base_source); + } + g_source_set_callback (source, (GSourceFunc) callback, user_data, NULL); + return source; +} + +void +soup_message_io_data_pause (SoupMessageIOData *io) +{ + if (io->io_source) { + g_source_destroy (io->io_source); + g_source_unref (io->io_source); + io->io_source = NULL; + } + + io->paused = TRUE; +} + +void +soup_message_io_data_unpause (SoupMessageIOData *io) +{ + io->paused = FALSE; +} diff --git a/libsoup/soup-message-io-data.h b/libsoup/soup-message-io-data.h new file mode 100644 index 00000000..0476a425 --- /dev/null +++ b/libsoup/soup-message-io-data.h @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#ifndef __SOUP_MESSAGE_IO_DATA_H__ +#define __SOUP_MESSAGE_IO_DATA_H__ 1 + +#include "soup-filter-input-stream.h" +#include "soup-message-headers.h" + +typedef enum { + SOUP_MESSAGE_IO_STATE_NOT_STARTED, + SOUP_MESSAGE_IO_STATE_ANY = SOUP_MESSAGE_IO_STATE_NOT_STARTED, + SOUP_MESSAGE_IO_STATE_HEADERS, + SOUP_MESSAGE_IO_STATE_BLOCKING, + SOUP_MESSAGE_IO_STATE_BODY_START, + SOUP_MESSAGE_IO_STATE_BODY, + SOUP_MESSAGE_IO_STATE_BODY_DATA, + SOUP_MESSAGE_IO_STATE_BODY_FLUSH, + SOUP_MESSAGE_IO_STATE_BODY_DONE, + SOUP_MESSAGE_IO_STATE_FINISHING, + SOUP_MESSAGE_IO_STATE_DONE +} SoupMessageIOState; + +#define SOUP_MESSAGE_IO_STATE_ACTIVE(state) \ + (state != SOUP_MESSAGE_IO_STATE_NOT_STARTED && \ + state != SOUP_MESSAGE_IO_STATE_BLOCKING && \ + state != SOUP_MESSAGE_IO_STATE_DONE) +#define SOUP_MESSAGE_IO_STATE_POLLABLE(state) \ + (SOUP_MESSAGE_IO_STATE_ACTIVE (state) && \ + state != SOUP_MESSAGE_IO_STATE_BODY_DONE) + +typedef enum { + SOUP_MESSAGE_IO_COMPLETE, + SOUP_MESSAGE_IO_INTERRUPTED, + SOUP_MESSAGE_IO_STOLEN +} SoupMessageIOCompletion; + +typedef void (*SoupMessageIOCompletionFn) (GObject *msg, + SoupMessageIOCompletion completion, + gpointer user_data); + +typedef struct { + GIOStream *iostream; + SoupFilterInputStream *istream; + GInputStream *body_istream; + GOutputStream *ostream; + GOutputStream *body_ostream; + GMainContext *async_context; + + SoupMessageIOState read_state; + SoupEncoding read_encoding; + GByteArray *read_header_buf; + goffset read_length; + + SoupMessageIOState write_state; + SoupEncoding write_encoding; + GString *write_buf; + GBytes *write_chunk; + goffset write_body_offset; + goffset write_length; + goffset written; + + GSource *io_source; + gboolean paused; + + GCancellable *async_wait; + GError *async_error; + + SoupMessageIOCompletionFn completion_cb; + gpointer completion_data; + +#ifdef HAVE_SYSPROF + gint64 begin_time_nsec; +#endif +} SoupMessageIOData; + +void soup_message_io_data_cleanup (SoupMessageIOData *io); + +gboolean soup_message_io_data_read_headers (SoupMessageIOData *io, + gboolean blocking, + GCancellable *cancellable, + GError **error); + +typedef gboolean (*SoupMessageIOSourceFunc) (GObject *msg, + gpointer user_data); + +GSource *soup_message_io_data_get_source (SoupMessageIOData *io, + GObject *msg, + GCancellable *cancellable, + SoupMessageIOSourceFunc callback, + gpointer user_data); + +void soup_message_io_data_pause (SoupMessageIOData *io); +void soup_message_io_data_unpause (SoupMessageIOData *io); + + +#endif /* __SOUP_MESSAGE_IO_DATA_H__ */ diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c index bfa14417..efa84ef6 100644 --- a/libsoup/soup-message-io.c +++ b/libsoup/soup-message-io.c @@ -28,60 +28,37 @@ #include "soup-misc.h" #include "soup-socket-private.h" +struct _SoupClientMessageIOData { + SoupMessageIOData base; + + SoupMessageQueueItem *item; + GCancellable *cancellable; + +#ifdef HAVE_SYSPROF + gint64 begin_time_nsec; +#endif +}; + #define RESPONSE_BLOCK_SIZE 8192 #define HEADER_SIZE_LIMIT (64 * 1024) void -soup_message_io_cleanup (SoupMessage *msg) +soup_client_message_io_data_free (SoupClientMessageIOData *io) { - SoupMessageIOData *io; - - io = soup_message_get_io_data (msg); if (!io) return; - if (io->io_source) { - g_source_destroy (io->io_source); - g_source_unref (io->io_source); - io->io_source = 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_cleanup (&io->base); + soup_message_queue_item_unref (io->item); - if (io->iostream) - g_object_unref (io->iostream); - if (io->body_istream) - g_object_unref (io->body_istream); - if (io->body_ostream) - g_object_unref (io->body_ostream); - if (io->async_context) - g_main_context_unref (io->async_context); - if (io->item) - soup_message_queue_item_unref (io->item); - - g_byte_array_free (io->read_header_buf, TRUE); - - g_string_free (io->write_buf, TRUE); - g_clear_pointer (&io->write_chunk, g_bytes_unref); - - if (io->async_wait) { - g_cancellable_cancel (io->async_wait); - g_clear_object (&io->async_wait); - } - g_clear_error (&io->async_error); - - g_slice_free (SoupMessageIOData, io); + g_slice_free (SoupClientMessageIOData, io); } void soup_message_io_finished (SoupMessage *msg) { - SoupMessageIOData *io; - SoupMessageCompletionFn completion_cb; + SoupClientMessageIOData *io; + SoupMessageIOCompletionFn completion_cb; gpointer completion_data; SoupMessageIOCompletion completion; @@ -89,11 +66,11 @@ soup_message_io_finished (SoupMessage *msg) if (!io) return; - completion_cb = io->completion_cb; - completion_data = io->completion_data; + completion_cb = io->base.completion_cb; + completion_data = io->base.completion_data; - if ((io->read_state >= SOUP_MESSAGE_IO_STATE_FINISHING && - io->write_state >= SOUP_MESSAGE_IO_STATE_FINISHING)) + 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; @@ -101,96 +78,35 @@ soup_message_io_finished (SoupMessage *msg) g_object_ref (msg); soup_message_set_io_data (msg, NULL); if (completion_cb) - completion_cb (msg, completion, completion_data); + completion_cb (G_OBJECT (msg), completion, completion_data); g_object_unref (msg); } GIOStream * soup_message_io_steal (SoupMessage *msg) { - SoupMessageIOData *io; - SoupMessageCompletionFn completion_cb; + SoupClientMessageIOData *io; + SoupMessageIOCompletionFn completion_cb; gpointer completion_data; GIOStream *iostream; io = soup_message_get_io_data (msg); - if (!io || !io->iostream) + if (!io || !io->base.iostream) return NULL; - iostream = g_object_ref (io->iostream); - completion_cb = io->completion_cb; - completion_data = io->completion_data; + iostream = g_object_ref (io->base.iostream); + completion_cb = io->base.completion_cb; + completion_data = io->base.completion_data; g_object_ref (msg); soup_message_set_io_data (msg, NULL); if (completion_cb) - completion_cb (msg, SOUP_MESSAGE_IO_STOLEN, completion_data); + completion_cb (G_OBJECT (msg), SOUP_MESSAGE_IO_STOLEN, completion_data); g_object_unref (msg); return iostream; } -gboolean -soup_message_io_read_headers (SoupMessage *msg, - SoupFilterInputStream *stream, - GByteArray *buffer, - gboolean blocking, - GCancellable *cancellable, - GError **error) -{ - gssize nread, old_len; - gboolean got_lf; - - while (1) { - old_len = buffer->len; - g_byte_array_set_size (buffer, old_len + RESPONSE_BLOCK_SIZE); - nread = soup_filter_input_stream_read_line (stream, - buffer->data + old_len, - RESPONSE_BLOCK_SIZE, - blocking, - &got_lf, - cancellable, error); - buffer->len = old_len + MAX (nread, 0); - if (nread == 0) { - if (buffer->len > 0) - break; - soup_message_set_status (msg, SOUP_STATUS_MALFORMED); - g_set_error_literal (error, G_IO_ERROR, - G_IO_ERROR_PARTIAL_INPUT, - _("Connection terminated unexpectedly")); - } - if (nread <= 0) - return FALSE; - - if (got_lf) { - if (nread == 1 && old_len >= 2 && - !strncmp ((char *)buffer->data + - buffer->len - 2, - "\n\n", 2)) { - buffer->len--; - break; - } else if (nread == 2 && old_len >= 3 && - !strncmp ((char *)buffer->data + - buffer->len - 3, - "\n\r\n", 3)) { - buffer->len -= 2; - break; - } - } - - if (buffer->len > HEADER_SIZE_LIMIT) { - soup_message_set_status (msg, SOUP_STATUS_MALFORMED); - g_set_error_literal (error, G_IO_ERROR, - G_IO_ERROR_PARTIAL_INPUT, - _("Header too big")); - return FALSE; - } - } - - buffer->data[buffer->len] = '\0'; - return TRUE; -} - static gint processing_stage_cmp (gconstpointer a, gconstpointer b) @@ -257,7 +173,7 @@ request_body_stream_wrote_cb (GOutputStream *ostream, GAsyncResult *result, SoupMessage *msg) { - SoupMessageIOData *io; + SoupClientMessageIOData *io; gssize nwrote; GCancellable *async_wait; GError *error = NULL; @@ -265,19 +181,19 @@ request_body_stream_wrote_cb (GOutputStream *ostream, nwrote = g_output_stream_splice_finish (ostream, result, &error); io = soup_message_get_io_data (msg); - if (!io || !io->async_wait || io->body_ostream != ostream) { + if (!io || !io->base.async_wait || io->base.body_ostream != ostream) { g_clear_error (&error); g_object_unref (msg); return; } if (nwrote != -1) - io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; + io->base.write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; if (error) - g_propagate_error (&io->async_error, error); - async_wait = io->async_wait; - io->async_wait = NULL; + g_propagate_error (&io->base.async_error, error); + async_wait = io->base.async_wait; + io->base.async_wait = NULL; g_cancellable_cancel (async_wait); g_object_unref (async_wait); @@ -291,20 +207,20 @@ closed_async (GObject *source, { GOutputStream *body_ostream = G_OUTPUT_STREAM (source); SoupMessage *msg = user_data; - SoupMessageIOData *io; + SoupClientMessageIOData *io; GCancellable *async_wait; io = soup_message_get_io_data (msg); - if (!io || !io->async_wait || io->body_ostream != body_ostream) { + 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); @@ -393,15 +309,6 @@ write_headers (SoupMessage *msg, g_free (uri_host); *encoding = soup_message_headers_get_encoding (msg->request_headers); - if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH || - *encoding == SOUP_ENCODING_NONE) && - (msg->request_body->length > 0 || - soup_message_headers_get_one (msg->request_headers, "Content-Type")) && - !soup_message_headers_get_content_length (msg->request_headers)) { - *encoding = SOUP_ENCODING_CONTENT_LENGTH; - soup_message_headers_set_content_length (msg->request_headers, - msg->request_body->length); - } soup_message_headers_iter_init (&iter, msg->request_headers); while (soup_message_headers_iter_next (&iter, &name, &value)) @@ -419,7 +326,8 @@ static gboolean io_write (SoupMessage *msg, gboolean blocking, GCancellable *cancellable, GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *client_io = soup_message_get_io_data (msg); + SoupMessageIOData *io = &client_io->base; gssize nwrote; if (io->async_error) { @@ -436,7 +344,7 @@ io_write (SoupMessage *msg, gboolean blocking, switch (io->write_state) { case SOUP_MESSAGE_IO_STATE_HEADERS: if (!io->write_buf->len) - write_headers (msg, io->write_buf, io->item->conn, &io->write_encoding); + write_headers (msg, io->write_buf, client_io->item->conn, &io->write_encoding); while (io->written < io->write_buf->len) { nwrote = g_pollable_stream_write (io->ostream, @@ -607,13 +515,17 @@ static gboolean io_read (SoupMessage *msg, gboolean blocking, GCancellable *cancellable, GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *client_io = soup_message_get_io_data (msg); + SoupMessageIOData *io = &client_io->base; guint status; switch (io->read_state) { case SOUP_MESSAGE_IO_STATE_HEADERS: - if (!soup_message_io_read_headers (msg, io->istream, io->read_header_buf, blocking, cancellable, error)) + if (!soup_message_io_data_read_headers (io, blocking, cancellable, error)) { + if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT)) + soup_message_set_status (msg, SOUP_STATUS_MALFORMED); return FALSE; + } status = parse_headers (msg, (char *)io->read_header_buf->data, @@ -658,7 +570,7 @@ io_read (SoupMessage *msg, gboolean blocking, /* If this was "101 Switching Protocols", then * the session may have stolen the connection... */ - if (io != soup_message_get_io_data (msg)) + if (client_io != soup_message_get_io_data (msg)) return FALSE; soup_message_cleanup_response (msg); @@ -698,7 +610,7 @@ io_read (SoupMessage *msg, gboolean blocking, io->read_length); io->body_istream = soup_message_setup_body_istream (body_istream, msg, - io->item->session, + client_io->item->session, SOUP_STAGE_MESSAGE_BODY); g_object_unref (body_istream); } @@ -753,148 +665,18 @@ io_read (SoupMessage *msg, gboolean blocking, return TRUE; } -typedef struct { - GSource source; - SoupMessage *msg; - gboolean paused; -} SoupMessageSource; - -static gboolean -message_source_check (GSource *source) -{ - SoupMessageSource *message_source = (SoupMessageSource *)source; - - if (message_source->paused) { - SoupMessageIOData *io = soup_message_get_io_data (message_source->msg); - - if (io && io->paused) - return FALSE; - else - return TRUE; - } else - return FALSE; -} - -static gboolean -message_source_prepare (GSource *source, - gint *timeout) -{ - *timeout = -1; - return message_source_check (source); -} - -static gboolean -message_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - SoupMessageSourceFunc func = (SoupMessageSourceFunc)callback; - SoupMessageSource *message_source = (SoupMessageSource *)source; - - return (*func) (message_source->msg, user_data); -} - -static void -message_source_finalize (GSource *source) -{ - SoupMessageSource *message_source = (SoupMessageSource *)source; - - g_object_unref (message_source->msg); -} - -static gboolean -message_source_closure_callback (SoupMessage *msg, - gpointer data) -{ - GClosure *closure = data; - GValue param = G_VALUE_INIT; - GValue result_value = G_VALUE_INIT; - gboolean result; - - g_value_init (&result_value, G_TYPE_BOOLEAN); - - g_value_init (¶m, SOUP_TYPE_MESSAGE); - g_value_set_object (¶m, msg); - - g_closure_invoke (closure, &result_value, 1, ¶m, NULL); - - result = g_value_get_boolean (&result_value); - g_value_unset (&result_value); - g_value_unset (¶m); - - return result; -} - -static GSourceFuncs message_source_funcs = -{ - message_source_prepare, - message_source_check, - message_source_dispatch, - message_source_finalize, - (GSourceFunc)message_source_closure_callback, - (GSourceDummyMarshal)g_cclosure_marshal_generic, -}; - -GSource * -soup_message_io_get_source (SoupMessage *msg, GCancellable *cancellable, - SoupMessageSourceFunc callback, gpointer user_data) -{ - SoupMessageIOData *io = soup_message_get_io_data (msg); - GSource *base_source, *source; - SoupMessageSource *message_source; - - if (!io) { - base_source = g_timeout_source_new (0); - } else if (io->paused) { - base_source = NULL; - } else if (io->async_wait) { - base_source = g_cancellable_source_new (io->async_wait); - } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) { - GPollableInputStream *istream; - - if (io->body_istream) - istream = G_POLLABLE_INPUT_STREAM (io->body_istream); - else - istream = G_POLLABLE_INPUT_STREAM (io->istream); - base_source = g_pollable_input_stream_create_source (istream, cancellable); - } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state)) { - GPollableOutputStream *ostream; - - if (io->body_ostream) - ostream = G_POLLABLE_OUTPUT_STREAM (io->body_ostream); - else - ostream = G_POLLABLE_OUTPUT_STREAM (io->ostream); - base_source = g_pollable_output_stream_create_source (ostream, cancellable); - } else - base_source = g_timeout_source_new (0); - - source = g_source_new (&message_source_funcs, - sizeof (SoupMessageSource)); - g_source_set_name (source, "SoupMessageSource"); - message_source = (SoupMessageSource *)source; - message_source->msg = g_object_ref (msg); - message_source->paused = io && io->paused; - - if (base_source) { - g_source_set_dummy_callback (base_source); - g_source_add_child_source (source, base_source); - g_source_unref (base_source); - } - g_source_set_callback (source, (GSourceFunc) callback, user_data, NULL); - return source; -} - static gboolean request_is_restartable (SoupMessage *msg, GError *error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *client_io = soup_message_get_io_data (msg); + SoupMessageIOData *io = &client_io->base; - if (!io) + if (!client_io) return FALSE; return (io->read_state <= SOUP_MESSAGE_IO_STATE_HEADERS && io->read_header_buf->len == 0 && - soup_connection_get_ever_used (io->item->conn) && + soup_connection_get_ever_used (client_io->item->conn) && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) && error->domain != G_TLS_ERROR && @@ -906,7 +688,8 @@ io_run_until (SoupMessage *msg, gboolean blocking, SoupMessageIOState read_state, SoupMessageIOState write_state, GCancellable *cancellable, GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *client_io = soup_message_get_io_data (msg); + SoupMessageIOData *io = &client_io->base; gboolean progress = TRUE, done; GError *my_error = NULL; @@ -921,7 +704,7 @@ io_run_until (SoupMessage *msg, gboolean blocking, g_object_ref (msg); - while (progress && soup_message_get_io_data (msg) == io && !io->paused && !io->async_wait && + while (progress && soup_message_get_io_data (msg) == client_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)) @@ -945,7 +728,7 @@ io_run_until (SoupMessage *msg, gboolean blocking, g_propagate_error (error, my_error); g_object_unref (msg); return FALSE; - } else if (soup_message_get_io_data (msg) != io) { + } else if (soup_message_get_io_data (msg) != client_io) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled")); @@ -979,7 +762,8 @@ io_run_until (SoupMessage *msg, gboolean blocking, /* FIXME: Expand and generalise sysprof support: * https://gitlab.gnome.org/GNOME/sysprof/-/issues/43 */ - sysprof_collector_mark_printf (io->begin_time_nsec, SYSPROF_CAPTURE_CURRENT_TIME - io->begin_time_nsec, + sysprof_collector_mark_printf (client_io->begin_time_nsec, + SYSPROF_CAPTURE_CURRENT_TIME - client_io->begin_time_nsec, "libsoup", "message", "%s request/response to %s: " "read %" G_GOFFSET_FORMAT "B, " @@ -1003,7 +787,7 @@ soup_message_io_update_status (SoupMessage *msg, GError *error) { if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *io = soup_message_get_io_data (msg); io->item->state = SOUP_MESSAGE_RESTARTING; } else if (error->domain == G_TLS_ERROR) { @@ -1029,7 +813,8 @@ void soup_message_io_run (SoupMessage *msg, gboolean blocking) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *client_io = soup_message_get_io_data (msg); + SoupMessageIOData *io = &client_io->base; GError *error = NULL; GCancellable *cancellable; @@ -1040,7 +825,7 @@ soup_message_io_run (SoupMessage *msg, } g_object_ref (msg); - cancellable = io->cancellable ? g_object_ref (io->cancellable) : NULL; + cancellable = client_io->cancellable ? g_object_ref (client_io->cancellable) : NULL; if (io_run_until (msg, blocking, SOUP_MESSAGE_IO_STATE_DONE, @@ -1049,10 +834,12 @@ soup_message_io_run (SoupMessage *msg, soup_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_message_get_io_data (msg) == client_io) soup_message_io_update_status (msg, error); g_error_free (error); @@ -1067,7 +854,7 @@ soup_message_io_run_until_read (SoupMessage *msg, GCancellable *cancellable, GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *io = soup_message_get_io_data (msg); if (io_run_until (msg, TRUE, SOUP_MESSAGE_IO_STATE_BODY, @@ -1098,7 +885,8 @@ static void io_run_until_read_async (SoupMessage *msg, GTask *task) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *client_io = soup_message_get_io_data (msg); + SoupMessageIOData *io = &client_io->base; GError *error = NULL; if (io->io_source) { @@ -1119,12 +907,14 @@ io_run_until_read_async (SoupMessage *msg, if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_error_free (error); - io->io_source = soup_message_io_get_source (msg, NULL, io_run_until_read_ready, task); + io->io_source = soup_message_io_data_get_source (io, G_OBJECT (msg), NULL, + (SoupMessageIOSourceFunc)io_run_until_read_ready, + task); g_source_attach (io->io_source, io->async_context); return; } - if (soup_message_get_io_data (msg) == io) + if (soup_message_get_io_data (msg) == client_io) soup_message_io_update_status (msg, error); g_task_return_error (task, error); @@ -1157,14 +947,14 @@ soup_message_io_run_until_finish (SoupMessage *msg, GCancellable *cancellable, GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *io = soup_message_get_io_data (msg); gboolean success; g_object_ref (msg); if (io) { - if (io->read_state < SOUP_MESSAGE_IO_STATE_BODY_DONE) - io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; + if (io->base.read_state < SOUP_MESSAGE_IO_STATE_BODY_DONE) + io->base.read_state = SOUP_MESSAGE_IO_STATE_FINISHING; } success = io_run_until (msg, blocking, @@ -1180,17 +970,17 @@ static void client_stream_eof (SoupClientInputStream *stream, gpointer user_data) { SoupMessage *msg = user_data; - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *io = soup_message_get_io_data (msg); - if (io && io->read_state == SOUP_MESSAGE_IO_STATE_BODY) - io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; + if (io && io->base.read_state == SOUP_MESSAGE_IO_STATE_BODY) + io->base.read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; } GInputStream * soup_message_io_get_response_istream (SoupMessage *msg, GError **error) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *io = soup_message_get_io_data (msg); GInputStream *client_stream; if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) { @@ -1199,7 +989,7 @@ soup_message_io_get_response_istream (SoupMessage *msg, return NULL; } - client_stream = soup_client_input_stream_new (io->body_istream, msg); + client_stream = soup_client_input_stream_new (io->base.body_istream, msg); g_signal_connect (client_stream, "eof", G_CALLBACK (client_stream_eof), msg); @@ -1208,28 +998,28 @@ soup_message_io_get_response_istream (SoupMessage *msg, void soup_message_send_request (SoupMessageQueueItem *item, - SoupMessageCompletionFn completion_cb, + SoupMessageIOCompletionFn completion_cb, gpointer user_data) { - SoupMessageIOData *io; + SoupClientMessageIOData *io; - io = g_slice_new0 (SoupMessageIOData); - io->completion_cb = completion_cb; - io->completion_data = user_data; + io = g_slice_new0 (SoupClientMessageIOData); + io->base.completion_cb = completion_cb; + io->base.completion_data = user_data; io->item = item; soup_message_queue_item_ref (item); io->cancellable = io->item->cancellable; - io->iostream = g_object_ref (soup_socket_get_iostream (soup_connection_get_socket (io->item->conn))); - 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 (); + io->base.iostream = g_object_ref (soup_socket_get_iostream (soup_connection_get_socket (io->item->conn))); + 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_NOT_STARTED; - io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; + io->base.read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED; + io->base.write_state = SOUP_MESSAGE_IO_STATE_HEADERS; #ifdef HAVE_SYSPROF io->begin_time_nsec = SYSPROF_CAPTURE_CURRENT_TIME; @@ -1238,65 +1028,25 @@ soup_message_send_request (SoupMessageQueueItem *item, soup_message_set_io_data (io->item->msg, io); } -void +void soup_message_io_pause (SoupMessage *msg) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *io = soup_message_get_io_data (msg); g_return_if_fail (io != NULL); + g_return_if_fail (io->base.read_state < SOUP_MESSAGE_IO_STATE_BODY); - if (io->item) - g_return_if_fail (io->read_state < SOUP_MESSAGE_IO_STATE_BODY); - - if (io->io_source) { - g_source_destroy (io->io_source); - g_source_unref (io->io_source); - io->io_source = NULL; - } - - if (io->unpause_source) { - g_source_destroy (io->unpause_source); - g_source_unref (io->unpause_source); - io->unpause_source = NULL; - } - - io->paused = TRUE; -} - -static gboolean -io_unpause_internal (gpointer msg) -{ - SoupMessageIOData *io = soup_message_get_io_data (msg); - - g_return_val_if_fail (io != NULL, FALSE); - - g_clear_pointer (&io->unpause_source, g_source_unref); - io->paused = FALSE; - - if (io->io_source) - return FALSE; - - soup_message_io_run (msg, FALSE); - return FALSE; + soup_message_io_data_pause (&io->base); } void soup_message_io_unpause (SoupMessage *msg) { - SoupMessageIOData *io = soup_message_get_io_data (msg); + SoupClientMessageIOData *io = soup_message_get_io_data (msg); g_return_if_fail (io != NULL); - - if (io->item) { - g_return_if_fail (io->read_state < SOUP_MESSAGE_IO_STATE_BODY); - io->paused = FALSE; - return; - } - - if (!io->unpause_source) { - io->unpause_source = soup_add_completion_reffed (io->async_context, - io_unpause_internal, msg, NULL); - } + g_return_if_fail (io->base.read_state < SOUP_MESSAGE_IO_STATE_BODY); + io->base.paused = FALSE; } /** @@ -1312,3 +1062,11 @@ soup_message_io_in_progress (SoupMessage *msg) { return soup_message_get_io_data (msg) != NULL; } + +gboolean +soup_message_is_io_paused (SoupMessage *msg) +{ + SoupClientMessageIOData *io = soup_message_get_io_data (msg); + + return io && io->base.paused; +} diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h index 1200adba..0ce07e8f 100644 --- a/libsoup/soup-message-private.h +++ b/libsoup/soup-message-private.h @@ -8,13 +8,17 @@ #include "soup-filter-input-stream.h" #include "soup-message.h" +#include "soup-message-io-data.h" #include "auth/soup-auth.h" #include "content-sniffer/soup-content-processor.h" #include "content-sniffer/soup-content-sniffer.h" #include "soup-session.h" +typedef struct _SoupClientMessageIOData SoupClientMessageIOData; +void soup_client_message_io_data_free (SoupClientMessageIOData *io); + typedef struct { - gpointer io_data; + SoupClientMessageIOData *io_data; guint msg_flags; gboolean server_side; @@ -46,12 +50,6 @@ typedef struct { void soup_message_cleanup_response (SoupMessage *msg); -typedef enum { - SOUP_MESSAGE_IO_COMPLETE, - SOUP_MESSAGE_IO_INTERRUPTED, - SOUP_MESSAGE_IO_STOLEN -} SoupMessageIOCompletion; - typedef void (*SoupMessageGetHeadersFn) (SoupMessage *msg, GString *headers, SoupEncoding *encoding, @@ -62,17 +60,9 @@ typedef guint (*SoupMessageParseHeadersFn)(SoupMessage *msg, SoupEncoding *encoding, gpointer user_data, GError **error); -typedef void (*SoupMessageCompletionFn) (SoupMessage *msg, - SoupMessageIOCompletion completion, - gpointer user_data); - void soup_message_send_request (SoupMessageQueueItem *item, - SoupMessageCompletionFn completion_cb, - gpointer user_data); -void soup_message_read_request (SoupMessage *msg, - SoupSocket *sock, - SoupMessageCompletionFn completion_cb, + SoupMessageIOCompletionFn completion_cb, gpointer user_data); /* Auth handling */ @@ -84,77 +74,13 @@ void soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *soup_message_get_proxy_auth (SoupMessage *msg); /* I/O */ -typedef enum { - SOUP_MESSAGE_IO_STATE_NOT_STARTED, - SOUP_MESSAGE_IO_STATE_ANY = SOUP_MESSAGE_IO_STATE_NOT_STARTED, - SOUP_MESSAGE_IO_STATE_HEADERS, - SOUP_MESSAGE_IO_STATE_BLOCKING, - SOUP_MESSAGE_IO_STATE_BODY_START, - SOUP_MESSAGE_IO_STATE_BODY, - SOUP_MESSAGE_IO_STATE_BODY_DATA, - SOUP_MESSAGE_IO_STATE_BODY_FLUSH, - SOUP_MESSAGE_IO_STATE_BODY_DONE, - SOUP_MESSAGE_IO_STATE_FINISHING, - SOUP_MESSAGE_IO_STATE_DONE -} SoupMessageIOState; - -#define SOUP_MESSAGE_IO_STATE_ACTIVE(state) \ - (state != SOUP_MESSAGE_IO_STATE_NOT_STARTED && \ - state != SOUP_MESSAGE_IO_STATE_BLOCKING && \ - state != SOUP_MESSAGE_IO_STATE_DONE) -#define SOUP_MESSAGE_IO_STATE_POLLABLE(state) \ - (SOUP_MESSAGE_IO_STATE_ACTIVE (state) && \ - state != SOUP_MESSAGE_IO_STATE_BODY_DONE) - -typedef struct { - /* Client only */ - SoupMessageQueueItem *item; - GCancellable *cancellable; - - /* Server only */ - SoupSocket *sock; - - GIOStream *iostream; - SoupFilterInputStream *istream; - GInputStream *body_istream; - GOutputStream *ostream; - GOutputStream *body_ostream; - GMainContext *async_context; - - SoupMessageIOState read_state; - SoupEncoding read_encoding; - GByteArray *read_header_buf; - goffset read_length; - - SoupMessageIOState write_state; - SoupEncoding write_encoding; - GString *write_buf; - GBytes *write_chunk; - goffset write_body_offset; - goffset write_length; - goffset written; - - GSource *io_source; - GSource *unpause_source; - gboolean paused; - - GCancellable *async_wait; - GError *async_error; - - SoupMessageCompletionFn completion_cb; - gpointer completion_data; - -#ifdef HAVE_SYSPROF - gint64 begin_time_nsec; -#endif -} SoupMessageIOData; - void soup_message_io_run (SoupMessage *msg, gboolean blocking); void soup_message_io_finished (SoupMessage *msg); void soup_message_io_cleanup (SoupMessage *msg); void soup_message_io_pause (SoupMessage *msg); void soup_message_io_unpause (SoupMessage *msg); +gboolean soup_message_is_io_paused (SoupMessage *msg); gboolean soup_message_io_in_progress (SoupMessage *msg); GIOStream *soup_message_io_steal (SoupMessage *msg); @@ -214,9 +140,9 @@ SoupConnection *soup_message_get_connection (SoupMessage *msg); void soup_message_set_connection (SoupMessage *msg, SoupConnection *conn); -gpointer soup_message_get_io_data (SoupMessage *msg); -void soup_message_set_io_data (SoupMessage *msg, - gpointer io); +SoupClientMessageIOData *soup_message_get_io_data (SoupMessage *msg); +void soup_message_set_io_data (SoupMessage *msg, + SoupClientMessageIOData *io); SoupContentSniffer *soup_message_get_content_sniffer (SoupMessage *msg); void soup_message_set_content_sniffer (SoupMessage *msg, @@ -224,7 +150,4 @@ void soup_message_set_content_sniffer (SoupMessage *msg void soup_message_set_bytes_for_sniffing (SoupMessage *msg, gsize bytes); -const char *soup_http_version_to_string (SoupHTTPVersion version); - - #endif /* __SOUP_MESSAGE_PRIVATE_H__ */ diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c index 5ead16d9..0a265dea 100644 --- a/libsoup/soup-message.c +++ b/libsoup/soup-message.c @@ -19,13 +19,13 @@ /** * SECTION:soup-message * @short_description: An HTTP request and response. - * @see_also: #SoupMessageHeaders, #SoupMessageBody + * @see_also: #SoupMessageHeaders * * A #SoupMessage represents an HTTP message that is being sent or * received. * - * For client-side usage, you would create a #SoupMessage with - * soup_message_new() or soup_message_new_from_uri(), set up its + * You would create a #SoupMessage with soup_message_new() or + * soup_message_new_from_uri(), set up its * fields appropriately, and send it. If you are using the newer * #SoupRequest API, you would create a request with * soup_session_request_http() or soup_session_request_http_uri(), and @@ -33,10 +33,6 @@ * #SoupMessage that you can retrieve via * soup_request_http_get_message(). * - * For server-side usage, #SoupServer will create #SoupMessage<!-- - * -->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 @@ -49,9 +45,7 @@ * @method: the HTTP method * @status_code: the HTTP status code * @reason_phrase: the status phrase associated with @status_code - * @request_body: the request body * @request_headers: the request headers - * @response_body: the response body * @response_headers: the response headers * * Represents an HTTP message being sent or received. @@ -66,48 +60,17 @@ * messages. Rather, you should look at @status_code, and determine an * end-user-appropriate message based on that and on what you were * trying to do. - * - * As described in the #SoupMessageBody documentation, the - * @request_body and @response_body <literal>data</literal> fields - * will not necessarily be filled in at all times. When the body - * fields are filled in, they will be terminated with a '\0' byte - * (which is not included in the <literal>length</literal>), so you - * can use them as ordinary C strings (assuming that you know that the - * body doesn't have any other '\0' bytes). - * - * For a client-side #SoupMessage, @request_body's - * <literal>data</literal> is usually filled in right before libsoup - * writes the request to the network, but you should not count on - * this; use soup_message_body_flatten() if you want to ensure that - * <literal>data</literal> is filled in. If you are not using - * #SoupRequest to read the response, then @response_body's - * <literal>data</literal> will be filled in before - * #SoupMessage::finished is emitted. (If you are using #SoupRequest, - * then the message body is not accumulated by default, so - * @response_body's <literal>data</literal> will always be %NULL.) - * - * For a server-side #SoupMessage, @request_body's %data will be - * filled in before #SoupMessage::got_body is emitted. - * - * To prevent the %data field from being filled in at all (eg, if you - * are handling the data from a #SoupMessage::got_chunk, and so don't - * need to see it all at the end), call - * soup_message_body_set_accumulate() on @response_body or - * @request_body as appropriate, passing %FALSE. - **/ + */ G_DEFINE_TYPE_WITH_PRIVATE (SoupMessage, soup_message, G_TYPE_OBJECT) enum { - WROTE_INFORMATIONAL, WROTE_HEADERS, - WROTE_CHUNK, WROTE_BODY_DATA, WROTE_BODY, GOT_INFORMATIONAL, GOT_HEADERS, - GOT_CHUNK, GOT_BODY, CONTENT_SNIFFED, @@ -129,15 +92,10 @@ enum { PROP_URI, PROP_HTTP_VERSION, PROP_FLAGS, - PROP_SERVER_SIDE, PROP_STATUS_CODE, PROP_REASON_PHRASE, PROP_FIRST_PARTY, - PROP_REQUEST_BODY, - PROP_REQUEST_BODY_DATA, PROP_REQUEST_HEADERS, - PROP_RESPONSE_BODY, - PROP_RESPONSE_BODY_DATA, PROP_RESPONSE_HEADERS, PROP_TLS_CERTIFICATE, PROP_TLS_ERRORS, @@ -156,9 +114,7 @@ soup_message_init (SoupMessage *msg) priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1; priv->priority = SOUP_MESSAGE_PRIORITY_NORMAL; - 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); } @@ -168,7 +124,7 @@ soup_message_finalize (GObject *object) SoupMessage *msg = SOUP_MESSAGE (object); SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - soup_message_io_cleanup (msg); + soup_client_message_io_data_free (priv->io_data); g_clear_pointer (&priv->uri, soup_uri_free); g_clear_pointer (&priv->first_party, soup_uri_free); @@ -181,11 +137,9 @@ soup_message_finalize (GObject *object) g_clear_object (&priv->tls_certificate); - soup_message_body_free (msg->request_body); soup_message_headers_free (msg->request_headers); - g_clear_object (&msg->request_body_stream); - soup_message_body_free (msg->response_body); soup_message_headers_free (msg->response_headers); + g_clear_object (&msg->request_body_stream); g_free (msg->reason_phrase); @@ -218,13 +172,6 @@ soup_message_set_property (GObject *object, guint prop_id, case PROP_FLAGS: soup_message_set_flags (msg, g_value_get_flags (value)); break; - case PROP_SERVER_SIDE: - priv->server_side = g_value_get_boolean (value); - if (priv->server_side) { - soup_message_headers_set_encoding (msg->response_headers, - SOUP_ENCODING_CONTENT_LENGTH); - } - break; case PROP_STATUS_CODE: soup_message_set_status (msg, g_value_get_uint (value)); break; @@ -266,7 +213,6 @@ soup_message_get_property (GObject *object, guint prop_id, { SoupMessage *msg = SOUP_MESSAGE (object); SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - GBytes *buf; switch (prop_id) { case PROP_METHOD: @@ -287,9 +233,6 @@ soup_message_get_property (GObject *object, guint prop_id, case PROP_FLAGS: g_value_set_flags (value, priv->msg_flags); break; - case PROP_SERVER_SIDE: - g_value_set_boolean (value, priv->server_side); - break; case PROP_STATUS_CODE: g_value_set_uint (value, msg->status_code); break; @@ -299,23 +242,9 @@ soup_message_get_property (GObject *object, guint prop_id, case PROP_FIRST_PARTY: g_value_set_boxed (value, priv->first_party); break; - case PROP_REQUEST_BODY: - g_value_set_boxed (value, msg->request_body); - break; - case PROP_REQUEST_BODY_DATA: - buf = soup_message_body_flatten (msg->request_body); - g_value_take_boxed (value, buf); - break; case PROP_REQUEST_HEADERS: g_value_set_boxed (value, msg->request_headers); break; - case PROP_RESPONSE_BODY: - g_value_set_boxed (value, msg->response_body); - break; - case PROP_RESPONSE_BODY_DATA: - buf = soup_message_body_flatten (msg->response_body); - g_value_take_boxed (value, buf); - break; case PROP_RESPONSE_HEADERS: g_value_set_boxed (value, msg->response_headers); break; @@ -335,26 +264,10 @@ soup_message_get_property (GObject *object, guint prop_id, } static void -soup_message_real_got_body (SoupMessage *msg) -{ - SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - SoupMessageBody *body; - - body = priv->server_side ? msg->request_body : msg->response_body; - if (soup_message_body_get_accumulate (body)) { - GBytes *buffer = soup_message_body_flatten (body); - g_bytes_unref (buffer); - } -} - -static void soup_message_class_init (SoupMessageClass *message_class) { GObjectClass *object_class = G_OBJECT_CLASS (message_class); - /* virtual method definition */ - message_class->got_body = soup_message_real_got_body; - /* virtual method override */ object_class->finalize = soup_message_finalize; object_class->set_property = soup_message_set_property; @@ -363,29 +276,11 @@ soup_message_class_init (SoupMessageClass *message_class) /* signals */ /** - * SoupMessage::wrote-informational: - * @msg: the message - * - * Emitted immediately after writing a 1xx (Informational) - * response for a (server-side) message. - **/ - signals[WROTE_INFORMATIONAL] = - g_signal_new ("wrote_informational", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (SoupMessageClass, wrote_informational), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** * SoupMessage::wrote-headers: * @msg: the message * - * Emitted immediately after writing the headers for a - * message. (For a client-side message, this is after writing - * the request headers; for a server-side message, it is after - * writing the response headers.) + * Emitted immediately after writing the request headers for a + * message. **/ signals[WROTE_HEADERS] = g_signal_new ("wrote_headers", @@ -397,28 +292,6 @@ soup_message_class_init (SoupMessageClass *message_class) G_TYPE_NONE, 0); /** - * SoupMessage::wrote-chunk: - * @msg: the message - * - * Emitted immediately after writing a body chunk for a message. - * - * Note that this signal is not parallel to - * #SoupMessage::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 - * #SoupMessage::wrote_body_data. - **/ - signals[WROTE_CHUNK] = - g_signal_new ("wrote_chunk", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk), - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** * SoupMessage::wrote-body-data: * @msg: the message * @chunk: the data written @@ -426,10 +299,6 @@ soup_message_class_init (SoupMessageClass *message_class) * Emitted immediately after writing a portion of the message * body to the network. * - * Unlike #SoupMessage::wrote_chunk, this is emitted after - * every successful write() call, not only after finishing a - * complete "chunk". - * * Since: 2.24 **/ signals[WROTE_BODY_DATA] = @@ -447,11 +316,7 @@ soup_message_class_init (SoupMessageClass *message_class) * @msg: the message * * Emitted immediately after writing the complete body for a - * message. (For a client-side message, this means that - * libsoup is done writing and is now waiting for the response - * from the server. For a server-side message, this means that - * libsoup has finished writing the response and is nearly - * done with the message.) + * message. **/ signals[WROTE_BODY] = g_signal_new ("wrote_body", @@ -489,11 +354,7 @@ soup_message_class_init (SoupMessageClass *message_class) * SoupMessage::got-headers: * @msg: the message * - * Emitted after receiving all message headers for a message. - * (For a client-side message, this is after receiving the - * Status-Line and response headers; for a server-side - * message, it is after receiving the Request-Line and request - * headers.) + * Emitted after receiving the Status-Line and response headers. * * See also soup_message_add_header_handler() and * soup_message_add_status_code_handler(), which can be used @@ -518,37 +379,10 @@ soup_message_class_init (SoupMessageClass *message_class) G_TYPE_NONE, 0); /** - * SoupMessage::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. - * - * If you cancel or requeue @msg while processing this signal, - * then the current HTTP I/O will be stopped after this signal - * emission finished, and @msg's connection will be closed. - **/ - signals[GOT_CHUNK] = - g_signal_new ("got_chunk", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (SoupMessageClass, got_chunk), - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - G_TYPE_BYTES); - - /** * SoupMessage::got-body: * @msg: the message * - * Emitted after receiving the complete message body. (For a - * server-side message, this means it has received the request - * body. For a client-side message, this means it has received - * the response body and is nearly done with the message.) + * Emitted after receiving the complete message request body. * * See also soup_message_add_header_handler() and * soup_message_add_status_code_handler(), which can be used @@ -642,8 +476,7 @@ soup_message_class_init (SoupMessageClass *message_class) * @msg: the message * * Emitted when all HTTP processing is finished for a message. - * (After #SoupMessage::got_body for client-side messages, or - * after #SoupMessage::wrote_body for server-side messages.) + * (After #SoupMessage::got_body). **/ signals[FINISHED] = g_signal_new ("finished", @@ -746,20 +579,6 @@ soup_message_class_init (SoupMessageClass *message_class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * SOUP_MESSAGE_SERVER_SIDE: - * - * Alias for the #SoupMessage:server-side property. (%TRUE if - * the message was created by #SoupServer.) - **/ - g_object_class_install_property ( - object_class, PROP_SERVER_SIDE, - g_param_spec_boolean (SOUP_MESSAGE_SERVER_SIDE, - "Server-side", - "Whether or not the message is server-side rather than client-side", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - /** * SOUP_MESSAGE_STATUS_CODE: * * Alias for the #SoupMessage:status-code property. (The @@ -841,43 +660,6 @@ soup_message_class_init (SoupMessageClass *message_class) FALSE, G_PARAM_READWRITE)); /** - * SOUP_MESSAGE_REQUEST_BODY: - * - * Alias for the #SoupMessage:request-body property. (The - * message's HTTP request body.) - **/ - g_object_class_install_property ( - object_class, PROP_REQUEST_BODY, - g_param_spec_boxed (SOUP_MESSAGE_REQUEST_BODY, - "Request Body", - "The HTTP request content", - SOUP_TYPE_MESSAGE_BODY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - /** - * SOUP_MESSAGE_REQUEST_BODY_DATA: - * - * Alias for the #SoupMessage:request-body-data property. (The - * message's HTTP request body, as a #GBytes.) - * - * Since: 2.46 - **/ - /** - * SoupMessage:request-body-data: - * - * The message's HTTP request body, as a #GBytes. - * - * Since: 2.46 - **/ - g_object_class_install_property ( - object_class, PROP_REQUEST_BODY_DATA, - g_param_spec_boxed (SOUP_MESSAGE_REQUEST_BODY_DATA, - "Request Body Data", - "The HTTP request body", - G_TYPE_BYTES, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - /** * SOUP_MESSAGE_REQUEST_HEADERS: * * Alias for the #SoupMessage:request-headers property. (The @@ -892,43 +674,6 @@ soup_message_class_init (SoupMessageClass *message_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** - * SOUP_MESSAGE_RESPONSE_BODY: - * - * Alias for the #SoupMessage:response-body property. (The - * message's HTTP response body.) - **/ - g_object_class_install_property ( - object_class, PROP_RESPONSE_BODY, - g_param_spec_boxed (SOUP_MESSAGE_RESPONSE_BODY, - "Response Body", - "The HTTP response content", - SOUP_TYPE_MESSAGE_BODY, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - /** - * SOUP_MESSAGE_RESPONSE_BODY_DATA: - * - * Alias for the #SoupMessage:response-body-data property. (The - * message's HTTP response body, as a #GBytes.) - * - * Since: 2.46 - **/ - /** - * SoupMessage:response-body-data: - * - * The message's HTTP response body, as a #GBytes. - * - * Since: 2.46 - **/ - g_object_class_install_property ( - object_class, PROP_RESPONSE_BODY_DATA, - g_param_spec_boxed (SOUP_MESSAGE_RESPONSE_BODY_DATA, - "Response Body Data", - "The HTTP response body", - G_TYPE_BYTES, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - /** * SOUP_MESSAGE_RESPONSE_HEADERS: * * Alias for the #SoupMessage:response-headers property. (The @@ -1059,42 +804,6 @@ soup_message_new_from_uri (const char *method, SoupURI *uri) } /** - * soup_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 #SoupMessage. If - * @content_type is %NULL, the response body must be empty as well. - */ -void -soup_message_set_response (SoupMessage *msg, - const char *content_type, - SoupMemoryUse resp_use, - const char *resp_body, - gsize resp_length) -{ - g_return_if_fail (SOUP_IS_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_message_set_request_body: * @msg: the message * @content_type: (allow-none): MIME Content-Type of the body @@ -1165,24 +874,12 @@ soup_message_set_request_body_from_bytes (SoupMessage *msg, } void -soup_message_wrote_informational (SoupMessage *msg) -{ - g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0); -} - -void soup_message_wrote_headers (SoupMessage *msg) { g_signal_emit (msg, signals[WROTE_HEADERS], 0); } void -soup_message_wrote_chunk (SoupMessage *msg) -{ - g_signal_emit (msg, signals[WROTE_CHUNK], 0); -} - -void soup_message_wrote_body_data (SoupMessage *msg, GBytes *chunk) { g_signal_emit (msg, signals[WROTE_BODY_DATA], 0, chunk); @@ -1207,12 +904,6 @@ soup_message_got_headers (SoupMessage *msg) } void -soup_message_got_chunk (SoupMessage *msg, GBytes *chunk) -{ - g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk); -} - -void soup_message_got_body (SoupMessage *msg) { g_signal_emit (msg, signals[GOT_BODY], 0); @@ -1233,11 +924,6 @@ soup_message_starting (SoupMessage *msg) void soup_message_restarted (SoupMessage *msg) { - SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - - if (priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD) - soup_message_body_truncate (msg->request_body); - g_clear_object (&msg->request_body_stream); g_signal_emit (msg, signals[RESTARTED], 0); @@ -1270,12 +956,9 @@ header_handler_metamarshal (GClosure *closure, GValue *return_value, gpointer invocation_hint, gpointer marshal_data) { SoupMessage *msg = g_value_get_object (¶m_values[0]); - SoupMessagePrivate *priv = soup_message_get_instance_private (msg); const char *header_name = marshal_data; - SoupMessageHeaders *hdrs; - hdrs = priv->server_side ? msg->request_headers : msg->response_headers; - if (soup_message_headers_get_one (hdrs, header_name)) { + if (soup_message_headers_get_one (msg->response_headers, header_name)) { closure->marshal (closure, return_value, n_param_values, param_values, invocation_hint, ((GCClosure *)closure)->callback); @@ -1292,9 +975,7 @@ header_handler_metamarshal (GClosure *closure, GValue *return_value, * * Adds a signal handler to @msg for @signal, as with * g_signal_connect(), but the @callback will only be run if @msg's - * incoming messages headers (that is, the - * <literal>request_headers</literal> for a client #SoupMessage, or - * the <literal>response_headers</literal> for a server #SoupMessage) + * incoming messages headers (that is, the <literal>request_headers</literal>) * contain a header named @header. * * Return value: the handler ID from g_signal_connect() @@ -1353,9 +1034,7 @@ status_handler_metamarshal (GClosure *closure, GValue *return_value, * the status @status_code. * * @signal must be a signal that will be emitted after @msg's status - * is set. For a client #SoupMessage, this means it can't be a "wrote" - * signal. For a server #SoupMessage, this means it can't be a "got" - * signal. + * is set (this means it can't be a "wrote" signal). * * Return value: the handler ID from g_signal_connect() **/ @@ -1379,7 +1058,6 @@ soup_message_add_status_code_handler (SoupMessage *msg, return g_signal_connect_closure (msg, signal, closure, FALSE); } - void soup_message_set_auth (SoupMessage *msg, SoupAuth *auth) { @@ -1470,12 +1148,7 @@ soup_message_cleanup_response (SoupMessage *msg) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - soup_message_body_truncate (msg->response_body); soup_message_headers_clear (msg->response_headers); - if (priv->server_side) { - soup_message_headers_set_encoding (msg->response_headers, - SOUP_ENCODING_CONTENT_LENGTH); - } priv->msg_flags &= ~SOUP_MESSAGE_CONTENT_DECODED; @@ -1498,9 +1171,6 @@ soup_message_cleanup_response (SoupMessage *msg) * SoupMessageFlags: * @SOUP_MESSAGE_NO_REDIRECT: The session should not follow redirect * (3xx) responses received by this message. - * @SOUP_MESSAGE_CAN_REBUILD: The caller will rebuild the request - * body if the message is restarted; see - * soup_message_body_set_accumulate() for more details. * @SOUP_MESSAGE_CONTENT_DECODED: Set by #SoupContentDecoder to * indicate that it has removed the Content-Encoding on a message (and * so headers such as Content-Length may no longer accurately describe @@ -1575,14 +1245,6 @@ soup_message_get_flags (SoupMessage *msg) } /** - * SoupHTTPVersion: - * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945) - * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616) - * - * Indicates the HTTP protocol version being used. - **/ - -/** * soup_message_set_http_version: * @msg: a #SoupMessage * @version: the HTTP version @@ -2061,9 +1723,6 @@ soup_message_set_https_status (SoupMessage *msg, SoupConnection *conn) * showing what problems, if any, have been found with that * certificate. * - * <note><para>This is only meaningful with messages processed by a #SoupSession and is - * not useful for messages received by a #SoupServer</para></note> - * * Return value: %TRUE if @msg used/attempted https, %FALSE if not * * Since: 2.34 @@ -2086,41 +1745,6 @@ soup_message_get_https_status (SoupMessage *msg, return priv->tls_certificate != NULL; } -/** - * soup_message_set_redirect: - * @msg: a #SoupMessage - * @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. - * - * Since: 2.38 - */ -void -soup_message_set_redirect (SoupMessage *msg, guint status_code, - const char *redirect_uri) -{ - SoupURI *location; - char *location_str; - - location = soup_uri_new_with_base (soup_message_get_uri (msg), redirect_uri); - g_return_if_fail (location != NULL); - - soup_message_set_status (msg, status_code); - 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); -} - void soup_message_set_soup_request (SoupMessage *msg, SoupRequest *req) @@ -2223,7 +1847,7 @@ soup_message_get_priority (SoupMessage *msg) return priv->priority; } -gpointer +SoupClientMessageIOData * soup_message_get_io_data (SoupMessage *msg) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); @@ -2232,11 +1856,12 @@ soup_message_get_io_data (SoupMessage *msg) } void -soup_message_set_io_data (SoupMessage *msg, gpointer io) +soup_message_set_io_data (SoupMessage *msg, + SoupClientMessageIOData *io) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - soup_message_io_cleanup (msg); + soup_client_message_io_data_free (priv->io_data); priv->io_data = io; } @@ -2266,17 +1891,3 @@ soup_message_set_bytes_for_sniffing (SoupMessage *msg, gsize bytes) priv->bytes_for_sniffing = bytes; } - -const char * -soup_http_version_to_string (SoupHTTPVersion version) -{ - switch (version) { - case SOUP_HTTP_1_0: - return "HTTP/1.0"; - case SOUP_HTTP_1_1: - return "HTTP/1.1"; - } - - g_assert_not_reached (); - return ""; -} diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h index 35560bf9..01664ab6 100644 --- a/libsoup/soup-message.h +++ b/libsoup/soup-message.h @@ -29,11 +29,8 @@ struct _SoupMessage { guint status_code; char *reason_phrase; - SoupMessageBody *request_body; GInputStream *request_body_stream; SoupMessageHeaders *request_headers; - - SoupMessageBody *response_body; SoupMessageHeaders *response_headers; }; @@ -41,13 +38,10 @@ typedef struct { GObjectClass parent_class; /* signals */ - void (*wrote_informational) (SoupMessage *msg); void (*wrote_headers) (SoupMessage *msg); - void (*wrote_chunk) (SoupMessage *msg); void (*wrote_body) (SoupMessage *msg); void (*got_informational) (SoupMessage *msg); void (*got_headers) (SoupMessage *msg); - void (*got_chunk) (SoupMessage *msg, GBytes *chunk); void (*got_body) (SoupMessage *msg); void (*restarted) (SoupMessage *msg); void (*finished) (SoupMessage *msg); @@ -89,12 +83,6 @@ SOUP_AVAILABLE_IN_2_4 SoupMessage *soup_message_new_from_uri (const char *method, SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_message_set_response (SoupMessage *msg, - const char *content_type, - SoupMemoryUse resp_use, - const char *resp_body, - gsize resp_length); SOUP_AVAILABLE_IN_ALL void soup_message_set_request_body (SoupMessage *msg, const char *content_type, @@ -105,11 +93,6 @@ void soup_message_set_request_body_from_bytes (SoupMessage *msg, const char *content_type, GBytes *bytes); -typedef enum { - SOUP_HTTP_1_0 = 0, /*< nick=http-1-0 >*/ - SOUP_HTTP_1_1 = 1 /*< nick=http-1-1 >*/ -} SoupHTTPVersion; - SOUP_AVAILABLE_IN_2_4 void soup_message_set_http_version (SoupMessage *msg, SoupHTTPVersion version); @@ -142,13 +125,12 @@ gboolean soup_message_get_is_top_level_navigation (SoupMessage *msg typedef enum { SOUP_MESSAGE_NO_REDIRECT = (1 << 1), - SOUP_MESSAGE_CAN_REBUILD = (1 << 2), - SOUP_MESSAGE_CONTENT_DECODED = (1 << 3), - SOUP_MESSAGE_CERTIFICATE_TRUSTED = (1 << 4), - SOUP_MESSAGE_NEW_CONNECTION = (1 << 5), - SOUP_MESSAGE_IDEMPOTENT = (1 << 6), - SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS = (1 << 7), - SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE = (1 << 8) + SOUP_MESSAGE_CONTENT_DECODED = (1 << 2), + SOUP_MESSAGE_CERTIFICATE_TRUSTED = (1 << 3), + SOUP_MESSAGE_NEW_CONNECTION = (1 << 4), + SOUP_MESSAGE_IDEMPOTENT = (1 << 5), + SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS = (1 << 6), + SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE = (1 << 7) } SoupMessageFlags; SOUP_AVAILABLE_IN_2_4 @@ -192,11 +174,6 @@ void soup_message_set_status_full (SoupMessage *msg, guint status_code, const char *reason_phrase); -SOUP_AVAILABLE_IN_2_38 -void soup_message_set_redirect (SoupMessage *msg, - guint status_code, - const char *redirect_uri); - SOUP_AVAILABLE_IN_2_28 void soup_message_disable_feature (SoupMessage *msg, GType feature_type); @@ -226,12 +203,8 @@ SOUP_AVAILABLE_IN_2_44 SoupMessagePriority soup_message_get_priority (SoupMessage *msg); SOUP_AVAILABLE_IN_2_4 -void soup_message_wrote_informational (SoupMessage *msg); -SOUP_AVAILABLE_IN_2_4 void soup_message_wrote_headers (SoupMessage *msg); SOUP_AVAILABLE_IN_2_4 -void soup_message_wrote_chunk (SoupMessage *msg); -SOUP_AVAILABLE_IN_2_4 void soup_message_wrote_body_data (SoupMessage *msg, GBytes *chunk); SOUP_AVAILABLE_IN_2_4 void soup_message_wrote_body (SoupMessage *msg); @@ -240,8 +213,6 @@ void soup_message_got_informational (SoupMessage *msg); SOUP_AVAILABLE_IN_2_4 void soup_message_got_headers (SoupMessage *msg); SOUP_AVAILABLE_IN_2_4 -void soup_message_got_chunk (SoupMessage *msg, GBytes *chunk); -SOUP_AVAILABLE_IN_2_4 void soup_message_got_body (SoupMessage *msg); SOUP_AVAILABLE_IN_2_4 void soup_message_content_sniffed (SoupMessage *msg, const char *content_type, GHashTable *params); diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index ce7dc713..d336a62d 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -1013,7 +1013,7 @@ soup_session_append_queue_item (SoupSession *session, static void soup_session_send_queue_item (SoupSession *session, SoupMessageQueueItem *item, - SoupMessageCompletionFn completion_cb) + SoupMessageIOCompletionFn completion_cb) { SoupSessionPrivate *priv = soup_session_get_instance_private (session); @@ -1389,7 +1389,7 @@ tunnel_message_completed (SoupMessage *msg, SoupMessageIOCompletion completion, if (tunnel_item->conn) { tunnel_item->state = SOUP_MESSAGE_RUNNING; soup_session_send_queue_item (session, tunnel_item, - tunnel_message_completed); + (SoupMessageIOCompletionFn)tunnel_message_completed); soup_message_io_run (msg, !tunnel_item->async); return; } @@ -1443,7 +1443,7 @@ tunnel_connect (SoupMessageQueueItem *item) g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn); soup_session_send_queue_item (session, tunnel_item, - tunnel_message_completed); + (SoupMessageIOCompletionFn)tunnel_message_completed); soup_message_io_run (msg, !item->async); g_object_unref (msg); } @@ -1709,7 +1709,8 @@ soup_session_process_queue_item (SoupSession *session, item->state = SOUP_MESSAGE_RUNNING; - soup_session_send_queue_item (session, item, message_completed); + soup_session_send_queue_item (session, item, + (SoupMessageIOCompletionFn)message_completed); if (item->async) async_send_request_running (session, item); diff --git a/libsoup/soup-status.c b/libsoup/soup-status.c index d4ed5c41..5eb1f635 100644 --- a/libsoup/soup-status.c +++ b/libsoup/soup-status.c @@ -304,3 +304,11 @@ soup_status_proxify (guint status_code) } G_DEFINE_QUARK (soup-http-error-quark, soup_http_error) + +/** + * SoupHTTPVersion: + * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945) + * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616) + * + * Indicates the HTTP protocol version being used. + */ diff --git a/libsoup/soup-status.h b/libsoup/soup-status.h index 040a858c..cc7512f9 100644 --- a/libsoup/soup-status.h +++ b/libsoup/soup-status.h @@ -93,6 +93,11 @@ typedef enum { SOUP_STATUS_NOT_EXTENDED = 510 /* RFC 2774 */ } SoupStatus; +typedef enum { + SOUP_HTTP_1_0 = 0, /*< nick=http-1-0 >*/ + SOUP_HTTP_1_1 = 1 /*< nick=http-1-1 >*/ +} SoupHTTPVersion; + SOUP_AVAILABLE_IN_2_4 const char *soup_status_get_phrase (guint status_code); SOUP_AVAILABLE_IN_2_26 diff --git a/libsoup/soup-types.h b/libsoup/soup-types.h index abd7ea21..49a6c9c4 100644 --- a/libsoup/soup-types.h +++ b/libsoup/soup-types.h @@ -25,6 +25,7 @@ typedef struct _SoupMessage SoupMessage; typedef struct _SoupRequest SoupRequest; typedef struct _SoupRequestHTTP SoupRequestHTTP; typedef struct _SoupServer SoupServer; +typedef struct _SoupServerMessage SoupServerMessage; typedef struct _SoupSession SoupSession; typedef struct _SoupSessionFeature SoupSessionFeature; typedef struct _SoupSocket SoupSocket; diff --git a/libsoup/soup.h b/libsoup/soup.h index 36485dc2..bd95488e 100644 --- a/libsoup/soup.h +++ b/libsoup/soup.h @@ -45,6 +45,7 @@ extern "C" { #include "server/soup-auth-domain-basic.h" #include "server/soup-auth-domain-digest.h" #include "server/soup-server.h" +#include "server/soup-server-message.h" #include "soup-session.h" #include "soup-session-feature.h" #include "soup-socket.h" diff --git a/libsoup/websocket/soup-websocket.c b/libsoup/websocket/soup-websocket.c index e7f29f49..48f9a7bd 100644 --- a/libsoup/websocket/soup-websocket.c +++ b/libsoup/websocket/soup-websocket.c @@ -27,6 +27,7 @@ #include "soup-websocket.h" #include "soup-headers.h" #include "soup-message-private.h" +#include "soup-server-message.h" #include "soup-websocket-extension.h" #define FIXED_DIGEST_LEN 20 @@ -205,9 +206,9 @@ compute_accept_key (const char *key) } static gboolean -choose_subprotocol (SoupMessage *msg, - const char **server_protocols, - const char **chosen_protocol) +choose_subprotocol (SoupServerMessage *msg, + const char **server_protocols, + const char **chosen_protocol) { const char *client_protocols_str; char **client_protocols; @@ -219,8 +220,9 @@ choose_subprotocol (SoupMessage *msg, if (!server_protocols) return TRUE; - client_protocols_str = soup_message_headers_get_one (msg->request_headers, - "Sec-Websocket-Protocol"); + client_protocols_str = + soup_message_headers_get_one (soup_server_message_get_request_headers (msg), + "Sec-Websocket-Protocol"); if (!client_protocols_str) return TRUE; @@ -406,10 +408,10 @@ soup_websocket_client_prepare_handshake_with_extensions (SoupMessage *msg, * Since: 2.50 */ gboolean -soup_websocket_server_check_handshake (SoupMessage *msg, - const char *expected_origin, - char **protocols, - GError **error) +soup_websocket_server_check_handshake (SoupServerMessage *msg, + const char *expected_origin, + char **protocols, + GError **error) { return soup_websocket_server_check_handshake_with_extensions (msg, expected_origin, protocols, NULL, error); } @@ -460,15 +462,15 @@ extract_extension_names_from_request (SoupMessage *msg) } static gboolean -process_extensions (SoupMessage *msg, - const char *extensions, - gboolean is_server, +process_extensions (const char *extensions, + SoupMessage *msg, GPtrArray *supported_extensions, GList **accepted_extensions, GError **error) { GSList *extension_list, *l; GHashTable *requested_extensions = NULL; + gboolean is_server = msg == NULL; if (!supported_extensions || supported_extensions->len == 0) { if (is_server) @@ -608,7 +610,7 @@ process_extensions (SoupMessage *msg, /** * soup_websocket_server_check_handshake_with_extensions: - * @msg: #SoupMessage containing the client side of a WebSocket handshake + * @msg: #SoupServerMessage containing the client side of a WebSocket handshake * @origin: (nullable): expected Origin header * @protocols: (nullable) (array zero-terminated=1): allowed WebSocket * protocols. @@ -640,19 +642,20 @@ process_extensions (SoupMessage *msg, * Since: 2.68 */ gboolean -soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, - const char *expected_origin, - char **protocols, - GPtrArray *supported_extensions, - GError **error) +soup_websocket_server_check_handshake_with_extensions (SoupServerMessage *msg, + const char *expected_origin, + char **protocols, + GPtrArray *supported_extensions, + GError **error) { const char *origin; const char *key; const char *extensions; + SoupMessageHeaders *request_headers; - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE); + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), FALSE); - if (msg->method != SOUP_METHOD_GET) { + if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) { g_set_error_literal (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET, @@ -660,8 +663,9 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, return FALSE; } - if (!soup_message_headers_header_equals (msg->request_headers, "Upgrade", "websocket") || - !soup_message_headers_header_contains (msg->request_headers, "Connection", "upgrade")) { + request_headers = soup_server_message_get_request_headers (msg); + if (!soup_message_headers_header_equals (request_headers, "Upgrade", "websocket") || + !soup_message_headers_header_contains (request_headers, "Connection", "upgrade")) { g_set_error_literal (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET, @@ -669,7 +673,7 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, return FALSE; } - if (!soup_message_headers_header_equals (msg->request_headers, "Sec-WebSocket-Version", "13")) { + if (!soup_message_headers_header_equals (request_headers, "Sec-WebSocket-Version", "13")) { g_set_error_literal (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE, @@ -677,7 +681,7 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, return FALSE; } - key = soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Key"); + key = soup_message_headers_get_one (request_headers, "Sec-WebSocket-Key"); if (key == NULL || !validate_key (key)) { g_set_error_literal (error, SOUP_WEBSOCKET_ERROR, @@ -687,7 +691,7 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, } if (expected_origin) { - origin = soup_message_headers_get_one (msg->request_headers, "Origin"); + origin = soup_message_headers_get_one (request_headers, "Origin"); if (!origin || g_ascii_strcasecmp (origin, expected_origin) != 0) { g_set_error (error, SOUP_WEBSOCKET_ERROR, @@ -705,9 +709,9 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, return FALSE; } - extensions = soup_message_headers_get_list (msg->request_headers, "Sec-WebSocket-Extensions"); + extensions = soup_message_headers_get_list (request_headers, "Sec-WebSocket-Extensions"); if (extensions && *extensions) { - if (!process_extensions (msg, extensions, TRUE, supported_extensions, NULL, error)) + if (!process_extensions (extensions, NULL, supported_extensions, NULL, error)) return FALSE; } @@ -718,32 +722,35 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, "<body>Received invalid WebSocket request</body></html>\r\n" static void -respond_handshake_forbidden (SoupMessage *msg) +respond_handshake_forbidden (SoupServerMessage *msg) { - soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN); - soup_message_headers_append (msg->response_headers, "Connection", "close"); - soup_message_set_response (msg, "text/html", SOUP_MEMORY_COPY, - RESPONSE_FORBIDDEN, strlen (RESPONSE_FORBIDDEN)); + soup_server_message_set_status (msg, SOUP_STATUS_FORBIDDEN, NULL); + soup_message_headers_append (soup_server_message_get_response_headers (msg), + "Connection", "close"); + soup_server_message_set_response (msg, "text/html", SOUP_MEMORY_COPY, + RESPONSE_FORBIDDEN, strlen (RESPONSE_FORBIDDEN)); } #define RESPONSE_BAD "<html><head><title>400 Bad Request</title></head>\r\n" \ "<body>Received invalid WebSocket request: %s</body></html>\r\n" static void -respond_handshake_bad (SoupMessage *msg, const char *why) +respond_handshake_bad (SoupServerMessage *msg, + const char *why) { char *text; text = g_strdup_printf (RESPONSE_BAD, why); - soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); - soup_message_headers_append (msg->response_headers, "Connection", "close"); - soup_message_set_response (msg, "text/html", SOUP_MEMORY_TAKE, - text, strlen (text)); + soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); + soup_message_headers_append (soup_server_message_get_response_headers (msg), + "Connection", "close"); + soup_server_message_set_response (msg, "text/html", SOUP_MEMORY_TAKE, + text, strlen (text)); } /** * soup_websocket_server_process_handshake: - * @msg: #SoupMessage containing the client side of a WebSocket handshake + * @msg: #SoupServerMessage containing the client side of a WebSocket handshake * @expected_origin: (allow-none): expected Origin header * @protocols: (allow-none) (array zero-terminated=1): allowed WebSocket * protocols. @@ -773,16 +780,16 @@ respond_handshake_bad (SoupMessage *msg, const char *why) * Since: 2.50 */ gboolean -soup_websocket_server_process_handshake (SoupMessage *msg, - const char *expected_origin, - char **protocols) +soup_websocket_server_process_handshake (SoupServerMessage *msg, + const char *expected_origin, + char **protocols) { return soup_websocket_server_process_handshake_with_extensions (msg, expected_origin, protocols, NULL, NULL); } /** * soup_websocket_server_process_handshake_with_extensions: - * @msg: #SoupMessage containing the client side of a WebSocket handshake + * @msg: #SoupServerMessage containing the client side of a WebSocket handshake * @expected_origin: (nullable): expected Origin header * @protocols: (nullable) (array zero-terminated=1): allowed WebSocket * protocols. @@ -813,18 +820,21 @@ soup_websocket_server_process_handshake (SoupMessage *msg, * Since: 2.68 */ gboolean -soup_websocket_server_process_handshake_with_extensions (SoupMessage *msg, - const char *expected_origin, - char **protocols, - GPtrArray *supported_extensions, - GList **accepted_extensions) +soup_websocket_server_process_handshake_with_extensions (SoupServerMessage *msg, + const char *expected_origin, + char **protocols, + GPtrArray *supported_extensions, + GList **accepted_extensions) { const char *chosen_protocol = NULL; const char *key; const char *extensions; char *accept_key; GError *error = NULL; + SoupMessageHeaders *request_headers; + SoupMessageHeaders *response_headers; + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), FALSE); g_return_val_if_fail (accepted_extensions == NULL || *accepted_extensions == NULL, FALSE); if (!soup_websocket_server_check_handshake_with_extensions (msg, expected_origin, protocols, supported_extensions, &error)) { @@ -838,25 +848,27 @@ soup_websocket_server_process_handshake_with_extensions (SoupMessage *msg, return FALSE; } - soup_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS); - soup_message_headers_replace (msg->response_headers, "Upgrade", "websocket"); - soup_message_headers_append (msg->response_headers, "Connection", "Upgrade"); + soup_server_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS, NULL); + response_headers = soup_server_message_get_response_headers (msg); + soup_message_headers_replace (response_headers, "Upgrade", "websocket"); + soup_message_headers_append (response_headers, "Connection", "Upgrade"); - key = soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Key"); + request_headers = soup_server_message_get_request_headers (msg); + key = soup_message_headers_get_one (request_headers, "Sec-WebSocket-Key"); accept_key = compute_accept_key (key); - soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Accept", accept_key); + soup_message_headers_append (response_headers, "Sec-WebSocket-Accept", accept_key); g_free (accept_key); choose_subprotocol (msg, (const char **) protocols, &chosen_protocol); if (chosen_protocol) - soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Protocol", chosen_protocol); + soup_message_headers_append (response_headers, "Sec-WebSocket-Protocol", chosen_protocol); - extensions = soup_message_headers_get_list (msg->request_headers, "Sec-WebSocket-Extensions"); + extensions = soup_message_headers_get_list (request_headers, "Sec-WebSocket-Extensions"); if (extensions && *extensions) { GList *websocket_extensions = NULL; GList *l; - process_extensions (msg, extensions, TRUE, supported_extensions, &websocket_extensions, NULL); + process_extensions (extensions, NULL, supported_extensions, &websocket_extensions, NULL); if (websocket_extensions) { GString *response_extensions; @@ -878,11 +890,11 @@ soup_websocket_server_process_handshake_with_extensions (SoupMessage *msg, } if (response_extensions->len > 0) { - soup_message_headers_replace (msg->response_headers, + soup_message_headers_replace (response_headers, "Sec-WebSocket-Extensions", response_extensions->str); } else { - soup_message_headers_remove (msg->response_headers, + soup_message_headers_remove (response_headers, "Sec-WebSocket-Extensions"); } g_string_free (response_extensions, TRUE); @@ -1009,7 +1021,7 @@ soup_websocket_client_verify_handshake_with_extensions (SoupMessage *msg, extensions = soup_message_headers_get_list (msg->response_headers, "Sec-WebSocket-Extensions"); if (extensions && *extensions) { - if (!process_extensions (msg, extensions, FALSE, supported_extensions, accepted_extensions, error)) + if (!process_extensions (extensions, msg, supported_extensions, accepted_extensions, error)) return FALSE; } diff --git a/libsoup/websocket/soup-websocket.h b/libsoup/websocket/soup-websocket.h index 9f265e0a..d347c213 100644 --- a/libsoup/websocket/soup-websocket.h +++ b/libsoup/websocket/soup-websocket.h @@ -87,28 +87,28 @@ gboolean soup_websocket_client_verify_handshake_with_extensions (SoupMessage *ms GError **error); SOUP_AVAILABLE_IN_2_50 -gboolean soup_websocket_server_check_handshake (SoupMessage *msg, - const char *origin, - char **protocols, - GError **error); +gboolean soup_websocket_server_check_handshake (SoupServerMessage *msg, + const char *origin, + char **protocols, + GError **error); SOUP_AVAILABLE_IN_2_68 gboolean -soup_websocket_server_check_handshake_with_extensions (SoupMessage *msg, - const char *origin, - char **protocols, - GPtrArray *supported_extensions, - GError **error); +soup_websocket_server_check_handshake_with_extensions (SoupServerMessage *msg, + const char *origin, + char **protocols, + GPtrArray *supported_extensions, + GError **error); SOUP_AVAILABLE_IN_2_50 -gboolean soup_websocket_server_process_handshake (SoupMessage *msg, - const char *expected_origin, - char **protocols); +gboolean soup_websocket_server_process_handshake (SoupServerMessage *msg, + const char *expected_origin, + char **protocols); SOUP_AVAILABLE_IN_2_68 gboolean -soup_websocket_server_process_handshake_with_extensions (SoupMessage *msg, - const char *expected_origin, - char **protocols, - GPtrArray *supported_extensions, - GList **accepted_extensions); +soup_websocket_server_process_handshake_with_extensions (SoupServerMessage *msg, + const char *expected_origin, + char **protocols, + GPtrArray *supported_extensions, + GList **accepted_extensions); G_END_DECLS |