This is the mail archive of the mauve-patches@sourceware.org mailing list for the Mauve project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFC: HTTPTestServer


Hi David,

David Daney wrote:
[...]
> 
> Not withstanding all of that, I think it is an excellent start to a very
> much needed capability.  Just because it is not exactly what I would
> have done does not mean that it is not good and useful.
> 
> My call:  You decide.  Commit it in as is, or keep hacking and commit
> later.

I will commit it now to have the tests available. However you are certainly
right that its nothing extensible and could be improved a lot. But I currently
have no time to do this further. If anyone in the future comes up with
something modular I will happily convert the tests to use that.

Committed as follows:

2006-03-06  Wolfgang Baer  <WBaer@gmx.de>

	* gnu/testlet/java/net/HttpURLConnection/TestHttpServer.java: New file.
	* gnu/testlet/java/net/HttpURLConnection/responseCodeTest.java:
	New test using TestHttpServer.java.
	* gnu/testlet/java/net/HttpURLConnection/responseHeadersTest.java:
	Likewise.


Wolfgang


//Tags: not-a-test

//Copyright (C) 2006 Free Software Foundation, Inc.
//Written by Wolfgang Baer (WBaer@gmx.de)

//This file is part of Mauve.

//Mauve is free software; you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation; either version 2, or (at your option)
//any later version.

//Mauve is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with Mauve; see the file COPYING.  If not, write to
//the Free Software Foundation, 51 Franklin Street, Fifth Floor,
//Boston, MA, 02110-1301 USA.

package gnu.testlet.java.net.HttpURLConnection;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;


/**
 * A HTTP server for testing purpose only. The server can
 * be started on a given port and the response headers and
 * response body to be returned set. This way one can test
 * arbritrary testcases for the http client side library.
 *  
 * @see gnu.testlet.java.net.HttpURLConnection.responseCodeTest
 * @see gnu.testlet.java.net.HttpURLConnection.responseHeadersTest
 * 
 * @author Wolfgang Baer (WBaer@gmx.de)
 */
public final class TestHttpServer implements Runnable
{ 
  /**
   * The interface to be implemented if checking
   * of the serverside received headers and body
   * is desired.
   */
  interface CheckReceivedRequest 
  {
    /**
     * Will be called with a List of headers in 
     * the sequence received by the test server.
     * 
     * @param headers List of headers
     */
    public void checkHeaders(List headers);
    
    /**
     * Will be called with the body if one was
     * received.
     * 
     * @param body the body
     */
    public void checkBody(byte[] body);    
  }
  
  /**
   * The actual request handler.
   * It always returns what is set in the TestHttpServer
   * for the response headers and response body.
   */
  class TestHttpRequestHandler implements Runnable
  {  
    Socket socket;
    OutputStream output;
    InputStream input;
  
    public TestHttpRequestHandler(Socket socket) throws Exception
    {
      this.socket = socket;
      output = socket.getOutputStream();
      input = socket.getInputStream();
    }
  
    public void run()
    {
      try
        {
          // Read the whole request into a byte array
          ByteArrayOutputStream buffer = new ByteArrayOutputStream();
          
          byte[] b = new byte[1024];
          int bytes = 0 ;
          while ((bytes = input.read(b)) != -1)
            { 
              buffer.write(b, 0, bytes);
              if (bytes < 1024)
                break;
            }
          
          byte[] request = buffer.toByteArray();

          // Parse the request headers from the byte array          
          List headerList = new ArrayList();
          
          ByteArrayOutputStream line;
          int i = 0;
          line = new ByteArrayOutputStream();
          for (; i < request.length; i++)
            {
              if (request[i] != (byte) 0x0a) // LF
                line.write(request[i]);
              else
                {
                  byte[] array = line.toByteArray();
                  if (array.length == 1) // the last is only a LF
                    break;

                  String headerLine = new String(array);
                  headerList.add(headerLine);
                  line = new ByteArrayOutputStream();
                }
            }

          // Put the remaining bytes into the request body 
          byte[] body = new byte[(request.length - (i + 1))];
          System.arraycopy(request, i + 1, body, 0, body.length);
          
          // Check everything          
          if (check != null)
            {
              check.checkHeaders(headerList);
              if (body.length > 0)
                check.checkBody(body);
            }         
             
          // Response writing          
          // write the response headers
          output.write(responseHeader);
          
          // write the body 
          if (responseBody != null)
            output.write(responseBody);
          
          // Clean up
          output.close();
          input.close();
          socket.close();            
        }
      catch (Exception e)
        {
          // ignore
        }
    }
  }

