1b90da8218274400c8710211d467ed28f23bc28baUrs Grob/* 2b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Copyright (C) 2007 The Android Open Source Project 3b90da8218274400c8710211d467ed28f23bc28baUrs Grob * 4b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Licensed under the Apache License, Version 2.0 (the "License"); 5b90da8218274400c8710211d467ed28f23bc28baUrs Grob * you may not use this file except in compliance with the License. 6b90da8218274400c8710211d467ed28f23bc28baUrs Grob * You may obtain a copy of the License at 7b90da8218274400c8710211d467ed28f23bc28baUrs Grob * 8b90da8218274400c8710211d467ed28f23bc28baUrs Grob * http://www.apache.org/licenses/LICENSE-2.0 9b90da8218274400c8710211d467ed28f23bc28baUrs Grob * 10b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Unless required by applicable law or agreed to in writing, software 11b90da8218274400c8710211d467ed28f23bc28baUrs Grob * distributed under the License is distributed on an "AS IS" BASIS, 12b90da8218274400c8710211d467ed28f23bc28baUrs Grob * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b90da8218274400c8710211d467ed28f23bc28baUrs Grob * See the License for the specific language governing permissions and 14b90da8218274400c8710211d467ed28f23bc28baUrs Grob * limitations under the License. 15b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 16b90da8218274400c8710211d467ed28f23bc28baUrs Grob 17b90da8218274400c8710211d467ed28f23bc28baUrs Grobpackage tests.support; 18b90da8218274400c8710211d467ed28f23bc28baUrs Grob 19b90da8218274400c8710211d467ed28f23bc28baUrs Grobimport java.io.*; 20b90da8218274400c8710211d467ed28f23bc28baUrs Grobimport java.lang.Thread; 21b90da8218274400c8710211d467ed28f23bc28baUrs Grobimport java.net.*; 22b90da8218274400c8710211d467ed28f23bc28baUrs Grobimport java.text.SimpleDateFormat; 23b90da8218274400c8710211d467ed28f23bc28baUrs Grobimport java.util.*; 24adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilsonimport java.util.concurrent.ConcurrentHashMap; 25b90da8218274400c8710211d467ed28f23bc28baUrs Grobimport java.util.logging.Logger; 26b90da8218274400c8710211d467ed28f23bc28baUrs Grob 27b90da8218274400c8710211d467ed28f23bc28baUrs Grob/** 28b90da8218274400c8710211d467ed28f23bc28baUrs Grob * TestWebServer is a simulated controllable test server that 29b90da8218274400c8710211d467ed28f23bc28baUrs Grob * can respond to requests from HTTP clients. 30b90da8218274400c8710211d467ed28f23bc28baUrs Grob * 31b90da8218274400c8710211d467ed28f23bc28baUrs Grob * The server can be controlled to change how it reacts to any 32b90da8218274400c8710211d467ed28f23bc28baUrs Grob * requests, and can be told to simulate various events (such as 33b90da8218274400c8710211d467ed28f23bc28baUrs Grob * network failure) that would happen in a real environment. 34b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 35b90da8218274400c8710211d467ed28f23bc28baUrs Grobpublic class Support_TestWebServer implements Support_HttpConstants { 36b90da8218274400c8710211d467ed28f23bc28baUrs Grob 37b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* static class data/methods */ 38b90da8218274400c8710211d467ed28f23bc28baUrs Grob 39b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* The ANDROID_LOG_TAG */ 40b90da8218274400c8710211d467ed28f23bc28baUrs Grob private final static String LOGTAG = "httpsv"; 41b90da8218274400c8710211d467ed28f23bc28baUrs Grob 42adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson /** maps the recently requested URLs to the full request snapshot */ 43adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson private final Map<String, Request> pathToRequest 44adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson = new ConcurrentHashMap<String, Request>(); 45adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 46b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* timeout on client connections */ 47b90da8218274400c8710211d467ed28f23bc28baUrs Grob int timeout = 0; 48b90da8218274400c8710211d467ed28f23bc28baUrs Grob 49b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Default socket timeout value */ 50b90da8218274400c8710211d467ed28f23bc28baUrs Grob final static int DEFAULT_TIMEOUT = 5000; 51b90da8218274400c8710211d467ed28f23bc28baUrs Grob 52b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Version string (configurable) */ 53b90da8218274400c8710211d467ed28f23bc28baUrs Grob protected String HTTP_VERSION_STRING = "HTTP/1.1"; 54b90da8218274400c8710211d467ed28f23bc28baUrs Grob 55b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Indicator for whether this server is configured as a HTTP/1.1 56b90da8218274400c8710211d467ed28f23bc28baUrs Grob * or HTTP/1.0 server 57b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 58b90da8218274400c8710211d467ed28f23bc28baUrs Grob private boolean http11 = true; 59b90da8218274400c8710211d467ed28f23bc28baUrs Grob 60b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* The thread handling new requests from clients */ 61b90da8218274400c8710211d467ed28f23bc28baUrs Grob private AcceptThread acceptT; 62b90da8218274400c8710211d467ed28f23bc28baUrs Grob 63b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* timeout on client connections */ 64b90da8218274400c8710211d467ed28f23bc28baUrs Grob int mTimeout; 65b90da8218274400c8710211d467ed28f23bc28baUrs Grob 66b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Server port */ 67b90da8218274400c8710211d467ed28f23bc28baUrs Grob int mPort; 68b90da8218274400c8710211d467ed28f23bc28baUrs Grob 69b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Switch on/off logging */ 70b90da8218274400c8710211d467ed28f23bc28baUrs Grob boolean mLog = false; 71b90da8218274400c8710211d467ed28f23bc28baUrs Grob 72b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* If set, this will keep connections alive after a request has been 73b90da8218274400c8710211d467ed28f23bc28baUrs Grob * processed. 74b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 75b90da8218274400c8710211d467ed28f23bc28baUrs Grob boolean keepAlive = true; 76b90da8218274400c8710211d467ed28f23bc28baUrs Grob 77b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* If set, this will cause response data to be sent in 'chunked' format */ 78b90da8218274400c8710211d467ed28f23bc28baUrs Grob boolean chunked = false; 79e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes int maxChunkSize = 1024; 80b90da8218274400c8710211d467ed28f23bc28baUrs Grob 81b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* If set, this will indicate a new redirection host */ 82b90da8218274400c8710211d467ed28f23bc28baUrs Grob String redirectHost = null; 83b90da8218274400c8710211d467ed28f23bc28baUrs Grob 84b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* If set, this indicates the reason for redirection */ 85b90da8218274400c8710211d467ed28f23bc28baUrs Grob int redirectCode = -1; 86b90da8218274400c8710211d467ed28f23bc28baUrs Grob 87b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Set the number of connections the server will accept before shutdown */ 88b90da8218274400c8710211d467ed28f23bc28baUrs Grob int acceptLimit = 100; 89b90da8218274400c8710211d467ed28f23bc28baUrs Grob 90b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Count of number of accepted connections */ 91b90da8218274400c8710211d467ed28f23bc28baUrs Grob int acceptedConnections = 0; 92b90da8218274400c8710211d467ed28f23bc28baUrs Grob 93b90da8218274400c8710211d467ed28f23bc28baUrs Grob public Support_TestWebServer() { 94b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 95b90da8218274400c8710211d467ed28f23bc28baUrs Grob 96b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 97b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param servePath the path to the dynamic web test data 98b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param contentType the type of the dynamic web test data 99b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 100903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes public int initServer(String servePath, String contentType) throws Exception { 101b90da8218274400c8710211d467ed28f23bc28baUrs Grob Support_TestWebData.initDynamicTestWebData(servePath, contentType); 102903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes return initServer(); 103b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 104b90da8218274400c8710211d467ed28f23bc28baUrs Grob 105903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes public int initServer() throws Exception { 106903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes mTimeout = DEFAULT_TIMEOUT; 107903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes mLog = false; 108b90da8218274400c8710211d467ed28f23bc28baUrs Grob keepAlive = true; 109b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (acceptT == null) { 110b90da8218274400c8710211d467ed28f23bc28baUrs Grob acceptT = new AcceptThread(); 111903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes mPort = acceptT.init(); 112b90da8218274400c8710211d467ed28f23bc28baUrs Grob acceptT.start(); 113b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 1146461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes return mPort; 115b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 116b90da8218274400c8710211d467ed28f23bc28baUrs Grob 117b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 118b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Print to the log file (if logging enabled) 119b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param s String to send to the log 120b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 121b90da8218274400c8710211d467ed28f23bc28baUrs Grob protected void log(String s) { 122b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (mLog) { 123b90da8218274400c8710211d467ed28f23bc28baUrs Grob Logger.global.fine(s); 124b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 125b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 126b90da8218274400c8710211d467ed28f23bc28baUrs Grob 127b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 128b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Set the server to be an HTTP/1.0 or HTTP/1.1 server. 129b90da8218274400c8710211d467ed28f23bc28baUrs Grob * This should be called prior to any requests being sent 130b90da8218274400c8710211d467ed28f23bc28baUrs Grob * to the server. 131b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param set True for the server to be HTTP/1.1, false for HTTP/1.0 132b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 133b90da8218274400c8710211d467ed28f23bc28baUrs Grob public void setHttpVersion11(boolean set) { 134b90da8218274400c8710211d467ed28f23bc28baUrs Grob http11 = set; 135b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (set) { 136b90da8218274400c8710211d467ed28f23bc28baUrs Grob HTTP_VERSION_STRING = "HTTP/1.1"; 137b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 138b90da8218274400c8710211d467ed28f23bc28baUrs Grob HTTP_VERSION_STRING = "HTTP/1.0"; 139b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 140b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 141b90da8218274400c8710211d467ed28f23bc28baUrs Grob 142b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 143b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Call this to determine whether server connection should remain open 144b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param value Set true to keep connections open after a request 145b90da8218274400c8710211d467ed28f23bc28baUrs Grob * completes 146b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 147b90da8218274400c8710211d467ed28f23bc28baUrs Grob public void setKeepAlive(boolean value) { 148b90da8218274400c8710211d467ed28f23bc28baUrs Grob keepAlive = value; 149b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 150b90da8218274400c8710211d467ed28f23bc28baUrs Grob 151b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 152b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Call this to indicate whether chunked data should be used 153b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param value Set true to make server respond with chunk encoded 154b90da8218274400c8710211d467ed28f23bc28baUrs Grob * content data. 155b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 156b90da8218274400c8710211d467ed28f23bc28baUrs Grob public void setChunked(boolean value) { 157b90da8218274400c8710211d467ed28f23bc28baUrs Grob chunked = value; 158b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 159b90da8218274400c8710211d467ed28f23bc28baUrs Grob 160b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 161e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * Sets the maximum byte count of any chunk if the server is using 162e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * the "chunked" transfer encoding. 163e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes */ 164e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes public void setMaxChunkSize(int maxChunkSize) { 165e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes this.maxChunkSize = maxChunkSize; 166e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes } 167e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes 168e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes /** 169b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Call this to specify the maximum number of sockets to accept 170b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param limit The number of sockets to accept 171b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 172b90da8218274400c8710211d467ed28f23bc28baUrs Grob public void setAcceptLimit(int limit) { 173b90da8218274400c8710211d467ed28f23bc28baUrs Grob acceptLimit = limit; 174b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 175b90da8218274400c8710211d467ed28f23bc28baUrs Grob 176b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 177b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Call this to indicate redirection port requirement. 178b90da8218274400c8710211d467ed28f23bc28baUrs Grob * When this value is set, the server will respond to a request with 179b90da8218274400c8710211d467ed28f23bc28baUrs Grob * a redirect code with the Location response header set to the value 180b90da8218274400c8710211d467ed28f23bc28baUrs Grob * specified. 181b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param redirect The location to be redirected to 182adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson * @param code The code to send when redirecting 183b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 184b90da8218274400c8710211d467ed28f23bc28baUrs Grob public void setRedirect(String redirect, int code) { 185b90da8218274400c8710211d467ed28f23bc28baUrs Grob redirectHost = redirect; 186b90da8218274400c8710211d467ed28f23bc28baUrs Grob redirectCode = code; 187b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Server will redirect output to "+redirect+" code "+code); 188b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 189b90da8218274400c8710211d467ed28f23bc28baUrs Grob 190b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 191adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson * Returns a map from recently-requested paths (like "/index.html") to a 192adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson * snapshot of the request data. 193adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson */ 194adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson public Map<String, Request> pathToRequest() { 195adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson return pathToRequest; 196adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson } 197adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 1988baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson public int getNumAcceptedConnections() { 1998baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson return acceptedConnections; 2008baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson } 2018baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson 202adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson /** 203b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Cause the thread accepting connections on the server socket to close 204b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 205b90da8218274400c8710211d467ed28f23bc28baUrs Grob public void close() { 206b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Stop the Accept thread */ 207b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (acceptT != null) { 208b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Closing AcceptThread"+acceptT); 209b90da8218274400c8710211d467ed28f23bc28baUrs Grob acceptT.close(); 210b90da8218274400c8710211d467ed28f23bc28baUrs Grob acceptT = null; 211b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 212b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 213b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 214b90da8218274400c8710211d467ed28f23bc28baUrs Grob * The AcceptThread is responsible for initiating worker threads 215b90da8218274400c8710211d467ed28f23bc28baUrs Grob * to handle incoming requests from clients. 216b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 217b90da8218274400c8710211d467ed28f23bc28baUrs Grob class AcceptThread extends Thread { 218b90da8218274400c8710211d467ed28f23bc28baUrs Grob 219b90da8218274400c8710211d467ed28f23bc28baUrs Grob ServerSocket ss = null; 220b90da8218274400c8710211d467ed28f23bc28baUrs Grob boolean running = false; 221b90da8218274400c8710211d467ed28f23bc28baUrs Grob 2226461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes /** 2236461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes * @param port the port to use, or 0 to let the OS choose. 2246461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes * Hard-coding ports is evil, so always pass 0! 2256461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes */ 226903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes public int init() throws IOException { 227903b3f5b6a6e524cb4d88b83ea32e7c355392271Elliott Hughes ss = new ServerSocket(0); 2286461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes ss.setSoTimeout(5000); 2296461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes ss.setReuseAddress(true); 2306461d8fe30e94c08d10ccd40c9ceeb83b9c43b7bElliott Hughes return ss.getLocalPort(); 231b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 232b90da8218274400c8710211d467ed28f23bc28baUrs Grob 233b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 234b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Main thread responding to new connections 235b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 236b90da8218274400c8710211d467ed28f23bc28baUrs Grob public synchronized void run() { 237b90da8218274400c8710211d467ed28f23bc28baUrs Grob running = true; 2388baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson while (running) { 2398baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson try { 240b90da8218274400c8710211d467ed28f23bc28baUrs Grob Socket s = ss.accept(); 241b90da8218274400c8710211d467ed28f23bc28baUrs Grob acceptedConnections++; 242b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (acceptedConnections >= acceptLimit) { 243b90da8218274400c8710211d467ed28f23bc28baUrs Grob running = false; 244b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 245a152bc98bbb8feadb1b8b755f1f40fb66548ea12Elliott Hughes new Thread(new Worker(s), "additional worker").start(); 2468baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson } catch (SocketException e) { 2478baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson log(e.getMessage()); 2488baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson } catch (IOException e) { 2498baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson log(e.getMessage()); 250b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 251b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 252b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("AcceptThread terminated" + this); 253b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 254b90da8218274400c8710211d467ed28f23bc28baUrs Grob 255b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Close this socket 256b90da8218274400c8710211d467ed28f23bc28baUrs Grob public void close() { 257b90da8218274400c8710211d467ed28f23bc28baUrs Grob try { 258b90da8218274400c8710211d467ed28f23bc28baUrs Grob running = false; 259b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Stop server socket from processing further. Currently 260b90da8218274400c8710211d467ed28f23bc28baUrs Grob this does not cause the SocketException from ss.accept 261b90da8218274400c8710211d467ed28f23bc28baUrs Grob therefore the acceptLimit functionality has been added 262b90da8218274400c8710211d467ed28f23bc28baUrs Grob to circumvent this limitation */ 263b90da8218274400c8710211d467ed28f23bc28baUrs Grob ss.close(); 264b90da8218274400c8710211d467ed28f23bc28baUrs Grob } catch (IOException e) { 265b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* We are shutting down the server, so we expect 266b90da8218274400c8710211d467ed28f23bc28baUrs Grob * things to die. Don't propagate. 267b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 268b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("IOException caught by server socket close"); 269b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 270b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 271b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 272b90da8218274400c8710211d467ed28f23bc28baUrs Grob 273b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Size of buffer for reading from the connection 274b90da8218274400c8710211d467ed28f23bc28baUrs Grob final static int BUF_SIZE = 2048; 275b90da8218274400c8710211d467ed28f23bc28baUrs Grob 276b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* End of line byte sequence */ 277b90da8218274400c8710211d467ed28f23bc28baUrs Grob static final byte[] EOL = {(byte)'\r', (byte)'\n' }; 278b90da8218274400c8710211d467ed28f23bc28baUrs Grob 279b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 280adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson * An immutable snapshot of an HTTP request. 281adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson */ 282adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson public static class Request { 283adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson private final String path; 284adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson private final Map<String, String> headers; 285adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson // TODO: include posted content? 286adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 287adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson public Request(String path, Map<String, String> headers) { 288adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson this.path = path; 289adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson this.headers = new LinkedHashMap<String, String>(headers); 290adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson } 291adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 292adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson public String getPath() { 293adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson return path; 294adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson } 295adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 296adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson public Map<String, String> getHeaders() { 297adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson return headers; 298adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson } 299adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson } 300adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 301adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson /** 302b90da8218274400c8710211d467ed28f23bc28baUrs Grob * The worker thread handles all interactions with a current open 303b90da8218274400c8710211d467ed28f23bc28baUrs Grob * connection. If pipelining is turned on, this will allow this 304b90da8218274400c8710211d467ed28f23bc28baUrs Grob * thread to continuously operate on numerous requests before the 305b90da8218274400c8710211d467ed28f23bc28baUrs Grob * connection is closed. 306b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 307b90da8218274400c8710211d467ed28f23bc28baUrs Grob class Worker implements Support_HttpConstants, Runnable { 308b90da8218274400c8710211d467ed28f23bc28baUrs Grob 309b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* buffer to use to hold request data */ 310b90da8218274400c8710211d467ed28f23bc28baUrs Grob byte[] buf; 311b90da8218274400c8710211d467ed28f23bc28baUrs Grob 312b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Socket to client we're handling */ 313b90da8218274400c8710211d467ed28f23bc28baUrs Grob private Socket s; 314b90da8218274400c8710211d467ed28f23bc28baUrs Grob 315b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Reference to current request method ID */ 316b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int requestMethod; 317b90da8218274400c8710211d467ed28f23bc28baUrs Grob 318b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Reference to current requests test file/data */ 319b90da8218274400c8710211d467ed28f23bc28baUrs Grob private String testID; 320b90da8218274400c8710211d467ed28f23bc28baUrs Grob 321adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson /* The requested path, such as "/test1" */ 322adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson private String path; 323adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 324b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Reference to test number from testID */ 325b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int testNum; 326b90da8218274400c8710211d467ed28f23bc28baUrs Grob 327b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Reference to whether new request has been initiated yet */ 328b90da8218274400c8710211d467ed28f23bc28baUrs Grob private boolean readStarted; 329b90da8218274400c8710211d467ed28f23bc28baUrs Grob 330b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Indicates whether current request has any data content */ 331b90da8218274400c8710211d467ed28f23bc28baUrs Grob private boolean hasContent = false; 332b90da8218274400c8710211d467ed28f23bc28baUrs Grob 333b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Request headers are stored here */ 334adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson private Map<String, String> headers = new LinkedHashMap<String, String>(); 335b90da8218274400c8710211d467ed28f23bc28baUrs Grob 336b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* Create a new worker thread */ 337a152bc98bbb8feadb1b8b755f1f40fb66548ea12Elliott Hughes Worker(Socket s) { 338a152bc98bbb8feadb1b8b755f1f40fb66548ea12Elliott Hughes this.buf = new byte[BUF_SIZE]; 339b90da8218274400c8710211d467ed28f23bc28baUrs Grob this.s = s; 340b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 341b90da8218274400c8710211d467ed28f23bc28baUrs Grob 342b90da8218274400c8710211d467ed28f23bc28baUrs Grob public synchronized void run() { 343a152bc98bbb8feadb1b8b755f1f40fb66548ea12Elliott Hughes try { 344a152bc98bbb8feadb1b8b755f1f40fb66548ea12Elliott Hughes handleClient(); 345a152bc98bbb8feadb1b8b755f1f40fb66548ea12Elliott Hughes } catch (Exception e) { 346a152bc98bbb8feadb1b8b755f1f40fb66548ea12Elliott Hughes log("Exception during handleClient in the TestWebServer: " + e.getMessage()); 347b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 348b90da8218274400c8710211d467ed28f23bc28baUrs Grob log(this+" terminated"); 349b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 350b90da8218274400c8710211d467ed28f23bc28baUrs Grob 351b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 352b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Zero out the buffer from last time 353b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 354b90da8218274400c8710211d467ed28f23bc28baUrs Grob private void clearBuffer() { 355b90da8218274400c8710211d467ed28f23bc28baUrs Grob for (int i = 0; i < BUF_SIZE; i++) { 356b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[i] = 0; 357b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 358b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 359b90da8218274400c8710211d467ed28f23bc28baUrs Grob 360b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 361b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Utility method to read a line of data from the input stream 362b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param is Inputstream to read 363b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @return number of bytes read 364b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 365b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int readOneLine(InputStream is) { 366b90da8218274400c8710211d467ed28f23bc28baUrs Grob 367b90da8218274400c8710211d467ed28f23bc28baUrs Grob int read = 0; 368b90da8218274400c8710211d467ed28f23bc28baUrs Grob 369b90da8218274400c8710211d467ed28f23bc28baUrs Grob clearBuffer(); 370b90da8218274400c8710211d467ed28f23bc28baUrs Grob try { 371b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Reading one line: started ="+readStarted+" avail="+is.available()); 372b90da8218274400c8710211d467ed28f23bc28baUrs Grob StringBuilder log = new StringBuilder(); 373b90da8218274400c8710211d467ed28f23bc28baUrs Grob while ((!readStarted) || (is.available() > 0)) { 374b90da8218274400c8710211d467ed28f23bc28baUrs Grob int data = is.read(); 375b90da8218274400c8710211d467ed28f23bc28baUrs Grob // We shouldn't get EOF but we need tdo check 376b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (data == -1) { 377b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("EOF returned"); 378b90da8218274400c8710211d467ed28f23bc28baUrs Grob return -1; 379b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 380b90da8218274400c8710211d467ed28f23bc28baUrs Grob 381b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[read] = (byte)data; 382b90da8218274400c8710211d467ed28f23bc28baUrs Grob 383b90da8218274400c8710211d467ed28f23bc28baUrs Grob log.append((char)data); 384b90da8218274400c8710211d467ed28f23bc28baUrs Grob 385b90da8218274400c8710211d467ed28f23bc28baUrs Grob readStarted = true; 386b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (buf[read++]==(byte)'\n') { 387b90da8218274400c8710211d467ed28f23bc28baUrs Grob log(log.toString()); 388b90da8218274400c8710211d467ed28f23bc28baUrs Grob return read; 389b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 390b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 391b90da8218274400c8710211d467ed28f23bc28baUrs Grob } catch (IOException e) { 392b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("IOException from readOneLine"); 393b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 394b90da8218274400c8710211d467ed28f23bc28baUrs Grob return read; 395b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 396b90da8218274400c8710211d467ed28f23bc28baUrs Grob 397b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 398b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Read a chunk of data 399b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param is Stream from which to read data 400b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param length Amount of data to read 401b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @return number of bytes read 402b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 403b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int readData(InputStream is, int length) { 404b90da8218274400c8710211d467ed28f23bc28baUrs Grob int read = 0; 405b90da8218274400c8710211d467ed28f23bc28baUrs Grob int count; 406b90da8218274400c8710211d467ed28f23bc28baUrs Grob // At the moment we're only expecting small data amounts 407b90da8218274400c8710211d467ed28f23bc28baUrs Grob byte[] buf = new byte[length]; 408b90da8218274400c8710211d467ed28f23bc28baUrs Grob 409b90da8218274400c8710211d467ed28f23bc28baUrs Grob try { 410b90da8218274400c8710211d467ed28f23bc28baUrs Grob while (is.available() > 0) { 411b90da8218274400c8710211d467ed28f23bc28baUrs Grob count = is.read(buf, read, length-read); 412b90da8218274400c8710211d467ed28f23bc28baUrs Grob read += count; 413b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 414b90da8218274400c8710211d467ed28f23bc28baUrs Grob } catch (IOException e) { 415b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("IOException from readData"); 416b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 417b90da8218274400c8710211d467ed28f23bc28baUrs Grob return read; 418b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 419b90da8218274400c8710211d467ed28f23bc28baUrs Grob 420b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 421b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Read the status line from the input stream extracting method 422b90da8218274400c8710211d467ed28f23bc28baUrs Grob * information. 423b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param is Inputstream to read 424b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @return number of bytes read 425b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 426b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int parseStatusLine(InputStream is) { 427b90da8218274400c8710211d467ed28f23bc28baUrs Grob int index; 428b90da8218274400c8710211d467ed28f23bc28baUrs Grob int nread = 0; 429b90da8218274400c8710211d467ed28f23bc28baUrs Grob 430b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Parse status line"); 431b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Check for status line first 432b90da8218274400c8710211d467ed28f23bc28baUrs Grob nread = readOneLine(is); 433b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Bomb out if stream closes prematurely 434b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (nread == -1) { 435b90da8218274400c8710211d467ed28f23bc28baUrs Grob requestMethod = UNKNOWN_METHOD; 436b90da8218274400c8710211d467ed28f23bc28baUrs Grob return -1; 437b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 438b90da8218274400c8710211d467ed28f23bc28baUrs Grob 439b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (buf[0] == (byte)'G' && 440b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[1] == (byte)'E' && 441b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[2] == (byte)'T' && 442b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[3] == (byte)' ') { 443b90da8218274400c8710211d467ed28f23bc28baUrs Grob requestMethod = GET_METHOD; 444b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("GET request"); 445b90da8218274400c8710211d467ed28f23bc28baUrs Grob index = 4; 446b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else if (buf[0] == (byte)'H' && 447b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[1] == (byte)'E' && 448b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[2] == (byte)'A' && 449b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[3] == (byte)'D' && 450b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[4] == (byte)' ') { 451b90da8218274400c8710211d467ed28f23bc28baUrs Grob requestMethod = HEAD_METHOD; 452b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("HEAD request"); 453b90da8218274400c8710211d467ed28f23bc28baUrs Grob index = 5; 454b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else if (buf[0] == (byte)'P' && 455b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[1] == (byte)'O' && 456b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[2] == (byte)'S' && 457b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[3] == (byte)'T' && 458b90da8218274400c8710211d467ed28f23bc28baUrs Grob buf[4] == (byte)' ') { 459b90da8218274400c8710211d467ed28f23bc28baUrs Grob requestMethod = POST_METHOD; 460b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("POST request"); 461b90da8218274400c8710211d467ed28f23bc28baUrs Grob index = 5; 462b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 463b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Unhandled request 464b90da8218274400c8710211d467ed28f23bc28baUrs Grob requestMethod = UNKNOWN_METHOD; 465b90da8218274400c8710211d467ed28f23bc28baUrs Grob return -1; 466b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 467b90da8218274400c8710211d467ed28f23bc28baUrs Grob 468b90da8218274400c8710211d467ed28f23bc28baUrs Grob // A valid method we understand 469b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (requestMethod > UNKNOWN_METHOD) { 470b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Read file name 471b90da8218274400c8710211d467ed28f23bc28baUrs Grob int i = index; 472b90da8218274400c8710211d467ed28f23bc28baUrs Grob while (buf[i] != (byte)' ') { 473b90da8218274400c8710211d467ed28f23bc28baUrs Grob // There should be HTTP/1.x at the end 474b90da8218274400c8710211d467ed28f23bc28baUrs Grob if ((buf[i] == (byte)'\n') || (buf[i] == (byte)'\r')) { 475b90da8218274400c8710211d467ed28f23bc28baUrs Grob requestMethod = UNKNOWN_METHOD; 476b90da8218274400c8710211d467ed28f23bc28baUrs Grob return -1; 477b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 478b90da8218274400c8710211d467ed28f23bc28baUrs Grob i++; 479b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 480b90da8218274400c8710211d467ed28f23bc28baUrs Grob 481adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson path = new String(buf, 0, index, i-index); 482adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson testID = path.substring(1); 483b90da8218274400c8710211d467ed28f23bc28baUrs Grob 484b90da8218274400c8710211d467ed28f23bc28baUrs Grob return nread; 485b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 486b90da8218274400c8710211d467ed28f23bc28baUrs Grob return -1; 487b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 488b90da8218274400c8710211d467ed28f23bc28baUrs Grob 489b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 490b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Read a header from the input stream 491b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param is Inputstream to read 492b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @return number of bytes read 493b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 494b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int parseHeader(InputStream is) { 495b90da8218274400c8710211d467ed28f23bc28baUrs Grob int index = 0; 496b90da8218274400c8710211d467ed28f23bc28baUrs Grob int nread = 0; 497b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Parse a header"); 498b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Check for status line first 499b90da8218274400c8710211d467ed28f23bc28baUrs Grob nread = readOneLine(is); 500b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Bomb out if stream closes prematurely 501b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (nread == -1) { 502b90da8218274400c8710211d467ed28f23bc28baUrs Grob requestMethod = UNKNOWN_METHOD; 503b90da8218274400c8710211d467ed28f23bc28baUrs Grob return -1; 504b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 505b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Read header entry 'Header: data' 506b90da8218274400c8710211d467ed28f23bc28baUrs Grob int i = index; 507b90da8218274400c8710211d467ed28f23bc28baUrs Grob while (buf[i] != (byte)':') { 508b90da8218274400c8710211d467ed28f23bc28baUrs Grob // There should be an entry after the header 509b90da8218274400c8710211d467ed28f23bc28baUrs Grob 510b90da8218274400c8710211d467ed28f23bc28baUrs Grob if ((buf[i] == (byte)'\n') || (buf[i] == (byte)'\r')) { 511b90da8218274400c8710211d467ed28f23bc28baUrs Grob return UNKNOWN_METHOD; 512b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 513b90da8218274400c8710211d467ed28f23bc28baUrs Grob i++; 514b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 515b90da8218274400c8710211d467ed28f23bc28baUrs Grob 516b90da8218274400c8710211d467ed28f23bc28baUrs Grob String headerName = new String(buf, 0, i); 517b90da8218274400c8710211d467ed28f23bc28baUrs Grob i++; // Over ':' 518b90da8218274400c8710211d467ed28f23bc28baUrs Grob while (buf[i] == ' ') { 519b90da8218274400c8710211d467ed28f23bc28baUrs Grob i++; 520b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 521398cf32a303a3b2a878a48cbae54be5ac23f7ecdJesse Wilson String headerValue = new String(buf, i, nread - i - 2); // drop \r\n 522b90da8218274400c8710211d467ed28f23bc28baUrs Grob 523b90da8218274400c8710211d467ed28f23bc28baUrs Grob headers.put(headerName, headerValue); 524b90da8218274400c8710211d467ed28f23bc28baUrs Grob return nread; 525b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 526b90da8218274400c8710211d467ed28f23bc28baUrs Grob 527b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 528b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Read all headers from the input stream 529b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param is Inputstream to read 530b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @return number of bytes read 531b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 532b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int readHeaders(InputStream is) { 533b90da8218274400c8710211d467ed28f23bc28baUrs Grob int nread = 0; 534b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Read headers"); 535b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Headers should be terminated by empty CRLF line 536b90da8218274400c8710211d467ed28f23bc28baUrs Grob while (true) { 537b90da8218274400c8710211d467ed28f23bc28baUrs Grob int headerLen = 0; 538b90da8218274400c8710211d467ed28f23bc28baUrs Grob headerLen = parseHeader(is); 539b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (headerLen == -1) 540b90da8218274400c8710211d467ed28f23bc28baUrs Grob return -1; 541b90da8218274400c8710211d467ed28f23bc28baUrs Grob nread += headerLen; 542b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (headerLen <= 2) { 543b90da8218274400c8710211d467ed28f23bc28baUrs Grob return nread; 544b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 545b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 546b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 547b90da8218274400c8710211d467ed28f23bc28baUrs Grob 548b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 549b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Read content data from the input stream 550b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param is Inputstream to read 551b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @return number of bytes read 552b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 553b90da8218274400c8710211d467ed28f23bc28baUrs Grob private int readContent(InputStream is) { 554b90da8218274400c8710211d467ed28f23bc28baUrs Grob int nread = 0; 555b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Read content"); 556b90da8218274400c8710211d467ed28f23bc28baUrs Grob String lengthString = headers.get(requestHeaders[REQ_CONTENT_LENGTH]); 557b90da8218274400c8710211d467ed28f23bc28baUrs Grob int length = new Integer(lengthString).intValue(); 558b90da8218274400c8710211d467ed28f23bc28baUrs Grob 559b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Read content 560b90da8218274400c8710211d467ed28f23bc28baUrs Grob length = readData(is, length); 561b90da8218274400c8710211d467ed28f23bc28baUrs Grob return length; 562b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 563b90da8218274400c8710211d467ed28f23bc28baUrs Grob 564b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 565b90da8218274400c8710211d467ed28f23bc28baUrs Grob * The main loop, reading requests. 566b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 567b90da8218274400c8710211d467ed28f23bc28baUrs Grob void handleClient() throws IOException { 568b90da8218274400c8710211d467ed28f23bc28baUrs Grob InputStream is = new BufferedInputStream(s.getInputStream()); 569b90da8218274400c8710211d467ed28f23bc28baUrs Grob PrintStream ps = new PrintStream(s.getOutputStream()); 570b90da8218274400c8710211d467ed28f23bc28baUrs Grob int nread = 0; 571b90da8218274400c8710211d467ed28f23bc28baUrs Grob 572b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* we will only block in read for this many milliseconds 573b90da8218274400c8710211d467ed28f23bc28baUrs Grob * before we fail with java.io.InterruptedIOException, 574b90da8218274400c8710211d467ed28f23bc28baUrs Grob * at which point we will abandon the connection. 575b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 576b90da8218274400c8710211d467ed28f23bc28baUrs Grob s.setSoTimeout(mTimeout); 577b90da8218274400c8710211d467ed28f23bc28baUrs Grob s.setTcpNoDelay(true); 578b90da8218274400c8710211d467ed28f23bc28baUrs Grob 579b90da8218274400c8710211d467ed28f23bc28baUrs Grob do { 580b90da8218274400c8710211d467ed28f23bc28baUrs Grob nread = parseStatusLine(is); 581b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (requestMethod != UNKNOWN_METHOD) { 582b90da8218274400c8710211d467ed28f23bc28baUrs Grob 583b90da8218274400c8710211d467ed28f23bc28baUrs Grob // If status line found, read any headers 584b90da8218274400c8710211d467ed28f23bc28baUrs Grob nread = readHeaders(is); 585b90da8218274400c8710211d467ed28f23bc28baUrs Grob 586adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson pathToRequest().put(path, new Request(path, headers)); 587adad03692b86958bbc1da598c260b5e322f4d8ceJesse Wilson 588b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Then read content (if any) 589b90da8218274400c8710211d467ed28f23bc28baUrs Grob // TODO handle chunked encoding from the client 590b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (headers.get(requestHeaders[REQ_CONTENT_LENGTH]) != null) { 591b90da8218274400c8710211d467ed28f23bc28baUrs Grob nread = readContent(is); 592b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 593b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 594b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (nread > 0) { 595b90da8218274400c8710211d467ed28f23bc28baUrs Grob /* we don't support this method */ 596b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.print(HTTP_VERSION_STRING + " " + HTTP_BAD_METHOD + 597b90da8218274400c8710211d467ed28f23bc28baUrs Grob " unsupported method type: "); 598b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.write(buf, 0, 5); 599b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.write(EOL); 600b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.flush(); 601b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 602b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 603b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (!keepAlive || nread <= 0) { 604b90da8218274400c8710211d467ed28f23bc28baUrs Grob headers.clear(); 605b90da8218274400c8710211d467ed28f23bc28baUrs Grob readStarted = false; 606b90da8218274400c8710211d467ed28f23bc28baUrs Grob 607b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("SOCKET CLOSED"); 608b90da8218274400c8710211d467ed28f23bc28baUrs Grob s.close(); 609b90da8218274400c8710211d467ed28f23bc28baUrs Grob return; 610b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 611b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 612b90da8218274400c8710211d467ed28f23bc28baUrs Grob 613b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Reset test number prior to outputing data 614b90da8218274400c8710211d467ed28f23bc28baUrs Grob testNum = -1; 615b90da8218274400c8710211d467ed28f23bc28baUrs Grob 616b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Write out the data 617b90da8218274400c8710211d467ed28f23bc28baUrs Grob printStatus(ps); 618b90da8218274400c8710211d467ed28f23bc28baUrs Grob printHeaders(ps); 619b90da8218274400c8710211d467ed28f23bc28baUrs Grob 620b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Write line between headers and body 621b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 622b90da8218274400c8710211d467ed28f23bc28baUrs Grob 623b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Write the body 624b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (redirectCode == -1) { 625b90da8218274400c8710211d467ed28f23bc28baUrs Grob switch (requestMethod) { 626b90da8218274400c8710211d467ed28f23bc28baUrs Grob case GET_METHOD: 627b90da8218274400c8710211d467ed28f23bc28baUrs Grob if ((testNum < -1) || (testNum > Support_TestWebData.tests.length - 1)) { 628b90da8218274400c8710211d467ed28f23bc28baUrs Grob send404(ps); 629b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 630b90da8218274400c8710211d467ed28f23bc28baUrs Grob sendFile(ps); 631b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 632b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 633b90da8218274400c8710211d467ed28f23bc28baUrs Grob case HEAD_METHOD: 634b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Nothing to do 635b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 636b90da8218274400c8710211d467ed28f23bc28baUrs Grob case POST_METHOD: 637b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Post method write body data 638b90da8218274400c8710211d467ed28f23bc28baUrs Grob if ((testNum > 0) || (testNum < Support_TestWebData.tests.length - 1)) { 639b90da8218274400c8710211d467ed28f23bc28baUrs Grob sendFile(ps); 640b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 641b90da8218274400c8710211d467ed28f23bc28baUrs Grob 642b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 643b90da8218274400c8710211d467ed28f23bc28baUrs Grob default: 644b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 645b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 646b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { // Redirecting 647b90da8218274400c8710211d467ed28f23bc28baUrs Grob switch (redirectCode) { 648b90da8218274400c8710211d467ed28f23bc28baUrs Grob case 301: 649b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Seems 301 needs a body by neon (although spec 650b90da8218274400c8710211d467ed28f23bc28baUrs Grob // says SHOULD). 651b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, Support_TestWebData.testServerResponse[Support_TestWebData.REDIRECT_301]); 652b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 653b90da8218274400c8710211d467ed28f23bc28baUrs Grob case 302: 654b90da8218274400c8710211d467ed28f23bc28baUrs Grob // 655b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, Support_TestWebData.testServerResponse[Support_TestWebData.REDIRECT_302]); 656b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 657b90da8218274400c8710211d467ed28f23bc28baUrs Grob case 303: 658b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, Support_TestWebData.testServerResponse[Support_TestWebData.REDIRECT_303]); 659b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 660b90da8218274400c8710211d467ed28f23bc28baUrs Grob case 307: 661b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, Support_TestWebData.testServerResponse[Support_TestWebData.REDIRECT_307]); 662b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 663b90da8218274400c8710211d467ed28f23bc28baUrs Grob default: 664b90da8218274400c8710211d467ed28f23bc28baUrs Grob break; 665b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 666b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 667b90da8218274400c8710211d467ed28f23bc28baUrs Grob 668b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.flush(); 669b90da8218274400c8710211d467ed28f23bc28baUrs Grob 670b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Reset for next request 671b90da8218274400c8710211d467ed28f23bc28baUrs Grob readStarted = false; 672b90da8218274400c8710211d467ed28f23bc28baUrs Grob headers.clear(); 673b90da8218274400c8710211d467ed28f23bc28baUrs Grob 674b90da8218274400c8710211d467ed28f23bc28baUrs Grob } while (keepAlive); 675b90da8218274400c8710211d467ed28f23bc28baUrs Grob 676b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("SOCKET CLOSED"); 677b90da8218274400c8710211d467ed28f23bc28baUrs Grob s.close(); 678b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 679b90da8218274400c8710211d467ed28f23bc28baUrs Grob 680b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Print string to log and output stream 681b90da8218274400c8710211d467ed28f23bc28baUrs Grob void psPrint(PrintStream ps, String s) throws IOException { 682b90da8218274400c8710211d467ed28f23bc28baUrs Grob log(s); 683b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.print(s); 684b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 685b90da8218274400c8710211d467ed28f23bc28baUrs Grob 686b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Print bytes to log and output stream 687e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes void psWrite(PrintStream ps, byte[] bytes, int offset, int count) throws IOException { 688b90da8218274400c8710211d467ed28f23bc28baUrs Grob log(new String(bytes)); 689e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes ps.write(bytes, offset, count); 690b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 691b90da8218274400c8710211d467ed28f23bc28baUrs Grob 692b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Print CRLF to log and output stream 693b90da8218274400c8710211d467ed28f23bc28baUrs Grob void psWriteEOL(PrintStream ps) throws IOException { 694b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("CRLF"); 695b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.write(EOL); 696b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 697b90da8218274400c8710211d467ed28f23bc28baUrs Grob 698b90da8218274400c8710211d467ed28f23bc28baUrs Grob 699b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Print status to log and output stream 700b90da8218274400c8710211d467ed28f23bc28baUrs Grob void printStatus(PrintStream ps) throws IOException { 701b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Handle redirects first. 702b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (redirectCode != -1) { 703b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("REDIRECTING TO "+redirectHost+" status "+redirectCode); 704b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, HTTP_VERSION_STRING + " " + redirectCode +" Moved permanently"); 705b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 706b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Location: " + redirectHost); 707b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 708b90da8218274400c8710211d467ed28f23bc28baUrs Grob return; 709b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 710b90da8218274400c8710211d467ed28f23bc28baUrs Grob 711b90da8218274400c8710211d467ed28f23bc28baUrs Grob 712b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (testID.startsWith("test")) { 713b90da8218274400c8710211d467ed28f23bc28baUrs Grob testNum = Integer.valueOf(testID.substring(4))-1; 714b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 715b90da8218274400c8710211d467ed28f23bc28baUrs Grob 716b90da8218274400c8710211d467ed28f23bc28baUrs Grob if ((testNum < -1) || (testNum > Support_TestWebData.tests.length - 1)) { 717b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, HTTP_VERSION_STRING + " " + HTTP_NOT_FOUND + " not found"); 718b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 719b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 720b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, HTTP_VERSION_STRING + " " + HTTP_OK+" OK"); 721b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 722b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 723b90da8218274400c8710211d467ed28f23bc28baUrs Grob 724b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Status sent"); 725b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 726b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 727b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Create the server response and output to the stream 728b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param ps The PrintStream to output response headers and data to 729b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 730b90da8218274400c8710211d467ed28f23bc28baUrs Grob void printHeaders(PrintStream ps) throws IOException { 731b90da8218274400c8710211d467ed28f23bc28baUrs Grob if ((testNum < -1) || (testNum > Support_TestWebData.tests.length - 1)) { 732b90da8218274400c8710211d467ed28f23bc28baUrs Grob // 404 status already sent 733b90da8218274400c8710211d467ed28f23bc28baUrs Grob return; 734b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 735b90da8218274400c8710211d467ed28f23bc28baUrs Grob SimpleDateFormat df = new SimpleDateFormat("EE, dd MMM yyyy HH:mm:ss"); 736b90da8218274400c8710211d467ed28f23bc28baUrs Grob 737b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps,"Server: TestWebServer"+mPort); 738b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 739b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Date: " + df.format(new Date())); 740b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 741b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Connection: " + ((keepAlive) ? "Keep-Alive" : "Close")); 742b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 743b90da8218274400c8710211d467ed28f23bc28baUrs Grob 744b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Yuk, if we're not redirecting, we add the file details 745b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (redirectCode == -1) { 746b90da8218274400c8710211d467ed28f23bc28baUrs Grob 747b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (testNum == -1) { 748b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (!Support_TestWebData.test0DataAvailable) { 749b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("testdata was not initilaized"); 750b90da8218274400c8710211d467ed28f23bc28baUrs Grob return; 751b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 752b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (chunked) { 753b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Transfer-Encoding: chunked"); 754b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 755b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Content-length: " 756b90da8218274400c8710211d467ed28f23bc28baUrs Grob + Support_TestWebData.test0Data.length); 757b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 758b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 759b90da8218274400c8710211d467ed28f23bc28baUrs Grob 760b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Last Modified: " + (new Date( 761b90da8218274400c8710211d467ed28f23bc28baUrs Grob Support_TestWebData.test0Params.testLastModified))); 762b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 763b90da8218274400c8710211d467ed28f23bc28baUrs Grob 764b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Content-type: " 765b90da8218274400c8710211d467ed28f23bc28baUrs Grob + Support_TestWebData.test0Params.testType); 766b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 767b90da8218274400c8710211d467ed28f23bc28baUrs Grob 768b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (Support_TestWebData.testParams[testNum].testExp > 0) { 769b90da8218274400c8710211d467ed28f23bc28baUrs Grob long exp; 770b90da8218274400c8710211d467ed28f23bc28baUrs Grob exp = Support_TestWebData.testParams[testNum].testExp; 771b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "expires: " 772b90da8218274400c8710211d467ed28f23bc28baUrs Grob + df.format(exp) + " GMT"); 773b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 774b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 775b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else if (!Support_TestWebData.testParams[testNum].testDir) { 776b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (chunked) { 777b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Transfer-Encoding: chunked"); 778b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 779b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Content-length: "+Support_TestWebData.testParams[testNum].testLength); 780b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 781b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 782b90da8218274400c8710211d467ed28f23bc28baUrs Grob 783b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps,"Last Modified: " + (new 784b90da8218274400c8710211d467ed28f23bc28baUrs Grob Date(Support_TestWebData.testParams[testNum].testLastModified))); 785b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 786b90da8218274400c8710211d467ed28f23bc28baUrs Grob 787b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Content-type: " + Support_TestWebData.testParams[testNum].testType); 788b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 789b90da8218274400c8710211d467ed28f23bc28baUrs Grob 790b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (Support_TestWebData.testParams[testNum].testExp > 0) { 791b90da8218274400c8710211d467ed28f23bc28baUrs Grob long exp; 792b90da8218274400c8710211d467ed28f23bc28baUrs Grob exp = Support_TestWebData.testParams[testNum].testExp; 793b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "expires: " 794b90da8218274400c8710211d467ed28f23bc28baUrs Grob + df.format(exp) + " GMT"); 795b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 796b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 797b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 798b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Content-type: text/html"); 799b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 800b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 801b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 802b90da8218274400c8710211d467ed28f23bc28baUrs Grob // Content-length of 301, 302, 303, 307 are the same. 803b90da8218274400c8710211d467ed28f23bc28baUrs Grob psPrint(ps, "Content-length: "+(Support_TestWebData.testServerResponse[Support_TestWebData.REDIRECT_301]).length()); 804b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 805b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 806b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 807b90da8218274400c8710211d467ed28f23bc28baUrs Grob log("Headers sent"); 808b90da8218274400c8710211d467ed28f23bc28baUrs Grob 809b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 810b90da8218274400c8710211d467ed28f23bc28baUrs Grob 811b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 812b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Sends the 404 not found message 813b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param ps The PrintStream to write to 814b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 815b90da8218274400c8710211d467ed28f23bc28baUrs Grob void send404(PrintStream ps) throws IOException { 816b90da8218274400c8710211d467ed28f23bc28baUrs Grob ps.println("Not Found\n\n"+ 817b90da8218274400c8710211d467ed28f23bc28baUrs Grob "The requested resource was not found.\n"); 818b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 819b90da8218274400c8710211d467ed28f23bc28baUrs Grob 820b90da8218274400c8710211d467ed28f23bc28baUrs Grob /** 821b90da8218274400c8710211d467ed28f23bc28baUrs Grob * Sends the data associated with the headers 822b90da8218274400c8710211d467ed28f23bc28baUrs Grob * @param ps The PrintStream to write to 823b90da8218274400c8710211d467ed28f23bc28baUrs Grob */ 824b90da8218274400c8710211d467ed28f23bc28baUrs Grob void sendFile(PrintStream ps) throws IOException { 825b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (testNum == -1) { 826b90da8218274400c8710211d467ed28f23bc28baUrs Grob if (!Support_TestWebData.test0DataAvailable) { 827e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes log("test data was not initialized"); 828b90da8218274400c8710211d467ed28f23bc28baUrs Grob return; 829b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 830e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes sendFile(ps, Support_TestWebData.test0Data); 831b90da8218274400c8710211d467ed28f23bc28baUrs Grob } else { 832e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes sendFile(ps, Support_TestWebData.tests[testNum]); 833e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes } 834e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes } 835e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes 836e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes void sendFile(PrintStream ps, byte[] bytes) throws IOException { 837e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes if (chunked) { 838e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes int offset = 0; 839e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes while (offset < bytes.length) { 840e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes int chunkSize = Math.min(bytes.length - offset, maxChunkSize); 841e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes psPrint(ps, Integer.toHexString(chunkSize)); 842b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 843e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes psWrite(ps, bytes, offset, chunkSize); 844b90da8218274400c8710211d467ed28f23bc28baUrs Grob psWriteEOL(ps); 845e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes offset += chunkSize; 846b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 847e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes psPrint(ps, "0"); 848e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes psWriteEOL(ps); 849e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes psWriteEOL(ps); 850e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes } else { 851e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes psWrite(ps, bytes, 0, bytes.length); 852b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 853b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 854b90da8218274400c8710211d467ed28f23bc28baUrs Grob } 855b90da8218274400c8710211d467ed28f23bc28baUrs Grob} 856