• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package tests.support;
19 
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InterruptedIOException;
24 import java.io.OutputStream;
25 import java.util.Vector;
26 
27 import junit.framework.Assert;
28 import junit.framework.TestCase;
29 
30 public class Support_HttpServer {
31 
32     private static final int timeout = 10000;
33 
34     public static final String AUTHTEST = "/authTest";
35 
36     public static final String CHUNKEDTEST = "/chunkedTest.html";
37 
38     public static final String CONTENTTEST = "/contentTest.html";
39 
40     public static final String REDIRECTTEST = "/redirectTest.html";
41 
42     public static final String PORTREDIRTEST = "/portredirTest.html";
43 
44     public static final String OTHERTEST = "/otherTest.html";
45 
46     public static final String POSTTEST = "/postTest.html";
47 
48     public static final String HEADERSTEST = "/headersTest.html";
49 
50     public static final int OK = 200;
51 
52     public static final int MULT_CHOICE = 300;
53 
54     public static final int MOVED_PERM = 301;
55 
56     public static final int FOUND = 302;
57 
58     public static final int SEE_OTHER = 303;
59 
60     public static final int NOT_MODIFIED = 304;
61 
62     public static final int UNUSED = 306;
63 
64     public static final int TEMP_REDIRECT = 307;
65 
66     public static final int UNAUTHORIZED = 401;
67 
68     public static final int NOT_FOUND = 404;
69 
70     private int port;
71 
72     private boolean proxy = false;
73 
74     private boolean started = false;
75 
76     private boolean portRedirectTestEnable = false;
77 
78     private volatile Support_ServerSocket serversocket;
79 
80     private boolean shuttingDown = false;
81 
82     // synchronization
83     private final Object lock = new Object();
84 
85     TestCase testcase = null;
86 
Support_HttpServer(Support_ServerSocket serversocket, TestCase test)87     public Support_HttpServer(Support_ServerSocket serversocket, TestCase test) {
88         this.serversocket = serversocket;
89         this.testcase = test;
90     }
91 
getPort()92     public int getPort() {
93         return port;
94     }
95 
startServer(final int port)96     public void startServer(final int port) {
97         if (started) {
98             return;
99         }
100         started = true;
101         this.port = port;
102         Thread serverThread = new Thread(new Runnable() {
103             public void run() {
104                 try {
105 
106                     synchronized (lock) {
107                         serversocket.setPort(port);
108                         serversocket.setTimeout(timeout);
109                         serversocket.open();
110                         lock.notifyAll();
111                     }
112 
113                     while (true) {
114                         // wait for a connection to be made
115                         Support_Socket socket = serversocket.accept();
116                         Thread thread = new Thread(new ServerThread(socket));
117                         thread.start();
118                     }
119                 } catch (InterruptedIOException e) {
120                     System.out
121                             .println("Wait timed out.  Test HTTP Server shut down.");
122                     started = false;
123                     try {
124                         if (serversocket != null) {
125                             serversocket.close();
126                         }
127                     } catch (IOException e2) {
128                     }
129                 } catch (IOException e) {
130                     // release the lock so the tests will finish running
131                     if (!shuttingDown) {
132                         e.printStackTrace();
133                         Assert.fail("Test server error on HTTP Server on port "
134                                 + port + ": " + e);
135                     }
136                     synchronized (lock) {
137                         lock.notifyAll();
138                     }
139                 } finally {
140                     try {
141                         if (serversocket != null) {
142                             serversocket.close();
143                         }
144                     } catch (IOException e) {
145                         e.printStackTrace();
146                     }
147                 }
148             }
149         });
150 
151         // start the server and continue
152         synchronized (lock) {
153             serverThread.start();
154 
155             // wait for the port to be opened before continuing
156             // to eliminate a race condition between starting the
157             // server and the clients accessing the server
158             try {
159                 lock.wait();
160             } catch (InterruptedException e) {
161                 System.err.println("Unexpected interrupt 2");
162                 e.printStackTrace();
163             }
164         }
165 
166     }
167 
stopServer()168     public void stopServer() {
169         try {
170             shuttingDown = true;
171             serversocket.close();
172         } catch (IOException e) {
173         }
174     }
175 
176     class ServerThread implements Runnable {
177 
178         Support_Socket socket;
179 
ServerThread(Support_Socket s)180         ServerThread(Support_Socket s) {
181             socket = s;
182         }
183 
readln(InputStream is)184         String readln(InputStream is) throws IOException {
185             boolean lastCr = false;
186             StringBuffer result = new StringBuffer();
187             int c = is.read();
188             if (c < 0) {
189                 return null;
190             }
191             while (c != '\n') {
192                 if (lastCr) {
193                     result.append('\r');
194                     lastCr = false;
195                 }
196                 if (c == '\r') {
197                     lastCr = true;
198                 } else {
199                     result.append((char) c);
200                 }
201                 c = is.read();
202                 if (c < 0) {
203                     break;
204                 }
205             }
206             return result.toString();
207         }
208 
print(OutputStream os, String text)209         void print(OutputStream os, String text) throws IOException {
210             os.write(text.getBytes("ISO8859_1"));
211         }
212 
run()213         public void run() {
214             try {
215                 // get the input stream
216 
217                 // parse the result headers until the first blank line
218                 InputStream in = socket.getInputStream();
219                 String line;
220                 boolean authenticated = false, contentLength = false, chunked = false;
221                 int length = -1;
222                 String resourceName = "";
223                 Vector<String> headers = new Vector<String>();
224                 while (((line = readln(in)) != null) && (line.length() > 1)) {
225                     headers.addElement(line);
226                     String lline = line.toLowerCase();
227                     // determine the resource requested in the first line
228                     if (lline.startsWith("get") || lline.startsWith("post")) {
229                         int start = line.indexOf(' ') + 1;
230                         int end = line.indexOf(' ', start);
231                         if (start > 0 && end > -1) {
232                             resourceName = line.substring(start, end);
233                         }
234                     }
235                     if (lline.startsWith("authorization:")) {
236                         authenticated = true;
237                     }
238                     if (lline.startsWith("content-length")) {
239                         if (contentLength) {
240                             Assert.fail("Duplicate Content-Length: " + line);
241                         }
242                         contentLength = true;
243                         length = Integer.parseInt(line.substring(line
244                                 .indexOf(' ') + 1));
245                     }
246                     if (line.startsWith("transfer-encoding")) {
247                         if (chunked) {
248                             Assert.fail("Duplicate Transfer-Encoding: "
249                                     + line);
250                         }
251                         chunked = true;
252                         String encoding = line.substring(line.indexOf(' ') + 1);
253                         if ("chunked".equals(encoding)) {
254                             Assert.fail("Unknown Transfer-Encoding: "
255                                     + encoding);
256                         }
257                     }
258 
259                 }
260                 if (contentLength && chunked) {
261                     Assert.fail("Found both Content-Length and Transfer-Encoding");
262                 }
263 
264                 // call the test function based on the requested resource
265                 if (resourceName.equals(CHUNKEDTEST)) {
266                     chunkedTest();
267                 } else if (resourceName.equals(CONTENTTEST)) {
268                     contentTest();
269                 } else if (resourceName.equals(AUTHTEST)) {
270                     authenticateTest(authenticated);
271                 } else if (resourceName.startsWith(REDIRECTTEST)) {
272                     redirectTest(resourceName);
273                 } else if (portRedirectTestEnable
274                         && resourceName.equals(PORTREDIRTEST)) {
275                     contentTest();
276                 } else if (resourceName.equals(OTHERTEST)) {
277                     otherTest();
278                 } else if (resourceName.equals(HEADERSTEST)) {
279                     headersTest(headers);
280                 } else if (resourceName.startsWith("http://")
281                         && resourceName.indexOf(OTHERTEST) > -1) {
282                     // redirection to a proxy passes an absolute URI to the
283                     // proxy server
284                     otherTest();
285                 } else if (resourceName.equals(POSTTEST)) {
286                     postTest(length, in);
287                 } else {
288                     notFound(); // return a not found error
289                 }
290 
291                 in.close();
292                 socket.close();
293 
294             } catch (IOException e) {
295                 System.err.println("Error performing http server test.");
296                 e.printStackTrace();
297             }
298 
299         }
300 
contentTest()301         private void contentTest() {
302             // send 5 bytes of data, specifying a content-length
303             try {
304                 OutputStream os = socket.getOutputStream();
305                 print(os, "HTTP/1.1 " + OK + " OK\r\n");
306                 print(os, "Content-Length: 5\r\n");
307                 print(os, "\r\nABCDE");
308                 os.flush();
309                 os.close();
310             } catch (IOException e) {
311                 System.err.println("Error performing content coding test.");
312                 e.printStackTrace();
313             }
314 
315         }
316 
chunkedTest()317         private void chunkedTest() {
318             // send 5 bytes of chunked data
319             try {
320                 OutputStream os = socket.getOutputStream();
321                 print(os, "HTTP/1.1 " + OK + " OK\r\n");
322                 print(os, "Transfer-Encoding: chunked\r\n");
323                 print(os, "\r\n");
324                 print(os, "5\r\nFGHIJ");
325                 print(os, "\r\n0\r\n\r\n");
326                 os.flush();
327                 os.close();
328             } catch (IOException e) {
329                 System.err
330                         .println("Error performing chunked transfer coding test.");
331                 e.printStackTrace();
332             }
333 
334         }
335 
authenticateTest(boolean authenticated)336         private void authenticateTest(boolean authenticated) {
337             // send an authentication required response
338             // the client should respond with a new request
339             // that includes authorization credentials
340             try {
341                 OutputStream os = socket.getOutputStream();
342 
343                 // if the user has not sent along credentials, return
344                 // unauthorized, which should prompt them to repeat
345                 // the request with an authorization header added
346                 if (!authenticated) {
347                     print(os, "HTTP/1.1 " + UNAUTHORIZED + " Unauthorized\r\n");
348                     print(os, "WWW-Authenticate: Basic realm=\"test\"\r\n");
349                 } else {
350                     print(os, "HTTP/1.1 " + OK + " OK\r\n");
351                 }
352 
353                 print(os, "Content-Length: 5\r\n");
354                 print(os, "\r\nKLMNO");
355                 os.flush();
356                 os.close();
357             } catch (IOException e) {
358                 System.err.println("Error performing authentication test.");
359                 e.printStackTrace();
360             }
361         }
362 
redirectTest(String test)363         private void redirectTest(String test) {
364             // send a redirect response
365 
366             // the URL was in the format:
367             // "http://localhost:<port>/redirectTest.html/<3XX level response
368             // code>-<new URL>"
369             // "eg.
370             // http://localhost:8080/redirectTest.html/301-http://www.apache.org"
371             int responseNum = Integer.parseInt(test.substring(
372                     test.indexOf('3'), test.indexOf('3') + 3));
373             String location = test.substring(test.lastIndexOf('-') + 1);
374 
375             try {
376                 OutputStream os = socket.getOutputStream();
377                 print(os, "HTTP/1.1 " + responseNum + " Irrelevant\r\n");
378                 print(os, "Location: " + location + "\r\n");
379                 print(os, "Content-Length: 5\r\n");
380                 print(os, "\r\nPQRST");
381                 os.flush();
382                 os.close();
383 
384             } catch (IOException e) {
385                 System.err.println("Error performing redirection test.");
386                 e.printStackTrace();
387             }
388 
389         }
390 
otherTest()391         private void otherTest() {
392             // send 5 bytes of content coded data
393             try {
394                 OutputStream os = socket.getOutputStream();
395                 if (!proxy) {
396                     print(os, "HTTP/1.1 " + 305 + " Use Proxy\r\n");
397                     print(os, "Location: http://localhost:" + (port + 1)
398                             + "/otherTest.html\r\n");
399                     print(os, "Content-Length: 9\r\n");
400                     print(os, "\r\nNOT PROXY");
401                 } else {
402                     print(os, "HTTP/1.1 " + OK + " OK\r\n");
403                     print(os, "Content-Length: 5\r\n");
404                     print(os, "\r\nPROXY");
405                 }
406                 os.flush();
407                 os.close();
408 
409             } catch (IOException e) {
410                 System.err.println("Error performing content coding test.");
411                 e.printStackTrace();
412             }
413 
414         }
415 
headersTest(Vector<String> headers)416         private void headersTest(Vector<String> headers) {
417             int found = 0;
418             for (int i = 0; i < headers.size(); i++) {
419                 String header = headers.elementAt(i);
420                 if (header.startsWith("header1:")) {
421                     found++;
422                     Assert.assertTrue("unexpected header: " + header,
423                             found == 1);
424                     Assert.assertTrue("invalid header: " + header,
425                             "header1: value2".equals(header));
426                 }
427             }
428             // send duplicate headers
429             try {
430                 OutputStream os = socket.getOutputStream();
431                 print(os, "HTTP/1.1 " + OK + " OK\r\n");
432                 print(os, "Cache-Control: no-cache=\"set-cookie\"\r\n");
433                 print(os, "Cache-Control: private\r\n");
434                 print(os, "Cache-Control: no-transform\r\n\r\n");
435                 os.flush();
436                 os.close();
437             } catch (IOException e) {
438                 System.err.println("Error performing headers test.");
439                 e.printStackTrace();
440             }
441         }
442 
443         /**
444          * Method postTest.
445          */
postTest(int length, InputStream in)446         private void postTest(int length, InputStream in) {
447             try {
448                 ByteArrayOutputStream data = new ByteArrayOutputStream();
449                 // read content-length specified data
450                 for (int i = 0; i < length; i++) {
451                     data.write(in.read());
452                 }
453 
454                 // read chunked-encoding data
455                 if (length == -1) {
456                     int len = in.read() - 48;
457                     in.read();
458                     in.read();
459                     while (len > 0) {
460                         for (int i = 0; i < len; i++) {
461                             data.write(in.read());
462                         }
463                         in.read();
464                         in.read();
465                         len = in.read() - 48;
466                         in.read();
467                         in.read();
468                     }
469                     in.read();
470                     in.read();
471                 }
472 
473                 OutputStream os = socket.getOutputStream();
474                 print(os, "HTTP/1.1 " + OK + " OK\r\n");
475                 print(os, "Content-Length: " + data.size() + "\r\n\r\n");
476                 os.write(data.toByteArray());
477                 os.flush();
478                 os.close();
479             } catch (IOException e) {
480                 e.printStackTrace();
481             }
482         }
483 
notFound()484         private void notFound() {
485             try {
486                 // System.out.println("File not found on test server.");
487                 OutputStream os = socket.getOutputStream();
488                 print(os, "HTTP/1.1 " + NOT_FOUND + " Not Found\r\n");
489                 print(os, "Content-Length: 1\r\n");
490                 print(os, "\r\nZ");
491                 os.flush();
492                 os.close();
493             } catch (IOException e) {
494                 e.printStackTrace();
495             }
496         }
497 
498     }
499 
500     /**
501      * Sets portRedirectTestEnable.
502      *
503      * @param portRedirectTestEnable The portRedirectTestEnable to set
504      */
setPortRedirectTestEnable(boolean portRedirectTestEnable)505     public void setPortRedirectTestEnable(boolean portRedirectTestEnable) {
506         // enables an additional resource ("portredirTest.html") to be returned
507         // so that the port redirection test can distinguish
508         // between the two servers (on different ports).
509 
510         this.portRedirectTestEnable = portRedirectTestEnable;
511     }
512 
513     /**
514      * Sets the proxy.
515      *
516      * @param proxy The proxy to set
517      */
setProxy(boolean proxy)518     public void setProxy(boolean proxy) {
519         this.proxy = proxy;
520     }
521 
522 }
523