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