Main Page | Modules | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages | Examples

proxy_http.c

Go to the documentation of this file.
00001 /* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
00002  * applicable.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 /* HTTP routines for Apache proxy */
00018 
00019 #include "mod_proxy.h"
00020 
00021 module AP_MODULE_DECLARE_DATA proxy_http_module;
00022 
00023 int ap_proxy_http_canon(request_rec *r, char *url);
00024 int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf,
00025                           char *url, const char *proxyname, 
00026                           apr_port_t proxyport);
00027 
00028 typedef struct {
00029     const char     *name;
00030     apr_port_t      port;
00031     apr_sockaddr_t *addr;
00032     apr_socket_t   *sock;
00033     int             close;
00034 } proxy_http_conn_t;
00035 
00036 static apr_status_t ap_proxy_http_cleanup(request_rec *r,
00037                                           proxy_http_conn_t *p_conn,
00038                                           proxy_conn_rec *backend);
00039 
00040 /*
00041  * Canonicalise http-like URLs.
00042  *  scheme is the scheme for the URL
00043  *  url    is the URL starting with the first '/'
00044  *  def_port is the default port for this scheme.
00045  */
00046 int ap_proxy_http_canon(request_rec *r, char *url)
00047 {
00048     char *host, *path, *search, sport[7];
00049     const char *err;
00050     const char *scheme;
00051     apr_port_t port, def_port;
00052 
00053     /* ap_port_of_scheme() */
00054     if (strncasecmp(url, "http:", 5) == 0) {
00055         url += 5;
00056         scheme = "http";
00057     }
00058     else if (strncasecmp(url, "https:", 6) == 0) {
00059         url += 6;
00060         scheme = "https";
00061     }
00062     else {
00063         return DECLINED;
00064     }
00065     def_port = apr_uri_port_of_scheme(scheme);
00066 
00067     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00068              "proxy: HTTP: canonicalising URL %s", url);
00069 
00070     /* do syntatic check.
00071      * We break the URL into host, port, path, search
00072      */
00073     port = def_port;
00074     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
00075     if (err) {
00076         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
00077                       "error parsing URL %s: %s",
00078                       url, err);
00079         return HTTP_BAD_REQUEST;
00080     }
00081 
00082     /* now parse path/search args, according to rfc1738 */
00083     /* N.B. if this isn't a true proxy request, then the URL _path_
00084      * has already been decoded.  True proxy requests have r->uri
00085      * == r->unparsed_uri, and no others have that property.
00086      */
00087     if (r->uri == r->unparsed_uri) {
00088         search = strchr(url, '?');
00089         if (search != NULL)
00090             *(search++) = '\0';
00091     }
00092     else
00093         search = r->args;
00094 
00095     /* process path */
00096     path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
00097     if (path == NULL)
00098         return HTTP_BAD_REQUEST;
00099 
00100     if (port != def_port)
00101         apr_snprintf(sport, sizeof(sport), ":%d", port);
00102     else
00103         sport[0] = '\0';
00104 
00105     if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
00106         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
00107     }
00108     r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, 
00109             "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
00110     return OK;
00111 }
00112  
00113 static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
00114 {
00115     struct proxy_alias *ent;
00116     int i, l1, l2;
00117     char *u;
00118 
00119     /* XXX FIXME: Make sure this handled the ambiguous case of the :80
00120      * after the hostname */
00121 
00122     l1 = strlen(url);
00123     ent = (struct proxy_alias *)conf->raliases->elts;
00124     for (i = 0; i < conf->raliases->nelts; i++) {
00125         l2 = strlen(ent[i].real);
00126         if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) {
00127             u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
00128             return ap_construct_url(r->pool, u, r);
00129         }
00130     }
00131     return url;
00132 }
00133 
00134 /* Clear all connection-based headers from the incoming headers table */
00135 static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
00136 {
00137     const char *name;
00138     char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
00139 
00140     apr_table_unset(headers, "Proxy-Connection");
00141     if (!next)
00142         return;
00143 
00144     while (*next) {
00145         name = next;
00146         while (*next && !apr_isspace(*next) && (*next != ',')) {
00147             ++next;
00148         }
00149         while (*next && (apr_isspace(*next) || (*next == ','))) {
00150             *next = '\0';
00151             ++next;
00152         }
00153         apr_table_unset(headers, name);
00154     }
00155     apr_table_unset(headers, "Connection");
00156 }
00157 
00158 static
00159 apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r,
00160                                                 proxy_http_conn_t *p_conn,
00161                                                 conn_rec *c,
00162                                                 proxy_server_conf *conf,
00163                                                 apr_uri_t *uri,
00164                                                 char **url,
00165                                                 const char *proxyname,
00166                                                 apr_port_t proxyport,
00167                                                 char *server_portstr,
00168                                                 int server_portstr_size) {
00169     int server_port;
00170     apr_status_t err;
00171     apr_sockaddr_t *uri_addr;
00172     /*
00173      * Break up the URL to determine the host to connect to
00174      */
00175 
00176     /* we break the URL into host, port, uri */
00177     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
00178         return ap_proxyerror(r, HTTP_BAD_REQUEST,
00179                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
00180                                          NULL));
00181     }
00182     if (!uri->port) {
00183         uri->port = apr_uri_port_of_scheme(uri->scheme);
00184     }
00185 
00186     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00187                  "proxy: HTTP connecting %s to %s:%d", *url, uri->hostname,
00188                  uri->port);
00189 
00190     /* do a DNS lookup for the destination host */
00191     /* see memory note above */
00192     err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri->hostname),
00193                                 APR_UNSPEC, uri->port, 0, c->pool);
00194 
00195     /* allocate these out of the connection pool - the check on
00196      * r->connection->id makes sure that this string does not get accessed
00197      * past the connection lifetime */
00198     /* are we connecting directly, or via a proxy? */
00199     if (proxyname) {
00200         p_conn->name = apr_pstrdup(c->pool, proxyname);
00201         p_conn->port = proxyport;
00202         /* see memory note above */
00203         err = apr_sockaddr_info_get(&p_conn->addr, p_conn->name, APR_UNSPEC,
00204                                     p_conn->port, 0, c->pool);
00205     } else {
00206         p_conn->name = apr_pstrdup(c->pool, uri->hostname);
00207         p_conn->port = uri->port;
00208         p_conn->addr = uri_addr;
00209         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
00210                            uri->query ? uri->query : "",
00211                            uri->fragment ? "#" : "",
00212                            uri->fragment ? uri->fragment : "", NULL);
00213     }
00214 
00215     if (err != APR_SUCCESS) {
00216         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
00217                              apr_pstrcat(p, "DNS lookup failure for: ",
00218                                          p_conn->name, NULL));
00219     }
00220 
00221     /* Get the server port for the Via headers */
00222     {
00223         server_port = ap_get_server_port(r);
00224         if (ap_is_default_port(server_port, r)) {
00225             strcpy(server_portstr,"");
00226         } else {
00227             apr_snprintf(server_portstr, server_portstr_size, ":%d",
00228                          server_port);
00229         }
00230     }
00231 
00232     /* check if ProxyBlock directive on this host */
00233     if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) {
00234         return ap_proxyerror(r, HTTP_FORBIDDEN,
00235                              "Connect to remote machine blocked");
00236     }
00237     return OK;
00238 }
00239 
00240 static
00241 apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r,
00242                                              proxy_http_conn_t *p_conn,
00243                                              conn_rec *c, conn_rec **origin,
00244                                              proxy_conn_rec *backend,
00245                                              proxy_server_conf *conf,
00246                                              const char *proxyname) {
00247     int failed=0, new=0;
00248     apr_socket_t *client_socket = NULL;
00249 
00250     /* We have determined who to connect to. Now make the connection, supporting
00251      * a KeepAlive connection.
00252      */
00253 
00254     /* get all the possible IP addresses for the destname and loop through them
00255      * until we get a successful connection
00256      */
00257 
00258     /* if a keepalive socket is already open, check whether it must stay
00259      * open, or whether it should be closed and a new socket created.
00260      */
00261     /* see memory note above */
00262     if (backend->connection) {
00263         client_socket = ap_get_module_config(backend->connection->conn_config, &core_module);
00264         if ((backend->connection->id == c->id) &&
00265             (backend->port == p_conn->port) &&
00266             (backend->hostname) &&
00267             (!apr_strnatcasecmp(backend->hostname, p_conn->name))) {
00268             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00269                          "proxy: keepalive address match (keep original socket)");
00270         } else {
00271             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00272                          "proxy: keepalive address mismatch / connection has"
00273                          " changed (close old socket (%s/%s, %d/%d))", 
00274                          p_conn->name, backend->hostname, p_conn->port,
00275                          backend->port);
00276             apr_socket_close(client_socket);
00277             backend->connection = NULL;
00278         }
00279     }
00280 
00281     /* get a socket - either a keepalive one, or a new one */
00282     new = 1;
00283     if ((backend->connection) && (backend->connection->id == c->id)) {
00284         apr_size_t buffer_len = 1;
00285         char test_buffer[1]; 
00286         apr_status_t socket_status;
00287         apr_interval_time_t current_timeout;
00288 
00289         /* use previous keepalive socket */
00290         *origin = backend->connection;
00291         p_conn->sock = client_socket;
00292         new = 0;
00293 
00294         /* save timeout */
00295         apr_socket_timeout_get(p_conn->sock, &current_timeout);
00296         /* set no timeout */
00297         apr_socket_timeout_set(p_conn->sock, 0);
00298         socket_status = apr_recv(p_conn->sock, test_buffer, &buffer_len);
00299         /* put back old timeout */
00300         apr_socket_timeout_set(p_conn->sock, current_timeout);
00301         if ( APR_STATUS_IS_EOF(socket_status) ) {
00302             ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
00303                          "proxy: previous connection is closed, creating a new connection.");
00304             new = 1;
00305         }
00306     }
00307     if (new) {
00308         int rc;
00309 
00310         /* create a new socket */
00311         backend->connection = NULL;
00312 
00313         /*
00314          * At this point we have a list of one or more IP addresses of
00315          * the machine to connect to. If configured, reorder this
00316          * list so that the "best candidate" is first try. "best
00317          * candidate" could mean the least loaded server, the fastest
00318          * responding server, whatever.
00319          *
00320          * For now we do nothing, ie we get DNS round robin.
00321          * XXX FIXME
00322          */
00323         failed = ap_proxy_connect_to_backend(&p_conn->sock, "HTTP",
00324                                              p_conn->addr, p_conn->name,
00325                                              conf, r->server, c->pool);
00326 
00327         /* handle a permanent error on the connect */
00328         if (failed) {
00329             if (proxyname) {
00330                 return DECLINED;
00331             } else {
00332                 return HTTP_BAD_GATEWAY;
00333             }
00334         }
00335 
00336         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00337                      "proxy: socket is connected");
00338 
00339         /* the socket is now open, create a new backend server connection */
00340         *origin = ap_run_create_connection(c->pool, r->server, p_conn->sock,
00341                                            r->connection->id,
00342                                            r->connection->sbh, c->bucket_alloc);
00343         if (!*origin) {
00344         /* the peer reset the connection already; ap_run_create_connection() 
00345          * closed the socket
00346          */
00347             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
00348                          r->server, "proxy: an error occurred creating a "
00349                          "new connection to %pI (%s)", p_conn->addr,
00350                          p_conn->name);
00351             apr_socket_close(p_conn->sock);
00352             return HTTP_INTERNAL_SERVER_ERROR;
00353         }
00354         backend->connection = *origin;
00355         backend->hostname = apr_pstrdup(c->pool, p_conn->name);
00356         backend->port = p_conn->port;
00357 
00358         if (backend->is_ssl) {
00359             if (!ap_proxy_ssl_enable(backend->connection)) {
00360                 ap_log_error(APLOG_MARK, APLOG_ERR, 0,
00361                              r->server, "proxy: failed to enable ssl support "
00362                              "for %pI (%s)", p_conn->addr, p_conn->name);
00363                 return HTTP_INTERNAL_SERVER_ERROR;
00364             }
00365         }
00366         else {
00367             ap_proxy_ssl_disable(backend->connection);
00368         }
00369 
00370         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00371                      "proxy: connection complete to %pI (%s)",
00372                      p_conn->addr, p_conn->name);
00373 
00374         /* set up the connection filters */
00375         rc = ap_run_pre_connection(*origin, p_conn->sock);
00376         if (rc != OK && rc != DONE) {
00377             (*origin)->aborted = 1;
00378             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00379                          "proxy: HTTP: pre_connection setup failed (%d)",
00380                          rc);
00381             return rc;
00382         }
00383     }
00384     return OK;
00385 }
00386 
00387 static
00388 apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
00389                                    proxy_http_conn_t *p_conn, conn_rec *origin, 
00390                                    proxy_server_conf *conf,
00391                                    apr_uri_t *uri,
00392                                    char *url, apr_bucket_brigade *bb,
00393                                    char *server_portstr) {
00394     conn_rec *c = r->connection;
00395     char *buf;
00396     apr_bucket *e;
00397     const apr_array_header_t *headers_in_array;
00398     const apr_table_entry_t *headers_in;
00399     int counter, seen_eos;
00400     apr_status_t status;
00401 
00402     /*
00403      * Send the HTTP/1.1 request to the remote server
00404      */
00405 
00406     /* strip connection listed hop-by-hop headers from the request */
00407     /* even though in theory a connection: close coming from the client
00408      * should not affect the connection to the server, it's unlikely
00409      * that subsequent client requests will hit this thread/process, so
00410      * we cancel server keepalive if the client does.
00411      */
00412     p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_in,
00413                                                      "Connection"), "close");
00414     /* sub-requests never use keepalives */
00415     if (r->main) {
00416         p_conn->close++;
00417     }
00418 
00419     ap_proxy_clear_connection(p, r->headers_in);
00420     if (p_conn->close) {
00421         apr_table_setn(r->headers_in, "Connection", "close");
00422         origin->keepalive = AP_CONN_CLOSE;
00423     }
00424 
00425     if ( apr_table_get(r->subprocess_env,"force-proxy-request-1.0")) {
00426         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
00427     } else {
00428         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
00429     }
00430     if ( apr_table_get(r->subprocess_env,"proxy-nokeepalive")) {
00431         apr_table_unset(r->headers_in, "Connection");
00432         origin->keepalive = AP_CONN_CLOSE;
00433     }
00434     ap_xlate_proto_to_ascii(buf, strlen(buf));
00435     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
00436     APR_BRIGADE_INSERT_TAIL(bb, e);
00437     if ( conf->preserve_host == 0 ) {
00438         if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
00439             buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str, CRLF,
00440                             NULL);
00441         } else {
00442             buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
00443         }
00444     } 
00445     else {
00446         /* don't want to use r->hostname, as the incoming header might have a 
00447          * port attached 
00448          */
00449         const char* hostname = apr_table_get(r->headers_in,"Host");        
00450         if (!hostname) {
00451             hostname =  r->server->server_hostname;
00452             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
00453                           "proxy: no HTTP 0.9 request (with no host line) "
00454                           "on incoming request and preserve host set "
00455                           "forcing hostname to be %s for uri %s", 
00456                           hostname, 
00457                           r->uri );
00458         }
00459         buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
00460     }
00461     ap_xlate_proto_to_ascii(buf, strlen(buf));
00462     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);        
00463     APR_BRIGADE_INSERT_TAIL(bb, e);
00464 
00465     /* handle Via */
00466     if (conf->viaopt == via_block) {
00467         /* Block all outgoing Via: headers */
00468         apr_table_unset(r->headers_in, "Via");
00469     } else if (conf->viaopt != via_off) {
00470         /* Create a "Via:" request header entry and merge it */
00471         /* Generate outgoing Via: header with/without server comment: */
00472         apr_table_mergen(r->headers_in, "Via",
00473                          (conf->viaopt == via_full)
00474                          ? apr_psprintf(p, "%d.%d %s%s (%s)",
00475                                         HTTP_VERSION_MAJOR(r->proto_num),
00476                                         HTTP_VERSION_MINOR(r->proto_num),
00477                                         ap_get_server_name(r), server_portstr,
00478                                         AP_SERVER_BASEVERSION)
00479                          : apr_psprintf(p, "%d.%d %s%s",
00480                                         HTTP_VERSION_MAJOR(r->proto_num),
00481                                         HTTP_VERSION_MINOR(r->proto_num),
00482                                         ap_get_server_name(r), server_portstr)
00483         );
00484     }
00485 
00486     /* X-Forwarded-*: handling
00487      *
00488      * XXX Privacy Note:
00489      * -----------------
00490      *
00491      * These request headers are only really useful when the mod_proxy
00492      * is used in a reverse proxy configuration, so that useful info
00493      * about the client can be passed through the reverse proxy and on
00494      * to the backend server, which may require the information to
00495      * function properly.
00496      *
00497      * In a forward proxy situation, these options are a potential
00498      * privacy violation, as information about clients behind the proxy
00499      * are revealed to arbitrary servers out there on the internet.
00500      *
00501      * The HTTP/1.1 Via: header is designed for passing client
00502      * information through proxies to a server, and should be used in
00503      * a forward proxy configuation instead of X-Forwarded-*. See the
00504      * ProxyVia option for details.
00505      */
00506 
00507     if (PROXYREQ_REVERSE == r->proxyreq) {
00508         const char *buf;
00509 
00510         /* Add X-Forwarded-For: so that the upstream has a chance to
00511          * determine, where the original request came from.
00512          */
00513         apr_table_mergen(r->headers_in, "X-Forwarded-For",
00514                        r->connection->remote_ip);
00515 
00516         /* Add X-Forwarded-Host: so that upstream knows what the
00517          * original request hostname was.
00518          */
00519         if ((buf = apr_table_get(r->headers_in, "Host"))) {
00520             apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
00521         }
00522 
00523         /* Add X-Forwarded-Server: so that upstream knows what the
00524          * name of this proxy server is (if there are more than one)
00525          * XXX: This duplicates Via: - do we strictly need it?
00526          */
00527         apr_table_mergen(r->headers_in, "X-Forwarded-Server",
00528                        r->server->server_hostname);
00529     }
00530 
00531     /* send request headers */
00532     proxy_run_fixups(r);
00533     headers_in_array = apr_table_elts(r->headers_in);
00534     headers_in = (const apr_table_entry_t *) headers_in_array->elts;
00535     for (counter = 0; counter < headers_in_array->nelts; counter++) {
00536         if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
00537 
00538         /* Clear out hop-by-hop request headers not to send
00539          * RFC2616 13.5.1 says we should strip these headers
00540          */
00541                 /* Already sent */
00542             || !apr_strnatcasecmp(headers_in[counter].key, "Host")
00543 
00544             || !apr_strnatcasecmp(headers_in[counter].key, "Keep-Alive")
00545             || !apr_strnatcasecmp(headers_in[counter].key, "TE")
00546             || !apr_strnatcasecmp(headers_in[counter].key, "Trailer")
00547             || !apr_strnatcasecmp(headers_in[counter].key, "Transfer-Encoding")
00548             || !apr_strnatcasecmp(headers_in[counter].key, "Upgrade")
00549 
00550         /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
00551          * suppressed if THIS server requested the authentication,
00552          * not when a frontend proxy requested it!
00553          *
00554          * The solution to this problem is probably to strip out
00555          * the Proxy-Authorisation header in the authorisation
00556          * code itself, not here. This saves us having to signal
00557          * somehow whether this request was authenticated or not.
00558          */
00559             || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authorization")
00560             || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authenticate")) {
00561             continue;
00562         }
00563         /* for sub-requests, ignore freshness/expiry headers */
00564         if (r->main) {
00565                 if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
00566                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Match")
00567                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Modified-Since")
00568                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Range")
00569                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Unmodified-Since")                     
00570                      || !apr_strnatcasecmp(headers_in[counter].key, "If-None-Match")) {
00571                     continue;
00572                 }
00573 
00574                 /* If you POST to a page that gets server-side parsed
00575                  * by mod_include, and the parsing results in a reverse
00576                  * proxy call, the proxied request will be a GET, but
00577                  * its request_rec will have inherited the Content-Length
00578                  * of the original request (the POST for the enclosing
00579                  * page).  We can't send the original POST's request body
00580                  * as part of the proxied subrequest, so we need to avoid
00581                  * sending the corresponding content length.  Otherwise,
00582                  * the server to which we're proxying will sit there
00583                  * forever, waiting for a request body that will never
00584                  * arrive.
00585                  */
00586                 if ((r->method_number == M_GET) && headers_in[counter].key &&
00587                     !apr_strnatcasecmp(headers_in[counter].key,
00588                                        "Content-Length")) {
00589                     continue;
00590                 }
00591         }
00592 
00593 
00594         buf = apr_pstrcat(p, headers_in[counter].key, ": ",
00595                           headers_in[counter].val, CRLF,
00596                           NULL);
00597         ap_xlate_proto_to_ascii(buf, strlen(buf));
00598         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
00599         APR_BRIGADE_INSERT_TAIL(bb, e);
00600     }
00601 
00602     /* add empty line at the end of the headers */
00603 #if APR_CHARSET_EBCDIC
00604     e = apr_bucket_immortal_create("\015\012", 2, c->bucket_alloc);
00605 #else
00606     e = apr_bucket_immortal_create(CRLF, sizeof(CRLF)-1, c->bucket_alloc);
00607 #endif
00608     APR_BRIGADE_INSERT_TAIL(bb, e);
00609     e = apr_bucket_flush_create(c->bucket_alloc);
00610     APR_BRIGADE_INSERT_TAIL(bb, e);
00611 
00612     status = ap_pass_brigade(origin->output_filters, bb);
00613 
00614     if (status != APR_SUCCESS) {
00615         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
00616                      "proxy: request failed to %pI (%s)",
00617                      p_conn->addr, p_conn->name);
00618         return status;
00619     }
00620 
00621     /* send the request data, if any. */
00622     seen_eos = 0;
00623     do {
00624         status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
00625                                 APR_BLOCK_READ, HUGE_STRING_LEN);
00626 
00627         if (status != APR_SUCCESS) {
00628             return status;
00629         }
00630 
00631         /* If this brigade contain EOS, either stop or remove it. */
00632         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
00633             /* As a shortcut, if this brigade is simply an EOS bucket,
00634              * don't send anything down the filter chain.
00635              */
00636             if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
00637                 break;
00638             }
00639 
00640             /* We can't pass this EOS to the output_filters. */
00641             e = APR_BRIGADE_LAST(bb);
00642             apr_bucket_delete(e);
00643             seen_eos = 1;
00644         }
00645 
00646         e = apr_bucket_flush_create(c->bucket_alloc);
00647         APR_BRIGADE_INSERT_TAIL(bb, e);
00648 
00649         status = ap_pass_brigade(origin->output_filters, bb);
00650         if (status != APR_SUCCESS) {
00651             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
00652                          "proxy: pass request data failed to %pI (%s)",
00653                          p_conn->addr, p_conn->name);
00654             return status;
00655         }
00656         apr_brigade_cleanup(bb);
00657     } while (!seen_eos);
00658 
00659     return APR_SUCCESS;
00660 }
00661 
00662 static
00663 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
00664                                             proxy_http_conn_t *p_conn,
00665                                             conn_rec *origin,
00666                                             proxy_conn_rec *backend,
00667                                             proxy_server_conf *conf,
00668                                             apr_bucket_brigade *bb,
00669                                             char *server_portstr) {
00670     conn_rec *c = r->connection;
00671     char buffer[HUGE_STRING_LEN];
00672     char keepchar;
00673     request_rec *rp;
00674     apr_bucket *e;
00675     int len, backasswards;
00676     int received_continue = 1; /* flag to indicate if we should
00677                                 * loop over response parsing logic
00678                                 * in the case that the origin told us
00679                                 * to HTTP_CONTINUE
00680                                 */
00681 
00682     /* Get response from the remote server, and pass it up the
00683      * filter chain
00684      */
00685 
00686     rp = ap_proxy_make_fake_req(origin, r);
00687     /* In case anyone needs to know, this is a fake request that is really a
00688      * response.
00689      */
00690     rp->proxyreq = PROXYREQ_RESPONSE;
00691 
00692     while (received_continue) {
00693         apr_brigade_cleanup(bb);
00694 
00695         len = ap_getline(buffer, sizeof(buffer), rp, 0);
00696         if (len == 0) {
00697             /* handle one potential stray CRLF */
00698             len = ap_getline(buffer, sizeof(buffer), rp, 0);
00699         }
00700         if (len <= 0) {
00701             apr_socket_close(p_conn->sock);
00702             backend->connection = NULL;
00703             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
00704                           "proxy: error reading status line from remote "
00705                           "server %s", p_conn->name);
00706             return ap_proxyerror(r, HTTP_BAD_GATEWAY,
00707                                  "Error reading from remote server");
00708         }
00709 
00710        /* Is it an HTTP/1 response?
00711         * This is buggy if we ever see an HTTP/1.10
00712         */
00713         if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
00714             int major, minor;
00715 
00716             if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
00717                 major = 1;
00718                 minor = 1;
00719             }
00720             /* If not an HTTP/1 message or
00721              * if the status line was > 8192 bytes
00722              */
00723             else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) {
00724                 apr_socket_close(p_conn->sock);
00725                 backend->connection = NULL;
00726                 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
00727                 apr_pstrcat(p, "Corrupt status line returned by remote "
00728                             "server: ", buffer, NULL));
00729             }
00730             backasswards = 0;
00731 
00732             keepchar = buffer[12];
00733             buffer[12] = '\0';
00734             r->status = atoi(&buffer[9]);
00735 
00736             if (keepchar != '\0') {
00737                 buffer[12] = keepchar;
00738             } else {
00739                 /* 2616 requires the space in Status-Line; the origin
00740                  * server may have sent one but ap_rgetline_core will
00741                  * have stripped it. */
00742                 buffer[12] = ' ';
00743                 buffer[13] = '\0';
00744             }
00745             r->status_line = apr_pstrdup(p, &buffer[9]);
00746             
00747 
00748             /* read the headers. */
00749             /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
00750             /* Also, take care with headers with multiple occurences. */
00751 
00752             r->headers_out = ap_proxy_read_headers(r, rp, buffer,
00753                                                    sizeof(buffer), origin);
00754             if (r->headers_out == NULL) {
00755                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
00756                              r->server, "proxy: bad HTTP/%d.%d header "
00757                              "returned by %s (%s)", major, minor, r->uri,
00758                              r->method);
00759                 p_conn->close += 1;
00760                 /*
00761                  * ap_send_error relies on a headers_out to be present. we
00762                  * are in a bad position here.. so force everything we send out
00763                  * to have nothing to do with the incoming packet
00764                  */
00765                 r->headers_out = apr_table_make(r->pool,1);
00766                 r->status = HTTP_BAD_GATEWAY;
00767                 r->status_line = "bad gateway";
00768                 return r->status;
00769 
00770             } else {
00771                 /* strip connection listed hop-by-hop headers from response */
00772                 const char *buf;
00773                 p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_out,
00774                                                                 "Connection"),
00775                                                   "close");
00776                 ap_proxy_clear_connection(p, r->headers_out);
00777                 if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
00778                     ap_set_content_type(r, apr_pstrdup(p, buf));
00779                 }            
00780                 ap_proxy_pre_http_request(origin,rp);
00781             }
00782 
00783             /* handle Via header in response */
00784             if (conf->viaopt != via_off && conf->viaopt != via_block) {
00785                 /* create a "Via:" response header entry and merge it */
00786                 apr_table_mergen(r->headers_out, "Via",
00787                                  (conf->viaopt == via_full)
00788                                      ? apr_psprintf(p, "%d.%d %s%s (%s)",
00789                                            HTTP_VERSION_MAJOR(r->proto_num),
00790                                            HTTP_VERSION_MINOR(r->proto_num),
00791                                            ap_get_server_name(r),
00792                                            server_portstr,
00793                                            AP_SERVER_BASEVERSION)
00794                                      : apr_psprintf(p, "%d.%d %s%s",
00795                                            HTTP_VERSION_MAJOR(r->proto_num),
00796                                            HTTP_VERSION_MINOR(r->proto_num),
00797                                            ap_get_server_name(r),
00798                                            server_portstr)
00799                 );
00800             }
00801 
00802             /* cancel keepalive if HTTP/1.0 or less */
00803             if ((major < 1) || (minor < 1)) {
00804                 p_conn->close += 1;
00805                 origin->keepalive = AP_CONN_CLOSE;
00806             }
00807         } else {
00808             /* an http/0.9 response */
00809             backasswards = 1;
00810             r->status = 200;
00811             r->status_line = "200 OK";
00812             p_conn->close += 1;
00813         }
00814 
00815         if ( r->status != HTTP_CONTINUE ) {
00816             received_continue = 0;
00817         } else {
00818             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
00819                          "proxy: HTTP: received 100 CONTINUE");
00820         }
00821 
00822         /* we must accept 3 kinds of date, but generate only 1 kind of date */
00823         {
00824             const char *buf;
00825             if ((buf = apr_table_get(r->headers_out, "Date")) != NULL) {
00826                 apr_table_set(r->headers_out, "Date",
00827                               ap_proxy_date_canon(p, buf));
00828             }
00829             if ((buf = apr_table_get(r->headers_out, "Expires")) != NULL) {
00830                 apr_table_set(r->headers_out, "Expires",
00831                               ap_proxy_date_canon(p, buf));
00832             }
00833             if ((buf = apr_table_get(r->headers_out, "Last-Modified")) != NULL) {
00834                 apr_table_set(r->headers_out, "Last-Modified",
00835                               ap_proxy_date_canon(p, buf));
00836             }
00837         }
00838 
00839         /* munge the Location and URI response headers according to
00840          * ProxyPassReverse
00841          */
00842         {
00843             const char *buf;
00844             if ((buf = apr_table_get(r->headers_out, "Location")) != NULL) {
00845                 apr_table_set(r->headers_out, "Location",
00846                               ap_proxy_location_reverse_map(r, conf, buf));
00847             }
00848             if ((buf = apr_table_get(r->headers_out, "Content-Location")) != NULL) {
00849                 apr_table_set(r->headers_out, "Content-Location",
00850                               ap_proxy_location_reverse_map(r, conf, buf));
00851             }
00852             if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) {
00853                 apr_table_set(r->headers_out, "URI",
00854                               ap_proxy_location_reverse_map(r, conf, buf));
00855             }
00856         }
00857 
00858         if ((r->status == 401) && (conf->error_override != 0)) {
00859             const char *buf;
00860             const char *wa = "WWW-Authenticate";
00861             if ((buf = apr_table_get(r->headers_out, wa))) {
00862                 apr_table_set(r->err_headers_out, wa, buf);
00863             } else {
00864                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00865                              "proxy: origin server sent 401 without WWW-Authenticate header");
00866             }
00867         }
00868 
00869         r->sent_bodyct = 1;
00870         /* Is it an HTTP/0.9 response? If so, send the extra data */
00871         if (backasswards) {
00872             apr_ssize_t cntr = len;
00873             e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
00874             APR_BRIGADE_INSERT_TAIL(bb, e);
00875         }
00876 
00877         /* send body - but only if a body is expected */
00878         if ((!r->header_only) &&                   /* not HEAD request */
00879             (r->status > 199) &&                   /* not any 1xx response */
00880             (r->status != HTTP_NO_CONTENT) &&      /* not 204 */
00881             (r->status != HTTP_RESET_CONTENT) &&   /* not 205 */
00882             (r->status != HTTP_NOT_MODIFIED)) {    /* not 304 */
00883 
00884             /* We need to copy the output headers and treat them as input
00885              * headers as well.  BUT, we need to do this before we remove
00886              * TE, so that they are preserved accordingly for
00887              * ap_http_filter to know where to end.
00888              */
00889             rp->headers_in = apr_table_copy(r->pool, r->headers_out);
00890 
00891             apr_table_unset(r->headers_out,"Transfer-Encoding");
00892 
00893             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00894                          "proxy: start body send");
00895              
00896             /*
00897              * if we are overriding the errors, we can't put the content
00898              * of the page into the brigade
00899              */
00900             if ( (conf->error_override ==0) || r->status < 400 ) {
00901 
00902                 /* read the body, pass it to the output filters */
00903                 int finish = FALSE;
00904                 while (ap_get_brigade(rp->input_filters, 
00905                                       bb, 
00906                                       AP_MODE_READBYTES, 
00907                                       APR_BLOCK_READ, 
00908                                       conf->io_buffer_size) == APR_SUCCESS) {
00909 #if DEBUGGING
00910                     {
00911                     apr_off_t readbytes;
00912                     apr_brigade_length(bb, 0, &readbytes);
00913                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
00914                                  r->server, "proxy (PID %d): readbytes: %#x",
00915                                  getpid(), readbytes);
00916                     }
00917 #endif
00918                     /* sanity check */
00919                     if (APR_BRIGADE_EMPTY(bb)) {
00920                         apr_brigade_cleanup(bb);
00921                         break;
00922                     }
00923 
00924                     /* found the last brigade? */
00925                     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
00926                         /* if this is the last brigade, cleanup the
00927                          * backend connection first to prevent the
00928                          * backend server from hanging around waiting
00929                          * for a slow client to eat these bytes
00930                          */
00931                         ap_proxy_http_cleanup(r, p_conn, backend);
00932                         /* signal that we must leave */
00933                         finish = TRUE;
00934                     }
00935 
00936                     /* try send what we read */
00937                     if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS
00938                         || c->aborted) {
00939                         /* Ack! Phbtt! Die! User aborted! */
00940                         p_conn->close = 1;  /* this causes socket close below */
00941                         finish = TRUE;
00942                     }
00943 
00944                     /* make sure we always clean up after ourselves */
00945                     apr_brigade_cleanup(bb);
00946 
00947                     /* if we are done, leave */
00948                     if (TRUE == finish) {
00949                         break;
00950                     }
00951                 }
00952             }
00953             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00954                          "proxy: end body send");
00955         } else {
00956             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
00957                          "proxy: header only");
00958         }
00959     }
00960 
00961     if ( conf->error_override ) {
00962         /* the code above this checks for 'OK' which is what the hook expects */
00963         if ( r->status == HTTP_OK )
00964             return OK;
00965         else  {
00966             /* clear r->status for override error, otherwise ErrorDocument
00967              * thinks that this is a recursive error, and doesn't find the
00968              * custom error page
00969              */
00970             int status = r->status;
00971             r->status = HTTP_OK;
00972             /* Discard body, if one is expected */
00973             if ((status > 199) && /* not any 1xx response */
00974                 (status != HTTP_NO_CONTENT) && /* not 204 */
00975                 (status != HTTP_RESET_CONTENT) && /* not 205 */
00976                 (status != HTTP_NOT_MODIFIED)) { /* not 304 */
00977                ap_discard_request_body(rp);
00978            }
00979             return status;
00980         }
00981     } else 
00982         return OK;
00983 }
00984 
00985 static
00986 apr_status_t ap_proxy_http_cleanup(request_rec *r, proxy_http_conn_t *p_conn,
00987                                    proxy_conn_rec *backend) {
00988     /* If there are no KeepAlives, or if the connection has been signalled
00989      * to close, close the socket and clean up
00990      */
00991 
00992     /* if the connection is < HTTP/1.1, or Connection: close,
00993      * we close the socket, otherwise we leave it open for KeepAlive support
00994      */
00995     if (p_conn->close || (r->proto_num < HTTP_VERSION(1,1))) {
00996         if (p_conn->sock) {
00997             apr_socket_close(p_conn->sock);
00998             p_conn->sock = NULL;
00999             backend->connection = NULL;
01000         }
01001     }
01002     return OK;
01003 }
01004 
01005 /*
01006  * This handles http:// URLs, and other URLs using a remote proxy over http
01007  * If proxyhost is NULL, then contact the server directly, otherwise
01008  * go via the proxy.
01009  * Note that if a proxy is used, then URLs other than http: can be accessed,
01010  * also, if we have trouble which is clearly specific to the proxy, then
01011  * we return DECLINED so that we can try another proxy. (Or the direct
01012  * route.)
01013  */
01014 int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf,
01015                           char *url, const char *proxyname, 
01016                           apr_port_t proxyport)
01017 {
01018     int status;
01019     char server_portstr[32];
01020     conn_rec *origin = NULL;
01021     proxy_conn_rec *backend = NULL;
01022     int is_ssl = 0;
01023 
01024     /* Note: Memory pool allocation.
01025      * A downstream keepalive connection is always connected to the existence
01026      * (or not) of an upstream keepalive connection. If this is not done then
01027      * load balancing against multiple backend servers breaks (one backend
01028      * server ends up taking 100% of the load), and the risk is run of
01029      * downstream keepalive connections being kept open unnecessarily. This
01030      * keeps webservers busy and ties up resources.
01031      *
01032      * As a result, we allocate all sockets out of the upstream connection
01033      * pool, and when we want to reuse a socket, we check first whether the
01034      * connection ID of the current upstream connection is the same as that
01035      * of the connection when the socket was opened.
01036      */
01037     apr_pool_t *p = r->connection->pool;
01038     conn_rec *c = r->connection;
01039     apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
01040     apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
01041     proxy_http_conn_t *p_conn = apr_pcalloc(r->connection->pool,
01042                                            sizeof(*p_conn));
01043 
01044     /* is it for us? */
01045     if (strncasecmp(url, "https:", 6) == 0) {
01046         if (!ap_proxy_ssl_enable(NULL)) {
01047             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
01048                          "proxy: HTTPS: declining URL %s"
01049                          " (mod_ssl not configured?)", url);
01050             return DECLINED;
01051         }
01052         is_ssl = 1;
01053     }
01054     else if (!(strncasecmp(url, "http:", 5)==0 || (strncasecmp(url, "ftp:", 4)==0 && proxyname))) {
01055         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
01056                      "proxy: HTTP: declining URL %s", url);
01057         return DECLINED; /* only interested in HTTP, or FTP via proxy */
01058     }
01059     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
01060              "proxy: HTTP: serving URL %s", url);
01061     
01062     
01063     /* only use stored info for top-level pages. Sub requests don't share 
01064      * in keepalives
01065      */
01066     if (!r->main) {
01067         backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
01068                                                       &proxy_http_module);
01069     }
01070     /* create space for state information */
01071     if (!backend) {
01072         backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
01073         backend->connection = NULL;
01074         backend->hostname = NULL;
01075         backend->port = 0;
01076         if (!r->main) {
01077             ap_set_module_config(c->conn_config, &proxy_http_module, backend);
01078         }
01079     }
01080 
01081     backend->is_ssl = is_ssl;
01082 
01083     /* Step One: Determine Who To Connect To */
01084     status = ap_proxy_http_determine_connection(p, r, p_conn, c, conf, uri,
01085                                                 &url, proxyname, proxyport,
01086                                                 server_portstr,
01087                                                 sizeof(server_portstr));
01088     if ( status != OK ) {
01089         return status;
01090     }
01091 
01092     /* Step Two: Make the Connection */
01093     status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend,
01094                                              conf, proxyname);
01095     if ( status != OK ) {
01096         return status;
01097     }
01098    
01099     /* Step Three: Send the Request */
01100     status = ap_proxy_http_request(p, r, p_conn, origin, conf, uri, url, bb,
01101                                    server_portstr);
01102     if ( status != OK ) {
01103         return status;
01104     }
01105 
01106     /* Step Four: Receive the Response */
01107     status = ap_proxy_http_process_response(p, r, p_conn, origin, backend, conf,
01108                                             bb, server_portstr);
01109     if ( status != OK ) {
01110         /* clean up even if there is an error */
01111         ap_proxy_http_cleanup(r, p_conn, backend);
01112         return status;
01113     }
01114 
01115     /* Step Five: Clean Up */
01116     status = ap_proxy_http_cleanup(r, p_conn, backend);
01117     if ( status != OK ) {
01118         return status;
01119     }
01120 
01121     return OK;
01122 }
01123 
01124 static void ap_proxy_http_register_hook(apr_pool_t *p)
01125 {
01126     proxy_hook_scheme_handler(ap_proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
01127     proxy_hook_canon_handler(ap_proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
01128 }
01129 
01130 module AP_MODULE_DECLARE_DATA proxy_http_module = {
01131     STANDARD20_MODULE_STUFF,
01132     NULL,              /* create per-directory config structure */
01133     NULL,              /* merge per-directory config structures */
01134     NULL,              /* create per-server config structure */
01135     NULL,              /* merge per-server config structures */
01136     NULL,              /* command apr_table_t */
01137     ap_proxy_http_register_hook/* register hooks */
01138 };
01139