1/*	$NetBSD: http-connect.c,v 1.1.1.2 2021/04/07 02:43:15 christos Exp $	*/
2#include "event2/event-config.h"
3#include <sys/cdefs.h>
4__RCSID("$NetBSD: http-connect.c,v 1.1.1.2 2021/04/07 02:43:15 christos Exp $");
5
6#include <event2/event.h>
7#include <event2/http.h>
8#include <event2/http_struct.h>
9#include <event2/buffer.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <limits.h>
13
14#define VERIFY(cond) do {                       \
15	if (!(cond)) {                              \
16		fprintf(stderr, "[error] %s\n", #cond); \
17		exit(EXIT_FAILURE);                     \
18	}                                           \
19} while (0);                                    \
20
21#define URL_MAX 4096
22
23struct connect_base
24{
25	struct evhttp_connection *evcon;
26	struct evhttp_uri *location;
27};
28
29static struct evhttp_uri* uri_parse(const char *str)
30{
31	struct evhttp_uri *uri;
32	VERIFY(uri = evhttp_uri_parse(str));
33	VERIFY(evhttp_uri_get_host(uri));
34	VERIFY(evhttp_uri_get_port(uri) > 0);
35	return uri;
36}
37static char* uri_path(struct evhttp_uri *uri, char buffer[URL_MAX])
38{
39	struct evhttp_uri *path;
40
41	VERIFY(evhttp_uri_join(uri, buffer, URL_MAX));
42
43	path = evhttp_uri_parse(buffer);
44	evhttp_uri_set_scheme(path, NULL);
45	evhttp_uri_set_userinfo(path, 0);
46	evhttp_uri_set_host(path, NULL);
47	evhttp_uri_set_port(path, -1);
48	VERIFY(evhttp_uri_join(path, buffer, URL_MAX));
49	return buffer;
50}
51static char* uri_hostport(struct evhttp_uri *uri, char buffer[URL_MAX])
52{
53	VERIFY(evhttp_uri_join(uri, buffer, URL_MAX));
54	VERIFY(evhttp_uri_get_host(uri));
55	VERIFY(evhttp_uri_get_port(uri) > 0);
56	evutil_snprintf(buffer, URL_MAX, "%s:%d",
57		evhttp_uri_get_host(uri), evhttp_uri_get_port(uri));
58	return buffer;
59}
60
61static void get_cb(struct evhttp_request *req, void *arg)
62{
63	ev_ssize_t len;
64	struct evbuffer *evbuf;
65
66	VERIFY(req);
67
68	evbuf = evhttp_request_get_input_buffer(req);
69	len = evbuffer_get_length(evbuf);
70	fwrite(evbuffer_pullup(evbuf, len), len, 1, stdout);
71	evbuffer_drain(evbuf, len);
72}
73
74static void connect_cb(struct evhttp_request *proxy_req, void *arg)
75{
76	struct connect_base *base = arg;
77	struct evhttp_connection *evcon = base->evcon;
78	struct evhttp_uri *location = base->location;
79	struct evhttp_request *req;
80	char buffer[URL_MAX];
81
82	VERIFY(proxy_req);
83	VERIFY(evcon);
84
85	req = evhttp_request_new(get_cb, NULL);
86	evhttp_add_header(req->output_headers, "Connection", "close");
87	evhttp_add_header(req->output_headers, "Host", evhttp_uri_get_host(location));
88	VERIFY(!evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
89		uri_path(location, buffer)));
90}
91
92int main(int argc, const char **argv)
93{
94	char hostport[URL_MAX];
95
96	struct evhttp_uri *location;
97	struct evhttp_uri *proxy;
98
99	struct event_base *base;
100	struct evhttp_connection *evcon;
101	struct evhttp_request *req;
102
103	struct connect_base connect_base;
104
105	if (argc != 3) {
106		printf("Usage: %s proxy url\n", argv[0]);
107		return 1;
108	}
109
110	proxy    = uri_parse(argv[1]);
111	location = uri_parse(argv[2]);
112
113	VERIFY(base = event_base_new());
114	VERIFY(evcon = evhttp_connection_base_new(base, NULL,
115		evhttp_uri_get_host(proxy), evhttp_uri_get_port(proxy)));
116	connect_base.evcon = evcon;
117	connect_base.location = location;
118	VERIFY(req = evhttp_request_new(connect_cb, &connect_base));
119
120	uri_hostport(location, hostport);
121	evhttp_add_header(req->output_headers, "Connection", "keep-alive");
122	evhttp_add_header(req->output_headers, "Proxy-Connection", "keep-alive");
123	evhttp_add_header(req->output_headers, "Host", hostport);
124	evhttp_make_request(evcon, req, EVHTTP_REQ_CONNECT, hostport);
125
126	event_base_dispatch(base);
127
128	evhttp_connection_free(evcon);
129	event_base_free(base);
130	evhttp_uri_free(proxy);
131	evhttp_uri_free(location);
132
133	return 0;
134}
135