1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package net.grinder.plugin.http;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.net.ServerSocket;
28 import java.net.Socket;
29 import java.net.SocketException;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 import junit.framework.Assert;
37 import net.grinder.common.UncheckedInterruptedException;
38 import HTTPClient.NVPair;
39
40
41
42
43
44
45 class HTTPRequestHandler extends Assert implements Runnable {
46 private static final Pattern s_contentLengthPattern;
47
48 private final List<NVPair> m_headers = new ArrayList<NVPair>();
49
50 static {
51 try {
52 s_contentLengthPattern =
53 Pattern.compile("^Content-Length:[ \\t]*(.*)\\r?$",
54 Pattern.MULTILINE |
55 Pattern.CASE_INSENSITIVE);
56 }
57 catch (Exception e) {
58 throw new ExceptionInInitializerError(e);
59 }
60 }
61
62 private final ServerSocket m_serverSocket;
63 private String m_lastRequestHeaders;
64 private byte[] m_lastRequestBody;
65 private String m_body;
66 private AtomicBoolean m_started = new AtomicBoolean();
67
68 private long m_responseDelay = 0;
69
70 public HTTPRequestHandler() throws Exception {
71 m_serverSocket = new ServerSocket(0);
72 }
73
74 public void start() throws InterruptedException {
75 if (m_started.get()) {
76 throw new AssertionError("Already started");
77 }
78
79 new Thread(this, getClass().getName()).start();
80
81 while (!m_started.get()) {
82 Thread.sleep(1);
83 }
84 }
85
86 public final void shutdown() throws Exception {
87 m_serverSocket.close();
88 }
89
90 public final String getURL() {
91 return "http://localhost:" + m_serverSocket.getLocalPort();
92 }
93
94 public final String getLastRequestHeaders() {
95 return m_lastRequestHeaders;
96 }
97
98 public final byte[] getLastRequestBody() {
99 return m_lastRequestBody;
100 }
101
102 public final String getRequestFirstHeader() {
103 final String text = getLastRequestHeaders();
104
105 final int i = text.indexOf("\r\n");
106 assertTrue("Has at least one line", i>=0);
107 return text.substring(0, i);
108 }
109
110 public final void assertRequestContainsHeader(String line) {
111 final String text = getLastRequestHeaders();
112
113 int start = 0;
114 int i;
115
116 while ((i = text.indexOf("\r\n", start)) != -1) {
117 if (text.substring(start, i).equals(line)) {
118 return;
119 }
120
121 start = i + 2;
122 }
123
124 if (text.substring(start).equals(line)) {
125 return;
126 }
127
128 fail(text + " does not contain " + line);
129 }
130
131 public final void assertRequestDoesNotContainHeader(String line) {
132 final String text = getLastRequestHeaders();
133
134 int start = 0;
135 int i;
136
137 while((i = text.indexOf("\r\n", start)) != -1) {
138 assertTrue(!text.substring(start, i).equals(line));
139 start = i + 2;
140 }
141
142 assertTrue(!text.substring(start).equals(line));
143 }
144
145 public final void run() {
146 try {
147 m_started.set(true);
148
149 while (true) {
150 final Socket localSocket;
151
152 try {
153 localSocket = m_serverSocket.accept();
154 }
155 catch (SocketException e) {
156
157 break;
158 }
159
160 final InputStream in = localSocket.getInputStream();
161
162 final StringBuffer headerBuffer = new StringBuffer();
163 final byte[] buffer = new byte[1000];
164 int n;
165 int bodyStart = -1;
166
167 READ_HEADERS:
168 while ((n = in.read(buffer, 0, buffer.length)) != -1) {
169
170 for (int i=0; i<n-3; ++i) {
171 if (buffer[i] == '\r' &&
172 buffer[i+1] == '\n' &&
173 buffer[i+2] == '\r' &&
174 buffer[i+3] == '\n') {
175
176 headerBuffer.append(new String(buffer, 0, i));
177 bodyStart = i + 4;
178 break READ_HEADERS;
179 }
180 }
181
182 headerBuffer.append(new String(buffer, 0, n));
183 }
184
185 if (bodyStart == -1) {
186 throw new IOException("No header boundary");
187 }
188
189 m_lastRequestHeaders = headerBuffer.toString();
190
191 final Matcher matcher =
192 s_contentLengthPattern.matcher(m_lastRequestHeaders);
193
194 if (matcher.find()) {
195 final int contentLength =
196 Integer.parseInt(matcher.group(1).trim());
197
198 m_lastRequestBody = new byte[contentLength];
199
200 int bodyBytes = n - bodyStart;
201
202 System.arraycopy(buffer, bodyStart, m_lastRequestBody, 0,
203 bodyBytes);
204
205 while (bodyBytes < m_lastRequestBody.length) {
206 final int bytesRead =
207 in.read(m_lastRequestBody, bodyBytes,
208 m_lastRequestBody.length - bodyBytes);
209
210 if (bytesRead == -1) {
211 throw new IOException("Content-length too large");
212 }
213
214 bodyBytes += bytesRead;
215 }
216
217 if (in.available() > 0) {
218 throw new IOException("Content-length too small");
219 }
220 }
221 else {
222 m_lastRequestBody = null;
223 }
224
225 try {
226 Thread.sleep(m_responseDelay);
227 }
228 catch (InterruptedException e) {
229 throw new UncheckedInterruptedException(e);
230 }
231
232 final OutputStream out = localSocket.getOutputStream();
233
234 final StringBuffer response = new StringBuffer();
235 writeHeaders(response);
236 response.append("\r\n");
237
238 if (m_body != null) {
239 response.append(m_body);
240 }
241
242 out.write(response.toString().getBytes());
243 out.flush();
244
245 localSocket.close();
246 }
247 }
248 catch (IOException e) {
249
250
251 }
252 finally {
253 try {
254 m_serverSocket.close();
255 }
256 catch (IOException e) {
257
258 }
259 }
260 }
261
262
263
264
265 protected void writeHeaders(StringBuffer response) {
266 response.append("HTTP/1.0 200 OK\r\n");
267
268 for (NVPair pair : m_headers) {
269 response.append(pair.getName()).append(": ").append(pair.getValue());
270 response.append("\r\n");
271 }
272 }
273
274 public void clearHeaders() {
275 m_headers.clear();
276 }
277
278 public void addHeader(String name, String value) {
279 m_headers.add(new NVPair(name, value));
280 }
281
282 public void setBody(String body) {
283 m_body = body;
284 }
285
286 public void setResponseDelay(long responseDelay) {
287 m_responseDelay = responseDelay;
288 }
289 }