/** * Final cleanup. */ void G_COLD ghc_close(void) { if (ghc_connecting) { if (ghc_ctx.ha != NULL) { http_async_cancel(ghc_ctx.ha); ghc_ctx.ha = NULL; } } ghc_connecting = FALSE; list_free_all(&ghc_list, cast_to_list_destroy(ghc_free)); }
/** * HTTP async callback, invoked when all the headers have been read. * * @return TRUE if we can continue with the request. */ static gboolean soap_header_ind(http_async_t *ha, header_t *header, int code, const char *message) { soap_rpc_t *sr = http_async_get_opaque(ha); const char *buf; soap_rpc_check(sr); g_assert(ha == sr->ha); if (GNET_PROPERTY(soap_debug) > 2) { g_debug("SOAP \"%s\" at \"%s\": got HTTP %d %s", sr->action, sr->url, code, message); } /* * Grab local socket address if they are interested. */ if (sr->options & SOAP_RPC_O_LOCAL_ADDR) sr->got_local_addr = http_async_get_local_addr(ha, &sr->local_addr); /* * If we sent a non-mandatory request and get a 405 "Method not allowed" * error, retry with M-POST. Likewise, a 510 "Not extended" reply is an * invitation to use the HTTP Extension Framework (RFC 2774). */ if ( (405 == code || 510 == code) && !sr->mandatory && !sr->retry && (sr->options & SOAP_RPC_O_MAN_RETRY) ) { if (GNET_PROPERTY(soap_debug) > 1) { g_message("SOAP \"%s\" at \"%s\": will be retrying with M-POST", sr->action, sr->url); } sr->retry = TRUE; /* Signal we should retry */ http_async_cancel(ha); return FALSE; } /* * If we sent a mandatory request, there needs to be an "Ext:" header * in the reply to show that the mandatory request was understood as such. */ if (sr->mandatory && 200 == code) { const char *ext = header_get(header, "Ext"); if (NULL == ext) { if (GNET_PROPERTY(soap_debug)) { g_warning("SOAP \"%s\" at \"%s\": M-POST not understood", sr->action, sr->url); } http_async_error(ha, HTTP_ASYNC_MAN_FAILURE); return FALSE; } } /* * Save the HTTP headers and code to be able to analyze the reply payload. * * Since the option HTTP_O_READ_REPLY is used, we'll get the reply data * from the server even if the status code is not 200 and we need to be * able to differentiate between a success report and an error. */ sr->header = header_refcnt_inc(header); sr->http_code = code; /* * See whether they advertise a Content-Length, which may not be the * case if chunked transfer encoding is used for the reply. In that * case, we shall dynamically adjust the reception buffer size. */ buf = header_get(header, "Content-Length"); if (buf != NULL) { guint32 len; int error; len = parse_uint32(buf, NULL, 10, &error); if (error) { if (GNET_PROPERTY(soap_debug)) { g_warning("SOAP \"%s\" at \"%s\": " "cannot parse Content-Length header: " "value is \"%s\", error is %s", sr->action, sr->url, buf, g_strerror(error)); } http_async_error(ha, HTTP_ASYNC_BAD_HEADER); return FALSE; } if (len > sr->maxlen) { http_async_error(ha, HTTP_ASYNC_DATA2BIG); return FALSE; } sr->content_len = len; } /* * Allocate data buffer: either they advertised content length, or 1/16th * of the maximum data length we accept to grab from the server. */ sr->reply_size = (buf != NULL) ? sr->content_len : (sr->maxlen >> 4); sr->reply_data = halloc(sr->reply_size); return TRUE; /* OK, go on */ }
/** * Analyze the data we have received, and give each line to the supplied * dispatcher callback `cb', after having chomped it. On EOF, call `eof' * to finalize parsing. */ static void parse_dispatch_lines(void *handle, const char *buf, size_t len, parse_dispatch_t cb, parse_eof_t eofile) { struct parse_context *ctx; const char *p = buf; size_t remain = len; /* * Retrieve parsing context, stored as an opaque attribute in the * asynchronous HTTP request handle. */ ctx = http_async_get_opaque(handle); g_assert(ctx->handle == handle); /* Make sure it's the right context */ if (len == 0) { /* Nothing to parse, got EOF */ if (eofile != NULL) (*eofile)(ctx); return; } /* * Read a line at a time. */ for (;;) { char *line; bool error; size_t line_len; size_t parsed; switch (getline_read(ctx->getline, p, remain, &parsed)) { case READ_OVERFLOW: http_async_cancel(handle); ghc_connecting = FALSE; return; case READ_DONE: p += parsed; remain -= parsed; break; case READ_MORE: /* ok, but needs more data */ g_assert(parsed == remain); return; } /* * We come here everytime we get a full line. */ line = h_strdup(getline_str(ctx->getline)); line_len = getline_length(ctx->getline); line_len = strchomp(line, line_len); error = !(*cb)(ctx, line, line_len); /* An ERROR was reported */ HFREE_NULL(line); if (error) { ghc_ctx.ha = NULL; ghc_connecting = FALSE; return; } /* * Make sure we don't process lines ad infinitum. */ ctx->lines++; if (ctx->lines >= ctx->maxlines) { const char *req; const char *url = http_async_info(handle, &req, NULL, NULL, NULL); if (GNET_PROPERTY(bootstrap_debug)) g_warning("BOOT GHC got %u+ lines from \"%s %s\", stopping", ctx->lines, req, url); http_async_close(handle); ghc_connecting = FALSE; return; } getline_reset(ctx->getline); } }
/** * Called from gwc_parse_dispatch_lines() for each complete line of output. * * @return FALSE to stop processing of any remaining data. */ static bool gwc_host_line(struct gwc_parse_context *ctx, const char *buf, size_t len) { int c; if (GNET_PROPERTY(bootstrap_debug) > 3) g_message("BOOT GWC host line (%lu bytes): %s", (ulong) len, buf); if (is_strprefix(buf, "ERROR")) { g_warning("GWC cache \"%s\" returned %s", http_async_url(ctx->handle), buf); http_async_cancel(ctx->handle); return FALSE; } if (len <= 2) return TRUE; /* Skip this line silently */ /* * A line starting with "H|" is a host, with "U|" a GWC URL. * Letters are case-insensitive. */ if (buf[1] != '|') goto malformed; c = ascii_toupper(buf[0]); if ('H' == c) { host_addr_t addr; uint16 port; if (string_to_host_addr_port(&buf[2], NULL, &addr, &port)) { ctx->processed++; hcache_add_caught(HOST_G2HUB, addr, port, "GWC"); if (GNET_PROPERTY(bootstrap_debug) > 1) { g_message("BOOT (G2) collected %s from GWC %s", host_addr_port_to_string(addr, port), http_async_url(ctx->handle)); } } return TRUE; } else if ('U' == c) { char *end = strchr(&buf[2], '|'); char *url; if (NULL == end) goto malformed; ctx->processed++; url = h_strndup(&buf[2], ptr_diff(end, &buf[2])); gwc_add(url); hfree(url); return TRUE; } else if ('I' == c) { return TRUE; /* Ignore information line */ } /* * If we come here, we did not recognize the line properly. */ if (GNET_PROPERTY(bootstrap_debug) > 2) { g_warning("GWC ignoring unknown line \"%s\" from %s", buf, http_async_url(ctx->handle)); } return TRUE; malformed: if (GNET_PROPERTY(bootstrap_debug)) { g_warning("GWC ignoring malformed line \"%s\" from %s", buf, http_async_url(ctx->handle)); } return TRUE; }