aboutsummaryrefslogtreecommitdiff
path: root/src/IO/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/IO/http.c')
-rw-r--r--src/IO/http.c270
1 files changed, 205 insertions, 65 deletions
diff --git a/src/IO/http.c b/src/IO/http.c
index a0021a9e..49b3a3ac 100644
--- a/src/IO/http.c
+++ b/src/IO/http.c
@@ -48,6 +48,8 @@ D_STMT_START { \
#define _MSG_BW(web, root, ...)
+static const int HTTP_PORT = 80;
+
static const int HTTP_SOCKET_USE_PROXY = 0x1;
static const int HTTP_SOCKET_QUEUED = 0x4;
static const int HTTP_SOCKET_TO_BE_FREED = 0x8;
@@ -67,28 +69,23 @@ typedef struct {
/* Data structures and functions to queue sockets that need to be
* delayed due to the per host connection limit.
*/
-typedef struct SocketQueueEntry {
- SocketData_t* sock;
- struct SocketQueueEntry *next ;
-} SocketQueueEntry_t;
-
-typedef struct {
- SocketQueueEntry_t *head;
- SocketQueueEntry_t *tail;
-} SocketQueue_t;
-
typedef struct {
char *host;
- int active_connections;
- SocketQueue_t queue;
+ int active_conns;
+ Dlist *queue;
} HostConnection_t;
-static void Http_socket_queue_init(SocketQueue_t *sq);
-static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock);
-static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq);
+typedef struct {
+ int fd;
+ int skey;
+} FdMapEntry_t;
+
+static void Http_socket_enqueue(HostConnection_t *hc, SocketData_t* sock);
+static SocketData_t* Http_socket_dequeue(HostConnection_t *hc);
static HostConnection_t *Http_host_connection_get(const char *host);
static void Http_host_connection_remove(HostConnection_t *hc);
static int Http_connect_socket(ChainLink *Info);
+static void Http_send_query(ChainLink *Info, SocketData_t *S);
static void Http_socket_free(int SKey);
/*
@@ -101,6 +98,11 @@ static char *HTTP_Proxy_Auth_base64 = NULL;
static char *HTTP_Language_hdr = NULL;
static Dlist *host_connections;
+/* TODO: If fd_map will stick around in its present form (FDs and SocketData_t)
+ * then consider whether having both this and ValidSocks is necessary.
+ */
+static Dlist *fd_map;
+
/*
* Initialize proxy vars and Accept-Language header
*/
@@ -125,6 +127,7 @@ int a_Http_init(void)
*/
host_connections = dList_new(5);
+ fd_map = dList_new(20);
return 0;
}
@@ -155,14 +158,39 @@ void a_Http_set_proxy_passwd(const char *str)
static int Http_sock_new(void)
{
SocketData_t *S = dNew0(SocketData_t, 1);
+ S->SockFD = -1;
return a_Klist_insert(&ValidSocks, S);
}
+/*
+ * Compare by FD.
+ */
+static int Http_fd_map_cmp(const void *v1, const void *v2)
+{
+ int fd = VOIDP2INT(v2);
+ const FdMapEntry_t *e = v1;
+
+ return (fd == e->fd) ? 0 : 1;
+}
+
+/*
+ * Remove and free entry from fd_map.
+ */
+static void Http_fd_map_remove_entry(int fd)
+{
+ void *data = dList_find_custom(fd_map, INT2VOIDP(fd), Http_fd_map_cmp);
+
+ if (data) {
+ dList_remove_fast(fd_map, data);
+ dFree(data);
+ }
+}
+
static void Http_connect_queued_sockets(HostConnection_t *hc)
{
SocketData_t *sd;
- while (hc->active_connections < prefs.http_max_conns &&
- (sd = Http_socket_dequeue(&hc->queue))) {
+ while (hc->active_conns < prefs.http_max_conns &&
+ (sd = Http_socket_dequeue(hc))) {
sd->flags &= ~HTTP_SOCKET_QUEUED;
@@ -177,8 +205,17 @@ static void Http_connect_queued_sockets(HostConnection_t *hc)
Http_socket_free(VOIDP2INT(Info->LocalKey)); /* free sd */
dFree(Info);
} else {
+ FdMapEntry_t *e = dNew0(FdMapEntry_t, 1);
+
+ e->fd = sd->SockFD;
+ e->skey = VOIDP2INT(sd->Info->LocalKey);
+ dList_append(fd_map, e);
+
+ hc->active_conns++;
+ a_Chain_bcb(OpSend, sd->Info, &sd->SockFD, "FD");
+ a_Chain_fcb(OpSend, sd->Info, &sd->SockFD, "FD");
+ Http_send_query(sd->Info, sd);
sd->connected_to = hc->host;
- hc->active_connections++;
}
}
}
@@ -197,11 +234,13 @@ static void Http_socket_free(int SKey)
if (S->flags & HTTP_SOCKET_QUEUED) {
S->flags |= HTTP_SOCKET_TO_BE_FREED;
} else {
+ if (S->SockFD != -1)
+ Http_fd_map_remove_entry(S->SockFD);
if (S->connected_to) {
HostConnection_t *hc = Http_host_connection_get(S->connected_to);
- hc->active_connections--;
+ hc->active_conns--;
Http_connect_queued_sockets(hc);
- if (hc->active_connections == 0)
+ if (hc->active_conns == 0)
Http_host_connection_remove(hc);
}
dFree(S);
@@ -275,6 +314,10 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
web_flags & WEB_Stylesheet ? "text/css,*/*;q=0.1" :
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
+ const char *connection_hdr_val =
+ (prefs.http_persistent_conns == TRUE &&
+ !dStrAsciiCasecmp(URL_SCHEME(url), "http")) ? "keep-alive" : "close";
+
if (use_proxy) {
dStr_sprintfa(request_uri, "%s%s",
URL_STR(url),
@@ -309,15 +352,15 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
"DNT: 1\r\n"
"%s" /* proxy auth */
"%s" /* referer */
- "Connection: close\r\n"
+ "Connection: %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %ld\r\n"
"%s" /* cookies */
"\r\n",
request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
- proxy_auth->str, referer, content_type->str, (long)URL_DATA(url)->len,
- cookies);
+ proxy_auth->str, referer, connection_hdr_val, content_type->str,
+ (long)URL_DATA(url)->len, cookies);
dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
dStr_free(content_type, TRUE);
} else {
@@ -333,13 +376,13 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
"DNT: 1\r\n"
"%s" /* proxy auth */
"%s" /* referer */
- "Connection: close\r\n"
+ "Connection: %s\r\n"
"%s" /* cache control */
"%s" /* cookies */
"\r\n",
request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
- proxy_auth->str, referer,
+ proxy_auth->str, referer, connection_hdr_val,
(URL_FLAGS(url) & URL_E2EQuery) ?
"Pragma: no-cache\r\nCache-Control: no-cache\r\n" : "",
cookies);
@@ -416,7 +459,7 @@ static int Http_connect_socket(ChainLink *Info)
struct sockaddr_in *sin = (struct sockaddr_in *)&name;
socket_len = sizeof(struct sockaddr_in);
sin->sin_family = dh->af;
- sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ sin->sin_port = S->port ? htons(S->port) : htons(HTTP_PORT);
memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
@@ -430,7 +473,7 @@ static int Http_connect_socket(ChainLink *Info)
socket_len = sizeof(struct sockaddr_in6);
sin6->sin6_family = dh->af;
sin6->sin6_port =
- S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ S->port ? htons(S->port) : htons(HTTP_PORT);
memcpy(&sin6->sin6_addr, dh->data, dh->alen);
inet_ntop(dh->af, dh->data, buf, sizeof(buf));
if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
@@ -447,9 +490,6 @@ static int Http_connect_socket(ChainLink *Info)
dClose(S->SockFD);
MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
} else {
- a_Chain_bcb(OpSend, Info, &S->SockFD, "FD");
- a_Chain_fcb(OpSend, Info, &S->SockFD, "FD");
- Http_send_query(S->Info, S);
return 0; /* Success */
}
}
@@ -491,7 +531,6 @@ static int Http_must_use_proxy(const DilloUrl *url)
/*
* Return a new string for the request used to tunnel HTTPS through a proxy.
- * As of 2009, the best reference appears to be section 5 of RFC 2817.
*/
char *a_Http_make_connect_str(const DilloUrl *url)
{
@@ -565,11 +604,11 @@ static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
hc = Http_host_connection_get(URL_HOST(HTTP_Proxy));
else
hc = Http_host_connection_get(URL_HOST(S->web->url));
- Http_socket_enqueue(&hc->queue, S);
+ Http_socket_enqueue(hc, S);
Http_connect_queued_sockets(hc);
} else {
/* DNS wasn't able to resolve the hostname */
- MSG_BW(S->web, 0, "ERROR: Dns can't resolve %s",
+ MSG_BW(S->web, 0, "ERROR: DNS can't resolve %s",
(S->flags & HTTP_SOCKET_USE_PROXY) ? URL_HOST_(HTTP_Proxy) :
URL_HOST_(S->web->url));
a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
@@ -620,6 +659,46 @@ static int Http_get(ChainLink *Info, void *Data1)
}
/*
+ * If any entry in the socket data queue can reuse our connection, set it up
+ * and send off a new query.
+ */
+static void Http_socket_reuse(int SKey)
+{
+ SocketData_t *new_sd, *old_sd = a_Klist_get_data(ValidSocks, SKey);
+ HostConnection_t *hc = Http_host_connection_get(old_sd->connected_to);
+ int i, n = dList_length(hc->queue);
+
+ for (i = 0; i < n; i++) {
+ new_sd = dList_nth_data(hc->queue, i);
+
+ if (a_Web_valid(new_sd->web) && old_sd->port == new_sd->port) {
+ new_sd->SockFD = old_sd->SockFD;
+ Http_fd_map_remove_entry(old_sd->SockFD);
+ a_Klist_remove(ValidSocks, SKey);
+ dFree(old_sd);
+
+ dList_remove(hc->queue, new_sd);
+ new_sd->flags &= ~HTTP_SOCKET_QUEUED;
+ FdMapEntry_t *e = dNew0(FdMapEntry_t, 1);
+ e->fd = new_sd->SockFD;
+ e->skey = VOIDP2INT(new_sd->Info->LocalKey);
+ dList_append(fd_map, e);
+
+ a_Chain_bcb(OpSend, new_sd->Info, &new_sd->SockFD, "FD");
+ a_Chain_fcb(OpSend, new_sd->Info, &new_sd->SockFD, "FD");
+ Http_send_query(new_sd->Info, new_sd);
+ new_sd->connected_to = hc->host;
+ return;
+ }
+ }
+ dClose(old_sd->SockFD);
+ Http_fd_map_remove_entry(old_sd->SockFD);
+ a_Klist_remove(ValidSocks, SKey);
+ hc->active_conns--;
+ dFree(old_sd);
+}
+
+/*
* CCC function for the HTTP module
*/
void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
@@ -648,7 +727,6 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
case OpEnd:
/* finished the HTTP query branch */
a_Chain_bcb(OpEnd, Info, NULL, NULL);
- Http_socket_free(SKey);
dFree(Info);
break;
case OpAbort:
@@ -659,51 +737,97 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
break;
}
} else { /* 1 FWD */
+ SocketData_t *sd;
/* HTTP send-query status branch */
switch (Op) {
+ case OpAbort:
+ if ((sd = a_Klist_get_data(ValidSocks, SKey)))
+ MSG_BW(sd->web, 1, "Can't get %s", URL_STR(sd->web->url));
+ a_Chain_fcb(OpAbort, Info, NULL, "Both");
+ Http_socket_free(SKey);
+ dFree(Info);
+ break;
default:
MSG_WARN("Unused CCC\n");
break;
}
}
+ } else if (Branch == 2) {
+ if (Dir == FWD) {
+ /* Receiving from server */
+ switch (Op) {
+ case OpSend:
+ /* Data1 = dbuf */
+ a_Chain_fcb(OpSend, Info, Data1, "send_page_2eof");
+ break;
+ case OpEnd:
+ a_Chain_fcb(OpEnd, Info, NULL, NULL);
+ Http_socket_free(SKey);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ } else { /* 2 BCK */
+ switch (Op) {
+ case OpStart:
+ a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 2, 2);
+ a_Chain_bcb(OpStart, Info, NULL, NULL); /* IORead */
+ break;
+ case OpSend:
+ if (Data2) {
+ if (!strcmp(Data2, "FD")) {
+ int fd = *(int*)Data1;
+ FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
+ Http_fd_map_cmp);
+ Info->LocalKey = INT2VOIDP(fme->skey);
+ a_Chain_bcb(OpSend, Info, Data1, Data2);
+ } else if (!strcmp(Data2, "reply_complete")) {
+ a_Chain_bfcb(OpEnd, Info, NULL, NULL);
+ Http_socket_reuse(SKey);
+ dFree(Info);
+ }
+ }
+ break;
+ case OpAbort:
+ a_Chain_bcb(OpAbort, Info, NULL, NULL);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ }
}
}
-
-static void Http_socket_queue_init(SocketQueue_t *sq)
-{
- sq->head = NULL;
- sq->tail = NULL;
-}
-
-static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock)
+/*
+ * Add socket data to the queue. Pages/stylesheets/etc. have higher priority
+ * than images.
+ */
+static void Http_socket_enqueue(HostConnection_t *hc, SocketData_t* sock)
{
- SocketQueueEntry_t *se = dNew(SocketQueueEntry_t, 1);
+ if ((sock->web->flags & WEB_Image) == 0) {
+ int i, n = dList_length(hc->queue);
- se->sock = sock;
- se->next = NULL;
+ for (i = 0; i < n; i++) {
+ SocketData_t *curr = dList_nth_data(hc->queue, i);
- if (sq->tail)
- sq->tail->next = se;
- sq->tail = se;
-
- if (! sq->head)
- sq->head = se;
+ if (a_Web_valid(curr->web) && (curr->web->flags & WEB_Image)) {
+ dList_insert_pos(hc->queue, sock, i);
+ return;
+ }
+ }
+ }
+ dList_append(hc->queue, sock);
}
-static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq)
+static SocketData_t* Http_socket_dequeue(HostConnection_t *hc)
{
- SocketQueueEntry_t *se = sq->head;
- SocketData_t *sd = NULL;
-
- if (se) {
- sq->head = se->next;
- if (sq->tail == se)
- sq->tail = NULL;
- sd = se->sock;
- dFree(se);
- }
+ SocketData_t *sd = dList_nth_data(hc->queue, 0);
+ dList_remove(hc->queue, sd);
return sd;
}
@@ -720,7 +844,7 @@ static HostConnection_t *Http_host_connection_get(const char *host)
}
hc = dNew0(HostConnection_t, 1);
- Http_socket_queue_init(&hc->queue);
+ hc->queue = dList_new(10);
hc->host = dStrdup(host);
dList_append(host_connections, hc);
@@ -729,7 +853,8 @@ static HostConnection_t *Http_host_connection_get(const char *host)
static void Http_host_connection_remove(HostConnection_t *hc)
{
- assert(hc->queue.head == NULL);
+ assert(dList_length(hc->queue) == 0);
+ dList_free(hc->queue);
dList_remove_fast(host_connections, hc);
dFree(hc->host);
dFree(hc);
@@ -738,15 +863,29 @@ static void Http_host_connection_remove(HostConnection_t *hc)
static void Http_host_connection_remove_all()
{
HostConnection_t *hc;
+ SocketData_t *sd;
while (dList_length(host_connections) > 0) {
hc = (HostConnection_t*) dList_nth_data(host_connections, 0);
- while (Http_socket_dequeue(&hc->queue));
+ while ((sd = Http_socket_dequeue(hc)))
+ dFree(sd);
Http_host_connection_remove(hc);
}
dList_free(host_connections);
}
+static void Http_fd_map_remove_all()
+{
+ FdMapEntry_t *fme;
+ int i, n = dList_length(fd_map);
+
+ for (i = 0; i < n; i++) {
+ fme = (FdMapEntry_t *) dList_nth_data(fd_map, i);
+ dFree(fme);
+ }
+ dList_free(fd_map);
+}
+
/*
* Deallocate memory used by http module
* (Call this one at exit time)
@@ -754,6 +893,7 @@ static void Http_host_connection_remove_all()
void a_Http_freeall(void)
{
Http_host_connection_remove_all();
+ Http_fd_map_remove_all();
a_Klist_free(&ValidSocks);
a_Url_free(HTTP_Proxy);
dFree(HTTP_Proxy_Auth_base64);