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 /**
145 * Parse one HTTP request or response
146 * @param rawHTTP Raw HTTP to parse
147 * @return Any unparsed data in the rawHTTP (eg pipelined requests)
148 * @throws IOException
149 */
150 public byte[] parse(byte[] rawHTTP) throws IOException
151 {
152 _charset = _defaultCharset;
153 ByteArrayBuffer buf = new ByteArrayBuffer(rawHTTP);
154 View view = new View(buf);
155 HttpParser parser = new HttpParser(view,new PH());
156 parser.parse();
157 return view.asArray();
158 }
159
160 /* ------------------------------------------------------------ */
161 public String generate() throws IOException
162 {
163 _charset = _defaultCharset;
164 _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER);
165 if(_contentType!=null)
166 {
167 String charset = MimeTypes.getCharsetFromContentType(_contentType);
168 if(charset!=null)
169 _charset = charset;
170 }
171 Buffer bb=new ByteArrayBuffer(32*1024 + (_genContent!=null?_genContent.length:0));
172 Buffer sb=new ByteArrayBuffer(4*1024);
173 StringEndPoint endp = new StringEndPoint(_charset);
174 HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
175
176 if (_method!=null)
177 {
178 generator.setRequest(getMethod(),getURI());
179 if (_version==null)
180 generator.setVersion(HttpVersions.HTTP_1_1_ORDINAL);
181 else
182 generator.setVersion(HttpVersions.CACHE.getOrdinal(HttpVersions.CACHE.lookup(_version)));
183 generator.completeHeader(_fields,false);
184 if (_genContent!=null)
185 generator.addContent(new View(new ByteArrayBuffer(_genContent)),false);
186 else if (_parsedContent!=null)
187 generator.addContent(new ByteArrayBuffer(_parsedContent.toByteArray()),false);
188 }
189
190 generator.complete();
191 generator.flushBuffer();
192 return endp.getOutput();
193 }
194
195 /* ------------------------------------------------------------ */
196 /**
197 * @return the method
198 */
199 public String getMethod()
200 {
201 return _method;
202 }
203
204 /* ------------------------------------------------------------ */
205 /**
206 * @param method the method to set
207 */
208 public void setMethod(String method)
209 {
210 _method=method;
211 }
212
213 /* ------------------------------------------------------------ */
214 /**
215 * @return the reason
216 */
217 public String getReason()
218 {
219 return _reason;
220 }
221
222 /* ------------------------------------------------------------ */
223 /**
224 * @param reason the reason to set
225 */
226 public void setReason(String reason)
227 {
228 _reason=reason;
229 }
230
231 /* ------------------------------------------------------------ */
232 /**
233 * @return the status
234 */
235 public int getStatus()
236 {
237 return _status;
238 }
239
240 /* ------------------------------------------------------------ */
241 /**
242 * @param status the status to set
243 */
244 public void setStatus(int status)
245 {
246 _status=status;
247 }
248
249 /* ------------------------------------------------------------ */
250 /**
251 * @return the uri
252 */
253 public String getURI()
254 {
255 return _uri;
256 }
257
258 /* ------------------------------------------------------------ */
259 /**
260 * @param uri the uri to set
261 */
262 public void setURI(String uri)
263 {
264 _uri=uri;
265 }
266
267 /* ------------------------------------------------------------ */
268 /**
269 * @return the version
270 */
271 public String getVersion()
272 {
273 return _version;
274 }
275
276 /* ------------------------------------------------------------ */
277 /**
278 * @param version the version to set
279 */
280 public void setVersion(String version)
281 {
282 _version=version;
283 }
284
285 /* ------------------------------------------------------------ */
286 public String getContentType()
287 {
288 return getString(_contentType);
289 }
290
291 /* ------------------------------------------------------------ */
292 public String getCharacterEncoding()
293 {
294 return _charset;
295 }
296
297 /* ------------------------------------------------------------ */
298 /**
299 * @param name
300 * @param value
301 * @throws IllegalArgumentException
302 * @see org.eclipse.jetty.http.HttpFields#add(java.lang.String, java.lang.String)
303 */
304 public void addHeader(String name, String value) throws IllegalArgumentException
305 {
306 _fields.add(name,value);
307 }
308
309 /* ------------------------------------------------------------ */
310 /**
311 * @param name
312 * @param date
313 * @see org.eclipse.jetty.http.HttpFields#addDateField(java.lang.String, long)
314 */
315 public void addDateHeader(String name, long date)
316 {
317 _fields.addDateField(name,date);
318 }
319
320 /* ------------------------------------------------------------ */
321 /**
322 * @param name
323 * @param value
324 * @see org.eclipse.jetty.http.HttpFields#addLongField(java.lang.String, long)
325 */
326 public void addLongHeader(String name, long value)
327 {
328 _fields.addLongField(name,value);
329 }
330
331 /* ------------------------------------------------------------ */
332 /**
333 * @param cookie
334 * @see org.eclipse.jetty.http.HttpFields#addSetCookie(org.eclipse.jetty.http.HttpCookie)
335 */
336 public void addSetCookie(Cookie cookie)
337 {
338 _fields.addSetCookie(
339 cookie.getName(),
340 cookie.getValue(),
341 cookie.getDomain(),
342 cookie.getPath(),
343 cookie.getMaxAge(),
344 cookie.getComment(),
345 cookie.getSecure(),
346 cookie.isHttpOnly(),
347 cookie.getVersion());
348 }
349
350 /* ------------------------------------------------------------ */
351 /**
352 * @param name
353 * @return the header value as a date
354 * @see org.eclipse.jetty.http.HttpFields#getDateField(java.lang.String)
355 */
356 public long getDateHeader(String name)
357 {
358 return _fields.getDateField(name);
359 }
360
361 /* ------------------------------------------------------------ */
362 /**
363 * @return the header value names
364 * @see org.eclipse.jetty.http.HttpFields#getFieldNames()
365 */
366 public Enumeration getHeaderNames()
367 {
368 return _fields.getFieldNames();
369 }
370
371 /* ------------------------------------------------------------ */
372 /**
373 * @param name
374 * @return the header value as a long
375 * @throws NumberFormatException
376 * @see org.eclipse.jetty.http.HttpFields#getLongField(java.lang.String)
377 */
378 public long getLongHeader(String name) throws NumberFormatException
379 {
380 return _fields.getLongField(name);
381 }
382
383 /* ------------------------------------------------------------ */
384 /**
385 * @param name
386 * @return the header value
387 * @see org.eclipse.jetty.http.HttpFields#getStringField(java.lang.String)
388 */
389 public String getHeader(String name)
390 {
391 return _fields.getStringField(name);
392 }
393
394 /* ------------------------------------------------------------ */
395 /**
396 * @param name
397 * @return the header values
398 * @see org.eclipse.jetty.http.HttpFields#getValues(java.lang.String)
399 */
400 public Enumeration getHeaderValues(String name)
401 {
402 return _fields.getValues(name);
403 }
404
405 /* ------------------------------------------------------------ */
406 /**
407 * @param name
408 * @param value
409 * @see org.eclipse.jetty.http.HttpFields#put(java.lang.String, java.lang.String)
410 */
411 public void setHeader(String name, String value)
412 {
413 _fields.put(name,value);
414 }
415
416 /* ------------------------------------------------------------ */
417 /**
418 * @param name
419 * @param date
420 * @see org.eclipse.jetty.http.HttpFields#putDateField(java.lang.String, long)
421 */
422 public void setDateHeader(String name, long date)
423 {
424 _fields.putDateField(name,date);
425 }
426
427 /* ------------------------------------------------------------ */
428 /**
429 * @param name
430 * @param value
431 * @see org.eclipse.jetty.http.HttpFields#putLongField(java.lang.String, long)
432 */
433 public void setLongHeader(String name, long value)
434 {
435 _fields.putLongField(name,value);
436 }
437
438 /* ------------------------------------------------------------ */
439 /**
440 * @param name
441 * @see org.eclipse.jetty.http.HttpFields#remove(java.lang.String)
442 */
443 public void removeHeader(String name)
444 {
445 _fields.remove(name);
446 }
447
448 /* ------------------------------------------------------------ */
449 public String getContent()
450 {
451 if (_parsedContent!=null)
452 return getString(_parsedContent.toByteArray());
453 if (_genContent!=null)
454 return getString(_genContent);
455 return null;
456 }
457
458 /* ------------------------------------------------------------ */
459 public byte[] getContentBytes()
460 {
461 if (_parsedContent!=null)
462 return _parsedContent.toByteArray();
463 if (_genContent!=null)
464 return _genContent;
465 return null;
466 }
467
468 /* ------------------------------------------------------------ */
469 public void setContent(String content)
470 {
471 _parsedContent=null;
472 if (content!=null)
473 {
474 _genContent=getByteArray(content);
475 setLongHeader(HttpHeaders.CONTENT_LENGTH,_genContent.length);
476 }
477 else
478 {
479 removeHeader(HttpHeaders.CONTENT_LENGTH);
480 _genContent=null;
481 }
482 }
483
484 /* ------------------------------------------------------------ */
485 private class PH extends HttpParser.EventHandler
486 {
487 @Override
488 public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException
489 {
490 reset();
491 _method=getString(method);
492 _uri=getString(url);
493 _version=getString(version);
494 }
495
496 @Override
497 public void startResponse(Buffer version, int status, Buffer reason) throws IOException
498 {
499 reset();
500 _version=getString(version);
501 _status=status;
502 _reason=getString(reason);
503 }
504
505 @Override
506 public void parsedHeader(Buffer name, Buffer value) throws IOException
507 {
508 _fields.add(name,value);
509 }
510
511 @Override
512 public void headerComplete() throws IOException
513 {
514 _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER);
515 if(_contentType!=null)
516 {
517 String charset = MimeTypes.getCharsetFromContentType(_contentType);
518 if(charset!=null)
519 _charset = charset;
520 }
521 }
522
523 @Override
524 public void messageComplete(long contextLength) throws IOException
525 {
526 }
527
528 @Override
529 public void content(Buffer ref) throws IOException
530 {
531 if (_parsedContent==null)
532 _parsedContent=new ByteArrayOutputStream2();
533 _parsedContent.write(ref.asArray());
534 }
535 }
536
537 }