  int port;
  byte[] responseHeader;
  byte[] responseBody;
  CheckReceivedRequest check;
  boolean kill = false;
  ServerSocket serverSocket;  

  /**
   * Create a TestHttpServer on given port
   * @param port the port to use.
   */
  public TestHttpServer(int port)
  {
    this.port = port;
  }
  
  /**
   * An object implementing the CheckReceivedRequest
   * interface. The methods will be called to enable
   * checks of the serverside received request.
   * 
   * @param responseBody the byte[] of the body
   */
  public void setCheckReceivedRequest(CheckReceivedRequest object)
  {
    this.check = object;
  }
  
  /**
   * The bytes which should be sent as the response body.
   * @param responseBody the byte[] of the body
   */
  public void setResponseBody(byte[] responseBody)
  {
    this.responseBody = responseBody;
  }
  
  /**
   * The bytes which should be sent as the response headers. 
   * @param responseHeaders the byte[] of the headers
   */
  public void setResponseHeaders(byte[] responseHeaders)
  {
    this.responseHeader = responseHeaders;
  }
  
  /**
   * This cleans up recources so more than one
   * TestHttpServer can be used in one mauve run.
   */
  public void killTestServer()
  {
    kill = true;
    try
      {
        serverSocket.close();
      }
    catch (IOException e)
      {
        // ignore
      }
  }
  
  /**
   * Listens on the port and creates a Handler for
   * incoming connections.
   */
  public void run() 
  {   
    try
      {
        serverSocket = new ServerSocket(port);
        while (! kill)
          {
            Socket socket = serverSocket.accept();
            try
              {
                TestHttpRequestHandler request = 
                  new TestHttpRequestHandler(socket);
                Thread thread = new Thread(request);
                
                thread.start();
              }
            catch (Exception e)
              {
                // ignore
              }
          }
      }
    catch (IOException e)
      {
        // ignore
      }
  }
}
//Tags: JDK1.1
//Uses: TestHttpServer

//Copyright (C) 2006 Free Software Foundation, Inc.
//Written by Wolfgang Baer (WBaer@gmx.de)

//This file is part of Mauve.

//Mauve is free software; you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation; either version 2, or (at your option)
//any later version.

//Mauve is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with Mauve; see the file COPYING.  If not, write to
//the Free Software Foundation, 51 Franklin Street, Fifth Floor,
//Boston, MA, 02110-1301 USA.

package gnu.testlet.java.net.HttpURLConnection;

import gnu.testlet.TestHarness;
import gnu.testlet.Testlet;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Tests correct behaviour of getInputStream(), getErrorStream()
 * for all error response codes.
 */
public class responseCodeTest implements Testlet
{
  /**
   * Starts an HTTP server on port 8080 and calls
   * the test_ResponseCode for the error codes.
   */
  public void test(TestHarness h) 
  {  
    TestHttpServer server = null;
    try
      {
        server = new TestHttpServer(8080);
        Thread thread = new Thread(server);
        thread.start();
        try
          {
//          SUN JDK needs some time to open sockets
            Thread.sleep(100);
          }
        catch (InterruptedException e1)
          {
          }
        
        for (int i=400; i < 418; i++)
          test_ResponseCode(i, h, server);
        
        for (int i=500; i < 506; i++)
          test_ResponseCode(i, h, server);
      }
    finally
      {
        server.killTestServer();
      }   
  }
  
  public void test_ResponseCode(int responseCode, 
      TestHarness h, TestHttpServer server)
  {    
    try
      {        
        URL url = new URL("http://localhost:8080/";);        
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();        
        conn.setRequestMethod("GET");
        
        h.checkPoint("Test " + responseCode + " response");

        // construct what should be returned by the test server as headers        
        ByteArrayOutputStream headers = new ByteArrayOutputStream();

        // status line (the responsecode encoded for the test)
        headers.write(new String("HTTP/1.0 " + responseCode + " OK\r\n").getBytes());
        headers.write("Server: TestServer\r\n\r\n".getBytes());

        server.setResponseHeaders(headers.toByteArray());
        
        // test the responsecode
        int code = conn.getResponseCode();
        h.check(code == responseCode);

        // getInputStream should always throw an IOException
        try 
          {
            conn.getInputStream();
            h.check(false);
          }
        catch (IOException e)
          {
            // for a 404/410 it must be a FNFE
            if (responseCode == 404 
                || responseCode == 410)
              {
                // Since JDK 1.5 as FNFE is thrown - so this will fail for 1.4
                if (e instanceof FileNotFoundException)
                  h.check(true);
                else
                  h.check(false);
              }
            else
              h.check(true);
          }
        
        // the errorstream must be set always
        InputStream error = conn.getErrorStream();
        h.check(error != null);
        
        conn.disconnect();       
      }   
    catch (IOException e)
      {       
        h.debug("Unexpected IOException");
        h.debug(e);
      }
  }
}
//Tags: JDK1.4
//Uses: TestHttpServer

