General comments - ---------------- This bug is reproducible. I have marked it a regression because to works fine in Apache 1.3 but not in Apache 2. Here is the link to the email exchange on users@httpd.apache.org on this issue. http://www.gossamer-threads.com/lists/apache/users/340314#340314. Overview Description: --------------------- For PUT requests with a Expect: 100-continue header, Apache 2.2.6 server sends a HTTP/1.1 100 Continue response before checking to see if a 401 or 405 response might need to be sent for the request. When the resource requires authentication, Apache 2.6 sends a 100 Continue instead of a 401. After the client sends the request body, Apache 2.2.6 returns a 401 and now the client ends up having to send the request body again. For large request body sending the entire body multiple times defeats the purpose of the continue response stated in the HTTP 1.1 RFC - http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3 When a resource does *not* require authentication Apache 2.2.6 and Apache 1.3 both send a 100 Continue as expected and there are no issues there. Steps to Reproduce: --------------------- 1-5 are to setup Apache for this test. 1. Create a directory named "secret" under htdocs. 2. Create a passwords file with user "test" and a passwd. 3. Create a .htaccess file in the secret directory My .htaccess had - AuthType Basic AuthName "secret_access" AuthBasicProvider file AuthUserFile /opt/apache226/htdocs/.passwd Require user test 4. Enable PUT method support by adding "Script PUT /cgi-bin/put.cgi" to the httpd.conf. 5. I used the put script available at - http://www.apacheweek.com/issues/put1 for this test and copied it to the cgi-bin dir. 6. Send the following PUT request to the Apache 2 server. PUT /secret/test.html HTTP/1.1 Host: 10.10.10.1:8080 Expect: 100-continue Date: Mon, 15 Oct 2007 20:05:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream Actual Results : ----------------- HTTP/1.1 100 Continue Expected Results: ------------------ HTTP/1.1 401 Authorization Required Date: Mon, 15 Oct 2007 20:05:24 GMT Server: Apache/2.2.6 (Unix) WWW-Authenticate: Basic realm="secret_access" Content-Length: 401 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive With Apache 1.3 the complete exchange was as follows - PUT /secret/test.html HTTP/1.1 Host: 10.10.10.1:8888 Expect: 100-continue Date: Mon, 15 Oct 2007 22:22:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream HTTP/1.1 401 Authorization Required Date: Mon, 15 Oct 2007 22:22:24 GMT Server: Apache/1.3.33 (Unix) WWW-Authenticate: Basic realm="secret_access" Content-Length: 401 Connection: close etc... PUT /secret/test.html HTTP/1.1 Host: 10.10.10.1:8080 Authorization: Basic dGVzdDp0ZXN0 Date: Mon, 15 Oct 2007 22:22:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream <html><body><h1>Secret works!</h1></body></html> HTTP/1.1 204
Thanks for entering this as a bug. The basic problem is that the HTTP protocol input filter (ap_http_filter) actually sends the 100-continue. That's way too early. I think the fix should be to remove that code from the protocol filter, and send the 100 response from a fixups hook. Bug me if I don't get around to doing anything about it.
Created attachment 21053 [details] Patch against trunk
Can you please check if the attached patch solves your problem?
Thanks for the patch. I patched in the change to the Apache 2.2.6 source code version. And it did not work as I expected it to. The server sent both a 401 AND a 100 response for the PUT request! Next I will pull the trunk version of the source code to test this patch and let you know the results.
For 2.2.6 you also need to apply the patch for PR38014 (http://svn.apache.org/viewvc?view=rev&revision=574950) which has been already backported to 2.2.x. Thus it worked for me.
Results of testing this using the trunk version of the source code + the patch. A 401 was returned instead of the 100 continue, so that is good. But when the PUT request with the auth header was sent by the client on that connection, the server's state seemed to be all wrong. Looking at the hex dump of the 401 response returned by the server, it looks like the last chunk of the response (the 401 response has Transfer-encoding = chunked) with 0 length and the CRLF was not sent by the server so the 401 response sent by the server is actually incomplete. Test 1 - PUT request for resource that requires authentication - Result is NOT OK ===> sending the request with out the Auth header PUT /secret/test.html HTTP/1.1 Host: 10.10.10.1:8080 Expect: 100-continue Date: Mon, 15 Oct 2007 20:05:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream HTTP/1.1 401 Authorization Required Date: Tue, 30 Oct 2007 00:04:18 GMT Server: Apache/2.3.0-dev (Unix) WWW-Authenticate: Basic realm="secret_access" Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/html; charset=iso-8859-1 192 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>401 Authorization Required</title> </head><body> <h1>Authorization Required</h1> <p>This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.</p> </body></html> ===> NOTE - the 0 length chunk was not sent. ===> sending the request WITH the auth header PUT /secret/test.html HTTP/1.1 Host: 10.10.10.1:8080 Authorization: Basic dGVzdDp0ZXN0 Date: Mon, 15 Oct 2007 22:22:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream0 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>501 Method Not Implemented</title> </head><body> <h1>Method Not Implemented</h1> <p>8080 to /index.html not supported.<br /> ===> the server thinks the new request method is 8080 - the characters halfway through the Host header in the request. </p> </body></html> Connection closed by foreign host. Looks like even though a 401 was sent out, the server is in some weird state. If the client sends two CRs at this point this results in the server sending back a 0 length chunk. Trying a GET request on this server confirmed that this server does send a correct and complete 401 in the case of GET as shown below. GET /secret/test.html HTTP/1.1 Host: 1.1.1.1 HTTP/1.1 401 Authorization Required Date: Tue, 30 Oct 2007 00:44:15 GMT Server: Apache/2.3.0-dev (Unix) WWW-Authenticate: Basic realm="secret_access" Transfer-Encoding: chunked Content-Type: text/html; charset=iso-8859-1 192 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>401 Authorization Required</title> </head><body> <h1>Authorization Required</h1> <p>This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.</p> </body></html> 0 Connection closed by foreign host. Test 2 - PUT request for resource that does not required authentication - OK. Same as before patch. PUT /test.html HTTP/1.1 Host: 10.10.10.1:8888 Expect: 100-continue Date: Mon, 15 Oct 2007 22:22:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream HTTP/1.1 100 Continue <html><body><h1>Secret works!</h1></body></html> HTTP/1.1 204 No Content Date: Mon, 29 Oct 2007 21:31:13 GMT Server: Apache/2.3.0-dev (Unix) Content-Length: 0 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html
Test results for Apache 2.2.6 + PR38014 patch + patch for this bug are the same as those reported in Comment #6. The hexdump for the 401 response is as follows - 0 : 48 54 54 50 2F 31 2E 31 20 34 30 31 20 41 75 74 *HTTP/1.1 401 Aut* 16 : 68 6F 72 69 7A 61 74 69 6F 6E 20 52 65 71 75 69 *horization Requi* 32 : 72 65 64 0D 0A 44 61 74 65 3A 20 54 75 65 2C 20 *red..Date: Tue, * 48 : 33 30 20 4F 63 74 20 32 30 30 37 20 30 31 3A 32 *30 Oct 2007 01:2* 64 : 37 3A 35 39 20 47 4D 54 0D 0A 53 65 72 76 65 72 *7:59 GMT..Server* 80 : 3A 20 41 70 61 63 68 65 2F 32 2E 32 2E 36 20 28 *: Apache/2.2.6 (* 96 : 55 6E 69 78 29 0D 0A 57 57 57 2D 41 75 74 68 65 *Unix)..WWW-Authe* 112 : 6E 74 69 63 61 74 65 3A 20 42 61 73 69 63 20 72 *nticate: Basic r* 128 : 65 61 6C 6D 3D 22 6C 65 76 65 6C 5F 31 35 5F 61 *ealm="level_15_a* 144 : 63 63 65 73 73 22 0D 0A 4B 65 65 70 2D 41 6C 69 *ccess"..Keep-Ali* 160 : 76 65 3A 20 74 69 6D 65 6F 75 74 3D 35 2C 20 6D *ve: timeout=5, m* 176 : 61 78 3D 31 30 30 0D 0A 43 6F 6E 6E 65 63 74 69 *ax=100..Connecti* 192 : 6F 6E 3A 20 4B 65 65 70 2D 41 6C 69 76 65 0D 0A *on: Keep-Alive..* 208 : 54 72 61 6E 73 66 65 72 2D 45 6E 63 6F 64 69 6E *Transfer-Encodin* 224 : 67 3A 20 63 68 75 6E 6B 65 64 0D 0A 43 6F 6E 74 *g: chunked..Cont* 240 : 65 6E 74 2D 54 79 70 65 3A 20 74 65 78 74 2F 68 *ent-Type: text/h* 256 : 74 6D 6C 3B 20 63 68 61 72 73 65 74 3D 69 73 6F *tml; charset=iso* 272 : 2D 38 38 35 39 2D 31 0D 0A 0D 0A 31 39 31 0D 0A *-8859-1....191..* 288 : 3C 21 44 4F 43 54 59 50 45 20 48 54 4D 4C 20 50 *<!DOCTYPE HTML P* 304 : 55 42 4C 49 43 20 22 2D 2F 2F 49 45 54 46 2F 2F *UBLIC "-//IETF//* 320 : 44 54 44 20 48 54 4D 4C 20 32 2E 30 2F 2F 45 4E *DTD HTML 2.0//EN* 336 : 22 3E 0A 3C 68 74 6D 6C 3E 3C 68 65 61 64 3E 0A *">.<html><head>.* 352 : 3C 74 69 74 6C 65 3E 34 30 31 20 41 75 74 68 6F *<title>401 Autho* 368 : 72 69 7A 61 74 69 6F 6E 20 52 65 71 75 69 72 65 *rization Require* 384 : 64 3C 2F 74 69 74 6C 65 3E 0A 3C 2F 68 65 61 64 *d</title>.</head* 400 : 3E 3C 62 6F 64 79 3E 0A 3C 68 31 3E 41 75 74 68 *><body>.<h1>Auth* 416 : 6F 72 69 7A 61 74 69 6F 6E 20 52 65 71 75 69 72 *orization Requir* 432 : 65 64 3C 2F 68 31 3E 0A 3C 70 3E 54 68 69 73 20 *ed</h1>.<p>This * 448 : 73 65 72 76 65 72 20 63 6F 75 6C 64 20 6E 6F 74 *server could not* 464 : 20 76 65 72 69 66 79 20 74 68 61 74 20 79 6F 75 * verify that you* 480 : 0A 61 72 65 20 61 75 74 68 6F 72 69 7A 65 64 20 *.are authorized * 496 : 74 6F 20 61 63 63 65 73 73 20 74 68 65 20 64 6F *to access the do* 512 : 63 75 6D 65 6E 74 0A 72 65 71 75 65 73 74 65 64 *cument.requested* 528 : 2E 20 20 45 69 74 68 65 72 20 79 6F 75 20 73 75 *. Either you su* 544 : 70 70 6C 69 65 64 20 74 68 65 20 77 72 6F 6E 67 *pplied the wrong* 560 : 0A 63 72 65 64 65 6E 74 69 61 6C 73 20 28 65 2E *.credentials (e.* 576 : 67 2E 2C 20 62 61 64 20 70 61 73 73 77 6F 72 64 *g., bad password* 592 : 29 2C 20 6F 72 20 79 6F 75 72 0A 62 72 6F 77 73 *), or your.brows* 608 : 65 72 20 64 6F 65 73 6E 27 74 20 75 6E 64 65 72 *er doesn't under* 624 : 73 74 61 6E 64 20 68 6F 77 20 74 6F 20 73 75 70 *stand how to sup* 640 : 70 6C 79 0A 74 68 65 20 63 72 65 64 65 6E 74 69 *ply.the credenti* 656 : 61 6C 73 20 72 65 71 75 69 72 65 64 2E 3C 2F 70 *als required.</p* 672 : 3E 0A 3C 2F 62 6F 64 79 3E 3C 2F 68 74 6D 6C 3E *>.</body></html>* 688 : 0A 0D 0A -- -- -- -- -- -- -- -- -- -- -- -- -- *...-------------*
Changing the state of the bug since the required information has been provided.
Created attachment 21407 [details] don't send 100 continue if a client error (4xx) occurred Could you please try this patch (made against apache 2.2.6) and let me know if it fixes your issue. Thanks
Created attachment 21408 [details] don't send 100 continue if a client error (4xx) occurred Try this instead of the previous patch (again made against 2.2.6)
Created attachment 21414 [details] don't send 100 continue if a client error (4xx) occurred - patch against 2.2.8 Patch to fix the issue made against httpd 2.2.8
(In reply to comment #11) > Created an attachment (id=21414) [edit] > don't send 100 continue if a client error (4xx) occurred - patch against 2.2.8 > > Patch to fix the issue made against httpd 2.2.8 This patch worked perfectly. Thanks for fixing this. FYI - Test result for resource that requires authentication - PUT /secret/test.html HTTP/1.1 Host: 10.10.10.1:8080 Expect: 100-continue Date: Mon, 15 Oct 2007 20:05:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream HTTP/1.1 401 Authorization Required Date: Fri, 25 Jan 2008 22:52:05 GMT Server: Apache/2.2.8 (Unix) WWW-Authenticate: Basic realm="test" Content-Length: 401 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 etc... PUT /secret/test.html HTTP/1.1 Host: 10.10.10.1:8080 Authorization: Basic dGVzdDp0ZXN0DQo= Date: Mon, 15 Oct 2007 22:22:24 GMT Connection: Keep-Alive Content-Length: 49 Content-Type: application/octet-stream <html><body><h1>Secret works!</h1></body></html> HTTP/1.1 204 No Content Date: Fri, 25 Jan 2008 22:52:10 GMT Server: Apache/2.2.8 (Unix) Content-Length: 0 Keep-Alive: timeout=5, max=99 Connection: Keep-Alive Content-Type: text/html
Fixed in trunk - r628644
Fix backported to 2.2.x in r630366 - will be in 2.2.9.
*** Bug 44492 has been marked as a duplicate of this bug. ***
I've the same issue but with a 307 (Redirect). Apache 2.2.6 sends a 100-continue before the redirect. This makes the client send the HTTP PUT stream on the wrong place (where none is listening). Afterwards the client is redirected ... but does not sends the put stream because was alreay sent ... With Apache 1.3 worked correctly (no 100 was echoed) My question is: Does the patch also solves this issue?
I don't think so, but have you checked?
<snip> + if (ap_is_HTTP_CLIENT_ERROR(f->r->status)) { + ctx->state = BODY_NONE; + ctx->eos_sent = 1; + } else { </snap> From the include/httpd.h: #define ap_is_HTTP_CLIENT_ERROR(x) (((x) >= 400)&&((x) < 500)) The patch does look if there is an error - 307 is not - in that case it will not send a 100 cont. So you will still have the 100 cont problem with this patch for an 307 status code. The patch should not do an 100 cont if != 2xx I think.
Sounds reasonable. In this case the following patch should fix this: Index: modules/http/http_filters.c =================================================================== --- modules/http/http_filters.c (revision 656113) +++ modules/http/http_filters.c (working copy) @@ -323,7 +323,7 @@ (ctx->state == BODY_LENGTH && ctx->remaining > 0)) && f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1) && !(f->r->eos_sent || f->r->bytes_sent)) { - if (ap_is_HTTP_CLIENT_ERROR(f->r->status)) { + if (!ap_is_HTTP_SUCCESS(f->r->status)) { ctx->state = BODY_NONE; ctx->eos_sent = 1; } else { Can someone try please?
I've tested the given code (https://issues.apache.org/bugzilla/attachment.cgi?id=21414 + Comment #19) It works now with 3xx requests! Many Thanks
Committed to trunk as r657933 (http://svn.apache.org/viewvc?view=rev&revision=657933).
Proposed for backport to 2.2.x as r660284 (http://svn.apache.org/viewvc?rev=660284&view=rev).
Backport to 2.2.x as r660569 (http://svn.apache.org/viewvc?rev=660569&view=rev).