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