1 /* $MirOS: src/usr.sbin/httpd/src/modules/proxy/proxy_http.c,v 1.8 2007/07/03 06:36:30 tg Exp $ */
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" must
29 * not be used to endorse or promote products derived from this
30 * software without prior written permission. For written
31 * permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * nor may "Apache" appear in their name, without prior written
35 * permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 *
56 * Portions of this software are based upon public domain software
57 * originally written at the National Center for Supercomputing Applications,
58 * University of Illinois, Urbana-Champaign.
59 */
60
61 /* HTTP routines for Apache proxy */
62
63 #include "mod_proxy.h"
64 #include "http_log.h"
65 #include "http_main.h"
66 #include "http_core.h"
67 #include "util_date.h"
68
69 __RCSID("$MirOS: src/usr.sbin/httpd/src/modules/proxy/proxy_http.c,v 1.8 2007/07/03 06:36:30 tg Exp $");
70
71 /*
72 * Canonicalise http-like URLs.
73 * scheme is the scheme for the URL
74 * url is the URL starting with the first '/'
75 * def_port is the default port for this scheme.
76 */
ap_proxy_http_canon(request_rec * r,char * url,const char * scheme,int def_port)77 int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_port)
78 {
79 char *host, *path, *search, sport[7];
80 const char *err;
81 int port;
82
83 /*
84 * do syntatic check. We break the URL into host, port, path, search
85 */
86 port = def_port;
87 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
88 if (err)
89 return HTTP_BAD_REQUEST;
90
91 /* now parse path/search args, according to rfc1738 */
92 /*
93 * N.B. if this isn't a true proxy request, then the URL _path_ has
94 * already been decoded. True proxy requests have r->uri ==
95 * r->unparsed_uri, and no others have that property.
96 */
97 if (r->uri == r->unparsed_uri) {
98 search = strchr(url, '?');
99 if (search != NULL)
100 *(search++) = '\0';
101 }
102 else
103 search = r->args;
104
105 /* process path */
106 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path,
107 r->proxyreq);
108 if (path == NULL)
109 return HTTP_BAD_REQUEST;
110
111 if (port != def_port)
112 snprintf(sport, sizeof(sport), ":%d", port);
113 else
114 sport[0] = '\0';
115
116 r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
117 path, (search) ? "?" : "", (search) ? search : "", NULL);
118 return OK;
119 }
120
121 /* handle the conversion of URLs in the ProxyPassReverse function */
proxy_location_reverse_map(request_rec * r,const char * url)122 static const char *proxy_location_reverse_map(request_rec *r, const char *url)
123 {
124 void *sconf;
125 proxy_server_conf *conf;
126 struct proxy_alias *ent;
127 int i, l1, l2;
128 char *u;
129
130 sconf = r->server->module_config;
131 conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
132 l1 = strlen(url);
133 ent = (struct proxy_alias *)conf->raliases->elts;
134 for (i = 0; i < conf->raliases->nelts; i++) {
135 l2 = strlen(ent[i].real);
136 if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) {
137 u = ap_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
138 return ap_construct_url(r->pool, u, r);
139 }
140 }
141 return url;
142 }
143
144 /*
145 * This handles http:// URLs, and other URLs using a remote proxy over http
146 * If proxyhost is NULL, then contact the server directly, otherwise
147 * go via the proxy.
148 * Note that if a proxy is used, then URLs other than http: can be accessed,
149 * also, if we have trouble which is clearly specific to the proxy, then
150 * we return DECLINED so that we can try another proxy. (Or the direct
151 * route.)
152 */
ap_proxy_http_handler(request_rec * r,cache_req * c,char * url,const char * proxyhost,int proxyport)153 int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
154 const char *proxyhost, int proxyport)
155 {
156 const char *strp;
157 char *strp2;
158 const char *err, *desthost, *hostname;
159 int i, j, sock,/* len,*/ backasswards;
160 table *req_hdrs, *resp_hdrs;
161 array_header *reqhdrs_arr;
162 table_entry *reqhdrs_elts;
163 BUFF *f;
164 char buffer[HUGE_STRING_LEN];
165 char portstr[32];
166 pool *p = r->pool;
167 int destport = 0;
168 int chunked = 0;
169 char *destportstr = NULL;
170 const char *urlptr = NULL;
171 const char *datestr, *urlstr;
172 int result, major, minor;
173 const char *content_length;
174 struct addrinfo hints, *res, *res0;
175 int error;
176 char *peer;
177
178 void *sconf = r->server->module_config;
179 proxy_server_conf *conf =
180 (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
181 struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
182 struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
183 int nocache = 0;
184
185 if (conf->cache.root == NULL)
186 nocache = 1;
187
188 /* We break the URL into host, port, path-search */
189
190 urlptr = strstr(url, "://");
191 if (urlptr == NULL)
192 return HTTP_BAD_REQUEST;
193 urlptr += 3;
194 destport = DEFAULT_HTTP_PORT;
195 snprintf(portstr, sizeof(portstr), "%d", DEFAULT_HTTP_PORT);
196 destportstr = portstr;
197 ap_hook_use("ap::mod_proxy::http::handler::set_destport",
198 AP_HOOK_SIG2(int,ptr),
199 AP_HOOK_TOPMOST,
200 &destport, r);
201 strp = strchr(urlptr, '/');
202 if (strp == NULL) {
203 desthost = ap_pstrdup(p, urlptr);
204 urlptr = "/";
205 }
206 else {
207 char *q = ap_palloc(p, strp - urlptr + 1);
208 memcpy(q, urlptr, strp - urlptr);
209 q[strp - urlptr] = '\0';
210 urlptr = strp;
211 desthost = q;
212 }
213
214 if (*desthost == '['){
215 char *u = strrchr(desthost+1, ']');
216 if (u){
217 desthost++;
218 *u = '\0';
219 if (*(u+1) == ':'){ /* [host]:xx */
220 strp2 = u+1;
221 }
222 else if (*(u+1) == '\0'){ /* [host] */
223 strp2 = NULL;
224 }
225 else
226 return HTTP_BAD_REQUEST;
227 }
228 else
229 return HTTP_BAD_REQUEST;
230 }
231 else
232 strp2 = strrchr(desthost, ':');
233 if (strp2 != NULL) {
234 *(strp2++) = '\0';
235 if (isdigit((unsigned char)*strp2)) {
236 destport = atoi(strp2);
237 destportstr = strp2;
238 }
239 }
240
241 memset(&hints, 0, sizeof(hints));
242 hints.ai_family = PF_UNSPEC;
243 hints.ai_socktype = SOCK_STREAM;
244 hints.ai_protocol = IPPROTO_TCP;
245 error = getaddrinfo(desthost, destportstr, &hints, &res0);
246 if (error && proxyhost == NULL) {
247 return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
248 gai_strerror(error)); /* give up */
249 }
250
251 /* check if ProxyBlock directive on this host */
252 for (i = 0; i < conf->noproxies->nelts; i++) {
253 int fail;
254 struct sockaddr_in *sin;
255
256 fail = 0;
257 if (npent[i].name != NULL && strstr(desthost, npent[i].name))
258 fail++;
259 if (npent[i].name != NULL && strcmp(npent[i].name, "*") == 0)
260 fail++;
261 for (res = res0; res; res = res->ai_next) {
262 switch (res->ai_family) {
263 case AF_INET:
264 sin = (struct sockaddr_in *)res->ai_addr;
265 if (sin->sin_addr.s_addr == npent[i].addr.s_addr)
266 fail++;
267 break;
268 }
269 }
270 if (fail) {
271 if (res0 != NULL)
272 freeaddrinfo(res0);
273 return ap_proxyerror(r, HTTP_FORBIDDEN,
274 "Connect to remote machine blocked");
275 }
276 }
277
278 if (proxyhost != NULL) {
279 char pbuf[10];
280
281 if (res0 != NULL)
282 freeaddrinfo(res0);
283
284 snprintf(pbuf, sizeof(pbuf), "%d", proxyport);
285 memset(&hints, 0, sizeof(hints));
286 hints.ai_family = PF_UNSPEC;
287 hints.ai_socktype = SOCK_STREAM;
288 hints.ai_protocol = IPPROTO_TCP;
289 error = getaddrinfo(proxyhost, pbuf, &hints, &res0);
290 if (error)
291 return DECLINED; /* try another */
292 peer = ap_psprintf(p, "%s:%u", proxyhost, proxyport);
293 }
294
295
296 /*
297 * we have worked out who exactly we are going to connect to, now make
298 * that connection...
299 */
300 sock = i = -1;
301 for (res = res0; res; res = res->ai_next) {
302 sock = ap_psocket_ex(p, res->ai_family, res->ai_socktype,
303 res->ai_protocol, 1);
304 if (sock < 0)
305 continue;
306
307 if (conf->recv_buffer_size) {
308 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
309 (const char *)&conf->recv_buffer_size, sizeof(int))
310 == -1) {
311 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
312 "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
313 }
314 }
315
316 i = ap_proxy_doconnect(sock, res->ai_addr, r);
317 if (i == 0)
318 break;
319 ap_pclosesocket(p, sock);
320 }
321 freeaddrinfo(res0);
322 if (i == -1) {
323 if (proxyhost != NULL)
324 return DECLINED; /* try again another way */
325 else
326 return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
327 "Could not connect to remote machine: ",
328 strerror(errno), NULL));
329 }
330
331 /* record request_time for HTTP/1.1 age calculation */
332 c->req_time = time(NULL);
333
334 /*
335 * build upstream-request headers by stripping r->headers_in from
336 * connection specific headers. We must not remove the Connection: header
337 * from r->headers_in, we still have to react to Connection: close
338 */
339 req_hdrs = ap_copy_table(r->pool, r->headers_in);
340 ap_proxy_clear_connection(r->pool, req_hdrs);
341
342 /*
343 * At this point, we start sending the HTTP/1.1 request to the remote
344 * server (proxy or otherwise).
345 */
346 f = ap_bcreate(p, B_RDWR | B_SOCKET);
347 ap_bpushfd(f, sock, sock);
348
349 {
350 char *errmsg = NULL;
351 ap_hook_use("ap::mod_proxy::http::handler::new_connection",
352 AP_HOOK_SIG4(ptr,ptr,ptr,ptr),
353 AP_HOOK_DECLINE(NULL),
354 &errmsg, r, f, peer);
355 if (errmsg != NULL)
356 return ap_proxyerror(r, HTTP_BAD_GATEWAY, errmsg);
357 }
358
359 ap_hard_timeout("proxy send", r);
360 ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.1" CRLF,
361 NULL);
362
363 if (conf->preserve_host) {
364 hostname = ap_table_get(r->headers_in, "Host");
365 if (!hostname) {
366 hostname = r->server->server_hostname;
367 ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
368 "proxy: No host line on incoming request "
369 "and preserve host set forcing hostname to "
370 "be %s for uri %s", hostname, r->uri);
371 }
372 strp2 = strchr(hostname, ':');
373 if (strp2 != NULL) {
374 *(strp2++) = '\0';
375 if (ap_isdigit(*strp2)) {
376 destport = atoi(strp2);
377 destportstr = strp2;
378 }
379 }
380 }
381 else
382 hostname = desthost;
383
384 {
385 int rc = DECLINED;
386 ap_hook_use("ap::mod_proxy::http::handler::write_host_header",
387 AP_HOOK_SIG6(int,ptr,ptr,ptr,int,ptr),
388 AP_HOOK_DECLINE(DECLINED),
389 &rc, r, f, hostname, destport, destportstr);
390 if (rc == DECLINED) {
391 if (destportstr != NULL && destport != DEFAULT_HTTP_PORT)
392 ap_bvputs(f, "Host: ", hostname, ":", destportstr, CRLF, NULL);
393 else
394 ap_bvputs(f, "Host: ", hostname, CRLF, NULL);
395 }
396 }
397
398 if (conf->viaopt == via_block) {
399 /* Block all outgoing Via: headers */
400 ap_table_unset(req_hdrs, "Via");
401 }
402 else if (conf->viaopt != via_off) {
403 /* Create a "Via:" request header entry and merge it */
404 i = ap_get_server_port(r);
405 if (ap_is_default_port(i, r)) {
406 strlcpy(portstr, "", sizeof(portstr));
407 }
408 else {
409 snprintf(portstr, sizeof portstr, ":%d", i);
410 }
411 /* Generate outgoing Via: header with/without server comment: */
412 ap_table_mergen(req_hdrs, "Via",
413 (conf->viaopt == via_full)
414 ? ap_psprintf(p, "%d.%d %s%s (%s)",
415 HTTP_VERSION_MAJOR(r->proto_num),
416 HTTP_VERSION_MINOR(r->proto_num),
417 ap_get_server_name(r), portstr,
418 SERVER_BASEVERSION)
419 : ap_psprintf(p, "%d.%d %s%s",
420 HTTP_VERSION_MAJOR(r->proto_num),
421 HTTP_VERSION_MINOR(r->proto_num),
422 ap_get_server_name(r), portstr)
423 );
424 }
425
426 /* the X-* headers are only added if we are a reverse
427 * proxy, otherwise we would be giving away private information.
428 */
429 if (r->proxyreq == PROXY_PASS) {
430 const char *buf;
431
432 /*
433 * Add X-Forwarded-For: so that the upstream has a chance to determine,
434 * where the original request came from.
435 */
436 ap_table_mergen(req_hdrs, "X-Forwarded-For", r->connection->remote_ip);
437
438 /* Add X-Forwarded-Host: so that upstream knows what the
439 * original request hostname was.
440 */
441 if ((buf = ap_table_get(r->headers_in, "Host"))) {
442 ap_table_mergen(req_hdrs, "X-Forwarded-Host", buf);
443 }
444
445 /* Add X-Forwarded-Server: so that upstream knows what the
446 * name of this proxy server is (if there are more than one)
447 * XXX: This duplicates Via: - do we strictly need it?
448 */
449 ap_table_mergen(req_hdrs, "X-Forwarded-Server", r->server->server_hostname);
450 }
451
452 /* we don't yet support keepalives - but we will soon, I promise! */
453 ap_table_set(req_hdrs, "Connection", "close");
454
455 reqhdrs_arr = ap_table_elts(req_hdrs);
456 reqhdrs_elts = (table_entry *)reqhdrs_arr->elts;
457 for (i = 0; i < reqhdrs_arr->nelts; i++) {
458 if (reqhdrs_elts[i].key == NULL || reqhdrs_elts[i].val == NULL
459
460 /*
461 * Clear out hop-by-hop request headers not to send: RFC2616 13.5.1
462 * says we should strip these headers:
463 */
464 || !strcasecmp(reqhdrs_elts[i].key, "Host") /* Already sent */
465 || !strcasecmp(reqhdrs_elts[i].key, "Keep-Alive")
466 || !strcasecmp(reqhdrs_elts[i].key, "TE")
467 || !strcasecmp(reqhdrs_elts[i].key, "Trailer")
468 || !strcasecmp(reqhdrs_elts[i].key, "Transfer-Encoding")
469 || !strcasecmp(reqhdrs_elts[i].key, "Upgrade")
470 /*
471 * XXX: @@@ FIXME: "Proxy-Authorization" should *only* be suppressed
472 * if THIS server requested the authentication, not when a frontend
473 * proxy requested it!
474 *
475 * The solution to this problem is probably to strip out the
476 * Proxy-Authorisation header in the authorisation code itself, not
477 * here. This saves us having to signal somehow whether this request
478 * was authenticated or not.
479 */
480 || !strcasecmp(reqhdrs_elts[i].key, "Proxy-Authorization"))
481 continue;
482 ap_bvputs(f, reqhdrs_elts[i].key, ": ", reqhdrs_elts[i].val, CRLF, NULL);
483 }
484
485 /* the obligatory empty line to mark the end of the headers */
486 ap_bputs(CRLF, f);
487
488 /* and flush the above away */
489 ap_bflush(f);
490
491 /* and kill the send timeout */
492 ap_kill_timeout(r);
493
494
495 /* read the request data, and pass it to the backend.
496 * we might encounter a stray 100-continue reponse from a PUT or POST,
497 * if this happens we ignore the 100 continue status line and read the
498 * response again.
499 */
500 {
501 /* send the request data, if any. */
502 ap_hard_timeout("proxy receive request data", r);
503 if (ap_should_client_block(r)) {
504 while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) {
505 ap_reset_timeout(r);
506 ap_bwrite(f, buffer, i);
507 }
508 }
509 ap_bflush(f);
510 ap_kill_timeout(r);
511
512
513 /* then, read a response line */
514 ap_hard_timeout("proxy receive response status line", r);
515 result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer)-1, &backasswards, &major, &minor);
516 ap_kill_timeout(r);
517
518 /* trap any errors */
519 if (result != OK) {
520 ap_bclose(f);
521 return result;
522 }
523
524 /* if this response was 100-continue, a stray response has been caught.
525 * read the line again for the real response
526 */
527 if (r->status == 100) {
528 ap_hard_timeout("proxy receive response status line", r);
529 result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer)-1, &backasswards, &major, &minor);
530 ap_kill_timeout(r);
531
532 /* trap any errors */
533 if (result != OK) {
534 ap_bclose(f);
535 return result;
536 }
537 }
538 }
539
540
541 /*
542 * We have our response status line from the convoluted code above,
543 * now we read the headers to continue.
544 */
545 ap_hard_timeout("proxy receive response headers", r);
546
547 /*
548 * Is it an HTTP/1 response? Do some sanity checks on the response. (This
549 * is buggy if we ever see an HTTP/1.10)
550 */
551 if (backasswards == 0) {
552
553 /* read the response headers. */
554 /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
555 /* Also, take care with headers with multiple occurences. */
556
557 resp_hdrs = ap_proxy_read_headers(r, buffer, sizeof(buffer), f);
558 if (resp_hdrs == NULL) {
559 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, r->server,
560 "proxy: Bad HTTP/%d.%d header returned by %s (%s)",
561 major, minor, r->uri, r->method);
562 resp_hdrs = ap_make_table(p, 20);
563 nocache = 1; /* do not cache this broken file */
564 }
565
566 /* handle Via header in the response */
567 if (conf->viaopt != via_off && conf->viaopt != via_block) {
568 /* Create a "Via:" response header entry and merge it */
569 i = ap_get_server_port(r);
570 if (ap_is_default_port(i, r)) {
571 strlcpy(portstr, "", sizeof(portstr));
572 }
573 else {
574 snprintf(portstr, sizeof portstr, ":%d", i);
575 }
576 ap_table_mergen((table *)resp_hdrs, "Via",
577 (conf->viaopt == via_full)
578 ? ap_psprintf(p, "%d.%d %s%s (%s)",
579 major, minor,
580 ap_get_server_name(r), portstr,
581 SERVER_BASEVERSION)
582 : ap_psprintf(p, "%d.%d %s%s",
583 major, minor,
584 ap_get_server_name(r), portstr)
585 );
586 }
587
588 /* is this content chunked? */
589 chunked = ap_find_last_token(r->pool,
590 ap_table_get(resp_hdrs, "Transfer-Encoding"),
591 "chunked");
592
593 /* strip hop-by-hop headers defined by Connection and RFC2616 */
594 ap_proxy_clear_connection(p, resp_hdrs);
595
596 content_length = ap_table_get(resp_hdrs, "Content-Length");
597 if (content_length != NULL) {
598 c->len = ap_strtol(content_length, NULL, 10);
599
600 if (c->len < 0) {
601 ap_kill_timeout(r);
602 return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
603 "Invalid Content-Length from remote server",
604 NULL));
605 }
606 }
607
608 }
609 else {
610 /* an http/0.9 response */
611
612 /* no headers */
613 resp_hdrs = ap_make_table(p, 20);
614 }
615
616 ap_kill_timeout(r);
617
618 /*
619 * HTTP/1.1 requires us to accept 3 types of dates, but only generate one
620 * type
621 */
622 /*
623 * we SET the dates here, obliterating possible multiple dates, as only
624 * one of each date makes sense in each response.
625 */
626 if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL)
627 ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr));
628 if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL)
629 ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr));
630 if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL)
631 ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr));
632
633 /* handle the ProxyPassReverse mappings */
634 if ((urlstr = ap_table_get(resp_hdrs, "Location")) != NULL)
635 ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, urlstr));
636 if ((urlstr = ap_table_get(resp_hdrs, "URI")) != NULL)
637 ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, urlstr));
638 if ((urlstr = ap_table_get(resp_hdrs, "Content-Location")) != NULL)
639 ap_table_set(resp_hdrs, "Content-Location", proxy_location_reverse_map(r, urlstr));
640
641 /* check if NoCache directive on this host */
642 {
643 struct sockaddr_in *sin;
644 #ifdef INET6
645 struct sockaddr_in6 *sin6;
646 #endif
647
648 if (nocache == 0) {
649 for (i = 0; i < conf->nocaches->nelts; i++) {
650 if (ncent[i].name != NULL &&
651 (ncent[i].name[0] == '*' ||
652 strstr(desthost, ncent[i].name) != NULL)) {
653 nocache = 1;
654 break;
655 }
656 switch (res->ai_addr->sa_family) {
657 case AF_INET:
658 sin = (struct sockaddr_in *)res->ai_addr;
659 if (sin->sin_addr.s_addr == ncent[i].addr.s_addr) {
660 nocache = 1;
661 break;
662 }
663 }
664 }
665
666 /* update the cache file, possibly even fulfilling the request if
667 * it turns out a conditional allowed us to serve the object from the
668 * cache...
669 */
670 i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache);
671 if (i != DECLINED) {
672 ap_bclose(f);
673 return i;
674 }
675
676 /* write status line and headers to the cache file */
677 ap_proxy_write_headers(c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), resp_hdrs);
678 }
679 }
680
681 /* Setup the headers for our client from upstreams response-headers */
682 ap_proxy_table_replace(r->headers_out, resp_hdrs);
683 /* Add X-Cache header - be careful not to obliterate any upstream headers */
684 ap_table_mergen(r->headers_out, "X-Cache",
685 ap_pstrcat(r->pool, "MISS from ",
686 ap_get_server_name(r), NULL));
687 /* The Content-Type of this response is the upstream one. */
688 r->content_type = ap_table_get(r->headers_out, "Content-Type");
689 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Content-Type: %s", r->content_type);
690
691 /* finally output the headers to the client */
692 ap_send_http_header(r);
693
694 /*
695 * Is it an HTTP/0.9 respose? If so, send the extra data we read from
696 * upstream as the start of the reponse to client
697 */
698 /* FIXME: This code is broken: we try and write a buffer and length that
699 * were never intelligently initialised. Rather have a bit of broken protocol
700 * handling for now than broken code.
701 */
702 /*
703 if (backasswards) {
704 ap_hard_timeout("proxy send assbackward", r);
705
706 ap_bwrite(r->connection->client, buffer, len);
707 if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) {
708 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
709 "proxy: error writing extra data to %s", c->tempfile);
710 c = ap_proxy_cache_error(c);
711 }
712 ap_kill_timeout(r);
713 }
714 */
715
716 /* send body */
717 /* if header only, then cache will be NULL */
718 /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
719 /* XXX CHANGEME: We want to eventually support keepalives, which means
720 * we must read content-length bytes... */
721 if (!r->header_only) {
722 /* we need to set this for ap_proxy_send_fb()... */
723 c->cache_completion = conf->cache.cache_completion;
724
725 /* XXX CHECKME: c->len should be the expected content length, or -1 if the
726 * content length is not known. We need to make 100% sure c->len is always
727 * set correctly before we get here to correctly do keepalive.
728 */
729 ap_proxy_send_fb(f, r, c, c->len, 0, chunked, conf->io_buffer_size);
730 }
731
732 /* ap_proxy_send_fb() closes the socket f for us */
733
734 ap_proxy_cache_tidy(c);
735
736 ap_proxy_garbage_coll(r);
737 return OK;
738 }
739