AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)
{
+ core_server_config *conf;
int rv;
- apr_bucket_brigade *b;
+ apr_bucket_brigade *bb;
header_struct h;
+ apr_bucket *b;
+ int body;
+ char *bodyread, *bodyoff;
+ apr_size_t bodylen = 0;
+ apr_size_t bodybuf;
+ long res;
if (r->method_number != M_TRACE) {
return DECLINED;
while (r->prev) {
r = r->prev;
}
+ conf = (core_server_config *)ap_get_module_config(r->server->module_config,
+ &core_module);
+
+ if (conf->trace_enable == AP_TRACE_DISABLE) {
+ apr_table_setn(r->notes, "error-notes",
+ "TRACE denied by server configuration");
+ return HTTP_FORBIDDEN;
+ }
- if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) {
+ if (conf->trace_enable == AP_TRACE_EXTENDED)
+ /* XX should be = REQUEST_CHUNKED_PASS */
+ body = REQUEST_CHUNKED_DECHUNK;
+ else
+ body = REQUEST_NO_BODY;
+
+ if ((rv = ap_setup_client_block(r, body))) {
+ if (rv == HTTP_REQUEST_ENTITY_TOO_LARGE)
+ apr_table_setn(r->notes, "error-notes",
+ "TRACE with a request body is not allowed");
return rv;
}
+ if (ap_should_client_block(r)) {
+
+ if (r->remaining > 0) {
+ if (r->remaining > 65536) {
+ apr_table_setn(r->notes, "error-notes",
+ "Extended TRACE request bodies cannot exceed 64k\n");
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+ /* always 32 extra bytes to catch chunk header exceptions */
+ bodybuf = (apr_size_t)r->remaining + 32;
+ }
+ else {
+ /* Add an extra 8192 for chunk headers */
+ bodybuf = 73730;
+ }
+
+ bodyoff = bodyread = apr_palloc(r->pool, bodybuf);
+
+ /* only while we have enough for a chunked header */
+ while ((!bodylen || bodybuf >= 32) &&
+ (res = ap_get_client_block(r, bodyoff, bodybuf)) > 0) {
+ bodylen += res;
+ bodybuf -= res;
+ bodyoff += res;
+ }
+ if (res > 0 && bodybuf < 32) {
+ /* discard_rest_of_request_body into our buffer */
+ while (ap_get_client_block(r, bodyread, bodylen) > 0)
+ ;
+ apr_table_setn(r->notes, "error-notes",
+ "Extended TRACE request bodies cannot exceed 64k\n");
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ if (res < 0) {
+ return HTTP_BAD_REQUEST;
+ }
+ }
+
ap_set_content_type(r, "message/http");
/* Now we recreate the request, and echo it back */
- b = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- apr_brigade_putstrs(b, NULL, NULL, r->the_request, CRLF, NULL);
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ apr_brigade_putstrs(bb, NULL, NULL, r->the_request, CRLF, NULL);
h.pool = r->pool;
- h.bb = b;
+ h.bb = bb;
apr_table_do((int (*) (void *, const char *, const char *))
form_header_field, (void *) &h, r->headers_in, NULL);
- apr_brigade_puts(b, NULL, NULL, CRLF);
- ap_pass_brigade(r->output_filters, b);
+ apr_brigade_puts(bb, NULL, NULL, CRLF);
+
+ /* If configured to accept a body, echo the body */
+ if (bodylen) {
+ b = apr_bucket_pool_create(bodyread, bodylen,
+ r->pool, bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ }
+
+ ap_pass_brigade(r->output_filters, bb);
return DONE;
}
* REQUEST_NO_BODY Send 413 error if message has any body
* REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
* REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
+ * REQUEST_CHUNKED_PASS If chunked, pass the chunk headers with body.
*
* In order to use the last two options, the caller MUST provide a buffer
* large enough to hold a chunk-size line, including any extensions.
if (maxfwd < 1) {
switch (r->method_number) {
case M_TRACE: {
+ core_server_config *coreconf = (core_server_config *)
+ ap_get_module_config(sconf, &core_module);
int access_status;
+
+ if (coreconf->trace_enable == AP_TRACE_DISABLE)
+ return ap_proxyerror(r, HTTP_NOT_IMPLEMENTED,
+ "TRACE denied by server configuration");
+
+ /* Can't test ap_should_client_block, we aren't ready to send
+ * the client a 100 Continue response till the connection has
+ * been established
+ */
+ if (coreconf->trace_enable != AP_TRACE_EXTENDED
+ && (r->read_length || (!r->read_chunked && (r->remaining <= 0))))
+ return ap_proxyerror(r, HTTP_REQUEST_ENTITY_TOO_LARGE,
+ "TRACE with request body is not allowed");
+
r->proxyreq = PROXYREQ_NONE;
if ((access_status = ap_send_http_trace(r)))
ap_die(access_status, r);