Re: [PATCH v3 11/18] gweb: Close web request session finalization 'hole'.
Grant Erickson <[email protected]> Mon, 24 Mar 2025 22:04:50 -0700
| Newsgroups | dev.linux.lists.connman |
|---|---|
| Message-ID | <[email protected]> |
On Mar 24, 2025, at 3:45 PM, Denis Kenzior <[email protected]> wrote: > On 3/20/25 6:39 PM, Grant Erickson wrote: >> There exist two "holes" in the finalization of web request sessions >> that can, for example in a client such as WISPr, leave one or both of >> IPv4 and IPv6 "online" HTTP-based Internet reachability checks >> "dangling" and non-renewing such that an expected active, default >> network service failover does not occur when it should. >> The first of those two failure "holes" involves an end-of-file (EOF) >> condition. In the normal case, there are a series of one or more data >> receipts for the web request as headers and, if present, the body of >> the request response are fulfilled. When there is no further data to >> send, the final receive completes with 'g_io_channel_read_chars' >> returning 'G_IO_STATUS_EOF' status and zero (0) bytes read. This should >> and does lead to a successful EOF closure of the web request. >> However, there is a second EOF case that appears to happen with a >> random distribution in the nginx server backing the default "online" >> HTTP-based Internet reachability check URLs, >> 'ipv[46].connman.net/online/status.html'. In that case, the nginx >> server appears to periodically do an unexpected and spontaneous remote >> connection close after the initial connection but before any data has >> been received. Presently, all such failures hit the same >> error-handling block which effectively maps such a failure to the >> effective "null" 'GWEB_HTTP_STATUS_CODE_UNKNOWN' status >> value. Unfortunately, clients such as WISPr have no way to distinguish >> this as an actual failure or success and so it lands on the case: >> case GWEB_HTTP_STATUS_CODE_UNKNOWN: >> wispr_portal_context_ref(wp_context); >> __connman_agent_request_browser(wp_context->service, >> wispr_portal_browser_reply_cb, >> wp_context->status_url, wp_context); >> which does not "bookend" the original, initiating WISPr web request >> and leaves the original request "dangling" and non-renewed, eventually >> leading to the aforementioned "hole". >> To handle this failure EOF case, if GWeb asked for a non-zero amount >> of data from 'g_io_channel_read_chars' but received none and has >> accumulated no headers thus far upon receiving the status >> 'G_IO_STATUS_EOF', then GWeb assumes that the remote peer server >> unexpectedly closed the connection, and synthesizes a new GError with >> the error 'ECONNRESET'. With the addition of the optional, immutable >> GError parameter to the GWebResult callback function, clients such as >> WISPr can now distinguish between a HTTP non-response in which no HTTP >> data was received and one in which HTTP data was received and the >> status can be disguished by the HTTP status code. >> The second of the two failure "holes" involves the case where >> 'g_io_channel_read_chars' returns 'G_IO_STATUS_ERROR' status. Prior to >> this change, this funneled to the same "hole" as the 'G_IO_STATUS_ERROR' >> failure with the same consequence to clients such as WISPr. >> To handle this error case, GWeb now passes a glib GError pointer to >> 'g_io_channel_read_chars'. If it is set, GWeb passes the GError >> through to the GWebResult callback function. Otherwise, it synthesizes >> a GError anew from 'EIO'. As with the failure EOF case, this allows >> clients to distinguish and handle such failures and to successfully >> "bookend" their initial web request. >> --- >> gweb/gweb.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++----- >> 1 file changed, 64 insertions(+), 6 deletions(-) > > <snip> > >> + if (status == G_IO_STATUS_ERROR) { >> + if (error != NULL) >> + passed_error = error; >> + else { >> + local_error = g_error_new_literal(G_IO_ERROR, >> + g_io_error_from_errno(EIO), >> + g_strerror(EIO)); >> + passed_error = local_error; >> + } > > Why is this needed? Isn't g_io_read_chars always creating a GError object for you in case of G_IO_STATUS_ERROR return? Without having looked at the actual implementation of ‘g_io_read_chars' in any particular version of glib, from the documentation alone, it is not clear that ‘error' is always set when the status is G_IO_STATUS_ERROR. As implemented, this seems more defensive (which is what’s desired for this overall patch set). Best, Grant -- Principal Nuovations [email protected] https://www.nuovations.com/