//Copyright (C) 2006 Free Software Foundation, Inc.
//Written by Wolfgang Baer (WBaer@gmx.de)

//This file is part of Mauve.

//Mauve is free software; you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation; either version 2, or (at your option)
//any later version.

//Mauve is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with Mauve; see the file COPYING.  If not, write to
//the Free Software Foundation, 51 Franklin Street, Fifth Floor,
//Boston, MA, 02110-1301 USA.

package gnu.testlet.java.net.HttpURLConnection;

import gnu.testlet.TestHarness;
import gnu.testlet.Testlet;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;

public class responseHeadersTest implements Testlet
{

  public void test(TestHarness harness)
  {
    TestHttpServer server = null;
    try
      {
        server = new TestHttpServer(8080);
        Thread thread = new Thread(server);
        thread.start();
        try
          {
            // SUN JDK needs some time to open sockets
            Thread.sleep(100);
          }
        catch (InterruptedException e1)
          {
          }
        
        test_MultiHeaders(harness, server);
        test_LowerUpperCaseHeaders(harness, server);
      }
    finally
      {
        server.killTestServer();
      }   
  }
  
  public void test_MultiHeaders(TestHarness h, TestHttpServer server)
  {    
    try
      {        
        URL url = new URL("http://localhost:8080/";);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");

        // construct what should be returned by the test server as headers
        ByteArrayOutputStream headers = new ByteArrayOutputStream();
        headers.write("HTTP/1.0 200 OK\r\n".getBytes());
        headers.write("Server: TestServer\r\n".getBytes());
        headers.write("Key1: value, value2\r\n".getBytes());
        // set the header a second time with different values
        // these values must be prepended to key1
        headers.write("Key1: value3\r\n".getBytes());
        headers.write("IntHeader: 1234\r\n".getBytes());
        headers.write("IntHeaderMalformed: 1234XY\r\n".getBytes());
        headers.write("DateHeader: Thu, 02 Mar 2006 14:34:55 +0000\r\n".getBytes());
        headers.write("DateHeaderMalformed: Thu, 02 Mar 2006V 14:13:07 +0000\r\n\r\n".getBytes());

        server.setResponseHeaders(headers.toByteArray());
        
        h.checkPoint("getHeaderFields()");
        Map fields = conn.getHeaderFields();
        
        // check that map is unmodifiable
        try 
          {
            fields.clear();
            h.check(false);
          }
        catch (UnsupportedOperationException e) 
          {
            h.check(true);
          }
      
        // exactly 7 headers with status and server header        
        h.check(fields.size() == 7);
        
        // check for list and that case matters for key
        Object obj = fields.get("Key1");
        if (! (obj instanceof List))
          h.check(false);
        else
          {
            h.check(true);
            List value = (List) obj;
            h.check(value.size() == 2);
            h.check(value.get(0).equals("value3"));
            h.check(value.get(1).equals("value, value2"));
            
            // check that it is an unmodifiable list
            try 
              {
                value.remove(0);
                h.check(false);
              }
            catch (UnsupportedOperationException e) 
              {
                h.check(true);
              }
          }
        
        // wrong case for key
        obj = fields.get("key1");
        h.check(obj == null);
          
        
        // checks for getHeaderField/Key(int)        
        h.checkPoint("getHeaderField(int)");
        // check that index 0 is the statusline
        String statusline = conn.getHeaderField(0);
        h.check(statusline.equals("HTTP/1.0 200 OK"));
        // indexes out of bound must return null
        String aboutIndex = conn.getHeaderField(44);
        h.check(aboutIndex == null);
        String belowIndex = conn.getHeaderField(-1);
        h.check(belowIndex == null);
        // check that correct key/value name is returned
        String key1_Value = conn.getHeaderField(2);
        h.check(key1_Value.equals("value, value2"));
        
        h.checkPoint("getHeaderFieldKey(int)");       
        // check that index 0 is the statusline
        String statuslineKey = conn.getHeaderFieldKey(0);        
        h.check(statuslineKey == null);
        // indexes out of bound must return null
        String aboutIndexKey = conn.getHeaderFieldKey(44);        
        h.check(aboutIndexKey == null);
        String belowIndexKey = conn.getHeaderFieldKey(-1);
        h.check(belowIndexKey == null);
        // check that correct key/value name is returned
        String key1_Key = conn.getHeaderFieldKey(2);
        h.check(key1_Key.equals("Key1"));
        
        
        // checks getHeaderFieldDate
        h.checkPoint("getHeaderFieldDate()");
        // correct date header field
        long dateHeader = conn.getHeaderFieldDate("DateHeader", 5555);
        h.check(dateHeader == 1141310095000L);
        // missing date header field
        dateHeader = conn.getHeaderFieldDate("DateHeaderXX", 5555);
        h.check(dateHeader == 5555);
        // malformed date header value 
        dateHeader = conn.getHeaderFieldDate("DateHeaderMalformed", 5555);
        h.check(dateHeader == 5555);
        
        // checks getHeaderFieldInt
        h.checkPoint("getHeaderFieldInt()");
        // correct int header field
        int intHeader = conn.getHeaderFieldInt("IntHeader", 5555);
        h.check(intHeader == 1234);
        // missing int header field
        intHeader = conn.getHeaderFieldInt("IntHeaderXX", 5555);
        h.check(intHeader == 5555);
        // malformed int header value 
        intHeader = conn.getHeaderFieldInt("IntHeaderMalformed", 5555);
        h.check(intHeader == 5555);
        
        
        // checks that the convenience methods of the headers
        // not set in this test return the correct default values
        h.checkPoint("convenience methods");
        h.check(conn.getLastModified() == 0);
        h.check(conn.getDate() == 0);
        h.check(conn.getExpiration() == 0);
        h.check(conn.getContentEncoding() == null);
        h.check(conn.getContentType() == null);
        h.check(conn.getContentLength() == -1);
        
        
        // checks getHeaderField(String)
        h.checkPoint("getHeaderField(String)");
        String field = conn.getHeaderField("Server");
        String field1 = conn.getHeaderField("server");
        h.check(field.equals("TestServer"));
        h.check(field == field1);
        
        String none = conn.getHeaderField("NotExistent");
        h.check(none == null);
        
        // check for multiple times same key
        String field_key1 = conn.getHeaderField("Key1");
        h.check(field_key1.equals("value3"));

      }
    catch (IOException e)
    {
      h.debug("Unexpected IOException");
      h.debug(e);
    }
    catch (RuntimeException e)
      {
        h.debug("Unexpected IOException");
        h.debug(e);
      }
    
  }
  
