diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2021-11-10 13:19:36 +0100 |
---|---|---|
committer | Patrick Griffis <pgriffis@igalia.com> | 2021-11-11 15:19:52 +0000 |
commit | 4787b4298075557f79b5348c6432ac27625cda8c (patch) | |
tree | d6f940137cf38c4a7549ffad29a289d3735ea095 /libsoup | |
parent | 6f7d0c8b9e66461e6e6255255272150b355f8fab (diff) | |
download | libsoup-4787b4298075557f79b5348c6432ac27625cda8c.tar.gz |
http2: ensure mem_send and mem_recv functions are not called from nghttp2 callbacks
Diffstat (limited to 'libsoup')
-rw-r--r-- | libsoup/http2/soup-client-message-io-http2.c | 49 |
1 files changed, 46 insertions, 3 deletions
diff --git a/libsoup/http2/soup-client-message-io-http2.c b/libsoup/http2/soup-client-message-io-http2.c index 86bf3b24..9658e630 100644 --- a/libsoup/http2/soup-client-message-io-http2.c +++ b/libsoup/http2/soup-client-message-io-http2.c @@ -87,6 +87,8 @@ typedef struct { GTask *close_task; gboolean session_terminated; gboolean goaway_sent; + + guint in_callback; } SoupClientMessageIOHTTP2; typedef struct { @@ -345,6 +347,7 @@ io_write (SoupClientMessageIOHTTP2 *io, if (io->write_buffer == NULL) { io->written_bytes = 0; + g_assert (io->in_callback == 0); io->write_buffer_size = nghttp2_session_mem_send (io->session, (const guint8**)&io->write_buffer); NGCHECK (io->write_buffer_size); if (io->write_buffer_size == 0) { @@ -400,10 +403,15 @@ io_try_write (SoupClientMessageIOHTTP2 *io, if (io->write_source) return; - while (nghttp2_session_want_write (io->session) && !error) - io_write (io, blocking, NULL, &error); + if (io->in_callback) { + if (blocking || !nghttp2_session_want_write (io->session)) + return; + } else { + while (nghttp2_session_want_write (io->session) && !error) + io_write (io, blocking, NULL, &error); + } - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + if (io->in_callback || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_clear_error (&error); io->write_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (io->ostream), NULL); g_source_set_name (io->write_source, "Soup HTTP/2 write source"); @@ -429,6 +437,7 @@ io_read (SoupClientMessageIOHTTP2 *io, blocking, cancellable, error)) < 0) return FALSE; + g_assert (io->in_callback == 0); ret = nghttp2_session_mem_recv (io->session, buffer, read); NGCHECK (ret); return ret != 0; @@ -535,19 +544,24 @@ on_header_callback (nghttp2_session *session, if (!data) return 0; + data->io->in_callback++; + SoupMessage *msg = data->msg; if (name[0] == ':') { if (strcmp ((char *)name, ":status") == 0) { guint status_code = (guint)g_ascii_strtoull ((char *)value, NULL, 10); soup_message_set_status (msg, status_code, NULL); + data->io->in_callback--; return 0; } g_debug ("Unknown header: %s = %s", name, value); + data->io->in_callback--; return 0; } soup_message_headers_append_untrusted_data (soup_message_get_response_headers (data->msg), (const char*)name, (const char*)value); + data->io->in_callback--; return 0; } @@ -577,6 +591,8 @@ on_begin_frame_callback (nghttp2_session *session, if (!data) return 0; + data->io->in_callback++; + switch (hd->type) { case NGHTTP2_HEADERS: if (data->state < STATE_READ_HEADERS) { @@ -602,6 +618,7 @@ on_begin_frame_callback (nghttp2_session *session, break; } + data->io->in_callback--; return 0; } @@ -634,6 +651,8 @@ on_frame_recv_callback (nghttp2_session *session, SoupClientMessageIOHTTP2 *io = user_data; SoupHTTP2MessageData *data; + io->in_callback++; + if (frame->hd.stream_id == 0) { h2_debug (io, NULL, "[RECV] [%s] Recieved (%u)", frame_type_to_string (frame->hd.type), frame->hd.flags); @@ -653,6 +672,7 @@ on_frame_recv_callback (nghttp2_session *session, break; } + io->in_callback--; return 0; } @@ -662,6 +682,7 @@ on_frame_recv_callback (nghttp2_session *session, if (!data) { if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) && frame->hd.type != NGHTTP2_RST_STREAM) g_warn_if_reached (); + io->in_callback--; return 0; } @@ -676,6 +697,7 @@ on_frame_recv_callback (nghttp2_session *session, soup_message_got_informational (data->msg); soup_message_cleanup_response (data->msg); advance_state_from (data, STATE_READ_HEADERS, STATE_READ_DONE); + io->in_callback--; return 0; } @@ -713,6 +735,7 @@ on_frame_recv_callback (nghttp2_session *session, break; }; + io->in_callback--; return 0; } @@ -730,6 +753,8 @@ on_data_chunk_recv_callback (nghttp2_session *session, if (!msgdata) return NGHTTP2_ERR_CALLBACK_FAILURE; + io->in_callback++; + h2_debug (io, msgdata, "[DATA] Recieved chunk, len=%zu, flags=%u, paused=%d", len, flags, msgdata->paused); g_assert (msgdata->body_istream != NULL); @@ -737,6 +762,7 @@ on_data_chunk_recv_callback (nghttp2_session *session, if (msgdata->state == STATE_READ_DATA_START) io_try_sniff_content (msgdata, FALSE, msgdata->item->cancellable); + io->in_callback--; return 0; } @@ -752,12 +778,15 @@ on_before_frame_send_callback (nghttp2_session *session, if (!data) return 0; + data->io->in_callback++; + switch (frame->hd.type) { case NGHTTP2_HEADERS: advance_state_from (data, STATE_NONE, STATE_WRITE_HEADERS); break; } + data->io->in_callback--; return 0; } @@ -777,6 +806,8 @@ on_frame_send_callback (nghttp2_session *session, SoupClientMessageIOHTTP2 *io = user_data; SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id); + io->in_callback++; + switch (frame->hd.type) { case NGHTTP2_HEADERS: g_assert (data); @@ -829,6 +860,7 @@ on_frame_send_callback (nghttp2_session *session, break; } + io->in_callback--; return 0; } @@ -858,9 +890,12 @@ on_stream_close_callback (nghttp2_session *session, if (!data) return 0; + data->io->in_callback++; + if (error_code == NGHTTP2_REFUSED_STREAM && data->state < STATE_READ_DATA) data->can_be_restarted = TRUE; + data->io->in_callback--; return 0; } @@ -935,6 +970,7 @@ on_data_source_read_callback (nghttp2_session *session, void *user_data) { SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, stream_id); + data->io->in_callback++; /* We support pollable streams in the best case because they * should perform better with one fewer copy of each buffer and no threading. */ @@ -960,10 +996,12 @@ on_data_source_read_callback (nghttp2_session *session, g_source_attach (data->data_source_poll, g_main_context_get_thread_default ()); g_error_free (error); + data->io->in_callback--; return NGHTTP2_ERR_DEFERRED; } set_error_for_data (data, g_steal_pointer (&error)); + data->io->in_callback--; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } else if (read == 0) { @@ -971,6 +1009,7 @@ on_data_source_read_callback (nghttp2_session *session, *data_flags |= NGHTTP2_DATA_FLAG_EOF; } + data->io->in_callback--; return read; } else { GInputStream *in_stream = G_INPUT_STREAM (source->ptr); @@ -989,13 +1028,16 @@ on_data_source_read_callback (nghttp2_session *session, memcpy (buf, data->data_source_buffer->data, buffer_len); log_request_data (data, buf, buffer_len); g_byte_array_set_size (data->data_source_buffer, 0); + data->io->in_callback--; return buffer_len; } else if (data->data_source_eof) { h2_debug (data->io, data, "[SEND_BODY] EOF"); *data_flags |= NGHTTP2_DATA_FLAG_EOF; + data->io->in_callback--; return 0; } else if (data->data_source_error) { set_error_for_data (data, g_steal_pointer (&data->data_source_error)); + data->io->in_callback--; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } else { h2_debug (data->io, data, "[SEND_BODY] Reading async"); @@ -1004,6 +1046,7 @@ on_data_source_read_callback (nghttp2_session *session, get_data_io_priority (data), data->item->cancellable, (GAsyncReadyCallback)on_data_read, data); + data->io->in_callback--; return NGHTTP2_ERR_DEFERRED; } } |