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