PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
[ start all new proposals below, under PATCHES PROPOSED. ]
- * mod_proxy_ajp: Do not retry request in the case that we either failed to
- sent a part of the request body or if the request is not idempotent. PR44334
- Trunk version of patch:
- http://svn.apache.org/viewvc?rev=617822&view=rev
- http://svn.apache.org/viewvc?rev=627097&view=rev
- http://svn.apache.org/viewvc?rev=660207&view=rev
- Backport version for 2.2.x of patch:
- Trunk version of patch works
- +1: rpluem, niq, jim
-
* mod_proxy: Do not try a direct connection if the connection via a
remote proxy failed before and the request has a request body.
Trunk version of patch:
return OK;
}
+#define METHOD_NON_IDEMPOTENT 0
+#define METHOD_IDEMPOTENT 1
+#define METHOD_IDEMPOTENT_WITH_ARGS 2
+
+static int is_idempotent(request_rec *r)
+{
+ /*
+ * RFC2616 (9.1.2): GET, HEAD, PUT, DELETE, OPTIONS, TRACE are considered
+ * idempotent. Hint: HEAD requests use M_GET as method number as well.
+ */
+ switch (r->method_number) {
+ case M_GET:
+ case M_DELETE:
+ case M_PUT:
+ case M_OPTIONS:
+ case M_TRACE:
+ /*
+ * If the request has arguments it might have side-effects and thus
+ * it might be undesirable to resent it to a backend again
+ * automatically.
+ */
+ if (r->args) {
+ return METHOD_IDEMPOTENT_WITH_ARGS;
+ }
+ return METHOD_IDEMPOTENT;
+ /* Everything else is not considered idempotent. */
+ default:
+ return METHOD_NON_IDEMPOTENT;
+ }
+}
+
/*
* XXX: AJP Auto Flushing
*
apr_bucket_brigade *input_brigade;
apr_bucket_brigade *output_brigade;
ajp_msg_t *msg;
- apr_size_t bufsiz;
+ apr_size_t bufsiz = 0;
char *buff;
apr_uint16_t size;
const char *tenc;
proxy_server_conf *psf =
ap_get_module_config(r->server->module_config, &proxy_module);
apr_size_t maxsize = AJP_MSG_BUFFER_SZ;
+ int send_body = 0;
if (psf->io_buffer_size_set)
maxsize = psf->io_buffer_size;
conn->worker->hostname);
if (status == AJP_EOVERFLOW)
return HTTP_BAD_REQUEST;
- else
- return HTTP_SERVICE_UNAVAILABLE;
+ else {
+ /*
+ * This is only non fatal when the method is idempotent. In this
+ * case we can dare to retry it with a different worker if we are
+ * a balancer member.
+ */
+ if (is_idempotent(r) == METHOD_IDEMPOTENT) {
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
}
/* allocate an AJP message to store the data of the buckets */
"proxy: send failed to %pI (%s)",
conn->worker->cp->addr,
conn->worker->hostname);
- return HTTP_SERVICE_UNAVAILABLE;
+ /*
+ * It is fatal when we failed to send a (part) of the request
+ * body.
+ */
+ return HTTP_INTERNAL_SERVER_ERROR;
}
conn->worker->s->transferred += bufsiz;
+ send_body = 1;
}
}
"proxy: read response failed from %pI (%s)",
conn->worker->cp->addr,
conn->worker->hostname);
- return HTTP_SERVICE_UNAVAILABLE;
+ /*
+ * This is only non fatal when we have not sent (parts) of a possible
+ * request body so far (we do not store it and thus cannot sent it
+ * again) and the method is idempotent. In this case we can dare to
+ * retry it with a different worker if we are a balancer member.
+ */
+ if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) {
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
}
/* parse the reponse */
result = ajp_parse_type(r, conn->data);