diff -N -c -r -X exclude_files squid-1.0.beta8/src/http.c squid-1.0.beta8.henrik/src/http.c *** squid-1.0.beta8/src/http.c Mon May 20 18:21:19 1996 --- squid-1.0.beta8.henrik/src/http.c Tue May 21 05:39:24 1996 *************** *** 80,85 **** --- 80,135 ---- comm_close(fd); } + /* Build a reply structure from HTTP mime headers */ + void httpParseHeaders(mime, reply) + char *mime; + struct _http_reply *reply; + { + char *headers, *t, *s; + + headers = xstrdup(mime); + t = strtok(headers, "\n"); + while (t) { + s = t + strlen(t); + while (*s == '\r') + *s-- = '\0'; + if (!strncasecmp(t, "HTTP", 4)) { + sscanf(t + 1, "%lf", &reply->version); + if ((t = strchr(t, ' '))) { + t++; + reply->code = atoi(t); + } + } else if (!strncasecmp(t, "Content-type:", 13)) { + if ((t = strchr(t, ' '))) { + t++; + strncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ - 1); + } + } else if (!strncasecmp(t, "Content-length:", 15)) { + if ((t = strchr(t, ' '))) { + t++; + reply->content_length = atoi(t); + } + } else if (!strncasecmp(t, "Date:", 5)) { + if ((t = strchr(t, ' '))) { + t++; + strncpy(reply->date, t, HTTP_REPLY_FIELD_SZ - 1); + } + } else if (!strncasecmp(t, "Expires:", 8)) { + if ((t = strchr(t, ' '))) { + t++; + strncpy(reply->expires, t, HTTP_REPLY_FIELD_SZ - 1); + } + } else if (!strncasecmp(t, "Last-Modified:", 14)) { + if ((t = strchr(t, ' '))) { + t++; + strncpy(reply->last_modified, t, HTTP_REPLY_FIELD_SZ - 1); + } + } + t = strtok(NULL, "\n"); + } + safe_free(headers); + } + /* This object can be cached for a long time */ static void httpMakePublic(entry) StoreEntry *entry; *************** *** 115,126 **** char *buf; /* chunk just read by httpReadReply() */ int size; { - char *s = NULL; char *t = NULL; - char *t1 = NULL; - char *t2 = NULL; StoreEntry *entry = httpState->entry; - char *headers = NULL; int room; int hdr_len; struct _http_reply *reply = NULL; --- 165,172 ---- *************** *** 141,158 **** httpState->reply_hdr_state += 2; return; } ! /* need to take the lowest, non-zero pointer to the end of the headers. ! * some objects have \n\n separating header and body, but \r\n\r\n in ! * body text. */ ! t1 = strstr(httpState->reply_hdr, "\r\n\r\n"); ! t2 = strstr(httpState->reply_hdr, "\n\n"); ! if (t1 && t2) ! t = t2 < t1 ? t2 : t1; ! else ! t = t2 ? t2 : t1; ! if (!t) return; /* headers not complete */ ! t += (t == t1 ? 4 : 2); *t = '\0'; reply = entry->mem_obj->reply; reply->hdr_sz = t - httpState->reply_hdr; --- 187,201 ---- httpState->reply_hdr_state += 2; return; } ! t=mime_headers_end(httpState->reply_hdr); ! if (!t && hdr_len<8191) { return; /* headers not complete */ ! } else if (!t) { ! /* Too much headers received */ ! debug(11, 3, "httpProcessReplyHeader: Too much headers received: '%s'\n", entry->key); ! httpState->reply_hdr_state += 2; ! return; ! } *t = '\0'; reply = entry->mem_obj->reply; reply->hdr_sz = t - httpState->reply_hdr; *************** *** 160,209 **** httpState->reply_hdr_state++; } if (httpState->reply_hdr_state == 1) { ! headers = xstrdup(httpState->reply_hdr); httpState->reply_hdr_state++; debug(11, 9, "GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", httpState->reply_hdr); ! t = strtok(headers, "\n"); ! while (t) { ! s = t + strlen(t); ! while (*s == '\r') ! *s-- = '\0'; ! if (!strncasecmp(t, "HTTP", 4)) { ! sscanf(t + 1, "%lf", &reply->version); ! if ((t = strchr(t, ' '))) { ! t++; ! reply->code = atoi(t); ! } ! } else if (!strncasecmp(t, "Content-type:", 13)) { ! if ((t = strchr(t, ' '))) { ! t++; ! strncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ - 1); ! } ! } else if (!strncasecmp(t, "Content-length:", 15)) { ! if ((t = strchr(t, ' '))) { ! t++; ! reply->content_length = atoi(t); ! } ! } else if (!strncasecmp(t, "Date:", 5)) { ! if ((t = strchr(t, ' '))) { ! t++; ! strncpy(reply->date, t, HTTP_REPLY_FIELD_SZ - 1); ! } ! } else if (!strncasecmp(t, "Expires:", 8)) { ! if ((t = strchr(t, ' '))) { ! t++; ! strncpy(reply->expires, t, HTTP_REPLY_FIELD_SZ - 1); ! } ! } else if (!strncasecmp(t, "Last-Modified:", 14)) { ! if ((t = strchr(t, ' '))) { ! t++; ! strncpy(reply->last_modified, t, HTTP_REPLY_FIELD_SZ - 1); ! } ! } ! t = strtok(NULL, "\n"); ! } ! safe_free(headers); if (reply->code) debug(11, 3, "httpProcessReplyHeader: HTTP CODE: %d\n", reply->code); switch (reply->code) { --- 203,213 ---- httpState->reply_hdr_state++; } if (httpState->reply_hdr_state == 1) { ! httpParseHeaders(httpState->reply_hdr,reply); httpState->reply_hdr_state++; debug(11, 9, "GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", httpState->reply_hdr); ! if (reply->code) debug(11, 3, "httpProcessReplyHeader: HTTP CODE: %d\n", reply->code); switch (reply->code) { diff -N -c -r -X exclude_files squid-1.0.beta8/src/http.h squid-1.0.beta8.henrik/src/http.h *** squid-1.0.beta8/src/http.h Mon May 20 18:21:20 1996 --- squid-1.0.beta8.henrik/src/http.h Tue May 21 03:35:13 1996 *************** *** 31,34 **** --- 31,35 ---- extern int httpCachable _PARAMS((char *, int)); extern int proxyhttpStart _PARAMS((edge *, char *, StoreEntry *)); extern int httpStart _PARAMS((int, char *, request_t *, char *, StoreEntry *)); + extern void httpParseHeaders _PARAMS((char *, struct _http_reply *)); extern void httpProcessReplyHeader _PARAMS((HttpStateData *, char *, int)); diff -N -c -r -X exclude_files squid-1.0.beta8/src/icp.c squid-1.0.beta8.henrik/src/icp.c *** squid-1.0.beta8/src/icp.c Tue May 21 00:20:10 1996 --- squid-1.0.beta8.henrik/src/icp.c Tue May 21 05:23:07 1996 *************** *** 1,6 **** - - - /* $Id: squid-1.0.beta8.alpha.IMS_GET.patch,v 1.1 2000/08/13 23:15:50 hno Exp $ */ /* --- 1,3 ---- *************** *** 97,102 **** --- 94,103 ---- /* Local functions */ static void icpHandleStore _PARAMS((int, StoreEntry *, icpStateData *)); static void icpHandleStoreComplete _PARAMS((int, char *, int, int, icpStateData *)); + static void icpHandleStoreIMS _PARAMS((int, StoreEntry *, icpStateData *)); + static void icpHandleIMSComplete _PARAMS((int, char *, int, int, icpStateData *)); + static int icpProcessHIT _PARAMS((int, icpStateData *)); + static int icpProcessIMS _PARAMS((int, icpStateData *)); static int icpProcessMISS _PARAMS((int, icpStateData *)); static void CheckQuickAbort _PARAMS((icpStateData *)); static void icpRead _PARAMS((int, int, char *, int, int, int, complete_handler, void *)); *************** *** 121,128 **** /* This is a handler normally called by comm_close() */ ! int icpStateFree(fdunused, icpState) ! int fdunused; icpStateData *icpState; { int size = 0; --- 122,129 ---- /* This is a handler normally called by comm_close() */ ! int icpStateFree(fd, icpState) ! int fd; icpStateData *icpState; { int size = 0; *************** *** 155,160 **** --- 156,166 ---- safe_free(icpState->inbuf); safe_free(icpState->url); safe_free(icpState->request_hdr); + if (icpState->entry) { + storeUnregister(icpState->entry, fd); + storeUnlockObject(icpState->entry); + icpState->entry=NULL; + } if (icpState->request && --icpState->request->link_count == 0) safe_free(icpState->request); safe_free(icpState); *************** *** 428,440 **** entry = icpState->entry; icpFreeBufOrPage(icpState); comm_close(fd); - /* If storeAbort() has been called, then we don't execute this. - * If we timed out on the client side, then we need to - * unregister/unlock */ - if (entry) { - storeUnregister(entry, fd); - storeUnlockObject(entry); - } } /* Send ERROR message. */ --- 434,439 ---- *************** *** 597,606 **** CacheInfo->proto_touchobject(CacheInfo, urlParseProtocol(entry->url), icpState->offset); ! /* Now we release the entry and DON'T touch it from here on out */ comm_close(fd); - storeUnregister(entry, fd); - storeUnlockObject(entry); } else if (icpState->offset < entry->mem_obj->e_current_len) { /* More data available locally; write it now */ icpSendMoreData(fd, icpState); --- 596,603 ---- CacheInfo->proto_touchobject(CacheInfo, urlParseProtocol(entry->url), icpState->offset); ! /* And close the connection */ comm_close(fd); } else if (icpState->offset < entry->mem_obj->e_current_len) { /* More data available locally; write it now */ icpSendMoreData(fd, icpState); *************** *** 611,618 **** CacheInfo->proto_id(entry->url), icpState->offset); comm_close(fd); - storeUnregister(entry, fd); - storeUnlockObject(entry); /* unlock after comm_close().. */ } else { /* More data will be coming from primary server; register with * storage manager. */ --- 608,613 ---- *************** *** 620,625 **** --- 615,741 ---- } } + static int icpGetHeadersForIMS(fd, icpState) + int fd; + icpStateData *icpState; + { + StoreEntry *entry = icpState->entry; + char *buf=icpState->buf; + int buf_len=icpState->offset; + int len; + int max_len=8191-buf_len; + char *p=buf+buf_len; + char *IMS_hdr; + time_t IMS; + int IMS_length; + time_t date; + int length; + + if(max_len<=0) { + debug(12,1,"icpGetHeadersForIMS: To much headers '%s'\n",entry->key?entry->key:entry->url); + icpFreeBufOrPage(icpState); + icpState->offset=0; + return icpProcessMISS(fd, icpState); + } + storeClientCopy(entry, icpState->offset, max_len, p, &len, fd); + buf_len = icpState->offset =+ len; + + if(!mime_headers_end(buf)) { + /* All headers are not yet available, wait for more data */ + storeRegister(entry,fd, (PIF)icpHandleStoreIMS, (void *)icpState); + return COMM_OK; + } + + /* All headers are available, check if object is modified or not */ + IMS_hdr=mime_get_header(icpState->request_hdr,"If-Modified-Since"); + httpParseHeaders(buf, entry->mem_obj->reply); + + /* Buf is no longer needed */ + icpFreeBufOrPage(icpState); + + if(!IMS_hdr) { + fatal_dump("icpGetHeadersForIMS: Cant find IMS header in request\n"); + } + + /* Restart the object from the beginning */ + icpState->offset=0; + + /* Only objects with statuscode==200 can be "Not modified" */ + /* XXX: Should we ignore this? */ + if(entry->mem_obj->reply->code != 200) { + return icpProcessMISS(fd, icpState); + } + + p=strtok(IMS_hdr,";"); + IMS=parse_rfc850(p); + IMS_length=-1; + while((p=strtok(NULL,";"))) { + while(*p) + p++; + if(strncasecmp(p,"length=",7)==0) + IMS_length=atoi(strchr(p,'=')+1); + } + + /* Find date when the object last was modified */ + if(*entry->mem_obj->reply->last_modified) + date=parse_rfc850(entry->mem_obj->reply->last_modified); + else if (*entry->mem_obj->reply->date) + date=parse_rfc850(entry->mem_obj->reply->date); + else + date=entry->timestamp; + + /* Find size of the object */ + if(entry->mem_obj->reply->content_length) + length=entry->mem_obj->reply->content_length; + else + length=entry->object_len - mime_headers_size(buf); + + /* Compare with If-Modified-Since header */ + if(IMS>date || (IMS==date && (IMS_length<0 || IMS_length==length))) { + /* The object is not modified */ + debug(12,4,"icpGetHeadersForIMS: Not modified '%s'\n",entry->url); + strcpy(buf,"HTTP/1.0 304 Not modified\n\r\n\r"); + icpWrite(fd, "HTTP/1.0 304 Not modified\r\n\r\n", strlen("HTTP/1.0 304 Not modified\r\n\r\n"), 30, icpHandleIMSComplete, (void *) icpState); + return COMM_OK; + } else { + debug(12,4,"icpGetHeadersForIMS: We have newer '%s'\n",entry->url); + /* We have a newer object */ + return icpProcessHIT(fd, icpState); + } + } + + static void icpHandleStoreIMS(fd,entry,icpState) + int fd; + StoreEntry *entry; + icpStateData *icpState; + { + icpGetHeadersForIMS(fd,icpState); + } + + static void icpHandleIMSComplete(fd, buf, size, errflag, icpState) + int fd; + char *buf; + int size; + int errflag; + icpStateData *icpState; + { + StoreEntry *entry = icpState->entry; + debug(12,5,"icpHandleIMSComplete: Not Modified sent '%s'\n",entry->url); + + /* XXX: What is this? */ + CacheInfo->proto_touchobject(CacheInfo, + CacheInfo->proto_id(entry->url), + strlen(buf)); + + /* Set up everything for the logging */ + storeUnlockObject(icpState->entry); + icpState->entry=NULL; + icpState->size=strlen(buf); + + comm_close(fd); + } + + #ifdef OLD_CODE int icpDoQuery(fd, icpState) int fd; *************** *** 676,736 **** if ((entry = storeGet(pubkey)) == NULL) { /* This object isn't in the cache. We do not hold a lock yet */ icpState->log_type = LOG_TCP_MISS; ! CacheInfo->proto_miss(CacheInfo, CacheInfo->proto_id(url)); ! icpProcessMISS(fd, icpState); ! return; ! } ! /* The object is in the cache, but is it valid? */ ! if (!storeEntryValidToSend(entry)) { storeRelease(entry); icpState->log_type = LOG_TCP_EXPIRED; - } else if (BIT_TEST(icpState->flags, REQ_IMS)) { - /* no storeRelease() here because this request will always - * start private (IMS clears HIERARCHICAL) */ - /* check IMS before nocache so IMS+NOCACHE won't eject valid object */ - icpState->log_type = LOG_TCP_IFMODSINCE; } else if (BIT_TEST(icpState->flags, REQ_NOCACHE)) { ! storeRelease(entry); icpState->log_type = LOG_TCP_USER_REFRESH; ! } else if (storeLockObject(entry, NULL, NULL) < 0) { ! storeRelease(entry); ! icpState->log_type = LOG_TCP_SWAPIN_FAIL; } else { icpState->log_type = LOG_TCP_HIT; } debug(12, 4, "icp_hit_or_miss: %s for '%s'\n", log_tags[icpState->log_type], icpState->url); - switch (icpState->log_type) { - case LOG_TCP_HIT: - /* We HOLD a lock on object "entry" */ - CacheInfo->proto_hit(CacheInfo, CacheInfo->proto_id(entry->url)); ! /* Reset header for reply. */ ! memset(&icpState->header, 0, sizeof(icp_common_t)); ! icpState->header.version = ICP_VERSION_CURRENT; ! /* icpState->header.reqnum = 0; */ ! icpState->header.shostid = 0; ! icpState->entry = entry; ! icpState->offset = 0; ! ! /* Send object to requestor */ ! entry->refcount++; /* HIT CASE */ ! icpSendMoreData(fd, icpState); break; default: - CacheInfo->proto_miss(CacheInfo, CacheInfo->proto_id(url)); icpProcessMISS(fd, icpState); break; } } /* * Prepare to fetch the object as it's a cache miss of some kind. - * The calling client should NOT hold a lock on object at this - * time, as we're about to release any TCP_MISS version of the object. */ static int icpProcessMISS(fd, icpState) int fd; --- 792,888 ---- if ((entry = storeGet(pubkey)) == NULL) { /* This object isn't in the cache. We do not hold a lock yet */ icpState->log_type = LOG_TCP_MISS; ! } else if (!storeEntryValidToSend(entry)) { ! /* The object is in the cache, but is not valid */ ! /* Eject old cached object */ storeRelease(entry); + entry=NULL; icpState->log_type = LOG_TCP_EXPIRED; } else if (BIT_TEST(icpState->flags, REQ_NOCACHE)) { ! /* IMS+NOCACHE should not eject valid object */ ! if(!BIT_TEST(icpState->flags, REQ_IMS)) { ! storeRelease(entry); ! entry=NULL; ! } icpState->log_type = LOG_TCP_USER_REFRESH; ! } else if (BIT_TEST(icpState->flags, REQ_IMS)) { ! /* A cached IMS request */ ! icpState->log_type = LOG_TCP_IFMODSINCE; } else { icpState->log_type = LOG_TCP_HIT; } + /* Lock the object */ + if (entry!=NULL && storeLockObject(entry, NULL, NULL) < 0) { + storeRelease(entry); + entry=NULL; + icpState->log_type = LOG_TCP_SWAPIN_FAIL; + } + + /* Reset header fields for reply. */ + memset(&icpState->header, 0, sizeof(icp_common_t)); + icpState->header.version = ICP_VERSION_CURRENT; + /* icpState->header.reqnum = 0; */ + icpState->header.shostid = 0; + icpState->entry = entry; /* Save a reference to the object */ + icpState->offset = 0; + debug(12, 4, "icp_hit_or_miss: %s for '%s'\n", log_tags[icpState->log_type], icpState->url); ! if(entry!=NULL) { ! CacheInfo->proto_hit(CacheInfo, CacheInfo->proto_id(entry->url)); ! entry->refcount++; ! } else { ! CacheInfo->proto_miss(CacheInfo, CacheInfo->proto_id(url)); ! } ! switch (icpState->log_type) { ! case LOG_TCP_HIT: ! icpProcessHIT(fd, icpState); ! break; ! case LOG_TCP_IFMODSINCE: ! icpProcessIMS(fd, icpState); break; default: icpProcessMISS(fd, icpState); break; } } /* + * Send object as a cache hit + */ + static int icpProcessHIT(fd, icpState) + int fd; + icpStateData *icpState; + { + /* Send object to requestor */ + return icpSendMoreData(fd, icpState); + } + + + /* + * Prepare to respond to a IMS request + * This requires fetching the Last-Modified (or Date) header + * and compare this with the request. + * If the object is unmodified (older, or same date(+size)) + * respond with 304, else process as a HIT + */ + static int icpProcessIMS(fd, icpState) + int fd; + icpStateData *icpState; + { + /* Use buf as a temporary storage for mime headers */ + icpState->buf=xcalloc(8192,1); + /* And fetch headers */ + return icpGetHeadersForIMS(fd, icpState); + } + + + /* * Prepare to fetch the object as it's a cache miss of some kind. */ static int icpProcessMISS(fd, icpState) int fd; *************** *** 758,763 **** --- 910,923 ---- } } #endif + + /* Get rid of any old object reference */ + if (icpState->entry) { + storeUnregister(icpState->entry, fd); + storeUnlockObject(icpState->entry); + icpState->entry=NULL; + } + entry = storeCreateEntry(url, request_hdr, icpState->flags, *************** *** 769,781 **** BIT_SET(entry->flag, IP_LOOKUP_PENDING); storeLockObject(entry, NULL, NULL); - /* Reset header fields for reply. */ - memset(&icpState->header, 0, sizeof(icp_common_t)); - icpState->header.version = ICP_VERSION_CURRENT; - /* icpState->header.reqnum = 0; */ - icpState->header.shostid = 0; icpState->entry = entry; - icpState->offset = 0; /* Register with storage manager to receive updates when data comes in. */ storeRegister(entry, fd, (PIF) icpHandleStore, (void *) icpState); --- 929,935 ---- *************** *** 1613,1622 **** * URL. */ protoUndispatch(fd, icpState->url, entry, icpState->request); comm_close(fd); - if (entry) { - storeUnregister(entry, fd); - storeUnlockObject(entry); - } } /* Handle a new connection on ascii input socket. */ --- 1767,1772 ---- *************** *** 1650,1655 **** --- 1800,1806 ---- icpState->header.shostid = htonl(peer.sin_addr.s_addr); icpState->peer = peer; icpState->me = me; + icpState->entry = NULL; comm_set_select_handler(fd, COMM_SELECT_LIFETIME, (PF) asciiConnLifetimeHandle, *************** *** 1716,1722 **** StoreEntry *entry = icpState->entry; n = read(fd, buf, 256); if (n > 0) { ! debug(12, 0, "icpDetectClientClose: FD %d, %d unexpected bytes\n", fd, n); comm_set_select_handler(fd, COMM_SELECT_READ, --- 1867,1873 ---- StoreEntry *entry = icpState->entry; n = read(fd, buf, 256); if (n > 0) { ! debug(12, 1, "icpDetectClientClose: FD %d, %d unexpected bytes\n", fd, n); comm_set_select_handler(fd, COMM_SELECT_READ, *************** *** 1732,1752 **** CacheInfo->proto_id(entry->url), icpState->offset); comm_close(fd); - storeUnregister(entry, fd); - storeUnlockObject(entry); /* unlock after comm_close().. */ } else { ! debug(12, 1, "icpDetectClientClose: FD %d\n", fd); ! debug(12, 1, "--> URL '%s'\n", icpState->url); if (n < 0) ! debug(12, 1, "--> ERROR %s\n", xstrerror()); CheckQuickAbort(icpState); if (entry && icpState->url) protoUndispatch(fd, icpState->url, entry, icpState->request); icpFreeBufOrPage(icpState); comm_close(fd); - if (entry) { - storeUnregister(entry, fd); - storeUnlockObject(entry); - } } } --- 1883,1901 ---- CacheInfo->proto_id(entry->url), icpState->offset); comm_close(fd); } else { ! debug(12, 2, "icpDetectClientClose: FD %d\n", fd); ! debug(12, 2, "--> URL '%s'\n", icpState->url); ! if(entry) ! debug(12, 2, "--> offset=%d, len=%d\n", icpState->offset,entry->object_len); ! else ! debug(12, 2, "--> offset=%d\n", icpState->offset); if (n < 0) ! debug(12, 1, "icpDetectClientClose: ERROR %s\n", xstrerror()); CheckQuickAbort(icpState); if (entry && icpState->url) protoUndispatch(fd, icpState->url, entry, icpState->request); icpFreeBufOrPage(icpState); comm_close(fd); } } diff -N -c -r -X exclude_files squid-1.0.beta8/src/mime.c squid-1.0.beta8.henrik/src/mime.c *** squid-1.0.beta8/src/mime.c Thu May 9 04:32:00 1996 --- squid-1.0.beta8.henrik/src/mime.c Tue May 21 03:06:11 1996 *************** *** 50,55 **** --- 50,85 ---- return NULL; } + /* need to take the lowest, non-zero pointer to the end of the headers. + * some objects have \n\n separating header and body, but \r\n\r\n in + * body text. */ + char *mime_headers_end(char *mime) + { + char *p1,*p2; + char *end=NULL; + + p1=strstr(mime,"\r\n\r\n"); + p2=strstr(mime,"\n\n"); + + if(p1) + end=p1+4; + if(p2 && p2