1   /*
2    * ====================================================================
3    *
4    *  Licensed to the Apache Software Foundation (ASF) under one or more
5    *  contributor license agreements.  See the NOTICE file distributed with
6    *  this work for additional information regarding copyright ownership.
7    *  The ASF licenses this file to You under the Apache License, Version 2.0
8    *  (the "License"); you may not use this file except in compliance with
9    *  the License.  You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *  Unless required by applicable law or agreed to in writing, software
14   *  distributed under the License is distributed on an "AS IS" BASIS,
15   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *  See the License for the specific language governing permissions and
17   *  limitations under the License.
18   * ====================================================================
19   *
20   * This software consists of voluntary contributions made by many
21   * individuals on behalf of the Apache Software Foundation.  For more
22   * information on the Apache Software Foundation, please see
23   * <http://www.apache.org/>.
24   *
25   * [Additional notices, if required by prior licensing conditions]
26   *
27   */
28  package org.apache.commons.httpclient;
29  
30  import java.io.IOException;
31  
32  import junit.framework.*;
33  import org.apache.commons.httpclient.methods.*;
34  import org.apache.commons.httpclient.params.HttpMethodParams;
35  import org.apache.commons.httpclient.server.HttpRequestHandler;
36  import org.apache.commons.httpclient.server.ResponseWriter;
37  import org.apache.commons.httpclient.server.SimpleHttpServerConnection;
38  import org.apache.commons.httpclient.server.SimpleRequest;
39  
40  /***
41   * Tests handling of non-compliant responses.
42   * 
43   * @author Oleg Kalnichevski
44   * @author Jeff Dever
45   */
46  public class TestNoncompliant extends HttpClientTestBase {
47  
48      public TestNoncompliant(String s) throws IOException {
49          super(s);
50      }
51  
52      public static Test suite() {
53          TestSuite suite = new TestSuite(TestNoncompliant.class);
54          return suite;
55      }
56  
57      /***
58       * Tests if client is able to recover gracefully when HTTP server or
59       * proxy fails to send 100 status code when expected. The client should
60       * resume sending the request body after a defined timeout without having
61       * received "continue" code.
62       */
63      public void testNoncompliantPostMethodString() throws Exception {
64          this.server.setRequestHandler(new HttpRequestHandler() {
65              public boolean processRequest(SimpleHttpServerConnection conn,
66                      SimpleRequest request) throws IOException {
67                  ResponseWriter out = conn.getWriter();
68                  out.println("HTTP/1.1 200 OK");
69                  out.println("Connection: close");
70                  out.println("Content-Length: 0");
71                  out.println();
72                  out.flush();
73                  return true;
74              }
75          });
76  
77          PostMethod method = new PostMethod("/");
78          method.getParams().setBooleanParameter(
79                  HttpMethodParams.USE_EXPECT_CONTINUE, true);
80          method.setRequestEntity(new StringRequestEntity(
81                  "This is data to be sent in the body of an HTTP POST.", null, null));
82          client.executeMethod(method);
83          assertEquals(200, method.getStatusCode());
84      }
85  
86      /***
87       * Tests that a response status line containing \r and \n is handled.
88       */
89      public void testNoncompliantStatusLine() {
90          this.server.setRequestHandler(new HttpRequestHandler() {
91              public boolean processRequest(SimpleHttpServerConnection conn,
92                      SimpleRequest request) throws IOException {
93                  ResponseWriter out = conn.getWriter();
94                  out.println("HTTP/1.1 444 This status message contains\n"
95                          + " a newline and a\r"
96                          + " carrage return but that should be OK.");
97                  out.println("Connection: close");
98                  out.println("Content-Length: 0");
99                  out.println();
100                 out.flush();
101                 return true;
102             }
103         });
104         GetMethod method = new GetMethod("/");
105         try {
106             client.executeMethod(method);
107         } catch (Exception e) {
108             e.printStackTrace();
109             fail("Unexpected exception: " + e.toString());
110         }
111         assertEquals(444, method.getStatusCode());
112     }
113 
114     /***
115      * Test if a response to HEAD method from non-compliant server that contains
116      * an unexpected body content can be correctly redirected
117      */
118     public void testNoncompliantHeadWithResponseBody() throws Exception {
119         final String body = "Test body";
120         this.server.setRequestHandler(new HttpRequestHandler() {
121             public boolean processRequest(SimpleHttpServerConnection conn,
122                     SimpleRequest request) throws IOException {
123                 ResponseWriter out = conn.getWriter();
124                 out.println("HTTP/1.1 200 OK");
125                 out.println("Connection: close");
126                 out.println("Content-Length: " + body.length());
127                 out.println();
128                 out.print(body);
129                 out.flush();
130                 return true;
131             }
132         });
133         HeadMethod method = new HeadMethod("/");
134         method.getParams().setIntParameter(
135                 HttpMethodParams.HEAD_BODY_CHECK_TIMEOUT, 50);
136         client.executeMethod(method);
137         assertEquals(200, method.getStatusCode());
138         method.releaseConnection();
139     }
140 
141     /***
142      * Test if a response to HEAD method from non-compliant server causes an
143      * HttpException to be thrown
144      */
145     public void testNoncompliantHeadStrictMode() throws Exception {
146         final String body = "Test body";
147         this.server.setRequestHandler(new HttpRequestHandler() {
148             public boolean processRequest(SimpleHttpServerConnection conn,
149                     SimpleRequest request) throws IOException {
150                 ResponseWriter out = conn.getWriter();
151                 out.println("HTTP/1.1 200 OK");
152                 out.println("Connection: close");
153                 out.println("Content-Length: " + body.length());
154                 out.println();
155                 out.print(body);
156                 out.flush();
157                 return true;
158             }
159         });
160         client.getParams().setBooleanParameter(
161                 HttpMethodParams.REJECT_HEAD_BODY, true);
162         HeadMethod method = new NoncompliantHeadMethod("/");
163         method.getParams().setIntParameter(
164                 HttpMethodParams.HEAD_BODY_CHECK_TIMEOUT, 50);
165         try {
166             client.executeMethod(method);
167             fail("HttpException should have been thrown");
168         } catch (HttpException e) {
169             // Expected
170         }
171         method.releaseConnection();
172     }
173 
174     /***
175      * Tests if client is able to handle gracefully malformed responses
176      * that may not include response body.
177      */
178     public void testMalformed304Response() throws Exception {
179         this.server.setRequestHandler(new HttpRequestHandler() {
180             public boolean processRequest(SimpleHttpServerConnection conn,
181                     SimpleRequest request) throws IOException {
182                 conn.setSocketTimeout(20000);
183                 ResponseWriter out = conn.getWriter();
184                 out.println("HTTP/1.1 304 OK");
185                 out.println("Connection: keep-alive");
186                 out.println("Content-Length: 100");
187                 out.println();
188                 out.flush();
189                 conn.setKeepAlive(true);
190                 return true;
191             }
192         });
193 
194         GetMethod method = new GetMethod("/");
195         method.getParams().setSoTimeout(1000);
196         client.executeMethod(method);
197         assertEquals(HttpStatus.SC_NOT_MODIFIED, method.getStatusCode());
198         method.getResponseBody();
199     }
200 
201     public void testMalformed204Response() throws Exception {
202         this.server.setRequestHandler(new HttpRequestHandler() {
203             public boolean processRequest(SimpleHttpServerConnection conn,
204                     SimpleRequest request) throws IOException {
205                 conn.setSocketTimeout(20000);
206                 ResponseWriter out = conn.getWriter();
207                 out.println("HTTP/1.1 204 OK");
208                 out.println("Connection: close");
209                 out.println("Content-Length: 100");
210                 out.println();
211                 out.flush();
212                 conn.setKeepAlive(true);
213                 return true;
214             }
215         });
216 
217         GetMethod method = new GetMethod("/");
218         method.getParams().setSoTimeout(1000);
219         client.executeMethod(method);
220         assertEquals(HttpStatus.SC_NO_CONTENT, method.getStatusCode());
221         method.getResponseBody();
222     }
223     
224 }