/* * Consume bytes until the whole header is got (up to a "\r\n\r\n" sequence) * (Also unfold multi-line fields and strip '\r' chars from header) */ static int Cache_get_header(CacheEntry_t *entry, const char *buf, size_t buf_size) { size_t N, i; Dstr *hdr = entry->Header; /* Header finishes when N = 2 */ N = (hdr->len && hdr->str[hdr->len - 1] == '\n'); for (i = 0; i < buf_size && N < 2; ++i) { if (buf[i] == '\r' || !buf[i]) continue; if (N == 1 && (buf[i] == ' ' || buf[i] == '\t')) { /* unfold multiple-line header */ _MSG("Multiple-line header!\n"); dStr_erase(hdr, hdr->len - 1, 1); } N = (buf[i] == '\n') ? N + 1 : 0; dStr_append_c(hdr, buf[i]); } if (N == 2) { /* Got whole header */ _MSG("Header [buf_size=%d]\n%s", i, hdr->str); entry->Flags |= CA_GotHeader; dStr_fit(hdr); /* Return number of header bytes in 'buf' [1 based] */ return i; } return 0; }
/* * Inject full page content directly into the cache. * Used for "about:splash". May be used for "about:cache" too. */ static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds) { CacheEntry_t *entry; if (!(entry = Cache_entry_search(Url))) entry = Cache_entry_add(Url); entry->Flags |= CA_GotData + CA_GotHeader + CA_GotLength + CA_InternalUrl; if (data_ds->len) entry->Flags &= ~CA_IsEmpty; dStr_truncate(entry->Data, 0); dStr_append_l(entry->Data, data_ds->str, data_ds->len); dStr_fit(entry->Data); entry->ExpectedSize = entry->TransferSize = entry->Data->len; }
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); } 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. * * This function gets called whenever the IO has new data. * '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) { int offset, len; const char *str; Dstr *dstr1, *dstr2, *dstr3; CacheEntry_t *entry = Cache_entry_search(Url); /* Assert a valid entry (not aborted) */ dReturn_if_fail (entry != NULL); _MSG("__a_Cache_process_dbuf__\n"); if (Op == IORead) { /* * Cache_get_header() will set CA_GotHeader if it has a full header, and * Cache_parse_header() will unset it if the header ends being * merely an informational response from the server (i.e., 100 Continue) */ for (offset = 0; !(entry->Flags & CA_GotHeader) && (len = Cache_get_header(entry, buf + offset, buf_size - offset)); Cache_parse_header(entry) ) { offset += len; } if (entry->Flags & CA_GotHeader) { str = buf + offset; len = buf_size - offset; entry->TransferSize += len; dstr1 = dstr2 = dstr3 = NULL; /* Decode arrived data (<= 3 stages) */ if (entry->TransferDecoder) { dstr1 = a_Decode_process(entry->TransferDecoder, str, len); str = dstr1->str; len = dstr1->len; } if (entry->ContentDecoder) { dstr2 = a_Decode_process(entry->ContentDecoder, str, len); str = dstr2->str; len = dstr2->len; } dStr_append_l(entry->Data, str, len); if (entry->CharsetDecoder && entry->UTF8Data) { dstr3 = a_Decode_process(entry->CharsetDecoder, str, len); dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len); } dStr_free(dstr1, 1); dStr_free(dstr2, 1); dStr_free(dstr3, 1); 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,\n" " at: %s\n", URL_STR_(entry->Url)); MSG("entry->ExpectedSize = %d, entry->TransferSize = %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_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); } } } else if (Op == IOAbort) { /* unused */ MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n"); } }