changeset 21793:e3b32123b0c2

lib-http: client: Implemented http_client_request_url_str() function that accepts an URL string rather than a pre-parsed URL object. If the provided HTTP URL is invalid, the callback with the error is called some time later from the ioloop. This change also amends the test-http-client-errors test suite with a new test for this new feature.
author Stephan Bosch <stephan.bosch@dovecot.fi>
date Fri, 17 Mar 2017 23:39:33 +0100
parents 40393fbf9954
children cc18acb777cb
files src/lib-http/http-client-request.c src/lib-http/http-client.h src/lib-http/test-http-client-errors.c
diffstat 3 files changed, 113 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-http/http-client-request.c	Fri Mar 17 23:51:19 2017 +0100
+++ b/src/lib-http/http-client-request.c	Fri Mar 17 23:39:33 2017 +0100
@@ -131,6 +131,39 @@
 	return req;
 }
 
+#undef http_client_request_url_str
+struct http_client_request *
+http_client_request_url_str(struct http_client *client,
+		    const char *method, const char *url_str,
+		    http_client_request_callback_t *callback, void *context)
+{
+	struct http_client_request *req, *tmpreq;
+	struct http_url *target_url;
+	const char *error;
+
+	req = tmpreq = http_client_request_new
+		(client, method, callback, context);
+
+	if (http_url_parse(url_str, NULL, HTTP_URL_ALLOW_USERINFO_PART,
+		req->pool, &target_url, &error) < 0) {
+		req->label = p_strdup_printf(req->pool,
+			"[Req%u: %s %s]", req->id, req->method, url_str);
+		http_client_request_error(&tmpreq,
+			HTTP_CLIENT_REQUEST_ERROR_INVALID_URL,
+			t_strdup_printf("Invalid HTTP URL: %s", error));
+		return req;
+	}
+
+	req->origin_url = *target_url;
+	req->target = p_strdup(req->pool, http_url_create_target(target_url));
+	if (target_url->user != NULL && *target_url->user != '\0' &&
+		target_url->password != NULL) {
+		req->username = p_strdup(req->pool, target_url->user);
+		req->password = p_strdup(req->pool, target_url->password);
+	}
+	return req;
+}
+
 #undef http_client_request_connect
 struct http_client_request *
 http_client_request_connect(struct http_client *client,
@@ -580,6 +613,8 @@
 		(req->host_socket != NULL) || (req->host_url != NULL);
 	const char *authority, *target;
 
+	if (req->state == HTTP_REQUEST_STATE_ABORTED)
+		return;
 	i_assert(req->state == HTTP_REQUEST_STATE_NEW);
 
 	authority = http_url_create_authority(&req->origin_url);
--- a/src/lib-http/http-client.h	Fri Mar 17 23:51:19 2017 +0100
+++ b/src/lib-http/http-client.h	Fri Mar 17 23:39:33 2017 +0100
@@ -133,6 +133,8 @@
 enum http_client_request_error {
 	/* The request was aborted */
 	HTTP_CLIENT_REQUEST_ERROR_ABORTED = HTTP_RESPONSE_STATUS_INTERNAL,
+	/* Failed to parse HTTP target url */
+	HTTP_CLIENT_REQUEST_ERROR_INVALID_URL,
 	/* Failed to perform DNS lookup for the host */
 	HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED,
 	/* Failed to setup any connection for the host and client settings allowed
@@ -207,6 +209,15 @@
 		CALLBACK_TYPECHECK(callback, void (*)( \
 			const struct http_response *response, typeof(context))), \
 		(http_client_request_callback_t *)callback, context)
+struct http_client_request *
+http_client_request_url_str(struct http_client *client,
+		    const char *method, const char *url_str,
+		    http_client_request_callback_t *callback, void *context);
+#define http_client_request_url_str(client, method, url_str, callback, context) \
+	http_client_request_url_str(client, method, url_str + \
+		CALLBACK_TYPECHECK(callback, void (*)( \
+			const struct http_response *response, typeof(context))), \
+		(http_client_request_callback_t *)callback, context)
 
 /* create new HTTP CONNECT request. If this HTTP is configured to use a proxy,
    a CONNECT request will be submitted at that proxy, otherwise the connection
--- a/src/lib-http/test-http-client-errors.c	Fri Mar 17 23:51:19 2017 +0100
+++ b/src/lib-http/test-http-client-errors.c	Fri Mar 17 23:39:33 2017 +0100
@@ -152,6 +152,72 @@
 }
 
 /*
+ * Invalid URL
+ */
+
+/* client */
+
+struct _invalid_url {
+	unsigned int count;
+};
+
+static void
+test_client_invalid_url_response(
+	const struct http_response *resp,
+	struct _invalid_url *ctx)
+{
+	if (debug)
+		i_debug("RESPONSE: %u %s", resp->status, resp->reason);
+
+	test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_INVALID_URL);
+	test_assert(resp->reason != NULL && *resp->reason != '\0');
+
+	if (--ctx->count == 0) {
+		i_free(ctx);
+		io_loop_stop(ioloop);
+	}
+}
+
+static bool
+test_client_invalid_url(const struct http_client_settings *client_set)
+{
+	struct http_client_request *hreq;
+	struct _invalid_url *ctx;
+
+	ctx = i_new(struct _invalid_url, 1);
+	ctx->count = 2;
+
+	http_client = http_client_init(client_set);
+
+	hreq = http_client_request_url_str(http_client,
+		"GET", "imap://example.com/INBOX",
+		test_client_invalid_url_response, ctx);
+	http_client_request_submit(hreq);
+
+	hreq = http_client_request_url_str(http_client,
+		"GET", "http:/www.example.com",
+		test_client_invalid_url_response, ctx);
+	http_client_request_submit(hreq);
+
+	return TRUE;
+}
+
+/* test */
+
+static void test_invalid_url(void)
+{
+	struct http_client_settings http_client_set;
+
+	test_client_defaults(&http_client_set);
+
+	test_begin("invalid url");
+	test_run_client_server(&http_client_set,
+		test_client_invalid_url,
+		NULL, 0, NULL);
+	test_end();
+}
+
+/*
  * Host lookup failed
  */
 
@@ -2679,6 +2745,7 @@
 
 static void (*test_functions[])(void) = {
 	test_unconfigured_ssl,
+	test_invalid_url,
 	test_host_lookup_failed,
 	test_connection_refused,
 	test_connection_lost_prematurely,