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