  public void test_LowerUpperCaseHeaders(TestHarness h, TestHttpServer server)
  {    
    try
      {        
        URL url = new URL("http://localhost:8080/";);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");

        // construct what should be returned by the test server as headers
        ByteArrayOutputStream headers = new ByteArrayOutputStream();
        headers.write("HTTP/1.0 200 OK\r\n".getBytes());
        headers.write("Server: TestServer\r\n".getBytes());  
        headers.write("AnotherKey: value\r\n".getBytes());
        headers.write("Key: value\r\n".getBytes());
        headers.write("Key: value1\r\n".getBytes());
        headers.write("key: value2\r\n".getBytes());
        headers.write("key: value3\r\n\r\n".getBytes());
        
        server.setResponseHeaders(headers.toByteArray());
        
        h.checkPoint("LowerUpperCase header fields tests");
        
        Map fields = conn.getHeaderFields();  
                     
        // exactly 5 headers with status and server header        
        h.check(fields.size() == 5);
        
        // check for list and that case matters for key
        List value = (List) fields.get("Key");
        h.check(value.size() == 2);
        h.check(value.get(0).equals("value1"));
        
        List value2 = (List) fields.get("key");
        h.check(value2.size() == 2);
        h.check(value2.get(0).equals("value3"));
        
        List value3 = (List) fields.get("AnotherKey");
        h.check(value3.get(0).equals("value"));
        
        value3 = (List) fields.get("anotherkey");
        h.check(value3 == null);
                     
        // checks getHeaderField(String)
        String field = conn.getHeaderField("Key");
        String field1 = conn.getHeaderField("key");
        h.check(field.equals("value3"));
        h.check(field == field1);
      }
    catch (IOException e)
    {
      h.debug("Unexpected IOException");
      h.debug(e);
    }
    catch (RuntimeException e)
      {
        h.debug("Unexpected IOException");
        h.debug(e);
      }
  }
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]