Exemple #1
0
/**
 * HTTP async callback, invoked when new HTTP data is read.
 * The EOF condition is indicated by data being NULL.
 */
static void
soap_data_ind(http_async_t *ha, char *data, int len)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);
	size_t new_length;

	soap_rpc_check(sr);

	/*
	 * When data is NULL, we reached EOF and we're done.  Time to process
	 * the data we got back.
	 *
	 * The HTTP asynchronous handle is nullified since it is about to be
	 * closed by the HTTP layer upon return.
	 */

	if (NULL == data) {
		sr->ha = NULL;
		soap_process_reply(sr);
		return;
	}

	/*
	 * Ensure we don't get too much and resize the memory buffer where
	 * we store the reply if needed.
	 */

	new_length = size_saturate_add(sr->reply_len, len);

	if (new_length > sr->content_len) {
		http_async_error(ha, HTTP_ASYNC_DATA2BIG);
		return;
	}

	if (new_length > sr->reply_size) {
		size_t new_size = size_saturate_mult(sr->reply_size, 2);

		sr->reply_data = hrealloc(sr->reply_data, new_size);
		sr->reply_size = new_size;
	}

	/*
	 * Append new data.
	 */

	memcpy(&sr->reply_data[sr->reply_len], data, len);
	sr->reply_len = new_length;
}
Exemple #2
0
/**
 * Callback invoked when the HTTP data of the request have been sent.
 */
static void
soap_sent_data(const struct http_async *ha,
	const struct gnutella_socket *s, const char *data, size_t len,
	gboolean deferred)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);

	soap_rpc_check(sr);

	if (GNET_PROPERTY(soap_trace) & SOCK_TRACE_OUT) {
		g_debug("----Sent SOAP HTTP data%s to %s (%u bytes):",
			deferred ? " completely" : "",
			host_addr_port_to_string(s->addr, s->port), (unsigned) len);
		dump_string(stderr, data, len, "----");
	}
}
Exemple #3
0
/**
 * HTTP async callback, invoked on errors.
 */
static void
soap_error_ind(http_async_t *ha, http_errtype_t type, void *val)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);
	soap_error_t err = SOAP_E_OK;

	soap_rpc_check(sr);

	if (GNET_PROPERTY(soap_debug)) {
		http_async_log_error_dbg(ha, type, val, "SOAP",
			GNET_PROPERTY(soap_debug) > 1);
	}

	if (HTTP_ASYNC_ERROR == type) {
		switch (GPOINTER_TO_INT(val)) {
		case HTTP_ASYNC_CANCELLED:
			/*
			 * Retry with M-POST if cancelled with sr->retry set to TRUE.
			 */
			if (sr->retry) {
				g_assert(NULL == sr->delay_ev);
				sr->delay_ev = cq_main_insert(1, soap_rpc_launch, sr);

				if (GNET_PROPERTY(soap_debug) > 1) {
					g_message("SOAP \"%s\" at \"%s\": retrying with M-POST",
						sr->action, sr->url);
				}
			}
			break;		/* No callback on explicit user cancel */
		case HTTP_ASYNC_DATA2BIG:
			err = SOAP_E_DATA2BIG;
			break;
		case HTTP_ASYNC_CONN_TIMEOUT:
		case HTTP_ASYNC_TIMEOUT:
			err = SOAP_E_TIMEOUT;
			break;
		default:
			err = SOAP_E_TRANSPORT;
			break;
		}
	} else {
		err = SOAP_E_TRANSPORT;
	}

	if (err != SOAP_E_OK)
		soap_error(sr, err);
}
Exemple #4
0
/**
 * Redefine callback invoked when we got the whole HTTP reply.
 */
static void
soap_got_reply(const http_async_t *ha,
	const struct gnutella_socket *s, const char *status, const header_t *header)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);

	soap_rpc_check(sr);
	
	if (GNET_PROPERTY(soap_trace) & SOCK_TRACE_IN) {
		g_debug("----Got SOAP HTTP reply from %s:",
			host_addr_to_string(s->addr));
		if (log_printable(LOG_STDERR)) {
			fprintf(stderr, "%s\n", status);
			header_dump(stderr, header, "----");
		}
	}
}
Exemple #5
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);
	}
}
Exemple #6
0
/**
 * Build our own HTTP request.
 *
 * See http_async_build_post_request() for the model and details about
 * the various parameters.
 *
 * @return length of generated request.
 */
