Home | History | Annotate | Download | only in support
      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 
     87     public Support_HttpServer(Support_ServerSocket serversocket, TestCase test) {
     88         this.serversocket = serversocket;
     89         this.testcase = test;
     90     }
     91 
     92     public int getPort() {
     93         return port;
     94     }
     95 
     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 
    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 
    180         ServerThread(Support_Socket s) {
    181             socket = s;
    182         }
    183 
    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 
    209         void print(OutputStream os, String text) throws IOException {
    210             os.write(text.getBytes("ISO8859_1"));
    211         }
    212 
    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 
    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 
    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 
    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 
    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 
    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 
    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          */
    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 
    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      */
    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      */
    518     public void setProxy(boolean proxy) {
    519         this.proxy = proxy;
    520     }
    521 
    522 }
    523