MEDIUM: httpclient: Refactor http-client appctx creation

A .init callback function is defined for the httpclient_applet applet. This
function finishes the appctx startup by calling appctx_finalize_startup()
and its handles the stream customization.
diff --git a/src/http_client.c b/src/http_client.c
index b818396..ac134ad 100644
--- a/src/http_client.c
+++ b/src/http_client.c
@@ -530,129 +530,29 @@
 {
 	struct applet *applet = &httpclient_applet;
 	struct appctx *appctx;
-	struct session *sess;
-	struct conn_stream *cs;
-	struct stream *s;
-	struct sockaddr_storage *addr = NULL;
-	struct sockaddr_storage ss_url = {};
-	struct sockaddr_storage *ss_dst;
-	enum obj_type *target = NULL;
-	struct ist host = IST_NULL;
-	enum http_scheme scheme;
-	int port;
-	int doresolve = 0;
 
 	/* if the client was started and not ended, an applet is already
 	 * running, we shouldn't try anything */
 	if (httpclient_started(hc) && !httpclient_ended(hc))
 		return NULL;
 
-	hc->flags = 0;
-
-	/* parse the URL and  */
-	httpclient_spliturl(hc->req.url, &scheme, &host, &port);
-
-	if (hc->dst) {
-		/* if httpclient_set_dst() was used, sets the alternative address */
-		ss_dst = hc->dst;
-	} else {
-		/* set the dst using the host, or 0.0.0.0 to resolve */
-
-		ist2str(trash.area, host);
-		ss_dst = str2ip2(trash.area, &ss_url, 0);
-		if (!ss_dst) { /* couldn't get an IP from that, try to resolve */
-			doresolve = 1;
-			ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
-		}
-		sock_inet_set_port(ss_dst, port);
-	}
-
 	/* The HTTP client will be created in the same thread as the caller,
 	 * avoiding threading issues */
 	appctx = appctx_new(applet, NULL);
 	if (!appctx)
 		goto out;
-
-	sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
-	if (!sess) {
-		ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
-		goto out_free_appctx;
-	}
-	appctx->sess = sess;
-
-	/* choose the SSL server or not */
-	switch (scheme) {
-		case SCH_HTTP:
-			target = &httpclient_srv_raw->obj_type;
-			break;
-		case SCH_HTTPS:
-#ifdef USE_OPENSSL
-			if (httpclient_srv_ssl) {
-				target = &httpclient_srv_ssl->obj_type;
-			} else {
-				ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
-				goto out_free_appctx;
-			}
-#else
-			ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
-			goto out_free_appctx;
-#endif
-			break;
-	}
-
-	if (!ss_dst) {
-		ha_alert("httpclient: Failed to initialize address %s:%d.\n", __FUNCTION__, __LINE__);
-		goto out_free_appctx;
-	}
+	appctx->svcctx = hc;
+	hc->flags = 0;
 
-	if (!sockaddr_alloc(&addr, ss_dst, sizeof(*ss_dst)))
+	if (appctx_init(appctx) == -1) {
+		ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
 		goto out_free_appctx;
-
-	cs = cs_new_from_endp(appctx->endp, sess, &hc->req.buf);
-	if (!cs) {
-		ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
-		goto out_free_addr;
-	}
-	s = DISGUISE(cs_strm(cs));
-
-	s->target = target;
-	/* set the "timeout server" */
-	s->req.wto = hc->timeout_server;
-	s->res.rto = hc->timeout_server;
-
-	if (doresolve) {
-		/* in order to do the set-dst we need to put the address on the front */
-		s->csf->dst = addr;
-	} else {
-		/* in cases we don't use the resolve we already have the address
-		 * and must put it on the backend side, some of the cases are
-		 * not meant to be used on the frontend (sockpair, unix socket etc.) */
-		s->csb->dst = addr;
 	}
 
-	s->csb->flags |= CS_FL_NOLINGER;
-	s->flags |= SF_ASSIGNED;
-	s->res.flags |= CF_READ_DONTWAIT;
-
-	/* applet is waiting for data */
-	cs_cant_get(s->csf);
-	appctx_wakeup(appctx);
-
-	hc->appctx = appctx;
-	hc->flags |= HTTPCLIENT_FS_STARTED;
-	appctx->svcctx = hc;
-
-	/* The request was transferred when the stream was created. So switch
-	 * directly to REQ_BODY or RES_STLINE state
-	 */
-	appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
-
 	return appctx;
 
-out_free_addr:
-	sockaddr_free(&addr);
 out_free_appctx:
-	appctx_free(appctx);
+	appctx_free_on_early_error(appctx);
 out:
 
 	return NULL;
@@ -1040,6 +940,109 @@
 	return;
 }
 
+static int httpclient_applet_init(struct appctx *appctx)
+{
+	struct httpclient *hc = appctx->svcctx;
+	struct stream *s;
+	struct sockaddr_storage *addr = NULL;
+	struct sockaddr_storage ss_url = {};
+	struct sockaddr_storage *ss_dst;
+	enum obj_type *target = NULL;
+	struct ist host = IST_NULL;
+	enum http_scheme scheme;
+	int port;
+	int doresolve = 0;
+
+
+	/* parse the URL and  */
+	httpclient_spliturl(hc->req.url, &scheme, &host, &port);
+
+	if (hc->dst) {
+		/* if httpclient_set_dst() was used, sets the alternative address */
+		ss_dst = hc->dst;
+	} else {
+		/* set the dst using the host, or 0.0.0.0 to resolve */
+		ist2str(trash.area, host);
+		ss_dst = str2ip2(trash.area, &ss_url, 0);
+		if (!ss_dst) { /* couldn't get an IP from that, try to resolve */
+			doresolve = 1;
+			ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
+		}
+		sock_inet_set_port(ss_dst, port);
+	}
+
+	if (!ss_dst) {
+		ha_alert("httpclient: Failed to initialize address %s:%d.\n", __FUNCTION__, __LINE__);
+		goto out_error;
+	}
+
+	if (!sockaddr_alloc(&addr, ss_dst, sizeof(*ss_dst)))
+		goto out_error;
+
+	/* choose the SSL server or not */
+	switch (scheme) {
+		case SCH_HTTP:
+			target = &httpclient_srv_raw->obj_type;
+			break;
+		case SCH_HTTPS:
+#ifdef USE_OPENSSL
+			if (httpclient_srv_ssl) {
+				target = &httpclient_srv_ssl->obj_type;
+			} else {
+				ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
+				goto out_free_addr;
+			}
+#else
+			ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
+			goto out_free_addr;
+#endif
+			break;
+	}
+
+	if (appctx_finalize_startup(appctx, httpclient_proxy, &hc->req.buf) == -1) {
+		ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
+		goto out_free_addr;
+	}
+
+	s = appctx_strm(appctx);
+	s->target = target;
+	/* set the "timeout server" */
+	s->req.wto = hc->timeout_server;
+	s->res.rto = hc->timeout_server;
+
+	if (doresolve) {
+		/* in order to do the set-dst we need to put the address on the front */
+		s->csf->dst = addr;
+	} else {
+		/* in cases we don't use the resolve we already have the address
+		 * and must put it on the backend side, some of the cases are
+		 * not meant to be used on the frontend (sockpair, unix socket etc.) */
+		s->csb->dst = addr;
+	}
+
+	s->csb->flags |= CS_FL_NOLINGER;
+	s->flags |= SF_ASSIGNED;
+	s->res.flags |= CF_READ_DONTWAIT;
+
+	/* applet is waiting for data */
+	cs_cant_get(s->csf);
+	appctx_wakeup(appctx);
+
+	hc->appctx = appctx;
+	hc->flags |= HTTPCLIENT_FS_STARTED;
+
+	/* The request was transferred when the stream was created. So switch
+	 * directly to REQ_BODY or RES_STLINE state
+	 */
+	appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
+	return 0;
+
+ out_free_addr:
+	sockaddr_free(&addr);
+ out_error:
+	return -1;
+}
+
 static void httpclient_applet_release(struct appctx *appctx)
 {
 	struct httpclient *hc = appctx->svcctx;
@@ -1066,6 +1069,7 @@
 	.obj_type = OBJ_TYPE_APPLET,
 	.name = "<HTTPCLIENT>",
 	.fct = httpclient_applet_io_handler,
+	.init = httpclient_applet_init,
 	.release = httpclient_applet_release,
 };