1 // ========================================================================
2 // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.testing;
15
16 import java.io.IOException;
17 import java.util.Enumeration;
18
19 import javax.servlet.http.Cookie;
20
21 import org.eclipse.jetty.http.HttpFields;
22 import org.eclipse.jetty.http.HttpGenerator;
23 import org.eclipse.jetty.http.HttpHeaders;
24 import org.eclipse.jetty.http.HttpParser;
25 import org.eclipse.jetty.http.HttpVersions;
26 import org.eclipse.jetty.http.MimeTypes;
27 import org.eclipse.jetty.io.Buffer;
28 import org.eclipse.jetty.io.ByteArrayBuffer;
29 import org.eclipse.jetty.io.SimpleBuffers;
30 import org.eclipse.jetty.io.View;
31 import org.eclipse.jetty.io.bio.StringEndPoint;
32 import org.eclipse.jetty.util.ByteArrayOutputStream2;
33
34 /* ------------------------------------------------------------ */
35 /** Test support class.
36 * Assist with parsing and generating HTTP requests and responses.
37 *
38 * <pre>
39 * HttpTester tester = new HttpTester();
40 *
41 * tester.parse(
42 * "GET /uri HTTP/1.1\r\n"+
43 * "Host: fakehost\r\n"+
44 * "Content-Length: 10\r\n" +
45 * "\r\n");
46 *
47 * System.err.println(tester.getMethod());
48 * System.err.println(tester.getURI());
49 * System.err.println(tester.getVersion());
50 * System.err.println(tester.getHeader("Host"));
51 * System.err.println(tester.getContent());
52 * </pre>
53 *
54 *
55 * @see org.eclipse.jetty.testing.ServletTester
56 */
57 public class HttpTester
58 {
59 protected HttpFields _fields=new HttpFields();
60 protected String _method;
61 protected String _uri;
62 protected String _version;
63 protected int _status;
64 protected String _reason;
65 protected ByteArrayOutputStream2 _parsedContent;
66 protected byte[] _genContent;
67
68 private String _charset, _defaultCharset;
69 private Buffer _contentType;
70
71 public HttpTester()
72 {
73 this("UTF-8");
74 }
75
76 public HttpTester(String charset)
77 {
78 _defaultCharset = charset;
79 }
80
81 public void reset()
82 {
83 _fields.clear();
84 _method=null;
85 _uri=null;
86 _version=null;
87 _status=0;
88 _reason=null;
89 _parsedContent=null;
90 _genContent=null;
91 }
92
93 private String getString(Buffer buffer)
94 {
95 return getString(buffer.asArray());
96 }
97
98 private String getString(byte[] b)
99 {
100 if(_charset==null)
101 return new String(b);
102 try
103 {
104 return new String(b, _charset);
105 }
106 catch(Exception e)
107 {
108 return new String(b);
109 }
110 }
111
112 private byte[] getByteArray(String str)
113 {
114 if(_charset==null)
115 return str.getBytes();
116 try
117 {
118 return str.getBytes(_charset);
119 }
120 catch(Exception e)
121 {
122 return str.getBytes();
123 }
124 }
125
126 /* ------------------------------------------------------------ */
127 /**
128 * Parse one HTTP request or response
129 * @param rawHTTP Raw HTTP to parse
130 * @return Any unparsed data in the rawHTTP (eg pipelined requests)
131 * @throws IOException
132 */
133 public String parse(String rawHTTP) throws IOException
134 {
135 _charset = _defaultCharset;
136 ByteArrayBuffer buf = new ByteArrayBuffer(getByteArray(rawHTTP));
137 View view = new View(buf);
138 HttpParser parser = new HttpParser(view,new PH());
139 parser.parse();
140 return getString(view.asArray());
141 }
142
143 /* ------------------------------------------------------------ */
144 public String generate() throws IOException
145 {
146 _charset = _defaultCharset;
147 _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER);
148 if(_contentType!=null)
149 {
150 String charset = MimeTypes.getCharsetFromContentType(_contentType);
151 if(charset!=null)
152 _charset = charset;
153 }
154 Buffer bb=new ByteArrayBuffer(32*1024 + (_genContent!=null?_genContent.length:0));
155 Buffer sb=new ByteArrayBuffer(4*1024);
156 StringEndPoint endp = new StringEndPoint(_charset);
157 HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
158
159 if (_method!=null)
160 {
161 generator.setRequest(getMethod(),getURI());
162 if (_version==null)
163 generator.setVersion(HttpVersions.HTTP_1_1_ORDINAL);
164 else
165 generator.setVersion(HttpVersions.CACHE.getOrdinal(HttpVersions.CACHE.lookup(_version)));
166 generator.completeHeader(_fields,false);
167 if (_genContent!=null)
168 generator.addContent(new View(new ByteArrayBuffer(_genContent)),false);
169 else if (_parsedContent!=null)
170 generator.addContent(new ByteArrayBuffer(_parsedContent.toByteArray()),false);
171 }
172
173 generator.complete();
174 generator.flushBuffer();
175 return endp.getOutput();
176 }
177
178 /* ------------------------------------------------------------ */
179 /**
180 * @return the method
181 */
182 public String getMethod()
183 {
184 return _method;
185 }
186
187 /* ------------------------------------------------------------ */
188 /**
189 * @param method the method to set
190 */
191 public void setMethod(String method)
192 {
193 _method=method;
194 }
195
196 /* ------------------------------------------------------------ */
197 /**
198 * @return the reason
199 */
200 public String getReason()
201 {
202 return _reason;
203 }
204
205 /* ------------------------------------------------------------ */
206 /**
207 * @param reason the reason to set
208 */
209 public void setReason(String reason)
210 {
211 _reason=reason;
212 }
213
214 /* ------------------------------------------------------------ */
215 /**
216 * @return the status
217 */
218 public int getStatus()
219 {
220 return _status;
221 }
222
223 /* ------------------------------------------------------------ */
224 /**
225 * @param status the status to set
226 */
227 public void setStatus(int status)
228 {
229 _status=status;
230 }
231
232 /* ------------------------------------------------------------ */
233 /**
234 * @return the uri
235 */
236 public String getURI()
237 {
238 return _uri;
239 }
240
241 /* ------------------------------------------------------------ */
242 /**
243 * @param uri the uri to set
244 */
245 public void setURI(String uri)
246 {
247 _uri=uri;
248 }
249
250 /* ------------------------------------------------------------ */
251 /**
252 * @return the version
253 */
254 public String getVersion()
255 {
256 return _version;
257 }
258
259 /* ------------------------------------------------------------ */
260 /**
261 * @param version the version to set
262 */
263 public void setVersion(String version)
264 {
265 _version=version;
266 }
267
268 /* ------------------------------------------------------------ */
269 public String getContentType()
270 {
271 return getString(_contentType);
272 }
273
274 /* ------------------------------------------------------------ */
275 public String getCharacterEncoding()
276 {
277 return _charset;
278 }
279
280 /* ------------------------------------------------------------ */
281 /**
282 * @param name
283 * @param value
284 * @throws IllegalArgumentException
285 * @see org.eclipse.jetty.http.HttpFields#add(java.lang.String, java.lang.String)
286 */
287 public void addHeader(String name, String value) throws IllegalArgumentException
288 {
289 _fields.add(name,value);
290 }
291
292 /* ------------------------------------------------------------ */
293 /**
294 * @param name
295 * @param date
296 * @see org.eclipse.jetty.http.HttpFields#addDateField(java.lang.String, long)
297 */
298 public void addDateHeader(String name, long date)
299 {
300 _fields.addDateField(name,date);
301 }
302
303 /* ------------------------------------------------------------ */
304 /**
305 * @param name
306 * @param value
307 * @see org.eclipse.jetty.http.HttpFields#addLongField(java.lang.String, long)
308 */
309 public void addLongHeader(String name, long value)
310 {
311 _fields.addLongField(name,value);
312 }
313
314 /* ------------------------------------------------------------ */
315 /**
316 * @param cookie
317 * @see org.eclipse.jetty.http.HttpFields#addSetCookie(javax.servlet.http.Cookie)
318 */
319 public void addSetCookie(Cookie cookie)
320 {
321 _fields.addSetCookie(
322 cookie.getName(),
323 cookie.getValue(),
324 cookie.getDomain(),
325 cookie.getPath(),
326 cookie.getMaxAge(),
327 cookie.getComment(),
328 cookie.getSecure(),
329 false,
330 cookie.getVersion());
331 }
332
333 /* ------------------------------------------------------------ */
334 /**
335 * @param name
336 * @return
337 * @see org.eclipse.jetty.http.HttpFields#getDateField(java.lang.String)
338 */
339 public long getDateHeader(String name)
340 {
341 return _fields.getDateField(name);
342 }
343
344 /* ------------------------------------------------------------ */
345 /**
346 * @return
347 * @see org.eclipse.jetty.http.HttpFields#getFieldNames()
348 */
349 public Enumeration getHeaderNames()
350 {
351 return _fields.getFieldNames();
352 }
353
354 /* ------------------------------------------------------------ */
355 /**
356 * @param name
357 * @return
358 * @throws NumberFormatException
359 * @see org.eclipse.jetty.http.HttpFields#getLongField(java.lang.String)
360 */
361 public long getLongHeader(String name) throws NumberFormatException
362 {
363 return _fields.getLongField(name);
364 }
365
366 /* ------------------------------------------------------------ */
367 /**
368 * @param name
369 * @return
370 * @see org.eclipse.jetty.http.HttpFields#getStringField(java.lang.String)
371 */
372 public String getHeader(String name)
373 {
374 return _fields.getStringField(name);
375 }
376
377 /* ------------------------------------------------------------ */
378 /**
379 * @param name
380 * @return
381 * @see org.eclipse.jetty.http.HttpFields#getValues(java.lang.String)
382 */
383 public Enumeration getHeaderValues(String name)
384 {
385 return _fields.getValues(name);
386 }
387
388 /* ------------------------------------------------------------ */
389 /**
390 * @param name
391 * @param value
392 * @see org.eclipse.jetty.http.HttpFields#put(java.lang.String, java.lang.String)
393 */
394 public void setHeader(String name, String value)
395 {
396 _fields.put(name,value);
397 }
398
399 /* ------------------------------------------------------------ */
400 /**
401 * @param name
402 * @param date
403 * @see org.eclipse.jetty.http.HttpFields#putDateField(java.lang.String, long)
404 */
405 public void setDateHeader(String name, long date)
406 {
407 _fields.putDateField(name,date);
408 }
409
410 /* ------------------------------------------------------------ */
411 /**
412 * @param name
413 * @param value
414 * @see org.eclipse.jetty.http.HttpFields#putLongField(java.lang.String, long)
415 */
416 public void setLongHeader(String name, long value)
417 {
418 _fields.putLongField(name,value);
419 }
420
421 /* ------------------------------------------------------------ */
422 /**
423 * @param name
424 * @see org.eclipse.jetty.http.HttpFields#remove(java.lang.String)
425 */
426 public void removeHeader(String name)
427 {
428 _fields.remove(name);
429 }
430
431 /* ------------------------------------------------------------ */
432 public String getContent()
433 {
434 if (_parsedContent!=null)
435 return getString(_parsedContent.toByteArray());
436 if (_genContent!=null)
437 return getString(_genContent);
438 return null;
439 }
440
441 /* ------------------------------------------------------------ */
442 public void setContent(String content)
443 {
444 _parsedContent=null;
445 if (content!=null)
446 {
447 _genContent=getByteArray(content);
448 setLongHeader(HttpHeaders.CONTENT_LENGTH,_genContent.length);
449 }
450 else
451 {
452 removeHeader(HttpHeaders.CONTENT_LENGTH);
453 _genContent=null;
454 }
455 }
456
457 /* ------------------------------------------------------------ */
458 private class PH extends HttpParser.EventHandler
459 {
460 public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException
461 {
462 reset();
463 _method=getString(method);
464 _uri=getString(url);
465 _version=getString(version);
466 }
467
468 public void startResponse(Buffer version, int status, Buffer reason) throws IOException
469 {
470 reset();
471 _version=getString(version);
472 _status=status;
473 _reason=getString(reason);
474 }
475
476 public void parsedHeader(Buffer name, Buffer value) throws IOException
477 {
478 _fields.add(name,value);
479 }
480
481 public void headerComplete() throws IOException
482 {
483 _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER);
484 if(_contentType!=null)
485 {
486 String charset = MimeTypes.getCharsetFromContentType(_contentType);
487 if(charset!=null)
488 _charset = charset;
489 }
490 }
491
492 public void messageComplete(long contextLength) throws IOException
493 {
494 }
495
496 public void content(Buffer ref) throws IOException
497 {
498 if (_parsedContent==null)
499 _parsedContent=new ByteArrayOutputStream2();
500 _parsedContent.write(ref.asArray());
501 }
502 }
503
504 }