81 #include <sys/types.h> 82 #include <sys/socket.h> 88 #define DUMP_RESPONSE 1 94 #define HTTP_SERVER_TIMEOUT 60 96 #define HTTP_SERVER_MAX_ACTIVE_CONNECTIONS 500 286 for (i = 0; i <
gwlist_len(exceptions); ++i) {
291 if (exceptions_regex != NULL &&
296 debug(
"gwlib.http", 0,
"Using proxy <%s:%d> with %s scheme",
390 error(0,
"HTTP: Unknown Transfer-Encoding <%s>",
404 error(0,
"HTTP: Content-Length header wrong: <%s>",
433 ent = gw_malloc(
sizeof(*ent));
572 old_state = ent->
state;
573 switch (ent->
state) {
613 panic(0,
"Internal error: Invalid HTTPEntity state.");
615 }
while (ent->
state != old_state);
653 #define HTTP_MAX_FOLLOW 5 661 "GET",
"POST",
"HEAD",
"PUT",
"DELETE",
"PATCH" 701 List *headers,
Octstr *body,
int follow_remaining,
706 trans = gw_malloc(
sizeof(*trans));
714 trans->
state = request_not_sent;
815 debug(
"gwlib.http", 0,
"HTTP:conn_pool_get: Server closed connection, destroying it <%s><%p><fd:%d>.",
832 debug(
"gwlib.http", 0,
"HTTP: Opening connection to `%s:%d' (fd=%d).",
835 debug(
"gwlib.http", 0,
"HTTP: Reusing connection to `%s:%d' (fd=%d).",
843 static void check_pool_conn(
Connection *conn,
void *data)
861 debug(
"gwlib.http", 0,
"HTTP: Server closed connection, destroying it <%s><%p><fd:%d>.",
966 if (trans->
port != 80 || trans->
ssl) {
1021 error(0,
"HTTP: Malformed status line from HTTP server: <%s>",
1054 while (trans->
state != transaction_done) {
1055 switch (trans->
state) {
1057 debug(
"gwlib.http", 0,
"Get info about connecting socket");
1059 debug(
"gwlib.http", 0,
"Socket not connected");
1064 trans->
state = reading_status;
1066 debug(
"gwlib.http", 0,
"Failed while sending request");
1071 case reading_status:
1079 debug(
"gwlib.http",0,
"Failed while reading status");
1081 }
else if (ret == 0) {
1083 trans->
state = reading_entity;
1090 case reading_entity:
1093 debug(
"gwlib.http",0,
"Failed reading entity");
1095 }
else if (ret == 0 &&
1098 trans->
state = reading_status;
1101 }
else if (ret == 0) {
1102 trans->
state = transaction_done;
1103 #ifdef DUMP_RESPONSE 1105 debug(
"gwlib.http", 0,
"HTTP: Received response:");
1116 panic(0,
"Internal error: Invalid HTTPServer state.");
1149 #ifdef USE_KEEPALIVE 1210 trans->
state = request_not_sent;
1228 if (trans->
conn != NULL) {
1250 int i, host_found = 0;
1255 #ifdef USE_KEEPALIVE 1259 for (i = 0; headers != NULL && i <
gwlist_len(headers); ++i) {
1282 if (request_body != NULL)
1300 for (i = 0; headers != NULL && i <
gwlist_len(headers); ++i) {
1355 debug(
"http.parse_url",0,
" Port: %ld", p->
port);
1380 Octstr *prefix, *prefix_https;
1382 int host_len,
colon, slash, at, auth_sep, query;
1383 host_len =
colon = slash = at = auth_sep = query = 0;
1392 debug(
"gwlib.http", 0,
"HTTPS URL; Using SSL for the connection");
1393 prefix = prefix_https;
1396 error(0,
"Attempt to use HTTPS <%s> but SSL not compiled in",
1401 error(0,
"URL <%s> doesn't start with `%s' nor `%s'",
1417 if (
colon == prefix_len || slash == prefix_len) {
1430 if ((slash == -1 || ( slash != -1 && at < slash))) {
1432 if (auth_sep != -1 && (auth_sep < at)) {
1450 if (slash == -1 &&
colon == -1) {
1460 else if (slash == -1) {
1461 host_len =
colon - prefix_len;
1463 error(0,
"URL <%s> has malformed port number.",
1471 host_len = slash - prefix_len;
1480 else if (
colon < slash) {
1481 host_len =
colon - prefix_len;
1483 error(0,
"URL <%s> has malformed port number.",
1490 error(0,
"Internal error in URL parsing logic.");
1505 if (p->
user != NULL)
1507 if (p->
pass != NULL)
1513 host_len = host_len - at + prefix_len - 1;
1514 prefix_len = at + 1;
1522 host_len = slash != -1 ? slash - prefix_len : query - prefix_len;
1526 p->
path = (slash == -1) ?
1545 if (p == NULL || t == NULL)
1564 && !t->
ssl) ? 1 : 0;
1575 if (!trans->
host && trans->
port == 0 && trans->
url != NULL) {
1632 warning(0,
"HTTP: GET or HEAD method request contains body:");
1658 debug(
"gwlib.http", 0,
"HTTP: Sending request:");
1701 if (trans->
conn == NULL)
1704 debug(
"gwlib.http", 0,
"Socket connected at once");
1707 trans->
state = reading_status;
1715 debug(
"gwlib.http", 0,
"Socket connecting");
1716 trans->
state = connecting;
1736 error(0,
"HTTP: Could not start client write_request thread.");
1761 Octstr *body,
int follow,
void *
id,
Octstr *certkeyfile)
1764 int follow_remaining;
1769 follow_remaining = 0;
1802 if (trans->
status >= 0) {
1803 *final_url = trans->
url;
1832 reply_headers, reply_body);
1907 if (conn_get_ssl(conn))
1908 debug(
"gwlib.http", 0,
"HTTP: Creating SSL-enabled HTTPClient for `%s', using cipher '%s'.",
1913 p = gw_malloc(
sizeof(*p));
1917 p->
state = reading_request_line;
1923 debug(
"gwlib.http", 0,
"HTTP: Created HTTPClient area %p.", p);
1945 panic(0,
"HTTP: Race condition in client_destroy(%p) detected!",
client);
1954 debug(
"gwlib.http", 0,
"HTTP: Destroying HTTPClient area %p.", p);
1955 gw_assert_allocated(p, __FILE__, __LINE__, __func__);
1956 debug(
"gwlib.http", 0,
"HTTP: Destroying HTTPClient for `%s'.",
1969 debug(
"gwlib.http", 0,
"HTTP: Resetting HTTPClient for `%s'.",
1971 p->
state = reading_request_line;
1988 return !use_version_1_0;
2001 ret = (!use_version_1_0);
2065 p = gw_malloc(
sizeof(*p));
2072 warning(0,
"HTTP: port_add called for existing port (%d)",
port);
2095 error(0,
"HTTP: Could not find port (%d) in port_collection.",
port);
2211 int *use_version_1_0,
Octstr *line)
2236 *use_version_1_0 = !ret;
2266 case reading_request_line:
2274 &
client->use_version_1_0, line);
2282 client->persistent_conn = 0;
2293 client->state = reading_request;
2296 case reading_request:
2301 client->state = request_is_being_handled;
2314 if (!
client->persistent_conn) {
2329 panic(0,
"Internal error: HTTPClient state is wrong.");
2346 struct pollfd *tab = NULL;
2347 struct port **ports = NULL;
2348 int tab_size = 0, n, i,
fd, ret, max_clients_reached;
2349 struct sockaddr_in addr;
2355 n = max_clients_reached = 0;
2360 debug(
"gwlib.http", 0,
"HTTP: No new servers. Quitting.");
2363 debug (
"gwlib.http", 0,
"HTTP: Including port %d, fd %d for polling in server thread", p->
port, p->
fd);
2365 if (tab_size <= n) {
2367 tab = gw_realloc(tab, tab_size *
sizeof(*tab));
2368 ports = gw_realloc(ports, tab_size *
sizeof(*ports));
2369 if (tab == NULL || ports == NULL) {
2385 }
else if (!max_clients_reached && (ret =
gwthread_poll(tab, n, -1.0)) == -1) {
2387 warning(errno,
"HTTP: gwthread_poll failed.");
2391 for (i = 0; i < n; ++i) {
2392 if (tab[i].revents &
POLLIN) {
2395 max_clients_reached = 1;
2398 max_clients_reached = 0;
2401 addrlen =
sizeof(addr);
2402 fd = accept(tab[i].
fd, (
struct sockaddr *) &addr, &addrlen);
2404 error(errno,
"HTTP: Error accepting a client.");
2416 error(0,
"HTTP: unsuccessful SSL handshake for client `%s'",
2425 for (i = 0; i < n; ++i) {
2426 if (ports[i]->
port == *portno) {
2427 (void) close(tab[i].
fd);
2435 tab[i].
fd = tab[n].
fd;
2439 ports[i] = ports[n];
2447 for (i = 0; i < n; ++i) {
2448 (void) close(tab[i].
fd);
2488 info(0,
"HTTP: Opening SSL server at port %d.",
port);
2490 info(0,
"HTTP: Opening server at port %d.",
port);
2519 p = gw_malloc(
sizeof(*p));
2546 long ampersand, equal,
start;
2551 if (ampersand == -1)
2552 ampersand = pairs_len;
2582 debug(
"gwlib.http", 0,
"HTTP: No clients with requests, quitting.");
2593 debug(
"gwlib.http", 0,
"HTTP: Got %s request with url='%s' and body='%s'",
2598 *headers =
client->request->headers;
2599 *body =
client->request->body;
2604 if (question_mark >= 0) {
2630 client->use_version_1_0);
2633 client->request->headers = NULL;
2634 client->request->body = NULL;
2655 return "No Content";
2657 return "Reset Content";
2659 return "Moved Permanently";
2665 return "Not Modified";
2667 return "Temporary Redirect";
2669 return "Bad Request";
2671 return "Unauthorized";
2677 return "Method Not Allowed";
2679 return "Not Acceptable";
2681 return "Request Entity Too Large";
2683 return "Unsupported Media Type";
2685 return "Internal Server Error";
2687 return "Not Implemented";
2689 return "Bad Gateway";
2703 if (
client->use_version_1_0)
2719 if (
client->persistent_conn)
2738 if (!
client->persistent_conn) {
2747 else if (ret == 1) {
2748 client->state = sending_reply;
2787 (void) close(pp->
fd);
2794 (void) close(*(
int *) p);
2918 error(0,
"HTTP: Header does not contain a colon. BAD.");
2976 if (headers == NULL)
2981 for (i = 0; i < len; ++i)
2987 #define MAX_HEADER_LENGTH 256 3078 for (i = 0; i <
gwlist_len(new_headers); i++) {
3164 List *connection_headers;
3206 Octstr *new_length = NULL;
3229 long semicolon, equals, len;
3243 if (semicolon == -1) {
3280 long start,
long end)
3303 for (pos =
start + 1; pos < len; pos++) {
3308 return pos -
start + 1;
3311 warning(0,
"Header contains unterminated quoted-string:");
3336 for (pos = 0; pos < len; pos++) {
3341 }
else if (c ==
'"') {
3402 if (isspace(c) || c ==
'=')
3416 char semicolon =
';';
3433 debug(
"gwlib.http", 0,
"Dumping HTTP headers:");
3434 for (i = 0; headers != NULL && i <
gwlist_len(headers); ++i)
3436 debug(
"gwlib.http", 0,
"End of dump.");
3449 debug(
"gwlib.http", 0,
"Dumping %ld cgi variables:", len);
3450 for (i = 0; i < len; i++) {
3455 debug(
"gwlib.http", 0,
"End of dump.");
3539 for (pos = 0; pos < len; pos++) {
3550 for (pos++; pos > 0 && pos < len &&
found == 0; pos++) {
3631 conn_shutdown_ssl();
3632 server_shutdown_ssl();
3646 if (code < 100 || code >= 600)
void parse_cgivars(List *cgivars, Octstr *pairs)
Dict * dict_create(long size_hint, void(*destroy_value)(void *))
static Connection * get_connection(HTTPServer *trans)
static void read_chunked_body_trailer(HTTPEntity *ent, Connection *conn)
Octstr * conn_read_line(Connection *conn)
char * http_method2name(int method)
void error(int err, const char *fmt,...)
static List * active_connections
void info(int err, const char *fmt,...)
static List * pending_requests
static void port_set_timeout(int port, long timeout)
static void parse2trans(HTTPURLParse *p, HTTPServer *t)
List * http_header_find_all(List *headers, char *name)
static void server_destroy(void *p)
void http_header_get(List *headers, long i, Octstr **name, Octstr **value)
void * gwlist_search(List *list, void *pattern, int(*cmp)(void *, void *))
static HTTPClient * port_get_request(int port)
Octstr * http_header_find_first_real(List *headers, char *name, const char *file, long line, const char *func)
static long server_thread_id
void http_header_add(List *headers, char *name, char *contents)
static void read_chunked_body_len(HTTPEntity *ent, Connection *conn)
static Octstr * method_name
static void proxy_init(void)
gw_assert(wtls_machine->packet_to_send !=NULL)
void http_caller_signal_shutdown(HTTPCaller *caller)
void dict_put(Dict *dict, Octstr *key, void *value)
void counter_destroy(Counter *counter)
void gwlist_append(List *list, void *item)
void http_close_client(HTTPClient *client)
void http_set_server_timeout(int port, long timeout)
List * http_header_split_auth_value(Octstr *value)
static Mutex * port_mutex
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
static void server_init(void)
void gwlist_produce(List *list, void *item)
static Octstr * get_redirection_location(HTTPServer *trans)
long gwlist_len(List *list)
int conn_is_connected(Connection *conn)
static Connection * conn_pool_get(Octstr *host, int port, int ssl, Octstr *certkeyfile, Octstr *our_host)
static void client(int port)
Octstr * octstr_copy_real(const Octstr *ostr, long from, long len, const char *file, long line, const char *func)
static HTTPCaller * caller
void * gwlist_get(List *list, long pos)
void http_header_combine(List *old_headers, List *new_headers)
int octstr_url_decode(Octstr *ostr)
Connection * conn_open_tcp_nb(Octstr *host, int port, Octstr *our_host)
void octstr_append_char(Octstr *ostr, int ch)
long chunked_body_chunk_len
void http_add_basic_auth(List *headers, Octstr *username, Octstr *password)
void octstr_binary_to_base64(Octstr *ostr)
static Octstr * port_key(int port)
void http_header_get_content_type(List *headers, Octstr **type, Octstr **charset)
static int client_is_persistent(List *headers, int use_version_1_0)
static Octstr * http_interface
static volatile sig_atomic_t client_threads_are_running
unsigned long counter_decrease(Counter *counter)
int http_name2method(Octstr *method)
long http_header_quoted_string_len(Octstr *header, long start)
void octstr_append_cstr(Octstr *ostr, const char *cstr)
void octstr_insert_data(Octstr *ostr, long pos, const char *data, long len)
int conn_eof(Connection *conn)
int gwthread_poll(struct pollfd *fds, long numfds, double timeout)
void octstr_strip_blanks(Octstr *text)
#define octstr_get_cstr(ostr)
#define octstr_copy(ostr, from, len)
long octstr_search_char(const Octstr *ostr, int ch, long pos)
void gwthread_join_every(gwthread_func_t *func)
Octstr * http_request_url(HTTPClient *client)
unsigned long counter_increase(Counter *counter)
void http_header_mark_transformation(List *headers, Octstr *new_body, Octstr *new_type)
static void client_init(void)
static struct pid_list * found
static List * new_server_sockets
static void client_destroy(void *client)
Octstr * http_cgi_variable(List *list, char *name)
static int parse_request_line(int *method, Octstr **url, int *use_version_1_0, Octstr *line)
List * gwlist_search_all(List *list, void *pattern, int(*cmp)(void *, void *))
static void read_chunked_body_data(HTTPEntity *ent, Connection *conn)
static void port_remove(int port)
void http_destroy_headers(List *headers)
static Octstr * build_response(List *headers, Octstr *body)
static regex_t * proxy_exceptions_regex
void gwlist_unlock(List *list)
static void handle_transaction(Connection *conn, void *data)
void http_urlparse_destroy(HTTPURLParse *p)
static int client_read_status(HTTPServer *trans)
void http_start_request(HTTPCaller *caller, int method, Octstr *url, List *headers, Octstr *body, int follow, void *id, Octstr *certkeyfile)
void http_destroy_cgiargs(List *args)
void http_append_headers(List *to, List *from)
static const char * http_reason_phrase(int status)
void http_send_reply(HTTPClient *client, int status, List *headers, Octstr *body)
Octstr * octstr_imm(const char *cstr)
static Mutex * client_thread_lock
static void read_body_until_eof(HTTPEntity *ent, Connection *conn)
static void conn_pool_init(void)
int conn_write(Connection *conn, Octstr *data)
int conn_get_connect_result(Connection *conn)
int http_method(HTTPClient *client)
void octstr_insert(Octstr *ostr1, const Octstr *ostr2, long pos)
void * dict_remove(Dict *dict, Octstr *key)
static FDSet * port_get_fdset(int port)
Counter * counter_create(void)
void http_close_proxy(void)
static void client_reset(HTTPClient *p)
static void port_init(void)
void * gwlist_extract_first(List *list)
static void server_shutdown(void)
static HTTPClient * client_create(int port, Connection *conn, Octstr *ip)
void gwlist_delete(List *list, long pos, long count)
static enum @59 run_status
int conn_get_id(Connection *conn)
static int entity_read(HTTPEntity *ent, Connection *conn)
#define conn_register(conn, fdset, callback, data)
void * dict_get(Dict *dict, Octstr *key)
HTTPClient * http_accept_request(int port, Octstr **client_ip, Octstr **url, List **headers, Octstr **body, List **cgivars)
void http_close_port(int port)
void octstr_delete(Octstr *ostr1, long pos, long len)
void parse_dump(HTTPURLParse *p)
void gwlist_remove_producer(List *list)
enum body_expectation expect_state
Counter * active_consumers
void conn_destroy(Connection *conn)
List * http_create_empty_headers(void)
int octstr_ncompare(const Octstr *ostr1, const Octstr *ostr2, long n)
static int send_request(HTTPServer *trans)
void http_header_pack(List *headers)
static void deduce_body_state(HTTPEntity *ent)
#define octstr_duplicate(ostr)
#define octstr_dump(ostr, level,...)
long octstr_case_search(const Octstr *haystack, const Octstr *needle, long pos)
int http_status_class(int code)
#define http_header_find_first(headers, name)
List * http_header_split_value(Octstr *value)
int conn_register_real(Connection *conn, FDSet *fdset, conn_callback_t callback, void *data, conn_callback_data_destroyer_t *data_destroyer)
#define MAX_HEADER_LENGTH
long gwlist_delete_equal(List *list, void *item)
Octstr * http_get_header_parameter(Octstr *value, Octstr *parameter)
int octstr_case_compare(const Octstr *os1, const Octstr *os2)
static void recover_absolute_uri(HTTPServer *trans, Octstr *loc)
int make_server_socket(int port, const char *interface_name)
void warning(int err, const char *fmt,...)
void http_cgivar_dump_into(List *cgiargs, Octstr *os)
long conn_outbuf_len(Connection *conn)
List * octstr_split_words(const Octstr *ostr)
static List * closed_server_sockets
static int http_something_accepted(List *headers, char *header_name, char *what)
List * clients_with_requests
void * http_receive_result_real(HTTPCaller *caller, int *status, Octstr **final_url, List **headers, Octstr **body, int blocking)
void http_remove_hop_headers(List *headers)
Octstr * octstr_format(const char *fmt,...)
void octstr_destroy(Octstr *ostr)
static int parse_http_version(Octstr *version)
void http_set_interface(const Octstr *our_host)
static Octstr * proxy_username
#define gwthread_create(func, arg)
#define octstr_create(cstr)
void octstr_destroy_item(void *os)
static Mutex * proxy_mutex
unsigned long counter_value(Counter *counter)
int octstr_item_case_match(void *item, void *pattern)
void gwthread_sleep(double seconds)
void mutex_destroy(Mutex *mutex)
static void proxy_add_authentication(List *headers)
const char * content_type
static void port_put_request(HTTPClient *client)
enum HTTPServer::@60 state
static Octstr * proxy_hostname
int http_open_port(int port, int ssl)
#define HTTP_SERVER_MAX_ACTIVE_CONNECTIONS
void fdset_destroy(FDSet *set)
void gwlist_insert(List *list, long pos, void *item)
static char * http_methods[]
void gwlist_lock(List *list)
static List * proxy_exceptions
#define http_receive_result(caller, status, final_url, headers, body)
static void entity_destroy(HTTPEntity *ent)
static int keep_servers_open
int http_open_port_if(int port, int ssl, Octstr *interface)
static void destroy_int_pointer(void *p)
void http_close_all_ports(void)
Octstr * http_header_value(List *headers, Octstr *name)
long octstr_len(const Octstr *ostr)
static void receive_request(Connection *conn, void *data)
void dict_destroy(Dict *dict)
void conn_unregister(Connection *conn)
int http_get_real(int method, Octstr *url, List *request_headers, Octstr **final_url, List **reply_headers, Octstr **reply_body)
int conn_wait(Connection *conn, double seconds)
void * gwlist_consume(List *list)
void http_cgivar_dump(List *cgiargs)
static int http_client_timeout
static void write_request_thread(void *arg)
void http_use_proxy(Octstr *hostname, int port, int ssl, List *exceptions, Octstr *username, Octstr *password, Octstr *exceptions_regex)
static void conn_pool_item_destroy(void *item)
static void conn_pool_shutdown(void)
Octstr * host_ip(struct sockaddr_in addr)
static int response_expectation(int method, int status)
void debug(const char *place, int err, const char *fmt,...)
static void destroy_struct_server(void *p)
void() gwlib_assert_init(void)
#define HTTP_SERVER_TIMEOUT
static int read_some_headers(Connection *conn, List *headers)
static Dict * port_collection
void gwthread_wakeup(long thread)
static Octstr * conn_pool_key(Octstr *host, int port, int ssl, Octstr *certfile, Octstr *our_host)
int octstr_str_compare(const Octstr *ostr, const char *str)
static HTTPEntity * entity_create(enum body_expectation exp)
long http_header_remove_all(List *headers, char *name)
static void start_server_thread(void)
long octstr_parse_long(long *nump, Octstr *ostr, long pos, int base)
HTTPCaller * http_caller_create(void)
void octstr_format_append(Octstr *os, const char *fmt,...)
int http_charset_accepted(List *headers, char *charset)
List * http_header_duplicate(List *headers)
enum HTTPClient::@61 state
static void port_shutdown(void)
static FDSet * client_fdset
void octstr_truncate(Octstr *ostr, int new_len)
static void server_thread(void *dummy)
Octstr * date_format_http(unsigned long unixtime)
HTTPURLParse * parse_url(Octstr *url)
void http_caller_destroy(HTTPCaller *caller)
Octstr * conn_read_fixed(Connection *conn, long length)
static void proxy_shutdown(void)
int conn_error(Connection *conn)
static void read_chunked_body_crlf(HTTPEntity *ent, Connection *conn)
static Mutex * server_thread_lock
void fdset_set_timeout(FDSet *set, long timeout)
int http_type_accepted(List *headers, char *type)
static HTTPServer * server_create(HTTPCaller *caller, int method, Octstr *url, List *headers, Octstr *body, int follow_remaining, Octstr *certkeyfile)
static void read_body_with_length(HTTPEntity *ent, Connection *conn)
static void client_shutdown(void)
void gwlist_add_producer(List *list)
static int response(List *push_headers, Octstr **username, Octstr **password)
void http_set_client_timeout(long timeout)
HTTPURLParse * http_urlparse_create(void)
int octstr_get_char(const Octstr *ostr, long pos)
FDSet * fdset_create_real(long timeout)
static void http_header_add_element(List *list, Octstr *value, long start, long end)
static int header_is_called(Octstr *header, char *name)
void octstr_set_char(Octstr *ostr, long pos, int ch)
static Octstr * build_request(char *method_name, Octstr *path_or_url, Octstr *host, long port, int ssl, List *headers, Octstr *request_body)
List * octstr_split(const Octstr *os, const Octstr *sep)
static Mutex * conn_pool_lock
void http_header_dump(List *headers)
static int port_match(void *client, void *port)
static struct port * port_add(int port)
Octstr * conn_read_everything(Connection *conn)
Connection * conn_wrap_fd(int fd, int ssl)
static volatile sig_atomic_t server_thread_is_running
static int proxy_used_for_host(Octstr *host, Octstr *url)
static Octstr * proxy_password
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
static void start_client_threads(void)
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)