xref: /libevent-2.1.12/sample/https-client.c (revision 42d7441a)
1be46c99bSCatalin Patulea /*
2be46c99bSCatalin Patulea   This is an example of how to hook up evhttp with bufferevent_ssl
3be46c99bSCatalin Patulea 
4be46c99bSCatalin Patulea   It just GETs an https URL given on the command-line and prints the response
5be46c99bSCatalin Patulea   body to stdout.
6be46c99bSCatalin Patulea 
7be46c99bSCatalin Patulea   Actually, it also accepts plain http URLs to make it easy to compare http vs
8be46c99bSCatalin Patulea   https code paths.
9be46c99bSCatalin Patulea 
10be46c99bSCatalin Patulea   Loosely based on le-proxy.c.
11be46c99bSCatalin Patulea  */
12be46c99bSCatalin Patulea 
13be46c99bSCatalin Patulea #include <stdio.h>
14be46c99bSCatalin Patulea #include <assert.h>
15be46c99bSCatalin Patulea #include <stdlib.h>
16be46c99bSCatalin Patulea #include <string.h>
17be46c99bSCatalin Patulea #include <errno.h>
18be46c99bSCatalin Patulea 
19be46c99bSCatalin Patulea #ifdef WIN32
20be46c99bSCatalin Patulea #include <winsock2.h>
21be46c99bSCatalin Patulea #include <ws2tcpip.h>
22be46c99bSCatalin Patulea #else
23be46c99bSCatalin Patulea #include <sys/socket.h>
24be46c99bSCatalin Patulea #include <netinet/in.h>
25be46c99bSCatalin Patulea #endif
26be46c99bSCatalin Patulea 
27be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h>
28be46c99bSCatalin Patulea #include <event2/bufferevent.h>
29be46c99bSCatalin Patulea #include <event2/buffer.h>
30be46c99bSCatalin Patulea #include <event2/listener.h>
31be46c99bSCatalin Patulea #include <event2/util.h>
32be46c99bSCatalin Patulea #include <event2/http.h>
33be46c99bSCatalin Patulea #include <event2/http_struct.h>
34be46c99bSCatalin Patulea 
35be46c99bSCatalin Patulea #include <openssl/ssl.h>
36be46c99bSCatalin Patulea #include <openssl/err.h>
37be46c99bSCatalin Patulea #include <openssl/rand.h>
38be46c99bSCatalin Patulea 
39be46c99bSCatalin Patulea static struct event_base *base;
40be46c99bSCatalin Patulea 
41be46c99bSCatalin Patulea static void
42be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
43be46c99bSCatalin Patulea {
44be46c99bSCatalin Patulea 	char buffer[256];
45be46c99bSCatalin Patulea 	int nread;
46be46c99bSCatalin Patulea 
47be46c99bSCatalin Patulea 	if (req == NULL) {
48be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
49be46c99bSCatalin Patulea 		return;
50be46c99bSCatalin Patulea 	}
51be46c99bSCatalin Patulea 
52be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
53be46c99bSCatalin Patulea 		req->response_code, req->response_code_line);
54be46c99bSCatalin Patulea 
55be46c99bSCatalin Patulea 	while ((nread = evbuffer_remove(req->input_buffer, buffer, sizeof(buffer)))
56be46c99bSCatalin Patulea 	       > 0) {
57*42d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
58*42d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
59be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
60be46c99bSCatalin Patulea 	}
61be46c99bSCatalin Patulea }
62be46c99bSCatalin Patulea 
63be46c99bSCatalin Patulea static void
64be46c99bSCatalin Patulea syntax(void)
65be46c99bSCatalin Patulea {
66be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
67be46c99bSCatalin Patulea 	fputs("   https-client <https-url>\n", stderr);
68be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
69be46c99bSCatalin Patulea 	fputs("   https-client https://ip.appspot.com/\n", stderr);
70be46c99bSCatalin Patulea 
71be46c99bSCatalin Patulea 	exit(1);
72be46c99bSCatalin Patulea }
73be46c99bSCatalin Patulea 
74be46c99bSCatalin Patulea static void
75be46c99bSCatalin Patulea die(const char *msg)
76be46c99bSCatalin Patulea {
77be46c99bSCatalin Patulea 	fputs(msg, stderr);
78be46c99bSCatalin Patulea 	exit(1);
79be46c99bSCatalin Patulea }
80be46c99bSCatalin Patulea 
81be46c99bSCatalin Patulea int
82be46c99bSCatalin Patulea main(int argc, char **argv)
83be46c99bSCatalin Patulea {
84be46c99bSCatalin Patulea 	int r;
85be46c99bSCatalin Patulea 
86be46c99bSCatalin Patulea 	struct evhttp_uri *http_uri;
87be46c99bSCatalin Patulea 	const char *url, *scheme, *host, *path, *query;
88be46c99bSCatalin Patulea 	char uri[256];
89be46c99bSCatalin Patulea 	int port;
90be46c99bSCatalin Patulea 
91be46c99bSCatalin Patulea 	SSL_CTX *ssl_ctx;
92be46c99bSCatalin Patulea 	SSL *ssl;
93be46c99bSCatalin Patulea 	struct bufferevent *bev;
94be46c99bSCatalin Patulea 	struct evhttp_connection *evcon;
95be46c99bSCatalin Patulea 	struct evhttp_request *req;
96be46c99bSCatalin Patulea 
97be46c99bSCatalin Patulea 	if (argc != 2)
98be46c99bSCatalin Patulea 		syntax();
99be46c99bSCatalin Patulea 
100be46c99bSCatalin Patulea 	url = argv[1];
101be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
102be46c99bSCatalin Patulea 	if (http_uri == NULL) {
103be46c99bSCatalin Patulea 		die("malformed url");
104be46c99bSCatalin Patulea 	}
105be46c99bSCatalin Patulea 
106be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
107be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
108be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
109be46c99bSCatalin Patulea 		die("url must be http or https");
110be46c99bSCatalin Patulea 	}
111be46c99bSCatalin Patulea 
112be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
113be46c99bSCatalin Patulea 	if (host == NULL) {
114be46c99bSCatalin Patulea 		die("url must have a host");
115be46c99bSCatalin Patulea 	}
116be46c99bSCatalin Patulea 
117be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
118be46c99bSCatalin Patulea 	if (port == -1) {
119be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
120be46c99bSCatalin Patulea 	}
121be46c99bSCatalin Patulea 
122be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
123be46c99bSCatalin Patulea 	if (path == NULL) {
124be46c99bSCatalin Patulea 		path = "/";
125be46c99bSCatalin Patulea 	}
126be46c99bSCatalin Patulea 
127be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
128be46c99bSCatalin Patulea 	if (query == NULL) {
129be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
130be46c99bSCatalin Patulea 	} else {
131be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
132be46c99bSCatalin Patulea 	}
133be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
134be46c99bSCatalin Patulea 
135be46c99bSCatalin Patulea 	// Initialize OpenSSL
136be46c99bSCatalin Patulea 	SSL_library_init();
137be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
138be46c99bSCatalin Patulea 	SSL_load_error_strings();
139be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
140be46c99bSCatalin Patulea 	r = RAND_poll();
141be46c99bSCatalin Patulea 	if (r == 0) {
142be46c99bSCatalin Patulea 		fprintf(stderr, "RAND_poll() failed.\n");
143be46c99bSCatalin Patulea 		return 1;
144be46c99bSCatalin Patulea 	}
145be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
146be46c99bSCatalin Patulea 
147be46c99bSCatalin Patulea 	// Create event base
148be46c99bSCatalin Patulea 	base = event_base_new();
149be46c99bSCatalin Patulea 	if (!base) {
150be46c99bSCatalin Patulea 		perror("event_base_new()");
151be46c99bSCatalin Patulea 		return 1;
152be46c99bSCatalin Patulea 	}
153be46c99bSCatalin Patulea 
154be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
155be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
156be46c99bSCatalin Patulea 	if (ssl == NULL) {
157be46c99bSCatalin Patulea 		fprintf(stderr, "SSL_new() failed\n");
158be46c99bSCatalin Patulea 		return 1;
159be46c99bSCatalin Patulea 	}
160be46c99bSCatalin Patulea 
161be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
162be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
163be46c99bSCatalin Patulea 	} else {
164be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
165be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
166be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
167be46c99bSCatalin Patulea 	}
168be46c99bSCatalin Patulea 
169be46c99bSCatalin Patulea 	if (bev == NULL) {
170be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
171be46c99bSCatalin Patulea 		return 1;
172be46c99bSCatalin Patulea 	}
173be46c99bSCatalin Patulea 
174be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
175be46c99bSCatalin Patulea 
176be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
177be46c99bSCatalin Patulea 	// asynchronous though.
178be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
179be46c99bSCatalin Patulea 		host, port);
180be46c99bSCatalin Patulea 	if (evcon == NULL) {
181be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
182be46c99bSCatalin Patulea 		return 1;
183be46c99bSCatalin Patulea 	}
184be46c99bSCatalin Patulea 
185be46c99bSCatalin Patulea 	// Fire off the request
186be46c99bSCatalin Patulea 	req = evhttp_request_new(http_request_done, NULL);
187be46c99bSCatalin Patulea 	if (req == NULL) {
188be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
189be46c99bSCatalin Patulea 		return 1;
190be46c99bSCatalin Patulea 	}
191be46c99bSCatalin Patulea 
192be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Host", host);
193be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Connection", "close");
194be46c99bSCatalin Patulea 
195be46c99bSCatalin Patulea 	r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri);
196be46c99bSCatalin Patulea 	if (r != 0) {
197be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
198be46c99bSCatalin Patulea 		return 1;
199be46c99bSCatalin Patulea 	}
200be46c99bSCatalin Patulea 
201be46c99bSCatalin Patulea 	event_base_dispatch(base);
202be46c99bSCatalin Patulea 
203be46c99bSCatalin Patulea 	evhttp_connection_free(evcon);
204be46c99bSCatalin Patulea 	event_base_free(base);
205be46c99bSCatalin Patulea 
206be46c99bSCatalin Patulea 	return 0;
207be46c99bSCatalin Patulea }
208