[PATCH] http client timeouts part1 (v4)
Alexander Malysh <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi, attached you can find a final version of this patch. This patch was tested with all possible stress tests and production systsem. For full description what this patch does: http://www.mail-archive.com/devel%40kannel.org/msg03831.html Changes since v3: - Fixed deadlock on conn_pool_lock mutex (Thanks to Enver for testing!!!). Comments/votes please! Btw. no one except Enver ALTIN tested this patch? P.S. If no objection to this patch, I will commit it monday. -- Thanks, Alex
http-client-timeout-part1-v4.patch
(text/x-diff, 25.3 KB)
Index: gwlib/conn.c
===================================================================
RCS file: /home/cvs/gateway/gwlib/conn.c,v
retrieving revision 1.72
diff -a -u -p -r1.72 conn.c
--- gwlib/conn.c 18 Feb 2004 16:52:33 -0000 1.72
+++ gwlib/conn.c 22 Nov 2004 00:01:26 -0000
@@ -139,6 +139,7 @@ struct Connection
FDSet *registered;
conn_callback_t *callback;
void *callback_data;
+ conn_callback_data_destroyer_t *callback_data_destroyer;
/* Protected by inlock */
int listening_pollin;
/* Protected by outlock */
@@ -166,7 +167,7 @@ static void unlocked_register_pollout(Co
#define unlock_out(conn) unlock_out_real(conn, __FILE__, __LINE__, __func__)
/* Lock a Connection's read direction, if the Connection is unclaimed */
-static void lock_in(Connection *conn)
+static void inline lock_in(Connection *conn)
{
gw_assert(conn != NULL);
@@ -177,23 +178,21 @@ static void lock_in(Connection *conn)
}
/* Unlock a Connection's read direction, if the Connection is unclaimed */
-static void unlock_in_real(Connection *conn, char *file, int line, const char *func)
+static void inline unlock_in_real(Connection *conn, char *file, int line, const char *func)
{
int ret;
gw_assert(conn != NULL);
- if (!conn->claimed) {
- if ((ret = mutex_unlock(conn->inlock)) != 0) {
- panic(0, "%s:%ld: %s: Mutex unlock failed. " \
- "(Called from %s:%ld:%s.)", \
- __FILE__, (long) __LINE__, __func__, \
- file, (long) line, func);
- }
- }
+ if (!conn->claimed && (ret = mutex_unlock(conn->inlock)) != 0) {
+ panic(0, "%s:%ld: %s: Mutex unlock failed. "
+ "(Called from %s:%ld:%s.)",
+ __FILE__, (long) __LINE__, __func__,
+ file, (long) line, func);
+ }
}
/* Lock a Connection's write direction, if the Connection is unclaimed */
-static void lock_out(Connection *conn)
+static void inline lock_out(Connection *conn)
{
gw_assert(conn != NULL);
@@ -204,29 +203,27 @@ static void lock_out(Connection *conn)
}
/* Unlock a Connection's write direction, if the Connection is unclaimed */
-static void unlock_out_real(Connection *conn, char *file, int line, const char *func)
+static void inline unlock_out_real(Connection *conn, char *file, int line, const char *func)
{
int ret;
gw_assert(conn != NULL);
- if (!conn->claimed) {
- if ((ret = mutex_unlock(conn->outlock)) != 0) {
- panic(0, "%s:%ld: %s: Mutex unlock failed. " \
- "(Called from %s:%ld:%s.)", \
- __FILE__, (long) __LINE__, __func__, \
- file, (long) line, func);
- }
- }
+ if (!conn->claimed && (ret = mutex_unlock(conn->outlock)) != 0) {
+ panic(0, "%s:%ld: %s: Mutex unlock failed. "
+ "(Called from %s:%ld:%s.)",
+ __FILE__, (long) __LINE__, __func__,
+ file, (long) line, func);
+ }
}
/* Return the number of bytes in the Connection's output buffer */
-static long unlocked_outbuf_len(Connection *conn)
+static long inline unlocked_outbuf_len(Connection *conn)
{
return octstr_len(conn->outbuf) - conn->outbufpos;
}
/* Return the number of bytes in the Connection's input buffer */
-static long unlocked_inbuf_len(Connection *conn)
+static long inline unlocked_inbuf_len(Connection *conn)
{
return octstr_len(conn->inbuf) - conn->inbufpos;
}
@@ -551,6 +548,7 @@ Connection *conn_wrap_fd(int fd, int ssl
conn->registered = NULL;
conn->callback = NULL;
conn->callback_data = NULL;
+ conn->callback_data_destroyer = NULL;
conn->listening_pollin = 0;
conn->listening_pollout = 0;
#ifdef HAVE_LIBSSL
@@ -595,8 +593,12 @@ void conn_destroy(Connection *conn)
/* No locking done here. conn_destroy should not be called
* if any thread might still be interested in the connection. */
- if (conn->registered)
+ if (conn->registered) {
fdset_unregister(conn->registered, conn->fd);
+ /* call data destroyer if any */
+ if (conn->callback_data != NULL && conn->callback_data_destroyer != NULL)
+ conn->callback_data_destroyer(conn->callback_data);
+ }
if (conn->fd >= 0) {
/* Try to flush any remaining data */
@@ -674,8 +676,8 @@ int conn_error(Connection *conn)
{
int err;
- lock_in(conn);
lock_out(conn);
+ lock_in(conn);
err = conn->io_error;
unlock_in(conn);
unlock_out(conn);
@@ -721,8 +723,8 @@ static void poll_callback(int fd, int re
* fdset and set the error condition variable to let the upper layer
* close and destroy the connection. */
if (revents & (POLLERR|POLLHUP)) {
- lock_in(conn);
lock_out(conn);
+ lock_in(conn);
if (conn->listening_pollin)
unlocked_register_pollin(conn, 0);
if (conn->listening_pollout)
@@ -756,8 +758,8 @@ static void poll_callback(int fd, int re
conn->callback(conn, conn->callback_data);
}
-int conn_register(Connection *conn, FDSet *fdset,
- conn_callback_t callback, void *data)
+int conn_register_real(Connection *conn, FDSet *fdset,
+ conn_callback_t callback, void *data, conn_callback_data_destroyer_t *data_destroyer)
{
int events;
int result = 0;
@@ -775,7 +777,11 @@ int conn_register(Connection *conn, FDSe
if (conn->registered == fdset) {
/* Re-registering. Change only the callback info. */
conn->callback = callback;
+ /* call data destroyer if new data supplied */
+ if (conn->callback_data != NULL && conn->callback_data != data && conn->callback_data_destroyer != NULL)
+ conn->callback_data_destroyer(conn->callback_data);
conn->callback_data = data;
+ conn->callback_data_destroyer = data_destroyer;
result = 0;
} else if (conn->registered) {
/* Already registered to a different fdset. */
@@ -795,14 +801,15 @@ int conn_register(Connection *conn, FDSe
conn->registered = fdset;
conn->callback = callback;
conn->callback_data = data;
+ conn->callback_data_destroyer = data_destroyer;
conn->listening_pollin = (events & POLLIN) != 0;
conn->listening_pollout = (events & POLLOUT) != 0;
fdset_register(fdset, conn->fd, events, poll_callback, conn);
result = 0;
}
- unlock_out(conn);
unlock_in(conn);
+ unlock_out(conn);
return result;
}
@@ -822,7 +829,11 @@ void conn_unregister(Connection *conn)
fdset_unregister(conn->registered, conn->fd);
conn->registered = NULL;
conn->callback = NULL;
+ /* call data destroyer */
+ if (conn->callback_data != NULL && conn->callback_data_destroyer != NULL)
+ conn->callback_data_destroyer(conn->callback_data);
conn->callback_data = NULL;
+ conn->callback_data_destroyer = NULL;
conn->listening_pollin = 0;
conn->listening_pollout = 0;
}
@@ -1173,12 +1184,13 @@ Octstr *conn_read_packet(Connection *con
X509 *conn_get_peer_certificate(Connection *conn)
{
/* Don't know if it needed to be locked , but better safe as crash */
- lock_in(conn);
lock_out(conn);
+ lock_in(conn);
if (conn->peer_certificate == NULL && conn->ssl != NULL)
conn->peer_certificate = SSL_get_peer_certificate(conn->ssl);
unlock_in(conn);
unlock_out(conn);
+
return conn->peer_certificate;
}
Index: gwlib/conn.h
===================================================================
RCS file: /home/cvs/gateway/gwlib/conn.h,v
retrieving revision 1.29
diff -a -u -p -r1.29 conn.h
--- gwlib/conn.h 16 Feb 2004 19:41:26 -0000 1.29
+++ gwlib/conn.h 22 Nov 2004 00:01:27 -0000
@@ -104,6 +104,14 @@ typedef struct Connection Connection;
* down the polling process. This may be good or bad. */
typedef void conn_callback_t(Connection *conn, void *data);
+/*
+ * If conn_register was called for this connection, a callback data destroyer
+ * function will be called if conn_unregister, conn_destroy or conn_register
+ * (with different data) called for this connection.
+ * This function is responsible to destroy callback data.
+ */
+typedef void conn_callback_data_destroyer_t(void *data);
+
#ifdef HAVE_LIBSSL
/* Open an SSL connection to the given host and port. Same behavior
* as conn_open_tcp() below. 'certkeyfile' specifies a PEM-encoded
@@ -198,10 +206,14 @@ void conn_set_output_buffering(Connectio
* than calling conn_unregister first.
* NOTE: Using conn_register will always mean that the Connection will be
* used by more than one thread, so don't also call conn_claim. */
-int conn_register(Connection *conn, FDSet *fdset,
- conn_callback_t callback, void *data);
+#define conn_register(conn, fdset, callback, data) \
+ conn_register_real(conn, fdset, callback, data, NULL)
+int conn_register_real(Connection *conn, FDSet *fdset,
+ conn_callback_t callback, void *data, conn_callback_data_destroyer_t destroyer);
-/* Remove the current registration. */
+/*
+ * Remove the current registration and call data destroyer if not NULL.
+ */
void conn_unregister(Connection *conn);
/* Block the thread until one of the following is true:
Index: gwlib/http.c
===================================================================
RCS file: /home/cvs/gateway/gwlib/http.c,v
retrieving revision 1.219
diff -a -u -p -r1.219 http.c
--- gwlib/http.c 11 Aug 2004 16:41:29 -0000 1.219
+++ gwlib/http.c 22 Nov 2004 00:01:28 -0000
@@ -601,6 +601,25 @@ static int entity_read(HTTPEntity *ent,
* HTTP client interface.
*/
+/*
+ * Internal lists of completely unhandled requests and requests for which
+ * a request has been sent but response has not yet been read.
+ */
+static List *pending_requests = NULL;
+
+
+/*
+ * Have background threads been started?
+ */
+static Mutex *client_thread_lock = NULL;
+static volatile sig_atomic_t client_threads_are_running = 0;
+
+
+/*
+ * Set of all connections to all servers. Used with conn_register to
+ * do I/O on several connections with a single thread.
+ */
+static FDSet *client_fdset = NULL;
/*
* Maximum number of HTTP redirections to follow. Making this infinite
@@ -629,7 +648,7 @@ typedef struct {
List *request_headers;
Octstr *request_body; /* NULL for GET or HEAD, non-NULL for POST */
enum {
- connecting,
+ connecting,
request_not_sent,
reading_status,
reading_entity,
@@ -707,17 +726,13 @@ static void server_destroy(void *p)
* Pool of open, but unused connections to servers or proxies. Key is
* "servername:port", value is List with Connection objects.
*/
-static Dict *conn_pool = NULL;
-static Mutex *conn_pool_lock = NULL;
+static Dict *conn_pool;
+static Mutex *conn_pool_lock;
static void conn_pool_item_destroy(void *item)
{
- Connection *conn;
-
- while ((conn = list_extract_first(item)) != NULL)
- conn_destroy(conn);
- list_destroy(item, NULL);
+ list_destroy(item, (void(*)(void*))conn_destroy);
}
static void conn_pool_init(void)
@@ -734,7 +749,7 @@ static void conn_pool_shutdown(void)
}
-static Octstr *conn_pool_key(Octstr *host, int port)
+static inline Octstr *conn_pool_key(Octstr *host, int port)
{
return octstr_format("%S:%d", host, port);
}
@@ -744,48 +759,94 @@ static Connection *conn_pool_get(Octstr
Octstr *our_host)
{
Octstr *key;
- List *list;
- Connection *conn;
-
- mutex_lock(conn_pool_lock);
- key = conn_pool_key(host, port);
- list = dict_get(conn_pool, key);
- octstr_destroy(key);
- if (list == NULL)
- conn = NULL;
- else {
- while (1) {
- conn = list_extract_first(list);
- if (conn == NULL)
- break;
- /* Check whether the server has closed the connection while
- * it has been in the pool. */
- conn_wait(conn, 0);
- if (!conn_eof(conn) && !conn_error(conn))
- break;
- conn_destroy(conn);
- }
- }
- mutex_unlock(conn_pool_lock);
+ List *list = NULL;
+ Connection *conn = NULL;
+ int retry = 0;
+
+ do {
+ mutex_lock(conn_pool_lock);
+ key = conn_pool_key(host, port);
+ list = dict_get(conn_pool, key);
+ octstr_destroy(key);
+ if (list != NULL)
+ conn = list_extract_first(list);
+ mutex_unlock(conn_pool_lock);
+ /*
+ * Note: we don't hold conn_pool_lock when we check/destroy/unregister
+ * connection because otherwise we can deadlock! And it's even better
+ * not to delay other threads while we check connection.
+ */
+ if (conn != NULL) {
+#ifdef USE_KEEPALIVE
+ /* unregister our server disconnect callback */
+ conn_unregister(conn);
+#endif
+ /*
+ * Check whether the server has closed the connection while
+ * it has been in the pool.
+ */
+ conn_wait(conn, 0);
+ if (conn_eof(conn) || conn_error(conn)) {
+ conn_destroy(conn);
+ retry = 1;
+ conn = NULL;
+ }
+ }
+ } while(retry == 1);
if (conn == NULL) {
#ifdef HAVE_LIBSSL
- if (ssl)
- conn = conn_open_ssl(host, port, certkeyfile, our_host);
- else
+ if (ssl)
+ conn = conn_open_ssl(host, port, certkeyfile, our_host);
+ else
#endif /* HAVE_LIBSSL */
- conn = conn_open_tcp_nb(host, port, our_host);
- debug("gwlib.http", 0, "HTTP: Opening connection to `%s:%d' (fd=%d).",
- octstr_get_cstr(host), port, conn_get_id(conn));
+ conn = conn_open_tcp_nb(host, port, our_host);
+ debug("gwlib.http", 0, "HTTP: Opening connection to `%s:%d' (fd=%d).",
+ octstr_get_cstr(host), port, conn_get_id(conn));
} else {
- debug("gwlib.http", 0, "HTTP: Reusing connection to `%s:%d' (fd=%d).",
- octstr_get_cstr(host), port, conn_get_id(conn));
+ debug("gwlib.http", 0, "HTTP: Reusing connection to `%s:%d' (fd=%d).",
+ octstr_get_cstr(host), port, conn_get_id(conn));
}
return conn;
}
#ifdef USE_KEEPALIVE
+static void check_pool_conn(Connection *conn, void *data)
+{
+ Octstr *key = data;
+
+ if (run_status != running) {
+ conn_unregister(conn);
+ return;
+ }
+ /* check if connection still ok */
+ conn_wait(conn, 0);
+ if (conn_error(conn) || conn_eof(conn)) {
+ List *list;
+ mutex_lock(conn_pool_lock);
+ list = dict_get(conn_pool, key);
+ if (list_delete_equal(list, conn) > 0) {
+ /*
+ * ok, connection was still within pool. So it's
+ * safe to destroy this connection.
+ */
+ debug("gwlib.http", 0, "HTTP: Server closed connection, destroying it <%s><%p>.",
+ octstr_get_cstr(key), conn);
+ /* implicit conn_unregister */
+ conn_destroy(conn);
+ }
+ /*
+ * it's perfectly valid if connection was not found in connection pool because
+ * in 'conn_pool_get' we first removed connection from pool with conn_pool_lock locked
+ * and then check connection for errors with conn_pool_lock unlocked. In the meantime
+ * fdset's poller may call us. So just ignore such "dummy" call.
+ */
+ mutex_unlock(conn_pool_lock);
+ }
+}
+
+
static void conn_pool_put(Connection *conn, Octstr *host, int port)
{
Octstr *key;
@@ -799,33 +860,13 @@ static void conn_pool_put(Connection *co
dict_put(conn_pool, key, list);
}
list_append(list, conn);
- octstr_destroy(key);
+ /* register connection to get server disconnect */
+ conn_register_real(conn, client_fdset, check_pool_conn, key, octstr_destroy_item);
mutex_unlock(conn_pool_lock);
}
#endif
-/*
- * Internal lists of completely unhandled requests and requests for which
- * a request has been sent but response has not yet been read.
- */
-static List *pending_requests = NULL;
-
-
-/*
- * Have background threads been started?
- */
-static Mutex *client_thread_lock = NULL;
-static volatile sig_atomic_t client_threads_are_running = 0;
-
-
-/*
- * Set of all connections to all servers. Used with conn_register to
- * do I/O on several connections with a single thread.
- */
-static FDSet *client_fdset = NULL;
-
-
HTTPCaller *http_caller_create(void)
{
HTTPCaller *caller;
@@ -1025,7 +1066,7 @@ static void handle_transaction(Connectio
#ifdef DUMP_RESPONSE
/* Dump the response */
- debug("wsp.http", 0, "HTTP: Received response:");
+ debug("gwlib.http", 0, "HTTP: Received response:");
h = build_response(trans->response->headers, trans->response->body);
octstr_dump(h, 0);
octstr_destroy(h);
@@ -1051,11 +1092,11 @@ static void handle_transaction(Connectio
if (trans->persistent) {
if (proxy_used_for_host(trans->host))
conn_pool_put(trans->conn, proxy_hostname, proxy_port);
- else
+ else
conn_pool_put(trans->conn, trans->host, trans->port);
} else
#endif
- conn_destroy(trans->conn);
+ conn_destroy(trans->conn);
trans->conn = NULL;
@@ -1422,13 +1463,11 @@ static void parse2trans(HTTPURLParse *p,
static Connection *get_connection(HTTPServer *trans)
{
- Connection *conn;
+ Connection *conn = NULL;
Octstr *host;
HTTPURLParse *p;
int port;
- conn = NULL;
-
/* if the parsing has not yet been done, then do it now */
if (!trans->host && trans->port == 0 && trans->url != NULL) {
if ((p = parse_url(trans->url)) != NULL) {
@@ -1447,26 +1486,17 @@ static Connection *get_connection(HTTPSe
port = trans->port;
}
- if (trans->retrying) {
-#ifdef HAVE_LIBSSL
- if (trans->ssl) conn = conn_open_ssl(host, port, trans->certkeyfile, http_interface);
- else
-#endif /* HAVE_LIBSSL */
- conn = conn_open_tcp_nb(host, port, http_interface);
- debug("gwlib.http", 0, "HTTP: Opening NEW connection to `%s:%d' (fd=%d).",
- octstr_get_cstr(host), port, conn_get_id(conn));
- } else
conn = conn_pool_get(host, port, trans->ssl, trans->certkeyfile,
http_interface);
if (conn == NULL)
goto error;
- return conn;
+ return conn;
- error:
- conn_destroy(conn);
- error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));
- return NULL;
+error:
+ conn_destroy(conn);
+ error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));
+ return NULL;
}
@@ -1476,47 +1506,47 @@ static Connection *get_connection(HTTPSe
*/
static int send_request(HTTPServer *trans)
{
- Octstr *request;
+ Octstr *request;
- request = NULL;
+ request = NULL;
- /*
- * we have to assume all values in trans are already set
- * by parse_url() before calling this.
- */
-
- if (trans->username != NULL)
- http_add_basic_auth(trans->request_headers, trans->username,
- trans->password);
-
- if (proxy_used_for_host(trans->host)) {
- proxy_add_authentication(trans->request_headers);
- request = build_request(http_method2name(trans->method),
- trans->url, trans->host, trans->port,
- trans->request_headers,
- trans->request_body);
- } else {
- request = build_request(http_method2name(trans->method), trans->uri,
- trans->host, trans->port,
- trans->request_headers,
- trans->request_body);
- }
+ /*
+ * we have to assume all values in trans are already set
+ * by parse_url() before calling this.
+ */
+
+ if (trans->username != NULL)
+ http_add_basic_auth(trans->request_headers, trans->username,
+ trans->password);
+
+ if (proxy_used_for_host(trans->host)) {
+ proxy_add_authentication(trans->request_headers);
+ request = build_request(http_method2name(trans->method),
+ trans->url, trans->host, trans->port,
+ trans->request_headers,
+ trans->request_body);
+ } else {
+ request = build_request(http_method2name(trans->method), trans->uri,
+ trans->host, trans->port,
+ trans->request_headers,
+ trans->request_body);
+ }
- debug("wsp.http", 0, "HTTP: Sending request:");
+ debug("gwlib.http", 0, "HTTP: Sending request:");
octstr_dump(request, 0);
- if (conn_write(trans->conn, request) == -1)
+ if (conn_write(trans->conn, request) == -1)
goto error;
octstr_destroy(request);
- return 0;
+ return 0;
- error:
- conn_destroy(trans->conn);
- trans->conn = NULL;
+error:
+ conn_destroy(trans->conn);
+ trans->conn = NULL;
octstr_destroy(request);
error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));
- return -1;
+ return -1;
}
@@ -1544,46 +1574,43 @@ static void write_request_thread(void *a
*/
trans->conn = get_connection(trans);
- if (trans->conn == NULL)
- list_produce(trans->caller, trans);
- else {
- if (conn_is_connected(trans->conn) == 0) {
- debug("gwlib.http", 0, "Socket connected at once");
-
- if (trans->method == HTTP_METHOD_POST) {
- /*
- * Add a Content-Length header. Override an existing one, if
- * necessary. We must have an accurate one in order to use the
- * connection for more than a single request.
- */
- http_header_remove_all(trans->request_headers, "Content-Length");
- sprintf(buf, "%ld", octstr_len(trans->request_body));
- http_header_add(trans->request_headers, "Content-Length", buf);
- }
+ if (trans->conn == NULL)
+ list_produce(trans->caller, trans);
+ else if (conn_is_connected(trans->conn) == 0) {
+ debug("gwlib.http", 0, "Socket connected at once");
+
+ if (trans->method == HTTP_METHOD_POST) {
+ /*
+ * Add a Content-Length header. Override an existing one, if
+ * necessary. We must have an accurate one in order to use the
+ * connection for more than a single request.
+ */
+ http_header_remove_all(trans->request_headers, "Content-Length");
+ sprintf(buf, "%ld", octstr_len(trans->request_body));
+ http_header_add(trans->request_headers, "Content-Length", buf);
+ }
/*
* ok, this has to be an GET or HEAD request method then,
* if it contains a body, then this is not HTTP conform, so at
* least warn the user
*/
- else if (trans->request_body != NULL) {
- warning(0, "HTTP: GET or HEAD method request contains body:");
- octstr_dump(trans->request_body, 0);
- }
- if ((rc = send_request(trans)) == 0) {
- trans->state = reading_status;
- conn_register(trans->conn, client_fdset, handle_transaction,
- trans);
+ else if (trans->request_body != NULL) {
+ warning(0, "HTTP: GET or HEAD method request contains body:");
+ octstr_dump(trans->request_body, 0);
+ }
+ if ((rc = send_request(trans)) == 0) {
+ trans->state = reading_status;
+ conn_register(trans->conn, client_fdset, handle_transaction,
+ trans);
} else {
- list_produce(trans->caller, trans);
+ list_produce(trans->caller, trans);
}
- } else { /* Socket not connected, wait for connection */
+ } else { /* Socket not connected, wait for connection */
debug("gwlib.http", 0, "Socket connecting");
trans->state = connecting;
conn_register(trans->conn, client_fdset, handle_transaction, trans);
}
-
- }
}
}
@@ -1609,7 +1636,7 @@ static void start_client_threads(void)
void http_set_interface(const Octstr *our_host)
{
- http_interface = octstr_duplicate(our_host);
+ http_interface = octstr_duplicate(our_host);
}
@@ -2656,7 +2683,7 @@ Octstr *http_header_value(List *headers,
List *http_header_duplicate(List *headers)
{
List *new;
- long i;
+ long i, len;
gwlib_assert_init();
@@ -2664,13 +2691,14 @@ List *http_header_duplicate(List *header
return NULL;
new = http_create_empty_headers();
- for (i = 0; i < list_len(headers); ++i)
+ len = list_len(headers);
+ for (i = 0; i < len; ++i)
list_append(new, octstr_duplicate(list_get(headers, i)));
return new;
}
-#define MAX_HEADER_LENGHT 256
+#define MAX_HEADER_LENGTH 256
/*
* Aggregate header in one (or more) lines with several parameters separated
* by commas, instead of one header per parameter
@@ -2697,7 +2725,7 @@ void http_header_pack(List *headers)
http_header_get(headers, j, &name2, &value2);
if(octstr_case_compare(name, name2) == 0) {
- if(octstr_len(value) + 2 + octstr_len(value2) > MAX_HEADER_LENGHT) {
+ if(octstr_len(value) + 2 + octstr_len(value2) > MAX_HEADER_LENGTH) {
octstr_destroy(name2);
octstr_destroy(value2);
break;
@@ -3278,8 +3306,8 @@ void http_shutdown(void)
run_status = terminating;
- conn_pool_shutdown();
port_shutdown();
+ conn_pool_shutdown();
client_shutdown();
server_shutdown();
proxy_shutdown();