1290001Sglebius/*
2290001Sglebius * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu>
3290001Sglebius * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4290001Sglebius *
5290001Sglebius * Redistribution and use in source and binary forms, with or without
6290001Sglebius * modification, are permitted provided that the following conditions
7290001Sglebius * are met:
8290001Sglebius * 1. Redistributions of source code must retain the above copyright
9290001Sglebius *    notice, this list of conditions and the following disclaimer.
10290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright
11290001Sglebius *    notice, this list of conditions and the following disclaimer in the
12290001Sglebius *    documentation and/or other materials provided with the distribution.
13290001Sglebius * 3. The name of the author may not be used to endorse or promote products
14290001Sglebius *    derived from this software without specific prior written permission.
15290001Sglebius *
16290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17290001Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18290001Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19290001Sglebius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20290001Sglebius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21290001Sglebius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22290001Sglebius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23290001Sglebius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24290001Sglebius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25290001Sglebius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26290001Sglebius */
27290001Sglebius
28290001Sglebius#include "event2/event-config.h"
29290001Sglebius#include "evconfig-private.h"
30290001Sglebius
31290001Sglebius#ifdef EVENT__HAVE_SYS_PARAM_H
32290001Sglebius#include <sys/param.h>
33290001Sglebius#endif
34290001Sglebius#ifdef EVENT__HAVE_SYS_TYPES_H
35290001Sglebius#include <sys/types.h>
36290001Sglebius#endif
37290001Sglebius
38290001Sglebius#ifdef EVENT__HAVE_SYS_TIME_H
39290001Sglebius#include <sys/time.h>
40290001Sglebius#endif
41290001Sglebius#ifdef HAVE_SYS_IOCCOM_H
42290001Sglebius#include <sys/ioccom.h>
43290001Sglebius#endif
44290001Sglebius
45290001Sglebius#ifndef _WIN32
46290001Sglebius#include <sys/resource.h>
47290001Sglebius#include <sys/socket.h>
48290001Sglebius#include <sys/stat.h>
49290001Sglebius#include <sys/wait.h>
50290001Sglebius#else
51290001Sglebius#include <winsock2.h>
52290001Sglebius#include <ws2tcpip.h>
53290001Sglebius#endif
54290001Sglebius
55290001Sglebius#include <sys/queue.h>
56290001Sglebius
57290001Sglebius#ifdef EVENT__HAVE_NETINET_IN_H
58290001Sglebius#include <netinet/in.h>
59290001Sglebius#endif
60290001Sglebius#ifdef EVENT__HAVE_ARPA_INET_H
61290001Sglebius#include <arpa/inet.h>
62290001Sglebius#endif
63290001Sglebius#ifdef EVENT__HAVE_NETDB_H
64290001Sglebius#include <netdb.h>
65290001Sglebius#endif
66290001Sglebius
67290001Sglebius#ifdef _WIN32
68290001Sglebius#include <winsock2.h>
69290001Sglebius#endif
70290001Sglebius
71290001Sglebius#include <errno.h>
72290001Sglebius#include <stdio.h>
73290001Sglebius#include <stdlib.h>
74290001Sglebius#include <string.h>
75290001Sglebius#ifndef _WIN32
76290001Sglebius#include <syslog.h>
77290001Sglebius#endif
78290001Sglebius#include <signal.h>
79290001Sglebius#include <time.h>
80290001Sglebius#ifdef EVENT__HAVE_UNISTD_H
81290001Sglebius#include <unistd.h>
82290001Sglebius#endif
83290001Sglebius#ifdef EVENT__HAVE_FCNTL_H
84290001Sglebius#include <fcntl.h>
85290001Sglebius#endif
86290001Sglebius
87290001Sglebius#undef timeout_pending
88290001Sglebius#undef timeout_initialized
89290001Sglebius
90290001Sglebius#include "strlcpy-internal.h"
91290001Sglebius#include "event2/http.h"
92290001Sglebius#include "event2/event.h"
93290001Sglebius#include "event2/buffer.h"
94290001Sglebius#include "event2/bufferevent.h"
95290001Sglebius#include "event2/http_struct.h"
96290001Sglebius#include "event2/http_compat.h"
97290001Sglebius#include "event2/util.h"
98290001Sglebius#include "event2/listener.h"
99290001Sglebius#include "log-internal.h"
100290001Sglebius#include "util-internal.h"
101290001Sglebius#include "http-internal.h"
102290001Sglebius#include "mm-internal.h"
103290001Sglebius#include "bufferevent-internal.h"
104290001Sglebius
105290001Sglebius#ifndef EVENT__HAVE_GETNAMEINFO
106290001Sglebius#define NI_MAXSERV 32
107290001Sglebius#define NI_MAXHOST 1025
108290001Sglebius
109290001Sglebius#ifndef NI_NUMERICHOST
110290001Sglebius#define NI_NUMERICHOST 1
111290001Sglebius#endif
112290001Sglebius
113290001Sglebius#ifndef NI_NUMERICSERV
114290001Sglebius#define NI_NUMERICSERV 2
115290001Sglebius#endif
116290001Sglebius
117290001Sglebiusstatic int
118290001Sglebiusfake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
119290001Sglebius	size_t hostlen, char *serv, size_t servlen, int flags)
120290001Sglebius{
121290001Sglebius	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
122290001Sglebius
123290001Sglebius	if (serv != NULL) {
124290001Sglebius		char tmpserv[16];
125290001Sglebius		evutil_snprintf(tmpserv, sizeof(tmpserv),
126290001Sglebius		    "%d", ntohs(sin->sin_port));
127290001Sglebius		if (strlcpy(serv, tmpserv, servlen) >= servlen)
128290001Sglebius			return (-1);
129290001Sglebius	}
130290001Sglebius
131290001Sglebius	if (host != NULL) {
132290001Sglebius		if (flags & NI_NUMERICHOST) {
133290001Sglebius			if (strlcpy(host, inet_ntoa(sin->sin_addr),
134290001Sglebius			    hostlen) >= hostlen)
135290001Sglebius				return (-1);
136290001Sglebius			else
137290001Sglebius				return (0);
138290001Sglebius		} else {
139290001Sglebius			struct hostent *hp;
140290001Sglebius			hp = gethostbyaddr((char *)&sin->sin_addr,
141290001Sglebius			    sizeof(struct in_addr), AF_INET);
142290001Sglebius			if (hp == NULL)
143290001Sglebius				return (-2);
144290001Sglebius
145290001Sglebius			if (strlcpy(host, hp->h_name, hostlen) >= hostlen)
146290001Sglebius				return (-1);
147290001Sglebius			else
148290001Sglebius				return (0);
149290001Sglebius		}
150290001Sglebius	}
151290001Sglebius	return (0);
152290001Sglebius}
153290001Sglebius
154290001Sglebius#endif
155290001Sglebius
156290001Sglebius#define REQ_VERSION_BEFORE(req, major_v, minor_v)			\
157290001Sglebius	((req)->major < (major_v) ||					\
158290001Sglebius	    ((req)->major == (major_v) && (req)->minor < (minor_v)))
159290001Sglebius
160290001Sglebius#define REQ_VERSION_ATLEAST(req, major_v, minor_v)			\
161290001Sglebius	((req)->major > (major_v) ||					\
162290001Sglebius	    ((req)->major == (major_v) && (req)->minor >= (minor_v)))
163290001Sglebius
164290001Sglebius#ifndef MIN
165290001Sglebius#define MIN(a,b) (((a)<(b))?(a):(b))
166290001Sglebius#endif
167290001Sglebius
168290001Sglebiusextern int debug;
169290001Sglebius
170290001Sglebiusstatic evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse);
171290001Sglebiusstatic evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse);
172290001Sglebiusstatic void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **);
173290001Sglebiusstatic int evhttp_associate_new_request_with_connection(
174290001Sglebius	struct evhttp_connection *evcon);
175290001Sglebiusstatic void evhttp_connection_start_detectclose(
176290001Sglebius	struct evhttp_connection *evcon);
177290001Sglebiusstatic void evhttp_connection_stop_detectclose(
178290001Sglebius	struct evhttp_connection *evcon);
179290001Sglebiusstatic void evhttp_request_dispatch(struct evhttp_connection* evcon);
180290001Sglebiusstatic void evhttp_read_firstline(struct evhttp_connection *evcon,
181290001Sglebius				  struct evhttp_request *req);
182290001Sglebiusstatic void evhttp_read_header(struct evhttp_connection *evcon,
183290001Sglebius    struct evhttp_request *req);
184290001Sglebiusstatic int evhttp_add_header_internal(struct evkeyvalq *headers,
185290001Sglebius    const char *key, const char *value);
186290001Sglebiusstatic const char *evhttp_response_phrase_internal(int code);
187290001Sglebiusstatic void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t);
188290001Sglebiusstatic void evhttp_write_buffer(struct evhttp_connection *,
189290001Sglebius    void (*)(struct evhttp_connection *, void *), void *);
190290001Sglebiusstatic void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
191290001Sglebius
192290001Sglebius/* callbacks for bufferevent */
193290001Sglebiusstatic void evhttp_read_cb(struct bufferevent *, void *);
194290001Sglebiusstatic void evhttp_write_cb(struct bufferevent *, void *);
195290001Sglebiusstatic void evhttp_error_cb(struct bufferevent *bufev, short what, void *arg);
196290001Sglebiusstatic int evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
197290001Sglebius		  const char *hostname);
198290001Sglebius
199290001Sglebius#ifndef EVENT__HAVE_STRSEP
200290001Sglebius/* strsep replacement for platforms that lack it.  Only works if
201290001Sglebius * del is one character long. */
202290001Sglebiusstatic char *
203290001Sglebiusstrsep(char **s, const char *del)
204290001Sglebius{
205290001Sglebius	char *d, *tok;
206290001Sglebius	EVUTIL_ASSERT(strlen(del) == 1);
207290001Sglebius	if (!s || !*s)
208290001Sglebius		return NULL;
209290001Sglebius	tok = *s;
210290001Sglebius	d = strstr(tok, del);
211290001Sglebius	if (d) {
212290001Sglebius		*d = '\0';
213290001Sglebius		*s = d + 1;
214290001Sglebius	} else
215290001Sglebius		*s = NULL;
216290001Sglebius	return tok;
217290001Sglebius}
218290001Sglebius#endif
219290001Sglebius
220290001Sglebiusstatic size_t
221290001Sglebiushtml_replace(const char ch, const char **escaped)
222290001Sglebius{
223290001Sglebius	switch (ch) {
224290001Sglebius	case '<':
225290001Sglebius		*escaped = "&lt;";
226290001Sglebius		return 4;
227290001Sglebius	case '>':
228290001Sglebius		*escaped = "&gt;";
229290001Sglebius		return 4;
230290001Sglebius	case '"':
231290001Sglebius		*escaped = "&quot;";
232290001Sglebius		return 6;
233290001Sglebius	case '\'':
234290001Sglebius		*escaped = "&#039;";
235290001Sglebius		return 6;
236290001Sglebius	case '&':
237290001Sglebius		*escaped = "&amp;";
238290001Sglebius		return 5;
239290001Sglebius	default:
240290001Sglebius		break;
241290001Sglebius	}
242290001Sglebius
243290001Sglebius	return 1;
244290001Sglebius}
245290001Sglebius
246290001Sglebius/*
247290001Sglebius * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
248290001Sglebius * &#039; and &amp; correspondingly.
249290001Sglebius *
250290001Sglebius * The returned string needs to be freed by the caller.
251290001Sglebius */
252290001Sglebius
253290001Sglebiuschar *
254290001Sglebiusevhttp_htmlescape(const char *html)
255290001Sglebius{
256290001Sglebius	size_t i;
257290001Sglebius	size_t new_size = 0, old_size = 0;
258290001Sglebius	char *escaped_html, *p;
259290001Sglebius
260290001Sglebius	if (html == NULL)
261290001Sglebius		return (NULL);
262290001Sglebius
263290001Sglebius	old_size = strlen(html);
264290001Sglebius	for (i = 0; i < old_size; ++i) {
265290001Sglebius		const char *replaced = NULL;
266290001Sglebius		const size_t replace_size = html_replace(html[i], &replaced);
267290001Sglebius		if (replace_size > EV_SIZE_MAX - new_size) {
268290001Sglebius			event_warn("%s: html_replace overflow", __func__);
269290001Sglebius			return (NULL);
270290001Sglebius		}
271290001Sglebius		new_size += replace_size;
272290001Sglebius	}
273290001Sglebius
274290001Sglebius	if (new_size == EV_SIZE_MAX)
275290001Sglebius		return (NULL);
276290001Sglebius	p = escaped_html = mm_malloc(new_size + 1);
277290001Sglebius	if (escaped_html == NULL) {
278290001Sglebius		event_warn("%s: malloc(%lu)", __func__,
279290001Sglebius		           (unsigned long)(new_size + 1));
280290001Sglebius		return (NULL);
281290001Sglebius	}
282290001Sglebius	for (i = 0; i < old_size; ++i) {
283290001Sglebius		const char *replaced = &html[i];
284290001Sglebius		const size_t len = html_replace(html[i], &replaced);
285290001Sglebius		memcpy(p, replaced, len);
286290001Sglebius		p += len;
287290001Sglebius	}
288290001Sglebius
289290001Sglebius	*p = '\0';
290290001Sglebius
291290001Sglebius	return (escaped_html);
292290001Sglebius}
293290001Sglebius
294290001Sglebius/** Given an evhttp_cmd_type, returns a constant string containing the
295290001Sglebius * equivalent HTTP command, or NULL if the evhttp_command_type is
296290001Sglebius * unrecognized. */
297290001Sglebiusstatic const char *
298290001Sglebiusevhttp_method(enum evhttp_cmd_type type)
299290001Sglebius{
300290001Sglebius	const char *method;
301290001Sglebius
302290001Sglebius	switch (type) {
303290001Sglebius	case EVHTTP_REQ_GET:
304290001Sglebius		method = "GET";
305290001Sglebius		break;
306290001Sglebius	case EVHTTP_REQ_POST:
307290001Sglebius		method = "POST";
308290001Sglebius		break;
309290001Sglebius	case EVHTTP_REQ_HEAD:
310290001Sglebius		method = "HEAD";
311290001Sglebius		break;
312290001Sglebius	case EVHTTP_REQ_PUT:
313290001Sglebius		method = "PUT";
314290001Sglebius		break;
315290001Sglebius	case EVHTTP_REQ_DELETE:
316290001Sglebius		method = "DELETE";
317290001Sglebius		break;
318290001Sglebius	case EVHTTP_REQ_OPTIONS:
319290001Sglebius		method = "OPTIONS";
320290001Sglebius		break;
321290001Sglebius	case EVHTTP_REQ_TRACE:
322290001Sglebius		method = "TRACE";
323290001Sglebius		break;
324290001Sglebius	case EVHTTP_REQ_CONNECT:
325290001Sglebius		method = "CONNECT";
326290001Sglebius		break;
327290001Sglebius	case EVHTTP_REQ_PATCH:
328290001Sglebius		method = "PATCH";
329290001Sglebius		break;
330290001Sglebius	default:
331290001Sglebius		method = NULL;
332290001Sglebius		break;
333290001Sglebius	}
334290001Sglebius
335290001Sglebius	return (method);
336290001Sglebius}
337290001Sglebius
338290001Sglebius/**
339290001Sglebius * Determines if a response should have a body.
340290001Sglebius * Follows the rules in RFC 2616 section 4.3.
341290001Sglebius * @return 1 if the response MUST have a body; 0 if the response MUST NOT have
342290001Sglebius *     a body.
343290001Sglebius */
344290001Sglebiusstatic int
345290001Sglebiusevhttp_response_needs_body(struct evhttp_request *req)
346290001Sglebius{
347290001Sglebius	return (req->response_code != HTTP_NOCONTENT &&
348290001Sglebius		req->response_code != HTTP_NOTMODIFIED &&
349290001Sglebius		(req->response_code < 100 || req->response_code >= 200) &&
350290001Sglebius		req->type != EVHTTP_REQ_HEAD);
351290001Sglebius}
352290001Sglebius
353290001Sglebius/** Helper: called after we've added some data to an evcon's bufferevent's
354290001Sglebius * output buffer.  Sets the evconn's writing-is-done callback, and puts
355290001Sglebius * the bufferevent into writing mode.
356290001Sglebius */
357290001Sglebiusstatic void
358290001Sglebiusevhttp_write_buffer(struct evhttp_connection *evcon,
359290001Sglebius    void (*cb)(struct evhttp_connection *, void *), void *arg)
360290001Sglebius{
361290001Sglebius	event_debug(("%s: preparing to write buffer\n", __func__));
362290001Sglebius
363290001Sglebius	/* Set call back */
364290001Sglebius	evcon->cb = cb;
365290001Sglebius	evcon->cb_arg = arg;
366290001Sglebius
367290001Sglebius	/* Disable the read callback: we don't actually care about data;
368290001Sglebius	 * we only care about close detection.  (We don't disable reading,
369290001Sglebius	 * since we *do* want to learn about any close events.) */
370290001Sglebius	bufferevent_setcb(evcon->bufev,
371290001Sglebius	    NULL, /*read*/
372290001Sglebius	    evhttp_write_cb,
373290001Sglebius	    evhttp_error_cb,
374290001Sglebius	    evcon);
375290001Sglebius
376290001Sglebius	bufferevent_enable(evcon->bufev, EV_WRITE);
377290001Sglebius}
378290001Sglebius
379290001Sglebiusstatic void
380290001Sglebiusevhttp_send_continue_done(struct evhttp_connection *evcon, void *arg)
381290001Sglebius{
382290001Sglebius	bufferevent_disable(evcon->bufev, EV_WRITE);
383290001Sglebius}
384290001Sglebius
385290001Sglebiusstatic void
386290001Sglebiusevhttp_send_continue(struct evhttp_connection *evcon,
387290001Sglebius			struct evhttp_request *req)
388290001Sglebius{
389290001Sglebius	bufferevent_enable(evcon->bufev, EV_WRITE);
390290001Sglebius	evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
391290001Sglebius			"HTTP/%d.%d 100 Continue\r\n\r\n",
392290001Sglebius			req->major, req->minor);
393290001Sglebius	evcon->cb = evhttp_send_continue_done;
394290001Sglebius	evcon->cb_arg = NULL;
395290001Sglebius	bufferevent_setcb(evcon->bufev,
396290001Sglebius	    evhttp_read_cb,
397290001Sglebius	    evhttp_write_cb,
398290001Sglebius	    evhttp_error_cb,
399290001Sglebius	    evcon);
400290001Sglebius}
401290001Sglebius
402290001Sglebius/** Helper: returns true iff evconn is in any connected state. */
403290001Sglebiusstatic int
404290001Sglebiusevhttp_connected(struct evhttp_connection *evcon)
405290001Sglebius{
406290001Sglebius	switch (evcon->state) {
407290001Sglebius	case EVCON_DISCONNECTED:
408290001Sglebius	case EVCON_CONNECTING:
409290001Sglebius		return (0);
410290001Sglebius	case EVCON_IDLE:
411290001Sglebius	case EVCON_READING_FIRSTLINE:
412290001Sglebius	case EVCON_READING_HEADERS:
413290001Sglebius	case EVCON_READING_BODY:
414290001Sglebius	case EVCON_READING_TRAILER:
415290001Sglebius	case EVCON_WRITING:
416290001Sglebius	default:
417290001Sglebius		return (1);
418290001Sglebius	}
419290001Sglebius}
420290001Sglebius
421290001Sglebius/* Create the headers needed for an outgoing HTTP request, adds them to
422290001Sglebius * the request's header list, and writes the request line to the
423290001Sglebius * connection's output buffer.
424290001Sglebius */
425290001Sglebiusstatic void
426290001Sglebiusevhttp_make_header_request(struct evhttp_connection *evcon,
427290001Sglebius    struct evhttp_request *req)
428290001Sglebius{
429290001Sglebius	const char *method;
430290001Sglebius
431290001Sglebius	evhttp_remove_header(req->output_headers, "Proxy-Connection");
432290001Sglebius
433290001Sglebius	/* Generate request line */
434290001Sglebius	method = evhttp_method(req->type);
435290001Sglebius	evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
436290001Sglebius	    "%s %s HTTP/%d.%d\r\n",
437290001Sglebius	    method, req->uri, req->major, req->minor);
438290001Sglebius
439290001Sglebius	/* Add the content length on a post or put request if missing */
440290001Sglebius	if ((req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
441290001Sglebius	    evhttp_find_header(req->output_headers, "Content-Length") == NULL){
442290001Sglebius		char size[22];
443290001Sglebius		evutil_snprintf(size, sizeof(size), EV_SIZE_FMT,
444290001Sglebius		    EV_SIZE_ARG(evbuffer_get_length(req->output_buffer)));
445290001Sglebius		evhttp_add_header(req->output_headers, "Content-Length", size);
446290001Sglebius	}
447290001Sglebius}
448290001Sglebius
449290001Sglebius/** Return true if the list of headers in 'headers', intepreted with respect
450290001Sglebius * to flags, means that we should send a "connection: close" when the request
451290001Sglebius * is done. */
452290001Sglebiusstatic int
453290001Sglebiusevhttp_is_connection_close(int flags, struct evkeyvalq* headers)
454290001Sglebius{
455290001Sglebius	if (flags & EVHTTP_PROXY_REQUEST) {
456290001Sglebius		/* proxy connection */
457290001Sglebius		const char *connection = evhttp_find_header(headers, "Proxy-Connection");
458290001Sglebius		return (connection == NULL || evutil_ascii_strcasecmp(connection, "keep-alive") != 0);
459290001Sglebius	} else {
460290001Sglebius		const char *connection = evhttp_find_header(headers, "Connection");
461290001Sglebius		return (connection != NULL && evutil_ascii_strcasecmp(connection, "close") == 0);
462290001Sglebius	}
463290001Sglebius}
464290001Sglebius
465290001Sglebius/* Return true iff 'headers' contains 'Connection: keep-alive' */
466290001Sglebiusstatic int
467290001Sglebiusevhttp_is_connection_keepalive(struct evkeyvalq* headers)
468290001Sglebius{
469290001Sglebius	const char *connection = evhttp_find_header(headers, "Connection");
470290001Sglebius	return (connection != NULL
471290001Sglebius	    && evutil_ascii_strncasecmp(connection, "keep-alive", 10) == 0);
472290001Sglebius}
473290001Sglebius
474290001Sglebius/* Add a correct "Date" header to headers, unless it already has one. */
475290001Sglebiusstatic void
476290001Sglebiusevhttp_maybe_add_date_header(struct evkeyvalq *headers)
477290001Sglebius{
478290001Sglebius	if (evhttp_find_header(headers, "Date") == NULL) {
479290001Sglebius		char date[50];
480290001Sglebius#ifndef _WIN32
481290001Sglebius		struct tm cur;
482290001Sglebius#endif
483290001Sglebius		struct tm *cur_p;
484290001Sglebius		time_t t = time(NULL);
485290001Sglebius#ifdef _WIN32
486290001Sglebius		cur_p = gmtime(&t);
487290001Sglebius#else
488290001Sglebius		gmtime_r(&t, &cur);
489290001Sglebius		cur_p = &cur;
490290001Sglebius#endif
491290001Sglebius		if (strftime(date, sizeof(date),
492290001Sglebius			"%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) {
493290001Sglebius			evhttp_add_header(headers, "Date", date);
494290001Sglebius		}
495290001Sglebius	}
496290001Sglebius}
497290001Sglebius
498290001Sglebius/* Add a "Content-Length" header with value 'content_length' to headers,
499290001Sglebius * unless it already has a content-length or transfer-encoding header. */
500290001Sglebiusstatic void
501290001Sglebiusevhttp_maybe_add_content_length_header(struct evkeyvalq *headers,
502290001Sglebius    size_t content_length)
503290001Sglebius{
504290001Sglebius	if (evhttp_find_header(headers, "Transfer-Encoding") == NULL &&
505290001Sglebius	    evhttp_find_header(headers,	"Content-Length") == NULL) {
506290001Sglebius		char len[22];
507290001Sglebius		evutil_snprintf(len, sizeof(len), EV_SIZE_FMT,
508290001Sglebius		    EV_SIZE_ARG(content_length));
509290001Sglebius		evhttp_add_header(headers, "Content-Length", len);
510290001Sglebius	}
511290001Sglebius}
512290001Sglebius
513290001Sglebius/*
514290001Sglebius * Create the headers needed for an HTTP reply in req->output_headers,
515290001Sglebius * and write the first HTTP response for req line to evcon.
516290001Sglebius */
517290001Sglebiusstatic void
518290001Sglebiusevhttp_make_header_response(struct evhttp_connection *evcon,
519290001Sglebius    struct evhttp_request *req)
520290001Sglebius{
521290001Sglebius	int is_keepalive = evhttp_is_connection_keepalive(req->input_headers);
522290001Sglebius	evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
523290001Sglebius	    "HTTP/%d.%d %d %s\r\n",
524290001Sglebius	    req->major, req->minor, req->response_code,
525290001Sglebius	    req->response_code_line);
526290001Sglebius
527290001Sglebius	if (req->major == 1) {
528290001Sglebius		if (req->minor >= 1)
529290001Sglebius			evhttp_maybe_add_date_header(req->output_headers);
530290001Sglebius
531290001Sglebius		/*
532290001Sglebius		 * if the protocol is 1.0; and the connection was keep-alive
533290001Sglebius		 * we need to add a keep-alive header, too.
534290001Sglebius		 */
535290001Sglebius		if (req->minor == 0 && is_keepalive)
536290001Sglebius			evhttp_add_header(req->output_headers,
537290001Sglebius			    "Connection", "keep-alive");
538290001Sglebius
539290001Sglebius		if ((req->minor >= 1 || is_keepalive) &&
540290001Sglebius		    evhttp_response_needs_body(req)) {
541290001Sglebius			/*
542290001Sglebius			 * we need to add the content length if the
543290001Sglebius			 * user did not give it, this is required for
544290001Sglebius			 * persistent connections to work.
545290001Sglebius			 */
546290001Sglebius			evhttp_maybe_add_content_length_header(
547290001Sglebius				req->output_headers,
548290001Sglebius				evbuffer_get_length(req->output_buffer));
549290001Sglebius		}
550290001Sglebius	}
551290001Sglebius
552290001Sglebius	/* Potentially add headers for unidentified content. */
553290001Sglebius	if (evhttp_response_needs_body(req)) {
554290001Sglebius		if (evhttp_find_header(req->output_headers,
555290001Sglebius			"Content-Type") == NULL
556290001Sglebius		    && evcon->http_server->default_content_type) {
557290001Sglebius			evhttp_add_header(req->output_headers,
558290001Sglebius			    "Content-Type",
559290001Sglebius			    evcon->http_server->default_content_type);
560290001Sglebius		}
561290001Sglebius	}
562290001Sglebius
563290001Sglebius	/* if the request asked for a close, we send a close, too */
564290001Sglebius	if (evhttp_is_connection_close(req->flags, req->input_headers)) {
565290001Sglebius		evhttp_remove_header(req->output_headers, "Connection");
566290001Sglebius		if (!(req->flags & EVHTTP_PROXY_REQUEST))
567290001Sglebius		    evhttp_add_header(req->output_headers, "Connection", "close");
568290001Sglebius		evhttp_remove_header(req->output_headers, "Proxy-Connection");
569290001Sglebius	}
570290001Sglebius}
571290001Sglebius
572290001Sglebius/** Generate all headers appropriate for sending the http request in req (or
573290001Sglebius * the response, if we're sending a response), and write them to evcon's
574290001Sglebius * bufferevent. Also writes all data from req->output_buffer */
575290001Sglebiusstatic void
576290001Sglebiusevhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
577290001Sglebius{
578290001Sglebius	struct evkeyval *header;
579290001Sglebius	struct evbuffer *output = bufferevent_get_output(evcon->bufev);
580290001Sglebius
581290001Sglebius	/*
582290001Sglebius	 * Depending if this is a HTTP request or response, we might need to
583290001Sglebius	 * add some new headers or remove existing headers.
584290001Sglebius	 */
585290001Sglebius	if (req->kind == EVHTTP_REQUEST) {
586290001Sglebius		evhttp_make_header_request(evcon, req);
587290001Sglebius	} else {
588290001Sglebius		evhttp_make_header_response(evcon, req);
589290001Sglebius	}
590290001Sglebius
591290001Sglebius	TAILQ_FOREACH(header, req->output_headers, next) {
592290001Sglebius		evbuffer_add_printf(output, "%s: %s\r\n",
593290001Sglebius		    header->key, header->value);
594290001Sglebius	}
595290001Sglebius	evbuffer_add(output, "\r\n", 2);
596290001Sglebius
597290001Sglebius	if (evbuffer_get_length(req->output_buffer) > 0) {
598290001Sglebius		/*
599290001Sglebius		 * For a request, we add the POST data, for a reply, this
600290001Sglebius		 * is the regular data.
601290001Sglebius		 */
602290001Sglebius		/* XXX We might want to support waiting (a limited amount of
603290001Sglebius		   time) for a continue status line from the server before
604290001Sglebius		   sending POST/PUT message bodies. */
605290001Sglebius		evbuffer_add_buffer(output, req->output_buffer);
606290001Sglebius	}
607290001Sglebius}
608290001Sglebius
609290001Sglebiusvoid
610290001Sglebiusevhttp_connection_set_max_headers_size(struct evhttp_connection *evcon,
611290001Sglebius    ev_ssize_t new_max_headers_size)
612290001Sglebius{
613290001Sglebius	if (new_max_headers_size<0)
614290001Sglebius		evcon->max_headers_size = EV_SIZE_MAX;
615290001Sglebius	else
616290001Sglebius		evcon->max_headers_size = new_max_headers_size;
617290001Sglebius}
618290001Sglebiusvoid
619290001Sglebiusevhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
620290001Sglebius    ev_ssize_t new_max_body_size)
621290001Sglebius{
622290001Sglebius	if (new_max_body_size<0)
623290001Sglebius		evcon->max_body_size = EV_UINT64_MAX;
624290001Sglebius	else
625290001Sglebius		evcon->max_body_size = new_max_body_size;
626290001Sglebius}
627290001Sglebius
628290001Sglebiusstatic int
629290001Sglebiusevhttp_connection_incoming_fail(struct evhttp_request *req,
630290001Sglebius    enum evhttp_request_error error)
631290001Sglebius{
632290001Sglebius	switch (error) {
633290001Sglebius	case EVREQ_HTTP_TIMEOUT:
634290001Sglebius	case EVREQ_HTTP_EOF:
635290001Sglebius		/*
636290001Sglebius		 * these are cases in which we probably should just
637290001Sglebius		 * close the connection and not send a reply.  this
638290001Sglebius		 * case may happen when a browser keeps a persistent
639290001Sglebius		 * connection open and we timeout on the read.  when
640290001Sglebius		 * the request is still being used for sending, we
641290001Sglebius		 * need to disassociated it from the connection here.
642290001Sglebius		 */
643290001Sglebius		if (!req->userdone) {
644290001Sglebius			/* remove it so that it will not be freed */
645290001Sglebius			TAILQ_REMOVE(&req->evcon->requests, req, next);
646290001Sglebius			/* indicate that this request no longer has a
647290001Sglebius			 * connection object
648290001Sglebius			 */
649290001Sglebius			req->evcon = NULL;
650290001Sglebius		}
651290001Sglebius		return (-1);
652290001Sglebius	case EVREQ_HTTP_INVALID_HEADER:
653290001Sglebius	case EVREQ_HTTP_BUFFER_ERROR:
654290001Sglebius	case EVREQ_HTTP_REQUEST_CANCEL:
655290001Sglebius	case EVREQ_HTTP_DATA_TOO_LONG:
656290001Sglebius	default:	/* xxx: probably should just error on default */
657290001Sglebius		/* the callback looks at the uri to determine errors */
658290001Sglebius		if (req->uri) {
659290001Sglebius			mm_free(req->uri);
660290001Sglebius			req->uri = NULL;
661290001Sglebius		}
662290001Sglebius		if (req->uri_elems) {
663290001Sglebius			evhttp_uri_free(req->uri_elems);
664290001Sglebius			req->uri_elems = NULL;
665290001Sglebius		}
666290001Sglebius
667290001Sglebius		/*
668290001Sglebius		 * the callback needs to send a reply, once the reply has
669290001Sglebius		 * been send, the connection should get freed.
670290001Sglebius		 */
671290001Sglebius		(*req->cb)(req, req->cb_arg);
672290001Sglebius	}
673290001Sglebius
674290001Sglebius	return (0);
675290001Sglebius}
676290001Sglebius
677290001Sglebius/* Called when evcon has experienced a (non-recoverable? -NM) error, as
678290001Sglebius * given in error. If it's an outgoing connection, reset the connection,
679290001Sglebius * retry any pending requests, and inform the user.  If it's incoming,
680290001Sglebius * delegates to evhttp_connection_incoming_fail(). */
681290001Sglebiusvoid
682290001Sglebiusevhttp_connection_fail_(struct evhttp_connection *evcon,
683290001Sglebius    enum evhttp_request_error error)
684290001Sglebius{
685290001Sglebius	const int errsave = EVUTIL_SOCKET_ERROR();
686290001Sglebius	struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
687290001Sglebius	void (*cb)(struct evhttp_request *, void *);
688290001Sglebius	void *cb_arg;
689290001Sglebius	void (*error_cb)(enum evhttp_request_error, void *);
690290001Sglebius	void *error_cb_arg;
691290001Sglebius	EVUTIL_ASSERT(req != NULL);
692290001Sglebius
693290001Sglebius	bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
694290001Sglebius
695290001Sglebius	if (evcon->flags & EVHTTP_CON_INCOMING) {
696290001Sglebius		/*
697290001Sglebius		 * for incoming requests, there are two different
698290001Sglebius		 * failure cases.  it's either a network level error
699290001Sglebius		 * or an http layer error. for problems on the network
700290001Sglebius		 * layer like timeouts we just drop the connections.
701290001Sglebius		 * For HTTP problems, we might have to send back a
702290001Sglebius		 * reply before the connection can be freed.
703290001Sglebius		 */
704290001Sglebius		if (evhttp_connection_incoming_fail(req, error) == -1)
705290001Sglebius			evhttp_connection_free(evcon);
706290001Sglebius		return;
707290001Sglebius	}
708290001Sglebius
709290001Sglebius	error_cb = req->error_cb;
710290001Sglebius	error_cb_arg = req->cb_arg;
711290001Sglebius	/* when the request was canceled, the callback is not executed */
712290001Sglebius	if (error != EVREQ_HTTP_REQUEST_CANCEL) {
713290001Sglebius		/* save the callback for later; the cb might free our object */
714290001Sglebius		cb = req->cb;
715290001Sglebius		cb_arg = req->cb_arg;
716290001Sglebius	} else {
717290001Sglebius		cb = NULL;
718290001Sglebius		cb_arg = NULL;
719290001Sglebius	}
720290001Sglebius
721290001Sglebius	/* do not fail all requests; the next request is going to get
722290001Sglebius	 * send over a new connection.   when a user cancels a request,
723290001Sglebius	 * all other pending requests should be processed as normal
724290001Sglebius	 */
725290001Sglebius	TAILQ_REMOVE(&evcon->requests, req, next);
726290001Sglebius	evhttp_request_free(req);
727290001Sglebius
728290001Sglebius	/* reset the connection */
729290001Sglebius	evhttp_connection_reset_(evcon);
730290001Sglebius
731290001Sglebius	/* We are trying the next request that was queued on us */
732290001Sglebius	if (TAILQ_FIRST(&evcon->requests) != NULL)
733290001Sglebius		evhttp_connection_connect_(evcon);
734290001Sglebius
735290001Sglebius	/* The call to evhttp_connection_reset_ overwrote errno.
736290001Sglebius	 * Let's restore the original errno, so that the user's
737290001Sglebius	 * callback can have a better idea of what the error was.
738290001Sglebius	 */
739290001Sglebius	EVUTIL_SET_SOCKET_ERROR(errsave);
740290001Sglebius
741290001Sglebius	/* inform the user */
742290001Sglebius	if (error_cb != NULL)
743290001Sglebius		error_cb(error, error_cb_arg);
744290001Sglebius	if (cb != NULL)
745290001Sglebius		(*cb)(NULL, cb_arg);
746290001Sglebius}
747290001Sglebius
748290001Sglebius/* Bufferevent callback: invoked when any data has been written from an
749290001Sglebius * http connection's bufferevent */
750290001Sglebiusstatic void
751290001Sglebiusevhttp_write_cb(struct bufferevent *bufev, void *arg)
752290001Sglebius{
753290001Sglebius	struct evhttp_connection *evcon = arg;
754290001Sglebius
755290001Sglebius	/* Activate our call back */
756290001Sglebius	if (evcon->cb != NULL)
757290001Sglebius		(*evcon->cb)(evcon, evcon->cb_arg);
758290001Sglebius}
759290001Sglebius
760290001Sglebius/**
761290001Sglebius * Advance the connection state.
762290001Sglebius * - If this is an outgoing connection, we've just processed the response;
763290001Sglebius *   idle or close the connection.
764290001Sglebius * - If this is an incoming connection, we've just processed the request;
765290001Sglebius *   respond.
766290001Sglebius */
767290001Sglebiusstatic void
768290001Sglebiusevhttp_connection_done(struct evhttp_connection *evcon)
769290001Sglebius{
770290001Sglebius	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
771290001Sglebius	int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
772290001Sglebius	int free_evcon = 0;
773290001Sglebius
774290001Sglebius	if (con_outgoing) {
775290001Sglebius		/* idle or close the connection */
776290001Sglebius		int need_close;
777290001Sglebius		TAILQ_REMOVE(&evcon->requests, req, next);
778290001Sglebius		req->evcon = NULL;
779290001Sglebius
780290001Sglebius		evcon->state = EVCON_IDLE;
781290001Sglebius
782290001Sglebius		need_close =
783290001Sglebius		    evhttp_is_connection_close(req->flags, req->input_headers)||
784290001Sglebius		    evhttp_is_connection_close(req->flags, req->output_headers);
785290001Sglebius
786290001Sglebius		/* check if we got asked to close the connection */
787290001Sglebius		if (need_close)
788290001Sglebius			evhttp_connection_reset_(evcon);
789290001Sglebius
790290001Sglebius		if (TAILQ_FIRST(&evcon->requests) != NULL) {
791290001Sglebius			/*
792290001Sglebius			 * We have more requests; reset the connection
793290001Sglebius			 * and deal with the next request.
794290001Sglebius			 */
795290001Sglebius			if (!evhttp_connected(evcon))
796290001Sglebius				evhttp_connection_connect_(evcon);
797290001Sglebius			else
798290001Sglebius				evhttp_request_dispatch(evcon);
799290001Sglebius		} else if (!need_close) {
800290001Sglebius			/*
801290001Sglebius			 * The connection is going to be persistent, but we
802290001Sglebius			 * need to detect if the other side closes it.
803290001Sglebius			 */
804290001Sglebius			evhttp_connection_start_detectclose(evcon);
805290001Sglebius		} else if ((evcon->flags & EVHTTP_CON_AUTOFREE)) {
806290001Sglebius			/*
807290001Sglebius			 * If we have no more requests that need completion
808290001Sglebius			 * and we're not waiting for the connection to close
809290001Sglebius			 */
810290001Sglebius			 free_evcon = 1;
811290001Sglebius		}
812290001Sglebius	} else {
813290001Sglebius		/*
814290001Sglebius		 * incoming connection - we need to leave the request on the
815290001Sglebius		 * connection so that we can reply to it.
816290001Sglebius		 */
817290001Sglebius		evcon->state = EVCON_WRITING;
818290001Sglebius	}
819290001Sglebius
820290001Sglebius	/* notify the user of the request */
821290001Sglebius	(*req->cb)(req, req->cb_arg);
822290001Sglebius
823290001Sglebius	/* if this was an outgoing request, we own and it's done. so free it.
824290001Sglebius	 * unless the callback specifically requested to own the request.
825290001Sglebius	 */
826290001Sglebius	if (con_outgoing && ((req->flags & EVHTTP_USER_OWNED) == 0)) {
827290001Sglebius		evhttp_request_free(req);
828290001Sglebius	}
829290001Sglebius
830290001Sglebius	/* If this was the last request of an outgoing connection and we're
831290001Sglebius	 * not waiting to receive a connection close event and we want to
832290001Sglebius	 * automatically free the connection. We check to ensure our request
833290001Sglebius	 * list is empty one last time just in case our callback added a
834290001Sglebius	 * new request.
835290001Sglebius	 */
836290001Sglebius	if (free_evcon && TAILQ_FIRST(&evcon->requests) == NULL) {
837290001Sglebius		evhttp_connection_free(evcon);
838290001Sglebius	}
839290001Sglebius}
840290001Sglebius
841290001Sglebius/*
842290001Sglebius * Handles reading from a chunked request.
843290001Sglebius *   return ALL_DATA_READ:
844290001Sglebius *     all data has been read
845290001Sglebius *   return MORE_DATA_EXPECTED:
846290001Sglebius *     more data is expected
847290001Sglebius *   return DATA_CORRUPTED:
848290001Sglebius *     data is corrupted
849290001Sglebius *   return REQUEST_CANCELED:
850290001Sglebius *     request was canceled by the user calling evhttp_cancel_request
851290001Sglebius *   return DATA_TOO_LONG:
852290001Sglebius *     ran over the maximum limit
853290001Sglebius */
854290001Sglebius
855290001Sglebiusstatic enum message_read_status
856290001Sglebiusevhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
857290001Sglebius{
858290001Sglebius	if (req == NULL || buf == NULL) {
859290001Sglebius	    return DATA_CORRUPTED;
860290001Sglebius	}
861290001Sglebius
862290001Sglebius	while (1) {
863290001Sglebius		size_t buflen;
864290001Sglebius
865290001Sglebius		if ((buflen = evbuffer_get_length(buf)) == 0) {
866290001Sglebius			break;
867290001Sglebius		}
868290001Sglebius
869290001Sglebius		/* evbuffer_get_length returns size_t, but len variable is ssize_t,
870290001Sglebius		 * check for overflow conditions */
871290001Sglebius		if (buflen > EV_SSIZE_MAX) {
872290001Sglebius			return DATA_CORRUPTED;
873290001Sglebius		}
874290001Sglebius
875290001Sglebius		if (req->ntoread < 0) {
876290001Sglebius			/* Read chunk size */
877290001Sglebius			ev_int64_t ntoread;
878290001Sglebius			char *p = evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF);
879290001Sglebius			char *endp;
880290001Sglebius			int error;
881290001Sglebius			if (p == NULL)
882290001Sglebius				break;
883290001Sglebius			/* the last chunk is on a new line? */
884290001Sglebius			if (strlen(p) == 0) {
885290001Sglebius				mm_free(p);
886290001Sglebius				continue;
887290001Sglebius			}
888290001Sglebius			ntoread = evutil_strtoll(p, &endp, 16);
889290001Sglebius			error = (*p == '\0' ||
890290001Sglebius			    (*endp != '\0' && *endp != ' ') ||
891290001Sglebius			    ntoread < 0);
892290001Sglebius			mm_free(p);
893290001Sglebius			if (error) {
894290001Sglebius				/* could not get chunk size */
895290001Sglebius				return (DATA_CORRUPTED);
896290001Sglebius			}
897290001Sglebius
898290001Sglebius			/* ntoread is signed int64, body_size is unsigned size_t, check for under/overflow conditions */
899290001Sglebius			if ((ev_uint64_t)ntoread > EV_SIZE_MAX - req->body_size) {
900290001Sglebius			    return DATA_CORRUPTED;
901290001Sglebius			}
902290001Sglebius
903290001Sglebius			if (req->body_size + (size_t)ntoread > req->evcon->max_body_size) {
904290001Sglebius				/* failed body length test */
905290001Sglebius				event_debug(("Request body is too long"));
906290001Sglebius				return (DATA_TOO_LONG);
907290001Sglebius			}
908290001Sglebius
909290001Sglebius			req->body_size += (size_t)ntoread;
910290001Sglebius			req->ntoread = ntoread;
911290001Sglebius			if (req->ntoread == 0) {
912290001Sglebius				/* Last chunk */
913290001Sglebius				return (ALL_DATA_READ);
914290001Sglebius			}
915290001Sglebius			continue;
916290001Sglebius		}
917290001Sglebius
918290001Sglebius		/* req->ntoread is signed int64, len is ssize_t, based on arch,
919290001Sglebius		 * ssize_t could only be 32b, check for these conditions */
920290001Sglebius		if (req->ntoread > EV_SSIZE_MAX) {
921290001Sglebius			return DATA_CORRUPTED;
922290001Sglebius		}
923290001Sglebius
924290001Sglebius		/* don't have enough to complete a chunk; wait for more */
925290001Sglebius		if (req->ntoread > 0 && buflen < (ev_uint64_t)req->ntoread)
926290001Sglebius			return (MORE_DATA_EXPECTED);
927290001Sglebius
928290001Sglebius		/* Completed chunk */
929290001Sglebius		evbuffer_remove_buffer(buf, req->input_buffer, (size_t)req->ntoread);
930290001Sglebius		req->ntoread = -1;
931290001Sglebius		if (req->chunk_cb != NULL) {
932290001Sglebius			req->flags |= EVHTTP_REQ_DEFER_FREE;
933290001Sglebius			(*req->chunk_cb)(req, req->cb_arg);
934290001Sglebius			evbuffer_drain(req->input_buffer,
935290001Sglebius			    evbuffer_get_length(req->input_buffer));
936290001Sglebius			req->flags &= ~EVHTTP_REQ_DEFER_FREE;
937290001Sglebius			if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) {
938290001Sglebius				return (REQUEST_CANCELED);
939290001Sglebius			}
940290001Sglebius		}
941290001Sglebius	}
942290001Sglebius
943290001Sglebius	return (MORE_DATA_EXPECTED);
944290001Sglebius}
945290001Sglebius
946290001Sglebiusstatic void
947290001Sglebiusevhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req)
948290001Sglebius{
949290001Sglebius	struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
950290001Sglebius
951290001Sglebius	switch (evhttp_parse_headers_(req, buf)) {
952290001Sglebius	case DATA_CORRUPTED:
953290001Sglebius	case DATA_TOO_LONG:
954290001Sglebius		evhttp_connection_fail_(evcon, EVREQ_HTTP_DATA_TOO_LONG);
955290001Sglebius		break;
956290001Sglebius	case ALL_DATA_READ:
957290001Sglebius		bufferevent_disable(evcon->bufev, EV_READ);
958290001Sglebius		evhttp_connection_done(evcon);
959290001Sglebius		break;
960290001Sglebius	case MORE_DATA_EXPECTED:
961290001Sglebius	case REQUEST_CANCELED: /* ??? */
962290001Sglebius	default:
963290001Sglebius		bufferevent_enable(evcon->bufev, EV_READ);
964290001Sglebius		break;
965290001Sglebius	}
966290001Sglebius}
967290001Sglebius
968290001Sglebiusstatic void
969290001Sglebiusevhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
970290001Sglebius{
971290001Sglebius	struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
972290001Sglebius
973290001Sglebius	if (req->chunked) {
974290001Sglebius		switch (evhttp_handle_chunked_read(req, buf)) {
975290001Sglebius		case ALL_DATA_READ:
976290001Sglebius			/* finished last chunk */
977290001Sglebius			evcon->state = EVCON_READING_TRAILER;
978290001Sglebius			evhttp_read_trailer(evcon, req);
979290001Sglebius			return;
980290001Sglebius		case DATA_CORRUPTED:
981290001Sglebius		case DATA_TOO_LONG:
982290001Sglebius			/* corrupted data */
983290001Sglebius			evhttp_connection_fail_(evcon,
984290001Sglebius			    EVREQ_HTTP_DATA_TOO_LONG);
985290001Sglebius			return;
986290001Sglebius		case REQUEST_CANCELED:
987290001Sglebius			/* request canceled */
988290001Sglebius			evhttp_request_free(req);
989290001Sglebius			return;
990290001Sglebius		case MORE_DATA_EXPECTED:
991290001Sglebius		default:
992290001Sglebius			break;
993290001Sglebius		}
994290001Sglebius	} else if (req->ntoread < 0) {
995290001Sglebius		/* Read until connection close. */
996290001Sglebius		if ((size_t)(req->body_size + evbuffer_get_length(buf)) < req->body_size) {
997290001Sglebius			evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
998290001Sglebius			return;
999290001Sglebius		}
1000290001Sglebius
1001290001Sglebius		req->body_size += evbuffer_get_length(buf);
1002290001Sglebius		evbuffer_add_buffer(req->input_buffer, buf);
1003290001Sglebius	} else if (req->chunk_cb != NULL || evbuffer_get_length(buf) >= (size_t)req->ntoread) {
1004290001Sglebius		/* XXX: the above get_length comparison has to be fixed for overflow conditions! */
1005290001Sglebius		/* We've postponed moving the data until now, but we're
1006290001Sglebius		 * about to use it. */
1007290001Sglebius		size_t n = evbuffer_get_length(buf);
1008290001Sglebius
1009290001Sglebius		if (n > (size_t) req->ntoread)
1010290001Sglebius			n = (size_t) req->ntoread;
1011290001Sglebius		req->ntoread -= n;
1012290001Sglebius		req->body_size += n;
1013290001Sglebius		evbuffer_remove_buffer(buf, req->input_buffer, n);
1014290001Sglebius	}
1015290001Sglebius
1016290001Sglebius	if (req->body_size > req->evcon->max_body_size ||
1017290001Sglebius	    (!req->chunked && req->ntoread >= 0 &&
1018290001Sglebius		(size_t)req->ntoread > req->evcon->max_body_size)) {
1019290001Sglebius		/* XXX: The above casted comparison must checked for overflow */
1020290001Sglebius		/* failed body length test */
1021290001Sglebius		event_debug(("Request body is too long"));
1022290001Sglebius		evhttp_connection_fail_(evcon,
1023290001Sglebius				       EVREQ_HTTP_DATA_TOO_LONG);
1024290001Sglebius		return;
1025290001Sglebius	}
1026290001Sglebius
1027290001Sglebius	if (evbuffer_get_length(req->input_buffer) > 0 && req->chunk_cb != NULL) {
1028290001Sglebius		req->flags |= EVHTTP_REQ_DEFER_FREE;
1029290001Sglebius		(*req->chunk_cb)(req, req->cb_arg);
1030290001Sglebius		req->flags &= ~EVHTTP_REQ_DEFER_FREE;
1031290001Sglebius		evbuffer_drain(req->input_buffer,
1032290001Sglebius		    evbuffer_get_length(req->input_buffer));
1033290001Sglebius		if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) {
1034290001Sglebius			evhttp_request_free(req);
1035290001Sglebius			return;
1036290001Sglebius		}
1037290001Sglebius	}
1038290001Sglebius
1039290001Sglebius	if (req->ntoread == 0) {
1040290001Sglebius		bufferevent_disable(evcon->bufev, EV_READ);
1041290001Sglebius		/* Completed content length */
1042290001Sglebius		evhttp_connection_done(evcon);
1043290001Sglebius		return;
1044290001Sglebius	}
1045290001Sglebius
1046290001Sglebius	/* Read more! */
1047290001Sglebius	bufferevent_enable(evcon->bufev, EV_READ);
1048290001Sglebius}
1049290001Sglebius
1050290001Sglebius#define get_deferred_queue(evcon)		\
1051290001Sglebius	((evcon)->base)
1052290001Sglebius
1053290001Sglebius/*
1054290001Sglebius * Gets called when more data becomes available
1055290001Sglebius */
1056290001Sglebius
1057290001Sglebiusstatic void
1058290001Sglebiusevhttp_read_cb(struct bufferevent *bufev, void *arg)
1059290001Sglebius{
1060290001Sglebius	struct evhttp_connection *evcon = arg;
1061290001Sglebius	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
1062290001Sglebius
1063290001Sglebius	/* Cancel if it's pending. */
1064290001Sglebius	event_deferred_cb_cancel_(get_deferred_queue(evcon),
1065290001Sglebius	    &evcon->read_more_deferred_cb);
1066290001Sglebius
1067290001Sglebius	switch (evcon->state) {
1068290001Sglebius	case EVCON_READING_FIRSTLINE:
1069290001Sglebius		evhttp_read_firstline(evcon, req);
1070290001Sglebius		/* note the request may have been freed in
1071290001Sglebius		 * evhttp_read_body */
1072290001Sglebius		break;
1073290001Sglebius	case EVCON_READING_HEADERS:
1074290001Sglebius		evhttp_read_header(evcon, req);
1075290001Sglebius		/* note the request may have been freed in
1076290001Sglebius		 * evhttp_read_body */
1077290001Sglebius		break;
1078290001Sglebius	case EVCON_READING_BODY:
1079290001Sglebius		evhttp_read_body(evcon, req);
1080290001Sglebius		/* note the request may have been freed in
1081290001Sglebius		 * evhttp_read_body */
1082290001Sglebius		break;
1083290001Sglebius	case EVCON_READING_TRAILER:
1084290001Sglebius		evhttp_read_trailer(evcon, req);
1085290001Sglebius		break;
1086290001Sglebius	case EVCON_IDLE:
1087290001Sglebius		{
1088290001Sglebius#ifdef USE_DEBUG
1089290001Sglebius			struct evbuffer *input;
1090290001Sglebius			size_t total_len;
1091290001Sglebius
1092290001Sglebius			input = bufferevent_get_input(evcon->bufev);
1093290001Sglebius			total_len = evbuffer_get_length(input);
1094290001Sglebius			event_debug(("%s: read "EV_SIZE_FMT
1095290001Sglebius				" bytes in EVCON_IDLE state,"
1096290001Sglebius				" resetting connection",
1097290001Sglebius				__func__, EV_SIZE_ARG(total_len)));
1098290001Sglebius#endif
1099290001Sglebius
1100290001Sglebius			evhttp_connection_reset_(evcon);
1101290001Sglebius		}
1102290001Sglebius		break;
1103290001Sglebius	case EVCON_DISCONNECTED:
1104290001Sglebius	case EVCON_CONNECTING:
1105290001Sglebius	case EVCON_WRITING:
1106290001Sglebius	default:
1107290001Sglebius		event_errx(1, "%s: illegal connection state %d",
1108290001Sglebius			   __func__, evcon->state);
1109290001Sglebius	}
1110290001Sglebius}
1111290001Sglebius
1112290001Sglebiusstatic void
1113290001Sglebiusevhttp_deferred_read_cb(struct event_callback *cb, void *data)
1114290001Sglebius{
1115290001Sglebius	struct evhttp_connection *evcon = data;
1116290001Sglebius	evhttp_read_cb(evcon->bufev, evcon);
1117290001Sglebius}
1118290001Sglebius
1119290001Sglebiusstatic void
1120290001Sglebiusevhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
1121290001Sglebius{
1122290001Sglebius	/* This is after writing the request to the server */
1123290001Sglebius	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
1124290001Sglebius	EVUTIL_ASSERT(req != NULL);
1125290001Sglebius
1126290001Sglebius	EVUTIL_ASSERT(evcon->state == EVCON_WRITING);
1127290001Sglebius
1128290001Sglebius	/* We are done writing our header and are now expecting the response */
1129290001Sglebius	req->kind = EVHTTP_RESPONSE;
1130290001Sglebius
1131290001Sglebius	evhttp_start_read_(evcon);
1132290001Sglebius}
1133290001Sglebius
1134290001Sglebius/*
1135290001Sglebius * Clean up a connection object
1136290001Sglebius */
1137290001Sglebius
1138290001Sglebiusvoid
1139290001Sglebiusevhttp_connection_free(struct evhttp_connection *evcon)
1140290001Sglebius{
1141290001Sglebius	struct evhttp_request *req;
1142290001Sglebius
1143290001Sglebius	/* notify interested parties that this connection is going down */
1144290001Sglebius	if (evcon->fd != -1) {
1145290001Sglebius		if (evhttp_connected(evcon) && evcon->closecb != NULL)
1146290001Sglebius			(*evcon->closecb)(evcon, evcon->closecb_arg);
1147290001Sglebius	}
1148290001Sglebius
1149290001Sglebius	/* remove all requests that might be queued on this
1150290001Sglebius	 * connection.  for server connections, this should be empty.
1151290001Sglebius	 * because it gets dequeued either in evhttp_connection_done or
1152290001Sglebius	 * evhttp_connection_fail_.
1153290001Sglebius	 */
1154290001Sglebius	while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) {
1155290001Sglebius		TAILQ_REMOVE(&evcon->requests, req, next);
1156290001Sglebius		evhttp_request_free(req);
1157290001Sglebius	}
1158290001Sglebius
1159290001Sglebius	if (evcon->http_server != NULL) {
1160290001Sglebius		struct evhttp *http = evcon->http_server;
1161290001Sglebius		TAILQ_REMOVE(&http->connections, evcon, next);
1162290001Sglebius	}
1163290001Sglebius
1164290001Sglebius	if (event_initialized(&evcon->retry_ev)) {
1165290001Sglebius		event_del(&evcon->retry_ev);
1166290001Sglebius		event_debug_unassign(&evcon->retry_ev);
1167290001Sglebius	}
1168290001Sglebius
1169290001Sglebius	if (evcon->bufev != NULL)
1170290001Sglebius		bufferevent_free(evcon->bufev);
1171290001Sglebius
1172290001Sglebius	event_deferred_cb_cancel_(get_deferred_queue(evcon),
1173290001Sglebius	    &evcon->read_more_deferred_cb);
1174290001Sglebius
1175290001Sglebius	if (evcon->fd != -1) {
1176290001Sglebius		shutdown(evcon->fd, EVUTIL_SHUT_WR);
1177290001Sglebius		if (!(bufferevent_get_options_(evcon->bufev) & BEV_OPT_CLOSE_ON_FREE)) {
1178290001Sglebius			evutil_closesocket(evcon->fd);
1179290001Sglebius		}
1180290001Sglebius	}
1181290001Sglebius
1182290001Sglebius	if (evcon->bind_address != NULL)
1183290001Sglebius		mm_free(evcon->bind_address);
1184290001Sglebius
1185290001Sglebius	if (evcon->address != NULL)
1186290001Sglebius		mm_free(evcon->address);
1187290001Sglebius
1188290001Sglebius	if (evcon->conn_address != NULL)
1189290001Sglebius		mm_free(evcon->conn_address);
1190290001Sglebius
1191290001Sglebius	mm_free(evcon);
1192290001Sglebius}
1193290001Sglebius
1194290001Sglebiusvoid
1195290001Sglebiusevhttp_connection_free_on_completion(struct evhttp_connection *evcon) {
1196290001Sglebius	evcon->flags |= EVHTTP_CON_AUTOFREE;
1197290001Sglebius}
1198290001Sglebius
1199290001Sglebiusvoid
1200290001Sglebiusevhttp_connection_set_local_address(struct evhttp_connection *evcon,
1201290001Sglebius    const char *address)
1202290001Sglebius{
1203290001Sglebius	EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
1204290001Sglebius	if (evcon->bind_address)
1205290001Sglebius		mm_free(evcon->bind_address);
1206290001Sglebius	if ((evcon->bind_address = mm_strdup(address)) == NULL)
1207290001Sglebius		event_warn("%s: strdup", __func__);
1208290001Sglebius}
1209290001Sglebius
1210290001Sglebiusvoid
1211290001Sglebiusevhttp_connection_set_local_port(struct evhttp_connection *evcon,
1212290001Sglebius    ev_uint16_t port)
1213290001Sglebius{
1214290001Sglebius	EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
1215290001Sglebius	evcon->bind_port = port;
1216290001Sglebius}
1217290001Sglebius
1218290001Sglebiusstatic void
1219290001Sglebiusevhttp_request_dispatch(struct evhttp_connection* evcon)
1220290001Sglebius{
1221290001Sglebius	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
1222290001Sglebius
1223290001Sglebius	/* this should not usually happy but it's possible */
1224290001Sglebius	if (req == NULL)
1225290001Sglebius		return;
1226290001Sglebius
1227290001Sglebius	/* delete possible close detection events */
1228290001Sglebius	evhttp_connection_stop_detectclose(evcon);
1229290001Sglebius
1230290001Sglebius	/* we assume that the connection is connected already */
1231290001Sglebius	EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
1232290001Sglebius
1233290001Sglebius	evcon->state = EVCON_WRITING;
1234290001Sglebius
1235290001Sglebius	/* Create the header from the store arguments */
1236290001Sglebius	evhttp_make_header(evcon, req);
1237290001Sglebius
1238290001Sglebius	evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
1239290001Sglebius}
1240290001Sglebius
1241290001Sglebius/* Reset our connection state: disables reading/writing, closes our fd (if
1242290001Sglebius* any), clears out buffers, and puts us in state DISCONNECTED. */
1243290001Sglebiusvoid
1244290001Sglebiusevhttp_connection_reset_(struct evhttp_connection *evcon)
1245290001Sglebius{
1246290001Sglebius	struct evbuffer *tmp;
1247290001Sglebius
1248290001Sglebius	/* XXXX This is not actually an optimal fix.  Instead we ought to have
1249290001Sglebius	   an API for "stop connecting", or use bufferevent_setfd to turn off
1250290001Sglebius	   connecting.  But for Libevent 2.0, this seems like a minimal change
1251290001Sglebius	   least likely to disrupt the rest of the bufferevent and http code.
1252290001Sglebius
1253290001Sglebius	   Why is this here?  If the fd is set in the bufferevent, and the
1254290001Sglebius	   bufferevent is connecting, then you can't actually stop the
1255290001Sglebius	   bufferevent from trying to connect with bufferevent_disable().  The
1256290001Sglebius	   connect will never trigger, since we close the fd, but the timeout
1257290001Sglebius	   might.  That caused an assertion failure in evhttp_connection_fail_.
1258290001Sglebius	*/
1259290001Sglebius	bufferevent_disable_hard_(evcon->bufev, EV_READ|EV_WRITE);
1260290001Sglebius
1261290001Sglebius	if (evcon->fd != -1) {
1262290001Sglebius		/* inform interested parties about connection close */
1263290001Sglebius		if (evhttp_connected(evcon) && evcon->closecb != NULL)
1264290001Sglebius			(*evcon->closecb)(evcon, evcon->closecb_arg);
1265290001Sglebius
1266290001Sglebius		shutdown(evcon->fd, EVUTIL_SHUT_WR);
1267290001Sglebius		evutil_closesocket(evcon->fd);
1268290001Sglebius		bufferevent_setfd(evcon->bufev, -1);
1269290001Sglebius		evcon->fd = -1;
1270290001Sglebius	}
1271290001Sglebius
1272290001Sglebius	/* we need to clean up any buffered data */
1273290001Sglebius	tmp = bufferevent_get_output(evcon->bufev);
1274290001Sglebius	evbuffer_drain(tmp, evbuffer_get_length(tmp));
1275290001Sglebius	tmp = bufferevent_get_input(evcon->bufev);
1276290001Sglebius	evbuffer_drain(tmp, evbuffer_get_length(tmp));
1277290001Sglebius
1278290001Sglebius	evcon->state = EVCON_DISCONNECTED;
1279290001Sglebius}
1280290001Sglebius
1281290001Sglebiusstatic void
1282290001Sglebiusevhttp_connection_start_detectclose(struct evhttp_connection *evcon)
1283290001Sglebius{
1284290001Sglebius	evcon->flags |= EVHTTP_CON_CLOSEDETECT;
1285290001Sglebius
1286290001Sglebius	bufferevent_enable(evcon->bufev, EV_READ);
1287290001Sglebius}
1288290001Sglebius
1289290001Sglebiusstatic void
1290290001Sglebiusevhttp_connection_stop_detectclose(struct evhttp_connection *evcon)
1291290001Sglebius{
1292290001Sglebius	evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
1293290001Sglebius
1294290001Sglebius	bufferevent_disable(evcon->bufev, EV_READ);
1295290001Sglebius}
1296290001Sglebius
1297290001Sglebiusstatic void
1298290001Sglebiusevhttp_connection_retry(evutil_socket_t fd, short what, void *arg)
1299290001Sglebius{
1300290001Sglebius	struct evhttp_connection *evcon = arg;
1301290001Sglebius
1302290001Sglebius	evcon->state = EVCON_DISCONNECTED;
1303290001Sglebius	evhttp_connection_connect_(evcon);
1304290001Sglebius}
1305290001Sglebius
1306290001Sglebiusstatic void
1307290001Sglebiusevhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
1308290001Sglebius{
1309290001Sglebius	struct evcon_requestq requests;
1310290001Sglebius
1311290001Sglebius	evhttp_connection_reset_(evcon);
1312290001Sglebius	if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
1313290001Sglebius		struct timeval tv_retry = evcon->initial_retry_timeout;
1314290001Sglebius		int i;
1315290001Sglebius		evtimer_assign(&evcon->retry_ev, evcon->base, evhttp_connection_retry, evcon);
1316290001Sglebius		/* XXXX handle failure from evhttp_add_event */
1317290001Sglebius		for (i=0; i < evcon->retry_cnt; ++i) {
1318290001Sglebius			tv_retry.tv_usec *= 2;
1319290001Sglebius			if (tv_retry.tv_usec > 1000000) {
1320290001Sglebius				tv_retry.tv_usec -= 1000000;
1321290001Sglebius				tv_retry.tv_sec += 1;
1322290001Sglebius			}
1323290001Sglebius			tv_retry.tv_sec *= 2;
1324290001Sglebius			if (tv_retry.tv_sec > 3600) {
1325290001Sglebius				tv_retry.tv_sec = 3600;
1326290001Sglebius				tv_retry.tv_usec = 0;
1327290001Sglebius			}
1328290001Sglebius		}
1329290001Sglebius		event_add(&evcon->retry_ev, &tv_retry);
1330290001Sglebius		evcon->retry_cnt++;
1331290001Sglebius		return;
1332290001Sglebius	}
1333290001Sglebius
1334290001Sglebius	/*
1335290001Sglebius	 * User callback can do evhttp_make_request() on the same
1336290001Sglebius	 * evcon so new request will be added to evcon->requests.  To
1337290001Sglebius	 * avoid freeing it prematurely we iterate over the copy of
1338290001Sglebius	 * the queue.
1339290001Sglebius	 */
1340290001Sglebius	TAILQ_INIT(&requests);
1341290001Sglebius	while (TAILQ_FIRST(&evcon->requests) != NULL) {
1342290001Sglebius		struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
1343290001Sglebius		TAILQ_REMOVE(&evcon->requests, request, next);
1344290001Sglebius		TAILQ_INSERT_TAIL(&requests, request, next);
1345290001Sglebius	}
1346290001Sglebius
1347290001Sglebius	/* for now, we just signal all requests by executing their callbacks */
1348290001Sglebius	while (TAILQ_FIRST(&requests) != NULL) {
1349290001Sglebius		struct evhttp_request *request = TAILQ_FIRST(&requests);
1350290001Sglebius		TAILQ_REMOVE(&requests, request, next);
1351290001Sglebius		request->evcon = NULL;
1352290001Sglebius
1353290001Sglebius		/* we might want to set an error here */
1354290001Sglebius		request->cb(request, request->cb_arg);
1355290001Sglebius		evhttp_request_free(request);
1356290001Sglebius	}
1357290001Sglebius}
1358290001Sglebius
1359290001Sglebiusstatic void
1360290001Sglebiusevhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
1361290001Sglebius{
1362290001Sglebius	struct evhttp_connection *evcon = arg;
1363290001Sglebius	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
1364290001Sglebius
1365290001Sglebius	if (evcon->fd == -1)
1366290001Sglebius		evcon->fd = bufferevent_getfd(bufev);
1367290001Sglebius
1368290001Sglebius	switch (evcon->state) {
1369290001Sglebius	case EVCON_CONNECTING:
1370290001Sglebius		if (what & BEV_EVENT_TIMEOUT) {
1371290001Sglebius			event_debug(("%s: connection timeout for \"%s:%d\" on "
1372290001Sglebius				EV_SOCK_FMT,
1373290001Sglebius				__func__, evcon->address, evcon->port,
1374290001Sglebius				EV_SOCK_ARG(evcon->fd)));
1375290001Sglebius			evhttp_connection_cb_cleanup(evcon);
1376290001Sglebius			return;
1377290001Sglebius		}
1378290001Sglebius		break;
1379290001Sglebius
1380290001Sglebius	case EVCON_READING_BODY:
1381290001Sglebius		if (!req->chunked && req->ntoread < 0
1382290001Sglebius		    && what == (BEV_EVENT_READING|BEV_EVENT_EOF)) {
1383290001Sglebius			/* EOF on read can be benign */
1384290001Sglebius			evhttp_connection_done(evcon);
1385290001Sglebius			return;
1386290001Sglebius		}
1387290001Sglebius		break;
1388290001Sglebius
1389290001Sglebius	case EVCON_DISCONNECTED:
1390290001Sglebius	case EVCON_IDLE:
1391290001Sglebius	case EVCON_READING_FIRSTLINE:
1392290001Sglebius	case EVCON_READING_HEADERS:
1393290001Sglebius	case EVCON_READING_TRAILER:
1394290001Sglebius	case EVCON_WRITING:
1395290001Sglebius	default:
1396290001Sglebius		break;
1397290001Sglebius	}
1398290001Sglebius
1399290001Sglebius	/* when we are in close detect mode, a read error means that
1400290001Sglebius	 * the other side closed their connection.
1401290001Sglebius	 */
1402290001Sglebius	if (evcon->flags & EVHTTP_CON_CLOSEDETECT) {
1403290001Sglebius		evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
1404290001Sglebius		EVUTIL_ASSERT(evcon->http_server == NULL);
1405290001Sglebius		/* For connections from the client, we just
1406290001Sglebius		 * reset the connection so that it becomes
1407290001Sglebius		 * disconnected.
1408290001Sglebius		 */
1409290001Sglebius		EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
1410290001Sglebius		evhttp_connection_reset_(evcon);
1411290001Sglebius
1412290001Sglebius		/*
1413290001Sglebius		 * If we have no more requests that need completion
1414290001Sglebius		 * and we want to auto-free the connection when all
1415290001Sglebius		 * requests have been completed.
1416290001Sglebius		 */
1417290001Sglebius		if (TAILQ_FIRST(&evcon->requests) == NULL
1418290001Sglebius		  && (evcon->flags & EVHTTP_CON_OUTGOING)
1419290001Sglebius		  && (evcon->flags & EVHTTP_CON_AUTOFREE)) {
1420290001Sglebius			evhttp_connection_free(evcon);
1421290001Sglebius		}
1422290001Sglebius		return;
1423290001Sglebius	}
1424290001Sglebius
1425290001Sglebius	if (what & BEV_EVENT_TIMEOUT) {
1426290001Sglebius		evhttp_connection_fail_(evcon, EVREQ_HTTP_TIMEOUT);
1427290001Sglebius	} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
1428290001Sglebius		evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
1429290001Sglebius	} else if (what == BEV_EVENT_CONNECTED) {
1430290001Sglebius	} else {
1431290001Sglebius		evhttp_connection_fail_(evcon, EVREQ_HTTP_BUFFER_ERROR);
1432290001Sglebius	}
1433290001Sglebius}
1434290001Sglebius
1435290001Sglebius/*
1436290001Sglebius * Event callback for asynchronous connection attempt.
1437290001Sglebius */
1438290001Sglebiusstatic void
1439290001Sglebiusevhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
1440290001Sglebius{
1441290001Sglebius	struct evhttp_connection *evcon = arg;
1442290001Sglebius	int error;
1443290001Sglebius	ev_socklen_t errsz = sizeof(error);
1444290001Sglebius	socklen_t conn_address_len = sizeof(*evcon->conn_address);
1445290001Sglebius
1446290001Sglebius	if (evcon->fd == -1)
1447290001Sglebius		evcon->fd = bufferevent_getfd(bufev);
1448290001Sglebius
1449290001Sglebius	if (!(what & BEV_EVENT_CONNECTED)) {
1450290001Sglebius		/* some operating systems return ECONNREFUSED immediately
1451290001Sglebius		 * when connecting to a local address.  the cleanup is going
1452290001Sglebius		 * to reschedule this function call.
1453290001Sglebius		 */
1454290001Sglebius#ifndef _WIN32
1455290001Sglebius		if (errno == ECONNREFUSED)
1456290001Sglebius			goto cleanup;
1457290001Sglebius#endif
1458290001Sglebius		evhttp_error_cb(bufev, what, arg);
1459290001Sglebius		return;
1460290001Sglebius	}
1461290001Sglebius
1462290001Sglebius	if (evcon->fd == -1) {
1463290001Sglebius		event_debug(("%s: bufferevent_getfd returned -1",
1464290001Sglebius			__func__));
1465290001Sglebius		goto cleanup;
1466290001Sglebius	}
1467290001Sglebius
1468290001Sglebius	/* Check if the connection completed */
1469290001Sglebius	if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
1470290001Sglebius		       &errsz) == -1) {
1471290001Sglebius		event_debug(("%s: getsockopt for \"%s:%d\" on "EV_SOCK_FMT,
1472290001Sglebius			__func__, evcon->address, evcon->port,
1473290001Sglebius			EV_SOCK_ARG(evcon->fd)));
1474290001Sglebius		goto cleanup;
1475290001Sglebius	}
1476290001Sglebius
1477290001Sglebius	if (error) {
1478290001Sglebius		event_debug(("%s: connect failed for \"%s:%d\" on "
1479290001Sglebius			EV_SOCK_FMT": %s",
1480290001Sglebius			__func__, evcon->address, evcon->port,
1481290001Sglebius			EV_SOCK_ARG(evcon->fd),
1482290001Sglebius			evutil_socket_error_to_string(error)));
1483290001Sglebius		goto cleanup;
1484290001Sglebius	}
1485290001Sglebius
1486290001Sglebius	/* We are connected to the server now */
1487290001Sglebius	event_debug(("%s: connected to \"%s:%d\" on "EV_SOCK_FMT"\n",
1488290001Sglebius			__func__, evcon->address, evcon->port,
1489290001Sglebius			EV_SOCK_ARG(evcon->fd)));
1490290001Sglebius
1491290001Sglebius	/* Reset the retry count as we were successful in connecting */
1492290001Sglebius	evcon->retry_cnt = 0;
1493290001Sglebius	evcon->state = EVCON_IDLE;
1494290001Sglebius
1495290001Sglebius	if (!evcon->conn_address) {
1496290001Sglebius		evcon->conn_address = mm_malloc(sizeof(*evcon->conn_address));
1497290001Sglebius	}
1498290001Sglebius	if (getpeername(evcon->fd, (struct sockaddr *)evcon->conn_address, &conn_address_len)) {
1499290001Sglebius		mm_free(evcon->conn_address);
1500290001Sglebius		evcon->conn_address = NULL;
1501290001Sglebius	}
1502290001Sglebius
1503290001Sglebius	/* reset the bufferevent cbs */
1504290001Sglebius	bufferevent_setcb(evcon->bufev,
1505290001Sglebius	    evhttp_read_cb,
1506290001Sglebius	    evhttp_write_cb,
1507290001Sglebius	    evhttp_error_cb,
1508290001Sglebius	    evcon);
1509290001Sglebius
1510290001Sglebius	if (!evutil_timerisset(&evcon->timeout)) {
1511290001Sglebius		const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
1512290001Sglebius		const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
1513290001Sglebius		bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
1514290001Sglebius	} else {
1515290001Sglebius		bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
1516290001Sglebius	}
1517290001Sglebius
1518290001Sglebius	/* try to start requests that have queued up on this connection */
1519290001Sglebius	evhttp_request_dispatch(evcon);
1520290001Sglebius	return;
1521290001Sglebius
1522290001Sglebius cleanup:
1523290001Sglebius	evhttp_connection_cb_cleanup(evcon);
1524290001Sglebius}
1525290001Sglebius
1526290001Sglebius/*
1527290001Sglebius * Check if we got a valid response code.
1528290001Sglebius */
1529290001Sglebius
1530290001Sglebiusstatic int
1531290001Sglebiusevhttp_valid_response_code(int code)
1532290001Sglebius{
1533290001Sglebius	if (code == 0)
1534290001Sglebius		return (0);
1535290001Sglebius
1536290001Sglebius	return (1);
1537290001Sglebius}
1538290001Sglebius
1539290001Sglebiusstatic int
1540290001Sglebiusevhttp_parse_http_version(const char *version, struct evhttp_request *req)
1541290001Sglebius{
1542290001Sglebius	int major, minor;
1543290001Sglebius	char ch;
1544290001Sglebius	int n = sscanf(version, "HTTP/%d.%d%c", &major, &minor, &ch);
1545290001Sglebius	if (n != 2 || major > 1) {
1546290001Sglebius		event_debug(("%s: bad version %s on message %p from %s",
1547290001Sglebius			__func__, version, req, req->remote_host));
1548290001Sglebius		return (-1);
1549290001Sglebius	}
1550290001Sglebius	req->major = major;
1551290001Sglebius	req->minor = minor;
1552290001Sglebius	return (0);
1553290001Sglebius}
1554290001Sglebius
1555290001Sglebius/* Parses the status line of a web server */
1556290001Sglebius
1557290001Sglebiusstatic int
1558290001Sglebiusevhttp_parse_response_line(struct evhttp_request *req, char *line)
1559290001Sglebius{
1560290001Sglebius	char *protocol;
1561290001Sglebius	char *number;
1562290001Sglebius	const char *readable = "";
1563290001Sglebius
1564290001Sglebius	protocol = strsep(&line, " ");
1565290001Sglebius	if (line == NULL)
1566290001Sglebius		return (-1);
1567290001Sglebius	number = strsep(&line, " ");
1568290001Sglebius	if (line != NULL)
1569290001Sglebius		readable = line;
1570290001Sglebius
1571290001Sglebius	if (evhttp_parse_http_version(protocol, req) < 0)
1572290001Sglebius		return (-1);
1573290001Sglebius
1574290001Sglebius	req->response_code = atoi(number);
1575290001Sglebius	if (!evhttp_valid_response_code(req->response_code)) {
1576290001Sglebius		event_debug(("%s: bad response code \"%s\"",
1577290001Sglebius			__func__, number));
1578290001Sglebius		return (-1);
1579290001Sglebius	}
1580290001Sglebius
1581290001Sglebius	if ((req->response_code_line = mm_strdup(readable)) == NULL) {
1582290001Sglebius		event_warn("%s: strdup", __func__);
1583290001Sglebius		return (-1);
1584290001Sglebius	}
1585290001Sglebius
1586290001Sglebius	return (0);
1587290001Sglebius}
1588290001Sglebius
1589290001Sglebius/* Parse the first line of a HTTP request */
1590290001Sglebius
1591290001Sglebiusstatic int
1592290001Sglebiusevhttp_parse_request_line(struct evhttp_request *req, char *line)
1593290001Sglebius{
1594290001Sglebius	char *method;
1595290001Sglebius	char *uri;
1596290001Sglebius	char *version;
1597290001Sglebius	const char *hostname;
1598290001Sglebius	const char *scheme;
1599290001Sglebius	size_t method_len;
1600290001Sglebius	enum evhttp_cmd_type type;
1601290001Sglebius
1602290001Sglebius	/* Parse the request line */
1603290001Sglebius	method = strsep(&line, " ");
1604290001Sglebius	if (line == NULL)
1605290001Sglebius		return (-1);
1606290001Sglebius	uri = strsep(&line, " ");
1607290001Sglebius	if (line == NULL)
1608290001Sglebius		return (-1);
1609290001Sglebius	version = strsep(&line, " ");
1610290001Sglebius	if (line != NULL)
1611290001Sglebius		return (-1);
1612290001Sglebius
1613290001Sglebius	method_len = (uri - method) - 1;
1614290001Sglebius	type       = EVHTTP_REQ_UNKNOWN_;
1615290001Sglebius
1616290001Sglebius	/* First line */
1617290001Sglebius	switch (method_len) {
1618290001Sglebius	    case 3:
1619290001Sglebius		/* The length of the method string is 3, meaning it can only be one of two methods: GET or PUT */
1620290001Sglebius
1621290001Sglebius		/* Since both GET and PUT share the same character 'T' at the end,
1622290001Sglebius		 * if the string doesn't have 'T', we can immediately determine this
1623290001Sglebius		 * is an invalid HTTP method */
1624290001Sglebius
1625290001Sglebius		if (method[2] != 'T') {
1626290001Sglebius		    break;
1627290001Sglebius		}
1628290001Sglebius
1629290001Sglebius		switch (*method) {
1630290001Sglebius		    case 'G':
1631290001Sglebius			/* This first byte is 'G', so make sure the next byte is
1632290001Sglebius			 * 'E', if it isn't then this isn't a valid method */
1633290001Sglebius
1634290001Sglebius			if (method[1] == 'E') {
1635290001Sglebius			    type = EVHTTP_REQ_GET;
1636290001Sglebius			}
1637290001Sglebius
1638290001Sglebius			break;
1639290001Sglebius		    case 'P':
1640290001Sglebius			/* First byte is P, check second byte for 'U', if not,
1641290001Sglebius			 * we know it's an invalid method */
1642290001Sglebius			if (method[1] == 'U') {
1643290001Sglebius			    type = EVHTTP_REQ_PUT;
1644290001Sglebius			}
1645290001Sglebius			break;
1646290001Sglebius		    default:
1647290001Sglebius			break;
1648290001Sglebius		}
1649290001Sglebius		break;
1650290001Sglebius	    case 4:
1651290001Sglebius		/* The method length is 4 bytes, leaving only the methods "POST" and "HEAD" */
1652290001Sglebius		switch (*method) {
1653290001Sglebius		    case 'P':
1654290001Sglebius			if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
1655290001Sglebius			    type = EVHTTP_REQ_POST;
1656290001Sglebius			}
1657290001Sglebius			break;
1658290001Sglebius		    case 'H':
1659290001Sglebius			if (method[3] == 'D' && method[2] == 'A' && method[1] == 'E') {
1660290001Sglebius			    type = EVHTTP_REQ_HEAD;
1661290001Sglebius			}
1662290001Sglebius			break;
1663290001Sglebius		    default:
1664290001Sglebius			break;
1665290001Sglebius		}
1666290001Sglebius		break;
1667290001Sglebius	    case 5:
1668290001Sglebius		/* Method length is 5 bytes, which can only encompass PATCH and TRACE */
1669290001Sglebius		switch (*method) {
1670290001Sglebius		    case 'P':
1671290001Sglebius			if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
1672290001Sglebius			    type = EVHTTP_REQ_PATCH;
1673290001Sglebius			}
1674290001Sglebius			break;
1675290001Sglebius		    case 'T':
1676290001Sglebius			if (method[4] == 'E' && method[3] == 'C' && method[2] == 'A' && method[1] == 'R') {
1677290001Sglebius			    type = EVHTTP_REQ_TRACE;
1678290001Sglebius			}
1679290001Sglebius
1680290001Sglebius			break;
1681290001Sglebius		    default:
1682290001Sglebius			break;
1683290001Sglebius		}
1684290001Sglebius		break;
1685290001Sglebius	    case 6:
1686290001Sglebius		/* Method length is 6, only valid method 6 bytes in length is DELEte */
1687290001Sglebius
1688290001Sglebius		/* If the first byte isn't 'D' then it's invalid */
1689290001Sglebius		if (*method != 'D') {
1690290001Sglebius		    break;
1691290001Sglebius		}
1692290001Sglebius
1693290001Sglebius		if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' && method[2] == 'L' && method[1] == 'E') {
1694290001Sglebius		    type = EVHTTP_REQ_DELETE;
1695290001Sglebius		}
1696290001Sglebius
1697290001Sglebius		break;
1698290001Sglebius	    case 7:
1699290001Sglebius		/* Method length is 7, only valid methods are "OPTIONS" and "CONNECT" */
1700290001Sglebius		switch (*method) {
1701290001Sglebius		    case 'O':
1702290001Sglebius			if (method[6] == 'S' && method[5] == 'N' && method[4] == 'O' &&
1703290001Sglebius				method[3] == 'I' && method[2] == 'T' && method[1] == 'P') {
1704290001Sglebius			    type = EVHTTP_REQ_OPTIONS;
1705290001Sglebius			}
1706290001Sglebius
1707290001Sglebius		       	break;
1708290001Sglebius		    case 'C':
1709290001Sglebius			if (method[6] == 'T' && method[5] == 'C' && method[4] == 'E' &&
1710290001Sglebius				method[3] == 'N' && method[2] == 'N' && method[1] == 'O') {
1711290001Sglebius			    type = EVHTTP_REQ_CONNECT;
1712290001Sglebius			}
1713290001Sglebius
1714290001Sglebius			break;
1715290001Sglebius		    default:
1716290001Sglebius			break;
1717290001Sglebius		}
1718290001Sglebius		break;
1719290001Sglebius	} /* switch */
1720290001Sglebius
1721290001Sglebius	if ((int)type == EVHTTP_REQ_UNKNOWN_) {
1722290001Sglebius	        event_debug(("%s: bad method %s on request %p from %s",
1723290001Sglebius			__func__, method, req, req->remote_host));
1724290001Sglebius                /* No error yet; we'll give a better error later when
1725290001Sglebius                 * we see that req->type is unsupported. */
1726290001Sglebius	}
1727290001Sglebius
1728290001Sglebius	req->type = type;
1729290001Sglebius
1730290001Sglebius	if (evhttp_parse_http_version(version, req) < 0)
1731290001Sglebius		return (-1);
1732290001Sglebius
1733290001Sglebius	if ((req->uri = mm_strdup(uri)) == NULL) {
1734290001Sglebius		event_debug(("%s: mm_strdup", __func__));
1735290001Sglebius		return (-1);
1736290001Sglebius	}
1737290001Sglebius
1738290001Sglebius	if ((req->uri_elems = evhttp_uri_parse_with_flags(req->uri,
1739290001Sglebius		    EVHTTP_URI_NONCONFORMANT)) == NULL) {
1740290001Sglebius		return -1;
1741290001Sglebius	}
1742290001Sglebius
1743290001Sglebius	/* If we have an absolute-URI, check to see if it is an http request
1744290001Sglebius	   for a known vhost or server alias. If we don't know about this
1745290001Sglebius	   host, we consider it a proxy request. */
1746290001Sglebius	scheme = evhttp_uri_get_scheme(req->uri_elems);
1747290001Sglebius	hostname = evhttp_uri_get_host(req->uri_elems);
1748290001Sglebius	if (scheme && (!evutil_ascii_strcasecmp(scheme, "http") ||
1749290001Sglebius		       !evutil_ascii_strcasecmp(scheme, "https")) &&
1750290001Sglebius	    hostname &&
1751290001Sglebius	    !evhttp_find_vhost(req->evcon->http_server, NULL, hostname))
1752290001Sglebius		req->flags |= EVHTTP_PROXY_REQUEST;
1753290001Sglebius
1754290001Sglebius	return (0);
1755290001Sglebius}
1756290001Sglebius
1757290001Sglebiusconst char *
1758290001Sglebiusevhttp_find_header(const struct evkeyvalq *headers, const char *key)
1759290001Sglebius{
1760290001Sglebius	struct evkeyval *header;
1761290001Sglebius
1762290001Sglebius	TAILQ_FOREACH(header, headers, next) {
1763290001Sglebius		if (evutil_ascii_strcasecmp(header->key, key) == 0)
1764290001Sglebius			return (header->value);
1765290001Sglebius	}
1766290001Sglebius
1767290001Sglebius	return (NULL);
1768290001Sglebius}
1769290001Sglebius
1770290001Sglebiusvoid
1771290001Sglebiusevhttp_clear_headers(struct evkeyvalq *headers)
1772290001Sglebius{
1773290001Sglebius	struct evkeyval *header;
1774290001Sglebius
1775290001Sglebius	for (header = TAILQ_FIRST(headers);
1776290001Sglebius	    header != NULL;
1777290001Sglebius	    header = TAILQ_FIRST(headers)) {
1778290001Sglebius		TAILQ_REMOVE(headers, header, next);
1779290001Sglebius		mm_free(header->key);
1780290001Sglebius		mm_free(header->value);
1781290001Sglebius		mm_free(header);
1782290001Sglebius	}
1783290001Sglebius}
1784290001Sglebius
1785290001Sglebius/*
1786290001Sglebius * Returns 0,  if the header was successfully removed.
1787290001Sglebius * Returns -1, if the header could not be found.
1788290001Sglebius */
1789290001Sglebius
1790290001Sglebiusint
1791290001Sglebiusevhttp_remove_header(struct evkeyvalq *headers, const char *key)
1792290001Sglebius{
1793290001Sglebius	struct evkeyval *header;
1794290001Sglebius
1795290001Sglebius	TAILQ_FOREACH(header, headers, next) {
1796290001Sglebius		if (evutil_ascii_strcasecmp(header->key, key) == 0)
1797290001Sglebius			break;
1798290001Sglebius	}
1799290001Sglebius
1800290001Sglebius	if (header == NULL)
1801290001Sglebius		return (-1);
1802290001Sglebius
1803290001Sglebius	/* Free and remove the header that we found */
1804290001Sglebius	TAILQ_REMOVE(headers, header, next);
1805290001Sglebius	mm_free(header->key);
1806290001Sglebius	mm_free(header->value);
1807290001Sglebius	mm_free(header);
1808290001Sglebius
1809290001Sglebius	return (0);
1810290001Sglebius}
1811290001Sglebius
1812290001Sglebiusstatic int
1813290001Sglebiusevhttp_header_is_valid_value(const char *value)
1814290001Sglebius{
1815290001Sglebius	const char *p = value;
1816290001Sglebius
1817290001Sglebius	while ((p = strpbrk(p, "\r\n")) != NULL) {
1818290001Sglebius		/* we really expect only one new line */
1819290001Sglebius		p += strspn(p, "\r\n");
1820290001Sglebius		/* we expect a space or tab for continuation */
1821290001Sglebius		if (*p != ' ' && *p != '\t')
1822290001Sglebius			return (0);
1823290001Sglebius	}
1824290001Sglebius	return (1);
1825290001Sglebius}
1826290001Sglebius
1827290001Sglebiusint
1828290001Sglebiusevhttp_add_header(struct evkeyvalq *headers,
1829290001Sglebius    const char *key, const char *value)
1830290001Sglebius{
1831290001Sglebius	event_debug(("%s: key: %s val: %s\n", __func__, key, value));
1832290001Sglebius
1833290001Sglebius	if (strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
1834290001Sglebius		/* drop illegal headers */
1835290001Sglebius		event_debug(("%s: dropping illegal header key\n", __func__));
1836290001Sglebius		return (-1);
1837290001Sglebius	}
1838290001Sglebius
1839290001Sglebius	if (!evhttp_header_is_valid_value(value)) {
1840290001Sglebius		event_debug(("%s: dropping illegal header value\n", __func__));
1841290001Sglebius		return (-1);
1842290001Sglebius	}
1843290001Sglebius
1844290001Sglebius	return (evhttp_add_header_internal(headers, key, value));
1845290001Sglebius}
1846290001Sglebius
1847290001Sglebiusstatic int
1848290001Sglebiusevhttp_add_header_internal(struct evkeyvalq *headers,
1849290001Sglebius    const char *key, const char *value)
1850290001Sglebius{
1851290001Sglebius	struct evkeyval *header = mm_calloc(1, sizeof(struct evkeyval));
1852290001Sglebius	if (header == NULL) {
1853290001Sglebius		event_warn("%s: calloc", __func__);
1854290001Sglebius		return (-1);
1855290001Sglebius	}
1856290001Sglebius	if ((header->key = mm_strdup(key)) == NULL) {
1857290001Sglebius		mm_free(header);
1858290001Sglebius		event_warn("%s: strdup", __func__);
1859290001Sglebius		return (-1);
1860290001Sglebius	}
1861290001Sglebius	if ((header->value = mm_strdup(value)) == NULL) {
1862290001Sglebius		mm_free(header->key);
1863290001Sglebius		mm_free(header);
1864290001Sglebius		event_warn("%s: strdup", __func__);
1865290001Sglebius		return (-1);
1866290001Sglebius	}
1867290001Sglebius
1868290001Sglebius	TAILQ_INSERT_TAIL(headers, header, next);
1869290001Sglebius
1870290001Sglebius	return (0);
1871290001Sglebius}
1872290001Sglebius
1873290001Sglebius/*
1874290001Sglebius * Parses header lines from a request or a response into the specified
1875290001Sglebius * request object given an event buffer.
1876290001Sglebius *
1877290001Sglebius * Returns
1878290001Sglebius *   DATA_CORRUPTED      on error
1879290001Sglebius *   MORE_DATA_EXPECTED  when we need to read more headers
1880290001Sglebius *   ALL_DATA_READ       when all headers have been read.
1881290001Sglebius */
1882290001Sglebius
1883290001Sglebiusenum message_read_status
1884290001Sglebiusevhttp_parse_firstline_(struct evhttp_request *req, struct evbuffer *buffer)
1885290001Sglebius{
1886290001Sglebius	char *line;
1887290001Sglebius	enum message_read_status status = ALL_DATA_READ;
1888290001Sglebius
1889290001Sglebius	size_t line_length;
1890290001Sglebius	/* XXX try */
1891290001Sglebius	line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF);
1892290001Sglebius	if (line == NULL) {
1893290001Sglebius		if (req->evcon != NULL &&
1894290001Sglebius		    evbuffer_get_length(buffer) > req->evcon->max_headers_size)
1895290001Sglebius			return (DATA_TOO_LONG);
1896290001Sglebius		else
1897290001Sglebius			return (MORE_DATA_EXPECTED);
1898290001Sglebius	}
1899290001Sglebius
1900290001Sglebius	if (req->evcon != NULL &&
1901290001Sglebius	    line_length > req->evcon->max_headers_size) {
1902290001Sglebius		mm_free(line);
1903290001Sglebius		return (DATA_TOO_LONG);
1904290001Sglebius	}
1905290001Sglebius
1906290001Sglebius	req->headers_size = line_length;
1907290001Sglebius
1908290001Sglebius	switch (req->kind) {
1909290001Sglebius	case EVHTTP_REQUEST:
1910290001Sglebius		if (evhttp_parse_request_line(req, line) == -1)
1911290001Sglebius			status = DATA_CORRUPTED;
1912290001Sglebius		break;
1913290001Sglebius	case EVHTTP_RESPONSE:
1914290001Sglebius		if (evhttp_parse_response_line(req, line) == -1)
1915290001Sglebius			status = DATA_CORRUPTED;
1916290001Sglebius		break;
1917290001Sglebius	default:
1918290001Sglebius		status = DATA_CORRUPTED;
1919290001Sglebius	}
1920290001Sglebius
1921290001Sglebius	mm_free(line);
1922290001Sglebius	return (status);
1923290001Sglebius}
1924290001Sglebius
1925290001Sglebiusstatic int
1926290001Sglebiusevhttp_append_to_last_header(struct evkeyvalq *headers, char *line)
1927290001Sglebius{
1928290001Sglebius	struct evkeyval *header = TAILQ_LAST(headers, evkeyvalq);
1929290001Sglebius	char *newval;
1930290001Sglebius	size_t old_len, line_len;
1931290001Sglebius
1932290001Sglebius	if (header == NULL)
1933290001Sglebius		return (-1);
1934290001Sglebius
1935290001Sglebius	old_len = strlen(header->value);
1936290001Sglebius
1937290001Sglebius	/* Strip space from start and end of line. */
1938290001Sglebius	while (*line == ' ' || *line == '\t')
1939290001Sglebius		++line;
1940290001Sglebius	evutil_rtrim_lws_(line);
1941290001Sglebius
1942290001Sglebius	line_len = strlen(line);
1943290001Sglebius
1944290001Sglebius	newval = mm_realloc(header->value, old_len + line_len + 2);
1945290001Sglebius	if (newval == NULL)
1946290001Sglebius		return (-1);
1947290001Sglebius
1948290001Sglebius	newval[old_len] = ' ';
1949290001Sglebius	memcpy(newval + old_len + 1, line, line_len + 1);
1950290001Sglebius	header->value = newval;
1951290001Sglebius
1952290001Sglebius	return (0);
1953290001Sglebius}
1954290001Sglebius
1955290001Sglebiusenum message_read_status
1956290001Sglebiusevhttp_parse_headers_(struct evhttp_request *req, struct evbuffer* buffer)
1957290001Sglebius{
1958290001Sglebius	enum message_read_status errcode = DATA_CORRUPTED;
1959290001Sglebius	char *line;
1960290001Sglebius	enum message_read_status status = MORE_DATA_EXPECTED;
1961290001Sglebius
1962290001Sglebius	struct evkeyvalq* headers = req->input_headers;
1963290001Sglebius	size_t line_length;
1964290001Sglebius	while ((line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF))
1965290001Sglebius	       != NULL) {
1966290001Sglebius		char *skey, *svalue;
1967290001Sglebius
1968290001Sglebius		req->headers_size += line_length;
1969290001Sglebius
1970290001Sglebius		if (req->evcon != NULL &&
1971290001Sglebius		    req->headers_size > req->evcon->max_headers_size) {
1972290001Sglebius			errcode = DATA_TOO_LONG;
1973290001Sglebius			goto error;
1974290001Sglebius		}
1975290001Sglebius
1976290001Sglebius		if (*line == '\0') { /* Last header - Done */
1977290001Sglebius			status = ALL_DATA_READ;
1978290001Sglebius			mm_free(line);
1979290001Sglebius			break;
1980290001Sglebius		}
1981290001Sglebius
1982290001Sglebius		/* Check if this is a continuation line */
1983290001Sglebius		if (*line == ' ' || *line == '\t') {
1984290001Sglebius			if (evhttp_append_to_last_header(headers, line) == -1)
1985290001Sglebius				goto error;
1986290001Sglebius			mm_free(line);
1987290001Sglebius			continue;
1988290001Sglebius		}
1989290001Sglebius
1990290001Sglebius		/* Processing of header lines */
1991290001Sglebius		svalue = line;
1992290001Sglebius		skey = strsep(&svalue, ":");
1993290001Sglebius		if (svalue == NULL)
1994290001Sglebius			goto error;
1995290001Sglebius
1996290001Sglebius		svalue += strspn(svalue, " ");
1997290001Sglebius		evutil_rtrim_lws_(svalue);
1998290001Sglebius
1999290001Sglebius		if (evhttp_add_header(headers, skey, svalue) == -1)
2000290001Sglebius			goto error;
2001290001Sglebius
2002290001Sglebius		mm_free(line);
2003290001Sglebius	}
2004290001Sglebius
2005290001Sglebius	if (status == MORE_DATA_EXPECTED) {
2006290001Sglebius		if (req->evcon != NULL &&
2007290001Sglebius		req->headers_size + evbuffer_get_length(buffer) > req->evcon->max_headers_size)
2008290001Sglebius			return (DATA_TOO_LONG);
2009290001Sglebius	}
2010290001Sglebius
2011290001Sglebius	return (status);
2012290001Sglebius
2013290001Sglebius error:
2014290001Sglebius	mm_free(line);
2015290001Sglebius	return (errcode);
2016290001Sglebius}
2017290001Sglebius
2018290001Sglebiusstatic int
2019290001Sglebiusevhttp_get_body_length(struct evhttp_request *req)
2020290001Sglebius{
2021290001Sglebius	struct evkeyvalq *headers = req->input_headers;
2022290001Sglebius	const char *content_length;
2023290001Sglebius	const char *connection;
2024290001Sglebius
2025290001Sglebius	content_length = evhttp_find_header(headers, "Content-Length");
2026290001Sglebius	connection = evhttp_find_header(headers, "Connection");
2027290001Sglebius
2028290001Sglebius	if (content_length == NULL && connection == NULL)
2029290001Sglebius		req->ntoread = -1;
2030290001Sglebius	else if (content_length == NULL &&
2031290001Sglebius	    evutil_ascii_strcasecmp(connection, "Close") != 0) {
2032290001Sglebius		/* Bad combination, we don't know when it will end */
2033290001Sglebius		event_warnx("%s: we got no content length, but the "
2034290001Sglebius		    "server wants to keep the connection open: %s.",
2035290001Sglebius		    __func__, connection);
2036290001Sglebius		return (-1);
2037290001Sglebius	} else if (content_length == NULL) {
2038290001Sglebius		req->ntoread = -1;
2039290001Sglebius	} else {
2040290001Sglebius		char *endp;
2041290001Sglebius		ev_int64_t ntoread = evutil_strtoll(content_length, &endp, 10);
2042290001Sglebius		if (*content_length == '\0' || *endp != '\0' || ntoread < 0) {
2043290001Sglebius			event_debug(("%s: illegal content length: %s",
2044290001Sglebius				__func__, content_length));
2045290001Sglebius			return (-1);
2046290001Sglebius		}
2047290001Sglebius		req->ntoread = ntoread;
2048290001Sglebius	}
2049290001Sglebius
2050290001Sglebius	event_debug(("%s: bytes to read: "EV_I64_FMT" (in buffer "EV_SIZE_FMT")\n",
2051290001Sglebius		__func__, EV_I64_ARG(req->ntoread),
2052290001Sglebius		EV_SIZE_ARG(evbuffer_get_length(bufferevent_get_input(req->evcon->bufev)))));
2053290001Sglebius
2054290001Sglebius	return (0);
2055290001Sglebius}
2056290001Sglebius
2057290001Sglebiusstatic int
2058290001Sglebiusevhttp_method_may_have_body(enum evhttp_cmd_type type)
2059290001Sglebius{
2060290001Sglebius	switch (type) {
2061290001Sglebius	case EVHTTP_REQ_POST:
2062290001Sglebius	case EVHTTP_REQ_PUT:
2063290001Sglebius	case EVHTTP_REQ_PATCH:
2064290001Sglebius		return 1;
2065290001Sglebius	case EVHTTP_REQ_TRACE:
2066290001Sglebius		return 0;
2067290001Sglebius	/* XXX May any of the below methods have a body? */
2068290001Sglebius	case EVHTTP_REQ_GET:
2069290001Sglebius	case EVHTTP_REQ_HEAD:
2070290001Sglebius	case EVHTTP_REQ_DELETE:
2071290001Sglebius	case EVHTTP_REQ_OPTIONS:
2072290001Sglebius	case EVHTTP_REQ_CONNECT:
2073290001Sglebius		return 0;
2074290001Sglebius	default:
2075290001Sglebius		return 0;
2076290001Sglebius	}
2077290001Sglebius}
2078290001Sglebius
2079290001Sglebiusstatic void
2080290001Sglebiusevhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
2081290001Sglebius{
2082290001Sglebius	const char *xfer_enc;
2083290001Sglebius
2084290001Sglebius	/* If this is a request without a body, then we are done */
2085290001Sglebius	if (req->kind == EVHTTP_REQUEST &&
2086290001Sglebius	    !evhttp_method_may_have_body(req->type)) {
2087290001Sglebius		evhttp_connection_done(evcon);
2088290001Sglebius		return;
2089290001Sglebius	}
2090290001Sglebius	evcon->state = EVCON_READING_BODY;
2091290001Sglebius	xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding");
2092290001Sglebius	if (xfer_enc != NULL && evutil_ascii_strcasecmp(xfer_enc, "chunked") == 0) {
2093290001Sglebius		req->chunked = 1;
2094290001Sglebius		req->ntoread = -1;
2095290001Sglebius	} else {
2096290001Sglebius		if (evhttp_get_body_length(req) == -1) {
2097290001Sglebius			evhttp_connection_fail_(evcon,
2098290001Sglebius			    EVREQ_HTTP_INVALID_HEADER);
2099290001Sglebius			return;
2100290001Sglebius		}
2101290001Sglebius		if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
2102290001Sglebius			/* An incoming request with no content-length and no
2103290001Sglebius			 * transfer-encoding has no body. */
2104290001Sglebius			evhttp_connection_done(evcon);
2105290001Sglebius			return;
2106290001Sglebius		}
2107290001Sglebius	}
2108290001Sglebius
2109290001Sglebius	/* Should we send a 100 Continue status line? */
2110290001Sglebius	if (req->kind == EVHTTP_REQUEST && REQ_VERSION_ATLEAST(req, 1, 1)) {
2111290001Sglebius		const char *expect;
2112290001Sglebius
2113290001Sglebius		expect = evhttp_find_header(req->input_headers, "Expect");
2114290001Sglebius		if (expect) {
2115290001Sglebius			if (!evutil_ascii_strcasecmp(expect, "100-continue")) {
2116290001Sglebius				/* XXX It would be nice to do some sanity
2117290001Sglebius				   checking here. Does the resource exist?
2118290001Sglebius				   Should the resource accept post requests? If
2119290001Sglebius				   no, we should respond with an error. For
2120290001Sglebius				   now, just optimistically tell the client to
2121290001Sglebius				   send their message body. */
2122290001Sglebius				if (req->ntoread > 0) {
2123290001Sglebius					/* ntoread is ev_int64_t, max_body_size is ev_uint64_t */
2124290001Sglebius					if ((req->evcon->max_body_size <= EV_INT64_MAX) && (ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
2125290001Sglebius						evhttp_send_error(req, HTTP_ENTITYTOOLARGE, NULL);
2126290001Sglebius						return;
2127290001Sglebius					}
2128290001Sglebius				}
2129290001Sglebius				if (!evbuffer_get_length(bufferevent_get_input(evcon->bufev)))
2130290001Sglebius					evhttp_send_continue(evcon, req);
2131290001Sglebius			} else {
2132290001Sglebius				evhttp_send_error(req, HTTP_EXPECTATIONFAILED,
2133290001Sglebius					NULL);
2134290001Sglebius				return;
2135290001Sglebius			}
2136290001Sglebius		}
2137290001Sglebius	}
2138290001Sglebius
2139290001Sglebius	evhttp_read_body(evcon, req);
2140290001Sglebius	/* note the request may have been freed in evhttp_read_body */
2141290001Sglebius}
2142290001Sglebius
2143290001Sglebiusstatic void
2144290001Sglebiusevhttp_read_firstline(struct evhttp_connection *evcon,
2145290001Sglebius		      struct evhttp_request *req)
2146290001Sglebius{
2147290001Sglebius	enum message_read_status res;
2148290001Sglebius
2149290001Sglebius	res = evhttp_parse_firstline_(req, bufferevent_get_input(evcon->bufev));
2150290001Sglebius	if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
2151290001Sglebius		/* Error while reading, terminate */
2152290001Sglebius		event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
2153290001Sglebius			__func__, EV_SOCK_ARG(evcon->fd)));
2154290001Sglebius		evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
2155290001Sglebius		return;
2156290001Sglebius	} else if (res == MORE_DATA_EXPECTED) {
2157290001Sglebius		/* Need more header lines */
2158290001Sglebius		return;
2159290001Sglebius	}
2160290001Sglebius
2161290001Sglebius	evcon->state = EVCON_READING_HEADERS;
2162290001Sglebius	evhttp_read_header(evcon, req);
2163290001Sglebius}
2164290001Sglebius
2165290001Sglebiusstatic void
2166290001Sglebiusevhttp_read_header(struct evhttp_connection *evcon,
2167290001Sglebius		   struct evhttp_request *req)
2168290001Sglebius{
2169290001Sglebius	enum message_read_status res;
2170290001Sglebius	evutil_socket_t fd = evcon->fd;
2171290001Sglebius
2172290001Sglebius	res = evhttp_parse_headers_(req, bufferevent_get_input(evcon->bufev));
2173290001Sglebius	if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
2174290001Sglebius		/* Error while reading, terminate */
2175290001Sglebius		event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
2176290001Sglebius			__func__, EV_SOCK_ARG(fd)));
2177290001Sglebius		evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
2178290001Sglebius		return;
2179290001Sglebius	} else if (res == MORE_DATA_EXPECTED) {
2180290001Sglebius		/* Need more header lines */
2181290001Sglebius		return;
2182290001Sglebius	}
2183290001Sglebius
2184290001Sglebius	/* Disable reading for now */
2185290001Sglebius	bufferevent_disable(evcon->bufev, EV_READ);
2186290001Sglebius
2187290001Sglebius	/* Callback can shut down connection with negative return value */
2188290001Sglebius	if (req->header_cb != NULL) {
2189290001Sglebius		if ((*req->header_cb)(req, req->cb_arg) < 0) {
2190290001Sglebius			evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF);
2191290001Sglebius			return;
2192290001Sglebius		}
2193290001Sglebius	}
2194290001Sglebius
2195290001Sglebius	/* Done reading headers, do the real work */
2196290001Sglebius	switch (req->kind) {
2197290001Sglebius	case EVHTTP_REQUEST:
2198290001Sglebius		event_debug(("%s: checking for post data on "EV_SOCK_FMT"\n",
2199290001Sglebius			__func__, EV_SOCK_ARG(fd)));
2200290001Sglebius		evhttp_get_body(evcon, req);
2201290001Sglebius		/* note the request may have been freed in evhttp_get_body */
2202290001Sglebius		break;
2203290001Sglebius
2204290001Sglebius	case EVHTTP_RESPONSE:
2205290001Sglebius		/* Start over if we got a 100 Continue response. */
2206290001Sglebius		if (req->response_code == 100) {
2207290001Sglebius			evhttp_start_read_(evcon);
2208290001Sglebius			return;
2209290001Sglebius		}
2210290001Sglebius		if (!evhttp_response_needs_body(req)) {
2211290001Sglebius			event_debug(("%s: skipping body for code %d\n",
2212290001Sglebius					__func__, req->response_code));
2213290001Sglebius			evhttp_connection_done(evcon);
2214290001Sglebius		} else {
2215290001Sglebius			event_debug(("%s: start of read body for %s on "
2216290001Sglebius				EV_SOCK_FMT"\n",
2217290001Sglebius				__func__, req->remote_host, EV_SOCK_ARG(fd)));
2218290001Sglebius			evhttp_get_body(evcon, req);
2219290001Sglebius			/* note the request may have been freed in
2220290001Sglebius			 * evhttp_get_body */
2221290001Sglebius		}
2222290001Sglebius		break;
2223290001Sglebius
2224290001Sglebius	default:
2225290001Sglebius		event_warnx("%s: bad header on "EV_SOCK_FMT, __func__,
2226290001Sglebius		    EV_SOCK_ARG(fd));
2227290001Sglebius		evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER);
2228290001Sglebius		break;
2229290001Sglebius	}
2230290001Sglebius	/* request may have been freed above */
2231290001Sglebius}
2232290001Sglebius
2233290001Sglebius/*
2234290001Sglebius * Creates a TCP connection to the specified port and executes a callback
2235290001Sglebius * when finished.  Failure or success is indicate by the passed connection
2236290001Sglebius * object.
2237290001Sglebius *
2238290001Sglebius * Although this interface accepts a hostname, it is intended to take
2239290001Sglebius * only numeric hostnames so that non-blocking DNS resolution can
2240290001Sglebius * happen elsewhere.
2241290001Sglebius */
2242290001Sglebius
2243290001Sglebiusstruct evhttp_connection *
2244290001Sglebiusevhttp_connection_new(const char *address, unsigned short port)
2245290001Sglebius{
2246290001Sglebius	return (evhttp_connection_base_new(NULL, NULL, address, port));
2247290001Sglebius}
2248290001Sglebius
2249290001Sglebiusstruct evhttp_connection *
2250290001Sglebiusevhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev,
2251290001Sglebius    const char *address, unsigned short port)
2252290001Sglebius{
2253290001Sglebius	struct evhttp_connection *evcon = NULL;
2254290001Sglebius
2255290001Sglebius	event_debug(("Attempting connection to %s:%d\n", address, port));
2256290001Sglebius
2257290001Sglebius	if ((evcon = mm_calloc(1, sizeof(struct evhttp_connection))) == NULL) {
2258290001Sglebius		event_warn("%s: calloc failed", __func__);
2259290001Sglebius		goto error;
2260290001Sglebius	}
2261290001Sglebius
2262290001Sglebius	evcon->fd = -1;
2263290001Sglebius	evcon->port = port;
2264290001Sglebius
2265290001Sglebius	evcon->max_headers_size = EV_SIZE_MAX;
2266290001Sglebius	evcon->max_body_size = EV_SIZE_MAX;
2267290001Sglebius
2268290001Sglebius	evutil_timerclear(&evcon->timeout);
2269290001Sglebius	evcon->retry_cnt = evcon->retry_max = 0;
2270290001Sglebius
2271290001Sglebius	if ((evcon->address = mm_strdup(address)) == NULL) {
2272290001Sglebius		event_warn("%s: strdup failed", __func__);
2273290001Sglebius		goto error;
2274290001Sglebius	}
2275290001Sglebius
2276290001Sglebius	if (bev == NULL) {
2277290001Sglebius		if (!(bev = bufferevent_socket_new(base, -1, 0))) {
2278290001Sglebius			event_warn("%s: bufferevent_socket_new failed", __func__);
2279290001Sglebius			goto error;
2280290001Sglebius		}
2281290001Sglebius	}
2282290001Sglebius
2283290001Sglebius	bufferevent_setcb(bev, evhttp_read_cb, evhttp_write_cb, evhttp_error_cb, evcon);
2284290001Sglebius	evcon->bufev = bev;
2285290001Sglebius
2286290001Sglebius	evcon->state = EVCON_DISCONNECTED;
2287290001Sglebius	TAILQ_INIT(&evcon->requests);
2288290001Sglebius
2289290001Sglebius	evcon->initial_retry_timeout.tv_sec = 2;
2290290001Sglebius	evcon->initial_retry_timeout.tv_usec = 0;
2291290001Sglebius
2292290001Sglebius	if (base != NULL) {
2293290001Sglebius		evcon->base = base;
2294290001Sglebius		if (bufferevent_get_base(bev) != base)
2295290001Sglebius			bufferevent_base_set(base, evcon->bufev);
2296290001Sglebius	}
2297290001Sglebius
2298290001Sglebius	event_deferred_cb_init_(
2299290001Sglebius	    &evcon->read_more_deferred_cb,
2300290001Sglebius	    bufferevent_get_priority(bev),
2301290001Sglebius	    evhttp_deferred_read_cb, evcon);
2302290001Sglebius
2303290001Sglebius	evcon->dns_base = dnsbase;
2304290001Sglebius	evcon->ai_family = AF_UNSPEC;
2305290001Sglebius
2306290001Sglebius	return (evcon);
2307290001Sglebius
2308290001Sglebius error:
2309290001Sglebius	if (evcon != NULL)
2310290001Sglebius		evhttp_connection_free(evcon);
2311290001Sglebius	return (NULL);
2312290001Sglebius}
2313290001Sglebius
2314290001Sglebiusstruct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
2315290001Sglebius{
2316290001Sglebius	return evcon->bufev;
2317290001Sglebius}
2318290001Sglebius
2319290001Sglebiusstruct evhttp *
2320290001Sglebiusevhttp_connection_get_server(struct evhttp_connection *evcon)
2321290001Sglebius{
2322290001Sglebius	return evcon->http_server;
2323290001Sglebius}
2324290001Sglebius
2325290001Sglebiusstruct evhttp_connection *
2326290001Sglebiusevhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
2327290001Sglebius    const char *address, unsigned short port)
2328290001Sglebius{
2329290001Sglebius	return evhttp_connection_base_bufferevent_new(base, dnsbase, NULL, address, port);
2330290001Sglebius}
2331290001Sglebius
2332290001Sglebiusvoid evhttp_connection_set_family(struct evhttp_connection *evcon,
2333290001Sglebius	int family)
2334290001Sglebius{
2335290001Sglebius	evcon->ai_family = family;
2336290001Sglebius}
2337290001Sglebius
2338290001Sglebiusvoid
2339290001Sglebiusevhttp_connection_set_base(struct evhttp_connection *evcon,
2340290001Sglebius    struct event_base *base)
2341290001Sglebius{
2342290001Sglebius	EVUTIL_ASSERT(evcon->base == NULL);
2343290001Sglebius	EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
2344290001Sglebius	evcon->base = base;
2345290001Sglebius	bufferevent_base_set(base, evcon->bufev);
2346290001Sglebius}
2347290001Sglebius
2348290001Sglebiusvoid
2349290001Sglebiusevhttp_connection_set_timeout(struct evhttp_connection *evcon,
2350290001Sglebius    int timeout_in_secs)
2351290001Sglebius{
2352290001Sglebius	if (timeout_in_secs == -1)
2353290001Sglebius		evhttp_connection_set_timeout_tv(evcon, NULL);
2354290001Sglebius	else {
2355290001Sglebius		struct timeval tv;
2356290001Sglebius		tv.tv_sec = timeout_in_secs;
2357290001Sglebius		tv.tv_usec = 0;
2358290001Sglebius		evhttp_connection_set_timeout_tv(evcon, &tv);
2359290001Sglebius	}
2360290001Sglebius}
2361290001Sglebius
2362290001Sglebiusvoid
2363290001Sglebiusevhttp_connection_set_timeout_tv(struct evhttp_connection *evcon,
2364290001Sglebius    const struct timeval* tv)
2365290001Sglebius{
2366290001Sglebius	if (tv) {
2367290001Sglebius		evcon->timeout = *tv;
2368290001Sglebius		bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
2369290001Sglebius	} else {
2370290001Sglebius		const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
2371290001Sglebius		const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
2372290001Sglebius		evutil_timerclear(&evcon->timeout);
2373290001Sglebius		bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
2374290001Sglebius	}
2375290001Sglebius}
2376290001Sglebius
2377290001Sglebiusvoid
2378290001Sglebiusevhttp_connection_set_initial_retry_tv(struct evhttp_connection *evcon,
2379290001Sglebius    const struct timeval *tv)
2380290001Sglebius{
2381290001Sglebius	if (tv) {
2382290001Sglebius		evcon->initial_retry_timeout = *tv;
2383290001Sglebius	} else {
2384290001Sglebius		evutil_timerclear(&evcon->initial_retry_timeout);
2385290001Sglebius		evcon->initial_retry_timeout.tv_sec = 2;
2386290001Sglebius	}
2387290001Sglebius}
2388290001Sglebius
2389290001Sglebiusvoid
2390290001Sglebiusevhttp_connection_set_retries(struct evhttp_connection *evcon,
2391290001Sglebius    int retry_max)
2392290001Sglebius{
2393290001Sglebius	evcon->retry_max = retry_max;
2394290001Sglebius}
2395290001Sglebius
2396290001Sglebiusvoid
2397290001Sglebiusevhttp_connection_set_closecb(struct evhttp_connection *evcon,
2398290001Sglebius    void (*cb)(struct evhttp_connection *, void *), void *cbarg)
2399290001Sglebius{
2400290001Sglebius	evcon->closecb = cb;
2401290001Sglebius	evcon->closecb_arg = cbarg;
2402290001Sglebius}
2403290001Sglebius
2404290001Sglebiusvoid
2405290001Sglebiusevhttp_connection_get_peer(struct evhttp_connection *evcon,
2406290001Sglebius    char **address, ev_uint16_t *port)
2407290001Sglebius{
2408290001Sglebius	*address = evcon->address;
2409290001Sglebius	*port = evcon->port;
2410290001Sglebius}
2411290001Sglebius
2412290001Sglebiusconst struct sockaddr*
2413290001Sglebiusevhttp_connection_get_addr(struct evhttp_connection *evcon)
2414290001Sglebius{
2415290001Sglebius	return (struct sockaddr *)evcon->conn_address;
2416290001Sglebius}
2417290001Sglebius
2418290001Sglebiusint
2419290001Sglebiusevhttp_connection_connect_(struct evhttp_connection *evcon)
2420290001Sglebius{
2421290001Sglebius	int old_state = evcon->state;
2422290001Sglebius
2423290001Sglebius	if (evcon->state == EVCON_CONNECTING)
2424290001Sglebius		return (0);
2425290001Sglebius
2426290001Sglebius	evhttp_connection_reset_(evcon);
2427290001Sglebius
2428290001Sglebius	EVUTIL_ASSERT(!(evcon->flags & EVHTTP_CON_INCOMING));
2429290001Sglebius	evcon->flags |= EVHTTP_CON_OUTGOING;
2430290001Sglebius
2431290001Sglebius	if (evcon->bind_address || evcon->bind_port) {
2432290001Sglebius		evcon->fd = bind_socket(
2433290001Sglebius			evcon->bind_address, evcon->bind_port, 0 /*reuse*/);
2434290001Sglebius		if (evcon->fd == -1) {
2435290001Sglebius			event_debug(("%s: failed to bind to \"%s\"",
2436290001Sglebius				__func__, evcon->bind_address));
2437290001Sglebius			return (-1);
2438290001Sglebius		}
2439290001Sglebius
2440290001Sglebius		bufferevent_setfd(evcon->bufev, evcon->fd);
2441290001Sglebius	} else {
2442290001Sglebius		bufferevent_setfd(evcon->bufev, -1);
2443290001Sglebius	}
2444290001Sglebius
2445290001Sglebius	/* Set up a callback for successful connection setup */
2446290001Sglebius	bufferevent_setcb(evcon->bufev,
2447290001Sglebius	    NULL /* evhttp_read_cb */,
2448290001Sglebius	    NULL /* evhttp_write_cb */,
2449290001Sglebius	    evhttp_connection_cb,
2450290001Sglebius	    evcon);
2451290001Sglebius	if (!evutil_timerisset(&evcon->timeout)) {
2452290001Sglebius		const struct timeval conn_tv = { HTTP_CONNECT_TIMEOUT, 0 };
2453290001Sglebius		bufferevent_set_timeouts(evcon->bufev, NULL, &conn_tv);
2454290001Sglebius	} else {
2455290001Sglebius		bufferevent_set_timeouts(evcon->bufev, NULL, &evcon->timeout);
2456290001Sglebius	}
2457290001Sglebius	/* make sure that we get a write callback */
2458290001Sglebius	bufferevent_enable(evcon->bufev, EV_WRITE);
2459290001Sglebius
2460290001Sglebius	evcon->state = EVCON_CONNECTING;
2461290001Sglebius
2462290001Sglebius	if (bufferevent_socket_connect_hostname(evcon->bufev, evcon->dns_base,
2463290001Sglebius		evcon->ai_family, evcon->address, evcon->port) < 0) {
2464290001Sglebius		evcon->state = old_state;
2465290001Sglebius		event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed",
2466290001Sglebius		    __func__, evcon->address);
2467290001Sglebius		/* some operating systems return ECONNREFUSED immediately
2468290001Sglebius		 * when connecting to a local address.  the cleanup is going
2469290001Sglebius		 * to reschedule this function call.
2470290001Sglebius		 */
2471290001Sglebius		evhttp_connection_cb_cleanup(evcon);
2472290001Sglebius		return (0);
2473290001Sglebius	}
2474290001Sglebius
2475290001Sglebius	return (0);
2476290001Sglebius}
2477290001Sglebius
2478290001Sglebius/*
2479290001Sglebius * Starts an HTTP request on the provided evhttp_connection object.
2480290001Sglebius * If the connection object is not connected to the web server already,
2481290001Sglebius * this will start the connection.
2482290001Sglebius */
2483290001Sglebius
2484290001Sglebiusint
2485290001Sglebiusevhttp_make_request(struct evhttp_connection *evcon,
2486290001Sglebius    struct evhttp_request *req,
2487290001Sglebius    enum evhttp_cmd_type type, const char *uri)
2488290001Sglebius{
2489290001Sglebius	/* We are making a request */
2490290001Sglebius	req->kind = EVHTTP_REQUEST;
2491290001Sglebius	req->type = type;
2492290001Sglebius	if (req->uri != NULL)
2493290001Sglebius		mm_free(req->uri);
2494290001Sglebius	if ((req->uri = mm_strdup(uri)) == NULL) {
2495290001Sglebius		event_warn("%s: strdup", __func__);
2496290001Sglebius		evhttp_request_free(req);
2497290001Sglebius		return (-1);
2498290001Sglebius	}
2499290001Sglebius
2500290001Sglebius	/* Set the protocol version if it is not supplied */
2501290001Sglebius	if (!req->major && !req->minor) {
2502290001Sglebius		req->major = 1;
2503290001Sglebius		req->minor = 1;
2504290001Sglebius	}
2505290001Sglebius
2506290001Sglebius	EVUTIL_ASSERT(req->evcon == NULL);
2507290001Sglebius	req->evcon = evcon;
2508290001Sglebius	EVUTIL_ASSERT(!(req->flags & EVHTTP_REQ_OWN_CONNECTION));
2509290001Sglebius
2510290001Sglebius	TAILQ_INSERT_TAIL(&evcon->requests, req, next);
2511290001Sglebius
2512290001Sglebius	/* If the connection object is not connected; make it so */
2513290001Sglebius	if (!evhttp_connected(evcon)) {
2514290001Sglebius		int res = evhttp_connection_connect_(evcon);
2515290001Sglebius		/* evhttp_connection_fail_(), which is called through
2516290001Sglebius		 * evhttp_connection_connect_(), assumes that req lies in
2517290001Sglebius		 * evcon->requests.  Thus, enqueue the request in advance and
2518290001Sglebius		 * remove it in the error case. */
2519290001Sglebius		if (res != 0)
2520290001Sglebius			TAILQ_REMOVE(&evcon->requests, req, next);
2521290001Sglebius
2522290001Sglebius		return res;
2523290001Sglebius	}
2524290001Sglebius
2525290001Sglebius	/*
2526290001Sglebius	 * If it's connected already and we are the first in the queue,
2527290001Sglebius	 * then we can dispatch this request immediately.  Otherwise, it
2528290001Sglebius	 * will be dispatched once the pending requests are completed.
2529290001Sglebius	 */
2530290001Sglebius	if (TAILQ_FIRST(&evcon->requests) == req)
2531290001Sglebius		evhttp_request_dispatch(evcon);
2532290001Sglebius
2533290001Sglebius	return (0);
2534290001Sglebius}
2535290001Sglebius
2536290001Sglebiusvoid
2537290001Sglebiusevhttp_cancel_request(struct evhttp_request *req)
2538290001Sglebius{
2539290001Sglebius	struct evhttp_connection *evcon = req->evcon;
2540290001Sglebius	if (evcon != NULL) {
2541290001Sglebius		/* We need to remove it from the connection */
2542290001Sglebius		if (TAILQ_FIRST(&evcon->requests) == req) {
2543290001Sglebius			/* it's currently being worked on, so reset
2544290001Sglebius			 * the connection.
2545290001Sglebius			 */
2546290001Sglebius			evhttp_connection_fail_(evcon,
2547290001Sglebius			    EVREQ_HTTP_REQUEST_CANCEL);
2548290001Sglebius
2549290001Sglebius			/* connection fail freed the request */
2550290001Sglebius			return;
2551290001Sglebius		} else {
2552290001Sglebius			/* otherwise, we can just remove it from the
2553290001Sglebius			 * queue
2554290001Sglebius			 */
2555290001Sglebius			TAILQ_REMOVE(&evcon->requests, req, next);
2556290001Sglebius		}
2557290001Sglebius	}
2558290001Sglebius
2559290001Sglebius	evhttp_request_free(req);
2560290001Sglebius}
2561290001Sglebius
2562290001Sglebius/*
2563290001Sglebius * Reads data from file descriptor into request structure
2564290001Sglebius * Request structure needs to be set up correctly.
2565290001Sglebius */
2566290001Sglebius
2567290001Sglebiusvoid
2568290001Sglebiusevhttp_start_read_(struct evhttp_connection *evcon)
2569290001Sglebius{
2570290001Sglebius	/* Set up an event to read the headers */
2571290001Sglebius	bufferevent_disable(evcon->bufev, EV_WRITE);
2572290001Sglebius	bufferevent_enable(evcon->bufev, EV_READ);
2573290001Sglebius	evcon->state = EVCON_READING_FIRSTLINE;
2574290001Sglebius	/* Reset the bufferevent callbacks */
2575290001Sglebius	bufferevent_setcb(evcon->bufev,
2576290001Sglebius	    evhttp_read_cb,
2577290001Sglebius	    evhttp_write_cb,
2578290001Sglebius	    evhttp_error_cb,
2579290001Sglebius	    evcon);
2580290001Sglebius
2581290001Sglebius	/* If there's still data pending, process it next time through the
2582290001Sglebius	 * loop.  Don't do it now; that could get recusive. */
2583290001Sglebius	if (evbuffer_get_length(bufferevent_get_input(evcon->bufev))) {
2584290001Sglebius		event_deferred_cb_schedule_(get_deferred_queue(evcon),
2585290001Sglebius		    &evcon->read_more_deferred_cb);
2586290001Sglebius	}
2587290001Sglebius}
2588290001Sglebius
2589290001Sglebiusstatic void
2590290001Sglebiusevhttp_send_done(struct evhttp_connection *evcon, void *arg)
2591290001Sglebius{
2592290001Sglebius	int need_close;
2593290001Sglebius	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
2594290001Sglebius	TAILQ_REMOVE(&evcon->requests, req, next);
2595290001Sglebius
2596290001Sglebius	if (req->on_complete_cb != NULL) {
2597290001Sglebius		req->on_complete_cb(req, req->on_complete_cb_arg);
2598290001Sglebius	}
2599290001Sglebius
2600290001Sglebius	need_close =
2601290001Sglebius	    (REQ_VERSION_BEFORE(req, 1, 1) &&
2602290001Sglebius		!evhttp_is_connection_keepalive(req->input_headers))||
2603290001Sglebius	    evhttp_is_connection_close(req->flags, req->input_headers) ||
2604290001Sglebius	    evhttp_is_connection_close(req->flags, req->output_headers);
2605290001Sglebius
2606290001Sglebius	EVUTIL_ASSERT(req->flags & EVHTTP_REQ_OWN_CONNECTION);
2607290001Sglebius	evhttp_request_free(req);
2608290001Sglebius
2609290001Sglebius	if (need_close) {
2610290001Sglebius		evhttp_connection_free(evcon);
2611290001Sglebius		return;
2612290001Sglebius	}
2613290001Sglebius
2614290001Sglebius	/* we have a persistent connection; try to accept another request. */
2615290001Sglebius	if (evhttp_associate_new_request_with_connection(evcon) == -1) {
2616290001Sglebius		evhttp_connection_free(evcon);
2617290001Sglebius	}
2618290001Sglebius}
2619290001Sglebius
2620290001Sglebius/*
2621290001Sglebius * Returns an error page.
2622290001Sglebius */
2623290001Sglebius
2624290001Sglebiusvoid
2625290001Sglebiusevhttp_send_error(struct evhttp_request *req, int error, const char *reason)
2626290001Sglebius{
2627290001Sglebius
2628290001Sglebius#define ERR_FORMAT "<HTML><HEAD>\n" \
2629290001Sglebius	    "<TITLE>%d %s</TITLE>\n" \
2630290001Sglebius	    "</HEAD><BODY>\n" \
2631290001Sglebius	    "<H1>%s</H1>\n" \
2632290001Sglebius	    "</BODY></HTML>\n"
2633290001Sglebius
2634290001Sglebius	struct evbuffer *buf = evbuffer_new();
2635290001Sglebius	if (buf == NULL) {
2636290001Sglebius		/* if we cannot allocate memory; we just drop the connection */
2637290001Sglebius		evhttp_connection_free(req->evcon);
2638290001Sglebius		return;
2639290001Sglebius	}
2640290001Sglebius	if (reason == NULL) {
2641290001Sglebius		reason = evhttp_response_phrase_internal(error);
2642290001Sglebius	}
2643290001Sglebius
2644290001Sglebius	evhttp_response_code_(req, error, reason);
2645290001Sglebius
2646290001Sglebius	evbuffer_add_printf(buf, ERR_FORMAT, error, reason, reason);
2647290001Sglebius
2648290001Sglebius	evhttp_send_page_(req, buf);
2649290001Sglebius
2650290001Sglebius	evbuffer_free(buf);
2651290001Sglebius#undef ERR_FORMAT
2652290001Sglebius}
2653290001Sglebius
2654290001Sglebius/* Requires that headers and response code are already set up */
2655290001Sglebius
2656290001Sglebiusstatic inline void
2657290001Sglebiusevhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
2658290001Sglebius{
2659290001Sglebius	struct evhttp_connection *evcon = req->evcon;
2660290001Sglebius
2661290001Sglebius	if (evcon == NULL) {
2662290001Sglebius		evhttp_request_free(req);
2663290001Sglebius		return;
2664290001Sglebius	}
2665290001Sglebius
2666290001Sglebius	EVUTIL_ASSERT(TAILQ_FIRST(&evcon->requests) == req);
2667290001Sglebius
2668290001Sglebius	/* we expect no more calls form the user on this request */
2669290001Sglebius	req->userdone = 1;
2670290001Sglebius
2671290001Sglebius	/* xxx: not sure if we really should expose the data buffer this way */
2672290001Sglebius	if (databuf != NULL)
2673290001Sglebius		evbuffer_add_buffer(req->output_buffer, databuf);
2674290001Sglebius
2675290001Sglebius	/* Adds headers to the response */
2676290001Sglebius	evhttp_make_header(evcon, req);
2677290001Sglebius
2678290001Sglebius	evhttp_write_buffer(evcon, evhttp_send_done, NULL);
2679290001Sglebius}
2680290001Sglebius
2681290001Sglebiusvoid
2682290001Sglebiusevhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
2683290001Sglebius    struct evbuffer *databuf)
2684290001Sglebius{
2685290001Sglebius	evhttp_response_code_(req, code, reason);
2686290001Sglebius
2687290001Sglebius	evhttp_send(req, databuf);
2688290001Sglebius}
2689290001Sglebius
2690290001Sglebiusvoid
2691290001Sglebiusevhttp_send_reply_start(struct evhttp_request *req, int code,
2692290001Sglebius    const char *reason)
2693290001Sglebius{
2694290001Sglebius	evhttp_response_code_(req, code, reason);
2695290001Sglebius	if (evhttp_find_header(req->output_headers, "Content-Length") == NULL &&
2696290001Sglebius	    REQ_VERSION_ATLEAST(req, 1, 1) &&
2697290001Sglebius	    evhttp_response_needs_body(req)) {
2698290001Sglebius		/*
2699290001Sglebius		 * prefer HTTP/1.1 chunked encoding to closing the connection;
2700290001Sglebius		 * note RFC 2616 section 4.4 forbids it with Content-Length:
2701290001Sglebius		 * and it's not necessary then anyway.
2702290001Sglebius		 */
2703290001Sglebius		evhttp_add_header(req->output_headers, "Transfer-Encoding",
2704290001Sglebius		    "chunked");
2705290001Sglebius		req->chunked = 1;
2706290001Sglebius	} else {
2707290001Sglebius		req->chunked = 0;
2708290001Sglebius	}
2709290001Sglebius	evhttp_make_header(req->evcon, req);
2710290001Sglebius	evhttp_write_buffer(req->evcon, NULL, NULL);
2711290001Sglebius}
2712290001Sglebius
2713290001Sglebiusvoid
2714290001Sglebiusevhttp_send_reply_chunk_with_cb(struct evhttp_request *req, struct evbuffer *databuf,
2715290001Sglebius    void (*cb)(struct evhttp_connection *, void *), void *arg)
2716290001Sglebius{
2717290001Sglebius	struct evhttp_connection *evcon = req->evcon;
2718290001Sglebius	struct evbuffer *output;
2719290001Sglebius
2720290001Sglebius	if (evcon == NULL)
2721290001Sglebius		return;
2722290001Sglebius
2723290001Sglebius	output = bufferevent_get_output(evcon->bufev);
2724290001Sglebius
2725290001Sglebius	if (evbuffer_get_length(databuf) == 0)
2726290001Sglebius		return;
2727290001Sglebius	if (!evhttp_response_needs_body(req))
2728290001Sglebius		return;
2729290001Sglebius	if (req->chunked) {
2730290001Sglebius		evbuffer_add_printf(output, "%x\r\n",
2731290001Sglebius				    (unsigned)evbuffer_get_length(databuf));
2732290001Sglebius	}
2733290001Sglebius	evbuffer_add_buffer(output, databuf);
2734290001Sglebius	if (req->chunked) {
2735290001Sglebius		evbuffer_add(output, "\r\n", 2);
2736290001Sglebius	}
2737290001Sglebius	evhttp_write_buffer(evcon, cb, arg);
2738290001Sglebius}
2739290001Sglebius
2740290001Sglebiusvoid
2741290001Sglebiusevhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
2742290001Sglebius{
2743290001Sglebius	evhttp_send_reply_chunk_with_cb(req, databuf, NULL, NULL);
2744290001Sglebius}
2745290001Sglebiusvoid
2746290001Sglebiusevhttp_send_reply_end(struct evhttp_request *req)
2747290001Sglebius{
2748290001Sglebius	struct evhttp_connection *evcon = req->evcon;
2749290001Sglebius	struct evbuffer *output;
2750290001Sglebius
2751290001Sglebius	if (evcon == NULL) {
2752290001Sglebius		evhttp_request_free(req);
2753290001Sglebius		return;
2754290001Sglebius	}
2755290001Sglebius
2756290001Sglebius	output = bufferevent_get_output(evcon->bufev);
2757290001Sglebius
2758290001Sglebius	/* we expect no more calls form the user on this request */
2759290001Sglebius	req->userdone = 1;
2760290001Sglebius
2761290001Sglebius	if (req->chunked) {
2762290001Sglebius		evbuffer_add(output, "0\r\n\r\n", 5);
2763290001Sglebius		evhttp_write_buffer(req->evcon, evhttp_send_done, NULL);
2764290001Sglebius		req->chunked = 0;
2765290001Sglebius	} else if (evbuffer_get_length(output) == 0) {
2766290001Sglebius		/* let the connection know that we are done with the request */
2767290001Sglebius		evhttp_send_done(evcon, NULL);
2768290001Sglebius	} else {
2769290001Sglebius		/* make the callback execute after all data has been written */
2770290001Sglebius		evcon->cb = evhttp_send_done;
2771290001Sglebius		evcon->cb_arg = NULL;
2772290001Sglebius	}
2773290001Sglebius}
2774290001Sglebius
2775290001Sglebiusstatic const char *informational_phrases[] = {
2776290001Sglebius	/* 100 */ "Continue",
2777290001Sglebius	/* 101 */ "Switching Protocols"
2778290001Sglebius};
2779290001Sglebius
2780290001Sglebiusstatic const char *success_phrases[] = {
2781290001Sglebius	/* 200 */ "OK",
2782290001Sglebius	/* 201 */ "Created",
2783290001Sglebius	/* 202 */ "Accepted",
2784290001Sglebius	/* 203 */ "Non-Authoritative Information",
2785290001Sglebius	/* 204 */ "No Content",
2786290001Sglebius	/* 205 */ "Reset Content",
2787290001Sglebius	/* 206 */ "Partial Content"
2788290001Sglebius};
2789290001Sglebius
2790290001Sglebiusstatic const char *redirection_phrases[] = {
2791290001Sglebius	/* 300 */ "Multiple Choices",
2792290001Sglebius	/* 301 */ "Moved Permanently",
2793290001Sglebius	/* 302 */ "Found",
2794290001Sglebius	/* 303 */ "See Other",
2795290001Sglebius	/* 304 */ "Not Modified",
2796290001Sglebius	/* 305 */ "Use Proxy",
2797290001Sglebius	/* 307 */ "Temporary Redirect"
2798290001Sglebius};
2799290001Sglebius
2800290001Sglebiusstatic const char *client_error_phrases[] = {
2801290001Sglebius	/* 400 */ "Bad Request",
2802290001Sglebius	/* 401 */ "Unauthorized",
2803290001Sglebius	/* 402 */ "Payment Required",
2804290001Sglebius	/* 403 */ "Forbidden",
2805290001Sglebius	/* 404 */ "Not Found",
2806290001Sglebius	/* 405 */ "Method Not Allowed",
2807290001Sglebius	/* 406 */ "Not Acceptable",
2808290001Sglebius	/* 407 */ "Proxy Authentication Required",
2809290001Sglebius	/* 408 */ "Request Time-out",
2810290001Sglebius	/* 409 */ "Conflict",
2811290001Sglebius	/* 410 */ "Gone",
2812290001Sglebius	/* 411 */ "Length Required",
2813290001Sglebius	/* 412 */ "Precondition Failed",
2814290001Sglebius	/* 413 */ "Request Entity Too Large",
2815290001Sglebius	/* 414 */ "Request-URI Too Large",
2816290001Sglebius	/* 415 */ "Unsupported Media Type",
2817290001Sglebius	/* 416 */ "Requested range not satisfiable",
2818290001Sglebius	/* 417 */ "Expectation Failed"
2819290001Sglebius};
2820290001Sglebius
2821290001Sglebiusstatic const char *server_error_phrases[] = {
2822290001Sglebius	/* 500 */ "Internal Server Error",
2823290001Sglebius	/* 501 */ "Not Implemented",
2824290001Sglebius	/* 502 */ "Bad Gateway",
2825290001Sglebius	/* 503 */ "Service Unavailable",
2826290001Sglebius	/* 504 */ "Gateway Time-out",
2827290001Sglebius	/* 505 */ "HTTP Version not supported"
2828290001Sglebius};
2829290001Sglebius
2830290001Sglebiusstruct response_class {
2831290001Sglebius	const char *name;
2832290001Sglebius	size_t num_responses;
2833290001Sglebius	const char **responses;
2834290001Sglebius};
2835290001Sglebius
2836290001Sglebius#ifndef MEMBERSOF
2837290001Sglebius#define MEMBERSOF(x) (sizeof(x)/sizeof(x[0]))
2838290001Sglebius#endif
2839290001Sglebius
2840290001Sglebiusstatic const struct response_class response_classes[] = {
2841290001Sglebius	/* 1xx */ { "Informational", MEMBERSOF(informational_phrases), informational_phrases },
2842290001Sglebius	/* 2xx */ { "Success", MEMBERSOF(success_phrases), success_phrases },
2843290001Sglebius	/* 3xx */ { "Redirection", MEMBERSOF(redirection_phrases), redirection_phrases },
2844290001Sglebius	/* 4xx */ { "Client Error", MEMBERSOF(client_error_phrases), client_error_phrases },
2845290001Sglebius	/* 5xx */ { "Server Error", MEMBERSOF(server_error_phrases), server_error_phrases }
2846290001Sglebius};
2847290001Sglebius
2848290001Sglebiusstatic const char *
2849290001Sglebiusevhttp_response_phrase_internal(int code)
2850290001Sglebius{
2851290001Sglebius	int klass = code / 100 - 1;
2852290001Sglebius	int subcode = code % 100;
2853290001Sglebius
2854290001Sglebius	/* Unknown class - can't do any better here */
2855290001Sglebius	if (klass < 0 || klass >= (int) MEMBERSOF(response_classes))
2856290001Sglebius		return "Unknown Status Class";
2857290001Sglebius
2858290001Sglebius	/* Unknown sub-code, return class name at least */
2859290001Sglebius	if (subcode >= (int) response_classes[klass].num_responses)
2860290001Sglebius		return response_classes[klass].name;
2861290001Sglebius
2862290001Sglebius	return response_classes[klass].responses[subcode];
2863290001Sglebius}
2864290001Sglebius
2865290001Sglebiusvoid
2866290001Sglebiusevhttp_response_code_(struct evhttp_request *req, int code, const char *reason)
2867290001Sglebius{
2868290001Sglebius	req->kind = EVHTTP_RESPONSE;
2869290001Sglebius	req->response_code = code;
2870290001Sglebius	if (req->response_code_line != NULL)
2871290001Sglebius		mm_free(req->response_code_line);
2872290001Sglebius	if (reason == NULL)
2873290001Sglebius		reason = evhttp_response_phrase_internal(code);
2874290001Sglebius	req->response_code_line = mm_strdup(reason);
2875290001Sglebius	if (req->response_code_line == NULL) {
2876290001Sglebius		event_warn("%s: strdup", __func__);
2877290001Sglebius		/* XXX what else can we do? */
2878290001Sglebius	}
2879290001Sglebius}
2880290001Sglebius
2881290001Sglebiusvoid
2882290001Sglebiusevhttp_send_page_(struct evhttp_request *req, struct evbuffer *databuf)
2883290001Sglebius{
2884290001Sglebius	if (!req->major || !req->minor) {
2885290001Sglebius		req->major = 1;
2886290001Sglebius		req->minor = 1;
2887290001Sglebius	}
2888290001Sglebius
2889290001Sglebius	if (req->kind != EVHTTP_RESPONSE)
2890290001Sglebius		evhttp_response_code_(req, 200, "OK");
2891290001Sglebius
2892290001Sglebius	evhttp_clear_headers(req->output_headers);
2893290001Sglebius	evhttp_add_header(req->output_headers, "Content-Type", "text/html");
2894290001Sglebius	evhttp_add_header(req->output_headers, "Connection", "close");
2895290001Sglebius
2896290001Sglebius	evhttp_send(req, databuf);
2897290001Sglebius}
2898290001Sglebius
2899290001Sglebiusstatic const char uri_chars[256] = {
2900290001Sglebius	/* 0 */
2901290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2902290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2903290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 1, 1, 0,
2904290001Sglebius	1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 0, 0, 0, 0, 0, 0,
2905290001Sglebius	/* 64 */
2906290001Sglebius	0, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
2907290001Sglebius	1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 0, 1,
2908290001Sglebius	0, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
2909290001Sglebius	1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 1, 0,
2910290001Sglebius	/* 128 */
2911290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2912290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2913290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2914290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2915290001Sglebius	/* 192 */
2916290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2917290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2918290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2919290001Sglebius	0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
2920290001Sglebius};
2921290001Sglebius
2922290001Sglebius#define CHAR_IS_UNRESERVED(c)			\
2923290001Sglebius	(uri_chars[(unsigned char)(c)])
2924290001Sglebius
2925290001Sglebius/*
2926290001Sglebius * Helper functions to encode/decode a string for inclusion in a URI.
2927290001Sglebius * The returned string must be freed by the caller.
2928290001Sglebius */
2929290001Sglebiuschar *
2930290001Sglebiusevhttp_uriencode(const char *uri, ev_ssize_t len, int space_as_plus)
2931290001Sglebius{
2932290001Sglebius	struct evbuffer *buf = evbuffer_new();
2933290001Sglebius	const char *p, *end;
2934290001Sglebius	char *result;
2935290001Sglebius
2936290001Sglebius	if (buf == NULL)
2937290001Sglebius		return (NULL);
2938290001Sglebius
2939290001Sglebius	if (len >= 0)
2940290001Sglebius		end = uri+len;
2941290001Sglebius	else
2942290001Sglebius		end = uri+strlen(uri);
2943290001Sglebius
2944290001Sglebius	for (p = uri; p < end; p++) {
2945290001Sglebius		if (CHAR_IS_UNRESERVED(*p)) {
2946290001Sglebius			evbuffer_add(buf, p, 1);
2947290001Sglebius		} else if (*p == ' ' && space_as_plus) {
2948290001Sglebius			evbuffer_add(buf, "+", 1);
2949290001Sglebius		} else {
2950290001Sglebius			evbuffer_add_printf(buf, "%%%02X", (unsigned char)(*p));
2951290001Sglebius		}
2952290001Sglebius	}
2953290001Sglebius	evbuffer_add(buf, "", 1); /* NUL-terminator. */
2954290001Sglebius	result = mm_malloc(evbuffer_get_length(buf));
2955290001Sglebius	if (result)
2956290001Sglebius		evbuffer_remove(buf, result, evbuffer_get_length(buf));
2957290001Sglebius	evbuffer_free(buf);
2958290001Sglebius
2959290001Sglebius	return (result);
2960290001Sglebius}
2961290001Sglebius
2962290001Sglebiuschar *
2963290001Sglebiusevhttp_encode_uri(const char *str)
2964290001Sglebius{
2965290001Sglebius	return evhttp_uriencode(str, -1, 0);
2966290001Sglebius}
2967290001Sglebius
2968290001Sglebius/*
2969290001Sglebius * @param decode_plus_ctl: if 1, we decode plus into space.  If 0, we don't.
2970290001Sglebius *     If -1, when true we transform plus to space only after we've seen
2971290001Sglebius *     a ?.  -1 is deprecated.
2972290001Sglebius * @return the number of bytes written to 'ret'.
2973290001Sglebius */
2974290001Sglebiusint
2975290001Sglebiusevhttp_decode_uri_internal(
2976290001Sglebius	const char *uri, size_t length, char *ret, int decode_plus_ctl)
2977290001Sglebius{
2978290001Sglebius	char c;
2979290001Sglebius	int j;
2980290001Sglebius	int decode_plus = (decode_plus_ctl == 1) ? 1: 0;
2981290001Sglebius	unsigned i;
2982290001Sglebius
2983290001Sglebius	for (i = j = 0; i < length; i++) {
2984290001Sglebius		c = uri[i];
2985290001Sglebius		if (c == '?') {
2986290001Sglebius			if (decode_plus_ctl < 0)
2987290001Sglebius				decode_plus = 1;
2988290001Sglebius		} else if (c == '+' && decode_plus) {
2989290001Sglebius			c = ' ';
2990290001Sglebius		} else if ((i + 2) < length && c == '%' &&
2991290001Sglebius			EVUTIL_ISXDIGIT_(uri[i+1]) && EVUTIL_ISXDIGIT_(uri[i+2])) {
2992290001Sglebius			char tmp[3];
2993290001Sglebius			tmp[0] = uri[i+1];
2994290001Sglebius			tmp[1] = uri[i+2];
2995290001Sglebius			tmp[2] = '\0';
2996290001Sglebius			c = (char)strtol(tmp, NULL, 16);
2997290001Sglebius			i += 2;
2998290001Sglebius		}
2999290001Sglebius		ret[j++] = c;
3000290001Sglebius	}
3001290001Sglebius	ret[j] = '\0';
3002290001Sglebius
3003290001Sglebius	return (j);
3004290001Sglebius}
3005290001Sglebius
3006290001Sglebius/* deprecated */
3007290001Sglebiuschar *
3008290001Sglebiusevhttp_decode_uri(const char *uri)
3009290001Sglebius{
3010290001Sglebius	char *ret;
3011290001Sglebius
3012290001Sglebius	if ((ret = mm_malloc(strlen(uri) + 1)) == NULL) {
3013290001Sglebius		event_warn("%s: malloc(%lu)", __func__,
3014290001Sglebius			  (unsigned long)(strlen(uri) + 1));
3015290001Sglebius		return (NULL);
3016290001Sglebius	}
3017290001Sglebius
3018290001Sglebius	evhttp_decode_uri_internal(uri, strlen(uri),
3019290001Sglebius	    ret, -1 /*always_decode_plus*/);
3020290001Sglebius
3021290001Sglebius	return (ret);
3022290001Sglebius}
3023290001Sglebius
3024290001Sglebiuschar *
3025290001Sglebiusevhttp_uridecode(const char *uri, int decode_plus, size_t *size_out)
3026290001Sglebius{
3027290001Sglebius	char *ret;
3028290001Sglebius	int n;
3029290001Sglebius
3030290001Sglebius	if ((ret = mm_malloc(strlen(uri) + 1)) == NULL) {
3031290001Sglebius		event_warn("%s: malloc(%lu)", __func__,
3032290001Sglebius			  (unsigned long)(strlen(uri) + 1));
3033290001Sglebius		return (NULL);
3034290001Sglebius	}
3035290001Sglebius
3036290001Sglebius	n = evhttp_decode_uri_internal(uri, strlen(uri),
3037290001Sglebius	    ret, !!decode_plus/*always_decode_plus*/);
3038290001Sglebius
3039290001Sglebius	if (size_out) {
3040290001Sglebius		EVUTIL_ASSERT(n >= 0);
3041290001Sglebius		*size_out = (size_t)n;
3042290001Sglebius	}
3043290001Sglebius
3044290001Sglebius	return (ret);
3045290001Sglebius}
3046290001Sglebius
3047290001Sglebius/*
3048290001Sglebius * Helper function to parse out arguments in a query.
3049290001Sglebius * The arguments are separated by key and value.
3050290001Sglebius */
3051290001Sglebius
3052290001Sglebiusstatic int
3053290001Sglebiusevhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
3054290001Sglebius    int is_whole_uri)
3055290001Sglebius{
3056290001Sglebius	char *line=NULL;
3057290001Sglebius	char *argument;
3058290001Sglebius	char *p;
3059290001Sglebius	const char *query_part;
3060290001Sglebius	int result = -1;
3061290001Sglebius	struct evhttp_uri *uri=NULL;
3062290001Sglebius
3063290001Sglebius	TAILQ_INIT(headers);
3064290001Sglebius
3065290001Sglebius	if (is_whole_uri) {
3066290001Sglebius		uri = evhttp_uri_parse(str);
3067290001Sglebius		if (!uri)
3068290001Sglebius			goto error;
3069290001Sglebius		query_part = evhttp_uri_get_query(uri);
3070290001Sglebius	} else {
3071290001Sglebius		query_part = str;
3072290001Sglebius	}
3073290001Sglebius
3074290001Sglebius	/* No arguments - we are done */
3075290001Sglebius	if (!query_part || !strlen(query_part)) {
3076290001Sglebius		result = 0;
3077290001Sglebius		goto done;
3078290001Sglebius	}
3079290001Sglebius
3080290001Sglebius	if ((line = mm_strdup(query_part)) == NULL) {
3081290001Sglebius		event_warn("%s: strdup", __func__);
3082290001Sglebius		goto error;
3083290001Sglebius	}
3084290001Sglebius
3085290001Sglebius	p = argument = line;
3086290001Sglebius	while (p != NULL && *p != '\0') {
3087290001Sglebius		char *key, *value, *decoded_value;
3088290001Sglebius		argument = strsep(&p, "&");
3089290001Sglebius
3090290001Sglebius		value = argument;
3091290001Sglebius		key = strsep(&value, "=");
3092290001Sglebius		if (value == NULL || *key == '\0') {
3093290001Sglebius			goto error;
3094290001Sglebius		}
3095290001Sglebius
3096290001Sglebius		if ((decoded_value = mm_malloc(strlen(value) + 1)) == NULL) {
3097290001Sglebius			event_warn("%s: mm_malloc", __func__);
3098290001Sglebius			goto error;
3099290001Sglebius		}
3100290001Sglebius		evhttp_decode_uri_internal(value, strlen(value),
3101290001Sglebius		    decoded_value, 1 /*always_decode_plus*/);
3102290001Sglebius		event_debug(("Query Param: %s -> %s\n", key, decoded_value));
3103290001Sglebius		evhttp_add_header_internal(headers, key, decoded_value);
3104290001Sglebius		mm_free(decoded_value);
3105290001Sglebius	}
3106290001Sglebius
3107290001Sglebius	result = 0;
3108290001Sglebius	goto done;
3109290001Sglebiuserror:
3110290001Sglebius	evhttp_clear_headers(headers);
3111290001Sglebiusdone:
3112290001Sglebius	if (line)
3113290001Sglebius		mm_free(line);
3114290001Sglebius	if (uri)
3115290001Sglebius		evhttp_uri_free(uri);
3116290001Sglebius	return result;
3117290001Sglebius}
3118290001Sglebius
3119290001Sglebiusint
3120290001Sglebiusevhttp_parse_query(const char *uri, struct evkeyvalq *headers)
3121290001Sglebius{
3122290001Sglebius	return evhttp_parse_query_impl(uri, headers, 1);
3123290001Sglebius}
3124290001Sglebiusint
3125290001Sglebiusevhttp_parse_query_str(const char *uri, struct evkeyvalq *headers)
3126290001Sglebius{
3127290001Sglebius	return evhttp_parse_query_impl(uri, headers, 0);
3128290001Sglebius}
3129290001Sglebius
3130290001Sglebiusstatic struct evhttp_cb *
3131290001Sglebiusevhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req)
3132290001Sglebius{
3133290001Sglebius	struct evhttp_cb *cb;
3134290001Sglebius	size_t offset = 0;
3135290001Sglebius	char *translated;
3136290001Sglebius	const char *path;
3137290001Sglebius
3138290001Sglebius	/* Test for different URLs */
3139290001Sglebius	path = evhttp_uri_get_path(req->uri_elems);
3140290001Sglebius	offset = strlen(path);
3141290001Sglebius	if ((translated = mm_malloc(offset + 1)) == NULL)
3142290001Sglebius		return (NULL);
3143290001Sglebius	evhttp_decode_uri_internal(path, offset, translated,
3144290001Sglebius	    0 /* decode_plus */);
3145290001Sglebius
3146290001Sglebius	TAILQ_FOREACH(cb, callbacks, next) {
3147290001Sglebius		if (!strcmp(cb->what, translated)) {
3148290001Sglebius			mm_free(translated);
3149290001Sglebius			return (cb);
3150290001Sglebius		}
3151290001Sglebius	}
3152290001Sglebius
3153290001Sglebius	mm_free(translated);
3154290001Sglebius	return (NULL);
3155290001Sglebius}
3156290001Sglebius
3157290001Sglebius
3158290001Sglebiusstatic int
3159290001Sglebiusprefix_suffix_match(const char *pattern, const char *name, int ignorecase)
3160290001Sglebius{
3161290001Sglebius	char c;
3162290001Sglebius
3163290001Sglebius	while (1) {
3164290001Sglebius		switch (c = *pattern++) {
3165290001Sglebius		case '\0':
3166290001Sglebius			return *name == '\0';
3167290001Sglebius
3168290001Sglebius		case '*':
3169290001Sglebius			while (*name != '\0') {
3170290001Sglebius				if (prefix_suffix_match(pattern, name,
3171290001Sglebius					ignorecase))
3172290001Sglebius					return (1);
3173290001Sglebius				++name;
3174290001Sglebius			}
3175290001Sglebius			return (0);
3176290001Sglebius		default:
3177290001Sglebius			if (c != *name) {
3178290001Sglebius				if (!ignorecase ||
3179290001Sglebius				    EVUTIL_TOLOWER_(c) != EVUTIL_TOLOWER_(*name))
3180290001Sglebius					return (0);
3181290001Sglebius			}
3182290001Sglebius			++name;
3183290001Sglebius		}
3184290001Sglebius	}
3185290001Sglebius	/* NOTREACHED */
3186290001Sglebius}
3187290001Sglebius
3188290001Sglebius/*
3189290001Sglebius   Search the vhost hierarchy beginning with http for a server alias
3190290001Sglebius   matching hostname.  If a match is found, and outhttp is non-null,
3191290001Sglebius   outhttp is set to the matching http object and 1 is returned.
3192290001Sglebius*/
3193290001Sglebius
3194290001Sglebiusstatic int
3195290001Sglebiusevhttp_find_alias(struct evhttp *http, struct evhttp **outhttp,
3196290001Sglebius		  const char *hostname)
3197290001Sglebius{
3198290001Sglebius	struct evhttp_server_alias *alias;
3199290001Sglebius	struct evhttp *vhost;
3200290001Sglebius
3201290001Sglebius	TAILQ_FOREACH(alias, &http->aliases, next) {
3202290001Sglebius		/* XXX Do we need to handle IP addresses? */
3203290001Sglebius		if (!evutil_ascii_strcasecmp(alias->alias, hostname)) {
3204290001Sglebius			if (outhttp)
3205290001Sglebius				*outhttp = http;
3206290001Sglebius			return 1;
3207290001Sglebius		}
3208290001Sglebius	}
3209290001Sglebius
3210290001Sglebius	/* XXX It might be good to avoid recursion here, but I don't
3211290001Sglebius	   see a way to do that w/o a list. */
3212290001Sglebius	TAILQ_FOREACH(vhost, &http->virtualhosts, next_vhost) {
3213290001Sglebius		if (evhttp_find_alias(vhost, outhttp, hostname))
3214290001Sglebius			return 1;
3215290001Sglebius	}
3216290001Sglebius
3217290001Sglebius	return 0;
3218290001Sglebius}
3219290001Sglebius
3220290001Sglebius/*
3221290001Sglebius   Attempts to find the best http object to handle a request for a hostname.
3222290001Sglebius   All aliases for the root http object and vhosts are searched for an exact
3223290001Sglebius   match. Then, the vhost hierarchy is traversed again for a matching
3224290001Sglebius   pattern.
3225290001Sglebius
3226290001Sglebius   If an alias or vhost is matched, 1 is returned, and outhttp, if non-null,
3227290001Sglebius   is set with the best matching http object. If there are no matches, the
3228290001Sglebius   root http object is stored in outhttp and 0 is returned.
3229290001Sglebius*/
3230290001Sglebius
3231290001Sglebiusstatic int
3232290001Sglebiusevhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
3233290001Sglebius		  const char *hostname)
3234290001Sglebius{
3235290001Sglebius	struct evhttp *vhost;
3236290001Sglebius	struct evhttp *oldhttp;
3237290001Sglebius	int match_found = 0;
3238290001Sglebius
3239290001Sglebius	if (evhttp_find_alias(http, outhttp, hostname))
3240290001Sglebius		return 1;
3241290001Sglebius
3242290001Sglebius	do {
3243290001Sglebius		oldhttp = http;
3244290001Sglebius		TAILQ_FOREACH(vhost, &http->virtualhosts, next_vhost) {
3245290001Sglebius			if (prefix_suffix_match(vhost->vhost_pattern,
3246290001Sglebius				hostname, 1 /* ignorecase */)) {
3247290001Sglebius				http = vhost;
3248290001Sglebius				match_found = 1;
3249290001Sglebius				break;
3250290001Sglebius			}
3251290001Sglebius		}
3252290001Sglebius	} while (oldhttp != http);
3253290001Sglebius
3254290001Sglebius	if (outhttp)
3255290001Sglebius		*outhttp = http;
3256290001Sglebius
3257290001Sglebius	return match_found;
3258290001Sglebius}
3259290001Sglebius
3260290001Sglebiusstatic void
3261290001Sglebiusevhttp_handle_request(struct evhttp_request *req, void *arg)
3262290001Sglebius{
3263290001Sglebius	struct evhttp *http = arg;
3264290001Sglebius	struct evhttp_cb *cb = NULL;
3265290001Sglebius	const char *hostname;
3266290001Sglebius
3267290001Sglebius	/* we have a new request on which the user needs to take action */
3268290001Sglebius	req->userdone = 0;
3269290001Sglebius
3270290001Sglebius	if (req->type == 0 || req->uri == NULL) {
3271290001Sglebius		evhttp_send_error(req, HTTP_BADREQUEST, NULL);
3272290001Sglebius		return;
3273290001Sglebius	}
3274290001Sglebius
3275290001Sglebius	if ((http->allowed_methods & req->type) == 0) {
3276290001Sglebius		event_debug(("Rejecting disallowed method %x (allowed: %x)\n",
3277290001Sglebius			(unsigned)req->type, (unsigned)http->allowed_methods));
3278290001Sglebius		evhttp_send_error(req, HTTP_NOTIMPLEMENTED, NULL);
3279290001Sglebius		return;
3280290001Sglebius	}
3281290001Sglebius
3282290001Sglebius	/* handle potential virtual hosts */
3283290001Sglebius	hostname = evhttp_request_get_host(req);
3284290001Sglebius	if (hostname != NULL) {
3285290001Sglebius		evhttp_find_vhost(http, &http, hostname);
3286290001Sglebius	}
3287290001Sglebius
3288290001Sglebius	if ((cb = evhttp_dispatch_callback(&http->callbacks, req)) != NULL) {
3289290001Sglebius		(*cb->cb)(req, cb->cbarg);
3290290001Sglebius		return;
3291290001Sglebius	}
3292290001Sglebius
3293290001Sglebius	/* Generic call back */
3294290001Sglebius	if (http->gencb) {
3295290001Sglebius		(*http->gencb)(req, http->gencbarg);
3296290001Sglebius		return;
3297290001Sglebius	} else {
3298290001Sglebius		/* We need to send a 404 here */
3299290001Sglebius#define ERR_FORMAT "<html><head>" \
3300290001Sglebius		    "<title>404 Not Found</title>" \
3301290001Sglebius		    "</head><body>" \
3302290001Sglebius		    "<h1>Not Found</h1>" \
3303290001Sglebius		    "<p>The requested URL %s was not found on this server.</p>"\
3304290001Sglebius		    "</body></html>\n"
3305290001Sglebius
3306290001Sglebius		char *escaped_html;
3307290001Sglebius		struct evbuffer *buf;
3308290001Sglebius
3309290001Sglebius		if ((escaped_html = evhttp_htmlescape(req->uri)) == NULL) {
3310290001Sglebius			evhttp_connection_free(req->evcon);
3311290001Sglebius			return;
3312290001Sglebius		}
3313290001Sglebius
3314290001Sglebius		if ((buf = evbuffer_new()) == NULL) {
3315290001Sglebius			mm_free(escaped_html);
3316290001Sglebius			evhttp_connection_free(req->evcon);
3317290001Sglebius			return;
3318290001Sglebius		}
3319290001Sglebius
3320290001Sglebius		evhttp_response_code_(req, HTTP_NOTFOUND, "Not Found");
3321290001Sglebius
3322290001Sglebius		evbuffer_add_printf(buf, ERR_FORMAT, escaped_html);
3323290001Sglebius
3324290001Sglebius		mm_free(escaped_html);
3325290001Sglebius
3326290001Sglebius		evhttp_send_page_(req, buf);
3327290001Sglebius
3328290001Sglebius		evbuffer_free(buf);
3329290001Sglebius#undef ERR_FORMAT
3330290001Sglebius	}
3331290001Sglebius}
3332290001Sglebius
3333290001Sglebius/* Listener callback when a connection arrives at a server. */
3334290001Sglebiusstatic void
3335290001Sglebiusaccept_socket_cb(struct evconnlistener *listener, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, void *arg)
3336290001Sglebius{
3337290001Sglebius	struct evhttp *http = arg;
3338290001Sglebius
3339290001Sglebius	evhttp_get_request(http, nfd, peer_sa, peer_socklen);
3340290001Sglebius}
3341290001Sglebius
3342290001Sglebiusint
3343290001Sglebiusevhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port)
3344290001Sglebius{
3345290001Sglebius	struct evhttp_bound_socket *bound =
3346290001Sglebius		evhttp_bind_socket_with_handle(http, address, port);
3347290001Sglebius	if (bound == NULL)
3348290001Sglebius		return (-1);
3349290001Sglebius	return (0);
3350290001Sglebius}
3351290001Sglebius
3352290001Sglebiusstruct evhttp_bound_socket *
3353290001Sglebiusevhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port)
3354290001Sglebius{
3355290001Sglebius	evutil_socket_t fd;
3356290001Sglebius	struct evhttp_bound_socket *bound;
3357290001Sglebius
3358290001Sglebius	if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1)
3359290001Sglebius		return (NULL);
3360290001Sglebius
3361290001Sglebius	if (listen(fd, 128) == -1) {
3362290001Sglebius		event_sock_warn(fd, "%s: listen", __func__);
3363290001Sglebius		evutil_closesocket(fd);
3364290001Sglebius		return (NULL);
3365290001Sglebius	}
3366290001Sglebius
3367290001Sglebius	bound = evhttp_accept_socket_with_handle(http, fd);
3368290001Sglebius
3369290001Sglebius	if (bound != NULL) {
3370290001Sglebius		event_debug(("Bound to port %d - Awaiting connections ... ",
3371290001Sglebius			port));
3372290001Sglebius		return (bound);
3373290001Sglebius	}
3374290001Sglebius
3375290001Sglebius	return (NULL);
3376290001Sglebius}
3377290001Sglebius
3378290001Sglebiusint
3379290001Sglebiusevhttp_accept_socket(struct evhttp *http, evutil_socket_t fd)
3380290001Sglebius{
3381290001Sglebius	struct evhttp_bound_socket *bound =
3382290001Sglebius		evhttp_accept_socket_with_handle(http, fd);
3383290001Sglebius	if (bound == NULL)
3384290001Sglebius		return (-1);
3385290001Sglebius	return (0);
3386290001Sglebius}
3387290001Sglebius
3388290001Sglebiusvoid
3389290001Sglebiusevhttp_foreach_bound_socket(struct evhttp *http,
3390290001Sglebius                            evhttp_bound_socket_foreach_fn *function,
3391290001Sglebius                            void *argument)
3392290001Sglebius{
3393290001Sglebius	struct evhttp_bound_socket *bound;
3394290001Sglebius
3395290001Sglebius	TAILQ_FOREACH(bound, &http->sockets, next)
3396290001Sglebius		function(bound, argument);
3397290001Sglebius}
3398290001Sglebius
3399290001Sglebiusstruct evhttp_bound_socket *
3400290001Sglebiusevhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd)
3401290001Sglebius{
3402290001Sglebius	struct evhttp_bound_socket *bound;
3403290001Sglebius	struct evconnlistener *listener;
3404290001Sglebius	const int flags =
3405290001Sglebius	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_CLOSE_ON_FREE;
3406290001Sglebius
3407290001Sglebius	listener = evconnlistener_new(http->base, NULL, NULL,
3408290001Sglebius	    flags,
3409290001Sglebius	    0, /* Backlog is '0' because we already said 'listen' */
3410290001Sglebius	    fd);
3411290001Sglebius	if (!listener)
3412290001Sglebius		return (NULL);
3413290001Sglebius
3414290001Sglebius	bound = evhttp_bind_listener(http, listener);
3415290001Sglebius	if (!bound) {
3416290001Sglebius		evconnlistener_free(listener);
3417290001Sglebius		return (NULL);
3418290001Sglebius	}
3419290001Sglebius	return (bound);
3420290001Sglebius}
3421290001Sglebius
3422290001Sglebiusstruct evhttp_bound_socket *
3423290001Sglebiusevhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener)
3424290001Sglebius{
3425290001Sglebius	struct evhttp_bound_socket *bound;
3426290001Sglebius
3427290001Sglebius	bound = mm_malloc(sizeof(struct evhttp_bound_socket));
3428290001Sglebius	if (bound == NULL)
3429290001Sglebius		return (NULL);
3430290001Sglebius
3431290001Sglebius	bound->listener = listener;
3432290001Sglebius	TAILQ_INSERT_TAIL(&http->sockets, bound, next);
3433290001Sglebius
3434290001Sglebius	evconnlistener_set_cb(listener, accept_socket_cb, http);
3435290001Sglebius	return bound;
3436290001Sglebius}
3437290001Sglebius
3438290001Sglebiusevutil_socket_t
3439290001Sglebiusevhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound)
3440290001Sglebius{
3441290001Sglebius	return evconnlistener_get_fd(bound->listener);
3442290001Sglebius}
3443290001Sglebius
3444290001Sglebiusstruct evconnlistener *
3445290001Sglebiusevhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound)
3446290001Sglebius{
3447290001Sglebius	return bound->listener;
3448290001Sglebius}
3449290001Sglebius
3450290001Sglebiusvoid
3451290001Sglebiusevhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound)
3452290001Sglebius{
3453290001Sglebius	TAILQ_REMOVE(&http->sockets, bound, next);
3454290001Sglebius	evconnlistener_free(bound->listener);
3455290001Sglebius	mm_free(bound);
3456290001Sglebius}
3457290001Sglebius
3458290001Sglebiusstatic struct evhttp*
3459290001Sglebiusevhttp_new_object(void)
3460290001Sglebius{
3461290001Sglebius	struct evhttp *http = NULL;
3462290001Sglebius
3463290001Sglebius	if ((http = mm_calloc(1, sizeof(struct evhttp))) == NULL) {
3464290001Sglebius		event_warn("%s: calloc", __func__);
3465290001Sglebius		return (NULL);
3466290001Sglebius	}
3467290001Sglebius
3468290001Sglebius	evutil_timerclear(&http->timeout);
3469290001Sglebius	evhttp_set_max_headers_size(http, EV_SIZE_MAX);
3470290001Sglebius	evhttp_set_max_body_size(http, EV_SIZE_MAX);
3471290001Sglebius	evhttp_set_default_content_type(http, "text/html; charset=ISO-8859-1");
3472290001Sglebius	evhttp_set_allowed_methods(http,
3473290001Sglebius	    EVHTTP_REQ_GET |
3474290001Sglebius	    EVHTTP_REQ_POST |
3475290001Sglebius	    EVHTTP_REQ_HEAD |
3476290001Sglebius	    EVHTTP_REQ_PUT |
3477290001Sglebius	    EVHTTP_REQ_DELETE);
3478290001Sglebius
3479290001Sglebius	TAILQ_INIT(&http->sockets);
3480290001Sglebius	TAILQ_INIT(&http->callbacks);
3481290001Sglebius	TAILQ_INIT(&http->connections);
3482290001Sglebius	TAILQ_INIT(&http->virtualhosts);
3483290001Sglebius	TAILQ_INIT(&http->aliases);
3484290001Sglebius
3485290001Sglebius	return (http);
3486290001Sglebius}
3487290001Sglebius
3488290001Sglebiusstruct evhttp *
3489290001Sglebiusevhttp_new(struct event_base *base)
3490290001Sglebius{
3491290001Sglebius	struct evhttp *http = NULL;
3492290001Sglebius
3493290001Sglebius	http = evhttp_new_object();
3494290001Sglebius	if (http == NULL)
3495290001Sglebius		return (NULL);
3496290001Sglebius	http->base = base;
3497290001Sglebius
3498290001Sglebius	return (http);
3499290001Sglebius}
3500290001Sglebius
3501290001Sglebius/*
3502290001Sglebius * Start a web server on the specified address and port.
3503290001Sglebius */
3504290001Sglebius
3505290001Sglebiusstruct evhttp *
3506290001Sglebiusevhttp_start(const char *address, unsigned short port)
3507290001Sglebius{
3508290001Sglebius	struct evhttp *http = NULL;
3509290001Sglebius
3510290001Sglebius	http = evhttp_new_object();
3511290001Sglebius	if (http == NULL)
3512290001Sglebius		return (NULL);
3513290001Sglebius	if (evhttp_bind_socket(http, address, port) == -1) {
3514290001Sglebius		mm_free(http);
3515290001Sglebius		return (NULL);
3516290001Sglebius	}
3517290001Sglebius
3518290001Sglebius	return (http);
3519290001Sglebius}
3520290001Sglebius
3521290001Sglebiusvoid
3522290001Sglebiusevhttp_free(struct evhttp* http)
3523290001Sglebius{
3524290001Sglebius	struct evhttp_cb *http_cb;
3525290001Sglebius	struct evhttp_connection *evcon;
3526290001Sglebius	struct evhttp_bound_socket *bound;
3527290001Sglebius	struct evhttp* vhost;
3528290001Sglebius	struct evhttp_server_alias *alias;
3529290001Sglebius
3530290001Sglebius	/* Remove the accepting part */
3531290001Sglebius	while ((bound = TAILQ_FIRST(&http->sockets)) != NULL) {
3532290001Sglebius		TAILQ_REMOVE(&http->sockets, bound, next);
3533290001Sglebius
3534290001Sglebius		evconnlistener_free(bound->listener);
3535290001Sglebius
3536290001Sglebius		mm_free(bound);
3537290001Sglebius	}
3538290001Sglebius
3539290001Sglebius	while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) {
3540290001Sglebius		/* evhttp_connection_free removes the connection */
3541290001Sglebius		evhttp_connection_free(evcon);
3542290001Sglebius	}
3543290001Sglebius
3544290001Sglebius	while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
3545290001Sglebius		TAILQ_REMOVE(&http->callbacks, http_cb, next);
3546290001Sglebius		mm_free(http_cb->what);
3547290001Sglebius		mm_free(http_cb);
3548290001Sglebius	}
3549290001Sglebius
3550290001Sglebius	while ((vhost = TAILQ_FIRST(&http->virtualhosts)) != NULL) {
3551290001Sglebius		TAILQ_REMOVE(&http->virtualhosts, vhost, next_vhost);
3552290001Sglebius
3553290001Sglebius		evhttp_free(vhost);
3554290001Sglebius	}
3555290001Sglebius
3556290001Sglebius	if (http->vhost_pattern != NULL)
3557290001Sglebius		mm_free(http->vhost_pattern);
3558290001Sglebius
3559290001Sglebius	while ((alias = TAILQ_FIRST(&http->aliases)) != NULL) {
3560290001Sglebius		TAILQ_REMOVE(&http->aliases, alias, next);
3561290001Sglebius		mm_free(alias->alias);
3562290001Sglebius		mm_free(alias);
3563290001Sglebius	}
3564290001Sglebius
3565290001Sglebius	mm_free(http);
3566290001Sglebius}
3567290001Sglebius
3568290001Sglebiusint
3569290001Sglebiusevhttp_add_virtual_host(struct evhttp* http, const char *pattern,
3570290001Sglebius    struct evhttp* vhost)
3571290001Sglebius{
3572290001Sglebius	/* a vhost can only be a vhost once and should not have bound sockets */
3573290001Sglebius	if (vhost->vhost_pattern != NULL ||
3574290001Sglebius	    TAILQ_FIRST(&vhost->sockets) != NULL)
3575290001Sglebius		return (-1);
3576290001Sglebius
3577290001Sglebius	vhost->vhost_pattern = mm_strdup(pattern);
3578290001Sglebius	if (vhost->vhost_pattern == NULL)
3579290001Sglebius		return (-1);
3580290001Sglebius
3581290001Sglebius	TAILQ_INSERT_TAIL(&http->virtualhosts, vhost, next_vhost);
3582290001Sglebius
3583290001Sglebius	return (0);
3584290001Sglebius}
3585290001Sglebius
3586290001Sglebiusint
3587290001Sglebiusevhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost)
3588290001Sglebius{
3589290001Sglebius	if (vhost->vhost_pattern == NULL)
3590290001Sglebius		return (-1);
3591290001Sglebius
3592290001Sglebius	TAILQ_REMOVE(&http->virtualhosts, vhost, next_vhost);
3593290001Sglebius
3594290001Sglebius	mm_free(vhost->vhost_pattern);
3595290001Sglebius	vhost->vhost_pattern = NULL;
3596290001Sglebius
3597290001Sglebius	return (0);
3598290001Sglebius}
3599290001Sglebius
3600290001Sglebiusint
3601290001Sglebiusevhttp_add_server_alias(struct evhttp *http, const char *alias)
3602290001Sglebius{
3603290001Sglebius	struct evhttp_server_alias *evalias;
3604290001Sglebius
3605290001Sglebius	evalias = mm_calloc(1, sizeof(*evalias));
3606290001Sglebius	if (!evalias)
3607290001Sglebius		return -1;
3608290001Sglebius
3609290001Sglebius	evalias->alias = mm_strdup(alias);
3610290001Sglebius	if (!evalias->alias) {
3611290001Sglebius		mm_free(evalias);
3612290001Sglebius		return -1;
3613290001Sglebius	}
3614290001Sglebius
3615290001Sglebius	TAILQ_INSERT_TAIL(&http->aliases, evalias, next);
3616290001Sglebius
3617290001Sglebius	return 0;
3618290001Sglebius}
3619290001Sglebius
3620290001Sglebiusint
3621290001Sglebiusevhttp_remove_server_alias(struct evhttp *http, const char *alias)
3622290001Sglebius{
3623290001Sglebius	struct evhttp_server_alias *evalias;
3624290001Sglebius
3625290001Sglebius	TAILQ_FOREACH(evalias, &http->aliases, next) {
3626290001Sglebius		if (evutil_ascii_strcasecmp(evalias->alias, alias) == 0) {
3627290001Sglebius			TAILQ_REMOVE(&http->aliases, evalias, next);
3628290001Sglebius			mm_free(evalias->alias);
3629290001Sglebius			mm_free(evalias);
3630290001Sglebius			return 0;
3631290001Sglebius		}
3632290001Sglebius	}
3633290001Sglebius
3634290001Sglebius	return -1;
3635290001Sglebius}
3636290001Sglebius
3637290001Sglebiusvoid
3638290001Sglebiusevhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
3639290001Sglebius{
3640290001Sglebius	if (timeout_in_secs == -1) {
3641290001Sglebius		evhttp_set_timeout_tv(http, NULL);
3642290001Sglebius	} else {
3643290001Sglebius		struct timeval tv;
3644290001Sglebius		tv.tv_sec = timeout_in_secs;
3645290001Sglebius		tv.tv_usec = 0;
3646290001Sglebius		evhttp_set_timeout_tv(http, &tv);
3647290001Sglebius	}
3648290001Sglebius}
3649290001Sglebius
3650290001Sglebiusvoid
3651290001Sglebiusevhttp_set_timeout_tv(struct evhttp* http, const struct timeval* tv)
3652290001Sglebius{
3653290001Sglebius	if (tv) {
3654290001Sglebius		http->timeout = *tv;
3655290001Sglebius	} else {
3656290001Sglebius		evutil_timerclear(&http->timeout);
3657290001Sglebius	}
3658290001Sglebius}
3659290001Sglebius
3660290001Sglebiusvoid
3661290001Sglebiusevhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_size)
3662290001Sglebius{
3663290001Sglebius	if (max_headers_size < 0)
3664290001Sglebius		http->default_max_headers_size = EV_SIZE_MAX;
3665290001Sglebius	else
3666290001Sglebius		http->default_max_headers_size = max_headers_size;
3667290001Sglebius}
3668290001Sglebius
3669290001Sglebiusvoid
3670290001Sglebiusevhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size)
3671290001Sglebius{
3672290001Sglebius	if (max_body_size < 0)
3673290001Sglebius		http->default_max_body_size = EV_UINT64_MAX;
3674290001Sglebius	else
3675290001Sglebius		http->default_max_body_size = max_body_size;
3676290001Sglebius}
3677290001Sglebius
3678290001Sglebiusvoid
3679290001Sglebiusevhttp_set_default_content_type(struct evhttp *http,
3680290001Sglebius	const char *content_type) {
3681290001Sglebius	http->default_content_type = content_type;
3682290001Sglebius}
3683290001Sglebius
3684290001Sglebiusvoid
3685290001Sglebiusevhttp_set_allowed_methods(struct evhttp* http, ev_uint16_t methods)
3686290001Sglebius{
3687290001Sglebius	http->allowed_methods = methods;
3688290001Sglebius}
3689290001Sglebius
3690290001Sglebiusint
3691290001Sglebiusevhttp_set_cb(struct evhttp *http, const char *uri,
3692290001Sglebius    void (*cb)(struct evhttp_request *, void *), void *cbarg)
3693290001Sglebius{
3694290001Sglebius	struct evhttp_cb *http_cb;
3695290001Sglebius
3696290001Sglebius	TAILQ_FOREACH(http_cb, &http->callbacks, next) {
3697290001Sglebius		if (strcmp(http_cb->what, uri) == 0)
3698290001Sglebius			return (-1);
3699290001Sglebius	}
3700290001Sglebius
3701290001Sglebius	if ((http_cb = mm_calloc(1, sizeof(struct evhttp_cb))) == NULL) {
3702290001Sglebius		event_warn("%s: calloc", __func__);
3703290001Sglebius		return (-2);
3704290001Sglebius	}
3705290001Sglebius
3706290001Sglebius	http_cb->what = mm_strdup(uri);
3707290001Sglebius	if (http_cb->what == NULL) {
3708290001Sglebius		event_warn("%s: strdup", __func__);
3709290001Sglebius		mm_free(http_cb);
3710290001Sglebius		return (-3);
3711290001Sglebius	}
3712290001Sglebius	http_cb->cb = cb;
3713290001Sglebius	http_cb->cbarg = cbarg;
3714290001Sglebius
3715290001Sglebius	TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next);
3716290001Sglebius
3717290001Sglebius	return (0);
3718290001Sglebius}
3719290001Sglebius
3720290001Sglebiusint
3721290001Sglebiusevhttp_del_cb(struct evhttp *http, const char *uri)
3722290001Sglebius{
3723290001Sglebius	struct evhttp_cb *http_cb;
3724290001Sglebius
3725290001Sglebius	TAILQ_FOREACH(http_cb, &http->callbacks, next) {
3726290001Sglebius		if (strcmp(http_cb->what, uri) == 0)
3727290001Sglebius			break;
3728290001Sglebius	}
3729290001Sglebius	if (http_cb == NULL)
3730290001Sglebius		return (-1);
3731290001Sglebius
3732290001Sglebius	TAILQ_REMOVE(&http->callbacks, http_cb, next);
3733290001Sglebius	mm_free(http_cb->what);
3734290001Sglebius	mm_free(http_cb);
3735290001Sglebius
3736290001Sglebius	return (0);
3737290001Sglebius}
3738290001Sglebius
3739290001Sglebiusvoid
3740290001Sglebiusevhttp_set_gencb(struct evhttp *http,
3741290001Sglebius    void (*cb)(struct evhttp_request *, void *), void *cbarg)
3742290001Sglebius{
3743290001Sglebius	http->gencb = cb;
3744290001Sglebius	http->gencbarg = cbarg;
3745290001Sglebius}
3746290001Sglebius
3747290001Sglebiusvoid
3748290001Sglebiusevhttp_set_bevcb(struct evhttp *http,
3749290001Sglebius    struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg)
3750290001Sglebius{
3751290001Sglebius	http->bevcb = cb;
3752290001Sglebius	http->bevcbarg = cbarg;
3753290001Sglebius}
3754290001Sglebius
3755290001Sglebius/*
3756290001Sglebius * Request related functions
3757290001Sglebius */
3758290001Sglebius
3759290001Sglebiusstruct evhttp_request *
3760290001Sglebiusevhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
3761290001Sglebius{
3762290001Sglebius	struct evhttp_request *req = NULL;
3763290001Sglebius
3764290001Sglebius	/* Allocate request structure */
3765290001Sglebius	if ((req = mm_calloc(1, sizeof(struct evhttp_request))) == NULL) {
3766290001Sglebius		event_warn("%s: calloc", __func__);
3767290001Sglebius		goto error;
3768290001Sglebius	}
3769290001Sglebius
3770290001Sglebius	req->headers_size = 0;
3771290001Sglebius	req->body_size = 0;
3772290001Sglebius
3773290001Sglebius	req->kind = EVHTTP_RESPONSE;
3774290001Sglebius	req->input_headers = mm_calloc(1, sizeof(struct evkeyvalq));
3775290001Sglebius	if (req->input_headers == NULL) {
3776290001Sglebius		event_warn("%s: calloc", __func__);
3777290001Sglebius		goto error;
3778290001Sglebius	}
3779290001Sglebius	TAILQ_INIT(req->input_headers);
3780290001Sglebius
3781290001Sglebius	req->output_headers = mm_calloc(1, sizeof(struct evkeyvalq));
3782290001Sglebius	if (req->output_headers == NULL) {
3783290001Sglebius		event_warn("%s: calloc", __func__);
3784290001Sglebius		goto error;
3785290001Sglebius	}
3786290001Sglebius	TAILQ_INIT(req->output_headers);
3787290001Sglebius
3788290001Sglebius	if ((req->input_buffer = evbuffer_new()) == NULL) {
3789290001Sglebius		event_warn("%s: evbuffer_new", __func__);
3790290001Sglebius		goto error;
3791290001Sglebius	}
3792290001Sglebius
3793290001Sglebius	if ((req->output_buffer = evbuffer_new()) == NULL) {
3794290001Sglebius		event_warn("%s: evbuffer_new", __func__);
3795290001Sglebius		goto error;
3796290001Sglebius	}
3797290001Sglebius
3798290001Sglebius	req->cb = cb;
3799290001Sglebius	req->cb_arg = arg;
3800290001Sglebius
3801290001Sglebius	return (req);
3802290001Sglebius
3803290001Sglebius error:
3804290001Sglebius	if (req != NULL)
3805290001Sglebius		evhttp_request_free(req);
3806290001Sglebius	return (NULL);
3807290001Sglebius}
3808290001Sglebius
3809290001Sglebiusvoid
3810290001Sglebiusevhttp_request_free(struct evhttp_request *req)
3811290001Sglebius{
3812290001Sglebius	if ((req->flags & EVHTTP_REQ_DEFER_FREE) != 0) {
3813290001Sglebius		req->flags |= EVHTTP_REQ_NEEDS_FREE;
3814290001Sglebius		return;
3815290001Sglebius	}
3816290001Sglebius
3817290001Sglebius	if (req->remote_host != NULL)
3818290001Sglebius		mm_free(req->remote_host);
3819290001Sglebius	if (req->uri != NULL)
3820290001Sglebius		mm_free(req->uri);
3821290001Sglebius	if (req->uri_elems != NULL)
3822290001Sglebius		evhttp_uri_free(req->uri_elems);
3823290001Sglebius	if (req->response_code_line != NULL)
3824290001Sglebius		mm_free(req->response_code_line);
3825290001Sglebius	if (req->host_cache != NULL)
3826290001Sglebius		mm_free(req->host_cache);
3827290001Sglebius
3828290001Sglebius	evhttp_clear_headers(req->input_headers);
3829290001Sglebius	mm_free(req->input_headers);
3830290001Sglebius
3831290001Sglebius	evhttp_clear_headers(req->output_headers);
3832290001Sglebius	mm_free(req->output_headers);
3833290001Sglebius
3834290001Sglebius	if (req->input_buffer != NULL)
3835290001Sglebius		evbuffer_free(req->input_buffer);
3836290001Sglebius
3837290001Sglebius	if (req->output_buffer != NULL)
3838290001Sglebius		evbuffer_free(req->output_buffer);
3839290001Sglebius
3840290001Sglebius	mm_free(req);
3841290001Sglebius}
3842290001Sglebius
3843290001Sglebiusvoid
3844290001Sglebiusevhttp_request_own(struct evhttp_request *req)
3845290001Sglebius{
3846290001Sglebius	req->flags |= EVHTTP_USER_OWNED;
3847290001Sglebius}
3848290001Sglebius
3849290001Sglebiusint
3850290001Sglebiusevhttp_request_is_owned(struct evhttp_request *req)
3851290001Sglebius{
3852290001Sglebius	return (req->flags & EVHTTP_USER_OWNED) != 0;
3853290001Sglebius}
3854290001Sglebius
3855290001Sglebiusstruct evhttp_connection *
3856290001Sglebiusevhttp_request_get_connection(struct evhttp_request *req)
3857290001Sglebius{
3858290001Sglebius	return req->evcon;
3859290001Sglebius}
3860290001Sglebius
3861290001Sglebiusstruct event_base *
3862290001Sglebiusevhttp_connection_get_base(struct evhttp_connection *conn)
3863290001Sglebius{
3864290001Sglebius	return conn->base;
3865290001Sglebius}
3866290001Sglebius
3867290001Sglebiusvoid
3868290001Sglebiusevhttp_request_set_chunked_cb(struct evhttp_request *req,
3869290001Sglebius    void (*cb)(struct evhttp_request *, void *))
3870290001Sglebius{
3871290001Sglebius	req->chunk_cb = cb;
3872290001Sglebius}
3873290001Sglebius
3874290001Sglebiusvoid
3875290001Sglebiusevhttp_request_set_header_cb(struct evhttp_request *req,
3876290001Sglebius    int (*cb)(struct evhttp_request *, void *))
3877290001Sglebius{
3878290001Sglebius	req->header_cb = cb;
3879290001Sglebius}
3880290001Sglebius
3881290001Sglebiusvoid
3882290001Sglebiusevhttp_request_set_error_cb(struct evhttp_request *req,
3883290001Sglebius    void (*cb)(enum evhttp_request_error, void *))
3884290001Sglebius{
3885290001Sglebius	req->error_cb = cb;
3886290001Sglebius}
3887290001Sglebius
3888290001Sglebiusvoid
3889290001Sglebiusevhttp_request_set_on_complete_cb(struct evhttp_request *req,
3890290001Sglebius    void (*cb)(struct evhttp_request *, void *), void *cb_arg)
3891290001Sglebius{
3892290001Sglebius	req->on_complete_cb = cb;
3893290001Sglebius	req->on_complete_cb_arg = cb_arg;
3894290001Sglebius}
3895290001Sglebius
3896290001Sglebius/*
3897290001Sglebius * Allows for inspection of the request URI
3898290001Sglebius */
3899290001Sglebius
3900290001Sglebiusconst char *
3901290001Sglebiusevhttp_request_get_uri(const struct evhttp_request *req) {
3902290001Sglebius	if (req->uri == NULL)
3903290001Sglebius		event_debug(("%s: request %p has no uri\n", __func__, req));
3904290001Sglebius	return (req->uri);
3905290001Sglebius}
3906290001Sglebius
3907290001Sglebiusconst struct evhttp_uri *
3908290001Sglebiusevhttp_request_get_evhttp_uri(const struct evhttp_request *req) {
3909290001Sglebius	if (req->uri_elems == NULL)
3910290001Sglebius		event_debug(("%s: request %p has no uri elems\n",
3911290001Sglebius			    __func__, req));
3912290001Sglebius	return (req->uri_elems);
3913290001Sglebius}
3914290001Sglebius
3915290001Sglebiusconst char *
3916290001Sglebiusevhttp_request_get_host(struct evhttp_request *req)
3917290001Sglebius{
3918290001Sglebius	const char *host = NULL;
3919290001Sglebius
3920290001Sglebius	if (req->host_cache)
3921290001Sglebius		return req->host_cache;
3922290001Sglebius
3923290001Sglebius	if (req->uri_elems)
3924290001Sglebius		host = evhttp_uri_get_host(req->uri_elems);
3925290001Sglebius	if (!host && req->input_headers) {
3926290001Sglebius		const char *p;
3927290001Sglebius		size_t len;
3928290001Sglebius
3929290001Sglebius		host = evhttp_find_header(req->input_headers, "Host");
3930290001Sglebius		/* The Host: header may include a port. Remove it here
3931290001Sglebius		   to be consistent with uri_elems case above. */
3932290001Sglebius		if (host) {
3933290001Sglebius			p = host + strlen(host) - 1;
3934290001Sglebius			while (p > host && EVUTIL_ISDIGIT_(*p))
3935290001Sglebius				--p;
3936290001Sglebius			if (p > host && *p == ':') {
3937290001Sglebius				len = p - host;
3938290001Sglebius				req->host_cache = mm_malloc(len + 1);
3939290001Sglebius				if (!req->host_cache) {
3940290001Sglebius					event_warn("%s: malloc", __func__);
3941290001Sglebius					return NULL;
3942290001Sglebius				}
3943290001Sglebius				memcpy(req->host_cache, host, len);
3944290001Sglebius				req->host_cache[len] = '\0';
3945290001Sglebius				host = req->host_cache;
3946290001Sglebius			}
3947290001Sglebius		}
3948290001Sglebius	}
3949290001Sglebius
3950290001Sglebius	return host;
3951290001Sglebius}
3952290001Sglebius
3953290001Sglebiusenum evhttp_cmd_type
3954290001Sglebiusevhttp_request_get_command(const struct evhttp_request *req) {
3955290001Sglebius	return (req->type);
3956290001Sglebius}
3957290001Sglebius
3958290001Sglebiusint
3959290001Sglebiusevhttp_request_get_response_code(const struct evhttp_request *req)
3960290001Sglebius{
3961290001Sglebius	return req->response_code;
3962290001Sglebius}
3963290001Sglebius
3964290001Sglebiusconst char *
3965290001Sglebiusevhttp_request_get_response_code_line(const struct evhttp_request *req)
3966290001Sglebius{
3967290001Sglebius	return req->response_code_line;
3968290001Sglebius}
3969290001Sglebius
3970290001Sglebius/** Returns the input headers */
3971290001Sglebiusstruct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req)
3972290001Sglebius{
3973290001Sglebius	return (req->input_headers);
3974290001Sglebius}
3975290001Sglebius
3976290001Sglebius/** Returns the output headers */
3977290001Sglebiusstruct evkeyvalq *evhttp_request_get_output_headers(struct evhttp_request *req)
3978290001Sglebius{
3979290001Sglebius	return (req->output_headers);
3980290001Sglebius}
3981290001Sglebius
3982290001Sglebius/** Returns the input buffer */
3983290001Sglebiusstruct evbuffer *evhttp_request_get_input_buffer(struct evhttp_request *req)
3984290001Sglebius{
3985290001Sglebius	return (req->input_buffer);
3986290001Sglebius}
3987290001Sglebius
3988290001Sglebius/** Returns the output buffer */
3989290001Sglebiusstruct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req)
3990290001Sglebius{
3991290001Sglebius	return (req->output_buffer);
3992290001Sglebius}
3993290001Sglebius
3994290001Sglebius
3995290001Sglebius/*
3996290001Sglebius * Takes a file descriptor to read a request from.
3997290001Sglebius * The callback is executed once the whole request has been read.
3998290001Sglebius */
3999290001Sglebius
4000290001Sglebiusstatic struct evhttp_connection*
4001290001Sglebiusevhttp_get_request_connection(
4002290001Sglebius	struct evhttp* http,
4003290001Sglebius	evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen)
4004290001Sglebius{
4005290001Sglebius	struct evhttp_connection *evcon;
4006290001Sglebius	char *hostname = NULL, *portname = NULL;
4007290001Sglebius	struct bufferevent* bev = NULL;
4008290001Sglebius
4009290001Sglebius	name_from_addr(sa, salen, &hostname, &portname);
4010290001Sglebius	if (hostname == NULL || portname == NULL) {
4011290001Sglebius		if (hostname) mm_free(hostname);
4012290001Sglebius		if (portname) mm_free(portname);
4013290001Sglebius		return (NULL);
4014290001Sglebius	}
4015290001Sglebius
4016290001Sglebius	event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
4017290001Sglebius		__func__, hostname, portname, EV_SOCK_ARG(fd)));
4018290001Sglebius
4019290001Sglebius	/* we need a connection object to put the http request on */
4020290001Sglebius	if (http->bevcb != NULL) {
4021290001Sglebius		bev = (*http->bevcb)(http->base, http->bevcbarg);
4022290001Sglebius	}
4023290001Sglebius	evcon = evhttp_connection_base_bufferevent_new(
4024290001Sglebius		http->base, NULL, bev, hostname, atoi(portname));
4025290001Sglebius	mm_free(hostname);
4026290001Sglebius	mm_free(portname);
4027290001Sglebius	if (evcon == NULL)
4028290001Sglebius		return (NULL);
4029290001Sglebius
4030290001Sglebius	evcon->max_headers_size = http->default_max_headers_size;
4031290001Sglebius	evcon->max_body_size = http->default_max_body_size;
4032290001Sglebius
4033290001Sglebius	evcon->flags |= EVHTTP_CON_INCOMING;
4034290001Sglebius	evcon->state = EVCON_READING_FIRSTLINE;
4035290001Sglebius
4036290001Sglebius	evcon->fd = fd;
4037290001Sglebius
4038290001Sglebius	bufferevent_setfd(evcon->bufev, fd);
4039290001Sglebius
4040290001Sglebius	return (evcon);
4041290001Sglebius}
4042290001Sglebius
4043290001Sglebiusstatic int
4044290001Sglebiusevhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
4045290001Sglebius{
4046290001Sglebius	struct evhttp *http = evcon->http_server;
4047290001Sglebius	struct evhttp_request *req;
4048290001Sglebius	if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
4049290001Sglebius		return (-1);
4050290001Sglebius
4051290001Sglebius	if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
4052290001Sglebius		event_warn("%s: strdup", __func__);
4053290001Sglebius		evhttp_request_free(req);
4054290001Sglebius		return (-1);
4055290001Sglebius	}
4056290001Sglebius	req->remote_port = evcon->port;
4057290001Sglebius
4058290001Sglebius	req->evcon = evcon;	/* the request ends up owning the connection */
4059290001Sglebius	req->flags |= EVHTTP_REQ_OWN_CONNECTION;
4060290001Sglebius
4061290001Sglebius	/* We did not present the request to the user user yet, so treat it as
4062290001Sglebius	 * if the user was done with the request.  This allows us to free the
4063290001Sglebius	 * request on a persistent connection if the client drops it without
4064290001Sglebius	 * sending a request.
4065290001Sglebius	 */
4066290001Sglebius	req->userdone = 1;
4067290001Sglebius
4068290001Sglebius	TAILQ_INSERT_TAIL(&evcon->requests, req, next);
4069290001Sglebius
4070290001Sglebius	req->kind = EVHTTP_REQUEST;
4071290001Sglebius
4072290001Sglebius
4073290001Sglebius	evhttp_start_read_(evcon);
4074290001Sglebius
4075290001Sglebius	return (0);
4076290001Sglebius}
4077290001Sglebius
4078290001Sglebiusstatic void
4079290001Sglebiusevhttp_get_request(struct evhttp *http, evutil_socket_t fd,
4080290001Sglebius    struct sockaddr *sa, ev_socklen_t salen)
4081290001Sglebius{
4082290001Sglebius	struct evhttp_connection *evcon;
4083290001Sglebius
4084290001Sglebius	evcon = evhttp_get_request_connection(http, fd, sa, salen);
4085290001Sglebius	if (evcon == NULL) {
4086290001Sglebius		event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
4087290001Sglebius		    __func__, EV_SOCK_ARG(fd));
4088290001Sglebius		evutil_closesocket(fd);
4089290001Sglebius		return;
4090290001Sglebius	}
4091290001Sglebius
4092290001Sglebius	/* the timeout can be used by the server to close idle connections */
4093290001Sglebius	if (evutil_timerisset(&http->timeout))
4094290001Sglebius		evhttp_connection_set_timeout_tv(evcon, &http->timeout);
4095290001Sglebius
4096290001Sglebius	/*
4097290001Sglebius	 * if we want to accept more than one request on a connection,
4098290001Sglebius	 * we need to know which http server it belongs to.
4099290001Sglebius	 */
4100290001Sglebius	evcon->http_server = http;
4101290001Sglebius	TAILQ_INSERT_TAIL(&http->connections, evcon, next);
4102290001Sglebius
4103290001Sglebius	if (evhttp_associate_new_request_with_connection(evcon) == -1)
4104290001Sglebius		evhttp_connection_free(evcon);
4105290001Sglebius}
4106290001Sglebius
4107290001Sglebius
4108290001Sglebius/*
4109290001Sglebius * Network helper functions that we do not want to export to the rest of
4110290001Sglebius * the world.
4111290001Sglebius */
4112290001Sglebius
4113290001Sglebiusstatic void
4114290001Sglebiusname_from_addr(struct sockaddr *sa, ev_socklen_t salen,
4115290001Sglebius    char **phost, char **pport)
4116290001Sglebius{
4117290001Sglebius	char ntop[NI_MAXHOST];
4118290001Sglebius	char strport[NI_MAXSERV];
4119290001Sglebius	int ni_result;
4120290001Sglebius
4121290001Sglebius#ifdef EVENT__HAVE_GETNAMEINFO
4122290001Sglebius	ni_result = getnameinfo(sa, salen,
4123290001Sglebius		ntop, sizeof(ntop), strport, sizeof(strport),
4124290001Sglebius		NI_NUMERICHOST|NI_NUMERICSERV);
4125290001Sglebius
4126290001Sglebius	if (ni_result != 0) {
4127290001Sglebius#ifdef EAI_SYSTEM
4128290001Sglebius		/* Windows doesn't have an EAI_SYSTEM. */
4129290001Sglebius		if (ni_result == EAI_SYSTEM)
4130290001Sglebius			event_err(1, "getnameinfo failed");
4131290001Sglebius		else
4132290001Sglebius#endif
4133290001Sglebius			event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result));
4134290001Sglebius		return;
4135290001Sglebius	}
4136290001Sglebius#else
4137290001Sglebius	ni_result = fake_getnameinfo(sa, salen,
4138290001Sglebius		ntop, sizeof(ntop), strport, sizeof(strport),
4139290001Sglebius		NI_NUMERICHOST|NI_NUMERICSERV);
4140290001Sglebius	if (ni_result != 0)
4141290001Sglebius			return;
4142290001Sglebius#endif
4143290001Sglebius
4144290001Sglebius	*phost = mm_strdup(ntop);
4145290001Sglebius	*pport = mm_strdup(strport);
4146290001Sglebius}
4147290001Sglebius
4148290001Sglebius/* Create a non-blocking socket and bind it */
4149290001Sglebius/* todo: rename this function */
4150290001Sglebiusstatic evutil_socket_t
4151290001Sglebiusbind_socket_ai(struct evutil_addrinfo *ai, int reuse)
4152290001Sglebius{
4153290001Sglebius	evutil_socket_t fd;
4154290001Sglebius
4155290001Sglebius	int on = 1, r;
4156290001Sglebius	int serrno;
4157290001Sglebius
4158290001Sglebius	/* Create listen socket */
4159290001Sglebius	fd = evutil_socket_(ai ? ai->ai_family : AF_INET,
4160290001Sglebius	    SOCK_STREAM|EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC, 0);
4161290001Sglebius	if (fd == -1) {
4162290001Sglebius			event_sock_warn(-1, "socket");
4163290001Sglebius			return (-1);
4164290001Sglebius	}
4165290001Sglebius
4166290001Sglebius	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on))<0)
4167290001Sglebius		goto out;
4168290001Sglebius	if (reuse) {
4169290001Sglebius		if (evutil_make_listen_socket_reuseable(fd) < 0)
4170290001Sglebius			goto out;
4171290001Sglebius	}
4172290001Sglebius
4173290001Sglebius	if (ai != NULL) {
4174290001Sglebius		r = bind(fd, ai->ai_addr, (ev_socklen_t)ai->ai_addrlen);
4175290001Sglebius		if (r == -1)
4176290001Sglebius			goto out;
4177290001Sglebius	}
4178290001Sglebius
4179290001Sglebius	return (fd);
4180290001Sglebius
4181290001Sglebius out:
4182290001Sglebius	serrno = EVUTIL_SOCKET_ERROR();
4183290001Sglebius	evutil_closesocket(fd);
4184290001Sglebius	EVUTIL_SET_SOCKET_ERROR(serrno);
4185290001Sglebius	return (-1);
4186290001Sglebius}
4187290001Sglebius
4188290001Sglebiusstatic struct evutil_addrinfo *
4189290001Sglebiusmake_addrinfo(const char *address, ev_uint16_t port)
4190290001Sglebius{
4191290001Sglebius	struct evutil_addrinfo *ai = NULL;
4192290001Sglebius
4193290001Sglebius	struct evutil_addrinfo hints;
4194290001Sglebius	char strport[NI_MAXSERV];
4195290001Sglebius	int ai_result;
4196290001Sglebius
4197290001Sglebius	memset(&hints, 0, sizeof(hints));
4198290001Sglebius	hints.ai_family = AF_UNSPEC;
4199290001Sglebius	hints.ai_socktype = SOCK_STREAM;
4200290001Sglebius	/* turn NULL hostname into INADDR_ANY, and skip looking up any address
4201290001Sglebius	 * types we don't have an interface to connect to. */
4202290001Sglebius	hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG;
4203290001Sglebius	evutil_snprintf(strport, sizeof(strport), "%d", port);
4204290001Sglebius	if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai))
4205290001Sglebius	    != 0) {
4206290001Sglebius		if (ai_result == EVUTIL_EAI_SYSTEM)
4207290001Sglebius			event_warn("getaddrinfo");
4208290001Sglebius		else
4209290001Sglebius			event_warnx("getaddrinfo: %s",
4210290001Sglebius			    evutil_gai_strerror(ai_result));
4211290001Sglebius		return (NULL);
4212290001Sglebius	}
4213290001Sglebius
4214290001Sglebius	return (ai);
4215290001Sglebius}
4216290001Sglebius
4217290001Sglebiusstatic evutil_socket_t
4218290001Sglebiusbind_socket(const char *address, ev_uint16_t port, int reuse)
4219290001Sglebius{
4220290001Sglebius	evutil_socket_t fd;
4221290001Sglebius	struct evutil_addrinfo *aitop = NULL;
4222290001Sglebius
4223290001Sglebius	/* just create an unbound socket */
4224290001Sglebius	if (address == NULL && port == 0)
4225290001Sglebius		return bind_socket_ai(NULL, 0);
4226290001Sglebius
4227290001Sglebius	aitop = make_addrinfo(address, port);
4228290001Sglebius
4229290001Sglebius	if (aitop == NULL)
4230290001Sglebius		return (-1);
4231290001Sglebius
4232290001Sglebius	fd = bind_socket_ai(aitop, reuse);
4233290001Sglebius
4234290001Sglebius	evutil_freeaddrinfo(aitop);
4235290001Sglebius
4236290001Sglebius	return (fd);
4237290001Sglebius}
4238290001Sglebius
4239290001Sglebiusstruct evhttp_uri {
4240290001Sglebius	unsigned flags;
4241290001Sglebius	char *scheme; /* scheme; e.g http, ftp etc */
4242290001Sglebius	char *userinfo; /* userinfo (typically username:pass), or NULL */
4243290001Sglebius	char *host; /* hostname, IP address, or NULL */
4244290001Sglebius	int port; /* port, or zero */
4245290001Sglebius	char *path; /* path, or "". */
4246290001Sglebius	char *query; /* query, or NULL */
4247290001Sglebius	char *fragment; /* fragment or NULL */
4248290001Sglebius};
4249290001Sglebius
4250290001Sglebiusstruct evhttp_uri *
4251290001Sglebiusevhttp_uri_new(void)
4252290001Sglebius{
4253290001Sglebius	struct evhttp_uri *uri = mm_calloc(sizeof(struct evhttp_uri), 1);
4254290001Sglebius	if (uri)
4255290001Sglebius		uri->port = -1;
4256290001Sglebius	return uri;
4257290001Sglebius}
4258290001Sglebius
4259290001Sglebiusvoid
4260290001Sglebiusevhttp_uri_set_flags(struct evhttp_uri *uri, unsigned flags)
4261290001Sglebius{
4262290001Sglebius	uri->flags = flags;
4263290001Sglebius}
4264290001Sglebius
4265290001Sglebius/* Return true if the string starting at s and ending immediately before eos
4266290001Sglebius * is a valid URI scheme according to RFC3986
4267290001Sglebius */
4268290001Sglebiusstatic int
4269290001Sglebiusscheme_ok(const char *s, const char *eos)
4270290001Sglebius{
4271290001Sglebius	/* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
4272290001Sglebius	EVUTIL_ASSERT(eos >= s);
4273290001Sglebius	if (s == eos)
4274290001Sglebius		return 0;
4275290001Sglebius	if (!EVUTIL_ISALPHA_(*s))
4276290001Sglebius		return 0;
4277290001Sglebius	while (++s < eos) {
4278290001Sglebius		if (! EVUTIL_ISALNUM_(*s) &&
4279290001Sglebius		    *s != '+' && *s != '-' && *s != '.')
4280290001Sglebius			return 0;
4281290001Sglebius	}
4282290001Sglebius	return 1;
4283290001Sglebius}
4284290001Sglebius
4285290001Sglebius#define SUBDELIMS "!$&'()*+,;="
4286290001Sglebius
4287290001Sglebius/* Return true iff [s..eos) is a valid userinfo */
4288290001Sglebiusstatic int
4289290001Sglebiususerinfo_ok(const char *s, const char *eos)
4290290001Sglebius{
4291290001Sglebius	while (s < eos) {
4292290001Sglebius		if (CHAR_IS_UNRESERVED(*s) ||
4293290001Sglebius		    strchr(SUBDELIMS, *s) ||
4294290001Sglebius		    *s == ':')
4295290001Sglebius			++s;
4296290001Sglebius		else if (*s == '%' && s+2 < eos &&
4297290001Sglebius		    EVUTIL_ISXDIGIT_(s[1]) &&
4298290001Sglebius		    EVUTIL_ISXDIGIT_(s[2]))
4299290001Sglebius			s += 3;
4300290001Sglebius		else
4301290001Sglebius			return 0;
4302290001Sglebius	}
4303290001Sglebius	return 1;
4304290001Sglebius}
4305290001Sglebius
4306290001Sglebiusstatic int
4307290001Sglebiusregname_ok(const char *s, const char *eos)
4308290001Sglebius{
4309290001Sglebius	while (s && s<eos) {
4310290001Sglebius		if (CHAR_IS_UNRESERVED(*s) ||
4311290001Sglebius		    strchr(SUBDELIMS, *s))
4312290001Sglebius			++s;
4313290001Sglebius		else if (*s == '%' &&
4314290001Sglebius		    EVUTIL_ISXDIGIT_(s[1]) &&
4315290001Sglebius		    EVUTIL_ISXDIGIT_(s[2]))
4316290001Sglebius			s += 3;
4317290001Sglebius		else
4318290001Sglebius			return 0;
4319290001Sglebius	}
4320290001Sglebius	return 1;
4321290001Sglebius}
4322290001Sglebius
4323290001Sglebiusstatic int
4324290001Sglebiusparse_port(const char *s, const char *eos)
4325290001Sglebius{
4326290001Sglebius	int portnum = 0;
4327290001Sglebius	while (s < eos) {
4328290001Sglebius		if (! EVUTIL_ISDIGIT_(*s))
4329290001Sglebius			return -1;
4330290001Sglebius		portnum = (portnum * 10) + (*s - '0');
4331290001Sglebius		if (portnum < 0)
4332290001Sglebius			return -1;
4333290001Sglebius		if (portnum > 65535)
4334290001Sglebius			return -1;
4335290001Sglebius		++s;
4336290001Sglebius	}
4337290001Sglebius	return portnum;
4338290001Sglebius}
4339290001Sglebius
4340290001Sglebius/* returns 0 for bad, 1 for ipv6, 2 for IPvFuture */
4341290001Sglebiusstatic int
4342290001Sglebiusbracket_addr_ok(const char *s, const char *eos)
4343290001Sglebius{
4344290001Sglebius	if (s + 3 > eos || *s != '[' || *(eos-1) != ']')
4345290001Sglebius		return 0;
4346290001Sglebius	if (s[1] == 'v') {
4347290001Sglebius		/* IPvFuture, or junk.
4348290001Sglebius		   "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
4349290001Sglebius		 */
4350290001Sglebius		s += 2; /* skip [v */
4351290001Sglebius		--eos;
4352290001Sglebius		if (!EVUTIL_ISXDIGIT_(*s)) /*require at least one*/
4353290001Sglebius			return 0;
4354290001Sglebius		while (s < eos && *s != '.') {
4355290001Sglebius			if (EVUTIL_ISXDIGIT_(*s))
4356290001Sglebius				++s;
4357290001Sglebius			else
4358290001Sglebius				return 0;
4359290001Sglebius		}
4360290001Sglebius		if (*s != '.')
4361290001Sglebius			return 0;
4362290001Sglebius		++s;
4363290001Sglebius		while (s < eos) {
4364290001Sglebius			if (CHAR_IS_UNRESERVED(*s) ||
4365290001Sglebius			    strchr(SUBDELIMS, *s) ||
4366290001Sglebius			    *s == ':')
4367290001Sglebius				++s;
4368290001Sglebius			else
4369290001Sglebius				return 0;
4370290001Sglebius		}
4371290001Sglebius		return 2;
4372290001Sglebius	} else {
4373290001Sglebius		/* IPv6, or junk */
4374290001Sglebius		char buf[64];
4375290001Sglebius		ev_ssize_t n_chars = eos-s-2;
4376290001Sglebius		struct in6_addr in6;
4377290001Sglebius		if (n_chars >= 64) /* way too long */
4378290001Sglebius			return 0;
4379290001Sglebius		memcpy(buf, s+1, n_chars);
4380290001Sglebius		buf[n_chars]='\0';
4381290001Sglebius		return (evutil_inet_pton(AF_INET6,buf,&in6)==1) ? 1 : 0;
4382290001Sglebius	}
4383290001Sglebius}
4384290001Sglebius
4385290001Sglebiusstatic int
4386290001Sglebiusparse_authority(struct evhttp_uri *uri, char *s, char *eos)
4387290001Sglebius{
4388290001Sglebius	char *cp, *port;
4389290001Sglebius	EVUTIL_ASSERT(eos);
4390290001Sglebius	if (eos == s) {
4391290001Sglebius		uri->host = mm_strdup("");
4392290001Sglebius		if (uri->host == NULL) {
4393290001Sglebius			event_warn("%s: strdup", __func__);
4394290001Sglebius			return -1;
4395290001Sglebius		}
4396290001Sglebius		return 0;
4397290001Sglebius	}
4398290001Sglebius
4399290001Sglebius	/* Optionally, we start with "userinfo@" */
4400290001Sglebius
4401290001Sglebius	cp = strchr(s, '@');
4402290001Sglebius	if (cp && cp < eos) {
4403290001Sglebius		if (! userinfo_ok(s,cp))
4404290001Sglebius			return -1;
4405290001Sglebius		*cp++ = '\0';
4406290001Sglebius		uri->userinfo = mm_strdup(s);
4407290001Sglebius		if (uri->userinfo == NULL) {
4408290001Sglebius			event_warn("%s: strdup", __func__);
4409290001Sglebius			return -1;
4410290001Sglebius		}
4411290001Sglebius	} else {
4412290001Sglebius		cp = s;
4413290001Sglebius	}
4414290001Sglebius	/* Optionally, we end with ":port" */
4415290001Sglebius	for (port=eos-1; port >= cp && EVUTIL_ISDIGIT_(*port); --port)
4416290001Sglebius		;
4417290001Sglebius	if (port >= cp && *port == ':') {
4418290001Sglebius		if (port+1 == eos) /* Leave port unspecified; the RFC allows a
4419290001Sglebius				    * nil port */
4420290001Sglebius			uri->port = -1;
4421290001Sglebius		else if ((uri->port = parse_port(port+1, eos))<0)
4422290001Sglebius			return -1;
4423290001Sglebius		eos = port;
4424290001Sglebius	}
4425290001Sglebius	/* Now, cp..eos holds the "host" port, which can be an IPv4Address,
4426290001Sglebius	 * an IP-Literal, or a reg-name */
4427290001Sglebius	EVUTIL_ASSERT(eos >= cp);
4428290001Sglebius	if (*cp == '[' && eos >= cp+2 && *(eos-1) == ']') {
4429290001Sglebius		/* IPv6address, IP-Literal, or junk. */
4430290001Sglebius		if (! bracket_addr_ok(cp, eos))
4431290001Sglebius			return -1;
4432290001Sglebius	} else {
4433290001Sglebius		/* Make sure the host part is ok. */
4434290001Sglebius		if (! regname_ok(cp,eos)) /* Match IPv4Address or reg-name */
4435290001Sglebius			return -1;
4436290001Sglebius	}
4437290001Sglebius	uri->host = mm_malloc(eos-cp+1);
4438290001Sglebius	if (uri->host == NULL) {
4439290001Sglebius		event_warn("%s: malloc", __func__);
4440290001Sglebius		return -1;
4441290001Sglebius	}
4442290001Sglebius	memcpy(uri->host, cp, eos-cp);
4443290001Sglebius	uri->host[eos-cp] = '\0';
4444290001Sglebius	return 0;
4445290001Sglebius
4446290001Sglebius}
4447290001Sglebius
4448290001Sglebiusstatic char *
4449290001Sglebiusend_of_authority(char *cp)
4450290001Sglebius{
4451290001Sglebius	while (*cp) {
4452290001Sglebius		if (*cp == '?' || *cp == '#' || *cp == '/')
4453290001Sglebius			return cp;
4454290001Sglebius		++cp;
4455290001Sglebius	}
4456290001Sglebius	return cp;
4457290001Sglebius}
4458290001Sglebius
4459290001Sglebiusenum uri_part {
4460290001Sglebius	PART_PATH,
4461290001Sglebius	PART_QUERY,
4462290001Sglebius	PART_FRAGMENT
4463290001Sglebius};
4464290001Sglebius
4465290001Sglebius/* Return the character after the longest prefix of 'cp' that matches...
4466290001Sglebius *   *pchar / "/" if allow_qchars is false, or
4467290001Sglebius *   *(pchar / "/" / "?") if allow_qchars is true.
4468290001Sglebius */
4469290001Sglebiusstatic char *
4470290001Sglebiusend_of_path(char *cp, enum uri_part part, unsigned flags)
4471290001Sglebius{
4472290001Sglebius	if (flags & EVHTTP_URI_NONCONFORMANT) {
4473290001Sglebius		/* If NONCONFORMANT:
4474290001Sglebius		 *   Path is everything up to a # or ? or nul.
4475290001Sglebius		 *   Query is everything up a # or nul
4476290001Sglebius		 *   Fragment is everything up to a nul.
4477290001Sglebius		 */
4478290001Sglebius		switch (part) {
4479290001Sglebius		case PART_PATH:
4480290001Sglebius			while (*cp && *cp != '#' && *cp != '?')
4481290001Sglebius				++cp;
4482290001Sglebius			break;
4483290001Sglebius		case PART_QUERY:
4484290001Sglebius			while (*cp && *cp != '#')
4485290001Sglebius				++cp;
4486290001Sglebius			break;
4487290001Sglebius		case PART_FRAGMENT:
4488290001Sglebius			cp += strlen(cp);
4489290001Sglebius			break;
4490290001Sglebius		};
4491290001Sglebius		return cp;
4492290001Sglebius	}
4493290001Sglebius
4494290001Sglebius	while (*cp) {
4495290001Sglebius		if (CHAR_IS_UNRESERVED(*cp) ||
4496290001Sglebius		    strchr(SUBDELIMS, *cp) ||
4497290001Sglebius		    *cp == ':' || *cp == '@' || *cp == '/')
4498290001Sglebius			++cp;
4499290001Sglebius		else if (*cp == '%' && EVUTIL_ISXDIGIT_(cp[1]) &&
4500290001Sglebius		    EVUTIL_ISXDIGIT_(cp[2]))
4501290001Sglebius			cp += 3;
4502290001Sglebius		else if (*cp == '?' && part != PART_PATH)
4503290001Sglebius			++cp;
4504290001Sglebius		else
4505290001Sglebius			return cp;
4506290001Sglebius	}
4507290001Sglebius	return cp;
4508290001Sglebius}
4509290001Sglebius
4510290001Sglebiusstatic int
4511290001Sglebiuspath_matches_noscheme(const char *cp)
4512290001Sglebius{
4513290001Sglebius	while (*cp) {
4514290001Sglebius		if (*cp == ':')
4515290001Sglebius			return 0;
4516290001Sglebius		else if (*cp == '/')
4517290001Sglebius			return 1;
4518290001Sglebius		++cp;
4519290001Sglebius	}
4520290001Sglebius	return 1;
4521290001Sglebius}
4522290001Sglebius
4523290001Sglebiusstruct evhttp_uri *
4524290001Sglebiusevhttp_uri_parse(const char *source_uri)
4525290001Sglebius{
4526290001Sglebius	return evhttp_uri_parse_with_flags(source_uri, 0);
4527290001Sglebius}
4528290001Sglebius
4529290001Sglebiusstruct evhttp_uri *
4530290001Sglebiusevhttp_uri_parse_with_flags(const char *source_uri, unsigned flags)
4531290001Sglebius{
4532290001Sglebius	char *readbuf = NULL, *readp = NULL, *token = NULL, *query = NULL;
4533290001Sglebius	char *path = NULL, *fragment = NULL;
4534290001Sglebius	int got_authority = 0;
4535290001Sglebius
4536290001Sglebius	struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
4537290001Sglebius	if (uri == NULL) {
4538290001Sglebius		event_warn("%s: calloc", __func__);
4539290001Sglebius		goto err;
4540290001Sglebius	}
4541290001Sglebius	uri->port = -1;
4542290001Sglebius	uri->flags = flags;
4543290001Sglebius
4544290001Sglebius	readbuf = mm_strdup(source_uri);
4545290001Sglebius	if (readbuf == NULL) {
4546290001Sglebius		event_warn("%s: strdup", __func__);
4547290001Sglebius		goto err;
4548290001Sglebius	}
4549290001Sglebius
4550290001Sglebius	readp = readbuf;
4551290001Sglebius	token = NULL;
4552290001Sglebius
4553290001Sglebius	/* We try to follow RFC3986 here as much as we can, and match
4554290001Sglebius	   the productions
4555290001Sglebius
4556290001Sglebius	      URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
4557290001Sglebius
4558290001Sglebius	      relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
4559290001Sglebius	 */
4560290001Sglebius
4561290001Sglebius	/* 1. scheme: */
4562290001Sglebius	token = strchr(readp, ':');
4563290001Sglebius	if (token && scheme_ok(readp,token)) {
4564290001Sglebius		*token = '\0';
4565290001Sglebius		uri->scheme = mm_strdup(readp);
4566290001Sglebius		if (uri->scheme == NULL) {
4567290001Sglebius			event_warn("%s: strdup", __func__);
4568290001Sglebius			goto err;
4569290001Sglebius		}
4570290001Sglebius		readp = token+1; /* eat : */
4571290001Sglebius	}
4572290001Sglebius
4573290001Sglebius	/* 2. Optionally, "//" then an 'authority' part. */
4574290001Sglebius	if (readp[0]=='/' && readp[1] == '/') {
4575290001Sglebius		char *authority;
4576290001Sglebius		readp += 2;
4577290001Sglebius		authority = readp;
4578290001Sglebius		path = end_of_authority(readp);
4579290001Sglebius		if (parse_authority(uri, authority, path) < 0)
4580290001Sglebius			goto err;
4581290001Sglebius		readp = path;
4582290001Sglebius		got_authority = 1;
4583290001Sglebius	}
4584290001Sglebius
4585290001Sglebius	/* 3. Query: path-abempty, path-absolute, path-rootless, or path-empty
4586290001Sglebius	 */
4587290001Sglebius	path = readp;
4588290001Sglebius	readp = end_of_path(path, PART_PATH, flags);
4589290001Sglebius
4590290001Sglebius	/* Query */
4591290001Sglebius	if (*readp == '?') {
4592290001Sglebius		*readp = '\0';
4593290001Sglebius		++readp;
4594290001Sglebius		query = readp;
4595290001Sglebius		readp = end_of_path(readp, PART_QUERY, flags);
4596290001Sglebius	}
4597290001Sglebius	/* fragment */
4598290001Sglebius	if (*readp == '#') {
4599290001Sglebius		*readp = '\0';
4600290001Sglebius		++readp;
4601290001Sglebius		fragment = readp;
4602290001Sglebius		readp = end_of_path(readp, PART_FRAGMENT, flags);
4603290001Sglebius	}
4604290001Sglebius	if (*readp != '\0') {
4605290001Sglebius		goto err;
4606290001Sglebius	}
4607290001Sglebius
4608290001Sglebius	/* These next two cases may be unreachable; I'm leaving them
4609290001Sglebius	 * in to be defensive. */
4610290001Sglebius	/* If you didn't get an authority, the path can't begin with "//" */
4611290001Sglebius	if (!got_authority && path[0]=='/' && path[1]=='/')
4612290001Sglebius		goto err;
4613290001Sglebius	/* If you did get an authority, the path must begin with "/" or be
4614290001Sglebius	 * empty. */
4615290001Sglebius	if (got_authority && path[0] != '/' && path[0] != '\0')
4616290001Sglebius		goto err;
4617290001Sglebius	/* (End of maybe-unreachable cases) */
4618290001Sglebius
4619290001Sglebius	/* If there was no scheme, the first part of the path (if any) must
4620290001Sglebius	 * have no colon in it. */
4621290001Sglebius	if (! uri->scheme && !path_matches_noscheme(path))
4622290001Sglebius		goto err;
4623290001Sglebius
4624290001Sglebius	EVUTIL_ASSERT(path);
4625290001Sglebius	uri->path = mm_strdup(path);
4626290001Sglebius	if (uri->path == NULL) {
4627290001Sglebius		event_warn("%s: strdup", __func__);
4628290001Sglebius		goto err;
4629290001Sglebius	}
4630290001Sglebius
4631290001Sglebius	if (query) {
4632290001Sglebius		uri->query = mm_strdup(query);
4633290001Sglebius		if (uri->query == NULL) {
4634290001Sglebius			event_warn("%s: strdup", __func__);
4635290001Sglebius			goto err;
4636290001Sglebius		}
4637290001Sglebius	}
4638290001Sglebius	if (fragment) {
4639290001Sglebius		uri->fragment = mm_strdup(fragment);
4640290001Sglebius		if (uri->fragment == NULL) {
4641290001Sglebius			event_warn("%s: strdup", __func__);
4642290001Sglebius			goto err;
4643290001Sglebius		}
4644290001Sglebius	}
4645290001Sglebius
4646290001Sglebius	mm_free(readbuf);
4647290001Sglebius
4648290001Sglebius	return uri;
4649290001Sglebiuserr:
4650290001Sglebius	if (uri)
4651290001Sglebius		evhttp_uri_free(uri);
4652290001Sglebius	if (readbuf)
4653290001Sglebius		mm_free(readbuf);
4654290001Sglebius	return NULL;
4655290001Sglebius}
4656290001Sglebius
4657290001Sglebiusvoid
4658290001Sglebiusevhttp_uri_free(struct evhttp_uri *uri)
4659290001Sglebius{
4660290001Sglebius#define URI_FREE_STR_(f)		\
4661290001Sglebius	if (uri->f) {			\
4662290001Sglebius		mm_free(uri->f);		\
4663290001Sglebius	}
4664290001Sglebius
4665290001Sglebius	URI_FREE_STR_(scheme);
4666290001Sglebius	URI_FREE_STR_(userinfo);
4667290001Sglebius	URI_FREE_STR_(host);
4668290001Sglebius	URI_FREE_STR_(path);
4669290001Sglebius	URI_FREE_STR_(query);
4670290001Sglebius	URI_FREE_STR_(fragment);
4671290001Sglebius
4672290001Sglebius	mm_free(uri);
4673290001Sglebius#undef URI_FREE_STR_
4674290001Sglebius}
4675290001Sglebius
4676290001Sglebiuschar *
4677290001Sglebiusevhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
4678290001Sglebius{
4679290001Sglebius	struct evbuffer *tmp = 0;
4680290001Sglebius	size_t joined_size = 0;
4681290001Sglebius	char *output = NULL;
4682290001Sglebius
4683290001Sglebius#define URI_ADD_(f)	evbuffer_add(tmp, uri->f, strlen(uri->f))
4684290001Sglebius
4685290001Sglebius	if (!uri || !buf || !limit)
4686290001Sglebius		return NULL;
4687290001Sglebius
4688290001Sglebius	tmp = evbuffer_new();
4689290001Sglebius	if (!tmp)
4690290001Sglebius		return NULL;
4691290001Sglebius
4692290001Sglebius	if (uri->scheme) {
4693290001Sglebius		URI_ADD_(scheme);
4694290001Sglebius		evbuffer_add(tmp, ":", 1);
4695290001Sglebius	}
4696290001Sglebius	if (uri->host) {
4697290001Sglebius		evbuffer_add(tmp, "//", 2);
4698290001Sglebius		if (uri->userinfo)
4699290001Sglebius			evbuffer_add_printf(tmp,"%s@", uri->userinfo);
4700290001Sglebius		URI_ADD_(host);
4701290001Sglebius		if (uri->port >= 0)
4702290001Sglebius			evbuffer_add_printf(tmp,":%d", uri->port);
4703290001Sglebius
4704290001Sglebius		if (uri->path && uri->path[0] != '/' && uri->path[0] != '\0')
4705290001Sglebius			goto err;
4706290001Sglebius	}
4707290001Sglebius
4708290001Sglebius	if (uri->path)
4709290001Sglebius		URI_ADD_(path);
4710290001Sglebius
4711290001Sglebius	if (uri->query) {
4712290001Sglebius		evbuffer_add(tmp, "?", 1);
4713290001Sglebius		URI_ADD_(query);
4714290001Sglebius	}
4715290001Sglebius
4716290001Sglebius	if (uri->fragment) {
4717290001Sglebius		evbuffer_add(tmp, "#", 1);
4718290001Sglebius		URI_ADD_(fragment);
4719290001Sglebius	}
4720290001Sglebius
4721290001Sglebius	evbuffer_add(tmp, "\0", 1); /* NUL */
4722290001Sglebius
4723290001Sglebius	joined_size = evbuffer_get_length(tmp);
4724290001Sglebius
4725290001Sglebius	if (joined_size > limit) {
4726290001Sglebius		/* It doesn't fit. */
4727290001Sglebius		evbuffer_free(tmp);
4728290001Sglebius		return NULL;
4729290001Sglebius	}
4730290001Sglebius       	evbuffer_remove(tmp, buf, joined_size);
4731290001Sglebius
4732290001Sglebius	output = buf;
4733290001Sglebiuserr:
4734290001Sglebius	evbuffer_free(tmp);
4735290001Sglebius
4736290001Sglebius	return output;
4737290001Sglebius#undef URI_ADD_
4738290001Sglebius}
4739290001Sglebius
4740290001Sglebiusconst char *
4741290001Sglebiusevhttp_uri_get_scheme(const struct evhttp_uri *uri)
4742290001Sglebius{
4743290001Sglebius	return uri->scheme;
4744290001Sglebius}
4745290001Sglebiusconst char *
4746290001Sglebiusevhttp_uri_get_userinfo(const struct evhttp_uri *uri)
4747290001Sglebius{
4748290001Sglebius	return uri->userinfo;
4749290001Sglebius}
4750290001Sglebiusconst char *
4751290001Sglebiusevhttp_uri_get_host(const struct evhttp_uri *uri)
4752290001Sglebius{
4753290001Sglebius	return uri->host;
4754290001Sglebius}
4755290001Sglebiusint
4756290001Sglebiusevhttp_uri_get_port(const struct evhttp_uri *uri)
4757290001Sglebius{
4758290001Sglebius	return uri->port;
4759290001Sglebius}
4760290001Sglebiusconst char *
4761290001Sglebiusevhttp_uri_get_path(const struct evhttp_uri *uri)
4762290001Sglebius{
4763290001Sglebius	return uri->path;
4764290001Sglebius}
4765290001Sglebiusconst char *
4766290001Sglebiusevhttp_uri_get_query(const struct evhttp_uri *uri)
4767290001Sglebius{
4768290001Sglebius	return uri->query;
4769290001Sglebius}
4770290001Sglebiusconst char *
4771290001Sglebiusevhttp_uri_get_fragment(const struct evhttp_uri *uri)
4772290001Sglebius{
4773290001Sglebius	return uri->fragment;
4774290001Sglebius}
4775290001Sglebius
4776290001Sglebius#define URI_SET_STR_(f) do {					\
4777290001Sglebius	if (uri->f)						\
4778290001Sglebius		mm_free(uri->f);				\
4779290001Sglebius	if (f) {						\
4780290001Sglebius		if ((uri->f = mm_strdup(f)) == NULL) {		\
4781290001Sglebius			event_warn("%s: strdup()", __func__);	\
4782290001Sglebius			return -1;				\
4783290001Sglebius		}						\
4784290001Sglebius	} else {						\
4785290001Sglebius		uri->f = NULL;					\
4786290001Sglebius	}							\
4787290001Sglebius	} while(0)
4788290001Sglebius
4789290001Sglebiusint
4790290001Sglebiusevhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme)
4791290001Sglebius{
4792290001Sglebius	if (scheme && !scheme_ok(scheme, scheme+strlen(scheme)))
4793290001Sglebius		return -1;
4794290001Sglebius
4795290001Sglebius	URI_SET_STR_(scheme);
4796290001Sglebius	return 0;
4797290001Sglebius}
4798290001Sglebiusint
4799290001Sglebiusevhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo)
4800290001Sglebius{
4801290001Sglebius	if (userinfo && !userinfo_ok(userinfo, userinfo+strlen(userinfo)))
4802290001Sglebius		return -1;
4803290001Sglebius	URI_SET_STR_(userinfo);
4804290001Sglebius	return 0;
4805290001Sglebius}
4806290001Sglebiusint
4807290001Sglebiusevhttp_uri_set_host(struct evhttp_uri *uri, const char *host)
4808290001Sglebius{
4809290001Sglebius	if (host) {
4810290001Sglebius		if (host[0] == '[') {
4811290001Sglebius			if (! bracket_addr_ok(host, host+strlen(host)))
4812290001Sglebius				return -1;
4813290001Sglebius		} else {
4814290001Sglebius			if (! regname_ok(host, host+strlen(host)))
4815290001Sglebius				return -1;
4816290001Sglebius		}
4817290001Sglebius	}
4818290001Sglebius
4819290001Sglebius	URI_SET_STR_(host);
4820290001Sglebius	return 0;
4821290001Sglebius}
4822290001Sglebiusint
4823290001Sglebiusevhttp_uri_set_port(struct evhttp_uri *uri, int port)
4824290001Sglebius{
4825290001Sglebius	if (port < -1)
4826290001Sglebius		return -1;
4827290001Sglebius	uri->port = port;
4828290001Sglebius	return 0;
4829290001Sglebius}
4830290001Sglebius#define end_of_cpath(cp,p,f) \
4831290001Sglebius	((const char*)(end_of_path(((char*)(cp)), (p), (f))))
4832290001Sglebius
4833290001Sglebiusint
4834290001Sglebiusevhttp_uri_set_path(struct evhttp_uri *uri, const char *path)
4835290001Sglebius{
4836290001Sglebius	if (path && end_of_cpath(path, PART_PATH, uri->flags) != path+strlen(path))
4837290001Sglebius		return -1;
4838290001Sglebius
4839290001Sglebius	URI_SET_STR_(path);
4840290001Sglebius	return 0;
4841290001Sglebius}
4842290001Sglebiusint
4843290001Sglebiusevhttp_uri_set_query(struct evhttp_uri *uri, const char *query)
4844290001Sglebius{
4845290001Sglebius	if (query && end_of_cpath(query, PART_QUERY, uri->flags) != query+strlen(query))
4846290001Sglebius		return -1;
4847290001Sglebius	URI_SET_STR_(query);
4848290001Sglebius	return 0;
4849290001Sglebius}
4850290001Sglebiusint
4851290001Sglebiusevhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment)
4852290001Sglebius{
4853290001Sglebius	if (fragment && end_of_cpath(fragment, PART_FRAGMENT, uri->flags) != fragment+strlen(fragment))
4854290001Sglebius		return -1;
4855290001Sglebius	URI_SET_STR_(fragment);
4856290001Sglebius	return 0;
4857290001Sglebius}
4858