1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  *    if any, must include the following acknowledgment:
22  *       "This product includes software developed by the
23  *        Kannel Group (http://www.kannel.org/)."
24  *    Alternately, this acknowledgment may appear in the software itself,
25  *    if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  *    endorse or promote products derived from this software without
29  *    prior written permission. For written permission, please
30  *    contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  *    nor may "Kannel" appear in their name, without prior written
34  *    permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group.  For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * http.c - HTTP protocol server and client implementation
59  *
60  * Implements major parts of the Hypertext Transfer Protocol HTTP/1.1 (RFC 2616)
61  * See http://www.w3.org/Protocols/rfc2616/rfc2616.txt
62  *
63  * Lars Wirzenius
64  */
65 
66 /* XXX re-implement socket pools, with idle connection killing to
67     	save sockets */
68 /* XXX implement http_abort */
69 /* XXX give maximum input size */
70 /* XXX kill http_get_real */
71 /* XXX the proxy exceptions list should be a dict, I guess */
72 /* XXX set maximum number of concurrent connections to same host, total? */
73 /* XXX 100 status codes. */
74 /* XXX stop destroying persistent connections when a request is redirected */
75 
76 #include <ctype.h>
77 #include <errno.h>
78 #include <unistd.h>
79 #include <string.h>
80 #include <signal.h>
81 #include <sys/types.h>
82 #include <sys/socket.h>
83 
84 #include "gwlib.h"
85 #include "gwlib/regex.h"
86 
87 /* comment this out if you don't want HTTP responses to be dumped */
88 #define DUMP_RESPONSE 1
89 
90 /* define http client connections timeout in seconds (set to -1 for disable) */
91 static int http_client_timeout = 240;
92 
93 /* define http server connections timeout in seconds (set to -1 for disable) */
94 #define HTTP_SERVER_TIMEOUT 60
95 /* max accepted clients */
96 #define HTTP_SERVER_MAX_ACTIVE_CONNECTIONS 500
97 
98 /***********************************************************************
99  * Stuff used in several sub-modules.
100  */
101 
102 
103 /*
104  * Default port to connect to for HTTP connections.
105  */
106 enum { HTTP_PORT = 80,
107        HTTPS_PORT = 443 };
108 
109 
110 /*
111  * Status of this module.
112  */
113 static enum {
114     limbo,
115     running,
116     terminating
117 } run_status = limbo;
118 
119 
120 /*
121  * Which interface to use for outgoing HTTP requests.
122  */
123 static Octstr *http_interface = NULL;
124 
125 
126 /*
127  * Read some headers, i.e., until the first empty line (read and discard
128  * the empty line as well). Return -1 for error, 0 for all headers read,
129  * 1 for more headers to follow.
130  */
read_some_headers(Connection * conn,List * headers)131 static int read_some_headers(Connection *conn, List *headers)
132 {
133     Octstr *line, *prev;
134 
135     if (gwlist_len(headers) == 0)
136         prev = NULL;
137     else
138     	prev = gwlist_get(headers, gwlist_len(headers) - 1);
139 
140     for (;;) {
141 	line = conn_read_line(conn);
142 	if (line == NULL) {
143             if (conn_eof(conn) || conn_error(conn))
144 	    	return -1;
145 	    return 1;
146 	}
147         if (octstr_len(line) == 0) {
148             octstr_destroy(line);
149             break;
150         }
151         if (isspace(octstr_get_char(line, 0)) && prev != NULL) {
152             octstr_append(prev, line);
153             octstr_destroy(line);
154         } else {
155             gwlist_append(headers, line);
156             prev = line;
157         }
158     }
159 
160     return 0;
161 }
162 
163 
164 /*
165  * Check that the HTTP version string is valid. Return -1 for invalid,
166  * 0 for version 1.0, 1 for 1.x.
167  */
parse_http_version(Octstr * version)168 static int parse_http_version(Octstr *version)
169 {
170     Octstr *prefix;
171     long prefix_len;
172     int digit;
173 
174     prefix = octstr_imm("HTTP/1.");
175     prefix_len = octstr_len(prefix);
176 
177     if (octstr_ncompare(version, prefix, prefix_len) != 0)
178     	return -1;
179     if (octstr_len(version) != prefix_len + 1)
180     	return -1;
181     digit = octstr_get_char(version, prefix_len);
182     if (!isdigit(digit))
183     	return -1;
184     if (digit == '0')
185     	return 0;
186     return 1;
187 }
188 
189 
190 /***********************************************************************
191  * Proxy support.
192  */
193 
194 
195 /*
196  * Data and functions needed to support proxy operations. If proxy_hostname
197  * is NULL, no proxy is used.
198  */
199 static Mutex *proxy_mutex = NULL;
200 static Octstr *proxy_hostname = NULL;
201 static int proxy_port = 0;
202 static int proxy_ssl = 0;
203 static Octstr *proxy_username = NULL;
204 static Octstr *proxy_password = NULL;
205 static List *proxy_exceptions = NULL;
206 static regex_t *proxy_exceptions_regex = NULL;
207 
208 
proxy_add_authentication(List * headers)209 static void proxy_add_authentication(List *headers)
210 {
211     Octstr *os;
212 
213     if (proxy_username == NULL || proxy_password == NULL)
214     	return;
215 
216     os = octstr_format("%S:%S", proxy_username, proxy_password);
217     octstr_binary_to_base64(os);
218     octstr_strip_blanks(os);
219     octstr_insert(os, octstr_imm("Basic "), 0);
220     http_header_add(headers, "Proxy-Authorization", octstr_get_cstr(os));
221     octstr_destroy(os);
222 }
223 
224 
proxy_init(void)225 static void proxy_init(void)
226 {
227     proxy_mutex = mutex_create();
228     proxy_exceptions = gwlist_create();
229 }
230 
231 
proxy_shutdown(void)232 static void proxy_shutdown(void)
233 {
234     http_close_proxy();
235     mutex_destroy(proxy_mutex);
236     proxy_mutex = NULL;
237 }
238 
239 
proxy_used_for_host(Octstr * host,Octstr * url)240 static int proxy_used_for_host(Octstr *host, Octstr *url)
241 {
242     int i;
243 
244     mutex_lock(proxy_mutex);
245 
246     if (proxy_hostname == NULL) {
247         mutex_unlock(proxy_mutex);
248         return 0;
249     }
250 
251     for (i = 0; i < gwlist_len(proxy_exceptions); ++i) {
252         if (octstr_compare(host, gwlist_get(proxy_exceptions, i)) == 0) {
253             mutex_unlock(proxy_mutex);
254             return 0;
255         }
256     }
257 
258     if (proxy_exceptions_regex != NULL && gw_regex_match_pre(proxy_exceptions_regex, url)) {
259             mutex_unlock(proxy_mutex);
260             return 0;
261     }
262 
263     mutex_unlock(proxy_mutex);
264     return 1;
265 }
266 
267 
http_use_proxy(Octstr * hostname,int port,int ssl,List * exceptions,Octstr * username,Octstr * password,Octstr * exceptions_regex)268 void http_use_proxy(Octstr *hostname, int port, int ssl, List *exceptions,
269     	    	    Octstr *username, Octstr *password, Octstr *exceptions_regex)
270 {
271     Octstr *e;
272     int i;
273 
274     gw_assert(run_status == running);
275     gw_assert(hostname != NULL);
276     gw_assert(octstr_len(hostname) > 0);
277     gw_assert(port > 0);
278 
279     http_close_proxy();
280     mutex_lock(proxy_mutex);
281 
282     proxy_hostname = octstr_duplicate(hostname);
283     proxy_port = port;
284     proxy_ssl = ssl;
285     proxy_exceptions = gwlist_create();
286     for (i = 0; i < gwlist_len(exceptions); ++i) {
287         e = gwlist_get(exceptions, i);
288         debug("gwlib.http", 0, "HTTP: Proxy exception `%s'.", octstr_get_cstr(e));
289         gwlist_append(proxy_exceptions, octstr_duplicate(e));
290     }
291     if (exceptions_regex != NULL &&
292         (proxy_exceptions_regex = gw_regex_comp(exceptions_regex, REG_EXTENDED)) == NULL)
293             panic(0, "Could not compile pattern '%s'", octstr_get_cstr(exceptions_regex));
294     proxy_username = octstr_duplicate(username);
295     proxy_password = octstr_duplicate(password);
296     debug("gwlib.http", 0, "Using proxy <%s:%d> with %s scheme",
297     	  octstr_get_cstr(proxy_hostname), proxy_port,
298     	  (proxy_ssl ? "HTTPS" : "HTTP"));
299 
300     mutex_unlock(proxy_mutex);
301 }
302 
303 
http_close_proxy(void)304 void http_close_proxy(void)
305 {
306     gw_assert(run_status == running || run_status == terminating);
307 
308     mutex_lock(proxy_mutex);
309     proxy_port = 0;
310     octstr_destroy(proxy_hostname);
311     octstr_destroy(proxy_username);
312     octstr_destroy(proxy_password);
313     proxy_hostname = NULL;
314     proxy_username = NULL;
315     proxy_password = NULL;
316     gwlist_destroy(proxy_exceptions, octstr_destroy_item);
317     gw_regex_destroy(proxy_exceptions_regex);
318     proxy_exceptions = NULL;
319     proxy_exceptions_regex = NULL;
320     mutex_unlock(proxy_mutex);
321 }
322 
323 
324 /***********************************************************************
325  * Common functions for reading request or result entities.
326  */
327 
328 /*
329  * Value to pass to entity_create.
330  */
331 enum body_expectation {
332    /*
333     * Message must not have a body, even if the headers indicate one.
334     * (i.e. response to HEAD method).
335     */
336    expect_no_body,
337    /*
338     * Message will have a body if Content-Length or Transfer-Encoding
339     * headers are present (i.e. most request methods).
340     */
341    expect_body_if_indicated,
342    /*
343     * Message will have a body, possibly zero-length.
344     * (i.e. 200 OK responses to a GET method.)
345     */
346    expect_body
347 };
348 
349 enum entity_state {
350     reading_headers,
351     reading_chunked_body_len,
352     reading_chunked_body_data,
353     reading_chunked_body_crlf,
354     reading_chunked_body_trailer,
355     reading_body_until_eof,
356     reading_body_with_length,
357     body_error,
358     entity_done
359 };
360 
361 typedef struct {
362     List *headers;
363     Octstr *body;
364     enum body_expectation expect_state;
365     enum entity_state state;
366     long chunked_body_chunk_len;
367     long expected_body_len;
368 } HTTPEntity;
369 
370 
371 /*
372  * The rules for message bodies (length and presence) are defined
373  * in RFC2616 paragraph 4.3 and 4.4.
374  */
deduce_body_state(HTTPEntity * ent)375 static void deduce_body_state(HTTPEntity *ent)
376 {
377     Octstr *h = NULL;
378 
379     if (ent->expect_state == expect_no_body) {
380         ent->state = entity_done;
381         return;
382     }
383 
384     ent->state = body_error;  /* safety net */
385 
386     h = http_header_find_first(ent->headers, "Transfer-Encoding");
387     if (h != NULL) {
388         octstr_strip_blanks(h);
389         if (octstr_str_compare(h, "chunked") != 0) {
390             error(0, "HTTP: Unknown Transfer-Encoding <%s>",
391                   octstr_get_cstr(h));
392             ent->state = body_error;
393         } else {
394             ent->state = reading_chunked_body_len;
395         }
396         octstr_destroy(h);
397         return;
398     }
399 
400     h = http_header_find_first(ent->headers, "Content-Length");
401     if (h != NULL) {
402         if (octstr_parse_long(&ent->expected_body_len, h, 0, 10) == -1 ||
403             ent->expected_body_len < 0) {
404             error(0, "HTTP: Content-Length header wrong: <%s>",
405                   octstr_get_cstr(h));
406             ent->state = body_error;
407         } else if (ent->expected_body_len == 0) {
408             ent->state = entity_done;
409         } else {
410             ent->state = reading_body_with_length;
411         }
412         octstr_destroy(h);
413         return;
414     }
415 
416     if (ent->expect_state == expect_body)
417         ent->state = reading_body_until_eof;
418     else
419         ent->state = entity_done;
420 }
421 
422 
423 /*
424  * Create a HTTPEntity structure suitable for reading the expected
425  * result or request message and decoding the transferred entity (if any).
426  * See the definition of enum body_expectation for the possible values
427  * of exp.
428  */
entity_create(enum body_expectation exp)429 static HTTPEntity *entity_create(enum body_expectation exp)
430 {
431     HTTPEntity *ent;
432 
433     ent = gw_malloc(sizeof(*ent));
434     ent->headers = http_create_empty_headers();
435     ent->body = octstr_create("");
436     ent->chunked_body_chunk_len = -1;
437     ent->expected_body_len = -1;
438     ent->state = reading_headers;
439     ent->expect_state = exp;
440 
441     return ent;
442 }
443 
444 
entity_destroy(HTTPEntity * ent)445 static void entity_destroy(HTTPEntity *ent)
446 {
447     if (ent == NULL)
448         return;
449 
450     http_destroy_headers(ent->headers);
451     octstr_destroy(ent->body);
452     gw_free(ent);
453 }
454 
455 
read_chunked_body_len(HTTPEntity * ent,Connection * conn)456 static void read_chunked_body_len(HTTPEntity *ent, Connection *conn)
457 {
458     Octstr *os;
459     long len;
460 
461     os = conn_read_line(conn);
462     if (os == NULL) {
463         if (conn_error(conn) || conn_eof(conn))
464 	    ent->state = body_error;
465         return;
466     }
467     if (octstr_parse_long(&len, os, 0, 16) == -1) {
468         octstr_destroy(os);
469 	ent->state = body_error;
470         return;
471     }
472     octstr_destroy(os);
473     if (len == 0)
474         ent->state = reading_chunked_body_trailer;
475     else {
476         ent->state = reading_chunked_body_data;
477         ent->chunked_body_chunk_len = len;
478     }
479 }
480 
481 
read_chunked_body_data(HTTPEntity * ent,Connection * conn)482 static void read_chunked_body_data(HTTPEntity *ent, Connection *conn)
483 {
484     Octstr *os;
485 
486     os = conn_read_fixed(conn, ent->chunked_body_chunk_len);
487     if (os == NULL) {
488         if (conn_error(conn) || conn_eof(conn))
489 	    ent->state = body_error;
490     } else {
491         octstr_append(ent->body, os);
492         octstr_destroy(os);
493         ent->state = reading_chunked_body_crlf;
494     }
495 }
496 
497 
read_chunked_body_crlf(HTTPEntity * ent,Connection * conn)498 static void read_chunked_body_crlf(HTTPEntity *ent, Connection *conn)
499 {
500     Octstr *os;
501 
502     os = conn_read_line(conn);
503     if (os == NULL) {
504         if (conn_error(conn) || conn_eof(conn))
505 	    ent->state = body_error;
506     } else {
507         octstr_destroy(os);
508         ent->state = reading_chunked_body_len;
509     }
510 }
511 
512 
read_chunked_body_trailer(HTTPEntity * ent,Connection * conn)513 static void read_chunked_body_trailer(HTTPEntity *ent, Connection *conn)
514 {
515     int ret;
516 
517     ret = read_some_headers(conn, ent->headers);
518     if (ret == -1)
519 	ent->state = body_error;
520     if (ret == 0)
521         ent->state = entity_done;
522 }
523 
524 
read_body_until_eof(HTTPEntity * ent,Connection * conn)525 static void read_body_until_eof(HTTPEntity *ent, Connection *conn)
526 {
527     Octstr *os;
528 
529     while ((os = conn_read_everything(conn)) != NULL) {
530         octstr_append(ent->body, os);
531         octstr_destroy(os);
532     }
533     if (conn_error(conn))
534 	ent->state = body_error;
535     if (conn_eof(conn))
536 	ent->state = entity_done;
537 }
538 
539 
read_body_with_length(HTTPEntity * ent,Connection * conn)540 static void read_body_with_length(HTTPEntity *ent, Connection *conn)
541 {
542     Octstr *os;
543 
544     os = conn_read_fixed(conn, ent->expected_body_len);
545     if (os == NULL) {
546         if (conn_error(conn) || conn_eof(conn))
547             ent->state = body_error;
548         return;
549     }
550     octstr_destroy(ent->body);
551     ent->body = os;
552     ent->state = entity_done;
553 }
554 
555 
556 /*
557  * Read headers and body (if any) from this connection.  Return 0 if it's
558  * complete, 1 if we expect more input, and -1 if there is something wrong.
559  */
entity_read(HTTPEntity * ent,Connection * conn)560 static int entity_read(HTTPEntity *ent, Connection *conn)
561 {
562     int ret;
563     enum entity_state old_state;
564 
565     /*
566      * In this loop, each state will process as much input as it needs
567      * and then switch to the next state, unless it's a final state in
568      * which case it returns directly, or unless it needs more input.
569      * So keep looping as long as the state changes.
570      */
571     do {
572         old_state = ent->state;
573         switch (ent->state) {
574         case reading_headers:
575             ret = read_some_headers(conn, ent->headers);
576                 if (ret == 0)
577                 deduce_body_state(ent);
578             if (ret < 0)
579             return -1;
580             break;
581 
582         case reading_chunked_body_len:
583             read_chunked_body_len(ent, conn);
584             break;
585 
586         case reading_chunked_body_data:
587             read_chunked_body_data(ent, conn);
588             break;
589 
590         case reading_chunked_body_crlf:
591             read_chunked_body_crlf(ent, conn);
592             break;
593 
594         case reading_chunked_body_trailer:
595             read_chunked_body_trailer(ent, conn);
596             break;
597 
598         case reading_body_until_eof:
599             read_body_until_eof(ent, conn);
600             break;
601 
602         case reading_body_with_length:
603             read_body_with_length(ent, conn);
604             break;
605 
606         case body_error:
607             return -1;
608 
609         case entity_done:
610             return 0;
611 
612         default:
613             panic(0, "Internal error: Invalid HTTPEntity state.");
614         }
615     } while (ent->state != old_state);
616 
617     /*
618      * If we got here, then the loop ended because a non-final state
619      * needed more input.
620      */
621     return 1;
622 }
623 
624 
625 /***********************************************************************
626  * HTTP client interface.
627  */
628 
629 /*
630  * Internal lists of completely unhandled requests and requests for which
631  * a request has been sent but response has not yet been read.
632  */
633 static List *pending_requests = NULL;
634 
635 
636 /*
637  * Have background threads been started?
638  */
639 static Mutex *client_thread_lock = NULL;
640 static volatile sig_atomic_t client_threads_are_running = 0;
641 
642 
643 /*
644  * Set of all connections to all servers. Used with conn_register to
645  * do I/O on several connections with a single thread.
646  */
647 static FDSet *client_fdset = NULL;
648 
649 /*
650  * Maximum number of HTTP redirections to follow. Making this infinite
651  * could cause infinite looping if the redirections loop.
652  */
653 #define HTTP_MAX_FOLLOW 5
654 
655 
656 /*
657  * The implemented HTTP method strings
658  * Order is sequenced by the enum in the header
659  */
660 static char *http_methods[] = {
661     "GET", "POST", "HEAD"
662 };
663 
664 /*
665  * Information about a server we've connected to.
666  */
667 typedef struct {
668     HTTPCaller *caller;
669     void *request_id;
670     int method;             /* uses enums from http.h for the HTTP methods */
671     Octstr *url;            /* the full URL, including scheme, host, etc. */
672     Octstr *uri;            /* the HTTP URI path only */
673     List *request_headers;
674     Octstr *request_body;   /* NULL for GET or HEAD, non-NULL for POST */
675     enum {
676 	connecting,
677 	request_not_sent,
678 	reading_status,
679 	reading_entity,
680 	transaction_done
681     } state;
682     long status;
683     int persistent;
684     HTTPEntity *response; /* Can only be NULL if status < 0 */
685     Connection *conn;
686     Octstr *host;
687     long port;
688     int follow_remaining;
689     Octstr *certkeyfile;
690     int ssl;
691     Octstr *username;	/* For basic authentication */
692     Octstr *password;
693 } HTTPServer;
694 
695 
696 static int send_request(HTTPServer *trans);
697 static Octstr *build_response(List *headers, Octstr *body);
698 static int header_is_called(Octstr *header, char *name);
699 
server_create(HTTPCaller * caller,int method,Octstr * url,List * headers,Octstr * body,int follow_remaining,Octstr * certkeyfile)700 static HTTPServer *server_create(HTTPCaller *caller, int method, Octstr *url,
701                                  List *headers, Octstr *body, int follow_remaining,
702                                  Octstr *certkeyfile)
703 {
704     HTTPServer *trans;
705 
706     trans = gw_malloc(sizeof(*trans));
707     trans->caller = caller;
708     trans->request_id = NULL;
709     trans->method = method;
710     trans->url = octstr_duplicate(url);
711     trans->uri = NULL;
712     trans->request_headers = http_header_duplicate(headers);
713     trans->request_body = octstr_duplicate(body);
714     trans->state = request_not_sent;
715     trans->status = -1;
716     trans->persistent = 0;
717     trans->response = NULL;
718     trans->conn = NULL;
719     trans->host = NULL;
720     trans->port = 0;
721     trans->username = NULL;
722     trans->password = NULL;
723     trans->follow_remaining = follow_remaining;
724     trans->certkeyfile = octstr_duplicate(certkeyfile);
725     trans->ssl = 0;
726     return trans;
727 }
728 
729 
server_destroy(void * p)730 static void server_destroy(void *p)
731 {
732     HTTPServer *trans;
733 
734     trans = p;
735     octstr_destroy(trans->url);
736     octstr_destroy(trans->uri);
737     http_destroy_headers(trans->request_headers);
738     trans->request_headers = NULL;
739     octstr_destroy(trans->request_body);
740     entity_destroy(trans->response);
741     octstr_destroy(trans->host);
742     octstr_destroy(trans->certkeyfile);
743     octstr_destroy(trans->username);
744     octstr_destroy(trans->password);
745     gw_free(trans);
746 }
747 
748 
749 /*
750  * Pool of open, but unused connections to servers or proxies. Key is
751  * "servername:port", value is List with Connection objects.
752  */
753 static Dict *conn_pool;
754 static Mutex *conn_pool_lock;
755 
756 
conn_pool_item_destroy(void * item)757 static void conn_pool_item_destroy(void *item)
758 {
759     gwlist_destroy(item, (void(*)(void*))conn_destroy);
760 }
761 
conn_pool_init(void)762 static void conn_pool_init(void)
763 {
764     conn_pool = dict_create(1024, conn_pool_item_destroy);
765     conn_pool_lock = mutex_create();
766 }
767 
768 
conn_pool_shutdown(void)769 static void conn_pool_shutdown(void)
770 {
771     dict_destroy(conn_pool);
772     mutex_destroy(conn_pool_lock);
773 }
774 
775 
conn_pool_key(Octstr * host,int port,int ssl,Octstr * certfile,Octstr * our_host)776 static inline Octstr *conn_pool_key(Octstr *host, int port, int ssl, Octstr *certfile, Octstr *our_host)
777 {
778     return octstr_format("%S:%d:%d:%S:%S", host, port, ssl?1:0, certfile?certfile:octstr_imm(""),
779                          our_host?our_host:octstr_imm(""));
780 }
781 
782 
conn_pool_get(Octstr * host,int port,int ssl,Octstr * certkeyfile,Octstr * our_host)783 static Connection *conn_pool_get(Octstr *host, int port, int ssl, Octstr *certkeyfile,
784 		Octstr *our_host)
785 {
786     Octstr *key;
787     List *list = NULL;
788     Connection *conn = NULL;
789     int retry;
790 
791     do {
792         retry = 0;
793         key = conn_pool_key(host, port, ssl, certkeyfile, our_host);
794         mutex_lock(conn_pool_lock);
795         list = dict_get(conn_pool, key);
796         if (list != NULL)
797             conn = gwlist_extract_first(list);
798         mutex_unlock(conn_pool_lock);
799         /*
800          * Note: we don't hold conn_pool_lock when we check/destroy/unregister
801          *       connection because otherwise we can deadlock! And it's even better
802          *       not to delay other threads while we check connection.
803          */
804         if (conn != NULL) {
805 #ifdef USE_KEEPALIVE
806             /* unregister our server disconnect callback */
807             conn_unregister(conn);
808 #endif
809             /*
810              * Check whether the server has closed the connection while
811              * it has been in the pool.
812              */
813             conn_wait(conn, 0);
814             if (conn_eof(conn) || conn_error(conn)) {
815                 debug("gwlib.http", 0, "HTTP:conn_pool_get: Server closed connection, destroying it <%s><%p><fd:%d>.",
816                       octstr_get_cstr(key), conn, conn_get_id(conn));
817                 conn_destroy(conn);
818                 retry = 1;
819                 conn = NULL;
820             }
821         }
822         octstr_destroy(key);
823     } while(retry == 1);
824 
825     if (conn == NULL) {
826 #ifdef HAVE_LIBSSL
827         if (ssl)
828             conn = conn_open_ssl_nb(host, port, certkeyfile, our_host);
829         else
830 #endif /* HAVE_LIBSSL */
831             conn = conn_open_tcp_nb(host, port, our_host);
832         debug("gwlib.http", 0, "HTTP: Opening connection to `%s:%d' (fd=%d).",
833               octstr_get_cstr(host), port, conn_get_id(conn));
834     } else {
835         debug("gwlib.http", 0, "HTTP: Reusing connection to `%s:%d' (fd=%d).",
836               octstr_get_cstr(host), port, conn_get_id(conn));
837     }
838 
839     return conn;
840 }
841 
842 #ifdef USE_KEEPALIVE
check_pool_conn(Connection * conn,void * data)843 static void check_pool_conn(Connection *conn, void *data)
844 {
845     Octstr *key = data;
846 
847     if (run_status != running) {
848         conn_unregister(conn);
849         return;
850     }
851     /* check if connection still ok */
852     if (conn_error(conn) || conn_eof(conn)) {
853         List *list;
854         mutex_lock(conn_pool_lock);
855         list = dict_get(conn_pool, key);
856         if (gwlist_delete_equal(list, conn) > 0) {
857             /*
858              * ok, connection was still within pool. So it's
859              * safe to destroy this connection.
860              */
861             debug("gwlib.http", 0, "HTTP: Server closed connection, destroying it <%s><%p><fd:%d>.",
862                   octstr_get_cstr(key), conn, conn_get_id(conn));
863             conn_unregister(conn);
864             conn_destroy(conn);
865         }
866         /*
867          * it's perfectly valid if connection was not found in connection pool because
868          * in 'conn_pool_get' we first removed connection from pool with conn_pool_lock locked
869          * and then check connection for errors with conn_pool_lock unlocked. In the meantime
870          * fdset's poller may call us. So just ignore such "dummy" call.
871         */
872         mutex_unlock(conn_pool_lock);
873     }
874 }
875 
876 
conn_pool_put(Connection * conn,Octstr * host,int port,int ssl,Octstr * certfile,Octstr * our_host)877 static void conn_pool_put(Connection *conn, Octstr *host, int port, int ssl, Octstr *certfile, Octstr *our_host)
878 {
879     Octstr *key;
880     List *list;
881 
882     key = conn_pool_key(host, port, ssl, certfile, our_host);
883     mutex_lock(conn_pool_lock);
884     list = dict_get(conn_pool, key);
885     if (list == NULL) {
886     	list = gwlist_create();
887         dict_put(conn_pool, key, list);
888     }
889     gwlist_append(list, conn);
890     /* register connection to get server disconnect */
891     conn_register_real(conn, client_fdset, check_pool_conn, key, octstr_destroy_item);
892     mutex_unlock(conn_pool_lock);
893 }
894 #endif
895 
896 
http_caller_create(void)897 HTTPCaller *http_caller_create(void)
898 {
899     HTTPCaller *caller;
900 
901     caller = gwlist_create();
902     gwlist_add_producer(caller);
903     return caller;
904 }
905 
906 
http_caller_destroy(HTTPCaller * caller)907 void http_caller_destroy(HTTPCaller *caller)
908 {
909     gwlist_destroy(caller, server_destroy);
910 }
911 
912 
http_caller_signal_shutdown(HTTPCaller * caller)913 void http_caller_signal_shutdown(HTTPCaller *caller)
914 {
915     gwlist_remove_producer(caller);
916 }
917 
918 
get_redirection_location(HTTPServer * trans)919 static Octstr *get_redirection_location(HTTPServer *trans)
920 {
921     if (trans->status < 0 || trans->follow_remaining <= 0)
922     	return NULL;
923     /* check for the redirection response codes */
924     if (trans->status != HTTP_MOVED_PERMANENTLY &&
925     	trans->status != HTTP_FOUND && trans->status != HTTP_SEE_OTHER &&
926         trans->status != HTTP_TEMPORARY_REDIRECT)
927 	return NULL;
928     if (trans->response == NULL)
929         return NULL;
930     return http_header_find_first(trans->response->headers, "Location");
931 }
932 
933 
934 /*
935  * Recovers a Location header value of format URI /xyz to an
936  * absoluteURI format according to the protocol rules.
937  * This simply implies that we re-create the prefixed scheme,
938  * user/passwd (if any), host and port string and prepend it
939  * to the location URI.
940  */
recover_absolute_uri(HTTPServer * trans,Octstr * loc)941 static void recover_absolute_uri(HTTPServer *trans, Octstr *loc)
942 {
943     Octstr *os;
944 
945     gw_assert(loc != NULL && trans != NULL);
946 
947     /* we'll only accept locations with a leading / */
948     if (octstr_get_char(loc, 0) == '/') {
949 
950         /* scheme */
951         os = trans->ssl ? octstr_create("https://") :
952             octstr_create("http://");
953 
954         /* credentials, if any */
955         if (trans->username && trans->password) {
956             octstr_append(os, trans->username);
957             octstr_append_char(os, ':');
958             octstr_append(os, trans->password);
959             octstr_append_char(os, '@');
960         }
961 
962         /* host */
963         octstr_append(os, trans->host);
964 
965         /* port, only added if literally not default. */
966         if (trans->port != 80 || trans->ssl) {
967             octstr_format_append(os, ":%ld", trans->port);
968         }
969 
970         /* prepend the created octstr to the loc, and destroy then. */
971         octstr_insert(loc, os, 0);
972         octstr_destroy(os);
973     }
974 }
975 
976 
977 /*
978  * Read and parse the status response line from an HTTP server.
979  * Fill in trans->persistent and trans->status with the findings.
980  * Return -1 for error, 1 for status line not yet available, 0 for OK.
981  */
client_read_status(HTTPServer * trans)982 static int client_read_status(HTTPServer *trans)
983 {
984     Octstr *line, *version;
985     long space;
986     int ret;
987 
988     line = conn_read_line(trans->conn);
989     if (line == NULL) {
990 	if (conn_eof(trans->conn) || conn_error(trans->conn))
991 	    return -1;
992     	return 1;
993     }
994 
995     debug("gwlib.http", 0, "HTTP: Status line: <%s>", octstr_get_cstr(line));
996 
997     space = octstr_search_char(line, ' ', 0);
998     if (space == -1)
999     	goto error;
1000 
1001     version = octstr_copy(line, 0, space);
1002     ret = parse_http_version(version);
1003     octstr_destroy(version);
1004     if (ret == -1)
1005     	goto error;
1006     trans->persistent = ret;
1007 
1008     octstr_delete(line, 0, space + 1);
1009     space = octstr_search_char(line, ' ', 0);
1010     if (space == -1)
1011     	goto error;
1012     octstr_truncate(line, space);
1013 
1014     if (octstr_parse_long(&trans->status, line, 0, 10) == -1)
1015         goto error;
1016 
1017     octstr_destroy(line);
1018     return 0;
1019 
1020 error:
1021     error(0, "HTTP: Malformed status line from HTTP server: <%s>",
1022 	  octstr_get_cstr(line));
1023     octstr_destroy(line);
1024     return -1;
1025 }
1026 
response_expectation(int method,int status)1027 static int response_expectation(int method, int status)
1028 {
1029     if (status == HTTP_NO_CONTENT ||
1030         status == HTTP_NOT_MODIFIED ||
1031         http_status_class(status) == HTTP_STATUS_PROVISIONAL ||
1032         method == HTTP_METHOD_HEAD)
1033 	return expect_no_body;
1034     else
1035         return expect_body;
1036 }
1037 
handle_transaction(Connection * conn,void * data)1038 static void handle_transaction(Connection *conn, void *data)
1039 {
1040     HTTPServer *trans;
1041     int ret;
1042     Octstr *h;
1043     int rc;
1044 
1045     trans = data;
1046 
1047     if (run_status != running) {
1048         conn_unregister(conn);
1049         return;
1050     }
1051 
1052     while (trans->state != transaction_done) {
1053         switch (trans->state) {
1054         case connecting:
1055             debug("gwlib.http", 0, "Get info about connecting socket");
1056             if (conn_get_connect_result(trans->conn) != 0) {
1057                 debug("gwlib.http", 0, "Socket not connected");
1058                 goto error;
1059             }
1060 
1061             if ((rc = send_request(trans)) == 0) {
1062                 trans->state = reading_status;
1063             } else {
1064                 debug("gwlib.http", 0, "Failed while sending request");
1065                 goto error;
1066             }
1067             break;
1068 
1069         case reading_status:
1070             ret = client_read_status(trans);
1071             if (ret < 0) {
1072                 /*
1073                  * Couldn't read the status from the socket. This may mean
1074                  * that the socket had been closed by the server after an
1075                  * idle timeout.
1076                  */
1077                 debug("gwlib.http",0,"Failed while reading status");
1078                 goto error;
1079             } else if (ret == 0) {
1080                 /* Got the status, go read headers and body next. */
1081                 trans->state = reading_entity;
1082                 trans->response = entity_create(response_expectation(trans->method, trans->status));
1083             } else {
1084                 return;
1085             }
1086             break;
1087 
1088         case reading_entity:
1089             ret = entity_read(trans->response, conn);
1090             if (ret < 0) {
1091                 debug("gwlib.http",0,"Failed reading entity");
1092                 goto error;
1093             } else if (ret == 0 &&
1094                        http_status_class(trans->status) == HTTP_STATUS_PROVISIONAL) {
1095                 /* This was a provisional reply; get the real one now. */
1096                 trans->state = reading_status;
1097                 entity_destroy(trans->response);
1098                 trans->response = NULL;
1099             } else if (ret == 0) {
1100                 trans->state = transaction_done;
1101 #ifdef DUMP_RESPONSE
1102                 /* Dump the response */
1103                 debug("gwlib.http", 0, "HTTP: Received response:");
1104                 h = build_response(trans->response->headers, trans->response->body);
1105                 octstr_dump(h, 0);
1106                 octstr_destroy(h);
1107 #endif
1108             } else {
1109                 return;
1110             }
1111             break;
1112 
1113         default:
1114             panic(0, "Internal error: Invalid HTTPServer state.");
1115         }
1116     }
1117 
1118     conn_unregister(trans->conn);
1119 
1120     /*
1121      * Take care of persistent connection handling.
1122      * At this point we have only obeyed if server responds in HTTP/1.0 or 1.1
1123      * and have assigned trans->persistent accordingly. This can be keept
1124      * for default usage, but if we have [Proxy-]Connection: keep-alive, then
1125      * we're still forcing persistancy of the connection.
1126      */
1127     h = http_header_find_first(trans->response->headers, "Connection");
1128     if (h != NULL && octstr_case_compare(h, octstr_imm("close")) == 0)
1129         trans->persistent = 0;
1130     if (h != NULL && octstr_case_compare(h, octstr_imm("keep-alive")) == 0)
1131         trans->persistent = 1;
1132     octstr_destroy(h);
1133     if (proxy_used_for_host(trans->host, trans->url)) {
1134         h = http_header_find_first(trans->response->headers, "Proxy-Connection");
1135         if (h != NULL && octstr_case_compare(h, octstr_imm("close")) == 0)
1136             trans->persistent = 0;
1137         if (h != NULL && octstr_case_compare(h, octstr_imm("keep-alive")) == 0)
1138             trans->persistent = 1;
1139         octstr_destroy(h);
1140     }
1141 
1142 #ifdef USE_KEEPALIVE
1143     if (trans->persistent) {
1144         if (proxy_used_for_host(trans->host, trans->url))
1145             conn_pool_put(trans->conn, proxy_hostname, proxy_port, trans->ssl, trans->certkeyfile, http_interface);
1146         else
1147             conn_pool_put(trans->conn, trans->host, trans->port, trans->ssl, trans->certkeyfile, http_interface);
1148     } else
1149 #endif
1150         conn_destroy(trans->conn);
1151 
1152     trans->conn = NULL;
1153 
1154     /*
1155      * Check if the HTTP server told us to look somewhere else,
1156      * hence if we got one of the following response codes:
1157      *   HTTP_MOVED_PERMANENTLY (301)
1158      *   HTTP_FOUND (302)
1159      *   HTTP_SEE_OTHER (303)
1160      *   HTTP_TEMPORARY_REDIRECT (307)
1161      */
1162     if ((h = get_redirection_location(trans)) != NULL) {
1163 
1164         /*
1165          * This is a redirected response, we have to follow.
1166          *
1167          * According to HTTP/1.1 (RFC 2616), section 14.30 any Location
1168          * header value should be 'absoluteURI', which is defined in
1169          * RFC 2616, section 3.2.1 General Syntax, and specifically in
1170          * RFC 2396, section 3 URI Syntactic Components as
1171          *
1172          *   absoluteURI   = scheme ":" ( hier_part | opaque_part )
1173          *
1174          * Some HTTP servers 'interpret' a leading UDI / as that kind
1175          * of absoluteURI, which is not correct, following the protocol in
1176          * detail. But we'll try to recover from that misleaded
1177          * interpreation and try to convert the partly absoluteURI to a
1178          * fully qualified absoluteURI.
1179          *
1180          *   http_URL = "http:" "//" [ userid : password "@"] host
1181          *      [ ":" port ] [ abs_path [ "?" query ]]
1182          *
1183          */
1184         octstr_strip_blanks(h);
1185         recover_absolute_uri(trans, h);
1186 
1187         /*
1188          * Clean up all trans stuff for the next request we do.
1189          */
1190         octstr_destroy(trans->url);
1191         octstr_destroy(trans->host);
1192         trans->port = 0;
1193         octstr_destroy(trans->uri);
1194         octstr_destroy(trans->username);
1195         octstr_destroy(trans->password);
1196         trans->host = NULL;
1197         trans->port = 0;
1198         trans->uri = NULL;
1199         trans->username = NULL;
1200         trans->password = NULL;
1201         trans->ssl = 0;
1202         trans->url = h; /* apply new absolute URL to next request */
1203         trans->state = request_not_sent;
1204         trans->status = -1;
1205         entity_destroy(trans->response);
1206         trans->response = NULL;
1207         --trans->follow_remaining;
1208         conn_destroy(trans->conn);
1209         trans->conn = NULL;
1210 
1211         /* re-inject request to the front of the queue */
1212         gwlist_insert(pending_requests, 0, trans);
1213 
1214     } else {
1215         /* handle this response as usual */
1216         gwlist_produce(trans->caller, trans);
1217     }
1218     return;
1219 
1220 error:
1221     conn_unregister(trans->conn);
1222     conn_destroy(trans->conn);
1223     trans->conn = NULL;
1224     error(0, "Couldn't fetch <%s>", octstr_get_cstr(trans->url));
1225     trans->status = -1;
1226     gwlist_produce(trans->caller, trans);
1227 }
1228 
1229 
1230 /*
1231  * Build a complete HTTP request given the host, port, path and headers.
1232  * Add Host: and Content-Length: headers (and others that may be necessary).
1233  * Return the request as an Octstr.
1234  */
build_request(char * method_name,Octstr * path_or_url,Octstr * host,long port,int ssl,List * headers,Octstr * request_body)1235 static Octstr *build_request(char *method_name, Octstr *path_or_url,
1236                              Octstr *host, long port, int ssl, List *headers,
1237                              Octstr *request_body)
1238 {
1239     /* XXX headers missing */
1240     Octstr *request;
1241     int i, host_found = 0;
1242 
1243     request = octstr_format("%s %S HTTP/1.1\r\n",
1244                             method_name, path_or_url);
1245 
1246 #ifdef USE_KEEPALIVE
1247     octstr_append(request, octstr_imm("Connection: keep-alive\r\n"));
1248 #endif
1249 
1250     for (i = 0; headers != NULL && i < gwlist_len(headers); ++i) {
1251         /* check if Host already set in the headers */
1252         if (header_is_called(gwlist_get(headers, i), "Host"))
1253             host_found = 1;
1254         octstr_append(request, gwlist_get(headers, i));
1255         octstr_append(request, octstr_imm("\r\n"));
1256     }
1257 
1258     if (!host_found) {
1259         octstr_format_append(request, "Host: %S", host);
1260         /*
1261          * In accordance with HTT/1.1 [RFC 2616], section 14.23 "Host"
1262          * we shall ONLY add the port number if it is not one of the
1263          * officially assigned port numbers. This means we need to obey
1264          * port 80 for non-SSL connections and port 443 for SSL-enabled.
1265          */
1266         if ((port != HTTP_PORT && !ssl) || (port != HTTPS_PORT && ssl))
1267             octstr_format_append(request, ":%ld", port);
1268         octstr_append(request, octstr_imm("\r\n"));
1269     }
1270 
1271     octstr_append(request, octstr_imm("\r\n"));
1272 
1273     if (request_body != NULL)
1274         octstr_append(request, request_body);
1275 
1276     return request;
1277 }
1278 
1279 
1280 /*
1281  * Re-build the HTTP response given the headers and the body.
1282  * Return the response as an Octstr.
1283  */
build_response(List * headers,Octstr * body)1284 static Octstr *build_response(List *headers, Octstr *body)
1285 {
1286     Octstr *response;
1287     int i;
1288 
1289     response = octstr_create("");
1290 
1291     for (i = 0; headers != NULL && i < gwlist_len(headers); ++i) {
1292         octstr_append(response, gwlist_get(headers, i));
1293         octstr_append(response, octstr_imm("\r\n"));
1294     }
1295     octstr_append(response, octstr_imm("\r\n"));
1296 
1297     if (body != NULL)
1298         octstr_append(response, body);
1299 
1300     return response;
1301 }
1302 
1303 
http_urlparse_create(void)1304 HTTPURLParse *http_urlparse_create(void)
1305 {
1306     HTTPURLParse *p;
1307 
1308     p = gw_malloc(sizeof(HTTPURLParse));
1309     p->url = NULL;
1310     p->scheme = NULL;
1311     p->host = NULL;
1312     p->port = 0;
1313     p->user = NULL;
1314     p->pass = NULL;
1315     p->path = NULL;
1316     p->query = NULL;
1317     p->fragment = NULL;
1318 
1319     return p;
1320 }
1321 
1322 
http_urlparse_destroy(HTTPURLParse * p)1323 void http_urlparse_destroy(HTTPURLParse *p)
1324 {
1325     gw_assert(p != NULL);
1326 
1327     octstr_destroy(p->url);
1328     octstr_destroy(p->scheme);
1329     octstr_destroy(p->host);
1330     octstr_destroy(p->user);
1331     octstr_destroy(p->pass);
1332     octstr_destroy(p->path);
1333     octstr_destroy(p->query);
1334     octstr_destroy(p->fragment);
1335     gw_free(p);
1336 }
1337 
1338 
parse_dump(HTTPURLParse * p)1339 void parse_dump(HTTPURLParse *p)
1340 {
1341     if (p == NULL)
1342         return;
1343     debug("http.parse_url",0,"Parsing URL `%s':", octstr_get_cstr(p->url));
1344     debug("http.parse_url",0,"  Scheme: %s", octstr_get_cstr(p->scheme));
1345     debug("http.parse_url",0,"  Host: %s", octstr_get_cstr(p->host));
1346     debug("http.parse_url",0,"  Port: %ld", p->port);
1347     debug("http.parse_url",0,"  Username: %s", octstr_get_cstr(p->user));
1348     debug("http.parse_url",0,"  Password: %s", octstr_get_cstr(p->pass));
1349     debug("http.parse_url",0,"  Path: %s", octstr_get_cstr(p->path));
1350     debug("http.parse_url",0,"  Query: %s", octstr_get_cstr(p->query));
1351     debug("http.parse_url",0,"  Fragment: %s", octstr_get_cstr(p->fragment));
1352 }
1353 
1354 
1355 /*
1356  * Parse the URL to get all components, which are: scheme, hostname,
1357  * port, username, password, path (URI), query (the CGI parameter list),
1358  * fragment (#).
1359  *
1360  * On success return the HTTPURLParse structure, otherwise NULL if the URL
1361  * seems malformed.
1362  *
1363  * We assume HTTP URLs of the form specified in "3.2.2 http URL" in
1364  * RFC 2616:
1365  *
1366  *  http_URL = "http:" "//" [ userid : password "@"] host [ ":" port ] [ abs_path [ "?" query ]]
1367  */
parse_url(Octstr * url)1368 HTTPURLParse *parse_url(Octstr *url)
1369 {
1370     HTTPURLParse *p;
1371     Octstr *prefix, *prefix_https;
1372     long prefix_len;
1373     int host_len, colon, slash, at, auth_sep, query;
1374     host_len = colon = slash = at = auth_sep = query = 0;
1375 
1376     prefix = octstr_imm("http://");
1377     prefix_https = octstr_imm("https://");
1378     prefix_len = octstr_len(prefix);
1379 
1380     if (octstr_case_search(url, prefix, 0) != 0) {
1381         if (octstr_case_search(url, prefix_https, 0) == 0) {
1382 #ifdef HAVE_LIBSSL
1383             debug("gwlib.http", 0, "HTTPS URL; Using SSL for the connection");
1384             prefix = prefix_https;
1385             prefix_len = octstr_len(prefix_https);
1386 #else
1387             error(0, "Attempt to use HTTPS <%s> but SSL not compiled in",
1388                   octstr_get_cstr(url));
1389             return NULL;
1390 #endif
1391         } else {
1392             error(0, "URL <%s> doesn't start with `%s' nor `%s'",
1393             octstr_get_cstr(url), octstr_get_cstr(prefix),
1394             octstr_get_cstr(prefix_https));
1395             return NULL;
1396         }
1397     }
1398 
1399     /* an URL should be more (at least one charset) then the scheme itself */
1400     if (octstr_len(url) == prefix_len) {
1401         error(0, "URL <%s> is malformed.", octstr_get_cstr(url));
1402         return NULL;
1403     }
1404 
1405     /* check if colon and slashes are within scheme */
1406     colon = octstr_search_char(url, ':', prefix_len);
1407     slash = octstr_search_char(url, '/', prefix_len);
1408     if (colon == prefix_len || slash == prefix_len) {
1409         error(0, "URL <%s> is malformed.", octstr_get_cstr(url));
1410         return NULL;
1411     }
1412 
1413     /* create struct and add values succesively while parsing */
1414     p = http_urlparse_create();
1415     p->url = octstr_duplicate(url);
1416     p->scheme = octstr_duplicate(prefix);
1417 
1418     /* try to parse authentication separator */
1419     at = octstr_search_char(url, '@', prefix_len);
1420     if (at != -1) {
1421         if ((slash == -1 || ( slash != -1 && at < slash))) {
1422             auth_sep = octstr_search_char(url, ':', prefix_len);
1423             if (auth_sep != -1 && (auth_sep < at)) {
1424                 octstr_set_char(url, auth_sep, '@');
1425                 colon = octstr_search_char(url, ':', prefix_len);
1426             }
1427         } else {
1428             at = -1;
1429         }
1430     }
1431 
1432     /*
1433      * We have to watch out here for 4 cases:
1434      *  a) hostname, no port or path
1435      *  b) hostname, port, no path
1436      *  c) hostname, path, no port
1437      *  d) hostname, port and path
1438      */
1439 
1440     /* we only have the hostname, no port or path. */
1441     if (slash == -1 && colon == -1) {
1442         host_len = octstr_len(url) - prefix_len;
1443 #ifdef HAVE_LIBSSL
1444         p->port = (octstr_compare(p->scheme, octstr_imm("https://")) == 0) ?
1445             HTTPS_PORT : HTTP_PORT;
1446 #else
1447         p->port = HTTP_PORT;
1448 #endif /* HAVE_LIBSSL */
1449     }
1450     /* we have a port, but no path. */
1451     else if (slash == -1) {
1452         host_len = colon - prefix_len;
1453         if (octstr_parse_long((long*) &(p->port), url, colon + 1, 10) == -1) {
1454             error(0, "URL <%s> has malformed port number.",
1455                   octstr_get_cstr(url));
1456             http_urlparse_destroy(p);
1457             return NULL;
1458         }
1459     }
1460     /* we have a path, but no port. */
1461     else if (colon == -1 || colon > slash) {
1462         host_len = slash - prefix_len;
1463 #ifdef HAVE_LIBSSL
1464         p->port = (octstr_compare(p->scheme, octstr_imm("https://")) == 0) ?
1465             HTTPS_PORT : HTTP_PORT;
1466 #else
1467         p->port = HTTP_PORT;
1468 #endif /* HAVE_LIBSSL */
1469     }
1470     /* we have both, path and port. */
1471     else if (colon < slash) {
1472         host_len = colon - prefix_len;
1473         if (octstr_parse_long((long*) &(p->port), url, colon + 1, 10) == -1) {
1474             error(0, "URL <%s> has malformed port number.",
1475                   octstr_get_cstr(url));
1476             http_urlparse_destroy(p);
1477             return NULL;
1478         }
1479     /* none of the above, so there is something wrong here */
1480     } else {
1481         error(0, "Internal error in URL parsing logic.");
1482         http_urlparse_destroy(p);
1483         return NULL;
1484     }
1485 
1486     /* there was an authenticator separator, so try to parse
1487      * the username and password credentials */
1488     if (at != -1) {
1489         int at2;
1490 
1491         at2 = octstr_search_char(url, '@', prefix_len);
1492         p->user = octstr_copy(url, prefix_len, at2 - prefix_len);
1493         p->pass = (at2 != at) ? octstr_copy(url, at2 + 1, at - at2 - 1) : NULL;
1494 
1495         if (auth_sep != -1)
1496             octstr_set_char(url, auth_sep, ':');
1497 
1498         host_len = host_len - at + prefix_len - 1;
1499         prefix_len = at + 1;
1500     }
1501 
1502     /* query (CGI vars) */
1503     query = octstr_search_char(url, '?', (slash == -1) ? prefix_len : slash);
1504     if (query != -1) {
1505         p->query = octstr_copy(url, query + 1, octstr_len(url));
1506         if (colon == -1)
1507             host_len = slash != -1 ? slash - prefix_len : query - prefix_len;
1508     }
1509 
1510     /* path */
1511     p->path = (slash == -1) ?
1512         octstr_create("/") : ((query != -1) && (query > slash) ?
1513             octstr_copy(url, slash, query - slash) :
1514             octstr_copy(url, slash, octstr_len(url) - slash));
1515 
1516     /* hostname */
1517     p->host = octstr_copy(url, prefix_len, host_len);
1518 
1519     /* XXX add fragment too */
1520 
1521     /* dump components */
1522     parse_dump(p);
1523 
1524     return p;
1525 }
1526 
1527 /* copy all relevant parsed data to the server info struct */
parse2trans(HTTPURLParse * p,HTTPServer * t)1528 static void parse2trans(HTTPURLParse *p, HTTPServer *t)
1529 {
1530     if (p == NULL || t == NULL)
1531         return;
1532 
1533     if (p->user && !t->username)
1534         t->username = octstr_duplicate(p->user);
1535     if (p->pass && !t->password)
1536         t->password = octstr_duplicate(p->pass);
1537     if (p->host && !t->host)
1538         t->host = octstr_duplicate(p->host);
1539     if (p->port && !t->port)
1540         t->port = p->port;
1541     if (p->path && !t->uri) {
1542         t->uri = octstr_duplicate(p->path);
1543         if (p->query) { /* add the query too */
1544             octstr_append_char(t->uri, '?');
1545             octstr_append(t->uri, p->query);
1546         }
1547     }
1548     t->ssl = (p->scheme && (octstr_compare(p->scheme, octstr_imm("https://")) == 0)
1549               && !t->ssl) ? 1 : 0;
1550 }
1551 
get_connection(HTTPServer * trans)1552 static Connection *get_connection(HTTPServer *trans)
1553 {
1554     Connection *conn = NULL;
1555     Octstr *host;
1556     HTTPURLParse *p;
1557     int port, ssl;
1558 
1559     /* if the parsing has not yet been done, then do it now */
1560     if (!trans->host && trans->port == 0 && trans->url != NULL) {
1561         if ((p = parse_url(trans->url)) != NULL) {
1562             parse2trans(p, trans);
1563             http_urlparse_destroy(p);
1564         } else {
1565             goto error;
1566         }
1567     }
1568 
1569     if (proxy_used_for_host(trans->host, trans->url)) {
1570         host = proxy_hostname;
1571         port = proxy_port;
1572         ssl = proxy_ssl;
1573     } else {
1574         host = trans->host;
1575         port = trans->port;
1576         ssl = trans->ssl;
1577     }
1578 
1579     conn = conn_pool_get(host, port, ssl, trans->certkeyfile,
1580                          http_interface);
1581     if (conn == NULL)
1582         goto error;
1583 
1584     return conn;
1585 
1586 error:
1587     conn_destroy(conn);
1588     error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));
1589     return NULL;
1590 }
1591 
1592 
1593 /*
1594  * Build and send the HTTP request. Return 0 for success or -1 for error.
1595  */
send_request(HTTPServer * trans)1596 static int send_request(HTTPServer *trans)
1597 {
1598     char buf[128];
1599     Octstr *request = NULL;
1600 
1601     if (trans->method == HTTP_METHOD_POST) {
1602         /*
1603          * Add a Content-Length header.  Override an existing one, if
1604          * necessary.  We must have an accurate one in order to use the
1605          * connection for more than a single request.
1606          */
1607         http_header_remove_all(trans->request_headers, "Content-Length");
1608         snprintf(buf, sizeof(buf), "%ld", octstr_len(trans->request_body));
1609         http_header_add(trans->request_headers, "Content-Length", buf);
1610     }
1611     /*
1612      * ok, this has to be an GET or HEAD request method then,
1613      * if it contains a body, then this is not HTTP conform, so at
1614      * least warn the user
1615      */
1616     else if (trans->request_body != NULL) {
1617         warning(0, "HTTP: GET or HEAD method request contains body:");
1618         octstr_dump(trans->request_body, 0);
1619     }
1620 
1621     /*
1622      * we have to assume all values in trans are already set
1623      * by parse_url() before calling this.
1624      */
1625 
1626     if (trans->username != NULL)
1627         http_add_basic_auth(trans->request_headers, trans->username,
1628                             trans->password);
1629 
1630     if (proxy_used_for_host(trans->host, trans->url)) {
1631         proxy_add_authentication(trans->request_headers);
1632         request = build_request(http_method2name(trans->method), trans->url,
1633                                 trans->host, trans->port, trans->ssl,
1634                                 trans->request_headers,
1635                                 trans->request_body);
1636     } else {
1637         request = build_request(http_method2name(trans->method), trans->uri,
1638                                 trans->host, trans->port, trans->ssl,
1639                                 trans->request_headers,
1640                                 trans->request_body);
1641     }
1642 
1643     debug("gwlib.http", 0, "HTTP: Sending request:");
1644     octstr_dump(request, 0);
1645     if (conn_write(trans->conn, request) == -1)
1646         goto error;
1647 
1648     octstr_destroy(request);
1649 
1650     return 0;
1651 
1652 error:
1653     conn_destroy(trans->conn);
1654     trans->conn = NULL;
1655     octstr_destroy(request);
1656     error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));
1657     return -1;
1658 }
1659 
1660 
1661 /*
1662  * This thread starts the transaction: it connects to the server and sends
1663  * the request. It then sends the transaction to the read_response_thread
1664  * via started_requests_queue.
1665  */
write_request_thread(void * arg)1666 static void write_request_thread(void *arg)
1667 {
1668     HTTPServer *trans;
1669     int rc;
1670 
1671     while (run_status == running) {
1672         trans = gwlist_consume(pending_requests);
1673         if (trans == NULL)
1674             break;
1675 
1676         gw_assert(trans->state == request_not_sent);
1677 
1678         debug("gwlib.http", 0, "Queue contains %ld pending requests.", gwlist_len(pending_requests));
1679 
1680         /*
1681          * get the connection to use
1682          * also calls parse_url() to populate the trans values
1683          */
1684         trans->conn = get_connection(trans);
1685 
1686         if (trans->conn == NULL)
1687             gwlist_produce(trans->caller, trans);
1688         else if (conn_is_connected(trans->conn) == 0) {
1689             debug("gwlib.http", 0, "Socket connected at once");
1690 
1691             if ((rc = send_request(trans)) == 0) {
1692                 trans->state = reading_status;
1693                 conn_register(trans->conn, client_fdset, handle_transaction,
1694                                 trans);
1695             } else {
1696                 gwlist_produce(trans->caller, trans);
1697             }
1698 
1699         } else { /* Socket not connected, wait for connection */
1700             debug("gwlib.http", 0, "Socket connecting");
1701             trans->state = connecting;
1702             conn_register(trans->conn, client_fdset, handle_transaction, trans);
1703         }
1704     }
1705 }
1706 
1707 
start_client_threads(void)1708 static void start_client_threads(void)
1709 {
1710     if (!client_threads_are_running) {
1711 	/*
1712 	 * To be really certain, we must repeat the test, but use the
1713 	 * lock first. If the test failed, however, we _know_ we've
1714 	 * already initialized. This strategy of double testing avoids
1715 	 * using the lock more than a few times at startup.
1716 	 */
1717 	mutex_lock(client_thread_lock);
1718 	if (!client_threads_are_running) {
1719 	    client_fdset = fdset_create_real(http_client_timeout);
1720 	    if (gwthread_create(write_request_thread, NULL) == -1) {
1721                 error(0, "HTTP: Could not start client write_request thread.");
1722                 fdset_destroy(client_fdset);
1723                 client_threads_are_running = 0;
1724             } else
1725                 client_threads_are_running = 1;
1726 	}
1727 	mutex_unlock(client_thread_lock);
1728     }
1729 }
1730 
http_set_interface(const Octstr * our_host)1731 void http_set_interface(const Octstr *our_host)
1732 {
1733     http_interface = octstr_duplicate(our_host);
1734 }
1735 
http_set_client_timeout(long timeout)1736 void http_set_client_timeout(long timeout)
1737 {
1738     http_client_timeout = timeout;
1739     if (client_fdset != NULL) {
1740         /* we are already initialized set timeout in fdset */
1741         fdset_set_timeout(client_fdset, http_client_timeout);
1742     }
1743 }
1744 
http_start_request(HTTPCaller * caller,int method,Octstr * url,List * headers,Octstr * body,int follow,void * id,Octstr * certkeyfile)1745 void http_start_request(HTTPCaller *caller, int method, Octstr *url, List *headers,
1746     	    	    	Octstr *body, int follow, void *id, Octstr *certkeyfile)
1747 {
1748     HTTPServer *trans;
1749     int follow_remaining;
1750 
1751     if (follow)
1752     	follow_remaining = HTTP_MAX_FOLLOW;
1753     else
1754     	follow_remaining = 0;
1755 
1756     trans = server_create(caller, method, url, headers, body, follow_remaining,
1757 			  certkeyfile);
1758 
1759     if (id == NULL)
1760         /* We don't leave this NULL so http_receive_result can use NULL
1761          * to signal no more requests */
1762         trans->request_id = http_start_request;
1763     else
1764         trans->request_id = id;
1765 
1766     gwlist_produce(pending_requests, trans);
1767     start_client_threads();
1768 }
1769 
1770 
http_receive_result_real(HTTPCaller * caller,int * status,Octstr ** final_url,List ** headers,Octstr ** body,int blocking)1771 void *http_receive_result_real(HTTPCaller *caller, int *status, Octstr **final_url,
1772     	    	    	 List **headers, Octstr **body, int blocking)
1773 {
1774     HTTPServer *trans;
1775     void *request_id;
1776 
1777     if (blocking == 0)
1778         trans = gwlist_extract_first(caller);
1779     else
1780         trans = gwlist_consume(caller);
1781     if (trans == NULL)
1782     	return NULL;
1783 
1784     request_id = trans->request_id;
1785     *status = trans->status;
1786 
1787     if (trans->status >= 0) {
1788         *final_url = trans->url;
1789         *headers = trans->response->headers;
1790         *body = trans->response->body;
1791 
1792         trans->url = NULL;
1793         trans->response->headers = NULL;
1794         trans->response->body = NULL;
1795     } else {
1796        *final_url = NULL;
1797        *headers = NULL;
1798        *body = NULL;
1799     }
1800 
1801     server_destroy(trans);
1802     return request_id;
1803 }
1804 
1805 
http_get_real(int method,Octstr * url,List * request_headers,Octstr ** final_url,List ** reply_headers,Octstr ** reply_body)1806 int http_get_real(int method, Octstr *url, List *request_headers, Octstr **final_url,
1807                   List **reply_headers, Octstr **reply_body)
1808 {
1809     HTTPCaller *caller;
1810     int status;
1811     void *ret;
1812 
1813     caller = http_caller_create();
1814     http_start_request(caller, method, url, request_headers,
1815                        NULL, 1, http_get_real, NULL);
1816     ret = http_receive_result(caller, &status, final_url,
1817     	    	    	      reply_headers, reply_body);
1818     http_caller_destroy(caller);
1819     if (ret == NULL)
1820     	return -1;
1821     return status;
1822 }
1823 
1824 
client_init(void)1825 static void client_init(void)
1826 {
1827     pending_requests = gwlist_create();
1828     gwlist_add_producer(pending_requests);
1829     client_thread_lock = mutex_create();
1830 }
1831 
1832 
client_shutdown(void)1833 static void client_shutdown(void)
1834 {
1835     gwlist_remove_producer(pending_requests);
1836     gwthread_join_every(write_request_thread);
1837     client_threads_are_running = 0;
1838     gwlist_destroy(pending_requests, server_destroy);
1839     mutex_destroy(client_thread_lock);
1840     fdset_destroy(client_fdset);
1841     client_fdset = NULL;
1842     octstr_destroy(http_interface);
1843     http_interface = NULL;
1844 }
1845 
1846 
1847 /***********************************************************************
1848  * HTTP server interface.
1849  */
1850 
1851 
1852 /*
1853  * Information about a client that has connected to the server we implement.
1854  */
1855 struct HTTPClient {
1856     int port;
1857     Connection *conn;
1858     Octstr *ip;
1859     enum {
1860         reading_request_line,
1861         reading_request,
1862         request_is_being_handled,
1863         sending_reply
1864     } state;
1865     int method;  /* HTTP_METHOD_ value */
1866     Octstr *url;
1867     int use_version_1_0;
1868     int persistent_conn;
1869     unsigned long conn_time; /* store time for timeouting */
1870     HTTPEntity *request;
1871 };
1872 
1873 
1874 /*
1875  * Variables related to server side implementation.
1876  */
1877 static Mutex *server_thread_lock = NULL;
1878 static volatile sig_atomic_t server_thread_is_running = 0;
1879 static long server_thread_id = -1;
1880 static List *new_server_sockets = NULL;
1881 static List *closed_server_sockets = NULL;
1882 static int keep_servers_open = 0;
1883 /* List with all active HTTPClient's */
1884 static List *active_connections;
1885 
1886 
client_create(int port,Connection * conn,Octstr * ip)1887 static HTTPClient *client_create(int port, Connection *conn, Octstr *ip)
1888 {
1889     HTTPClient *p;
1890 
1891 #ifdef HAVE_LIBSSL
1892     if (conn_get_ssl(conn))
1893         debug("gwlib.http", 0, "HTTP: Creating SSL-enabled HTTPClient for `%s', using cipher '%s'.",
1894     	      octstr_get_cstr(ip), SSL_get_cipher_version(conn_get_ssl(conn)));
1895     else
1896 #endif
1897         debug("gwlib.http", 0, "HTTP: Creating HTTPClient for `%s'.", octstr_get_cstr(ip));
1898     p = gw_malloc(sizeof(*p));
1899     p->port = port;
1900     p->conn = conn;
1901     p->ip = ip;
1902     p->state = reading_request_line;
1903     p->url = NULL;
1904     p->use_version_1_0 = 0;
1905     p->persistent_conn = 1;
1906     p->conn_time = time(NULL);
1907     p->request = NULL;
1908     debug("gwlib.http", 0, "HTTP: Created HTTPClient area %p.", p);
1909 
1910     /* add this client to active_connections */
1911     gwlist_produce(active_connections, p);
1912 
1913     return p;
1914 }
1915 
1916 
client_destroy(void * client)1917 static void client_destroy(void *client)
1918 {
1919     HTTPClient *p;
1920     long a_len;
1921 
1922     if (client == NULL)
1923         return;
1924 
1925     p = client;
1926 
1927     /* drop this client from active_connections list */
1928     gwlist_lock(active_connections);
1929     if (gwlist_delete_equal(active_connections, p) != 1)
1930         panic(0, "HTTP: Race condition in client_destroy(%p) detected!", client);
1931 
1932     /* signal server thread that client slot is free */
1933     a_len = gwlist_len(active_connections);
1934     gwlist_unlock(active_connections);
1935 
1936     if (a_len >= HTTP_SERVER_MAX_ACTIVE_CONNECTIONS - 1)
1937         gwthread_wakeup(server_thread_id);
1938 
1939     debug("gwlib.http", 0, "HTTP: Destroying HTTPClient area %p.", p);
1940     gw_assert_allocated(p, __FILE__, __LINE__, __func__);
1941     debug("gwlib.http", 0, "HTTP: Destroying HTTPClient for `%s'.",
1942           octstr_get_cstr(p->ip));
1943 
1944     conn_destroy(p->conn);
1945     octstr_destroy(p->ip);
1946     octstr_destroy(p->url);
1947     entity_destroy(p->request);
1948     gw_free(p);
1949 }
1950 
1951 
client_reset(HTTPClient * p)1952 static void client_reset(HTTPClient *p)
1953 {
1954     debug("gwlib.http", 0, "HTTP: Resetting HTTPClient for `%s'.",
1955     	  octstr_get_cstr(p->ip));
1956     p->state = reading_request_line;
1957     p->conn_time = time(NULL);
1958     gw_assert(p->request == NULL);
1959 }
1960 
1961 
1962 /*
1963  * Checks whether the client connection is meant to be persistent or not.
1964  * Returns 1 for true, 0 for false.
1965  */
1966 
client_is_persistent(List * headers,int use_version_1_0)1967 static int client_is_persistent(List *headers, int use_version_1_0)
1968 {
1969     Octstr *h = http_header_find_first(headers, "Connection");
1970 
1971     if (h == NULL) {
1972         return !use_version_1_0;
1973     } else {
1974         List *values = octstr_split(h, octstr_imm(","));
1975         octstr_destroy(h);
1976         if (!use_version_1_0) {
1977             if (gwlist_search(values, octstr_imm("keep-alive"), octstr_item_case_match) != NULL) {
1978                 gwlist_destroy(values, octstr_destroy_item);
1979                 return 1;
1980             } else {
1981                 gwlist_destroy(values, octstr_destroy_item);
1982                 return 0;
1983             }
1984         } else if (gwlist_search(values, octstr_imm("close"), octstr_item_case_match) != NULL) {
1985             gwlist_destroy(values, octstr_destroy_item);
1986             return 0;
1987         }
1988         gwlist_destroy(values, octstr_destroy_item);
1989     }
1990 
1991     return 1;
1992 }
1993 
1994 
1995 /*
1996  * Port specific lists of clients with requests.
1997  */
1998 struct port {
1999     int fd;
2000     int port;
2001     int ssl;
2002     List *clients_with_requests;
2003     Counter *active_consumers;
2004     FDSet *server_fdset;
2005 };
2006 
2007 
2008 static Mutex *port_mutex = NULL;
2009 static Dict *port_collection = NULL;
2010 
2011 
port_match(void * client,void * port)2012 static int port_match(void *client, void *port)
2013 {
2014     return ((HTTPClient*)client)->port == *((int*)port);
2015 }
2016 
2017 
port_init(void)2018 static void port_init(void)
2019 {
2020     port_mutex = mutex_create();
2021     port_collection = dict_create(1024, NULL);
2022     /* create list with all active_connections */
2023     active_connections = gwlist_create();
2024 }
2025 
port_shutdown(void)2026 static void port_shutdown(void)
2027 {
2028     mutex_destroy(port_mutex);
2029     dict_destroy(port_collection);
2030     /* destroy active_connections list */
2031     gwlist_destroy(active_connections, client_destroy);
2032 }
2033 
2034 
port_key(int port)2035 static Octstr *port_key(int port)
2036 {
2037     return octstr_format("%d", port);
2038 }
2039 
2040 
port_add(int port)2041 static struct port *port_add(int port)
2042 {
2043     Octstr *key;
2044     struct port *p;
2045 
2046     key = port_key(port);
2047     mutex_lock(port_mutex);
2048     if ((p = dict_get(port_collection, key)) == NULL) {
2049         p = gw_malloc(sizeof(*p));
2050         p->clients_with_requests = gwlist_create();
2051         gwlist_add_producer(p->clients_with_requests);
2052         p->active_consumers = counter_create();
2053         p->server_fdset = fdset_create_real(HTTP_SERVER_TIMEOUT);
2054         dict_put(port_collection, key, p);
2055     } else {
2056         warning(0, "HTTP: port_add called for existing port (%d)", port);
2057     }
2058     mutex_unlock(port_mutex);
2059     octstr_destroy(key);
2060 
2061     return p;
2062 }
2063 
2064 
port_remove(int port)2065 static void port_remove(int port)
2066 {
2067     Octstr *key;
2068     struct port *p;
2069     List *l;
2070     HTTPClient *client;
2071 
2072     key = port_key(port);
2073     mutex_lock(port_mutex);
2074     p = dict_remove(port_collection, key);
2075     mutex_unlock(port_mutex);
2076     octstr_destroy(key);
2077 
2078     if (p == NULL) {
2079         error(0, "HTTP: Could not find port (%d) in port_collection.", port);
2080         return;
2081     }
2082 
2083     gwlist_remove_producer(p->clients_with_requests);
2084     while (counter_value(p->active_consumers) > 0)
2085        gwthread_sleep(0.1);    /* Reasonable use of busy waiting. */
2086 
2087     gwlist_destroy(p->clients_with_requests, client_destroy);
2088     counter_destroy(p->active_consumers);
2089 
2090     /*
2091      * In order to avoid race conditions with FDSet thread, we
2092      * destroy Clients for this port in two steps:
2093      * 1) unregister from fdset with gwlist_lock held, so client_destroy
2094      *    cannot destroy our client that we currently use
2095      * 2) without gwlist_lock held destroy every client, we can do this
2096      *    because we only one thread that can use this client struct
2097      */
2098     gwlist_lock(active_connections);
2099     l = gwlist_search_all(active_connections, &port, port_match);
2100     while(l != NULL && (client = gwlist_extract_first(l)) != NULL)
2101         conn_unregister(client->conn);
2102     gwlist_unlock(active_connections);
2103     gwlist_destroy(l, NULL);
2104     while((client = gwlist_search(active_connections, &port, port_match)) != NULL)
2105         client_destroy(client);
2106 
2107     /* now destroy fdset */
2108     fdset_destroy(p->server_fdset);
2109     gw_free(p);
2110 }
2111 
2112 
port_put_request(HTTPClient * client)2113 static void port_put_request(HTTPClient *client)
2114 {
2115     Octstr *key;
2116     struct port *p;
2117 
2118     mutex_lock(port_mutex);
2119     key = port_key(client->port);
2120     p = dict_get(port_collection, key);
2121     octstr_destroy(key);
2122     if (p == NULL) {
2123         /* client was too slow and we closed port already */
2124         mutex_unlock(port_mutex);
2125         client_destroy(client);
2126         return;
2127     }
2128     gwlist_produce(p->clients_with_requests, client);
2129     mutex_unlock(port_mutex);
2130 }
2131 
2132 
port_get_request(int port)2133 static HTTPClient *port_get_request(int port)
2134 {
2135     Octstr *key;
2136     struct port *p;
2137     HTTPClient *client;
2138 
2139     mutex_lock(port_mutex);
2140     key = port_key(port);
2141     p = dict_get(port_collection, key);
2142     octstr_destroy(key);
2143 
2144     if (p == NULL) {
2145        client = NULL;
2146        mutex_unlock(port_mutex);
2147     } else {
2148        counter_increase(p->active_consumers);
2149        mutex_unlock(port_mutex);   /* Placement of this unlock is tricky. */
2150        client = gwlist_consume(p->clients_with_requests);
2151        counter_decrease(p->active_consumers);
2152     }
2153     return client;
2154 }
2155 
2156 
port_set_timeout(int port,long timeout)2157 static void port_set_timeout(int port, long timeout)
2158 {
2159     Octstr *key;
2160     struct port *p;
2161 
2162     mutex_lock(port_mutex);
2163     key = port_key(port);
2164     p = dict_get(port_collection, key);
2165     octstr_destroy(key);
2166 
2167     if (p != NULL)
2168         fdset_set_timeout(p->server_fdset, timeout);
2169 
2170     mutex_unlock(port_mutex);
2171 }
2172 
2173 
port_get_fdset(int port)2174 static FDSet *port_get_fdset(int port)
2175 {
2176     Octstr *key;
2177     struct port *p;
2178     FDSet *ret = NULL;
2179 
2180     mutex_lock(port_mutex);
2181     key = port_key(port);
2182     p = dict_get(port_collection, key);
2183     octstr_destroy(key);
2184 
2185     if (p != NULL)
2186         ret = p->server_fdset;
2187 
2188     mutex_unlock(port_mutex);
2189 
2190     return ret;
2191 }
2192 
2193 
parse_request_line(int * method,Octstr ** url,int * use_version_1_0,Octstr * line)2194 static int parse_request_line(int *method, Octstr **url,
2195                               int *use_version_1_0, Octstr *line)
2196 {
2197     List *words;
2198     Octstr *version;
2199     Octstr *method_str;
2200     int ret;
2201 
2202     words = octstr_split_words(line);
2203     if (gwlist_len(words) != 3) {
2204         gwlist_destroy(words, octstr_destroy_item);
2205         return -1;
2206     }
2207 
2208     method_str = gwlist_get(words, 0);
2209     *url = gwlist_get(words, 1);
2210     version = gwlist_get(words, 2);
2211     gwlist_destroy(words, NULL);
2212 
2213     if (octstr_compare(method_str, octstr_imm("GET")) == 0)
2214         *method = HTTP_METHOD_GET;
2215     else if (octstr_compare(method_str, octstr_imm("POST")) == 0)
2216         *method = HTTP_METHOD_POST;
2217     else if (octstr_compare(method_str, octstr_imm("HEAD")) == 0)
2218         *method = HTTP_METHOD_HEAD;
2219     else
2220         goto error;
2221 
2222     ret = parse_http_version(version);
2223     if (ret < 0)
2224         goto error;
2225     *use_version_1_0 = !ret;
2226 
2227     octstr_destroy(method_str);
2228     octstr_destroy(version);
2229     return 0;
2230 
2231 error:
2232     octstr_destroy(method_str);
2233     octstr_destroy(*url);
2234     octstr_destroy(version);
2235     *url = NULL;
2236     return -1;
2237 }
2238 
2239 
receive_request(Connection * conn,void * data)2240 static void receive_request(Connection *conn, void *data)
2241 {
2242     HTTPClient *client;
2243     Octstr *line;
2244     int ret;
2245 
2246     if (run_status != running) {
2247         conn_unregister(conn);
2248         return;
2249     }
2250 
2251     client = data;
2252 
2253     for (;;) {
2254         switch (client->state) {
2255             case reading_request_line:
2256                 line = conn_read_line(conn);
2257                 if (line == NULL) {
2258                     if (conn_eof(conn) || conn_error(conn))
2259                         goto error;
2260                     return;
2261                 }
2262                 ret = parse_request_line(&client->method, &client->url,
2263                                          &client->use_version_1_0, line);
2264                 octstr_destroy(line);
2265                 /* client sent bad request? */
2266                 if (ret == -1) {
2267                     /*
2268                      * mark client as not persistent in order to destroy connection
2269                      * afterwards
2270                      */
2271                     client->persistent_conn = 0;
2272                     /* unregister connection, http_send_reply handle this */
2273                     conn_unregister(conn);
2274                     http_send_reply(client, HTTP_BAD_REQUEST, NULL, NULL);
2275                     return;
2276                 }
2277                 /*
2278                  * RFC2616 (4.3) says we should read a message body if there
2279                  * is one, even on GET requests.
2280                  */
2281                 client->request = entity_create(expect_body_if_indicated);
2282                 client->state = reading_request;
2283                 break;
2284 
2285             case reading_request:
2286                 ret = entity_read(client->request, conn);
2287                 if (ret < 0)
2288                     goto error;
2289                 if (ret == 0) {
2290                     client->state = request_is_being_handled;
2291                     conn_unregister(conn);
2292                     port_put_request(client);
2293                 }
2294                 return;
2295 
2296             case sending_reply:
2297                 /* Implicit conn_unregister() and _destroy */
2298                 if (conn_error(conn))
2299                     goto error;
2300                 if (conn_outbuf_len(conn) > 0)
2301                     return;
2302                 /* Reply has been sent completely */
2303                 if (!client->persistent_conn) {
2304                     /*
2305                      * in order to avoid race conditions while conn will be destroyed but
2306                      * conn is still in use, we call conn_unregister explicit here because
2307                      * conn_unregister call uses locks
2308                      */
2309                     conn_unregister(conn);
2310                     client_destroy(client);
2311                     return;
2312                 }
2313                 /* Start reading another request */
2314                 client_reset(client);
2315                 break;
2316 
2317             default:
2318                 panic(0, "Internal error: HTTPClient state is wrong.");
2319         }
2320     }
2321 
2322 error:
2323     /*
2324      * in order to avoid race conditions while conn will be destroyed but
2325      * conn is still in use, we call conn_unregister explicit here because
2326      * conn_unregister call uses locks
2327      */
2328     conn_unregister(conn);
2329     client_destroy(client);
2330 }
2331 
2332 
server_thread(void * dummy)2333 static void server_thread(void *dummy)
2334 {
2335     struct pollfd *tab = NULL;
2336     struct port **ports = NULL;
2337     int tab_size = 0, n, i, fd, ret, max_clients_reached;
2338     struct sockaddr_in addr;
2339     socklen_t addrlen;
2340     HTTPClient *client;
2341     Connection *conn;
2342     int *portno;
2343 
2344     n = max_clients_reached = 0;
2345     while (run_status == running && keep_servers_open) {
2346         while (n == 0 || gwlist_len(new_server_sockets) > 0) {
2347             struct port *p = gwlist_consume(new_server_sockets);
2348             if (p == NULL) {
2349                 debug("gwlib.http", 0, "HTTP: No new servers. Quitting.");
2350                 break;
2351             } else {
2352                 debug ("gwlib.http", 0, "HTTP: Including port %d, fd %d for polling in server thread", p->port, p->fd);
2353             }
2354             if (tab_size <= n) {
2355                 tab_size++;
2356                 tab = gw_realloc(tab, tab_size * sizeof(*tab));
2357                 ports = gw_realloc(ports, tab_size * sizeof(*ports));
2358                 if (tab == NULL || ports == NULL) {
2359                     tab_size--;
2360                     port_remove(p->port);
2361                     continue;
2362                 }
2363             }
2364             tab[n].fd = p->fd;
2365             tab[n].events = POLLIN;
2366             ports[n] = p;
2367             n++;
2368         }
2369 
2370         if (max_clients_reached && gwlist_len(active_connections) >= HTTP_SERVER_MAX_ACTIVE_CONNECTIONS) {
2371             /* TODO start cleanup of stale connections */
2372             /* wait for slots to become free */
2373             gwthread_sleep(1.0);
2374         } else if (!max_clients_reached && (ret = gwthread_poll(tab, n, -1.0)) == -1) {
2375             if (errno != EINTR) /* a signal was caught during poll() function */
2376                 warning(errno, "HTTP: gwthread_poll failed.");
2377             continue;
2378         }
2379 
2380         for (i = 0; i < n; ++i) {
2381             if (tab[i].revents & POLLIN) {
2382                 /* check our limit */
2383                 if (gwlist_len(active_connections) >= HTTP_SERVER_MAX_ACTIVE_CONNECTIONS) {
2384                     max_clients_reached = 1;
2385                     break;
2386                 } else {
2387                     max_clients_reached = 0;
2388                 }
2389 
2390                 addrlen = sizeof(addr);
2391                 fd = accept(tab[i].fd, (struct sockaddr *) &addr, &addrlen);
2392                 if (fd == -1) {
2393                     error(errno, "HTTP: Error accepting a client.");
2394                 } else {
2395                     Octstr *client_ip = host_ip(addr);
2396                     /*
2397                      * Be aware that conn_wrap_fd() will return NULL if SSL
2398                      * handshake has failed, so we only client_create() if
2399                      * there is an conn.
2400                      */
2401                     if ((conn = conn_wrap_fd(fd, ports[i]->ssl))) {
2402                         client = client_create(ports[i]->port, conn, client_ip);
2403                         conn_register(conn, ports[i]->server_fdset, receive_request, client);
2404                     } else {
2405                         error(0, "HTTP: unsuccessful SSL handshake for client `%s'",
2406                         octstr_get_cstr(client_ip));
2407                         octstr_destroy(client_ip);
2408                     }
2409                 }
2410             }
2411         }
2412 
2413         while ((portno = gwlist_extract_first(closed_server_sockets)) != NULL) {
2414             for (i = 0; i < n; ++i) {
2415                 if (ports[i]->port == *portno) {
2416                     (void) close(tab[i].fd);
2417                     tab[i].fd = -1;
2418                     tab[i].events = 0;
2419                     port_remove(ports[i]->port);
2420                     ports[i] = NULL;
2421                     n--;
2422 
2423                     /* now put the last entry on this place */
2424                     tab[i].fd = tab[n].fd;
2425                     tab[i].events = tab[n].events;
2426                     tab[n].fd = -1;
2427                     tab[n].events = 0;
2428                     ports[i] = ports[n];
2429                 }
2430             }
2431             gw_free(portno);
2432         }
2433     }
2434 
2435     /* make sure we close all ports */
2436     for (i = 0; i < n; ++i) {
2437         (void) close(tab[i].fd);
2438         port_remove(ports[i]->port);
2439     }
2440     gw_free(tab);
2441     gw_free(ports);
2442 
2443     server_thread_id = -1;
2444 }
2445 
2446 
start_server_thread(void)2447 static void start_server_thread(void)
2448 {
2449     if (!server_thread_is_running) {
2450         /*
2451          * To be really certain, we must repeat the test, but use the
2452          * lock first. If the test failed, however, we _know_ we've
2453          * already initialized. This strategy of double testing avoids
2454          * using the lock more than a few times at startup.
2455          */
2456         mutex_lock(server_thread_lock);
2457         if (!server_thread_is_running) {
2458             server_thread_id = gwthread_create(server_thread, NULL);
2459             server_thread_is_running = 1;
2460         }
2461         mutex_unlock(server_thread_lock);
2462     }
2463 }
2464 
2465 
http_set_server_timeout(int port,long timeout)2466 void http_set_server_timeout(int port, long timeout)
2467 {
2468     port_set_timeout(port, timeout);
2469 }
2470 
2471 
http_open_port_if(int port,int ssl,Octstr * interface)2472 int http_open_port_if(int port, int ssl, Octstr *interface)
2473 {
2474     struct port *p;
2475 
2476     if (ssl)
2477         info(0, "HTTP: Opening SSL server at port %d.", port);
2478     else
2479         info(0, "HTTP: Opening server at port %d.", port);
2480     p = port_add(port);
2481     p->port = port;
2482     p->ssl = ssl;
2483     p->fd = make_server_socket(port, (interface ? octstr_get_cstr(interface) : NULL));
2484     if (p->fd == -1) {
2485         port_remove(port);
2486     	return -1;
2487     }
2488 
2489     gwlist_produce(new_server_sockets, p);
2490     keep_servers_open = 1;
2491     start_server_thread();
2492     gwthread_wakeup(server_thread_id);
2493 
2494     return 0;
2495 }
2496 
2497 
http_open_port(int port,int ssl)2498 int http_open_port(int port, int ssl)
2499 {
2500     return http_open_port_if(port, ssl, NULL);
2501 }
2502 
2503 
http_close_port(int port)2504 void http_close_port(int port)
2505 {
2506     int *p;
2507 
2508     p = gw_malloc(sizeof(*p));
2509     *p = port;
2510     gwlist_produce(closed_server_sockets, p);
2511     gwthread_wakeup(server_thread_id);
2512 }
2513 
2514 
http_close_all_ports(void)2515 void http_close_all_ports(void)
2516 {
2517     if (server_thread_id != -1) {
2518         keep_servers_open = 0;
2519         gwthread_wakeup(server_thread_id);
2520         gwthread_join_every(server_thread);
2521         server_thread_is_running = 0;
2522     }
2523 }
2524 
2525 
2526 /*
2527  * Parse CGI variables from the path given in a GET. Return a list
2528  * of HTTPCGIvar pointers. Modify the url so that the variables are
2529  * removed.
2530  */
parse_cgivars(Octstr * url)2531 static List *parse_cgivars(Octstr *url)
2532 {
2533     HTTPCGIVar *v;
2534     List *list;
2535     int query, et, equals;
2536     Octstr *arg, *args;
2537 
2538     query = octstr_search_char(url, '?', 0);
2539     if (query == -1)
2540         return gwlist_create();
2541 
2542     args = octstr_copy(url, query + 1, octstr_len(url));
2543     octstr_truncate(url, query);
2544 
2545     list = gwlist_create();
2546 
2547     while (octstr_len(args) > 0) {
2548         et = octstr_search_char(args, '&', 0);
2549         if (et == -1)
2550             et = octstr_len(args);
2551         arg = octstr_copy(args, 0, et);
2552         octstr_delete(args, 0, et + 1);
2553 
2554         equals = octstr_search_char(arg, '=', 0);
2555         if (equals == -1)
2556             equals = octstr_len(arg);
2557 
2558         v = gw_malloc(sizeof(HTTPCGIVar));
2559         v->name = octstr_copy(arg, 0, equals);
2560         v->value = octstr_copy(arg, equals + 1, octstr_len(arg));
2561         octstr_url_decode(v->name);
2562         octstr_url_decode(v->value);
2563 
2564         octstr_destroy(arg);
2565 
2566         gwlist_append(list, v);
2567     }
2568     octstr_destroy(args);
2569 
2570     return list;
2571 }
2572 
2573 
http_accept_request(int port,Octstr ** client_ip,Octstr ** url,List ** headers,Octstr ** body,List ** cgivars)2574 HTTPClient *http_accept_request(int port, Octstr **client_ip, Octstr **url,
2575     	    	    	    	List **headers, Octstr **body,
2576                                 List **cgivars)
2577 {
2578     HTTPClient *client;
2579 
2580     do {
2581         client = port_get_request(port);
2582         if (client == NULL) {
2583             debug("gwlib.http", 0, "HTTP: No clients with requests, quitting.");
2584             return NULL;
2585         }
2586         /* check whether client connection still ok */
2587         conn_wait(client->conn, 0);
2588         if (conn_error(client->conn) || conn_eof(client->conn)) {
2589             client_destroy(client);
2590             client = NULL;
2591         }
2592     } while(client == NULL);
2593 
2594     *client_ip = octstr_duplicate(client->ip);
2595     *url = client->url;
2596     *headers = client->request->headers;
2597     *body = client->request->body;
2598     *cgivars = parse_cgivars(client->url);
2599 
2600     if (client->method != HTTP_METHOD_POST) {
2601         octstr_destroy(*body);
2602         *body = NULL;
2603     }
2604 
2605     client->persistent_conn = client_is_persistent(client->request->headers,
2606                                                    client->use_version_1_0);
2607 
2608     client->url = NULL;
2609     client->request->headers = NULL;
2610     client->request->body = NULL;
2611     entity_destroy(client->request);
2612     client->request = NULL;
2613 
2614     return client;
2615 }
2616 
2617 /*
2618  * The http_send_reply(...) uses this function to determinate the
2619  * reason pahrase for a status code.
2620  */
http_reason_phrase(int status)2621 static const char *http_reason_phrase(int status)
2622 {
2623 	switch (status) {
2624 	case HTTP_OK:
2625 		return "OK";						/* 200 */
2626 	case HTTP_CREATED:
2627 		return "Created";					/* 201 */
2628 	case HTTP_ACCEPTED:
2629 		return "Accepted";					/* 202 */
2630 	case HTTP_NO_CONTENT:
2631 		return "No Content";				/* 204 */
2632 	case HTTP_RESET_CONTENT:
2633 		return "Reset Content";				/* 205 */
2634 	case HTTP_MOVED_PERMANENTLY:
2635 		return "Moved Permanently"; 		/* 301 */
2636 	case HTTP_FOUND:
2637 		return "Found";						/* 302 */
2638 	case HTTP_SEE_OTHER:
2639 		return "See Other";					/* 303 */
2640 	case HTTP_NOT_MODIFIED:
2641 		return "Not Modified";				/* 304 */
2642 	case HTTP_TEMPORARY_REDIRECT:
2643 		return "Temporary Redirect";		/* 307 */
2644 	case HTTP_BAD_REQUEST:
2645 		return "Bad Request";				/* 400 */
2646 	case HTTP_UNAUTHORIZED:
2647 		return "Unauthorized";				/* 401 */
2648 	case HTTP_FORBIDDEN:
2649 		return "Forbidden";					/* 403 */
2650 	case HTTP_NOT_FOUND:
2651 		return "Not Found";					/* 404 */
2652 	case HTTP_BAD_METHOD:
2653 		return "Method Not Allowed";		/* 405 */
2654 	case HTTP_NOT_ACCEPTABLE:
2655 		return "Not Acceptable";			/* 406 */
2656 	case HTTP_REQUEST_ENTITY_TOO_LARGE:
2657 		return "Request Entity Too Large";	/* 413 */
2658 	case HTTP_UNSUPPORTED_MEDIA_TYPE:
2659 		return "Unsupported Media Type";	/* 415 */
2660 	case HTTP_INTERNAL_SERVER_ERROR:
2661 		return "Internal Server Error";		/* 500 */
2662 	case HTTP_NOT_IMPLEMENTED:
2663 		return "Not Implemented";			/* 501 */
2664 	case HTTP_BAD_GATEWAY:
2665 		return "Bad Gateway";				/* 502 */
2666 	}
2667 	return "Foo";
2668 }
2669 
2670 
http_send_reply(HTTPClient * client,int status,List * headers,Octstr * body)2671 void http_send_reply(HTTPClient *client, int status, List *headers,
2672     	    	     Octstr *body)
2673 {
2674     Octstr *response;
2675     Octstr *date;
2676     long i;
2677     int ret;
2678 
2679     if (client->use_version_1_0)
2680     	response = octstr_format("HTTP/1.0 %d %s\r\n", status, http_reason_phrase(status));
2681     else
2682     	response = octstr_format("HTTP/1.1 %d %s\r\n", status, http_reason_phrase(status));
2683 
2684     /* identify ourselfs */
2685     octstr_format_append(response, "Server: " GW_NAME "/%s\r\n", GW_VERSION);
2686 
2687     /* let's inform the client of our time */
2688     date = date_format_http(time(NULL));
2689     octstr_format_append(response, "Date: %s\r\n", octstr_get_cstr(date));
2690     octstr_destroy(date);
2691 
2692     octstr_format_append(response, "Content-Length: %ld\r\n", octstr_len(body));
2693 
2694     /*
2695      * RFC2616, sec. 8.1.2.1 says that if the server chooses to close the
2696      * connection, it *should* send a coresponding header
2697      */
2698     if (!client->use_version_1_0 && !client->persistent_conn)
2699         octstr_format_append(response, "Connection: close\r\n");
2700 
2701     for (i = 0; i < gwlist_len(headers); ++i)
2702     	octstr_format_append(response, "%S\r\n", gwlist_get(headers, i));
2703     octstr_format_append(response, "\r\n");
2704 
2705     if (body != NULL && client->method != HTTP_METHOD_HEAD)
2706     	octstr_append(response, body);
2707 
2708     ret = conn_write(client->conn, response);
2709     octstr_destroy(response);
2710 
2711     /* obey return code of conn_write() */
2712     /* sending response was successful */
2713     if (ret == 0) {
2714         /* HTTP/1.0 or 1.1, hence keep-alive or keep-alive */
2715         if (!client->persistent_conn) {
2716             client_destroy(client);
2717         } else {
2718             /* XXX mark this HTTPClient in the keep-alive cleaner thread */
2719             client_reset(client);
2720             conn_register(client->conn, port_get_fdset(client->port), receive_request, client);
2721         }
2722     }
2723     /* queued for sending, we don't want to block */
2724     else if (ret == 1) {
2725         client->state = sending_reply;
2726         conn_register(client->conn, port_get_fdset(client->port), receive_request, client);
2727     }
2728     /* error while sending response */
2729     else {
2730         client_destroy(client);
2731     }
2732 }
2733 
2734 
http_close_client(HTTPClient * client)2735 void http_close_client(HTTPClient *client)
2736 {
2737     client_destroy(client);
2738 }
2739 
http_method(HTTPClient * client)2740 int http_method(HTTPClient *client)
2741 {
2742     return client->method;
2743 }
2744 
http_request_url(HTTPClient * client)2745 Octstr *http_request_url(HTTPClient *client)
2746 {
2747     return client->url;
2748 }
2749 
server_init(void)2750 static void server_init(void)
2751 {
2752     new_server_sockets = gwlist_create();
2753     gwlist_add_producer(new_server_sockets);
2754     closed_server_sockets = gwlist_create();
2755     server_thread_lock = mutex_create();
2756 }
2757 
2758 
destroy_struct_server(void * p)2759 static void destroy_struct_server(void *p)
2760 {
2761     struct port *pp;
2762 
2763     pp = p;
2764     (void) close(pp->fd);
2765     port_remove(pp->port);
2766 }
2767 
2768 
destroy_int_pointer(void * p)2769 static void destroy_int_pointer(void *p)
2770 {
2771     (void) close(*(int *) p);
2772     gw_free(p);
2773 }
2774 
2775 
server_shutdown(void)2776 static void server_shutdown(void)
2777 {
2778     gwlist_remove_producer(new_server_sockets);
2779     if (server_thread_id != -1) {
2780         gwthread_wakeup(server_thread_id);
2781         gwthread_join_every(server_thread);
2782         server_thread_is_running = 0;
2783     }
2784     mutex_destroy(server_thread_lock);
2785     gwlist_destroy(new_server_sockets, destroy_struct_server);
2786     gwlist_destroy(closed_server_sockets, destroy_int_pointer);
2787 }
2788 
2789 
2790 /***********************************************************************
2791  * CGI variable manipulation.
2792  */
2793 
2794 
http_destroy_cgiargs(List * args)2795 void http_destroy_cgiargs(List *args)
2796 {
2797     HTTPCGIVar *v;
2798 
2799     gwlib_assert_init();
2800 
2801     if (args == NULL)
2802         return ;
2803 
2804     while ((v = gwlist_extract_first(args)) != NULL) {
2805         octstr_destroy(v->name);
2806         octstr_destroy(v->value);
2807         gw_free(v);
2808     }
2809     gwlist_destroy(args, NULL);
2810 }
2811 
2812 
http_cgi_variable(List * list,char * name)2813 Octstr *http_cgi_variable(List *list, char *name)
2814 {
2815     int i;
2816     HTTPCGIVar *v;
2817 
2818     gwlib_assert_init();
2819     gw_assert(list != NULL);
2820     gw_assert(name != NULL);
2821 
2822     for (i = 0; i < gwlist_len(list); ++i) {
2823         v = gwlist_get(list, i);
2824         if (octstr_str_compare(v->name, name) == 0)
2825             return v->value;
2826     }
2827     return NULL;
2828 }
2829 
2830 
2831 /***********************************************************************
2832  * Header manipulation.
2833  */
2834 
2835 
header_is_called(Octstr * header,char * name)2836 static int header_is_called(Octstr *header, char *name)
2837 {
2838     long colon;
2839 
2840     colon = octstr_search_char(header, ':', 0);
2841     if (colon == -1)
2842         return 0;
2843     if ((long) strlen(name) != colon)
2844         return 0;
2845     return strncasecmp(octstr_get_cstr(header), name, colon) == 0;
2846 }
2847 
2848 
http_create_empty_headers(void)2849 List *http_create_empty_headers(void)
2850 {
2851     gwlib_assert_init();
2852     return gwlist_create();
2853 }
2854 
2855 
http_destroy_headers(List * headers)2856 void http_destroy_headers(List *headers)
2857 {
2858     gwlib_assert_init();
2859     gwlist_destroy(headers, octstr_destroy_item);
2860 }
2861 
2862 
http_header_add(List * headers,char * name,char * contents)2863 void http_header_add(List *headers, char *name, char *contents)
2864 {
2865     gwlib_assert_init();
2866     gw_assert(headers != NULL);
2867     gw_assert(name != NULL);
2868     gw_assert(contents != NULL);
2869 
2870     gwlist_append(headers, octstr_format("%s: %s", name, contents));
2871 }
2872 
2873 
2874 /*
2875  * Given an headers list and a position, returns its header name and value,
2876  * or (X-Unknown, header) if it doesn't exist or if it's malformed - missing
2877  * ":" for example
2878  */
http_header_get(List * headers,long i,Octstr ** name,Octstr ** value)2879 void http_header_get(List *headers, long i, Octstr **name, Octstr **value)
2880 {
2881     Octstr *os;
2882     long colon;
2883 
2884     gwlib_assert_init();
2885     gw_assert(i >= 0);
2886     gw_assert(name != NULL);
2887     gw_assert(value != NULL);
2888 
2889     os = gwlist_get(headers, i);
2890     if (os == NULL)
2891         colon = -1;
2892     else
2893         colon = octstr_search_char(os, ':', 0);
2894     if (colon == -1) {
2895         error(0, "HTTP: Header does not contain a colon. BAD.");
2896         *name = octstr_create("X-Unknown");
2897         *value = octstr_duplicate(os);
2898     } else {
2899         *name = octstr_copy(os, 0, colon);
2900         *value = octstr_copy(os, colon + 1, octstr_len(os) - colon - 1);
2901         octstr_strip_blanks(*value);
2902     }
2903 }
2904 
2905 /*
2906  * Given an headers list and a name, returns its value or NULL if it
2907  * doesn't exist
2908  */
http_header_value(List * headers,Octstr * name)2909 Octstr *http_header_value(List *headers, Octstr *name)
2910 {
2911     Octstr *value;
2912     long i;
2913     Octstr *os;
2914     long colon;
2915     Octstr *current_name;
2916 
2917     gwlib_assert_init();
2918     gw_assert(name);
2919 
2920     value = NULL;
2921     i = 0;
2922     while (i < gwlist_len(headers)) {
2923         os = gwlist_get(headers, i);
2924         if (os == NULL)
2925             colon = -1;
2926         else
2927             colon = octstr_search_char(os, ':', 0);
2928         if (colon == -1) {
2929             return NULL;
2930         } else {
2931             current_name = octstr_copy(os, 0, colon);
2932         }
2933         if (octstr_case_compare(current_name, name) == 0) {
2934             value = octstr_copy(os, colon + 1, octstr_len(os) - colon - 1);
2935             octstr_strip_blanks(value);
2936             octstr_destroy(current_name);
2937             return value;
2938         }
2939         octstr_destroy(current_name);
2940         ++i;
2941     }
2942 
2943     return NULL;
2944 }
2945 
http_header_duplicate(List * headers)2946 List *http_header_duplicate(List *headers)
2947 {
2948     List *new;
2949     long i, len;
2950 
2951     gwlib_assert_init();
2952 
2953     if (headers == NULL)
2954         return NULL;
2955 
2956     new = http_create_empty_headers();
2957     len = gwlist_len(headers);
2958     for (i = 0; i < len; ++i)
2959         gwlist_append(new, octstr_duplicate(gwlist_get(headers, i)));
2960     return new;
2961 }
2962 
2963 
2964 #define MAX_HEADER_LENGTH 256
2965 /*
2966  * Aggregate header in one (or more) lines with several parameters separated
2967  * by commas, instead of one header per parameter
2968  */
http_header_pack(List * headers)2969 void http_header_pack(List *headers)
2970 {
2971     Octstr *name, *value;
2972     Octstr *name2, *value2;
2973     long i, j;
2974 
2975     gwlib_assert_init();
2976     gw_assert(headers != NULL);
2977 
2978     /*
2979      * For each header, search forward headers for similar ones and if possible,
2980      * add it to current header and delete it
2981      */
2982     for(i = 0; i < gwlist_len(headers); i++) {
2983         http_header_get(headers, i, &name, &value);
2984 	/* debug("http_header_pack", 0, "HTTP_HEADER_PACK: Processing header %d. [%s: %s]",
2985 	       i, octstr_get_cstr(name), octstr_get_cstr(value)); */
2986 
2987         for(j=i+1; j < gwlist_len(headers); j++) {
2988             http_header_get(headers, j, &name2, &value2);
2989 
2990             if(octstr_case_compare(name, name2) == 0) {
2991                 if(octstr_len(value) + 2 + octstr_len(value2) > MAX_HEADER_LENGTH) {
2992 		    octstr_destroy(name2);
2993 		    octstr_destroy(value2);
2994                     break;
2995                 } else {
2996 		    Octstr *header;
2997 
2998 		    /* Delete old header */
2999 		    header = gwlist_get(headers, i);
3000 		    octstr_destroy(header);
3001                     gwlist_delete(headers, i, 1);
3002 
3003 		    /* Adds comma and new value to old header value */
3004                     octstr_append(value, octstr_imm(", "));
3005                     octstr_append(value, value2);
3006 		    /* Creates a new header */
3007 		    header = octstr_create("");
3008                     octstr_append(header, name);
3009                     octstr_append(header, octstr_imm(": "));
3010                     octstr_append(header, value);
3011                     gwlist_insert(headers, i, header);
3012 
3013 		    /* Delete this header */
3014 		    header = gwlist_get(headers, j);
3015 		    octstr_destroy(header);
3016                     gwlist_delete(headers, j, 1);
3017                     j--;
3018                 }
3019             }
3020 	    octstr_destroy(name2);
3021 	    octstr_destroy(value2);
3022         }
3023 	octstr_destroy(name);
3024 	octstr_destroy(value);
3025     }
3026 }
3027 
3028 
http_append_headers(List * to,List * from)3029 void http_append_headers(List *to, List *from)
3030 {
3031     Octstr *header;
3032     long i;
3033 
3034     gwlib_assert_init();
3035     gw_assert(to != NULL);
3036     gw_assert(from != NULL);
3037 
3038     for (i = 0; i < gwlist_len(from); ++i) {
3039         header = gwlist_get(from, i);
3040         gwlist_append(to, octstr_duplicate(header));
3041     }
3042 }
3043 
3044 
http_header_combine(List * old_headers,List * new_headers)3045 void http_header_combine(List *old_headers, List *new_headers)
3046 {
3047     long i;
3048     Octstr *name;
3049     Octstr *value;
3050 
3051     /*
3052      * Avoid doing this scan if old_headers is empty anyway.
3053      */
3054     if (gwlist_len(old_headers) > 0) {
3055         for (i = 0; i < gwlist_len(new_headers); i++) {
3056   	    http_header_get(new_headers, i, &name, &value);
3057 	    http_header_remove_all(old_headers, octstr_get_cstr(name));
3058             octstr_destroy(name);
3059             octstr_destroy(value);
3060         }
3061     }
3062 
3063     http_append_headers(old_headers, new_headers);
3064 }
3065 
3066 
http_header_find_first_real(List * headers,char * name,const char * file,long line,const char * func)3067 Octstr *http_header_find_first_real(List *headers, char *name, const char *file, long line,
3068                                     const char *func)
3069 {
3070     long i, name_len;
3071     Octstr *h, *value;
3072 
3073     gwlib_assert_init();
3074     gw_assert(headers != NULL);
3075     gw_assert(name != NULL);
3076 
3077     name_len = strlen(name);
3078 
3079     for (i = 0; i < gwlist_len(headers); ++i) {
3080         h = gwlist_get(headers, i);
3081         if (header_is_called(h, name)) {
3082             value = octstr_copy_real(h, name_len + 1, octstr_len(h),
3083                                      file, line, func);
3084 	    octstr_strip_blanks(value);
3085 	    return value;
3086 	}
3087     }
3088     return NULL;
3089 }
3090 
3091 
http_header_find_all(List * headers,char * name)3092 List *http_header_find_all(List *headers, char *name)
3093 {
3094     List *list;
3095     long i;
3096     Octstr *h;
3097 
3098     gwlib_assert_init();
3099     gw_assert(headers != NULL);
3100     gw_assert(name != NULL);
3101 
3102     list = gwlist_create();
3103     for (i = 0; i < gwlist_len(headers); ++i) {
3104         h = gwlist_get(headers, i);
3105         if (header_is_called(h, name))
3106             gwlist_append(list, octstr_duplicate(h));
3107     }
3108     return list;
3109 }
3110 
3111 
http_header_remove_all(List * headers,char * name)3112 long http_header_remove_all(List *headers, char *name)
3113 {
3114     long i;
3115     Octstr *h;
3116     long count;
3117 
3118     gwlib_assert_init();
3119     gw_assert(headers != NULL);
3120     gw_assert(name != NULL);
3121 
3122     i = 0;
3123     count = 0;
3124     while (i < gwlist_len(headers)) {
3125 	h = gwlist_get(headers, i);
3126 	if (header_is_called(h, name)) {
3127 	    gwlist_delete(headers, i, 1);
3128 	    octstr_destroy(h);
3129 	    count++;
3130 	} else
3131 	    i++;
3132     }
3133 
3134     return count;
3135 }
3136 
3137 
http_remove_hop_headers(List * headers)3138 void http_remove_hop_headers(List *headers)
3139 {
3140     Octstr *h;
3141     List *connection_headers;
3142 
3143     gwlib_assert_init();
3144     gw_assert(headers != NULL);
3145 
3146     /*
3147      * The hop-by-hop headers are a standard list, plus those named
3148      * in the Connection header(s).
3149      */
3150 
3151     connection_headers = http_header_find_all(headers, "Connection");
3152     while ((h = gwlist_consume(connection_headers))) {
3153 	List *hop_headers;
3154 	Octstr *e;
3155 
3156 	octstr_delete(h, 0, strlen("Connection:"));
3157 	hop_headers = http_header_split_value(h);
3158 	octstr_destroy(h);
3159 
3160 	while ((e = gwlist_consume(hop_headers))) {
3161 	    http_header_remove_all(headers, octstr_get_cstr(e));
3162 	    octstr_destroy(e);
3163 	}
3164 
3165 	gwlist_destroy(hop_headers, NULL);
3166     }
3167     gwlist_destroy(connection_headers, NULL);
3168 
3169     http_header_remove_all(headers, "Connection");
3170     http_header_remove_all(headers, "Keep-Alive");
3171     http_header_remove_all(headers, "Proxy-Authenticate");
3172     http_header_remove_all(headers, "Proxy-Authorization");
3173     http_header_remove_all(headers, "TE");
3174     http_header_remove_all(headers, "Trailers");
3175     http_header_remove_all(headers, "Transfer-Encoding");
3176     http_header_remove_all(headers, "Upgrade");
3177 }
3178 
3179 
http_header_mark_transformation(List * headers,Octstr * new_body,Octstr * new_type)3180 void http_header_mark_transformation(List *headers,
3181     	    	    	    	     Octstr *new_body, Octstr *new_type)
3182 {
3183     Octstr *new_length = NULL;
3184 
3185     /* Remove all headers that no longer apply to the new body. */
3186     http_header_remove_all(headers, "Content-Length");
3187     http_header_remove_all(headers, "Content-MD5");
3188     http_header_remove_all(headers, "Content-Type");
3189 
3190     /* Add headers that we need to describe the new body. */
3191     new_length = octstr_format("%ld", octstr_len(new_body));
3192     http_header_add(headers, "Content-Length", octstr_get_cstr(new_length));
3193     if(octstr_len(new_type))
3194 	http_header_add(headers, "Content-Type", octstr_get_cstr(new_type));
3195 
3196     /* Perhaps we should add Warning: 214 "Transformation applied" too? */
3197 
3198     octstr_destroy(new_length);
3199 }
3200 
3201 
http_header_get_content_type(List * headers,Octstr ** type,Octstr ** charset)3202 void http_header_get_content_type(List *headers, Octstr **type,
3203                                   Octstr **charset)
3204 {
3205     Octstr *h;
3206     long semicolon, equals, len;
3207 
3208     gwlib_assert_init();
3209     gw_assert(headers != NULL);
3210     gw_assert(type != NULL);
3211     gw_assert(charset != NULL);
3212 
3213     h = http_header_find_first(headers, "Content-Type");
3214     if (h == NULL) {
3215         *type = octstr_create("application/octet-stream");
3216         *charset = octstr_create("");
3217     } else {
3218         octstr_strip_blanks(h);
3219         semicolon = octstr_search_char(h, ';', 0);
3220         if (semicolon == -1) {
3221             *type = h;
3222             *charset = octstr_create("");
3223         } else {
3224             *charset = octstr_duplicate(h);
3225             octstr_delete(*charset, 0, semicolon + 1);
3226             octstr_strip_blanks(*charset);
3227             equals = octstr_search_char(*charset, '=', 0);
3228             if (equals == -1)
3229                 octstr_truncate(*charset, 0);
3230             else {
3231                 octstr_delete(*charset, 0, equals + 1);
3232                 if (octstr_get_char(*charset, 0) == '"')
3233                     octstr_delete(*charset, 0, 1);
3234                 len = octstr_len(*charset);
3235                 if (octstr_get_char(*charset, len - 1) == '"')
3236                     octstr_truncate(*charset, len - 1);
3237             }
3238 
3239             octstr_truncate(h, semicolon);
3240             octstr_strip_blanks(h);
3241             *type = h;
3242         }
3243 
3244         /*
3245          * According to HTTP/1.1 (RFC 2616, section 3.7.1) we have to ensure
3246          * to return charset 'iso-8859-1' in case of no given encoding and
3247          * content-type is a 'text' subtype.
3248          */
3249         if (octstr_len(*charset) == 0 &&
3250             octstr_ncompare(*type, octstr_imm("text"), 4) == 0)
3251             octstr_append_cstr(*charset, "ISO-8859-1");
3252     }
3253 }
3254 
3255 
http_header_add_element(List * list,Octstr * value,long start,long end)3256 static void http_header_add_element(List *list, Octstr *value,
3257 				    long start, long end)
3258 {
3259     Octstr *element;
3260 
3261     element = octstr_copy(value, start, end - start);
3262     octstr_strip_blanks(element);
3263     if (octstr_len(element) == 0)
3264 	octstr_destroy(element);
3265     else
3266     	gwlist_append(list, element);
3267 }
3268 
3269 
http_header_quoted_string_len(Octstr * header,long start)3270 long http_header_quoted_string_len(Octstr *header, long start)
3271 {
3272     long len;
3273     long pos;
3274     int c;
3275 
3276     if (octstr_get_char(header, start) != '"')
3277 	return -1;
3278 
3279     len = octstr_len(header);
3280     for (pos = start + 1; pos < len; pos++) {
3281 	c = octstr_get_char(header, pos);
3282 	if (c == '\\')    /* quoted-pair */
3283 	    pos++;
3284 	else if (c == '"')
3285 	    return pos - start + 1;
3286     }
3287 
3288     warning(0, "Header contains unterminated quoted-string:");
3289     warning(0, "%s", octstr_get_cstr(header));
3290     return len - start;
3291 }
3292 
3293 
http_header_split_value(Octstr * value)3294 List *http_header_split_value(Octstr *value)
3295 {
3296     long start;  /* start of current element */
3297     long pos;
3298     long len;
3299     List *result;
3300     int c;
3301 
3302     /*
3303      * According to RFC2616 section 4.2, a field-value is either *TEXT
3304      * (the caller is responsible for not feeding us one of those) or
3305      * combinations of token, separators, and quoted-string.  We're
3306      * looking for commas which are separators, and have to skip
3307      * commas in quoted-strings.
3308      */
3309 
3310     result = gwlist_create();
3311     len = octstr_len(value);
3312     start = 0;
3313     for (pos = 0; pos < len; pos++) {
3314 	c = octstr_get_char(value, pos);
3315 	if (c == ',') {
3316 	    http_header_add_element(result, value, start, pos);
3317 	    start = pos + 1;
3318 	} else if (c == '"') {
3319             pos += http_header_quoted_string_len(value, pos);
3320 	    pos--; /* compensate for the loop's pos++ */
3321         }
3322     }
3323     http_header_add_element(result, value, start, len);
3324     return result;
3325 }
3326 
3327 
http_header_split_auth_value(Octstr * value)3328 List *http_header_split_auth_value(Octstr *value)
3329 {
3330     List *result;
3331     Octstr *auth_scheme;
3332     Octstr *element;
3333     long i;
3334 
3335     /*
3336      * According to RFC2617, both "challenge" and "credentials"
3337      * consist of an auth-scheme followed by a list of auth-param.
3338      * Since we have to parse a list of challenges or credentials,
3339      * we have to look for auth-scheme to signal the start of
3340      * a new element.  (We can't just split on commas because
3341      * they are also used to separate the auth-params.)
3342      *
3343      * An auth-scheme is a single token, while an auth-param is
3344      * always a key=value pair.  So we can recognize an auth-scheme
3345      * as a token that is not followed by a '=' sign.
3346      *
3347      * Simple approach: First split at all commas, then recombine
3348      * the elements that belong to the same challenge or credential.
3349      * This is somewhat expensive but saves programmer thinking time.
3350      *
3351      * Richard Braakman
3352      */
3353 
3354     result = http_header_split_value(value);
3355     if (gwlist_len(result) == 0)
3356         return result;
3357 
3358     auth_scheme = gwlist_get(result, 0);
3359     i = 1;
3360     while (i < gwlist_len(result)) {
3361         int c;
3362         long pos;
3363 
3364         element = gwlist_get(result, i);
3365 
3366         /*
3367          * If the element starts with: token '='
3368          * then it's just an auth_param; append it to the current
3369          * auth_scheme.  If it starts with: token token '='
3370          * then it's the start of a new auth scheme.
3371          *
3372          * To make the scan easier, we consider anything other
3373          * than whitespace or '=' to be part of a token.
3374          */
3375 
3376         /* Skip first token */
3377         for (pos = 0; pos < octstr_len(element); pos++) {
3378             c = octstr_get_char(element, pos);
3379             if (isspace(c) || c == '=')
3380                 break;
3381         }
3382 
3383         /* Skip whitespace, if any */
3384         while (isspace(octstr_get_char(element, pos)))
3385             pos++;
3386 
3387         if (octstr_get_char(element, pos) == '=') {
3388             octstr_append_char(auth_scheme, ';');
3389             octstr_append(auth_scheme, element);
3390             gwlist_delete(result, i, 1);
3391             octstr_destroy(element);
3392         } else {
3393             char semicolon = ';';
3394             octstr_insert_data(element, pos, &semicolon, 1);
3395             auth_scheme = element;
3396             i++;
3397         }
3398     }
3399 
3400     return result;
3401 }
3402 
3403 
http_header_dump(List * headers)3404 void http_header_dump(List *headers)
3405 {
3406     long i;
3407 
3408     gwlib_assert_init();
3409 
3410     debug("gwlib.http", 0, "Dumping HTTP headers:");
3411     for (i = 0; headers != NULL && i < gwlist_len(headers); ++i)
3412         octstr_dump(gwlist_get(headers, i), 1);
3413     debug("gwlib.http", 0, "End of dump.");
3414 }
3415 
3416 
http_cgivar_dump(List * cgiargs)3417 void http_cgivar_dump(List *cgiargs)
3418 {
3419     HTTPCGIVar *v;
3420     long i, len;
3421 
3422     gwlib_assert_init();
3423 
3424     len = gwlist_len(cgiargs);
3425 
3426     debug("gwlib.http", 0, "Dumping %ld cgi variables:", len);
3427     for (i = 0; i < len; i++) {
3428         v = gwlist_get(cgiargs, i);
3429         octstr_dump(v->name, 0);
3430         octstr_dump(v->value, 0);
3431     }
3432     debug("gwlib.http", 0, "End of dump.");
3433 }
3434 
3435 
http_cgivar_dump_into(List * cgiargs,Octstr * os)3436 void http_cgivar_dump_into(List *cgiargs, Octstr *os)
3437 {
3438     HTTPCGIVar *v;
3439     long i;
3440 
3441     if (os == NULL)
3442         return;
3443 
3444     gwlib_assert_init();
3445 
3446     for (i = 0; i < gwlist_len(cgiargs); i++) {
3447         v = gwlist_get(cgiargs, i);
3448         octstr_format_append(os, "&%E=%E", v->name, v->value);
3449     }
3450 }
3451 
3452 
http_something_accepted(List * headers,char * header_name,char * what)3453 static int http_something_accepted(List *headers, char *header_name,
3454                                    char *what)
3455 {
3456     int found;
3457     long i;
3458     List *accepts;
3459     Octstr *needle = octstr_create(what);
3460 
3461     gwlib_assert_init();
3462     gw_assert(headers != NULL);
3463     gw_assert(what != NULL);
3464 
3465     /* return all headers with this name */
3466     accepts = http_header_find_all(headers, header_name);
3467 
3468     found = 0;
3469     for (i = 0; !found && i < gwlist_len(accepts); ++i) {
3470         Octstr *header_value = gwlist_get(accepts, i);
3471         if (octstr_case_search(header_value, needle, 0) != -1)
3472             found = 1;
3473     }
3474 	octstr_destroy(needle);
3475     http_destroy_headers(accepts);
3476     return found;
3477 }
3478 
3479 
http_type_accepted(List * headers,char * type)3480 int http_type_accepted(List *headers, char *type)
3481 {
3482     return http_something_accepted(headers, "Accept", type);
3483 }
3484 
3485 
http_charset_accepted(List * headers,char * charset)3486 int http_charset_accepted(List *headers, char *charset)
3487 {
3488     return http_something_accepted(headers, "Accept-Charset", charset);
3489 }
3490 
3491 
http_add_basic_auth(List * headers,Octstr * username,Octstr * password)3492 void http_add_basic_auth(List *headers, Octstr *username, Octstr *password)
3493 {
3494     Octstr *os;
3495 
3496     if (password != NULL)
3497       os = octstr_format("%S:%S", username, password);
3498     else
3499       os = octstr_format("%S", username);
3500     octstr_binary_to_base64(os);
3501     octstr_strip_blanks(os);
3502     octstr_insert(os, octstr_imm("Basic "), 0);
3503     http_header_add(headers, "Authorization", octstr_get_cstr(os));
3504     octstr_destroy(os);
3505 }
3506 
3507 
http_get_header_parameter(Octstr * value,Octstr * parameter)3508 Octstr *http_get_header_parameter(Octstr *value, Octstr *parameter)
3509 {
3510     long pos, len, end;
3511     int c, found = 0;
3512     Octstr *result = NULL;
3513 
3514     len = octstr_len(value);
3515     /* Find the start of the first parameter. */
3516     for (pos = 0; pos < len; pos++) {
3517         c = octstr_get_char(value, pos);
3518         if (c == ';')
3519             break;
3520         else if (c == '"')
3521             pos += http_header_quoted_string_len(value, pos) - 1;
3522     }
3523 
3524     if (pos >= len)
3525         return NULL;   /* no parameters */
3526 
3527     for (pos++; pos > 0 && pos < len && found == 0; pos++) {
3528         Octstr *key = NULL;
3529         Octstr *val = NULL;
3530 
3531         end = octstr_search_char(value, '=', pos);
3532         if (end < 0)
3533             end = octstr_search_char(value, ';', pos);
3534         if (end < 0)
3535             end = octstr_len(value);
3536         key = octstr_copy(value, pos, end - pos);
3537         octstr_strip_blanks(key);
3538         pos = end;
3539 
3540         if (octstr_get_char(value, pos) == '=') {
3541             pos++;
3542             while (isspace(octstr_get_char(value, pos)))
3543                 pos++;
3544             if (octstr_get_char(value, pos) == '"')
3545                 end = pos + http_header_quoted_string_len(value, pos);
3546             else
3547                 end = octstr_search_char(value, ';', pos);
3548             if (end < 0)
3549                 end = octstr_len(value);
3550             val = octstr_copy(value, pos, end - pos);
3551             octstr_strip_blanks(val);
3552             pos = end;
3553             pos = octstr_search_char(value, ';', pos);
3554         }
3555 
3556         /* is this the pair we look for? bail out then*/
3557         if (octstr_case_compare(key, parameter) == 0) {
3558             found++;
3559             result = octstr_duplicate(val);
3560         }
3561 
3562         octstr_destroy(key);
3563         octstr_destroy(val);
3564     }
3565 
3566     return result;
3567 }
3568 
3569 
3570 /***********************************************************************
3571  * Module initialization and shutdown.
3572  */
3573 
3574 
http_init(void)3575 void http_init(void)
3576 {
3577     gw_assert(run_status == limbo);
3578 
3579 #ifdef HAVE_LIBSSL
3580     openssl_init_locks();
3581     conn_init_ssl();
3582 #endif /* HAVE_LIBSSL */
3583     proxy_init();
3584     client_init();
3585     conn_pool_init();
3586     port_init();
3587     server_init();
3588 #ifdef HAVE_LIBSSL
3589     server_ssl_init();
3590 #endif /* HAVE_LIBSSL */
3591 
3592     run_status = running;
3593 }
3594 
3595 
http_shutdown(void)3596 void http_shutdown(void)
3597 {
3598     gwlib_assert_init();
3599     gw_assert(run_status == running);
3600 
3601     run_status = terminating;
3602 
3603     conn_pool_shutdown();
3604     client_shutdown();
3605     server_shutdown();
3606     port_shutdown();
3607     proxy_shutdown();
3608 #ifdef HAVE_LIBSSL
3609     openssl_shutdown_locks();
3610     conn_shutdown_ssl();
3611     server_shutdown_ssl();
3612 #endif /* HAVE_LIBSSL */
3613     run_status = limbo;
3614 }
3615 
3616 
3617 /*
3618  * This function relies on the HTTP_STATUS_* enum values being
3619  * chosen to fit this.
3620  */
http_status_class(int code)3621 int http_status_class(int code)
3622 {
3623     int sclass;
3624 
3625     if (code < 100 || code >= 600)
3626         sclass = HTTP_STATUS_UNKNOWN;
3627     else
3628         sclass = code - (code % 100);
3629     return sclass;
3630 }
3631 
3632 
http_name2method(Octstr * method)3633 int http_name2method(Octstr *method)
3634 {
3635     gw_assert(method != NULL);
3636 
3637     if (octstr_str_compare(method, "GET") == 0) {
3638         return HTTP_METHOD_GET;
3639     }
3640     else if (octstr_str_compare(method, "POST") == 0) {
3641         return HTTP_METHOD_POST;
3642     }
3643     else if (octstr_str_compare(method, "HEAD") == 0) {
3644         return HTTP_METHOD_HEAD;
3645     }
3646 
3647     return -1;
3648 }
3649 
3650 
http_method2name(int method)3651 char *http_method2name(int method)
3652 {
3653     gw_assert(method > 0 && method <= 3);
3654 
3655     return http_methods[method-1];
3656 }
3657 
3658