martin 98/08/16 13:21:30
Modified: src CHANGES src/include ap_mmn.h src/modules/proxy mod_proxy.c mod_proxy.h proxy_cache.c proxy_connect.c proxy_ftp.c proxy_http.c proxy_util.c Log: Modify the proxy to use tables instead of array_headers for header lines. That simplifies some code, but changes many of the proxy-internal interfaces. It should also prevent the proxy from merging multiple "Set-Cookie:" headers into one. Revision Changes Path 1.1027 +3 -0 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1026 retrieving revision 1.1027 diff -u -u -r1.1026 -r1.1027 --- CHANGES 1998/08/15 15:02:44 1.1026 +++ CHANGES 1998/08/16 20:21:24 1.1027 @@ -1,5 +1,8 @@ Changes with Apache 1.3.2 + *) Change the proxy to use tables instead of array_headers for + the header lines. [Martin Kraemer] + *) Make sure the config.status file is not overridden when just ``configure --help'' is used. [Ralf S. Engelschall] PR#2844 1.4 +9 -2 apache-1.3/src/include/ap_mmn.h Index: ap_mmn.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/ap_mmn.h,v retrieving revision 1.3 retrieving revision 1.4 diff -u -u -r1.3 -r1.4 --- ap_mmn.h 1998/08/14 02:49:09 1.3 +++ ap_mmn.h 1998/08/16 20:21:25 1.4 @@ -66,6 +66,7 @@ * * MODULE_MAGIC_NUMBER_MINOR * Minor API changes that do not cause binary compatibility problems. + * Should be reset to 0 when upgrading MODULE_MAGIC_NUMBER_MAJOR. * * See the MODULE_MAGIC_AT_LEAST macro below for an example. */ @@ -164,12 +165,18 @@ * ap_get_limit_req_body() to get its value. * 19980812 (1.3.2-dev) - split off MODULE_MAGIC_NUMBER * 19980812.2 - add ap_overlap_tables() + * 19980816 (1.3.2-dev) - change proxy to use tables for headers, change + * struct cache_req to typedef cache_req. + * Delete ap_proxy_get_header(), ap_proxy_add_header(), + * ap_proxy_del_header(). Change interface of + * ap_proxy_send_fb() and ap_proxy_cache_error(). + * Add ap_proxy_send_hdr_line() and ap_proxy_bputs2(). */ #ifndef MODULE_MAGIC_NUMBER_MAJOR -#define MODULE_MAGIC_NUMBER_MAJOR 19980812 +#define MODULE_MAGIC_NUMBER_MAJOR 19980816 #endif -#define MODULE_MAGIC_NUMBER_MINOR 2 +#define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */ #define MODULE_MAGIC_NUMBER MODULE_MAGIC_NUMBER_MAJOR /* backward compat */ /* Useful for testing for features. */ 1.59 +1 -1 apache-1.3/src/modules/proxy/mod_proxy.c Index: mod_proxy.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/proxy/mod_proxy.c,v retrieving revision 1.58 retrieving revision 1.59 diff -u -u -r1.58 -r1.59 --- mod_proxy.c 1998/08/06 17:30:40 1.58 +++ mod_proxy.c 1998/08/16 20:21:26 1.59 @@ -292,7 +292,7 @@ array_header *proxies = conf->proxies; struct proxy_remote *ents = (struct proxy_remote *) proxies->elts; int i, rc; - struct cache_req *cr; + cache_req *cr; int direct_connect = 0; const char *maxfwd_str; 1.38 +26 -17 apache-1.3/src/modules/proxy/mod_proxy.h Index: mod_proxy.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/proxy/mod_proxy.h,v retrieving revision 1.37 retrieving revision 1.38 diff -u -u -r1.37 -r1.38 --- mod_proxy.h 1998/07/09 19:45:56 1.37 +++ mod_proxy.h 1998/08/16 20:21:27 1.38 @@ -55,6 +55,9 @@ * */ +#ifndef MOD_PROXY_H +#define MOD_PROXY_H + /* * Main include file for the Apache proxy */ @@ -218,7 +221,7 @@ }; /* caching information about a request */ -struct cache_req { +typedef struct { request_rec *req; /* the request */ char *url; /* the URL requested */ char *filename; /* name of the cache file, or NULL if no cache */ @@ -237,35 +240,41 @@ unsigned int written; /* total *content* bytes written to cache */ float cache_completion; /* specific to this request */ char *resp_line; /* the whole status like (protocol, code + message) */ - array_header *hdrs; /* the HTTP headers of the file */ + table *hdrs; /* the HTTP headers of the file */ +} cache_req; + +/* Additional information passed to the function called by ap_table_do() */ +struct tbl_do_args { + request_rec *req; + cache_req *cache; }; /* Function prototypes */ /* proxy_cache.c */ -void ap_proxy_cache_tidy(struct cache_req *c); +void ap_proxy_cache_tidy(cache_req *c); int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, - struct cache_req **cr); -int ap_proxy_cache_update(struct cache_req *c, array_header *resp_hdrs, + cache_req **cr); +int ap_proxy_cache_update(cache_req *c, table *resp_hdrs, const int is_HTTP1, int nocache); void ap_proxy_garbage_coll(request_rec *r); /* proxy_connect.c */ -int ap_proxy_connect_handler(request_rec *r, struct cache_req *c, char *url, +int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, const char *proxyhost, int proxyport); /* proxy_ftp.c */ int ap_proxy_ftp_canon(request_rec *r, char *url); -int ap_proxy_ftp_handler(request_rec *r, struct cache_req *c, char *url); +int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url); /* proxy_http.c */ int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_port); -int ap_proxy_http_handler(request_rec *r, struct cache_req *c, char *url, +int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, const char *proxyhost, int proxyport); /* proxy_util.c */ @@ -277,19 +286,14 @@ char *ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp, char **passwordp, char **hostp, int *port); const char *ap_proxy_date_canon(pool *p, const char *x); -array_header *ap_proxy_read_headers(pool *p, char *buffer, int size, BUFF *f); -long int ap_proxy_send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c); -struct hdr_entry *ap_proxy_get_header(array_header *hdrs_arr, const char *name); -struct hdr_entry *ap_proxy_add_header(array_header *hdrs_arr, const char *field, - const char *value, int rep); -void ap_proxy_del_header(array_header *hdrs_arr, const char *field); -void ap_proxy_send_headers(request_rec *r, const char *respline, - array_header *hdrs_arr); +table *ap_proxy_read_headers(pool *p, char *buffer, int size, BUFF *f); +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c); +void ap_proxy_send_headers(request_rec *r, const char *respline, table *hdrs); int ap_proxy_liststr(const char *list, const char *val); void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength); int ap_proxy_hex2sec(const char *x); void ap_proxy_sec2hex(int t, char *y); -BUFF *ap_proxy_cache_error(struct cache_req *r); +cache_req *ap_proxy_cache_error(cache_req *r); int ap_proxyerror(request_rec *r, const char *message); const char *ap_proxy_host2addr(const char *host, struct hostent *reqhp); int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p); @@ -298,3 +302,8 @@ int ap_proxy_is_word(struct dirconn_entry *This, pool *p); int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r); int ap_proxy_garbage_init(server_rec *, pool *); +/* This function is called by ap_table_do() for all header lines */ +int ap_proxy_send_hdr_line(void *p, const char *key, const char *value); +unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache); + +#endif /*MOD_PROXY_H*/ 1.50 +50 -56 apache-1.3/src/modules/proxy/proxy_cache.c Index: proxy_cache.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/proxy/proxy_cache.c,v retrieving revision 1.49 retrieving revision 1.50 diff -u -u -r1.49 -r1.50 --- proxy_cache.c 1998/08/06 17:30:41 1.49 +++ proxy_cache.c 1998/08/16 20:21:27 1.50 @@ -535,7 +535,7 @@ * 0 on failure (bad file or wrong URL) * -1 on UNIX error */ -static int rdcache(pool *p, BUFF *cachefp, struct cache_req *c) +static int rdcache(pool *p, BUFF *cachefp, cache_req *c) { char urlbuff[1034], *strp; int len; @@ -544,7 +544,7 @@ * date SP lastmod SP expire SP count SP content-length CRLF * dates are stored as hex seconds since 1970 */ - len = ap_bgets(urlbuff, 1034, cachefp); + len = ap_bgets(urlbuff, sizeof urlbuff, cachefp); if (len == -1) return -1; if (len == 0 || urlbuff[len - 1] != '\n') @@ -562,7 +562,7 @@ c->len = ap_proxy_hex2sec(urlbuff + 36); /* check that we have the same URL */ - len = ap_bgets(urlbuff, 1034, cachefp); + len = ap_bgets(urlbuff, sizeof urlbuff, cachefp); if (len == -1) return -1; if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 || @@ -573,7 +573,7 @@ return 0; /* What follows is the message */ - len = ap_bgets(urlbuff, 1034, cachefp); + len = ap_bgets(urlbuff, sizeof urlbuff, cachefp); if (len == -1) return -1; if (len == 0 || urlbuff[len - 1] != '\n') @@ -586,16 +586,13 @@ return 0; c->status = atoi(strp); - c->hdrs = ap_proxy_read_headers(p, urlbuff, 1034, cachefp); + c->hdrs = ap_proxy_read_headers(p, urlbuff, sizeof urlbuff, cachefp); if (c->hdrs == NULL) return -1; if (c->len != -1) { /* add a content-length header */ - struct hdr_entry *q; - q = ap_proxy_get_header(c->hdrs, "Content-Length"); - if (q == NULL) { - strp = ap_palloc(p, 15); - ap_snprintf(strp, 15, "%lu", (unsigned long)c->len); - ap_proxy_add_header(c->hdrs, "Content-Length", strp, HDR_REP); + if (ap_table_get(c->hdrs, "Content-Length") == NULL) { + ap_table_set(c->hdrs, "Content-Length", + ap_psprintf(p, "%lu", (unsigned long)c->len)); } } return 1; @@ -617,11 +614,11 @@ * last modified date to request */ int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, - struct cache_req **cr) + cache_req **cr) { char hashfile[66]; const char *imstr, *pragma, *auth; - struct cache_req *c; + cache_req *c; time_t now; BUFF *cachefp; int cfd, i; @@ -630,7 +627,7 @@ proxy_server_conf *pconf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); - c = ap_pcalloc(r->pool, sizeof(struct cache_req)); + c = ap_pcalloc(r->pool, sizeof(cache_req)); *cr = c; c->req = r; c->url = ap_pstrdup(r->pool, url); @@ -696,7 +693,7 @@ /* fixed? in this case, we want to get the headers from the remote server it will be handled later if we don't do this (I hope ;-) if (cachefp == NULL) - c->hdrs = ap_make_array(r->pool, 2, sizeof(struct hdr_entry)); + c->hdrs = ap_make_table(r->pool, 20); */ /* FIXME: Shouldn't we check the URL somewhere? */ now = time(NULL); @@ -708,16 +705,12 @@ /* has the cached file changed since this request? */ if (c->date == BAD_DATE || c->date > c->ims) { /* No, but these header values may have changed, so we send them with the - * 304 response + * 304 HTTP_NOT_MODIFIED response */ - /* CHECKME: surely this was wrong? (Ben) - p = table_get(r->headers_in, "Expires"); - */ - struct hdr_entry *q; - - q = ap_proxy_get_header(c->hdrs, "Expires"); - if (q != NULL && q->value != NULL) - ap_table_set(r->headers_out, "Expires", q->value); + const char *q; + + if ((q = ap_table_get(c->hdrs, "Expires")) != NULL) + ap_table_set(r->headers_out, "Expires", q); } ap_pclosef(r->pool, cachefp->fd); Explain0("Use local copy, cached file hasn't changed"); @@ -736,7 +729,7 @@ ap_bsetopt(r->connection->client, BO_BYTECT, &zero); r->sent_bodyct = 1; if (!r->header_only) - ap_proxy_send_fb(cachefp, r, NULL, NULL); + ap_proxy_send_fb(cachefp, r, NULL); ap_pclosef(r->pool, cachefp->fd); return OK; } @@ -751,13 +744,11 @@ * from the cache */ if (c->ims == BAD_DATE || c->ims < c->lmod) { - struct hdr_entry *q; - - q = ap_proxy_get_header(c->hdrs, "Last-Modified"); + const char *q; - if (q != NULL && q->value != NULL) + if ((q = ap_table_get(c->hdrs, "Last-Modified")) != NULL) ap_table_set(r->headers_in, "If-Modified-Since", - (char *) q->value); + (char *) q); } } c->fp = cachefp; @@ -779,7 +770,7 @@ * from the cache, maybe updating the header line * otherwise, delete the old cached file and open a new temporary file */ -int ap_proxy_cache_update(struct cache_req *c, array_header *resp_hdrs, +int ap_proxy_cache_update(cache_req *c, table *resp_hdrs, const int is_HTTP1, int nocache) { #ifdef ULTRIX_BRAIN_DEATH @@ -788,7 +779,7 @@ request_rec *r = c->req; char *p; int i; - struct hdr_entry *expire, *dates, *lmods, *clen; + const char *expire, *lmods, *dates, *clen; time_t expc, date, lmod, now; char buff[46]; void *sconf = r->server->module_config; @@ -802,21 +793,20 @@ /* read expiry date; if a bad date, then leave it so the client can * read it */ - expire = ap_proxy_get_header(resp_hdrs, "Expires"); + expire = ap_table_get(resp_hdrs, "Expires"); if (expire != NULL) - expc = ap_parseHTTPdate(expire->value); + expc = ap_parseHTTPdate(expire); else expc = BAD_DATE; /* * read the last-modified date; if the date is bad, then delete it */ - lmods = ap_proxy_get_header(resp_hdrs, "Last-Modified"); + lmods = ap_table_get(resp_hdrs, "Last-Modified"); if (lmods != NULL) { - lmod = ap_parseHTTPdate(lmods->value); + lmod = ap_parseHTTPdate(lmods); if (lmod == BAD_DATE) { /* kill last modified date */ - lmods->value = NULL; lmods = NULL; } } @@ -826,16 +816,18 @@ /* * what responses should we not cache? * Unknown status responses and those known to be uncacheable - * 304 response when we have no valid cache file, or - * 200 response from HTTP/1.0 and up without a Last-Modified header, or + * 304 HTTP_NOT_MODIFIED response when we have no valid cache file, or + * 200 HTTP_OK response from HTTP/1.0 and up without a Last-Modified header, or * HEAD requests, or * requests with an Authorization header, or * protocol requests nocache (e.g. ftp with user/password) */ - if ((r->status != 200 && r->status != 301 && r->status != 304) || +/* @@@ XXX FIXME: is the test "r->status != HTTP_MOVED_PERMANENTLY" corerct? + * or shouldn't it be "is_HTTP_REDIRECT(r->status)" ? -MnKr */ + if ((r->status != HTTP_OK && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) || (expire != NULL && expc == BAD_DATE) || - (r->status == 304 && c->fp == NULL) || - (r->status == 200 && lmods == NULL && is_HTTP1) || + (r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) || + (r->status == HTTP_OK && lmods == NULL && is_HTTP1) || r->header_only || ap_table_get(r->headers_in, "Authorization") != NULL || nocache) { @@ -855,9 +847,9 @@ /* * Read the date. Generate one if one is not supplied */ - dates = ap_proxy_get_header(resp_hdrs, "Date"); + dates = ap_table_get(resp_hdrs, "Date"); if (dates != NULL) - date = ap_parseHTTPdate(dates->value); + date = ap_parseHTTPdate(dates); else date = BAD_DATE; @@ -868,8 +860,8 @@ /* add one; N.B. use the time _now_ rather than when we were checking the cache */ date = now; - p = ap_gm_timestr_822(r->pool, now); - dates = ap_proxy_add_header(resp_hdrs, "Date", p, HDR_REP); + dates = ap_gm_timestr_822(r->pool, now); + ap_table_set(resp_hdrs, "Date", dates); Explain0("Added date header"); } @@ -878,7 +870,7 @@ /* if its in the future, then replace by date */ { lmod = date; - lmods->value = dates->value; + lmods = dates; Explain0("Last modified is in the future, replacing with now"); } /* if the response did not contain the header, then use the cached version */ @@ -889,9 +881,9 @@ /* we now need to calculate the expire data for the object. */ if (expire == NULL && c->fp != NULL) { /* no expiry data sent in response */ - expire = ap_proxy_get_header(c->hdrs, "Expires"); + expire = ap_table_get(c->hdrs, "Expires"); if (expire != NULL) - expc = ap_parseHTTPdate(expire->value); + expc = ap_parseHTTPdate(expire); } /* so we now have the expiry date */ /* if no expiry date then @@ -915,11 +907,11 @@ } /* get the content-length header */ - clen = ap_proxy_get_header(resp_hdrs, "Content-Length"); + clen = ap_table_get(resp_hdrs, "Content-Length"); if (clen == NULL) c->len = -1; else - c->len = atoi(clen->value); + c->len = atoi(clen); ap_proxy_sec2hex(date, buff); buff[8] = ' '; @@ -934,7 +926,7 @@ buff[45] = '\0'; /* if file not modified */ - if (r->status == 304) { + if (r->status == HTTP_NOT_MODIFIED) { if (c->ims != BAD_DATE && lmod != BAD_DATE && lmod <= c->ims) { /* set any changed headers somehow */ /* update dates and version, but not content-length */ @@ -967,7 +959,7 @@ ap_bsetopt(r->connection->client, BO_BYTECT, &zero); r->sent_bodyct = 1; if (!r->header_only) - ap_proxy_send_fb(c->fp, r, NULL, NULL); + ap_proxy_send_fb(c->fp, r, NULL); /* set any changed headers somehow */ /* update dates and version, but not content-length */ if (lmod != c->lmod || expc != c->expire || date != c->date) { @@ -1030,13 +1022,15 @@ return DECLINED; } -void ap_proxy_cache_tidy(struct cache_req *c) +void ap_proxy_cache_tidy(cache_req *c) { - server_rec *s = c->req->server; + server_rec *s; long int bc; - if (c->fp == NULL) + if (c == NULL || c->fp == NULL) return; + + s = c->req->server; /* don't care how much was sent, but rather how much was written to cache ap_bgetopt(c->req->connection->client, BO_BYTECT, &bc); 1.31 +1 -1 apache-1.3/src/modules/proxy/proxy_connect.c Index: proxy_connect.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/proxy/proxy_connect.c,v retrieving revision 1.30 retrieving revision 1.31 diff -u -u -r1.30 -r1.31 --- proxy_connect.c 1998/08/06 17:30:42 1.30 +++ proxy_connect.c 1998/08/16 20:21:27 1.31 @@ -97,7 +97,7 @@ * FIXME: no check for r->assbackwards, whatever that is. */ -int ap_proxy_connect_handler(request_rec *r, struct cache_req *c, char *url, +int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, const char *proxyhost, int proxyport) { struct sockaddr_in server; 1.66 +38 -75 apache-1.3/src/modules/proxy/proxy_ftp.c Index: proxy_ftp.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/proxy/proxy_ftp.c,v retrieving revision 1.65 retrieving revision 1.66 diff -u -u -r1.65 -r1.66 --- proxy_ftp.c 1998/08/06 17:30:43 1.65 +++ proxy_ftp.c 1998/08/16 20:21:28 1.66 @@ -263,7 +263,7 @@ return status; } -static long int send_dir(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c, char *url) +static long int send_dir(BUFF *f, request_rec *r, cache_req *c, char *url) { char buf[IOBUFSIZE]; char buf2[IOBUFSIZE]; @@ -278,7 +278,6 @@ int hostlen; conn_rec *con = r->connection; char *dir, *path, *reldir, *site, *psite; - const char *sig; tempurl = ap_pstrdup(r->pool, url); @@ -318,10 +317,7 @@ "<BODY><H2>Directory of " "<A HREF=\"/\">%s</A>/", tempurl, psite, path, site); - ap_bputs(buf, con->client); - if (f2 != NULL) - ap_bputs(buf, f2); - total_bytes_sent += strlen(buf); + total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); while ((dir = strchr(dir+1, '/')) != NULL) { @@ -332,17 +328,12 @@ ++reldir; /* print "path/" component */ ap_snprintf(buf, sizeof(buf), "<A HREF=\"/%s/\">%s</A>/", path+1, reldir); - ap_bputs(buf, con->client); - if (f2 != NULL) - ap_bputs(buf, f2); + total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); total_bytes_sent += strlen(buf); *dir = '/'; } ap_snprintf(buf, sizeof(buf), "</H2>\n<HR><PRE>"); - ap_bputs(buf, con->client); - if (f2 != NULL) - ap_bputs(buf, f2); - total_bytes_sent += strlen(buf); + total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); for (hostlen=0; url[hostlen]!='/'; ++hostlen) continue; @@ -355,8 +346,8 @@ while (!con->aborted) { n = ap_bgets(buf, sizeof buf, f); if (n == -1) { /* input error */ - if (f2 != NULL) - f2 = ap_proxy_cache_error(c); + if (c != NULL) + c = ap_proxy_cache_error(c); break; } if (n == 0) @@ -416,9 +407,8 @@ o = 0; total_bytes_sent += n; - if (f2 != NULL) - if (ap_bwrite(f2, buf, n) != n) - f2 = ap_proxy_cache_error(c); + if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) + c = ap_proxy_cache_error(c); while (n && !r->connection->aborted) { w = ap_bwrite(con->client, &buf[o], n); @@ -429,23 +419,11 @@ o += w; } } - site = "</PRE><HR>\n"; - ap_bputs(site, con->client); - if (f2 != NULL) - ap_bputs(site, f2); - total_bytes_sent += strlen(site); - - sig = ap_psignature("", r); - ap_bputs(sig, con->client); - if (f2 != NULL) - ap_bputs(sig, f2); - total_bytes_sent += strlen(sig); - - site = "</BODY></HTML>\n"; - ap_bputs(site, con->client); - if (f2 != NULL) - ap_bputs(site, f2); - total_bytes_sent += strlen(site); + + total_bytes_sent += ap_proxy_bputs2("</PRE><HR>\n", con->client, c); + total_bytes_sent += ap_proxy_bputs2(ap_psignature("", r), con->client, c); + total_bytes_sent += ap_proxy_bputs2("</BODY></HTML>\n", con->client, c); + ap_bflush(con->client); return total_bytes_sent; @@ -457,7 +435,7 @@ * Troy Morrison <[EMAIL PROTECTED]> * PASV added by Chuck */ -int ap_proxy_ftp_handler(request_rec *r, struct cache_req *c, char *url) +int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) { char *host, *path, *strp, *user, *password, *parms; const char *err; @@ -466,15 +444,15 @@ int csd = 0; struct sockaddr_in server; struct hostent server_hp; - struct hdr_entry *hdr; struct in_addr destaddr; - array_header *resp_hdrs; - BUFF *f, *cache; + table *resp_hdrs; + BUFF *f; BUFF *data = NULL; pool *p = r->pool; int one = 1; const long int zero = 0L; NET_SIZE_T clen; + struct tbl_do_args tdo; void *sconf = r->server->module_config; proxy_server_conf *conf = @@ -999,25 +977,24 @@ if (rc != 125 && rc != 150 && rc != 226 && rc != 250) return HTTP_BAD_GATEWAY; - r->status = 200; + r->status = HTTP_OK; r->status_line = "200 OK"; - resp_hdrs = ap_make_array(p, 2, sizeof(struct hdr_entry)); + resp_hdrs = ap_make_table(p, 2); c->hdrs = resp_hdrs; if (parms[0] == 'd') - ap_proxy_add_header(resp_hdrs, "Content-Type", "text/html", HDR_REP); + ap_table_set(resp_hdrs, "Content-Type", "text/html"); else { if (r->content_type != NULL) { - ap_proxy_add_header(resp_hdrs, "Content-Type", r->content_type, - HDR_REP); + ap_table_set(resp_hdrs, "Content-Type", r->content_type); Explain1("FTP: Content-Type set to %s", r->content_type); } else { - ap_proxy_add_header(resp_hdrs, "Content-Type", "text/plain", HDR_REP); + ap_table_set(resp_hdrs, "Content-Type", "text/plain"); } if (parms[0] != 'a' && size != NULL) { - ap_proxy_add_header(resp_hdrs, "Content-Length", size, HDR_REP); + ap_table_set(resp_hdrs, "Content-Length", size); Explain1("FTP: Content-Length set to %s", size); } } @@ -1037,10 +1014,6 @@ return i; } - cache = c->fp; - - c->hdrs = resp_hdrs; - if (!pasvmode) { /* wait for connection */ ap_hard_timeout("proxy ftp data connect", r); clen = sizeof(struct sockaddr_in); @@ -1053,7 +1026,8 @@ ap_pclosesocket(p, dsock); ap_bclose(f); ap_kill_timeout(r); - ap_proxy_cache_error(c); + if (c != NULL) + c = ap_proxy_cache_error(c); return HTTP_BAD_GATEWAY; } ap_note_cleanups_for_socket(p, csd); @@ -1075,31 +1049,19 @@ /* write status line */ if (!r->assbackwards) ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); - if (cache != NULL) - if (ap_bvputs(cache, "HTTP/1.0 ", r->status_line, CRLF, - NULL) == -1) - cache = ap_proxy_cache_error(c); + if (c != NULL && c->fp != NULL && + ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) + c = ap_proxy_cache_error(c); /* send headers */ - len = resp_hdrs->nelts; - hdr = (struct hdr_entry *) resp_hdrs->elts; - for (i = 0; i < len; i++) { - if (hdr[i].field == NULL || hdr[i].value == NULL || - hdr[i].value[0] == '\0') - continue; - if (!r->assbackwards) - ap_rvputs(r, hdr[i].field, ": ", hdr[i].value, CRLF, NULL); - if (cache != NULL) - if (ap_bvputs(cache, hdr[i].field, ": ", hdr[i].value, CRLF, - NULL) == -1) - cache = ap_proxy_cache_error(c); - } + tdo.req = r; + tdo.cache = c; + ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL); if (!r->assbackwards) ap_rputs(CRLF, r); - if (cache != NULL) - if (ap_bputs(CRLF, cache) == -1) - cache = ap_proxy_cache_error(c); + if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) + c = ap_proxy_cache_error(c); ap_bsetopt(r->connection->client, BO_BYTECT, &zero); r->sent_bodyct = 1; @@ -1107,15 +1069,16 @@ if (!r->header_only) { if (parms[0] != 'd') { /* we need to set this for ap_proxy_send_fb()... */ - c->cache_completion = 0; - ap_proxy_send_fb(data, r, cache, c); + if (c != NULL) + c->cache_completion = 0; + ap_proxy_send_fb(data, r, c); } else - send_dir(data, r, cache, c, url); + send_dir(data, r, c, url); if (rc == 125 || rc == 150) rc = ftp_getrc(f); if (rc != 226 && rc != 250) - ap_proxy_cache_error(c); + c = ap_proxy_cache_error(c); } else { /* abort the transfer */ 1.57 +32 -50 apache-1.3/src/modules/proxy/proxy_http.c Index: proxy_http.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/proxy/proxy_http.c,v retrieving revision 1.56 retrieving revision 1.57 diff -u -u -r1.56 -r1.57 --- proxy_http.c 1998/08/09 17:39:25 1.56 +++ proxy_http.c 1998/08/16 20:21:28 1.57 @@ -165,26 +165,28 @@ * we return DECLINED so that we can try another proxy. (Or the direct * route.) */ -int ap_proxy_http_handler(request_rec *r, struct cache_req *c, char *url, +int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, const char *proxyhost, int proxyport) { const char *strp; char *strp2; const char *err, *desthost; int i, j, sock, len, backasswards; - array_header *reqhdrs_arr, *resp_hdrs; + array_header *reqhdrs_arr; + table *resp_hdrs; table_entry *reqhdrs; struct sockaddr_in server; struct in_addr destaddr; struct hostent server_hp; - BUFF *f, *cache; - struct hdr_entry *hdr; + BUFF *f; char buffer[HUGE_STRING_LEN]; pool *p = r->pool; const long int zero = 0L; int destport = 0; char *destportstr = NULL; const char *urlptr = NULL; + const char *datestr; + struct tbl_do_args tdo; void *sconf = r->server->module_config; proxy_server_conf *conf = @@ -313,7 +315,7 @@ if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL /* Clear out headers not to send */ || !strcasecmp(reqhdrs[i].key, "Host") /* Already sent */ - ||!strcasecmp(reqhdrs[i].key, "Proxy-Authorization")) + || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization")) continue; ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL); } @@ -322,7 +324,7 @@ /* send the request data, if any. N.B. should we trap SIGPIPE ? */ if (ap_should_client_block(r)) { - while ((i = ap_get_client_block(r, buffer, HUGE_STRING_LEN)) > 0) + while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) ap_bwrite(f, buffer, i); } ap_bflush(f); @@ -330,7 +332,7 @@ ap_hard_timeout("proxy receive", r); - len = ap_bgets(buffer, HUGE_STRING_LEN - 1, f); + len = ap_bgets(buffer, sizeof buffer - 1, f); if (len == -1 || len == 0) { ap_bclose(f); ap_kill_timeout(r); @@ -368,7 +370,7 @@ r->status_line = "200 OK"; /* no headers */ - resp_hdrs = ap_make_array(p, 2, sizeof(struct hdr_entry)); + resp_hdrs = ap_make_table(p, 20); } c->hdrs = resp_hdrs; @@ -379,21 +381,18 @@ * HTTP/1.0 requires us to accept 3 types of dates, but only generate * one type */ + if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL) + ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr)); + if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL) + ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr)); + if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL) + ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr)); + + if ((datestr = ap_table_get(resp_hdrs, "Location")) != NULL) + ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, datestr)); + if ((datestr = ap_table_get(resp_hdrs, "URI")) != NULL) + ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, datestr)); - hdr = (struct hdr_entry *) resp_hdrs->elts; - for (i = 0; i < resp_hdrs->nelts; i++) { - if (hdr[i].value[0] == '\0') - continue; - strp = hdr[i].field; - if (strcasecmp(strp, "Date") == 0 || - strcasecmp(strp, "Last-Modified") == 0 || - strcasecmp(strp, "Expires") == 0) - hdr[i].value = ap_proxy_date_canon(p, hdr[i].value); - if (strcasecmp(strp, "Location") == 0 || - strcasecmp(strp, "URI") == 0) - hdr[i].value = proxy_location_reverse_map(r, hdr[i].value); - } - /* check if NoCache directive on this host */ for (i = 0; i < conf->nocaches->nelts; i++) { if ((ncent[i].name != NULL && strstr(desthost, ncent[i].name) != NULL) @@ -407,49 +406,32 @@ return i; } - cache = c->fp; - ap_hard_timeout("proxy receive", r); /* write status line */ if (!r->assbackwards) ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); - if (cache != NULL) - if (ap_bvputs(cache, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) - cache = ap_proxy_cache_error(c); + if (c != NULL && c->fp != NULL && + ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) + c = ap_proxy_cache_error(c); /* send headers */ - for (i = 0; i < resp_hdrs->nelts; i++) { - if (hdr[i].field == NULL || hdr[i].value == NULL || - hdr[i].value[0] == '\0') - continue; - if (!r->assbackwards) { - ap_rvputs(r, hdr[i].field, ": ", hdr[i].value, CRLF, NULL); - /* XXX: can't this be ap_table_setn? -djg */ - ap_table_set(r->headers_out, hdr[i].field, hdr[i].value); - /* XXX: regardless, there's an O(n^2) attack here, which - * could be fixed with ap_overlap_tables */ - } - if (cache != NULL) - if (ap_bvputs(cache, hdr[i].field, ": ", hdr[i].value, CRLF, - NULL) == -1) - cache = ap_proxy_cache_error(c); - } + tdo.req = r; + tdo.cache = c; + ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL); if (!r->assbackwards) ap_rputs(CRLF, r); - if (cache != NULL) - if (ap_bputs(CRLF, cache) == -1) - cache = ap_proxy_cache_error(c); + if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) + c = ap_proxy_cache_error(c); ap_bsetopt(r->connection->client, BO_BYTECT, &zero); r->sent_bodyct = 1; /* Is it an HTTP/0.9 respose? If so, send the extra data */ if (backasswards) { ap_bwrite(r->connection->client, buffer, len); - if (cache != NULL) - if (ap_bwrite(f, buffer, len) != len) - cache = ap_proxy_cache_error(c); + if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) + c = ap_proxy_cache_error(c); } ap_kill_timeout(r); @@ -467,7 +449,7 @@ if (!r->header_only) { /* we need to set this for ap_proxy_send_fb()... */ c->cache_completion = conf->cache.cache_completion; - ap_proxy_send_fb(f, r, cache, c); + ap_proxy_send_fb(f, r, c); } ap_proxy_cache_tidy(c); 1.69 +152 -150 apache-1.3/src/modules/proxy/proxy_util.c Index: proxy_util.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/proxy/proxy_util.c,v retrieving revision 1.68 retrieving revision 1.69 diff -u -u -r1.68 -r1.69 --- proxy_util.c 1998/08/09 17:39:25 1.68 +++ proxy_util.c 1998/08/16 20:21:28 1.69 @@ -359,89 +359,118 @@ return q; } + +/* NOTE: This routine is taken from http_protocol::getline() + * because the old code found in the proxy module was too + * difficult to understand and maintain. + */ +/* Get a line of protocol input, including any continuation lines + * caused by MIME folding (or broken clients) if fold != 0, and place it + * in the buffer s, of size n bytes, without the ending newline. + * + * Returns -1 on error, or the length of s. + * + * Note: Because bgets uses 1 char for newline and 1 char for NUL, + * the most we can get is (n - 2) actual characters if it + * was ended by a newline, or (n - 1) characters if the line + * length exceeded (n - 1). So, if the result == (n - 1), + * then the actual input line exceeded the buffer length, + * and it would be a good idea for the caller to puke 400 or 414. + */ +static int proxy_getline(char *s, int n, BUFF *in, int fold) +{ + char *pos, next; + int retval; + int total = 0; + + pos = s; + + do { + retval = ap_bgets(pos, n, in); /* retval == -1 if error, 0 if EOF */ + + if (retval <= 0) + return ((retval < 0) && (total == 0)) ? -1 : total; + + /* retval is the number of characters read, not including NUL */ + + n -= retval; /* Keep track of how much of s is full */ + pos += (retval - 1); /* and where s ends */ + total += retval; /* and how long s has become */ + + if (*pos == '\n') { /* Did we get a full line of input? */ + *pos = '\0'; + --total; + ++n; + } + else + return total; /* if not, input line exceeded buffer size */ + + /* Continue appending if line folding is desired and + * the last line was not empty and we have room in the buffer and + * the next line begins with a continuation character. + */ + } while (fold && (retval != 1) && (n > 1) + && (ap_blookc(&next, in) == 1) + && ((next == ' ') || (next == '\t'))); + + return total; +} + + /* * Reads headers from a buffer and returns an array of headers. * Returns NULL on file error - */ -array_header * - ap_proxy_read_headers(pool *p, char *buffer, int size, BUFF *f) -{ - int gotcr, len, i, j; - array_header *resp_hdrs; - struct hdr_entry *hdr; - char *strp; - const char *strcp; - - resp_hdrs = ap_make_array(p, 10, sizeof(struct hdr_entry)); - hdr = NULL; - - gotcr = 1; - for (;;) { - len = ap_bgets(buffer, size, f); - if (len == -1) + * This routine tries to deal with too long lines and continuation lines. + * @@@: XXX: FIXME: currently the headers are passed thru un-merged. + * Is that okay, or should they be collapsed where possible? + */ +table *ap_proxy_read_headers(pool *p, char *buffer, int size, BUFF *f) +{ + table *resp_hdrs; + int len; + char *value, *end; + char field[MAX_STRING_LEN]; + + resp_hdrs = ap_make_table(p, 20); + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), or we timeout. + */ + while ((len = proxy_getline(buffer, size, f, 1)) > 0) { + + if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ return NULL; - if (len == 0) - break; - if (buffer[len - 1] == '\n') { - buffer[--len] = '\0'; - i = 1; } - else - i = 0; - if (!gotcr || buffer[0] == ' ' || buffer[0] == '\t') { - /* a continuation header */ - if (hdr == NULL) { - /* error!! */ - if (!i) { - i = ap_bskiplf(f); - if (i == -1) - return NULL; - } - gotcr = 1; - continue; - } - hdr->value = ap_pstrcat(p, hdr->value, buffer, NULL); - } - else if (gotcr && len == 0) - break; - else { - strp = strchr(buffer, ':'); - if (strp == NULL) { - /* error!! */ - if (!gotcr) { - i = ap_bskiplf(f); - if (i == -1) - return NULL; - } - gotcr = 1; - hdr = NULL; - continue; + *value = '\0'; + ++value; + /* XXX: RFC2068 defines only SP and HT as whitespace, this test is + * wrong... and so are many others probably. + */ + while (ap_isspace(*value)) + ++value; /* Skip to start of value */ + + /* should strip trailing whitespace as well */ + for (end = &value[strlen(value)-1]; end > value && ap_isspace(*end); --end) + *end = '\0'; + + ap_table_add(resp_hdrs, buffer, value); + + /* the header was too long; at the least we should skip extra data */ + if (len >= size - 1) { + while ((len = proxy_getline(field, MAX_STRING_LEN, f, 1)) + >= MAX_STRING_LEN - 1) { + /* soak up the extra data */ } - hdr = ap_push_array(resp_hdrs); - *(strp++) = '\0'; - hdr->field = ap_pstrdup(p, buffer); - while (*strp == ' ' || *strp == '\t') - strp++; - hdr->value = ap_pstrdup(p, strp); - gotcr = i; + if (len == 0) /* time to exit the larger loop as well */ + break; } } - - hdr = (struct hdr_entry *) resp_hdrs->elts; - for (i = 0; i < resp_hdrs->nelts; i++) { - strcp = hdr[i].value; - j = strlen(strcp); - while (j > 0 && (strcp[j - 1] == ' ' || strcp[j - 1] == '\t')) - j--; - /* Note that this is OK, coz we created the header above */ - ((char *)strcp)[j] = '\0'; - } - return resp_hdrs; } -long int ap_proxy_send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c) +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c) { int ok = 1; char buf[IOBUFSIZE]; @@ -457,8 +486,8 @@ #ifdef CHARSET_EBCDIC /* The cache copy is ASCII, not EBCDIC, even for text/html) */ ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); - if (f2 != NULL) - ap_bsetflag(f2, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); + if (c != NULL && c->fp != NULL) + ap_bsetflag(c->fp, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); ap_bsetflag(con->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); #endif @@ -492,10 +521,11 @@ } #endif - while (ok && f != NULL) { + while (ok) { if (alt_to) ap_hard_timeout("proxy send body", r); + /* Read block from server */ n = ap_bread(f, buf, IOBUFSIZE); if (alt_to) @@ -504,8 +534,8 @@ ap_reset_timeout(r); if (n == -1) { /* input error */ - if (f2 != NULL) - f2 = ap_proxy_cache_error(c); + if (c != NULL) + c = ap_proxy_cache_error(c); break; } if (n == 0) @@ -513,14 +543,16 @@ o = 0; total_bytes_rcv += n; - if (f2 != NULL) { - if (ap_bwrite(f2, &buf[0], n) != n) { - f2 = ap_proxy_cache_error(c); + /* Write to cache first. */ + if (c != NULL && c->fp != NULL) { + if (ap_bwrite(c->fp, &buf[0], n) != n) { + c = ap_proxy_cache_error(c); } else { c->written += n; } } + /* Write the block to the client, detect aborted transfers */ while (n && !con->aborted) { if (alt_to) ap_soft_timeout("proxy send body", r); @@ -533,7 +565,7 @@ ap_reset_timeout(r); if (w <= 0) { - if (f2 != NULL) { + if (c != NULL) { /* when a send failure occurs, we need to decide * whether to continue loading and caching the * document, or to abort the whole thing @@ -545,8 +577,8 @@ if (! ok) { ap_pclosef(c->req->pool, c->fp->fd); c->fp = NULL; - f2 = NULL; unlink(c->tempfile); + c = NULL; } } con->aborted = 1; @@ -565,85 +597,28 @@ } /* - * Read a header from the array, returning the first entry - */ -struct hdr_entry * - ap_proxy_get_header(array_header *hdrs_arr, const char *name) -{ - struct hdr_entry *hdrs; - int i; - - hdrs = (struct hdr_entry *) hdrs_arr->elts; - for (i = 0; i < hdrs_arr->nelts; i++) - if (hdrs[i].field != NULL && strcasecmp(name, hdrs[i].field) == 0) - return &hdrs[i]; - - return NULL; -} - -/* - * Add to the header reply, either concatenating, or replacing existin - * headers. It stores the pointers provided, so make sure the data - * is not subsequently overwritten - */ -struct hdr_entry * - ap_proxy_add_header(array_header *hdrs_arr, const char *field, const char *value, - int rep) -{ - int i; - struct hdr_entry *hdrs; - - hdrs = (struct hdr_entry *) hdrs_arr->elts; - if (rep) - for (i = 0; i < hdrs_arr->nelts; i++) - if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) { - hdrs[i].value = value; - return hdrs; - } - - hdrs = ap_push_array(hdrs_arr); - hdrs->field = field; - hdrs->value = value; - - return hdrs; -} - -void ap_proxy_del_header(array_header *hdrs_arr, const char *field) -{ - int i; - struct hdr_entry *hdrs; - - hdrs = (struct hdr_entry *) hdrs_arr->elts; - - for (i = 0; i < hdrs_arr->nelts; i++) - if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) - hdrs[i].value = NULL; -} - -/* * Sends response line and headers. Uses the client fd and the * headers_out array from the passed request_rec to talk to the client * and to properly set the headers it sends for things such as logging. * * A timeout should be set before calling this routine. */ -void ap_proxy_send_headers(request_rec *r, const char *respline, array_header *hdrs_arr) +void ap_proxy_send_headers(request_rec *r, const char *respline, table *t) { - struct hdr_entry *hdrs; int i; BUFF *fp = r->connection->client; - - hdrs = (struct hdr_entry *) hdrs_arr->elts; + table_entry *elts = (table_entry *) ap_table_elts(t)->elts; ap_bputs(respline, fp); ap_bputs(CRLF, fp); - for (i = 0; i < hdrs_arr->nelts; i++) { - if (hdrs[i].field == NULL) - continue; - ap_bvputs(fp, hdrs[i].field, ": ", hdrs[i].value, CRLF, NULL); - /* XXX: can't this be ap_table_setn? -djg */ - ap_table_set(r->headers_out, hdrs[i].field, hdrs[i].value); - /* XXX: another O(n^2) attack, fixed by ap_overlap_tables */ + + for (i = 0; i < ap_table_elts(t)->nelts; ++i) { + if (elts[i].key != NULL) { + ap_bvputs(fp, elts[i].key, ": ", elts[i].val, CRLF, NULL); + /* FIXME: @@@ This used to be ap_table_set(), but I think + * ap_table_addn() is correct. MnKr */ + ap_table_addn(r->headers_out, elts[i].key, elts[i].val); + } } ap_bputs(CRLF, fp); @@ -829,8 +804,8 @@ y[8] = '\0'; } -BUFF * - ap_proxy_cache_error(struct cache_req *c) + +cache_req *ap_proxy_cache_error(cache_req *c) { ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, "proxy: error writing to cache file %s", c->tempfile); @@ -1253,3 +1228,30 @@ return i; } + +/* This function is called by ap_table_do() for all header lines */ +/* (from proxy_http.c and proxy_ftp.c) */ +/* It is passed a table_do_args struct pointer and a MIME field and value pair */ +int ap_proxy_send_hdr_line(void *p, const char *key, const char *value) +{ + struct tbl_do_args *parm = (struct tbl_do_args *)p; + + if (key == NULL || value == NULL || value[0] == '\0') + return 1; + if (!parm->req->assbackwards) + ap_rvputs(parm->req, key, ": ", value, CRLF, NULL); + if (parm->cache != NULL && parm->cache->fp != NULL && + ap_bvputs(parm->cache->fp, key, ": ", value, CRLF, NULL) == -1) + parm->cache = ap_proxy_cache_error(parm->cache); + return 1; /* tell ap_table_do() to continue calling us for more headers */ +} + +/* send a text line to one or two BUFF's; return line length */ +unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache) +{ + unsigned len = ap_bputs(data, client); + if (cache != NULL && cache->fp != NULL) + ap_bputs(data, cache->fp); + return len; +} +