📄 cache.c
字号:
CCC_FWD, cccFunct); cccFunct(OpStart, 1, link, (void *)entry->Url, Web); } else { a_Interface_msg(Web->bw, "ERROR: unsupported protocol"); a_Cache_ccc(OpAbort, 1, entry->CCCQuery, NULL, NULL); } } else { /* Feed our client with cached data */ ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData); Cache_delayed_process_queue(entry); } return ClientKey;}/* * Try finding the url in the cache. If it hits, send the cache contents * from there. If it misses, set up a new connection. * * - 'Web' is an auxiliar data structure with misc. parameters. * - '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 */}/* * Return the pointer to URL 'Data' in the cache. * (We also return its size in 'Size' and take care of redirections) */char *a_Cache_url_read(const DilloUrl *Url, gint *Size){ CacheData_t **stack = NULL; gint stackSize = 0; gint stackMax = 32; CacheData_t *entry = NULL; gint i; do { entry = Cache_entry_search(Url); if (entry && entry->Flags & CA_Redirect && entry->Location) { a_List_add (stack, stackSize, stackMax); stack[stackSize++] = entry; Url = entry->Location; } else break; /* Test for a redirection loop */ for (i = 0; i < stackSize - 1; i++) { if (stack[i] == entry) { g_warning ("Redirect loop for URL: >%s<\n", URL_STR_(Url)); for (i = 0; i < stackSize; i++) stack[i]->Flags |= CA_RedirectLoop; entry = NULL; } } } while (entry); g_free (stack); *Size = (entry) ? entry->ValidSize : 0; return (entry) ? (char *) entry->Data : NULL;}/* * 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; gint 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;}/* * Extract multiple fields from the header. */GList *Cache_parse_multiple_fields(const char *header, const char *fieldname){ gint 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;}/* * Detects 'Content-Type' when the server does not supply one. * It uses the magic(5) logic from file(1). Currently, it * only checks the few mime types that Dillo supports. * * 'Data' is a pointer to the first bytes of the raw data. */static gchar *Cache_get_type_from_data(void *Data, size_t Size){ static gchar *Types[] = { "application/octet-stream", "text/html", "text/plain", "image/gif", "image/png", "image/jpeg", }; gint Type = 0; gchar *p = Data; int i; /* HTML try */ for (i = 0; i < Size && isspace(p[i]); ++i); if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) || (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) || (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) || (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14))) { Type = 1; /* Images */ } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) { Type = 3; } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) { Type = 4; } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) { /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking * at the character representation should be machine independent. */ Type = 5; /* Text */ } else { /* We'll assume ASCII if chars are below 128 (after all, this * is a last resort when the server doesn't send Content-Type) */ for (i = 0; i < Size && i < 256; i++) if ((guchar) p[i] > 127) break; Type = (i < 12 || (guchar) p[i] > 127) ? 0 : 2; } return g_strdup(Types[Type]);}/* * 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; GList *Cookies; if ( header[9] == '3' && header[10] == '0' ) { /* 30x: URL redirection */ entry->Flags |= CA_Redirect; if ( header[11] == '1' ) /* 301 Moved Permanently */ entry->Flags |= CA_ForceRedirect; location_str = Cache_parse_field(header, "Location"); entry->Location = a_Url_new(location_str, URL_STR_(entry->Url), 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->IOVec.iov_base+HdrLen, io->Status-HdrLen); /* Free preallocated buffer */ if (io->Flags & IOFlag_FreeIOVec) g_free(io->IOVec.iov_base); /* Prepare next read */ io->IOVec.iov_base = (char *)entry->Data + entry->ValidSize; io->IOVec.iov_len = entry->TotalSize - entry->ValidSize; io->Flags &= ~IOFlag_FreeIOVec; } 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->IOVec.iov_base+HdrLen, entry->ValidSize); /* Prepare next read */ if (io->Flags & IOFlag_FreeIOVec) g_free(io->IOVec.iov_base); io->IOVec.iov_base = (char *)entry->Data + entry->ValidSize; io->IOVec.iov_len = entry->BuffSize; io->Flags &= ~IOFlag_FreeIOVec; } /* Get Content-Type */ if ( (Type = Cache_parse_field(header, "Content-Type")) == NULL ) { DEBUG_HTTP_MSG("Server didn't send Content-Type in header.\n"); Type = Cache_get_type_from_data(entry->Data, entry->ValidSize); } entry->Type = Type;}/* * 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->IOVec.iov_base; /* 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 */ DEBUG_MSG(2, "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){ DEBUG_HTTP_MSG("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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -