Beispiel #1
0
/**
 * 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));
}
Beispiel #2
0
/**
 * 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 */
}
Beispiel #3
0
/**
 * 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);
	}
}
Beispiel #4
0
/**
 * 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;
}