diff options
-rw-r--r-- | src/IO/IO.c | 4 | ||||
-rw-r--r-- | src/IO/http.c | 115 | ||||
-rw-r--r-- | src/cache.c | 129 | ||||
-rw-r--r-- | src/cache.h | 3 | ||||
-rw-r--r-- | src/capi.c | 10 | ||||
-rw-r--r-- | src/decode.c | 24 | ||||
-rw-r--r-- | src/decode.h | 16 |
7 files changed, 226 insertions, 75 deletions
diff --git a/src/IO/IO.c b/src/IO/IO.c index a0a8bba5..43a3ff3a 100644 --- a/src/IO/IO.c +++ b/src/IO/IO.c @@ -350,6 +350,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, switch (Op) { case OpStart: io = IO_new(IOWrite); + io->Info = Info; Info->LocalKey = io; break; case OpSend: @@ -406,9 +407,10 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, IO_submit(io); } break; + case OpEnd: case OpAbort: io = Info->LocalKey; - IO_close_fd(io, IO_StopRdWr); + IO_close_fd(io, Op == OpEnd ? IO_StopRd : IO_StopRdWr); IO_free(io); dFree(Info); break; diff --git a/src/IO/http.c b/src/IO/http.c index 23101ba7..917cb95e 100644 --- a/src/IO/http.c +++ b/src/IO/http.c @@ -79,7 +79,8 @@ typedef struct { typedef struct { char *host; - int active_connections; + Dlist *active_fds; + int avail_fd; SocketQueue_t main_q, image_q; /* imgs have separate lower-priority queue */ } HostConnection_t; @@ -89,6 +90,7 @@ 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); /* @@ -160,8 +162,9 @@ static int Http_sock_new(void) static void Http_connect_queued_sockets(HostConnection_t *hc) { + bool_t all_is_well = TRUE; SocketData_t *sd; - while (hc->active_connections < prefs.http_max_conns && + while (dList_length(hc->active_fds) < prefs.http_max_conns && (sd = Http_socket_dequeue(hc))) { sd->flags &= ~HTTP_SOCKET_QUEUED; @@ -170,18 +173,30 @@ static void Http_connect_queued_sockets(HostConnection_t *hc) dFree(sd); } else if (a_Web_valid(sd->web)) { /* start connecting the socket */ - if (Http_connect_socket(sd->Info) < 0) { + if (hc->avail_fd != -1) { + sd->SockFD = hc->avail_fd; + hc->avail_fd = -1; + } else if (Http_connect_socket(sd->Info) < 0) { ChainLink *Info = sd->Info; + all_is_well = FALSE; MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err)); a_Chain_bfcb(OpAbort, Info, NULL, "Both"); Http_socket_free(VOIDP2INT(Info->LocalKey)); /* free sd */ dFree(Info); - } else { + } + if (all_is_well) { + 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++; + dList_append(hc->active_fds, (void *)sd->SockFD); } } } + if (hc->avail_fd != -1) { + dClose(hc->avail_fd); + hc->avail_fd = -1; + } } /* @@ -199,9 +214,9 @@ static void Http_socket_free(int SKey) } else { if (S->connected_to) { HostConnection_t *hc = Http_host_connection_get(S->connected_to); - hc->active_connections--; + dList_remove_fast(hc->active_fds, (void *)S->SockFD); Http_connect_queued_sockets(hc); - if (hc->active_connections == 0) + if (dList_length(hc->active_fds) == 0) Http_host_connection_remove(hc); } dFree(S); @@ -275,6 +290,9 @@ 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 = + dStrAsciiCasecmp(URL_SCHEME(url), "http") == 0 ? "keep-alive" : "close"; + if (use_proxy) { dStr_sprintfa(request_uri, "%s%s", URL_STR(url), @@ -309,15 +327,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 +351,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); @@ -447,9 +465,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 */ } } @@ -618,6 +633,28 @@ static int Http_get(ChainLink *Info, void *Data1) return 0; } +static void Http_make_fd_available(int fd) +{ + int i, j; + + for (i = 0; i < dList_length(host_connections); i++) { + HostConnection_t *hc = + (HostConnection_t*) dList_nth_data(host_connections, i); + + for (j = 0; j < dList_length(hc->active_fds); j++) { + void *data = dList_nth_data(hc->active_fds, j); + + if (fd == VOIDP2INT(data)) { + _MSG("host %s ", hc->host); + _MSG("Shift fd %d from active to avail\n", fd); + dList_remove_fast (hc->active_fds, data); + hc->avail_fd = fd; + return; + } + } + } +} + /* * CCC function for the HTTP module */ @@ -665,6 +702,51 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info, 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); + 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")) { + Info->LocalKey = (void *)*(int*)Data1; + a_Chain_bcb(OpSend, Info, Data1, Data2); + } else if (!strcmp(Data2, "reply_complete")) { + int fd = VOIDP2INT(Info->LocalKey); + + Http_make_fd_available(fd); + a_Chain_bfcb(OpEnd, Info, NULL, NULL); + dFree(Info); + } + } + break; + case OpAbort: + a_Chain_bcb(OpAbort, Info, NULL, NULL); + dFree(Info); + break; + default: + MSG_WARN("Unused CCC\n"); + break; + } + } } } @@ -726,6 +808,8 @@ static HostConnection_t *Http_host_connection_get(const char *host) Http_socket_queue_init(&hc->main_q); Http_socket_queue_init(&hc->image_q); hc->host = dStrdup(host); + hc->active_fds = dList_new(prefs.http_max_conns); + hc->avail_fd = -1; dList_append(host_connections, hc); return hc; @@ -736,6 +820,7 @@ static void Http_host_connection_remove(HostConnection_t *hc) assert(hc->main_q.head == NULL && hc->image_q.head == NULL); dList_remove_fast(host_connections, hc); dFree(hc->host); + dList_free(hc->active_fds); dFree(hc); } diff --git a/src/cache.c b/src/cache.c index c4bc1f9b..4b216c55 100644 --- a/src/cache.c +++ b/src/cache.c @@ -55,7 +55,7 @@ typedef struct { Dstr *Data; /* Pointer to raw data */ Dstr *UTF8Data; /* Data after charset translation */ int DataRefcount; /* Reference count */ - Decode *TransferDecoder; /* Transfer decoder (e.g., chunked) */ + DecodeTransfer *TransferDecoder; /* Transfer decoder (e.g., chunked) */ Decode *ContentDecoder; /* Data decoder (e.g., gzip) */ Decode *CharsetDecoder; /* Translates text to UTF-8 encoding */ int ExpectedSize; /* Goal size of the HTTP transfer (0 if unknown)*/ @@ -205,7 +205,7 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url) NewEntry->CharsetDecoder = NULL; NewEntry->ExpectedSize = 0; NewEntry->TransferSize = 0; - NewEntry->Flags = CA_IsEmpty; + NewEntry->Flags = CA_IsEmpty | CA_KeepAlive; } /* @@ -308,7 +308,7 @@ static void Cache_entry_free(CacheEntry_t *entry) if (entry->CharsetDecoder) a_Decode_free(entry->CharsetDecoder); if (entry->TransferDecoder) - a_Decode_free(entry->TransferDecoder); + a_Decode_transfer_free(entry->TransferDecoder); if (entry->ContentDecoder) a_Decode_free(entry->ContentDecoder); dFree(entry); @@ -652,7 +652,8 @@ static Dlist *Cache_parse_multiple_fields(const char *header, static void Cache_parse_header(CacheEntry_t *entry) { char *header = entry->Header->str; - char *Length, *Type, *location_str, *encoding; + bool_t server1point0 = !strncmp(entry->Header->str, "HTTP/1.0", 8); + char *Length, *Type, *location_str, *encoding, *connection; #ifndef DISABLE_COOKIES Dlist *Cookies; #endif @@ -716,6 +717,17 @@ static void Cache_parse_header(CacheEntry_t *entry) dList_free(warnings); } + if (server1point0) + entry->Flags &= ~CA_KeepAlive; + + if ((connection = Cache_parse_field(header, "Connection"))) { + if (!dStrAsciiCasecmp(connection, "close")) + entry->Flags &= ~CA_KeepAlive; + else if (server1point0 && !dStrAsciiCasecmp(connection, "keep-alive")) + entry->Flags |= CA_KeepAlive; + dFree(connection); + } + /* * Get Transfer-Encoding and initialize decoder */ @@ -834,6 +846,53 @@ static int Cache_get_header(CacheEntry_t *entry, return 0; } +static void Cache_finish_msg(CacheEntry_t *entry) +{ + if (entry->Flags & CA_GotData) { + /* already finished */ + return; + } + + if ((entry->ExpectedSize || entry->TransferSize) && + entry->TypeHdr == NULL) { + MSG_HTTP("Message with a body lacked Content-Type header.\n"); + } + if ((entry->Flags & CA_GotLength) && + (entry->ExpectedSize != entry->TransferSize)) { + MSG_HTTP("Content-Length does NOT match message body at\n" + "%s\n", URL_STR_(entry->Url)); + MSG("Expected size: %d, Transfer size: %d\n", + entry->ExpectedSize, entry->TransferSize); + } + if (!entry->TransferSize && !(entry->Flags & CA_Redirect) && + (entry->Flags & WEB_RootUrl)) { + char *eol = strchr(entry->Header->str, '\n'); + if (eol) { + char *status_line = dStrndup(entry->Header->str, + eol - entry->Header->str); + MSG_HTTP("Body was empty. Server sent status: %s\n", status_line); + dFree(status_line); + } + } + entry->Flags |= CA_GotData; + entry->Flags &= ~CA_Stopped; /* it may catch up! */ + if (entry->TransferDecoder) { + a_Decode_transfer_free(entry->TransferDecoder); + entry->TransferDecoder = NULL; + } + if (entry->ContentDecoder) { + a_Decode_free(entry->ContentDecoder); + entry->ContentDecoder = NULL; + } + dStr_fit(entry->Data); /* fit buffer size! */ + + if ((entry = Cache_process_queue(entry))) { + if (entry->Flags & CA_GotHeader) { + Cache_unref_data(entry); + } + } +} + /* * Receive new data, update the reception buffer (for next read), update the * cache, and service the client queue. @@ -842,16 +901,17 @@ static int Cache_get_header(CacheEntry_t *entry, * 'Op' is the operation to perform * 'VPtr' is a (void) pointer to the IO control structure */ -void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, - const DilloUrl *Url) +bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, + const DilloUrl *Url) { int offset, len; const char *str; Dstr *dstr1, *dstr2, *dstr3; + bool_t done = FALSE; CacheEntry_t *entry = Cache_entry_search(Url); /* Assert a valid entry (not aborted) */ - dReturn_if_fail (entry != NULL); + dReturn_val_if_fail (entry != NULL, FALSE); _MSG("__a_Cache_process_dbuf__\n"); @@ -875,7 +935,8 @@ void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, /* Decode arrived data (<= 3 stages) */ if (entry->TransferDecoder) { - dstr1 = a_Decode_process(entry->TransferDecoder, str, len); + dstr1 = a_Decode_transfer_process(entry->TransferDecoder, str,len); + done = a_Decode_transfer_finished(entry->TransferDecoder); str = dstr1->str; len = dstr1->len; } @@ -896,51 +957,27 @@ void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, if (entry->Data->len) entry->Flags &= ~CA_IsEmpty; - entry = Cache_process_queue(entry); - } - } else if (Op == IOClose) { - if ((entry->ExpectedSize || entry->TransferSize) && - entry->TypeHdr == NULL) { - MSG_HTTP("Message with a body lacked Content-Type header.\n"); - } - if ((entry->Flags & CA_GotLength) && - (entry->ExpectedSize != entry->TransferSize)) { - MSG_HTTP("Content-Length does NOT match message body at\n" - "%s\n", URL_STR_(entry->Url)); - MSG("Expected size: %d, Transfer size: %d\n", - entry->ExpectedSize, entry->TransferSize); - } - if (!entry->TransferSize && !(entry->Flags & CA_Redirect) && - (entry->Flags & WEB_RootUrl)) { - char *eol = strchr(entry->Header->str, '\n'); - if (eol) { - char *status_line = dStrndup(entry->Header->str, - eol - entry->Header->str); - MSG_HTTP("Body was empty. Server sent status: %s\n", status_line); - dFree(status_line); + if ((entry->Flags & CA_GotLength) && + (entry->TransferSize >= entry->ExpectedSize)) { + done = TRUE; } - } - entry->Flags |= CA_GotData; - entry->Flags &= ~CA_Stopped; /* it may catch up! */ - if (entry->TransferDecoder) { - a_Decode_free(entry->TransferDecoder); - entry->TransferDecoder = NULL; - } - if (entry->ContentDecoder) { - a_Decode_free(entry->ContentDecoder); - entry->ContentDecoder = NULL; - } - dStr_fit(entry->Data); /* fit buffer size! */ - - if ((entry = Cache_process_queue(entry))) { - if (entry->Flags & CA_GotHeader) { - Cache_unref_data(entry); + if (!(entry->Flags & CA_KeepAlive)) { + /* Let IOClose finish it later */ + done = FALSE; } + + entry = Cache_process_queue(entry); + + if (entry && done) + Cache_finish_msg(entry); } + } else if (Op == IOClose) { + Cache_finish_msg(entry); } else if (Op == IOAbort) { /* unused */ MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n"); } + return done; } /* diff --git a/src/cache.h b/src/cache.h index c39e4600..f3b064f2 100644 --- a/src/cache.h +++ b/src/cache.h @@ -33,6 +33,7 @@ extern "C" { #define CA_InternalUrl 0x800 /* URL content is generated by dillo */ #define CA_HugeFile 0x1000 /* URL content is too big */ #define CA_IsEmpty 0x2000 /* True until a byte of content arrives */ +#define CA_KeepAlive 0x4000 typedef struct CacheClient CacheClient_t; @@ -67,7 +68,7 @@ const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype, const char *from); uint_t a_Cache_get_flags(const DilloUrl *url); uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url); -void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, +bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, const DilloUrl *Url); int a_Cache_download_enabled(const DilloUrl *url); void a_Cache_entry_remove_by_url(DilloUrl *url); @@ -714,7 +714,10 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info, Capi_conn_ref(conn); Info->LocalKey = conn; conn->InfoRecv = Info; - a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2); + if (strcmp(conn->server, "http") == 0) + a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Http_ccc, 2, 2); + else + a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2); a_Chain_bcb(OpStart, Info, NULL, Data2); break; case OpSend: @@ -744,7 +747,10 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info, if (strcmp(Data2, "send_page_2eof") == 0) { /* Data1 = dbuf */ DataBuf *dbuf = Data1; - a_Cache_process_dbuf(IORead, dbuf->Buf, dbuf->Size, conn->url); + bool_t finished = a_Cache_process_dbuf(IORead, dbuf->Buf, + dbuf->Size, conn->url); + if (finished) + a_Chain_bcb(OpSend, Info, NULL, "reply_complete"); } else if (strcmp(Data2, "send_status_message") == 0) { a_UIcmd_set_msg(conn->bw, "%s", Data1); } else if (strcmp(Data2, "chat") == 0) { diff --git a/src/decode.c b/src/decode.c index 53a0d621..76dd6ef7 100644 --- a/src/decode.c +++ b/src/decode.c @@ -21,9 +21,10 @@ static const int bufsize = 8*1024; /* - * Decode chunked data + * Decode 'Transfer-Encoding: chunked' data */ -static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen) +Dstr *a_Decode_transfer_process(DecodeTransfer *dc, const char *instr, + int inlen) { char *inputPtr, *eol; int inputRemaining; @@ -66,6 +67,7 @@ static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen) } if (!(chunkRemaining = strtol(inputPtr, NULL, 0x10))) { + dc->finished = TRUE; break; /* A chunk length of 0 means we're done! */ } inputRemaining -= (eol - inputPtr) + 1; @@ -80,10 +82,16 @@ static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen) return output; } -static void Decode_chunked_free(Decode *dc) +bool_t a_Decode_transfer_finished(DecodeTransfer *dc) +{ + return dc->finished; +} + +void a_Decode_transfer_free(DecodeTransfer *dc) { dFree(dc->state); dStr_free(dc->leftover, 1); + dFree(dc); } static void Decode_compression_free(Decode *dc) @@ -280,19 +288,17 @@ static void Decode_charset_free(Decode *dc) /* * Initialize transfer decoder. Currently handles "chunked". */ -Decode *a_Decode_transfer_init(const char *format) +DecodeTransfer *a_Decode_transfer_init(const char *format) { - Decode *dc = NULL; + DecodeTransfer *dc = NULL; if (format && !dStrAsciiCasecmp(format, "chunked")) { int *chunk_remaining = dNew(int, 1); *chunk_remaining = 0; - dc = dNew(Decode, 1); + dc = dNew(DecodeTransfer, 1); dc->leftover = dStr_new(""); dc->state = chunk_remaining; - dc->decode = Decode_chunked; - dc->free = Decode_chunked_free; - dc->buffer = NULL; /* not used */ + dc->finished = FALSE; _MSG("chunked!\n"); } return dc; diff --git a/src/decode.h b/src/decode.h index 279807a6..06c987f6 100644 --- a/src/decode.h +++ b/src/decode.h @@ -15,7 +15,21 @@ typedef struct Decode { void (*free) (struct Decode *dc); } Decode; -Decode *a_Decode_transfer_init(const char *format); +/* I'm not going to shoehorn the decoders into the same form anymore. They + * can evolve independently. + */ +typedef struct DecodeTransfer { + Dstr *leftover; + void *state; + bool_t finished; /* has the terminating chunk been seen? */ +} DecodeTransfer; + +DecodeTransfer *a_Decode_transfer_init(const char *format); +Dstr *a_Decode_transfer_process(DecodeTransfer *dc, const char *instr, + int inlen); +bool_t a_Decode_transfer_finished(DecodeTransfer *dc); +void a_Decode_transfer_free(DecodeTransfer *dc); + Decode *a_Decode_content_init(const char *format); Decode *a_Decode_charset_init(const char *format); Dstr *a_Decode_process(Decode *dc, const char *instr, int inlen); |