static size_t
soap_build_request(const http_async_t *ha,
	char *buf, size_t len, const char *verb, const char *path,
	const char *content_type, size_t content_len)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);
	size_t rw;
	const char *fixed_header;

	soap_rpc_check(sr);
	g_assert(len <= INT_MAX);

	if (sr->options & SOAP_RPC_O_MAN_FORCE) {
		sr->mandatory = TRUE;
	} else if ((sr->options & SOAP_RPC_O_MAN_RETRY) && sr->regular) {
		sr->mandatory = TRUE;
	} else {
		sr->mandatory = FALSE;
		sr->regular = TRUE;
	}

	if (sr->options & SOAP_RPC_O_ALL_CAPS) {
		fixed_header =
			"ACCEPT-ENCODING: deflate\r\n"
			"CONNECTION: close\r\n"
			"CACHE-CONTROL: no-cache\r\n"
			"PRAGMA: no-cache\r\n";
	} else {
		fixed_header =
			"Accept-Encoding: deflate\r\n"
			"Connection: close\r\n"
			"Cache-Control: no-cache\r\n"
			"Pragma: no-cache\r\n";
	}

	if (sr->mandatory) {
		if (sr->options & SOAP_RPC_O_ALL_CAPS) {
			rw = gm_snprintf(buf, len,
				"M-%s %s HTTP/1.1\r\n"
				"HOST: %s\r\n"
				"USER-AGENT: %s\r\n"
				"CONTENT-TYPE: %s\r\n"
				"CONTENT-LENGTH: %s\r\n"
				"%s"						/* Fixed header part */
				"MAN: \"%s\"; ns=01\r\n"
				"01-SOAPACTION: \"%s\"\r\n"
				"\r\n",
				verb, path,
				http_async_remote_host_port(ha),
				version_string, content_type, size_t_to_string(content_len),
				fixed_header, SOAP_NAMESPACE, sr->action);
		} else {
			rw = gm_snprintf(buf, len,
				"M-%s %s HTTP/1.1\r\n"
				"Host: %s\r\n"
				"User-Agent: %s\r\n"
				"Content-Type: %s\r\n"
				"Content-Length: %s\r\n"
				"%s"						/* Fixed header part */
				"Man: \"%s\"; ns=01\r\n"
				"01-SOAPAction: \"%s\"\r\n"
				"\r\n",
				verb, path,
				http_async_remote_host_port(ha),
				version_string, content_type, size_t_to_string(content_len),
				fixed_header, SOAP_NAMESPACE, sr->action);
		}
	} else {
		if (sr->options & SOAP_RPC_O_ALL_CAPS) {
			rw = gm_snprintf(buf, len,
				"%s %s HTTP/1.1\r\n"
				"HOST: %s\r\n"
				"USER-AGENT: %s\r\n"
				"CONTENT-TYPE: %s\r\n"
				"CONTENT-LENGTH: %s\r\n"
				"%s"						/* Fixed header part */
				"SOAPACTION: \"%s\"\r\n"
				"\r\n",
				verb, path,
				http_async_remote_host_port(ha),
				version_string, content_type, size_t_to_string(content_len),
				fixed_header, sr->action);
		} else {
			rw = gm_snprintf(buf, len,
				"%s %s HTTP/1.1\r\n"
				"Host: %s\r\n"
				"User-Agent: %s\r\n"
				"Content-Type: %s\r\n"
				"Content-Length: %s\r\n"
				"%s"						/* Fixed header part */
				"SOAPAction: \"%s\"\r\n"
				"\r\n",
				verb, path,
				http_async_remote_host_port(ha),
				version_string, content_type, size_t_to_string(content_len),
				fixed_header, sr->action);
		}
	}

	return rw;
}
Exemple #7
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 */
}