📄 cache.c
字号:
* - 'Call' is the callback that receives the data * - 'CbData' is custom data passed to 'Call' * Note: 'Call' and/or 'CbData' can be NULL, in that case they get set * later by a_Web_dispatch_by_type, based on content/type and 'Web' data. * * Return value: A primary key for identifying the client. */gint a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData){ gint ClientKey; DICacheEntry *DicEntry; CacheData_t *entry; DilloWeb *Web = web; DilloUrl *Url = Web->url; if (URL_FLAGS(Url) & URL_E2EReload) { /* Reload operation */ Cache_prepare_reload(Url); } if ( Call ) { /* This is a verbatim request */ ClientKey = Cache_open_url(Web, Call, CbData); } else if ( (DicEntry = a_Dicache_get_entry(Url)) && (entry = Cache_entry_search(Url)) ) { /* We have it in the Dicache! */ ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData); Cache_delayed_process_queue(entry); } else { /* It can be anything; let's request it and decide what to do when we get more info... */ ClientKey = Cache_open_url(Web, Call, CbData); } if (g_slist_find_custom(ClientQueue, GINT_TO_POINTER(ClientKey), Cache_client_key_cmp)) return ClientKey; else return 0; /* Aborted */}/* * Get the pointer to the URL document, and its size, from the cache entry. * Return: 1 cached, 0 not cached. */gint a_Cache_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize){ CacheData_t *entry; while ((entry = Cache_entry_search(Url))) { /* Test for a redirection loop */ if (entry->Flags & CA_RedirectLoop) { g_warning ("Redirect loop for URL: >%s<\n", URL_STR_(Url)); break; } /* Test for a working redirection */ if (entry && entry->Flags & CA_Redirect && entry->Location) { Url = entry->Location; } else break; } *BufSize = (entry) ? entry->ValidSize : 0; *PBuf = (entry) ? (gchar *) entry->Data : NULL; return (entry ? 1 : 0);}/* * Extract a single field from the header, allocating and storing the value * in 'field'. ('fieldname' must not include the trailing ':') * Return a new string with the field-content if found (NULL on error) * (This function expects a '\r' stripped header) */static char *Cache_parse_field(const char *header, const char *fieldname){ char *field; guint i, j; for ( i = 0; header[i]; i++ ) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) if ( tolower(fieldname[j]) != tolower(header[i + j])) break; if ( fieldname[j] ) { /* skip to next line */ for ( i += j; header[i] != '\n'; i++); continue; } i += j; while (header[i] == ' ') i++; if (header[i] == ':' ) { /* Field found! */ while (header[++i] == ' '); for (j = 0; header[i + j] != '\n'; j++); field = g_strndup(header + i, j); return field; } } return NULL;}#ifndef DISABLE_COOKIES/* * Extract multiple fields from the header. */static GList *Cache_parse_multiple_fields(const char *header, const char *fieldname){ guint i, j; GList *fields = NULL; char *field; for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) if (tolower(fieldname[j]) != tolower(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ for (i += j; header[i] != '\n'; i++); continue; } i += j; for ( ; header[i] == ' '; i++); if (header[i] == ':' ) { /* Field found! */ while (header[++i] == ' '); for (j = 0; header[i + j] != '\n'; j++); field = g_strndup(header + i, j); fields = g_list_append(fields, field); } } return fields;}#endif /* !DISABLE_COOKIES *//* * Scan, allocate, and set things according to header info. * (This function needs the whole header to work) */static void Cache_parse_header(CacheData_t *entry, IOData_t *io, gint HdrLen){ gchar *header = entry->Header->str; gchar *Length, *Type, *location_str;#ifndef DISABLE_COOKIES GList *Cookies;#endif if ( HdrLen < 12 ) { /* Not enough info. */ } if ( header[9] == '3' && header[10] == '0' ) { /* 30x: URL redirection */ entry->Flags |= CA_Redirect; if ( header[11] == '1' ) entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */ else if ( header[11] == '2' ) entry->Flags |= CA_TempRedirect; /* 302 Temporal Redirect */ location_str = Cache_parse_field(header, "Location"); entry->Location = a_Url_new(location_str, URL_STR_(entry->Url), 0, 0, 0); g_free(location_str); } else if ( strncmp(header + 9, "404", 3) == 0 ) { entry->Flags |= CA_NotFound; } entry->ValidSize = io->Status - HdrLen; if ( (Length = Cache_parse_field(header, "Content-Length")) != NULL ) { entry->Flags |= CA_GotLength; entry->TotalSize = strtol(Length, NULL, 10); g_free(Length); if (entry->TotalSize < entry->ValidSize) entry->TotalSize = 0; }#ifndef DISABLE_COOKIES /* BUG: If a server feels like mixing Set-Cookie2 and Set-Cookie * responses which aren't identical, then we have a problem. I don't * know if that is a real issue though. */ if ( (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie2")) || (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie")) ) { a_Cookies_set(Cookies, entry->Url); g_list_foreach(Cookies, (GFunc)g_free, NULL); g_list_free(Cookies); }#endif /* !DISABLE_COOKIES */ if ( entry->TotalSize > 0 && entry->TotalSize >= entry->ValidSize ) { entry->Data = g_malloc(entry->TotalSize); memcpy(entry->Data, (char*)io->Buf+HdrLen, (size_t)io->Status-HdrLen); /* Prepare next read */ a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize, entry->TotalSize - entry->ValidSize); } else { /* We don't know the size of the transfer; A lazy server? ;) */ entry->Data = g_malloc(entry->ValidSize + entry->BuffSize); memcpy(entry->Data, (char *)io->Buf+HdrLen, entry->ValidSize); /* Prepare next read */ a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize, entry->BuffSize); } /* Get Content-Type */ if ( (Type = Cache_parse_field(header, "Content-Type")) == NULL ) { MSG_HTTP("Server didn't send Content-Type in header.\n"); } else { entry->TypeHdr = Type; /* This Content-Type is not trusted. It's checked against real data * in Cache_process_queue(); only then CA_GotContentType becomes true. */ }}/* * Consume bytes until the whole header is got (up to a "\r\n\r\n" sequence) * (Also strip '\r' chars from header) */static gint Cache_get_header(IOData_t *io, CacheData_t *entry){ gint N, i; GString *hdr = entry->Header; guchar *data = io->Buf; /* Header finishes when N = 2 */ N = (hdr->len && hdr->str[hdr->len - 1] == '\n'); for ( i = 0; i < io->Status && N < 2; ++i ) { if ( data[i] == '\r' || !data[i] ) continue; N = (data[i] == '\n') ? N + 1 : 0; g_string_append_c(hdr, data[i]); } if ( N == 2 ){ /* Got whole header */ _MSG("Header [io_len=%d]\n%s", i, hdr->str); entry->Flags |= CA_GotHeader; /* Return number of original-header bytes in this io [1 based] */ return i; } return 0;}/* * 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 */static void Cache_process_io(int Op, void *VPtr){ gint Status, len; IOData_t *io = VPtr; const DilloUrl *Url = io->ExtData; CacheData_t *entry = Cache_entry_search(Url); /* Assert a valid entry (not aborted) */ if ( !entry ) return; /* Keep track of this entry's io */ entry->io = io; if ( Op == IOClose ) { if (entry->Flags & CA_GotLength && entry->TotalSize != entry->ValidSize){ MSG_HTTP("Content-Length does NOT match message body,\n" " at: %s\n", URL_STR_(entry->Url)); } entry->Flags |= CA_GotData; entry->Flags &= ~CA_Stopped; /* it may catch up! */ entry->TotalSize = entry->ValidSize; entry->io = NULL; entry->CCCAnswer = NULL; Cache_process_queue(entry); return; } else if ( Op == IOAbort ) { /* todo: implement Abort * (eliminate cache entry and anything related) */ DEBUG_MSG(5, "Cache_process_io Op = IOAbort; not implemented yet\n"); entry->io = NULL; entry->CCCAnswer = NULL; return; } if ( !(entry->Flags & CA_GotHeader) ) { /* Haven't got the whole header yet */ len = Cache_get_header(io, entry); if ( entry->Flags & CA_GotHeader ) { /* Let's scan, allocate, and set things according to header info */ Cache_parse_header(entry, io, len); /* Now that we have it parsed, let's update our clients */ Cache_process_queue(entry); } return; } Status = io->Status; entry->ValidSize += Status; if ( Status < (gint)io->BufSize ) { /* An incomplete buffer; update buffer & size */ a_IO_set_buf(io, (char *)io->Buf + Status, io->BufSize - Status); } else if ( Status == (gint)io->BufSize ) { /* A full buffer! */ if ( !entry->TotalSize ) { /* We are receiving in small chunks... */ entry->Data = g_realloc(entry->Data,entry->ValidSize+entry->BuffSize); a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize, entry->BuffSize); } else { /* We have a preallocated buffer! */ a_IO_set_buf(io, (char *)io->Buf + Status, io->BufSize - Status); } } Cache_process_queue(entry);}/* * Process redirections (HTTP 30x answers) * (This is a work in progress --not finished yet) */static gint Cache_redirect(CacheData_t *entry, gint Flags, BrowserWindow *bw){ DilloUrl *NewUrl; _MSG(" Cache_redirect: redirect_level = %d\n", bw->redirect_level); /* if there's a redirect loop, stop now */ if (bw->redirect_level >= 5) entry->Flags |= CA_RedirectLoop; if (entry->Flags & CA_RedirectLoop) { a_Interface_msg(bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url)); bw->redirect_level = 0; return 0; } if ((entry->Flags & CA_Redirect && entry->Location) && (entry->Flags & CA_ForceRedirect || entry->Flags & CA_TempRedirect || !entry->ValidSize || entry->ValidSize < 1024 )) { _MSG(">>>Redirect from: %s\n to %s\n", URL_STR_(entry->Url), URL_STR_(entry->Location)); _MSG("%s", entry->Header->str); if (Flags & WEB_RootUrl) { /* Redirection of the main page */ NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url), 0, 0, 0); if (entry->Flags & CA_TempRedirect) a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EReload); a_Nav_push(bw, NewUrl); a_Url_free(NewUrl); } else { /* Sub entity redirection (most probably an image) */ if ( !entry->ValidSize ) { DEBUG_MSG(3,">>>Image redirection without entity-content<<<\n"); } else { DEBUG_MSG(3, ">>>Image redirection with entity-content<<<\n"); } } } return 0;}/* * Check whether a URL scheme is downloadable. * Return: 1 enabled, 0 disabled. */int Cache_download_enabled(const DilloUrl *url){ if (!strcasecmp(URL_SCHEME(url), "http") || !strcasecmp(URL_SCHEME(url), "https") || !strcasecmp(URL_SCHEME(url), "ftp") ) return 1; return 0;}/* * Don't process data any further, but let the cache fill the entry. * (Currently used to handle WEB_RootUrl redirects, * and to ignore image redirects --Jcid) */void a_Cache_null_client(int Op, CacheClient_t *Client){ DilloWeb *Web = Client->Web; /* make the stop button insensitive when done */ if (Op == CA_Close) { if (Web->flags & WEB_RootUrl) { /* Remove this client from our active list */ a_Interface_close_client(Web->bw, Client->Key); } } /* else ignore */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -