From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 4156 invoked by alias); 2 Mar 2006 21:26:41 -0000 Received: (qmail 4145 invoked by uid 22791); 2 Mar 2006 21:26:39 -0000 X-Spam-Check-By: sourceware.org Received: from mail.gmx.net (HELO mail.gmx.net) (213.165.64.20) by sourceware.org (qpsmtpd/0.31) with SMTP; Thu, 02 Mar 2006 21:26:37 +0000 Received: (qmail invoked by alias); 02 Mar 2006 21:26:33 -0000 Received: from p5481B872.dip0.t-ipconnect.de (EHLO [192.168.0.203]) [84.129.184.114] by mail.gmx.net (mp027) with SMTP; 02 Mar 2006 22:26:33 +0100 X-Authenticated: #1941950 Message-ID: <44076403.5080207@gmx.de> Date: Thu, 02 Mar 2006 21:26:00 -0000 From: Wolfgang Baer User-Agent: Debian Thunderbird 1.0.7 (X11/20051017) MIME-Version: 1.0 To: mauve-patches@sources.redhat.com CC: tromey@redhat.com, David Daney Subject: RFC: HTTPTestServer (was: Re: FYI: New tests for HttpURLConnection) References: <43E4B5F1.8050002@gmx.de> <43EAF479.8090400@gmx.de> In-Reply-To: <43EAF479.8090400@gmx.de> Content-Type: multipart/mixed; boundary="------------060108040201090406020602" X-Y-GMX-Trusted: 0 X-IsSubscribed: yes Mailing-List: contact mauve-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: mauve-patches-owner@sourceware.org X-SW-Source: 2006/txt/msg00190.txt.bz2 This is a multi-part message in MIME format. --------------060108040201090406020602 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-length: 1686 Hi, Wolfgang Baer wrote: > Tom Tromey wrote: >>David Daney had the interesting idea that we could have a mini http >>server inside of Mauve, which we could then use to test all the http >>modes we care about. Specifically what he had suggested was encoding >>the desired response into the request, so we could check 404s, >>redirects, chunking, etc. > > > Thats definitly the way to go. > >>Just in case you're really motivated to hack in this area ... :-) > > > Not at this time. I just tried to get the bugs fixed I found during > working on the IPP backend for printing and which prevented me from > hacking. Well, as I wanted to test Davids Headers rewrite patch I needed to do a first hack of a TestHttpServer. Here we go: The TestHttpServer has methods to set the Headers and the Body it should return upon request. Furthermore it provides an interface which can be implemented to test the serverside received headers and body. As I am not experienced in this networking area this might be a hack. However a hack which is working so far :-) I also wrote two tests to show the usage. responseCodeTest - test for all error codes the correct handling for getInputStream and getErrorStream. responseHeadersTest - tests everything with response headers. 2006-03-02 Wolfgang Baer * 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. Comments or OK to commit ? BTW, whats the best port to use for the TestHttpServer ? Wolfgang --------------060108040201090406020602 Content-Type: text/x-java; name="TestHttpServer.java" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="TestHttpServer.java" Content-length: 6776 //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 } } } --------------060108040201090406020602 Content-Type: text/x-java; name="responseCodeTest.java" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="responseCodeTest.java" Content-length: 3809 //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 { Thread.sleep(500); } 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) { 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); } } } --------------060108040201090406020602 Content-Type: text/x-java; name="responseHeadersTest.java" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="responseHeadersTest.java" Content-length: 7648 //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 { Thread.sleep(500); } catch (InterruptedException e1) { } test_MultiHeaders(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); } } } --------------060108040201090406020602--