From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 22078 invoked by alias); 6 Mar 2006 18:46:54 -0000 Received: (qmail 22065 invoked by uid 22791); 6 Mar 2006 18:46:52 -0000 X-Spam-Check-By: sourceware.org Received: from mail.gmx.de (HELO mail.gmx.net) (213.165.64.20) by sourceware.org (qpsmtpd/0.31) with SMTP; Mon, 06 Mar 2006 18:46:48 +0000 Received: (qmail invoked by alias); 06 Mar 2006 18:46:44 -0000 Received: from p548185E1.dip0.t-ipconnect.de (EHLO [192.168.0.203]) [84.129.133.225] by mail.gmx.net (mp001) with SMTP; 06 Mar 2006 19:46:44 +0100 X-Authenticated: #1941950 Message-ID: <440C8496.1060801@gmx.de> Date: Mon, 06 Mar 2006 18:46:00 -0000 From: Wolfgang Baer User-Agent: Debian Thunderbird 1.0.7 (X11/20051017) MIME-Version: 1.0 To: David Daney CC: mauve-patches@sources.redhat.com Subject: Re: RFC: HTTPTestServer References: <43E4B5F1.8050002@gmx.de> <43EAF479.8090400@gmx.de> <44076403.5080207@gmx.de> <44078472.4010802@avtrex.com> In-Reply-To: <44078472.4010802@avtrex.com> Content-Type: multipart/mixed; boundary="------------090406060105020709050409" 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/msg00197.txt.bz2 This is a multi-part message in MIME format. --------------090406060105020709050409 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-length: 952 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 * 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 --------------090406060105020709050409 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 } } } --------------090406060105020709050409 Content-Type: text/x-java; name="responseCodeTest.java" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="responseCodeTest.java" Content-length: 3940 //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); } } } --------------090406060105020709050409 Content-Type: text/x-java; name="responseHeadersTest.java" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="responseHeadersTest.java" Content-length: 9894 //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); } } } --------------090406060105020709050409--