Is the "Step Three-and-a-Half" really required since
ap_proxy_connect_backend() (that does the same is_socket_connected() check)
is run almost before? May the ap_proxy_connection_create() function in
between take some time or is it a last chance to catch the race?

Regards


On Thu, Oct 3, 2013 at 12:38 AM, Yann Ylavic <ylavic....@gmail.com> wrote:

> A late (little) fix below...
>
>
> On Thu, Oct 3, 2013 at 12:14 AM, Yann Ylavic <ylavic....@gmail.com> wrote:
>
>>
>> Index: modules/proxy/proxy_util.c
>> ===================================================================
>> --- modules/proxy/proxy_util.c    (revision 1528615)
>> +++ modules/proxy/proxy_util.c    (working copy)
>> @@ -3092,7 +3092,9 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_poo
>>          buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF,
>> NULL);
>>      }
>>      if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
>> -        origin->keepalive = AP_CONN_CLOSE;
>> +        if (origin) {
>> +            origin->keepalive = AP_CONN_CLOSE;
>> +        }
>>          p_conn->close = 1;
>>      }
>>      ap_xlate_proto_to_ascii(buf, strlen(buf));
>> Index: modules/proxy/mod_proxy_http.c
>> ===================================================================
>> --- modules/proxy/mod_proxy_http.c    (revision 1528615)
>> +++ modules/proxy/mod_proxy_http.c    (working copy)
>> @@ -677,30 +677,41 @@ static apr_status_t proxy_buckets_lifetime_transfo
>>      return rv;
>>  }
>>
>> +enum rb_methods {
>> +    RB_INIT,
>> +    RB_STREAM_CL,
>> +    RB_STREAM_CHUNKED,
>> +    RB_SPOOL_CL
>> +};
>> +
>>  static
>> -int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
>> +int ap_proxy_http_prefetch(apr_pool_t *p, request_rec *r,
>>                                     proxy_conn_rec *p_conn, proxy_worker
>> *worker,
>>                                     proxy_server_conf *conf,
>>                                     apr_uri_t *uri,
>> -                                   char *url, char *server_portstr)
>> +                                   char *url, char *server_portstr,
>> +                                   apr_bucket_brigade *header_brigade,
>> +                                   apr_bucket_brigade *input_brigade,
>> +                                   char **old_cl_val, char **old_te_val,
>> +                                   enum rb_methods *rb_method, int
>> *keepalive)
>>  {
>>      conn_rec *c = r->connection;
>>      apr_bucket_alloc_t *bucket_alloc = c->bucket_alloc;
>> -    apr_bucket_brigade *header_brigade;
>> -    apr_bucket_brigade *input_brigade;
>>      apr_bucket_brigade *temp_brigade;
>>      apr_bucket *e;
>>      char *buf;
>>      apr_status_t status;
>> -    enum rb_methods {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED,
>> RB_SPOOL_CL};
>> -    enum rb_methods rb_method = RB_INIT;
>> -    char *old_cl_val = NULL;
>> -    char *old_te_val = NULL;
>>      apr_off_t bytes_read = 0;
>>
>>      apr_off_t bytes;
>>      int force10, rv;
>> -    conn_rec *origin = p_conn->connection;
>>
>> +    /* XXX: This somehow should be fixed in the API.
>> +     * Assumes p_conn->close is 0 here since ap_proxy_create_hdrbrgd and
>> code
>> +     * below will use it to disable keep-alive and not for the
>> connection to
>> +     * be closed before reuse.
>> +     */
>> +    AP_DEBUG_ASSERT(!p_conn->close);
>>
>> +
>>      if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
>>          if (r->expecting_100) {
>>               return HTTP_EXPECTATION_FAILED;
>> @@ -710,17 +721,13 @@ static
>>          force10 = 0;
>>      }
>>
>> -    header_brigade = apr_brigade_create(p, origin->bucket_alloc);
>>
>>      rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, p_conn,
>>                                   worker, conf, uri, url, server_portstr,
>> -                                 &old_cl_val, &old_te_val);
>> +                                 old_cl_val, old_te_val);
>>
>>      if (rv != OK) {
>>          return rv;
>>      }
>>
>> -    /* We have headers, let's figure out our request body... */
>> -    input_brigade = apr_brigade_create(p, bucket_alloc);
>> -
>>      /* sub-requests never use keepalives, and mustn't pass request
>> bodies.
>>       * Because the new logic looks at input_brigade, we will
>> self-terminate
>>       * input_brigade and jump past all of the request body logic...
>> @@ -733,15 +740,15 @@ static
>>      if (!r->kept_body && r->main) {
>>          /* XXX: Why DON'T sub-requests use keepalives? */
>>          p_conn->close = 1;
>> -        if (old_cl_val) {
>> -            old_cl_val = NULL;
>> +        if (*old_cl_val) {
>> +            *old_cl_val = NULL;
>>              apr_table_unset(r->headers_in, "Content-Length");
>>          }
>> -        if (old_te_val) {
>> -            old_te_val = NULL;
>> +        if (*old_te_val) {
>> +            *old_te_val = NULL;
>>              apr_table_unset(r->headers_in, "Transfer-Encoding");
>>          }
>> -        rb_method = RB_STREAM_CL;
>> +        *rb_method = RB_STREAM_CL;
>>          e = apr_bucket_eos_create(input_brigade->bucket_alloc);
>>          APR_BRIGADE_INSERT_TAIL(input_brigade, e);
>>          goto skip_body;
>> @@ -755,20 +762,19 @@ static
>>       * encoding has been done by the extensions' handler, and
>>       * do not modify add_te_chunked's logic
>>       */
>> -    if (old_te_val && strcasecmp(old_te_val, "chunked") != 0) {
>> +    if (*old_te_val && strcasecmp(*old_te_val, "chunked") != 0) {
>>          ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01093)
>>                        "%s Transfer-Encoding is not supported",
>> old_te_val);
>>          return HTTP_INTERNAL_SERVER_ERROR;
>>      }
>>
>> -    if (old_cl_val && old_te_val) {
>> +    if (*old_cl_val && *old_te_val) {
>>          ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01094)
>>                        "client %s (%s) requested Transfer-Encoding "
>>                        "chunked body with Content-Length (C-L ignored)",
>>                        c->client_ip, c->remote_host ? c->remote_host: "");
>>          apr_table_unset(r->headers_in, "Content-Length");
>> -        old_cl_val = NULL;
>> -        origin->keepalive = AP_CONN_CLOSE;
>> +        *old_cl_val = NULL;
>>          p_conn->close = 1;
>>      }
>>
>> @@ -867,42 +873,42 @@ static
>>           * If we expected no body, and read no body, do not set
>>           * the Content-Length.
>>           */
>> -        if (old_cl_val || old_te_val || bytes_read) {
>> -            old_cl_val = apr_off_t_toa(r->pool, bytes_read);
>> +        if (*old_cl_val || *old_te_val || bytes_read) {
>> +            *old_cl_val = apr_off_t_toa(r->pool, bytes_read);
>>          }
>> -        rb_method = RB_STREAM_CL;
>> +        *rb_method = RB_STREAM_CL;
>>      }
>> -    else if (old_te_val) {
>> +    else if (*old_te_val) {
>>
>>          if (force10
>>               || (apr_table_get(r->subprocess_env, "proxy-sendcl")
>>                    && !apr_table_get(r->subprocess_env,
>> "proxy-sendchunks")
>>                    && !apr_table_get(r->subprocess_env,
>> "proxy-sendchunked"))) {
>> -            rb_method = RB_SPOOL_CL;
>> +            *rb_method = RB_SPOOL_CL;
>>          }
>>          else {
>> -            rb_method = RB_STREAM_CHUNKED;
>> +            *rb_method = RB_STREAM_CHUNKED;
>>          }
>>      }
>> -    else if (old_cl_val) {
>> +    else if (*old_cl_val) {
>>          if (r->input_filters == r->proto_input_filters) {
>> -            rb_method = RB_STREAM_CL;
>> +            *rb_method = RB_STREAM_CL;
>>
>>          }
>>          else if (!force10
>>                    && (apr_table_get(r->subprocess_env,
>> "proxy-sendchunks")
>>                        || apr_table_get(r->subprocess_env,
>> "proxy-sendchunked"))
>>
>>                    && !apr_table_get(r->subprocess_env, "proxy-sendcl")) {
>>  -            rb_method = RB_STREAM_CHUNKED;
>> +            *rb_method = RB_STREAM_CHUNKED;
>>          }
>>          else {
>> -            rb_method = RB_SPOOL_CL;
>> +            *rb_method = RB_SPOOL_CL;
>>          }
>>      }
>>      else {
>>          /* This is an appropriate default; very efficient for no-body
>>           * requests, and has the behavior that it will not add any C-L
>> -         * when the old_cl_val is NULL.
>> +         * when the *old_cl_val is NULL.
>>           */
>> -        rb_method = RB_SPOOL_CL;
>> +        *rb_method = RB_SPOOL_CL;
>>      }
>>
>>  /* Yes I hate gotos.  This is the subrequest shortcut */
>> @@ -924,6 +930,28 @@ skip_body:
>>          APR_BRIGADE_INSERT_TAIL(header_brigade, e);
>>      }
>>
>> +    /* XXX: This somehow should be fixed in the API (see beginning
>> comment).
>> +     * backend->close is set to disable keepalive, no need to close the
>> socket
>> +     * until further notice, let the caller know about that though.
>> +     */
>> +    *keepalive = !p_conn->close;
>> +    p_conn->close = 0;
>> +
>> +    return OK;
>> +}
>> +
>> +static
>> +int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
>> +                                   proxy_conn_rec *p_conn,
>> +                                   apr_bucket_brigade *header_brigade,
>> +                                   apr_bucket_brigade *input_brigade,
>> +                                   char *old_cl_val, char *old_te_val,
>> +                                   enum rb_methods rb_method)
>> +{
>> +    int rv;
>> +    apr_off_t bytes_read = 0;
>> +    conn_rec *origin = p_conn->connection;
>> +
>>      /* send the request body, if any. */
>>      switch(rb_method) {
>>      case RB_STREAM_CHUNKED:
>> @@ -935,6 +963,7 @@ skip_body:
>>                                     input_brigade, old_cl_val);
>>          break;
>>      case RB_SPOOL_CL:
>> +        apr_brigade_length(input_brigade, 1, &bytes_read);
>>          rv = spool_reqbody_cl(p, r, p_conn, origin, header_brigade,
>>                                    input_brigade, (old_cl_val != NULL)
>>                                                || (old_te_val != NULL)
>> @@ -947,6 +976,7 @@ skip_body:
>>      }
>>
>>      if (rv != OK) {
>> +        conn_rec *c = r->connection;
>>          /* apr_status_t value has been logged in lower level method */
>>          ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01097)
>>                        "pass request body failed to %pI (%s) from %s
>> (%s)",
>> @@ -1854,10 +1884,16 @@ static int proxy_http_handler(request_rec *r, prox
>>      char *scheme;
>>      const char *proxy_function;
>>      const char *u;
>> +    apr_bucket_brigade *header_brigade, *header_bb;
>> +    apr_bucket_brigade *input_brigade, *input_bb;
>>      proxy_conn_rec *backend = NULL;
>>      int is_ssl = 0;
>>      conn_rec *c = r->connection;
>>      int retry = 0;
>> +    char *old_cl_val = NULL, *old_te_val = NULL;
>> +    enum rb_methods rb_method = RB_INIT;
>> +    char *locurl = url;
>> +    int keepalive = 1;
>>      /*
>>       * Use a shorter-lived pool to reduce memory usage
>>       * and avoid a memory leak
>> @@ -1924,16 +1960,47 @@ static int proxy_http_handler(request_rec *r, prox
>>          backend->close = 1;
>>      }
>>
>> +    /* Step One: Determine Who To Connect To */
>> +    if ((status = ap_proxy_determine_connection(p, r, conf, worker,
>> backend,
>> +                                            uri, &locurl, proxyname,
>> +                                            proxyport, server_portstr,
>> +                                            sizeof(server_portstr))) !=
>> OK)
>> +        goto cleanup;
>> +
>> +
>> +    /* Step Once: Prefetch (partially) the request body so to increase
>> the
>> +     * chances to get whole (or enough) body and determine
>> Content-Length vs
>> +     * chunked or spool. By doing this before connecting or reusing a
>> backend
>> +     * connection, minimize the delay between checking whether this
>> connection
>> +     * is still alive and the first packet sent, should the client be
>> slow or
>> +     * some input filter retain the data.
>> +     */
>> +    input_brigade = apr_brigade_create(p, c->bucket_alloc);
>> +    header_brigade = apr_brigade_create(p, c->bucket_alloc);
>> +    if ((status = ap_proxy_http_prefetch(p, r, backend, worker, conf,
>> uri,
>> +                    locurl, server_portstr, header_brigade,
>> input_brigade,
>> +                    &old_cl_val, &old_te_val, &rb_method, &keepalive))
>> != OK) {
>> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO()
>> +                      "HTTP: failed to prefetch the request body: %s",
>> +                      backend->hostname);
>> +        goto cleanup;
>> +    }
>> +
>>      while (retry < 2) {
>> -        char *locurl = url;
>> +        if (retry) {
>> +            char *newurl = url;
>> +            /* Step One-Retry: Redetermine Who To Connect To */
>> +            if ((status = ap_proxy_determine_connection(p, r, conf,
>> worker,
>> +                            backend, uri, &newurl, proxyname, proxyport,
>> +                            server_portstr, sizeof(server_portstr))) !=
>> OK)
>> +                break;
>> +            /* XXX: the code assumes locurl is not changed during the
>> loops,
>> +             * or ap_proxy_http_prefetch would have to be called every
>> time,
>> +             * and header_brigade changed accordingly...
>> +             */
>> +            AP_DEBUG_ASSERT(!strcmp(newurl, locurl));
>> +        }
>>
>> -        /* Step One: Determine Who To Connect To */
>> -        if ((status = ap_proxy_determine_connection(p, r, conf, worker,
>> backend,
>> -                                                uri, &locurl, proxyname,
>> -                                                proxyport,
>> server_portstr,
>> -                                                sizeof(server_portstr)))
>> != OK)
>> -            break;
>> -
>>          /* Step Two: Make the Connection */
>>          if (ap_proxy_connect_backend(proxy_function, backend, worker,
>> r->server)) {
>>              ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01114)
>> @@ -1974,6 +2041,9 @@ static int proxy_http_handler(request_rec *r, prox
>>                                ssl_hostname);
>>              }
>>          }
>>
>
> Move this block :
>
>
>> +        if (!keepalive) {
>> +            backend->connection->keepalive = AP_CONN_CLOSE;
>>
> including this addon to ensure closure when not kept alive :
>
> +            backend->close = 1;
>
>>  +        }
>>
> after
> "Step Three-and-a-Half" below.
>
>
>>
>>          /* Step Three-and-a-Half: See if the socket is still connected
>> (if desired) */
>>          if (worker->s->ping_timeout_set && worker->s->ping_timeout < 0 &&
>> @@ -1986,12 +2056,18 @@ static int proxy_http_handler(request_rec *r, prox
>>              continue;
>>          }
>>
>> +        /* Preserve the header/input brigades since they may be retried
>> */
>> +        input_bb = apr_brigade_create(p,
>> backend->connection->bucket_alloc);
>> +        header_bb = apr_brigade_create(p,
>> backend->connection->bucket_alloc);
>> +        proxy_buckets_lifetime_transform(r, input_brigade, input_bb);
>> +        proxy_buckets_lifetime_transform(r, header_brigade, header_bb);
>> +
>>          /* Step Four: Send the Request
>>           * On the off-chance that we forced a 100-Continue as a
>>           * kinda HTTP ping test, allow for retries
>>           */
>> -        if ((status = ap_proxy_http_request(p, r, backend, worker,
>> -                                        conf, uri, locurl,
>> server_portstr)) != OK) {
>> +        if ((status = ap_proxy_http_request(p, r, backend, header_bb,
>> input_bb,
>> +                        old_cl_val, old_te_val, rb_method)) != OK) {
>>              if ((status == HTTP_SERVICE_UNAVAILABLE) &&
>> worker->s->ping_timeout_set &&
>>                   worker->s->ping_timeout > 0) {
>>                  backend->close = 1;
>> [EOS]
>>
>>
>

Reply via email to