Bug 43711 - 100-continue response when 401 expected
Summary: 100-continue response when 401 expected
Status: RESOLVED FIXED
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: Core (show other bugs)
Version: 2.2.6
Hardware: Sun Solaris
: P2 regression (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL:
Keywords: FixedInTrunk, RFC
: 44492 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-10-26 14:08 UTC by Ragini Bisarya
Modified: 2008-05-27 09:02 UTC (History)
2 users (show)



Attachments
Patch against trunk (443 bytes, patch)
2007-10-27 08:01 UTC, Ruediger Pluem
Details | Diff
don't send 100 continue if a client error (4xx) occurred (1.82 KB, patch)
2008-01-19 17:06 UTC, Chetan Reddy
Details | Diff
don't send 100 continue if a client error (4xx) occurred (1.85 KB, patch)
2008-01-19 18:15 UTC, Chetan Reddy
Details | Diff
don't send 100 continue if a client error (4xx) occurred - patch against 2.2.8 (1.73 KB, patch)
2008-01-22 15:47 UTC, Chetan Reddy
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ragini Bisarya 2007-10-26 14:08:22 UTC
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
Comment 1 Nick Kew 2007-10-26 16:19:03 UTC
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.
Comment 2 Ruediger Pluem 2007-10-27 08:01:31 UTC
Created attachment 21053 [details]
Patch against trunk
Comment 3 Ruediger Pluem 2007-10-27 08:02:10 UTC
Can you please check if the attached patch solves your problem?
Comment 4 Ragini Bisarya 2007-10-29 11:33:49 UTC
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.

Comment 5 Ruediger Pluem 2007-10-29 13:37:31 UTC
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.
Comment 6 Ragini Bisarya 2007-10-29 17:49:21 UTC
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
Comment 7 Ragini Bisarya 2007-10-29 18:34:57 UTC
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 -- -- -- -- --  -- -- -- -- -- -- -- --   *...-------------*
Comment 8 Ragini Bisarya 2007-11-14 10:53:06 UTC
Changing the state of the bug since the required information has been provided.
Comment 9 Chetan Reddy 2008-01-19 17:06:15 UTC
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
Comment 10 Chetan Reddy 2008-01-19 18:15:29 UTC
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)
Comment 11 Chetan Reddy 2008-01-22 15:47:48 UTC
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
Comment 12 Ragini Bisarya 2008-01-25 16:46:24 UTC
(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

Comment 13 Nick Kew 2008-02-18 00:38:21 UTC
Fixed in trunk - r628644
Comment 14 Nick Kew 2008-02-22 16:37:33 UTC
Fix backported to 2.2.x in r630366 - will be in 2.2.9.
Comment 15 Ruediger Pluem 2008-02-26 11:43:24 UTC
*** Bug 44492 has been marked as a duplicate of this bug. ***
Comment 16 Curzio Della Santa 2008-05-15 10:04:55 UTC
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?
Comment 17 Ruediger Pluem 2008-05-15 12:16:30 UTC
I don't think so, but have you checked?
Comment 18 Christian Liesch 2008-05-16 06:31:30 UTC
<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.
Comment 19 Ruediger Pluem 2008-05-16 07:58:22 UTC
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?
Comment 20 Curzio Della Santa 2008-05-19 07:45:19 UTC
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
Comment 21 Ruediger Pluem 2008-05-19 12:30:55 UTC
Committed to trunk as r657933 (http://svn.apache.org/viewvc?view=rev&revision=657933).
Comment 22 Ruediger Pluem 2008-05-26 13:06:52 UTC
Proposed for backport to 2.2.x as r660284 (http://svn.apache.org/viewvc?rev=660284&view=rev).
Comment 23 Ruediger Pluem 2008-05-27 09:02:48 UTC
Backport to 2.2.x as r660569
(http://svn.apache.org/viewvc?rev=660569&view=rev).