This patch is generated from the icap branch of HEAD in squid Wed Apr 6 02:19:37 2005 GMT See http://devel.squid-cache.org/ Index: squid/configure.in diff -u squid/configure.in:1.76 squid/configure.in:1.52.2.3 --- squid/configure.in:1.76 Tue Oct 22 01:35:46 2002 +++ squid/configure.in Fri Jan 3 04:28:53 2003 @@ -453,6 +453,18 @@ fi ]) +dnl Enable ICAP Support +AM_CONDITIONAL(USE_ICAP, false) +AC_ARG_ENABLE(icap-support, +[ --enable-icap-support Enable iCAP client capability], +[ if test "$enableval" = "yes" ; then + echo "ICAP support enabled" + AC_DEFINE([HS_FEAT_ICAP],1,[Content Adaptation using ICAP]) + AM_CONDITIONAL(USE_ICAP, true) + fi +]) + + dnl This is a developer only option. Developers know how to set defines dnl dnl AC_ARG_ENABLE(mem-gen-trace, Index: squid/src/Makefile.am diff -u squid/src/Makefile.am:1.31 squid/src/Makefile.am:1.19.2.3 --- squid/src/Makefile.am:1.31 Sun Nov 10 14:41:02 2002 +++ squid/src/Makefile.am Fri Jan 3 04:28:55 2003 @@ -6,6 +6,12 @@ # Uncomment and customize the following to suit your needs: # +if USE_ICAP +ICAPSOURCE = icap.c icap_opt.c +else +ICAPSOURCE = +endif + if USE_DNSSERVER DNSSOURCE = dns.c DNSSERVER = dnsserver @@ -106,6 +112,8 @@ dnsserver.c \ dns_internal.c \ htcp.c \ + icap.c \ + icap_opt.c\ leakfinder.c \ snmp_core.c \ snmp_agent.c \ @@ -140,6 +148,7 @@ $(DELAY_POOL_SOURCE) \ disk.c \ $(DNSSOURCE) \ + $(ICAPSOURCE) \ enums.h \ errorpage.c \ ETag.c \ Index: squid/src/cache_cf.c diff -u squid/src/cache_cf.c:1.53 squid/src/cache_cf.c:1.44.2.2 --- squid/src/cache_cf.c:1.53 Thu Nov 14 19:13:25 2002 +++ squid/src/cache_cf.c Fri Jan 3 04:28:55 2003 @@ -2106,6 +2106,556 @@ return bodylist.head == NULL; } +#ifdef HS_FEAT_ICAP + +/*************************************************** + * prototypes + */ +static int icap_service_process(icap_service *s); +static void icap_service_init(icap_service *s); +static void icap_service_destroy(icap_service *s); +icap_service* icap_service_lookup(char *name); +static int icap_class_process(icap_class *c); +static void icap_class_destroy(icap_class *c); +static void icap_access_destroy(icap_access* a); +static void dump_wordlist(StoreEntry * entry, const char *name, wordlist * list); +static void icap_class_add(icap_class *c); + +/*************************************************** + * icap_service + */ + +/* + * example: + * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod + */ + +static void +parse_icap_service_type(IcapConfig * cfg) +{ + icap_service *A = NULL; + icap_service *B = NULL; + icap_service **T = NULL; + + A = cbdataAlloc(icap_service); + icap_service_init(A); + parse_string(&A->name); + parse_string(&A->type_name); + parse_ushort(&A->bypass); + parse_string(&A->uri); + debug(3,5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); + + if (icap_service_process(A)) { + /* put into linked list */ + for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); + *T = A; + } else { + /* clean up structure */ + debug(3,0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); + icap_service_destroy(A); + cbdataFree(A); + } + +} + +static void +dump_icap_service_type(StoreEntry *e, const char *name, IcapConfig cfg) +{ + icap_service *current_node = NULL; + + if (!cfg.service_head) { + storeAppendPrintf(e, "%s 0\n", name); + return; + } + + current_node = cfg.service_head; + + while (current_node) { + storeAppendPrintf(e, "%s %s %s %d %s\n", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); + current_node = current_node->next; + } + +} + +static void +free_icap_service_type(IcapConfig * cfg) +{ + icap_service *current_node = NULL; + icap_service *next_node = NULL; + if (cfg->service_head) { + current_node = cfg->service_head; + while (current_node) { + next_node = current_node->next; + icap_service_destroy(current_node); + cbdataFree(current_node); + current_node = next_node; + } + } +} + +/* + * parse the raw string and cache some parts that are needed later + * returns 1 if everything was ok + */ +static int +icap_service_process(icap_service *s) +{ + char *start, *end; + char *tailp; + unsigned int len; + int port_in_uri; + s->type = icapServiceToType(s->type_name); + if (s->type >= ICAP_SERVICE_MAX) { + debug(3,0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); + return 0; + } + + if (s->type == ICAP_SERVICE_REQMOD_PRECACHE || s->type == ICAP_SERVICE_REQMOD_POSTCACHE) { + s->method = ICAP_METHOD_REQMOD; + } else if(s->type == ICAP_SERVICE_RESPMOD_PRECACHE || s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) { + s->method = ICAP_METHOD_RESPMOD; + } + + debug(3,5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); + if (strncmp (s->uri,"icap://", 7) != 0) { + debug(3,0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); + return 0; + } + start = s->uri + 7; + if ((end = strchr(start, ':')) != NULL ) { + /* ok */ + port_in_uri=1; + debug(3,5) ("icap_service_process (line %d): port given\n", config_lineno); + } else if ((end = strchr(start,'/')) != NULL ) { + /* ok */ + port_in_uri=0; + debug(3,5) ("icap_service_process (line %d): no port given\n", config_lineno); + } else { + debug(3,0) ("icap_service_process (line %d): wrong service uri: %s\n", config_lineno, s->uri); + return 0; + } + len = end - start; + s->hostname = xstrndup(start, len + 1); + s->hostname[len] = 0; + start = end; + + if (port_in_uri) { + start++; /* skip ':' */ + if ((end = strchr(start, '/')) != NULL) { + s->port = strtoul(start, &tailp, 0) % 65536; + if (tailp != end) { + debug(3,0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); + return 0; + } + debug(3,5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); + start = end; + if (start[0] != '/') { + debug(3,0) ("icap_service_process (line %d): no '/' between port and resource found in %s\n", config_lineno, s->uri); + return 0; + } + } + } else { + /* no explicit ICAP port; first ask by getservbyname or default to + hardwired port 1344 per ICAP specification section 4.2 */ + struct servent *serv = getservbyname("icap", "tcp"); + if (serv) { + s->port = htons(serv->s_port); + debug(3,5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); + } else { + s->port = 1344; + debug(3,5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); + } + } + + start++; /* skip '/' */ + /* the rest is resource name */ + end = strchr(start, '\0'); + len = end - start; + if (len > 1024) { + debug(3,0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); + } + s->resource = xstrndup(start, len + 1); + s->resource[len] = 0; + /* check bypass */ + if ( (s->bypass != 0) && (s->bypass != 1)) { + debug(3,0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); + return 0; + } + return 1; +} + +/* + * constructor + */ +static void +icap_service_init(icap_service *s) +{ + s->type = ICAP_SERVICE_MAX; /* means undefined */ + s->preview = Config.icapcfg.preview_size; + s->opt = 0; + s->istag = StringNull; + s->transfer_preview = StringNull; + s->transfer_ignore = StringNull; + s->transfer_complete = StringNull; +} + +/* + * destructor + * frees only strings, but don't touch the linked list + */ +static void +icap_service_destroy(icap_service *s) +{ + xfree(s->name); + xfree(s->uri); + xfree(s->type_name); + xfree(s->hostname); + xfree(s->resource); + assert(s->opt == 0); /* there should be no opt request running now */ + stringClean(&s->istag); + stringClean(&s->transfer_preview); + stringClean(&s->transfer_ignore); + stringClean(&s->transfer_complete); +} + +icap_service * +icap_service_lookup(char *name) +{ + icap_service *iter; + for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { + if (! strcmp(name, iter->name)) { + return iter; + } + } + return NULL; +} + +/*************************************************** + * icap_service_list + */ + +static void +icap_service_list_add(icap_service_list **isl, icap_service *service) +{ + icap_service_list **iter; + icap_service_list *new; + + new = memAllocate(MEM_ICAP_SERVICE_LIST); + new->service = service; + + if (*isl) { + iter = isl; + while ((*iter)->next) + iter = &((*iter)->next); + (*iter)->next = new; + } else { + *isl = new; + } +} + +/* + * free the linked list without touching references icap_service + */ +static void +icap_service_list_destroy(icap_service_list* isl) +{ + icap_service_list *current; + icap_service_list *next; + + current = isl; + while (current) { + next = current->next; + memFree(current, MEM_ICAP_SERVICE_LIST); + current = next; + } +} + +/*************************************************** + * icap_class + */ +static void +parse_icap_class_type(IcapConfig *cfg) +{ + icap_class *s = NULL; + + s = memAllocate(MEM_ICAP_CLASS); + parse_string(&s->name); + parse_wordlist(&s->services); + + if (icap_class_process(s)) { + /* if ok, put into linked list */ + icap_class_add(s); + } else { + /* clean up structure */ + debug(3,0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); + icap_class_destroy(s); + memFree(s, MEM_ICAP_CLASS); + } +} + +static void +dump_icap_class_type(StoreEntry *e, const char *name, IcapConfig cfg) +{ + icap_class* current_node = NULL; + LOCAL_ARRAY(char, nom, 64); + + if (!cfg.class_head) { + storeAppendPrintf(e, "%s 0\n", name); + return; + } + + current_node = cfg.class_head; + + while (current_node) { + snprintf(nom, 64, "%s %s", name, current_node->name); + dump_wordlist(e, nom, current_node->services); + current_node = current_node->next; + } +} + +static void +free_icap_class_type(IcapConfig *cfg) +{ + icap_class *current_node = NULL; + icap_class *next_node = NULL; + if (cfg->class_head) { + current_node = cfg->class_head; + while (current_node) { + next_node = current_node->next; + icap_class_destroy(current_node); + memFree(current_node, MEM_ICAP_CLASS); + current_node = next_node; + } + } +} + +/* + * process services list, return 1, if at least one service was found + */ +static int +icap_class_process(icap_class *c) +{ + icap_service_list *isl = NULL; + wordlist *iter; + icap_service *service; + /* take services list and build icap_service_list from it */ + for (iter = c->services; iter; iter = iter->next) { + service = icap_service_lookup(iter->key); + if (service) { + icap_service_list_add(&isl, service); + } else { + debug(3,0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); + } + } + + if (isl) { + c->isl = isl; + return 1; + } + return 0; +} + +/* + * search for an icap_class in the global IcapConfig + * classes with hidden-flag are skipped + */ +icap_class * +icap_class_lookup(char *name) +{ + icap_class *iter; + for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { + if ((!strcmp(name, iter->name)) && (!iter->hidden)) { + return iter; + } + } + return NULL; +} + +/* + * adds an icap_class to the global IcapConfig + */ +static void +icap_class_add(icap_class *c) +{ + icap_class *cp = NULL; + icap_class **t = NULL; + IcapConfig *cfg = &Config.icapcfg; + if (c) { + for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); + *t = c; + } +} + +/* + * free allocated memory inside icap_class + */ +static void +icap_class_destroy(icap_class *c) +{ + xfree(c->name); + wordlistDestroy(&c->services); + icap_service_list_destroy(c->isl); +} + +/*************************************************** + * icap_access + */ + +/* format: icap_access {allow|deny} acl, ... */ +static void +parse_icap_access_type(IcapConfig * cfg) +{ + icap_access *A = NULL; + icap_access *B = NULL; + icap_access **T = NULL; + icap_service *s = NULL; + icap_class *c = NULL; + ushort no_class = 0; + + A = memAllocate(MEM_ICAP_ACCESS); + parse_string(&A->service_name); + + /* + * try to find a class with the given name first. if not found, search + * the services. if a service is found, create a new hidden class with + * only this service. this is for backward compatibility. + * + * the special classname All is allowed only in deny rules, because + * the class is not used there. + */ + if (!strcmp(A->service_name, "None")) { + no_class = 1; + } else { + A->class = icap_class_lookup(A->service_name); + if (!A->class) { + s = icap_service_lookup(A->service_name); + if (s) { + c = memAllocate(MEM_ICAP_CLASS); + c->name = xstrdup("(hidden)"); + c->hidden = 1; + wordlistAdd(&c->services, A->service_name); + c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); + c->isl->service = s; + icap_class_add(c); + A->class = c; + } else { + debug(3,0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); + memFree(A, MEM_ICAP_ACCESS); + return; + } + } + } + + aclParseAccessLine(&(A->access)); + debug(3,5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); + + /* check that All class is only used in deny rule */ + if (no_class && A->access->allow) { + memFree(A, MEM_ICAP_ACCESS); + debug(3,0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); + return; + } + + if (A->access) { + for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); + *T = A; + } else { + debug(3,0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); + memFree(A, MEM_ICAP_ACCESS); + } +} + +static void +dump_icap_access_type(StoreEntry *e, const char *name, IcapConfig cfg) +{ + icap_access* current_node = NULL; + LOCAL_ARRAY(char, nom, 64); + + if (!cfg.access_head) { + storeAppendPrintf(e, "%s 0\n", name); + return; + } + + current_node = cfg.access_head; + + while (current_node) { + snprintf(nom, 64, "%s %s", name, current_node->service_name); + dump_acl_access(e, nom, current_node->access); + current_node = current_node->next; + } +} + +static void +free_icap_access_type(IcapConfig * cfg) +{ + icap_access *current_node = NULL; + icap_access *next_node = NULL; + if (cfg->access_head) { + current_node = cfg->access_head; + while (current_node) { + next_node = current_node->next; + icap_access_destroy(current_node); + memFree(current_node, MEM_ICAP_ACCESS); + current_node = next_node; + } + } +} + +/* + * destructor + * frees everything but the linked list + */ +static void +icap_access_destroy(icap_access* a) +{ + xfree(a->service_name); + aclDestroyAccessList(&a->access); +} + +/*************************************************** + * for debugging purposes only + */ +void +dump_icap_config(IcapConfig* cfg) { + icap_service* s_iter; + icap_class* c_iter; + icap_access* a_iter; + icap_service_list *isl_iter; + acl_list* l; + debug(3,0)("IcapConfig: onoff = %d\n", cfg->onoff); + debug(3,0)("IcapConfig: service_head = %d\n", (int) cfg->service_head); + debug(3,0)("IcapConfig: class_head = %d\n", (int) cfg->class_head); + debug(3,0)("IcapConfig: access_head = %d\n", (int) cfg->access_head); + + debug(3,0) ("IcapConfig: services =\n"); + for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { + printf(" %s: \n", s_iter->name); + printf(" bypass = %d\n", s_iter->bypass); + printf(" hostname = %s\n", s_iter->hostname); + printf(" port = %d\n", s_iter->port); + printf(" resource = %s\n", s_iter->resource); + } + debug(3,0) ("IcapConfig: classes =\n"); + for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { + printf(" %s: \n", c_iter->name); + printf(" services = \n"); + for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { + printf(" %s\n", isl_iter->service->name); + } + } + debug(3,0)("IcapConfig: access =\n"); + for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { + printf(" service_name = %s\n", a_iter->service_name); + printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); + for (l = a_iter->access->aclList; l != NULL; l = l->next) { + printf(" %s%s", + l->op ? null_string : "!", + l->_acl->name); + } + printf("\n"); + } +} +#endif /* HS_FEAT_ICAP */ static void parse_kb_size_t(size_t * var) Index: squid/src/cbdata.c diff -u squid/src/cbdata.c:1.16 squid/src/cbdata.c:1.15.2.3 --- squid/src/cbdata.c:1.16 Tue Sep 24 03:59:13 2002 +++ squid/src/cbdata.c Fri Jan 3 04:28:56 2003 @@ -133,6 +133,10 @@ CREATE_CBDATA(statefulhelper); CREATE_CBDATA(helper_stateful_server); CREATE_CBDATA(HttpStateData); +#ifdef HS_FEAT_ICAP + CREATE_CBDATA(IcapStateData); + CREATE_CBDATA(icap_service); +#endif CREATE_CBDATA_FREE(peer, peerDestroy); CREATE_CBDATA(ps_state); CREATE_CBDATA(RemovalPolicy); Index: squid/src/cf.data.pre diff -u squid/src/cf.data.pre:1.86 squid/src/cf.data.pre:1.59.2.3 --- squid/src/cf.data.pre:1.86 Sun Nov 10 14:41:04 2002 +++ squid/src/cf.data.pre Fri Jan 3 04:28:56 2003 @@ -2574,6 +2574,104 @@ DOC_END COMMENT_START + ICAP OPTIONS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: icap_enable +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.onoff +DEFAULT: off +DOC_START + If you want to enable the icap client module, set this to on +DOC_END + +NAME: icap_preview_size +TYPE: int +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg.preview_size +DEFAULT: -1 +DOC_START + The default size of preview data to be sent to the ICAP server. + -1 means no preview. This value might be overwritten on a per server + basis by OPTIONS requests. +DOC_END + +NAME: icap_send_client_ip +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.send_client_ip +DEFAULT: off +DOC_START + This adds the header "X-Client-IP" to ICAP requests. +DOC_END + +NAME: icap_service +TYPE: icap_service_type +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg +DEFAULT: none +DOC_START + Defines a single icap service + + icap_service servicename vectoring_point bypass service_url + + vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache + this specifies, at which point of request processing the icap + service should be plugged in + bypass = 1|0 + if set to 1 and the icap server can not be reached, the request will go + through without being processed by an icap server + service_url = icap://servername:port/service + +Example: +icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod +icap_service service_2 respmod_precache 0 icap://icap2.mydomain.net:1344/respmod +DOC_END + +NAME: icap_class +TYPE: icap_class_type +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg +DEFAULT: none +DOC_START + Defines an ICAP service chain. If there are multiple services per + vectoring point, they are processed in the specified order. + + icap_class classname servicename... + +Example: +icap_class class_1 service_1 service_2 +icap class class_2 service_1 service_3 +DOC_END + +NAME: icap_access +TYPE: icap_access_type +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg +DEFAULT: none +DOC_START + Redirects a request through an icap service class, depending + on given acls + + icap_access classname allow|deny [!]aclname... + + The icap_access statements are processed in the order they appear in + this configuration file. If an access list matches, the processing stops. + For an "allow" rule, the specified class is used for the request. A "deny" + rule simply stops processing without using the class. You can also use the + special classname "None" there. + + For backward compatibility, it is also possible to use services + directly here. +Example: +icap_access class_1 allow all +DOC_END + +COMMENT_START MISCELLANEOUS ----------------------------------------------------------------------------- COMMENT_END @@ -3944,5 +4042,4 @@ Squid will not service requests for some amount of time until all the child processes have been started. DOC_END - EOF Index: squid/src/cf_gen_defines diff -u squid/src/cf_gen_defines:1.5 squid/src/cf_gen_defines:1.5.20.1 --- squid/src/cf_gen_defines:1.5 Mon Dec 3 00:03:21 2001 +++ squid/src/cf_gen_defines Fri Jan 3 04:28:57 2003 @@ -18,6 +18,7 @@ define["USE_UNLINKD"]="--enable-unlinkd" define["USE_USERAGENT_LOG"]="--enable-useragent-log" define["USE_WCCP"]="--enable-wccp" + define["HS_FEAT_ICAP"]="--enable-icap-support" } /^IFDEF:/ { if (define[$2] != "") Index: squid/src/client_side.c diff -u squid/src/client_side.c:1.80 squid/src/client_side.c:1.58.2.3 --- squid/src/client_side.c:1.80 Sun Nov 10 14:41:05 2002 +++ squid/src/client_side.c Fri Jan 3 04:28:57 2003 @@ -59,6 +59,12 @@ #include "clientStream.h" #include "IPInterception.h" +/* I am using some of these functions in ICAP */ +#ifdef HS_FEAT_ICAP +#define STATIC +#else +#define STATIC static +#endif #if LINGERING_CLOSE #define comm_close comm_lingering_close #endif @@ -108,8 +114,8 @@ /* other */ static CWCB clientWriteComplete; static CWCB clientWriteBodyComplete; -static PF clientReadRequest; -static PF connStateFree; +STATIC PF clientReadRequest; +STATIC PF connStateFree; static PF requestTimeout; static PF clientLifetimeTimeout; static void checkFailureRatio(err_type, hier_code); @@ -170,10 +176,10 @@ static void connNoteUseOfBuffer(ConnStateData * conn, int byteCount); static int connKeepReadingIncompleteRequest(ConnStateData * conn); static void connCancelIncompleteRequests(ConnStateData * conn); -static ConnStateData *connStateCreate(struct sockaddr_in peer, struct sockaddr_in me, int fd); +STATIC ConnStateData *connStateCreate(struct sockaddr_in peer, struct sockaddr_in me, int fd); static clientStreamNode *getClientReplyContext(clientSocketContext * context); static int connAreAllContextsForThisConnection(ConnStateData * connState); -static void connFreeAllContexts(ConnStateData * connState); +STATIC void connFreeAllContexts(ConnStateData * connState); static void clientPullData(clientSocketContext * context); clientStreamNode * @@ -241,7 +247,6 @@ ConnStateData *conn = data; xstrncpy(conn->rfc931, ident ? ident : dash_str, USER_IDENT_SZ); } - #endif void @@ -466,7 +471,7 @@ } /* This is a handler normally called by comm_close() */ -static void +STATIC void connStateFree(int fd, void *data) { ConnStateData *connState = data; @@ -563,6 +568,16 @@ return conn->currentobject; } +#ifdef HS_FEAT_ICAP +clientHttpRequest * +connGetCurrentHReq(ConnStateData * conn) +{ + clientSocketContext *cs = connGetCurrentContext(conn); + assert(cs); + return cs->http; +} +#endif + void contextDeferRecipientForLater(clientSocketContext * context, clientStreamNode * node, HttpReply * rep, StoreIOBuffer recievedData) { @@ -1268,7 +1283,7 @@ clientPullData(context); } -static void +STATIC void clientReadRequest(int fd, void *data) { ConnStateData *conn = data; @@ -1454,6 +1469,12 @@ break; } } +#ifdef HS_FEAT_ICAP + /* I need to read the modified request header from icap server - + allow me. If it has come through Accept go ahead and service.. + else don;t */ + if (conn->me.sin_family) +#endif clientAccessCheck(http); continue; /* while offset > 0 && body.size_left == 0 */ } else if (parser_return_code == 0) { @@ -1732,8 +1753,9 @@ commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0); commSetDefer(fd, clientReadDefer, connState); clientdbEstablished(peer.sin_addr, 1); - assert(N); - (*N)++; + /* fix this later */ + assert(N); + (*N)++; } } @@ -2066,3 +2088,4 @@ return ch; } + Index: squid/src/client_side_reply.c diff -u squid/src/client_side_reply.c:1.13 squid/src/client_side_reply.c:1.13.4.2 --- squid/src/client_side_reply.c:1.13 Sun Nov 17 00:52:55 2002 +++ squid/src/client_side_reply.c Fri Jan 3 04:28:58 2003 @@ -37,6 +37,13 @@ #include "StoreClient.h" #include "clientStream.h" +/* I am using some of these functions in ICAP */ +#ifdef HS_FEAT_ICAP +#define STATIC +#else +#define STATIC static +#endif + typedef struct _clientReplyContext { clientHttpRequest *http; int headers_sz; @@ -70,7 +77,7 @@ static void clientProcessOnlyIfCachedMiss(clientReplyContext *); static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request); -static STCB clientHandleIMSReply; +STATIC STCB clientHandleIMSReply; static int modifiedSince(StoreEntry *, request_t *); static log_type clientIdentifyStoreObject(clientReplyContext *); static void clientPurgeRequest(clientReplyContext *); Index: squid/src/client_side_request.c diff -u squid/src/client_side_request.c:1.6 squid/src/client_side_request.c:1.6.12.2 --- squid/src/client_side_request.c:1.6 Thu Oct 3 05:55:28 2002 +++ squid/src/client_side_request.c Fri Jan 3 04:28:58 2003 @@ -524,6 +524,13 @@ http->request = requestLink(new_request); } clientInterpretRequestHeaders(http); +#if HS_FEAT_ICAP + /* only for testing, try to do aclCheck for icap */ + http->request->flags.do_icap = 0; + if (Config.icapcfg.onoff) + if (icapCheckAcl(http)) + http->request->flags.do_icap = 1; +#endif #if HEADERS_LOG headersLog(0, 1, request->method, request); #endif Index: squid/src/enums.h diff -u squid/src/enums.h:1.40 squid/src/enums.h:1.32.6.3 --- squid/src/enums.h:1.40 Tue Sep 24 03:59:15 2002 +++ squid/src/enums.h Fri Jan 3 04:28:58 2003 @@ -630,6 +630,12 @@ MEM_EVENT, MEM_TLV, MEM_SWAP_LOG_DATA, +#if HS_FEAT_ICAP + MEM_ICAP_OPT_DATA, + MEM_ICAP_SERVICE_LIST, + MEM_ICAP_CLASS, + MEM_ICAP_ACCESS, +#endif MEM_MAX } mem_type; @@ -726,9 +732,26 @@ CBDATA_RemovalPolicy, CBDATA_RemovalPolicyWalker, CBDATA_RemovalPurgeWalker, +#ifdef HS_FEAT_ICAP + CBDATA_IcapStateData, + CBDATA_icap_service, +#endif CBDATA_FIRST_CUSTOM_TYPE = 1000 } cbdata_type; + +/* + * ICAP states: Maily required for Preview support for RESPMOD. + */ +typedef enum { + ICAP_EndOfInputData = 1, /* No more data to be expected on HTTP connection */ + ICAP_PreviewDone = 2, /* Handling of preview data finished or no preview at all */ + ICAP_WaitForPreviewReply = 4, /* Expect reply from ICAP server after preview */ + ICAP_WaitForReply = 8, /* Expect final reply from ICAP server */ + ICAP_GotReply = 16, +} icapState; + + /* * Return codes from checkVary(request) */ @@ -783,4 +806,67 @@ #endif +#if HS_FEAT_ICAP +typedef enum { + ICAP_STATUS_NONE = 0, + ICAP_STATUS_CONTINUE = 100, + ICAP_STATUS_SWITCHING_PROTOCOLS = 101, + ICAP_STATUS_STATUS_OK = 200, + ICAP_CREATED = 201, + ICAP_STATUS_ACCEPTED = 202, + ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, + ICAP_STATUS_RESET_CONTENT = 205, + ICAP_STATUS_PARTIAL_CONTENT = 206, + ICAP_STATUS_MULTIPLE_CHOICES = 300, + ICAP_STATUS_MOVED_PERMANENTLY = 301, + ICAP_STATUS_MOVED_TEMPORARILY = 302, + ICAP_STATUS_SEE_OTHER = 303, + ICAP_STATUS_NOT_MODIFIED = 304, + ICAP_STATUS_USE_PROXY = 305, + ICAP_STATUS_BAD_REQUEST = 400, + ICAP_STATUS_UNAUTHORIZED = 401, + ICAP_STATUS_PAYMENT_REQUIRED = 402, + ICAP_STATUS_FORBIDDEN = 403, + ICAP_STATUS_SERVICE_NOT_FOUND = 404, + ICAP_STATUS_METHOD_NOT_ALLOWED = 405, + ICAP_STATUS_NOT_ACCEPTABLE = 406, + ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + ICAP_STATUS_REQUEST_TIMEOUT = 408, + ICAP_STATUS_CONFLICT = 409, + ICAP_STATUS_GONE = 410, + ICAP_STATUS_LENGTH_REQUIRED = 411, + ICAP_STATUS_PRECONDITION_FAILED = 412, + ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, + ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, + ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, + ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, + ICAP_STATUS_NOT_IMPLEMENTED = 501, + ICAP_STATUS_BAD_GATEWAY = 502, + ICAP_STATUS_SERVICE_OVERLOADED = 503, + ICAP_STATUS_GATEWAY_TIMEOUT = 504, + ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, + ICAP_STATUS_INVALID_HEADER = 600 +} icap_status; + +/* + * these values are used as index in an array, so it seems to be better to + * assign some numbers + */ +typedef enum { + ICAP_SERVICE_REQMOD_PRECACHE = 0, + ICAP_SERVICE_REQMOD_POSTCACHE = 1, + ICAP_SERVICE_RESPMOD_PRECACHE = 2, + ICAP_SERVICE_RESPMOD_POSTCACHE = 3, + ICAP_SERVICE_MAX = 4 +} icap_service_t; + +typedef enum { + ICAP_METHOD_NONE, + ICAP_METHOD_OPTION, + ICAP_METHOD_REQMOD, + ICAP_METHOD_RESPMOD +} icap_method_t; +#endif /* HS_FEAT_ICAP */ + #endif /* SQUID_ENUMS_H */ Index: squid/src/forward.c diff -u squid/src/forward.c:1.17 squid/src/forward.c:1.15.2.3 --- squid/src/forward.c:1.17 Sun Sep 15 04:06:32 2002 +++ squid/src/forward.c Fri Jan 3 04:28:59 2003 @@ -36,11 +36,17 @@ #include "squid.h" +/* I am using some of these functions in ICAP */ +#ifdef HS_FEAT_ICAP +#define STATIC +#else +#define STATIC static +#endif static PSC fwdStartComplete; static void fwdDispatch(FwdState *); -static void fwdConnectStart(void *); /* should be same as EVH */ -static void fwdStateFree(FwdState * fwdState); -static PF fwdConnectTimeout; +STATIC void fwdConnectStart(void *); /* should be same as EVH */ +STATIC void fwdStateFree(FwdState * fwdState); +STATIC PF fwdConnectTimeout; static PF fwdServerClosed; static CNCB fwdConnectDone; static int fwdCheckRetry(FwdState * fwdState); @@ -76,7 +82,7 @@ memFree(fs, MEM_FWD_SERVER); } -static void +STATIC void fwdStateFree(FwdState * fwdState) { StoreEntry *e = fwdState->entry; @@ -239,7 +245,7 @@ current = NULL; } -static void +STATIC void fwdConnectTimeout(int fd, void *data) { FwdState *fwdState = data; @@ -317,7 +323,7 @@ return aclMapTOS(Config.accessList.outgoing_tos, &ch); } -static void +STATIC void fwdConnectStart(void *data) { FwdState *fwdState = data; @@ -680,6 +686,13 @@ fwdUnregister(int fd, FwdState * fwdState) { debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); +#if defined (HS_FEAT_ICAP) && defined (ICAP_CHUNKED) + /* ugly hack to get not so much assertions + * problem is, that fd might be different from fwd fd, because + * of the icap connection + */ + fd = fwdState->server_fd; +#endif assert(fd == fwdState->server_fd); assert(fd > -1); comm_remove_close_handler(fd, fwdServerClosed, fwdState); Index: squid/src/http.c diff -u squid/src/http.c:1.26 squid/src/http.c:1.20.10.3 --- squid/src/http.c:1.26 Fri Oct 4 14:45:32 2002 +++ squid/src/http.c Fri Jan 3 04:28:59 2003 @@ -45,23 +45,31 @@ static CWCB httpSendComplete; static CWCB httpSendRequestEntity; -static PF httpReadReply; -static void httpSendRequest(HttpStateData *); -static PF httpStateFree; -static PF httpTimeout; +/* I am using some of these functions in ICAP */ +#ifdef HS_FEAT_ICAP +#define STATIC +#define ICAP_FIELD(httpState) (httpState)->icap +#else +#define STATIC static +#endif + +STATIC PF httpReadReply; +STATIC void httpSendRequest(HttpStateData *); +STATIC PF httpStateFree; +STATIC PF httpTimeout; static void httpCacheNegatively(StoreEntry *); static void httpMakePrivate(StoreEntry *); static void httpMakePublic(StoreEntry *); static int httpCachableReply(HttpStateData *); static void httpMaybeRemovePublic(StoreEntry *, http_status); -static mb_size_t httpBuildRequestPrefix(request_t * request, +STATIC mb_size_t httpBuildRequestPrefix(request_t * request, request_t * orig_request, StoreEntry * entry, MemBuf * mb, http_state_flags); static void httpProcessReplyHeader(HttpStateData *, const char *, int); -static void +STATIC void httpStateFree(int fd, void *data) { HttpStateData *httpState = data; @@ -70,6 +78,7 @@ #endif if (httpState == NULL) return; + debug(11, 0) ("httpStateFree: FD %d, httpState = %p\n", fd, data); storeUnlockObject(httpState->entry); if (httpState->reply_hdr) { memFree(httpState->reply_hdr, MEM_8K_BUF); @@ -92,7 +101,7 @@ return 1; } -static void +STATIC void httpTimeout(int fd, void *data) { HttpStateData *httpState = data; @@ -503,8 +512,6 @@ * If we didn't send a keep-alive request header, then this * can not be a persistent connection. */ - if (!httpState->flags.keepalive) - return 0; /* * What does the reply have to say about keep-alive? */ @@ -519,6 +526,13 @@ */ if (!reply->keep_alive) return 0; +#if defined (HS_FEAT_ICAP) && defined (ICAP_CHUNKED) + if (httpState->icap && httpState->icap->icap_fd == httpState->fd && httpState->icap->chunk_size == -2) { + /* zero end chunk reached */ + debug(11, 5) ("httpPconnTransferDone: zero end chunk on ICAP connection reached\n"); + return 1; + } +#endif debug(11, 5) ("httpPconnTransferDone: content_length=%d\n", reply->content_length); /* If we haven't seen the end of reply headers, we are not done */ @@ -526,11 +540,15 @@ return 0; clen = httpReplyBodySize(httpState->request->method, reply); /* If there is no message body, we can be persistent */ - if (0 == clen) + if (0 == clen) { + debug(11, 3) ("GEE:httpPconnTransferDone: FD %d no content returning 1\n", httpState->fd); return 1; + } /* If the body size is unknown we must wait for EOF */ if (clen < 0) return 0; + + /* If the body size is known, we must wait until we've gotten all of it. */ if (mem->inmem_hi < reply->content_length + reply->hdr_sz) return 0; @@ -541,10 +559,13 @@ /* This will be called when data is ready to be read from fd. Read until * error or connection closed. */ /* XXX this function is too long! */ -static void +STATIC void httpReadReply(int fd, void *data) { HttpStateData *httpState = data; +#ifdef HS_FEAT_ICAP + IcapStateData *icap; +#endif LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF); StoreEntry *entry = httpState->entry; const request_t *request = httpState->request; @@ -561,6 +582,7 @@ else delay_id = delayMostBytesAllowed(entry->mem_obj); #endif + debug(11, 5) ("httpReadReply: FD %d: httpState %p.\n", fd, data); if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { comm_close(fd); return; @@ -581,7 +603,11 @@ #endif kb_incr(&statCounter.server.all.kbytes_in, len); kb_incr(&statCounter.server.http.kbytes_in, len); +#ifdef HS_FEAT_ICAP + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +#else commSetTimeout(fd, Config.Timeout.read, NULL, NULL); +#endif IOStats.Http.reads++; for (clen = len - 1, bin = 0; clen; bin++) clen >>= 1; @@ -631,6 +657,12 @@ * we want to process the reply headers. */ httpProcessReplyHeader(httpState, buf, len); +#ifdef HS_FEAT_ICAP + if (ICAP_FIELD(httpState)) { + icapSendRespMod(httpState, buf, len, 1); + return; + } +#endif fwdComplete(httpState->fwd); comm_close(fd); } else { @@ -649,7 +681,31 @@ EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); } } - storeAppend(entry, buf, len); +#if defined (HS_FEAT_ICAP) && defined (ICAP_CHUNKED) + /* read data on ICAP connection in chunked encoding */ + if (httpState->icap && httpState->icap->icap_fd == fd) { + if (icapReadChunkedBody (fd, httpState, len, buf)) + return; + } else + if (fd == httpState->fd) +#endif + storeAppend(entry, buf, len); + + debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); + +#ifdef ICAP_CHUNKED + /* + * when headers are complete and no body is specified in encapsulated header + * we probably can finish this request + */ + icap = ICAP_FIELD(httpState); + if (icap && icap->reqmod && icap->enc_null_body >= 0 && httpState->reply_hdr_state == 2) { + fwdComplete(httpState->fwd); + comm_close(fd); + return; + } +#endif + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { /* * the above storeAppend() call could ABORT this entry, @@ -659,6 +715,13 @@ (void) 0; } else if (httpPconnTransferDone(httpState)) { /* yes we have to clear all these! */ +#ifdef HS_FEAT_ICAP + /* only enter respmod if we are not in reqmod mode */ + if (ICAP_FIELD(httpState) && !ICAP_FIELD(httpState)->reqmod && ICAP_FIELD(httpState)->http_fd == fd) { + icapSendRespMod(httpState, buf, len, 1); + return; + } +#endif commSetDefer(fd, NULL, NULL); commSetTimeout(fd, -1, NULL, NULL); commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); @@ -672,8 +735,14 @@ httpState->fd = -1; httpStateFree(fd, httpState); } else { - /* Wait for EOF condition */ - commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); +#ifdef HS_FEAT_ICAP + /* don't enter respmod for reqmod replies from icap server */ + if (ICAP_FIELD(httpState) && !ICAP_FIELD(httpState)->reqmod && ICAP_FIELD(httpState)->http_fd == fd) { + icapSendRespMod(httpState, buf, len, 0); + } else +#endif + /* Wait for EOF condition */ + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); } } } @@ -707,6 +776,12 @@ return; } else { /* Schedule read reply. */ +#if defined (HS_FEAT_ICAP) + if (httpState->orig_request->flags.do_icap) { + if (startICAPrespmod(httpState) >= 0) + return; + } +#endif commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); /* * Set the read timeout here because it hasn't been set yet. @@ -717,7 +792,13 @@ * request bodies. */ commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); +#ifdef HS_FEAT_ICAP + /* Read fully - don't put limits on the read ahead count */ + /* Oh! I broke my head for this */ + commSetDefer(fd, NULL, NULL); +#else commSetDefer(fd, fwdCheckDeferRead, entry); +#endif } } @@ -884,8 +965,11 @@ if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); httpHdrCcSetMaxAge(cc, getMaxAge(url)); +#ifndef HS_FEAT_ICAP + /* Don;t bother - if the url you want to cache is redirected? */ if (strLen(request->urlpath)) assert(strstr(url, strBuf(request->urlpath))); +#endif } if (flags.only_if_cached) EBIT_SET(cc->mask, CC_ONLY_IF_CACHED); @@ -933,7 +1017,7 @@ return mb->size - offset; } /* This will be called when connect completes. Write request. */ -static void +STATIC void httpSendRequest(HttpStateData * httpState) { MemBuf mb; @@ -1021,12 +1105,19 @@ httpState->request = requestLink(orig_req); httpState->orig_request = requestLink(orig_req); } +#ifdef HS_FEAT_ICAP + if (httpState->orig_request->flags.do_icap) { + if (startICAPreqmod(httpState) >= 0) + return; + } +#endif /* * register the handler to free HTTP state data when the FD closes */ comm_add_close_handler(fd, httpStateFree, httpState); statCounter.server.all.requests++; statCounter.server.http.requests++; + httpSendRequest(httpState); /* * We used to set the read timeout here, but not any more. @@ -1109,3 +1200,4 @@ version->major = major; version->minor = minor; } + Index: squid/src/icap.c diff -u /dev/null squid/src/icap.c:1.1.2.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap.c Fri Jan 3 04:28:59 2003 @@ -0,0 +1,1402 @@ +/* + * $Id$ + * This file has the ICAP client code... + * + * Author: Geetha Manjunath, + * Hewlett Packard Company + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + /******** TEMP!!!! ************/ + #define cbdataLock(cc) + #define cbdataUnlock(cc) + #define cbdataValid(cc) (1) + +#include "squid.h" +#include "clientStream.h" +#define ICAP_FIELD(httpState) (httpState)->icap + +static const char *const crlf = "\r\n"; +#define EXPECTED_ICAP_HEADER_LEN 256 +#define ICAP_OPTIONS_REQUEST + +PF httpReadReply; +PF httpTimeout; +PF httpStateFree; +void httpSendRequest(HttpStateData *); +void clientIdentDone(const char *ident, void *data); +int clientReadDefer(int fdnotused, void *data); +void clientReadRequest(int fd, void *data); +PF connStateFree; +PF requestTimeout; +PF icapConnectTimeout; +extern clientHttpRequest * connGetCurrentHReq(ConnStateData * conn); + +void icapReadResponse(int fd, HttpStateData * httpState); +void icapReqModReadReply(int fd, void *data); +void icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data); +void icapSendReqMod(int fd, int status, void *data); +void getICAPReqModString(MemBuf * mb, int hdrLen, int bodyLen, char *service, char* client_addr); +void getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, char *service, char* client_addr); +int initICAP(); +int startICAPrespmod(HttpStateData * httpState); +int startICAPreqmod(HttpStateData * httpState); +IcapStateData *startICAP(); +void icapRespMod(int fd, int status, void *data); +int icapSendRespMod(HttpStateData * httpState, void *buf, int len, int theEnd); +int buildRespModHeader (MemBuf * mb, HttpStateData * httpState, char *buf, + ssize_t len, int theEnd); +void scheduleReadReply(int fd, void *data); +mb_size_t httpBuildRequestPrefix(request_t * request, request_t * orig_request, StoreEntry * entry, MemBuf * mb, http_state_flags); +void connFreeAllContexts(ConnStateData * connState); + +/* + * parse the contents of the encapsulated header (buffer between enc_start + * and enc_end) and put the result into IcapStateData + */ +static void +icapParseEncapsulated(IcapStateData * icap, const char *enc_start, + const char *enc_end) +{ + char *current, *end; + + assert(icap); + assert(enc_start); + assert(enc_end); + + current = strchr(enc_start, ':'); + current++; + while (current < enc_end) { + while (isspace(*current)) + current++; + if (!strncmp(current, "res-hdr=", 8)) { + current += 8; + icap->enc_res_hdr = strtol(current, &end, 10); + } else if (!strncmp(current, "null-body=", 10)) { + current += 10; + icap->enc_null_body = strtol(current, &end, 10); + } else if (!strncmp(current, "res-body=", 9)) { + current += 9; + icap->enc_res_body = strtol(current, &end, 10); + } else { + /* invalid header */ + debug(21, 5) ("icapParseEncapsulated: error in: %s\n", current); + return; + } + current = end; + current = strchr(current, ','); + if (current == NULL) + break; + else + current++; /* skip ',' */ + } + +} + + +/* This will be called when data is ready to be read from fd. Read until + * error or connection closed. */ +void +icapReqModReadReply(int fd, void *data) +{ + void fwdConnectStart(void *); + PF connStateFree; + PF requestTimeout; + HttpStateData *httpState = data; + LOCAL_ARRAY(char, icap_header, SQUID_TCP_SO_RCVBUF); + LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); + int read_sz, len, headlen; + char *tmpicap, *header_enc, *header_enc_end; + StoreEntry *entry = httpState->entry; + int status, isIcap, directResponse = 0; + float ver; + ErrorState *err; + + debug(21, 5) ("icapReqModReadReply: FD %d httpState = %p\n", fd, data); + statCounter.syscalls.sock.reads++; + + /* go over the buf till I find icap reply header boundary. + * If it is a request, give the channel over to httpReadReply. + * It is a modified request, call a httpRequest handler fn.. + * Note that I am peaking here: will read up the ICAP header part later. + */ + read_sz = EXPECTED_ICAP_HEADER_LEN; + /* To fix: If the len of ICAP header is exactly read_sz - we have problems */ + /* tmp = NULL; */ + len = 0; + headlen = 0; + tmpicap = icap_header; + isIcap = 0; + while (!headlen && len >= 0) { + /* TODO: under some conditions we might cycle endless here, fix this */ + /* len = read(fd, tmpbuf, read_sz); */ + len = recv(fd, tmpbuf, read_sz, MSG_PEEK); + /* tmp = strstr(tmpbuf, "\r\n\r\n"); */ + if (len > 0) { + headlen = headersEnd(tmpbuf, len); + if (headlen) + /* End of ICAP header found - Read the \r\n\r\n too */ + len = headlen; + else { + /* Header is bigger */ + read_sz *=2; + if (read_sz < SQUID_TCP_SO_RCVBUF) continue; + } + if (!isIcap) + isIcap = !(!strstr(tmpbuf, "ICAP")); + } + } + if (!isIcap && headlen) { + /* Could be sending the HTTP reply directly - bug but still allow :-) */ + if (strstr(tmpbuf, "HTTP")) + directResponse = 1; + } else { + if (!headlen || (read(fd, icap_header, headlen) != headlen)) { + /* Did not find a proper ICAP response */ + debug(21, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->request = requestLink((request_t *) httpState->request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + return; + } + /* Read the ICAP header only - leave the rest */ + icap_header[headlen] = '\0'; + debug(21, 3) ("Read icap header : <%s>\n", icap_header); + ver = -999.999; /* initalize the version to a bogus number. I + * think that we should parse it using 2 + * integers and a %d.%d scanf format - Basile + * june 2002 */ + if (sscanf(icap_header, "ICAP/%f %d %s\r", &ver, &status, tmpbuf) < 3 + || ver <= 0.0) { + debug(21, 1) ("BAD ICAP status line <%s>\n", icap_header); + /* is this correct in case of ICAP protocol error? */ + err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->request = requestLink((request_t *) httpState->request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + return; + }; + /* printf("Version = %g, Status = %d, String = %s\n",ver,status,tmpbuf); */ + /* directResponse = !(!strstr(icap_header,"res-body")); res-hdr or res-body */ + header_enc = strstr(icap_header, "Encapsulated:"); + if (header_enc) { + header_enc_end = header_enc + strcspn(header_enc, "\r\n"); + /* we should check if there is an Encapsulated - what happens + * if there is no such field? Basile + * + * it crashed. Ralf + */ + icapParseEncapsulated(ICAP_FIELD(httpState), header_enc, header_enc_end); + } + directResponse = !(!strstr(icap_header, "res-")); + } + + /* Check whether it is a direct reply - if so over to http part */ + if (directResponse) { + debug(21, 3) ("icapReqModReadReply: fd %d, ICAP request satisfaction mode\n", fd); + /* got the reply, no need to come here again */ + httpState->icap->state &= ~ICAP_WaitForReply; + httpState->icap->state |= ICAP_GotReply; + /* Read the response here */ + icapReadResponse(fd, httpState); + } else { + /* Read the new modified request from ICAP server and call httpSendRequest for that */ + struct sockaddr_in peer, me; + ConnStateData *connState = NULL; + clientHttpRequest *http = NULL; + ConnStateData *connStateCreate(struct sockaddr_in peer, struct sockaddr_in me, int fd); + void icap_clientReadRequest(int fd, void *data); + + memset(&peer, '\0', sizeof(struct sockaddr_in)); + memset(&me, '\0', sizeof(struct sockaddr_in)); + + /* Code flicked from httpAccept */ +#if 1 + connState = connStateCreate(peer, me, fd); +#else + connState = cbdataAlloc(ConnStateData); + connState->peer = peer; + connState->log_addr = peer.sin_addr; + connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr; + connState->me = me; + connState->fd = fd; + connState->in.allocatedSize = CLIENT_REQ_BUF_SZ; + /* + * connState->in.buf = memAllocate(MEM_CLIENT_REQ_BUF); + */ + connState->in.buf = memAllocBuf(CLIENT_REQ_BUF_SZ, &connState->in.allocatedSize); +#endif + /* XXX account connState->in.buf */ + comm_add_close_handler(fd, connStateFree, connState); + cbdataLock(httpState); + commSetTimeout(fd, Config.Timeout.request, icapConnectTimeout, connState); + clientReadRequest(fd, connState); + + /* Now download the modified request just read */ + if (connState->nrequests > 0) { + request_t *hreq; + commSetTimeout(fd, -1, 0, connState); + http = connGetCurrentHReq(connState); + hreq = http->request; + /* + if ( ! (httpState->fwd->servers && httpState->fwd->servers->code == DIRECT)) + stringReset(&(hreq->urlpath), http->uri); + */ + httpState->request = requestLink(hreq); + connState->nrequests = 0; + /* connState->chr = NULL; */ + if (httpState->fwd->servers && httpState->fwd->servers->code == DIRECT) { + /* Direct connection to internet! Open a connection to the new host */ + httpState->fwd->request = requestLink(http->request); + httpState->fwd->server_fd = -1; + fwdConnectStart(httpState->fwd); + httpState->fd = httpState->fwd->server_fd; + comm_add_close_handler(httpState->fd, httpStateFree, httpState); + } else { + stringReset(&(hreq->urlpath), http->uri); + } + /* Change orig request's host too : See httpBuildRequestHeader + - puts default Host header from orig_request */ + strcpy(httpState->orig_request->host, httpState->request->host); + httpState->orig_request->port = httpState->request->port; + /** GEETHA: Added this to fix bugs related to header modifying reqmod filters. */ + httpState->orig_request->header = httpState->request->header; + + { /* UNTESTED CODE */ + /* connFreeAllContexts(connState); */ + extern void httpRequestFreeResources(clientHttpRequest * http); + /* + void * connGetCurrentContext(ConnStateData * conn); + extern void clientRequestContextFree(void *data); + clientRequestContextFree(connGetCurrentContext(connState)); + httpRequestFreeResources(http); + */ + connState->currentobject=NULL; + } + connStateFree(fd,connState); + httpSendRequest(httpState); + } + icapStateFree(ICAP_FIELD(httpState)); + ICAP_FIELD(httpState) = NULL; + } +} + +/* Type should be CWCB */ +void +icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + HttpStateData *httpState = data; + ErrorState *err; + StoreEntry *entry = httpState->entry; + + debug(21, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", + fd, size, errflag); + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.server.all.kbytes_out, size); + kb_incr(&statCounter.server.http.kbytes_out, size); + } + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->xerrno = errno; + err->request = requestLink(httpState->orig_request); + errorAppendEntry(entry, err); + comm_close(fd); + return; + } + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; + } + /* Schedule read reply. */ + cbdataLock(httpState); + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadReply, httpState, 0); + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been + * fully written to the server-side. If we start the timeout + * after connection establishment, then we are likely to hit + * the timeout for POST/PUT requests that have very large + * request bodies. + */ + commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, httpState); +/* commSetDefer(fd, fwdCheckDeferRead, entry); */ +} + + +/* commConnect callback will be called with "callback(fd, status, data);" in + * commConnectCallback(ConnectStateData * cs, int status) + */ +void +icapSendReqMod(int fd, int status, void *data) +{ + MemBuf mb; + MemBuf mb_hdr; + HttpStateData *httpState = data; + IcapStateData *icap = ICAP_FIELD(httpState); + request_t *req = httpState->request; + StoreEntry *entry = httpState->entry; + char *client_addr; + int cfd; + int blen = 0; + void *b = NULL; + int icap_fd = icap->icap_fd; + icap_service *service; + + if (req->method == METHOD_POST) { + /* TODO: + * Data is usually missing. Sending of data has to be postponed until + * it is read. But we have to take care that some ICAP servers may not + * be able to start the ICAP response before the complete request body + * is read. In that case we do not have the request header to send to + * the HTTP server that the HTTP code needs before reading the body + * from the client. + * At least way, it could work that POSTs are sent to the ICAP server + * without body for now. + */ + ConnStateData *conn = httpState->orig_request->body_connection; + if (conn) { + blen = conn->body.bufsize; + b = conn->body.buf; + if ( ! b) + blen = 0; /* make sure this goes together */ + } + } + /* cfd = client fd */ + if (entry->mem_obj) + cfd = entry->mem_obj->fd; + else + cfd = -1; + assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type); + + memBufDefInit(&mb); + memBufDefInit(&mb_hdr); + + /* memBufPrintf(&buf, ...); + * * memBufAppend(MemBuf * mb, const char *buf, mb_size_t sz) + * * comm_write_mbuf(fd, buf, handler, data); + * * -- memBufClean(&buf); + * * memBufPrintf(mb, "%s %s HTTP/1.0\r\n", + */ + httpBuildRequestPrefix(req, + httpState->orig_request, entry, &mb_hdr, httpState->flags); + + service = icap->current_service; + assert(service); + client_addr = inet_ntoa(httpState->orig_request->client_addr); + + getICAPReqModString(&mb, mb_hdr.size, blen, service->uri, client_addr); + memBufAppend(&mb, "Connection: close\r\n", 19); + memBufAppend(&mb, crlf, 2); + memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); + memBufClean(&mb_hdr); + + if (blen > 0) { + memBufPrintf(&mb, "%x\r\n", blen); + memBufAppend(&mb, b, blen); + memBufAppend(&mb, crlf, 2); + /* if (httpState->orig_request->content_length > 0 + * Need to check and see if we have to put content length header */ + } + memBufAppend(&mb, crlf, 2); + debug(21, 3) ("icapSendReqMod: FD %d:\n%s\n", httpState->fd, mb.buf); + cbdataLock(httpState); + comm_write_mbuf(icap_fd, mb, icapSendReqModDone, httpState); +} + +int +startICAPreqmod(HttpStateData * httpState) +{ + IcapStateData *icap; + PF httpStateFree; + icap_service_list *isl_iter; + icap_service *service = NULL; + icap_service_t type; + + if (ICAP_FIELD(httpState) + || !httpState->orig_request->flags.do_icap) + return -1; + + isl_iter = httpState->orig_request->class->isl; + + while (isl_iter) { + type = isl_iter->service->type; + if (type == ICAP_SERVICE_REQMOD_PRECACHE + || type == ICAP_SERVICE_REQMOD_POSTCACHE) { + service = isl_iter->service; + break; + } + isl_iter = isl_iter->next; + } + + if (!service) + return -1; /* no service found */ + + icap = startICAP(service->uri); + if (!icap) + return -1; + + icap->current_service = service; + icap->reqmod = 1; + icap->http_fd = httpState->fd; + ICAP_FIELD(httpState) = icap; + +#if 1 + /* register the handler to free HTTP state data when the FD closes */ + comm_add_close_handler(httpState->fd, httpStateFree, httpState); +#endif + + commSetTimeout(icap->icap_fd, Config.Timeout.connect, icapConnectTimeout, + httpState); + cbdataLock(httpState); + commConnectStart(icap->icap_fd, service->hostname, service->port, + icapSendReqMod, httpState); + /* Calls icapSendReqMod after connection is started */ + return 0; +} + + +/***************************** RESPMOD ********************************/ +void +icapRespModReadReply(int fd, void *data) +{ + PF connStateFree; + PF requestTimeout; + HttpStateData *httpState = data; + LOCAL_ARRAY(char, icap_header, SQUID_TCP_SO_RCVBUF); + LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); + int read_sz, len, headlen; + char *tmpicap; + StoreEntry *entry = httpState->entry; + int status = 0, isIcap, directResponse = 0; + float ver; + ErrorState *err; + + debug(21, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); + statCounter.syscalls.sock.reads++; + + assert(httpState->orig_request); + /* Else it will loop */ + httpState->orig_request->flags.do_icap = 0; + + read_sz = EXPECTED_ICAP_HEADER_LEN; + /* To fix: If the len of ICAP header is exactly read_sz - we have problems */ + /* tmp = NULL; */ + len = 0; + headlen = 0; + tmpicap = icap_header; + isIcap = 0; + while (!headlen && len >= 0) { + /* len = read(fd, tmpbuf, read_sz); */ + len = recv(fd, tmpbuf, read_sz, MSG_PEEK); + /* tmp = strstr(tmpbuf, "\r\n\r\n"); */ + if (len > 0) { + headlen = headersEnd(tmpbuf, len); + if (headlen) + /* End of ICAP header found - Read the \r\n\r\n too */ + len = headlen; + else { + /* Header is bigger */ + read_sz *=2; + if (read_sz < SQUID_TCP_SO_RCVBUF) continue; + } + if (!isIcap) + isIcap = !(!strstr(tmpbuf, "ICAP")); + } + if (!len && !headlen) { + /* No data */ + cbdataLock(httpState); + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, httpState, + 0); + commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, + httpState); + /* commSetDefer(fd, fwdCheckDeferRead, entry); */ + return; + } + } + if (!isIcap && headlen) { + /* Could be sending the HTTP reply directly without ICAP header - bug + * but still allow :-) */ + if (strstr(tmpbuf, "HTTP")) + directResponse = 1; + } else { + /* Read the ICAP header only - leave the rest */ + len = read(fd, icap_header, headlen); + if (len > 0) { + icap_header[headlen] = '\0'; + debug(21, 3) ("Read icap header : <%s>\n", icap_header); + ver = -999.999; /* initalize the version to a bogus number. I + * think that we should parse it using 2 + * integers and a %d.%d scanf format - Basile + * june 2002 */ + if (sscanf(icap_header, "ICAP/%f %d %s\r", &ver, &status, + tmpbuf) < 3 || ver <= 0.0) { + debug(21, 1) ("BAD ICAP status line <%s>\n", icap_header); + /* is this correct in case of ICAP protocol error? */ + err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->request = requestLink((request_t *) httpState->request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + return; + }; + /* printf("Version = %g, Status = %d, String = %s\n",ver,status,tmpbuf); */ + } + + if ((httpState->icap->state & ICAP_WaitForPreviewReply) != 0) { + if (status == 100) { + debug(21, 5) ("icapRespModReadReply: 100 Continue received, httpState=%p\n", httpState); + httpState->icap->state &= ~ICAP_WaitForPreviewReply; + /* call again icapSendRespMod to handle data that was received while waiting fot this ICAP response */ + icapSendRespMod (httpState, NULL, 0, 0); + return; + } else if (status == 204) { + IcapStateData *icap = httpState->icap; + comm_close(fd); + ICAP_FIELD(httpState) = NULL; + if ((icap->state & ICAP_EndOfInputData) != 0) { + /* Reset is required to avoid duplicate stmemFreeDataUpto , + * but will I loose all info now ? */ + /* storeEntryReset(entry); */ + /* stmemFreeDataUpto(&(entry->mem_obj->data_hdr), -icap->sc); */ + fwdComplete(httpState->fwd); + } else { + cbdataLock(httpState); + commSetSelect(httpState->fd, COMM_SELECT_READ, httpReadReply, + httpState, 0); + } + icapStateFree(icap); + return; + } + } + /* if (!headlen || len != headlen || status != 200) */ + if (!headlen || len != headlen || status < 200 || status > 300) { + /* Did not find a proper ICAP response */ + debug(21, 3) ("ICAP : Error path!\n"); + storeEntryReset(entry); + err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->request = requestLink((request_t *) httpState->request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + return; + } + /* directResponse = !(!strstr(icap_header,"res-body")); res-hdr or res-body */ + directResponse = !(!strstr(icap_header, "res-")); + } + + /* Check whether it is a direct reply - if so over to http part */ + if (!directResponse) { + /* Did not find a proper ICAP response */ + debug(21, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->request = requestLink((request_t *) httpState->request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + return; + } + /* got the reply, no need to come here again */ + httpState->icap->state &= ~ICAP_WaitForReply; + httpState->icap->state |= ICAP_GotReply; + /* Read the response here */ + icapReadResponse(fd, httpState); +} + + + +void +icapReadResponse(int fd, HttpStateData * httpState) +{ + StoreEntry *entry; + MemObject *mem; + entry = httpState->entry; + mem = entry->mem_obj; + + assert (httpState->icap); + debug(21, 5) ("icapReadResponse: fd %d, httpState %p, ICAP state flags %X\n", fd, httpState, httpState->icap->state); + /* do not delete the ICAP field here. + * it is needed to route data now still coming from the HTTP + * connection up to the ICAP server + icapStateFree(ICAP_FIELD(httpState)); + ICAP_FIELD(httpState) = NULL; + */ + httpState->fd = fd; /* now read the HTTP data from the ICAP connection */ + comm_add_close_handler(fd, httpStateFree, httpState); + /* stmemFreeDataUpto(&mem->data_hdr, -icap->sc); */ + storeEntryReset(entry); +#ifdef ICAP_CHUNKED + httpState->reply_hdr_state = 0; /* parse modified HTTP header */ + httpState->reply_hdr_size = 0; + httpState->icap->chunk_size = 0; /* body comes in chunked encoding */ +#endif + scheduleReadReply(fd, httpState); + cbdataUnlock(httpState); +} + + +void +icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + HttpStateData *httpState = data; + ErrorState *err; + StoreEntry *entry = httpState->entry; + + debug(21, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", + fd, size, errflag); + if (httpState->icap) { + debug(21, 5) ("icapSendRespModDone: ICAP state flags %X\n", httpState->icap->state); + } + if (cbdataValid(httpState)) { + if (httpState->icap && cbdataValid(httpState->icap)) { + if ((httpState->icap->state & ICAP_EndOfInputData) == 0) { + cbdataLock(httpState); + debug(21, 5) ("icapSendRespModDone: FD %d: commSetSelect on read httpReadReply get more orig data.\n", httpState->icap->http_fd); + commSetSelect(httpState->icap->http_fd, COMM_SELECT_READ, httpReadReply, + httpState, 0); + } + } + } + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.server.all.kbytes_out, size); + kb_incr(&statCounter.server.http.kbytes_out, size); + } + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->xerrno = errno; + if (cbdataValid(httpState)) + err->request = requestLink(httpState->request); + storeEntryReset(entry); + errorAppendEntry(entry, err); + comm_close(fd); + return; + } + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; + } + if (httpState->icap && + (httpState->icap->state & (ICAP_WaitForPreviewReply | ICAP_WaitForReply)) != 0) { + /* Schedule reading the ICAP response */ + debug(21, 3) ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", fd); + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, httpState, 0); + cbdataLock(httpState); + if ((httpState->icap->state & (ICAP_WaitForPreviewReply | ICAP_EndOfInputData)) != 0) { + /* + * Set the read timeout only after all data has been sent + * or we are waiting for a preview response + * If the ICAP server does not return any data till all data + * has been sent, we are likely to hit the timeout for large + * HTTP bodies + */ + commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, httpState); + } + } +} + +void +icapConnectOver(int fd, int status, void *data) +{ + ErrorState *err; + HttpStateData *httpState = data; + StoreEntry *entry = httpState->entry; + if (status < 0) { + err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); + err->xerrno = errno; + err->request = requestLink(httpState->orig_request); + errorAppendEntry(entry, err); + cbdataUnlock(httpState); + ICAP_FIELD(httpState) = NULL; + comm_close(fd); + return; + } + fd = httpState->fd; /* Now read from upstream server */ + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); + /* Read fully - don't put limits on the read ahead count */ + /* Oh! I broke my head for this */ + commSetDefer(fd, NULL, NULL); +} + +int +icapSendRespMod(HttpStateData * httpState, void *buf, int len, int theEnd) +{ + MemBuf mb; + IcapStateData *icap = ICAP_FIELD(httpState); + int size; + const int preview_size = icap->preview_size; + char ch; + debug(21, 5) ("icapSendRespMod: httpState %p: len %d, theEnd %d, icap state %X.\n", + httpState, len, theEnd, icap->state); + memBufDefInit(&mb); + + if (theEnd) + icap->state |= ICAP_EndOfInputData; + + if (icap->sc == 0) { + /* No data sent yet. Start with headers */ + icap->sc = buildRespModHeader (&mb, httpState, buf, len, theEnd); + buf = ((char*) buf) + icap->sc; + len -= icap->sc; + } + + if (preview_size < 0) /* preview feature off */ + icap->state |= ICAP_PreviewDone; + + if ((icap->state & ICAP_PreviewDone) == 0) { + /* preview not yet sent */ + if (icap->sc > 0 && icap->buffer.size <= preview_size && len > 0) { + /* Try to collect at least preview_size+1 bytes */ + /* By collecting one more byte than needed for preview we know best */ + /* whether we have to send the ieof chunk extension */ + size = icap->buffer.size + len; + if (size > preview_size+1) + size = preview_size+1; + size -= icap->buffer.size; + debug(21, 3) ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", icap->http_fd, size); + memBufAppend (&icap->buffer, buf, size); + buf = ((char*) buf) + size; + len -= size; + } + + if (icap->buffer.size > preview_size || theEnd) { + /* we got enough bytes for preview or this is the last call */ + /* add preview preview now */ + if (icap->buffer.size > 0) { + size = icap->buffer.size; + if (size > preview_size) + size = preview_size; + memBufPrintf(&mb, "%x\r\n", size); + memBufAppend(&mb, icap->buffer.buf, size); + memBufAppend(&mb, crlf, 2); + icap->sc += size; + } + if (icap->buffer.size <= preview_size) { + /* content length is less than preview size+1 */ + memBufAppend(&mb, "0; ieof\r\n\r\n", 11); + memBufReset (&icap->buffer); /* will now be used for other data */ + } else { + memBufAppend(&mb, "0\r\n\r\n", 5); + /* end of preview, wait for continue or 204 signal */ + /* copy the extra byte and all other data to the icap buffer */ + /* so that it can be handled next time */ + ch = icap->buffer.buf[preview_size]; + memBufReset (&icap->buffer); /* will now be used for other data */ + memBufAppend(&icap->buffer, &ch, 1); + debug(21, 3) ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", icap->http_fd, len+1); + if (len > 0) + memBufAppend(&icap->buffer, buf, len); + } + icap->state |= ICAP_PreviewDone | ICAP_WaitForPreviewReply; + } + } else if ((icap->state & ICAP_WaitForPreviewReply) != 0) { + /* received new data while waiting for preview response */ + /* add data to internal buffer and send later */ + debug(21, 3) ("icapSendRespMod: FD %d: add %d more bytes to internal buf while wiaiting for preview-response.\n", icap->http_fd, len); + if (len > 0) + memBufAppend (&icap->buffer, buf, len); + /* do not send any data now while waiting for preview response */ + /* but prepare for read more data on the HTTP connection */ + if ((icap->state & ICAP_EndOfInputData) == 0) { + cbdataLock (httpState); + debug(21, 3) ("icapSendRespMod: FD %d: commSetSelect on read httpReadReply waiting for preview response.\n", icap->http_fd); + commSetSelect(icap->http_fd, COMM_SELECT_READ, httpReadReply, + httpState, 0); + } + return 0; + } else { + /* after preview completed and ICAP preview response received*/ + /* there may still be some data in the buffer */ + if (icap->buffer.size > 0) { + memBufPrintf(&mb, "%x\r\n", icap->buffer.size); + memBufAppend(&mb, icap->buffer.buf, icap->buffer.size); + memBufAppend(&mb, crlf, 2); + icap->sc += icap->buffer.size; + memBufReset (&icap->buffer); + } + if (len > 0) { + memBufPrintf(&mb, "%x\r\n", len); + memBufAppend(&mb, buf, len); + memBufAppend(&mb, crlf, 2); + icap->sc += len; + } + if ((icap->state & ICAP_EndOfInputData) != 0) { + /* send zero end chunk */ + memBufAppend(&mb, "0\r\n\r\n", 5); + } + /* wait for data coming from ICAP server as soon as we sent something */ + /* but of course only until we got the response header */ + if ((icap->state & ICAP_GotReply) == 0) + icap->state |= ICAP_WaitForReply; + } + commSetDefer(icap->icap_fd, NULL, NULL); + commSetTimeout(icap->icap_fd, -1, NULL, NULL); + + cbdataLock(icap); + cbdataLock(httpState); + comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, httpState); + return 1; +} + +int +buildRespModHeader(MemBuf * mb, HttpStateData * httpState, char *buf, + ssize_t len, int theEnd) +{ + IcapStateData *icap = ICAP_FIELD(httpState); + MemBuf mb_hdr; + char *client_addr; + request_t *req = httpState->request; + StoreEntry *entry = httpState->entry; + int o2, o3, hlen = 0; + HttpReply *reply = httpState->entry->mem_obj->reply; + icap_service *service; + + debug(21, 5) ("icapbuildRespModHeader: httpState %p: len %d.\n", httpState, + len); + /* Copy request header */ + memBufDefInit(&mb_hdr); + httpBuildRequestPrefix(req, httpState->orig_request, entry, &mb_hdr, httpState->flags); + o2 = mb_hdr.size; + + /* Copy response header - Append to request header mbuffer */ + /* I have to send the response header separately because the + * response body goes with a len prefix, while buf has response as is . */ + if (strlen(httpState->reply_hdr) > 0) { + memBufAppend(&mb_hdr, httpState->reply_hdr, + strlen(httpState->reply_hdr)); + o3 = mb_hdr.size; + hlen = headersEnd(buf, len); +#if DEBUG + if (len < strlen(httpState->reply_hdr) || !hlen) + printf("Looks like there is some problem in respmod!\n"); +#endif + len -= hlen; + } else { + o3 = o2; + o2 = -1; + } + + service = icap->current_service; + assert(service); + client_addr = inet_ntoa(httpState->orig_request->client_addr); + + /* What if the buf contains only header info */ + if ((len > 0 || reply->content_length > 0) || !theEnd) + getICAPRespModString(mb, 0, o2, o3, service->uri, client_addr); + else +#ifdef ICAP_CHUNKED + getICAPRespModString(mb, 0, o2, -o3, service->uri, client_addr); +#else + getICAPRespModString(mb, 0, o2, -1, service->uri, client_addr); +#endif + if (icap->preview_size >= 0 ) + memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); + + memBufAppend(mb, "Connection: close\r\n", 19); + memBufAppend(mb, crlf, 2); + memBufAppend(mb, mb_hdr.buf, mb_hdr.size); + memBufClean(&mb_hdr); + + return hlen; +} + + +/* Schedule read reply. */ +void +scheduleReadReply(int fd, void *data) +{ + HttpStateData *httpState = data; + if (httpState->orig_request->flags.do_icap) + if (startICAPrespmod(httpState) >= 0) + return; + debug(21, 3) ("scheduleReadReply: FD %d, httpState %p: call httpReadReply directly.\n", fd, httpState); + httpReadReply(fd, httpState); +} + + + +int +startICAPrespmod(HttpStateData * httpState) +{ + IcapStateData *icap; + int fd; + icap_service_list *isl_iter; + icap_service *service = NULL; + icap_service_t type; + + if (ICAP_FIELD(httpState) || + !httpState->orig_request->flags.do_icap) + return -1; + + isl_iter = httpState->orig_request->class->isl; + + while (isl_iter) { + type = isl_iter->service->type; + if (type == ICAP_SERVICE_RESPMOD_PRECACHE + || type == ICAP_SERVICE_RESPMOD_POSTCACHE) { + service = isl_iter->service; + break; + } + isl_iter = isl_iter->next; + } + + if (!service) + return -1; /* no service found */ + + icap = startICAP(service->uri); + if (!icap) + return -1; + + icap->current_service = service; + icap->preview_size = service->preview; + ICAP_FIELD(httpState) = icap; + fd = icap->icap_fd; + icap->http_fd = httpState->fd; + + /* Dont send to client just now */ + storeBuffer(httpState->entry); +#if 1 /* TEMP */ + EBIT_CLR(httpState->entry->flags, ENTRY_CACHABLE); +#endif + cbdataLock(httpState); + + commConnectStart(icap->icap_fd, service->hostname, service->port, + icapConnectOver, httpState); + commSetTimeout(fd, Config.Timeout.connect, icapConnectTimeout, httpState); + return 0; +} + +/**********************************************************************/ + +IcapStateData * +startICAP(const char *note) +{ + int fd; + IcapStateData *icap; + + /* fd = comm_open(SOCK_STREAM, 0, Config.Addrs.tcp_outgoing, + * 0, COMM_NONBLOCKING, p->host); */ + + /* fd = comm_open(SOCK_STREAM, 0, icap_server_addr, 0, */ + if (!Config.icapcfg.onoff) + return 0; + fd = comm_open(SOCK_STREAM, 0, no_addr, 0, COMM_NONBLOCKING, note); + if (fd < 0) + return NULL; + + /* + * icap = memAllocate(MEM_ICAP_STATE_DATA); + * cbdataAdd(icap, memFree, MEM_ICAP_STATE_DATA); + */ + icap = cbdataAlloc(IcapStateData); + icap->icap_fd = fd; + icap->enc_res_hdr = -1; + icap->enc_null_body = -1; + icap->enc_res_body = -1; + icap->state = 0; + icap->chunk_size = -1; + memBufDefInit(&icap->buffer); + + debug(21, 3) ("New ICAP connection: fd %d\n", fd); + return icap; +} + +void icapStateFree(IcapStateData *icap) +{ + if (icap) { + memBufClean(&icap->buffer); + cbdataFree (icap); + } +} + +int +icapInit() +{ +#ifdef ICAP_OPTIONS_REQUEST + icapOptInit(); +#endif + return 0; +} + +void +icapConnectTimeout(int fd, void *data) +{ + HttpStateData *httpState = data; + StoreEntry *entry = httpState->entry; + ErrorState *err; + debug(17, 2) ("icapConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); + + if (cbdataValid(httpState)) { + assert(fd == ICAP_FIELD(httpState)->icap_fd); + cbdataUnlock(httpState); + } + if (entry->mem_obj->inmem_hi == 0) { + err = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT); + err->request = requestLink(httpState->request); + err->xerrno = ETIMEDOUT; + comm_close(fd); + } +} + +void +getICAPReqModString(MemBuf * mb, int hdrLen, int bodyLen, char *service, char* client_addr) +{ + /* memBufAppend(mb, reqModString,strlen(reqModString)); */ + memBufPrintf(mb, "REQMOD %s ICAP/1.0\r\n", service); + memBufPrintf(mb, "Encapsulated: req-hdr=0"); + /* TODO: Change the offset using 'request' if needed */ + if (bodyLen > 0) + memBufPrintf(mb, ", req-body=%d", hdrLen); + else + memBufPrintf(mb, ", null-body=%d", hdrLen); + memBufAppend(mb, crlf, 2); + if (Config.icapcfg.send_client_ip) { + memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); + } +} + + +void +getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, char *service, char* client_addr) +{ + /* memBufAppend(mb, respModString,strlen(respModString)); */ + memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service); + if (o1 >= 0) + memBufPrintf(mb, " req-hdr=%1d", o1); + if (o2 >= 0) + memBufPrintf(mb, ", res-hdr=%1d", o2); + if (o3 >= 0) + memBufPrintf(mb, ", res-body=%1d", o3); + else + memBufPrintf(mb, ", null-body=%1d", -o3); + + memBufPrintf(mb, "\r\n"); + if (Config.icapcfg.send_client_ip) { + memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); + } +} + +/* + * this is done now with a squid.conf statement + */ +#if 0 +int +enable_icap(char *name) +{ + enable_icap_flag = 1; + + if (name) + icap_server_host = xstrdup(name); + return 0; +} +#endif + + +/*************************************************** + * service_list + */ +void +icapServiceListDump(icap_service_list * isl[]) +{ + icap_service_list *isl_element; + int i; + + for (i = 0; i < ICAP_SERVICE_MAX; i++) { + isl_element = isl[i]; + /* debug(81,5) ("icapDumpServiceList: %s\n", connState->chr->uri); */ + while (isl_element) { + debug(81, 5) ("%30s * %s\n", + icapServiceToStr(isl_element->service->type), + isl_element->service->name); + isl_element = isl_element->next; + } + } +} + +void +icapServiceListFree(icap_service_list * isl[]) +{ + int i; + icap_service_list *next; + + for (i = 0; i < ICAP_SERVICE_MAX; i++) { + while (isl[i]) { + next = isl[i]->next; + memFree(isl[i], MEM_ICAP_SERVICE_LIST); + isl[i] = next; + } + } +} + +/*************************************************** + * icap_service + */ + +icap_service_t +icapServiceToType(const char *s) +{ + if (!strcmp(s, "reqmod_precache")) + return ICAP_SERVICE_REQMOD_PRECACHE; + if (!strcmp(s, "reqmod_postcache")) + return ICAP_SERVICE_REQMOD_POSTCACHE; + if (!strcmp(s, "respmod_precache")) + return ICAP_SERVICE_RESPMOD_PRECACHE; + if (!strcmp(s, "respmod_postcache")) + return ICAP_SERVICE_RESPMOD_POSTCACHE; + return ICAP_SERVICE_MAX; +} + +extern const char *icap_service_type_str[]; + +const char * +icapServiceToStr(const icap_service_t type) +{ + if (type >= 0 && type < ICAP_SERVICE_MAX) + return icap_service_type_str[type]; + else + return "error"; +} + +/* + * code copied from acl.c because we need to differentiate three cases: + * match with allow (1), match with deny (2), no match (0). + */ +int +icapCheckFast(const acl_access * A, aclCheck_t * checklist) +{ + allow_t allow = ACCESS_DENIED; + debug(81, 5) ("icapCheckFast: list: %p\n", A); + while (A) { + allow = A->allow; + if (aclMatchAclList(A->aclList, checklist)) + return allow == ACCESS_ALLOWED; + A = A->next; + } + debug(81, 5) ("icapCheckFast: no matches, returning: %d\n", + allow == ACCESS_DENIED); + return allow == ACCESS_DENIED; +} + + +/* + * check wether we do icap for a request + */ +int +icapCheckAcl(clientHttpRequest * http) +{ + icap_access *iter; + aclCheck_t icapChecklist; + + memset(&icapChecklist, '\0', sizeof(aclCheck_t)); + icapChecklist.src_addr = http->conn->peer.sin_addr; + icapChecklist.request = http->request; + for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { + acl_access *A = iter->access; + if (aclMatchAclList(A->aclList, &icapChecklist)) { + debug(81, 5) ("icapCheckAcl: match for class=%s\n", + iter->class->name); + if (A->allow) { + /* allow rule, do icap and use associated class */ + http->request->class = iter->class; + return 1; + } else { + /* deny rule, stop processing */ + return 0; + } + } + } + return 0; +} + + +#if 0 +/* Made it a macro */ +getIcapService(HttpStateData * httpState, icap_service_t vpoint) +{ + icap_service_list **isl; + isl = httpState->orig_request->isl; + /* Selecting the first matching service */ + if (isl[vpoint]) + return isl[vpoint]->service; +} + +#endif + + +/* returns the amount of data until lineending ( \r or \r\n ) */ +static size_t +icapLineLength(char *start, int len) +{ + size_t lineLen = 0; + char *end = (char *) memchr(start, 0x0A, len); + if (end) { + lineLen = end - start + 1; + if (lineLen < len) { + if (end[1] == 0x0D) + lineLen++; + } + } + return lineLen; +} + +int +icapReadChunkSize(void *data, char *buf, int len) +{ + IcapStateData *icap = data; + int chunkSize = 0; + char c; + size_t start; + size_t end; + size_t nextStart = 0; + do { + start = nextStart; + if (len <= start) { + /* end of buffer, so far no lines or only empty lines, wait for more data */ + icap->chunk_size = 0; /* read chunk size with next buffer */ + return nextStart; + } + end = start + icapLineLength(buf + start, len - start); + nextStart = end; + if (end <= start) { + /* no line found, need more code here, now we are in deep trouble, buffer stops with */ + /* half a chunk size line. For now stop here. */ + icap->chunk_size = -2; // we are done + + return nextStart; + } + while (start < end) { + if (buf[start] != ' ' && buf[start] != '\t' && buf[start] != '\r' && buf[start] != '\n') + break; + start++; + } + while (start < end) { + if (buf[end - 1] != ' ' && buf[end - 1] != '\t' && buf[end - 1] != '\r' && buf[end - 1] != '\n') + break; + end--; + } + /* if now end <= start we got an empty line */ + /* The previous chunk data should stop with a CRLF. In case */ + /* that the other end does not follow the specs and sends no */ + /* CRLF or too many empty lines, just continue till we have */ + /* a non-empty line. */ + } while (end <= start); + + /* Non-empty line: Parse the chunk size */ + while (start < end) { + c = buf[start++]; + if (c >= 'a' && c <= 'f') { + chunkSize = chunkSize * 16 + c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + chunkSize = chunkSize * 16 + c - 'A' + 10; + } else if (c >= '0' && c <= '9') { + chunkSize = chunkSize * 16 + c - '0'; + } else { + if (!(c == ';' || c == ' ' || c == '\t')) { + /*Syntax error: Chunksize expected. */ + icap->chunk_size = -2; // we are done + + return nextStart; + } + /* Next comes a chunk extension */ + break; + } + } + /* if we read a zero chunk, we reached the end. Mark this for httpPconnTransferDone */ + icap->chunk_size = (chunkSize > 0) ? chunkSize : -2; + return nextStart; +} + +int +icapReadChunkedBody (int fd, void* data, int len, char* buf) +{ + HttpStateData *httpState = data; + StoreEntry *entry = httpState->entry; + int bufOffset; + + if (httpState->icap->chunk_size == -2) { + debug(21, 3) ("zero end chunk reached\n"); + len = 0; /* don't add anything else */ + } else if (httpState->reply_hdr_state == 2 && httpState->icap->chunk_size >= 0) { + /* read data which is transfered in chunked encoding */ + /* set bufOffset to beginning of body data */ + /* If header was already written to store, we can read from beginning */ + if (entry->mem_obj && entry->mem_obj->inmem_hi > 0) + bufOffset = 0; + else + bufOffset = headersEnd(httpState->reply_hdr, httpState->reply_hdr_size); + + /* Store the header if we didn't do so yet */ + assert (fd == httpState->fd); /* check the condition that the other storeAppends must fulfill */ + if (bufOffset > 0) + storeAppend(entry, buf, bufOffset); + + while (bufOffset < len) { + if (httpState->icap->chunk_size == 0) { + bufOffset += icapReadChunkSize(httpState->icap, buf + bufOffset, len - bufOffset); + debug(21, 3) ("got chunksize %d, new offset %d\n", httpState->icap->chunk_size, bufOffset); + if (httpState->icap->chunk_size == -2) { + debug(21, 3) ("zero end chunk reached\n"); + len = 0; /* don't add anything else */ + /* break; */ + /* let's try to close the connection here */ + fwdComplete(httpState->fwd); + comm_close(fd); + return 1; /* break from httpReadReply */ + } + } + if (httpState->icap->chunk_size > 0) { + if (httpState->icap->chunk_size >= len - bufOffset) { + storeAppend(entry, buf + bufOffset, len - bufOffset); + httpState->icap->chunk_size -= len - bufOffset; + bufOffset = len; + } else { + storeAppend(entry, buf + bufOffset, httpState->icap->chunk_size); + bufOffset += httpState->icap->chunk_size; + httpState->icap->chunk_size = 0; + } + } + } + } else { + if (fd == httpState->fd) + storeAppend(entry, buf, len); + } + return 0; +} + + Index: squid/src/main.c diff -u squid/src/main.c:1.39 squid/src/main.c:1.32.4.3 --- squid/src/main.c:1.39 Sun Nov 10 14:41:07 2002 +++ squid/src/main.c Fri Jan 3 04:29:00 2003 @@ -351,6 +351,9 @@ idnsInit(); #endif redirectInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif authenticateInit(&Config.authConfig); externalAclInit(); #if USE_WCCP @@ -394,6 +397,9 @@ dnsInit(); #endif redirectInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif authenticateInit(&Config.authConfig); externalAclInit(); } @@ -476,6 +482,9 @@ idnsInit(); #endif redirectInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif authenticateInit(&Config.authConfig); externalAclInit(); useragentOpenLog(); Index: squid/src/mem.c diff -u squid/src/mem.c:1.21 squid/src/mem.c:1.19.2.2 --- squid/src/mem.c:1.21 Sun Sep 15 04:06:33 2002 +++ squid/src/mem.c Fri Jan 3 04:29:00 2003 @@ -393,6 +393,13 @@ memDataInit(MEM_TLV, "storeSwapTLV", sizeof(tlv), 0); memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); +#ifdef HS_FEAT_ICAP + memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); + memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list),0); + memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); + memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); +#endif + /* init string pools */ for (i = 0; i < mem_str_pool_count; i++) { StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); Index: squid/src/mk-string-arrays.pl diff -u squid/src/mk-string-arrays.pl:1.2 squid/src/mk-string-arrays.pl:1.2.62.1 --- squid/src/mk-string-arrays.pl:1.2 Mon Oct 23 08:04:21 2000 +++ squid/src/mk-string-arrays.pl Fri Jan 3 04:29:00 2003 @@ -16,6 +16,7 @@ $pat{'icp_opcode'} = "icp_opcode_str"; $pat{'swap_log_op'} = "swap_log_op_str"; $pat{'lookup_t'} = "lookup_t_str"; +$pat{'icap_service_t'} = "icap_service_type_str"; $state = 0; # start state while (<>) { Index: squid/src/protos.h diff -u squid/src/protos.h:1.68 squid/src/protos.h:1.52.2.2 --- squid/src/protos.h:1.68 Sun Nov 10 14:41:07 2002 +++ squid/src/protos.h Fri Jan 3 04:29:00 2003 @@ -1338,4 +1338,23 @@ extern void externalAclShutdown(void); extern char *strtokFile(void); +#ifdef HS_FEAT_ICAP +/* icap.c */ +int icapInit(); +const char* icapServiceToStr(const icap_service_t type); +icap_service_t icapServiceToType(const char *s); +void icapServiceListFree(icap_service_list* isl[]); +void icapServiceListDump(icap_service_list* isl[]); +int icapCheckAcl( clientHttpRequest *http ); +void icapStateFree (IcapStateData *icap); +int icapReadChunkSize(void *data, char *buf, int len); +int icapReadChunkedBody (int fd, void* data, int len, char* buf); +int icapSendRespMod(HttpStateData * httpState, void *buf, int len, int theEnd); +int startICAPrespmod(HttpStateData * httpState); +int startICAPreqmod(HttpStateData * httpState); +/* icap_opt.c */ +void icapOptInit(); +void icapOptShutdown(); +#endif + #endif /* SQUID_PROTOS_H */ Index: squid/src/squid.h diff -u squid/src/squid.h:1.22 squid/src/squid.h:1.16.2.2 --- squid/src/squid.h:1.22 Mon Oct 7 14:45:41 2002 +++ squid/src/squid.h Fri Jan 3 04:29:01 2003 @@ -37,6 +37,10 @@ #include "config.h" +#ifdef HS_FEAT_ICAP +#define ICAP_CHUNKED 1 +#endif + #if PURIFY #define assert(EX) ((void)0) #elif defined(NODEBUG) @@ -412,3 +416,4 @@ #define FD_WRITE_METHOD(fd, buf, len) (*fd_table[fd].write_method)(fd, buf, len) #endif /* SQUID_H */ + Index: squid/src/store_client.c diff -u squid/src/store_client.c:1.16 squid/src/store_client.c:1.12.2.2 --- squid/src/store_client.c:1.16 Thu Sep 26 14:46:06 2002 +++ squid/src/store_client.c Tue Dec 10 00:23:59 2002 @@ -1,4 +1,6 @@ + + /* * $Id$ * Index: squid/src/structs.h diff -u squid/src/structs.h:1.71 squid/src/structs.h:1.55.2.3 --- squid/src/structs.h:1.71 Sun Nov 10 14:41:08 2002 +++ squid/src/structs.h Fri Jan 3 04:29:02 2003 @@ -373,6 +373,18 @@ wordlist *args; }; +#if HS_FEAT_ICAP +struct _IcapConfig { + int onoff; + icap_service *service_head; + /* icap_service_list *service_list_head; */ + icap_class *class_head; + icap_access *access_head; + int preview_size; + int send_client_ip; +}; +#endif /* HS_FEAT_ICAP */ + struct _SquidConfig { struct { size_t maxSize; @@ -696,6 +708,10 @@ } warnings; char *store_dir_select_algorithm; int sleep_after_fork; /* microseconds */ +#ifdef HS_FEAT_ICAP + char *icap_mode; + IcapConfig icapcfg; +#endif external_acl *externalAclHelperList; }; @@ -974,6 +990,76 @@ unsigned int only_if_cached:1; }; +#ifdef HS_FEAT_ICAP +struct _IcapStateData { + int icap_fd; + int http_fd; + int sc; + icapState state; + icap_service *current_service; + int enc_res_hdr; + int enc_null_body; + int enc_res_body; + ushort reqmod; + MemBuf buffer; + int chunk_size; + int preview_size; +}; + +struct _icap_service { + icap_service *next; + char *name; /* name to be used when referencing ths service */ + char *uri; /* uri of server/service to use */ + char *type_name; /* {req|resp}mod_{pre|post}cache */ + + char *hostname; + unsigned short int port; + char *resource; + icap_service_t type; /* parsed type */ + icap_method_t method; + ushort bypass; /* flag: bypass allowed */ + ushort unreachable; /* flag: set to 1 if options request fails */ + IcapOptData *opt; /* temp data needed during opt request */ + struct { + unsigned int allow_204:1; + } flags; + int preview; + String istag; + String transfer_preview; + String transfer_ignore; + String transfer_complete; + int max_connections; + int options_ttl; +}; + +struct _icap_service_list { + icap_service_list* next; + icap_service* service; +}; + +struct _icap_class { + icap_class *next; + char* name; + wordlist* services; + icap_service_list *isl; + ushort hidden; /* for unnamed classes */ +}; + +struct _icap_access { + icap_access *next; + char *service_name; + icap_class *class; + acl_access *access; +}; + +struct _IcapOptData { + char *buf; + off_t offset; + size_t size; +}; + +#endif + struct _HttpStateData { StoreEntry *entry; request_t *request; @@ -986,6 +1072,9 @@ int fd; http_state_flags flags; FwdState *fwd; +#ifdef HS_FEAT_ICAP + struct _IcapStateData *icap; +#endif }; struct _icpUdpData { @@ -1593,6 +1682,9 @@ unsigned int internalclient:1; unsigned int body_sent:1; unsigned int reset_tcp:1; +#ifdef HS_FEAT_ICAP + unsigned int do_icap:1; +#endif }; struct _link_list { @@ -1649,6 +1741,11 @@ char *peer_login; /* Configured peer login:password */ time_t lastmod; /* Used on refreshes */ const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ +#if HS_FEAT_ICAP + /* linked lists of pointers to icap_services to use */ + /* icap_service_list* isl[ICAP_SERVICE_MAX]; */ + icap_class *class; +#endif }; struct _cachemgr_passwd { Index: squid/src/typedefs.h diff -u squid/src/typedefs.h:1.29 squid/src/typedefs.h:1.26.4.3 --- squid/src/typedefs.h:1.29 Tue Sep 24 03:59:17 2002 +++ squid/src/typedefs.h Fri Jan 3 04:29:03 2003 @@ -107,6 +107,15 @@ typedef struct _HttpBody HttpBody; typedef struct _HttpReply HttpReply; typedef struct _HttpStateData HttpStateData; +#ifdef HS_FEAT_ICAP +typedef struct _IcapStateData IcapStateData; +typedef struct _IcapConfig IcapConfig; +typedef struct _icap_service icap_service; +typedef struct _icap_service_list icap_service_list; +typedef struct _icap_class icap_class; +typedef struct _icap_access icap_access; +typedef struct _IcapOptData IcapOptData; +#endif typedef struct _icpUdpData icpUdpData; typedef struct _clientHttpRequest clientHttpRequest; typedef struct _ConnStateData ConnStateData;