View Javadoc

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.EofException;
30  import org.eclipse.jetty.io.SimpleBuffers;
31  import org.eclipse.jetty.io.View;
32  import org.eclipse.jetty.io.bio.StringEndPoint;
33  import org.eclipse.jetty.util.ByteArrayOutputStream2;
34  
35  /* ------------------------------------------------------------ */
36  /** Test support class.
37   * Assist with parsing and generating HTTP requests and responses.
38   * 
39   * <pre>
40   *      HttpTester tester = new HttpTester();
41   *      
42   *      tester.parse(
43   *          "GET /uri HTTP/1.1\r\n"+
44   *          "Host: fakehost\r\n"+
45   *          "Content-Length: 10\r\n" +
46   *          "\r\n");
47   *     
48   *      System.err.println(tester.getMethod());
49   *      System.err.println(tester.getURI());
50   *      System.err.println(tester.getVersion());
51   *      System.err.println(tester.getHeader("Host"));
52   *      System.err.println(tester.getContent());
53   * </pre>      
54   * 
55   * 
56   * @see org.eclipse.jetty.testing.ServletTester
57   */
58  public class HttpTester
59  {
60      protected HttpFields _fields=new HttpFields();
61      protected String _method;
62      protected String _uri;
63      protected String _version;
64      protected int _status;
65      protected String _reason;
66      protected ByteArrayOutputStream2 _parsedContent;
67      protected byte[] _genContent;
68      
69      private String _charset, _defaultCharset;
70      private Buffer _contentType;
71      
72      public HttpTester()
73      {
74          this("UTF-8");
75      }
76      
77      public HttpTester(String charset)
78      {
79          _defaultCharset = charset;
80      }
81      
82      public void reset()
83      {
84          _fields.clear();
85           _method=null;
86           _uri=null;
87           _version=null;
88           _status=0;
89           _reason=null;
90           _parsedContent=null;
91           _genContent=null;
92      }
93      
94      private String getString(Buffer buffer)
95      {
96          return getString(buffer.asArray());
97      }
98      
99      private String getString(byte[] b)
100     {
101         if(_charset==null)
102             return new String(b);
103         try
104         {
105             return new String(b, _charset);
106         }
107         catch(Exception e)
108         {
109             return new String(b);
110         }
111     }
112     
113     private byte[] getByteArray(String str)
114     {
115         if(_charset==null)
116             return str.getBytes();
117         try
118         {
119             return str.getBytes(_charset);
120         }
121         catch(Exception e)
122         {
123             return str.getBytes();
124         }
125     }
126 
127     /* ------------------------------------------------------------ */
128     /**
129      * Parse one HTTP request or response
130      * @param rawHTTP Raw HTTP to parse
131      * @return Any unparsed data in the rawHTTP (eg pipelined requests)
132      * @throws IOException
133      */
134     public String parse(String rawHTTP, boolean isHeadResponse) throws IOException
135     {
136         _charset = _defaultCharset;
137         ByteArrayBuffer buf = new ByteArrayBuffer(getByteArray(rawHTTP));
138         View view = new View(buf);
139         PH ph = new PH();
140         HttpParser parser = new HttpParser(view,ph);
141         parser.setHeadResponse(isHeadResponse);
142         parser.parse();
143         if (ph.isEarlyEOF())
144             throw new EofException();
145         return getString(view.asArray());
146     }
147     
148     
149     /* ------------------------------------------------------------ */
150     /**
151      * Parse one HTTP request or response
152      * @param rawHTTP Raw HTTP to parse
153      * @return Any unparsed data in the rawHTTP (eg pipelined requests)
154      * @throws IOException
155      */
156     public String parse(String rawHTTP) throws IOException
157     {
158         return parse(rawHTTP, false);
159     }
160 
161     /* ------------------------------------------------------------ */
162     /**
163      * Parse one HTTP request or response
164      * @param rawHTTP Raw HTTP to parse
165      * @return Any unparsed data in the rawHTTP (eg pipelined requests)
166      * @throws IOException
167      */
168     public byte[] parse(byte[] rawHTTP, boolean isHeadResponse) throws IOException
169     {
170         _charset = _defaultCharset;
171         ByteArrayBuffer buf = new ByteArrayBuffer(rawHTTP);
172         View view = new View(buf);
173         PH ph = new PH();
174         HttpParser parser = new HttpParser(view,ph);
175         parser.setHeadResponse(isHeadResponse);
176         parser.parse();
177         if (ph.isEarlyEOF())
178             throw new EofException();
179         return view.asArray();        
180     }
181     
182     /* ------------------------------------------------------------ */
183     /**
184      * Parse one HTTP request or response
185      * @param rawHTTP Raw HTTP to parse
186      * @return Any unparsed data in the rawHTTP (eg pipelined requests)
187      * @throws IOException
188      */
189     public byte[] parse(byte[] rawHTTP) throws IOException
190     {
191         return parse(rawHTTP, false);
192     }
193     
194     /* ------------------------------------------------------------ */
195     public String generate() throws IOException
196     {
197         _charset = _defaultCharset;
198         _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER);
199         if(_contentType!=null)
200         {
201             String charset = MimeTypes.getCharsetFromContentType(_contentType);
202             if(charset!=null)
203                 _charset = charset;
204         }
205         Buffer bb=new ByteArrayBuffer(32*1024 + (_genContent!=null?_genContent.length:0));
206         Buffer sb=new ByteArrayBuffer(4*1024);
207         StringEndPoint endp = new StringEndPoint(_charset);
208         HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
209         
210         if (_method!=null)
211         {
212             generator.setRequest(getMethod(),getURI());
213             if (_version==null)
214                 generator.setVersion(HttpVersions.HTTP_1_1_ORDINAL);
215             else
216                 generator.setVersion(HttpVersions.CACHE.getOrdinal(HttpVersions.CACHE.lookup(_version)));
217             generator.completeHeader(_fields,false);
218             if (_genContent!=null)
219                 generator.addContent(new View(new ByteArrayBuffer(_genContent)),false);
220             else if (_parsedContent!=null)
221                 generator.addContent(new ByteArrayBuffer(_parsedContent.toByteArray()),false);
222         }
223         
224         generator.complete();
225         generator.flushBuffer();
226         return endp.getOutput();
227     }
228     
229     /* ------------------------------------------------------------ */
230     /**
231      * @return the method
232      */
233     public String getMethod()
234     {
235         return _method;
236     }
237 
238     /* ------------------------------------------------------------ */
239     /**
240      * @param method the method to set
241      */
242     public void setMethod(String method)
243     {
244         _method=method;
245     }
246 
247     /* ------------------------------------------------------------ */
248     /**
249      * @return the reason
250      */
251     public String getReason()
252     {
253         return _reason;
254     }
255 
256     /* ------------------------------------------------------------ */
257     /**
258      * @param reason the reason to set
259      */
260     public void setReason(String reason)
261     {
262         _reason=reason;
263     }
264 
265     /* ------------------------------------------------------------ */
266     /**
267      * @return the status
268      */
269     public int getStatus()
270     {
271         return _status;
272     }
273 
274     /* ------------------------------------------------------------ */
275     /**
276      * @param status the status to set
277      */
278     public void setStatus(int status)
279     {
280         _status=status;
281     }
282 
283     /* ------------------------------------------------------------ */
284     /**
285      * @return the uri
286      */
287     public String getURI()
288     {
289         return _uri;
290     }
291 
292     /* ------------------------------------------------------------ */
293     /**
294      * @param uri the uri to set
295      */
296     public void setURI(String uri)
297     {
298         _uri=uri;
299     }
300 
301     /* ------------------------------------------------------------ */
302     /**
303      * @return the version
304      */
305     public String getVersion()
306     {
307         return _version;
308     }
309 
310     /* ------------------------------------------------------------ */
311     /**
312      * @param version the version to set
313      */
314     public void setVersion(String version)
315     {
316         _version=version;
317     }
318     
319     /* ------------------------------------------------------------ */
320     public String getContentType()
321     {
322         return getString(_contentType);
323     }
324     
325     /* ------------------------------------------------------------ */
326     public String getCharacterEncoding()
327     {
328         return _charset;
329     }
330 
331     /* ------------------------------------------------------------ */
332     /**
333      * @param name
334      * @param value
335      * @throws IllegalArgumentException
336      * @see org.eclipse.jetty.http.HttpFields#add(java.lang.String, java.lang.String)
337      */
338     public void addHeader(String name, String value) throws IllegalArgumentException
339     {
340         _fields.add(name,value);
341     }
342 
343     /* ------------------------------------------------------------ */
344     /**
345      * @param name
346      * @param date
347      * @see org.eclipse.jetty.http.HttpFields#addDateField(java.lang.String, long)
348      */
349     public void addDateHeader(String name, long date)
350     {
351         _fields.addDateField(name,date);
352     }
353 
354     /* ------------------------------------------------------------ */
355     /**
356      * @param name
357      * @param value
358      * @see org.eclipse.jetty.http.HttpFields#addLongField(java.lang.String, long)
359      */
360     public void addLongHeader(String name, long value)
361     {
362         _fields.addLongField(name,value);
363     }
364 
365     /* ------------------------------------------------------------ */
366     /**
367      * @param cookie
368      * @see org.eclipse.jetty.http.HttpFields#addSetCookie(org.eclipse.jetty.http.HttpCookie)
369      */
370     public void addSetCookie(Cookie cookie)
371     {
372         _fields.addSetCookie(
373                 cookie.getName(),
374                 cookie.getValue(),
375                 cookie.getDomain(),
376                 cookie.getPath(),
377                 cookie.getMaxAge(),
378                 cookie.getComment(),
379                 cookie.getSecure(),
380                 false,
381                 cookie.getVersion());
382     }
383 
384     /* ------------------------------------------------------------ */
385     /**
386      * @param name
387      * @return the header value as a date
388      * @see org.eclipse.jetty.http.HttpFields#getDateField(java.lang.String)
389      */
390     public long getDateHeader(String name)
391     {
392         return _fields.getDateField(name);
393     }
394 
395     /* ------------------------------------------------------------ */
396     /**
397      * @return the header value names
398      * @see org.eclipse.jetty.http.HttpFields#getFieldNames()
399      */
400     public Enumeration getHeaderNames()
401     {
402         return _fields.getFieldNames();
403     }
404 
405     /* ------------------------------------------------------------ */
406     /**
407      * @param name
408      * @return the header value as a long
409      * @throws NumberFormatException
410      * @see org.eclipse.jetty.http.HttpFields#getLongField(java.lang.String)
411      */
412     public long getLongHeader(String name) throws NumberFormatException
413     {
414         return _fields.getLongField(name);
415     }
416 
417     /* ------------------------------------------------------------ */
418     /**
419      * @param name
420      * @return the header value
421      * @see org.eclipse.jetty.http.HttpFields#getStringField(java.lang.String)
422      */
423     public String getHeader(String name)
424     {
425         return _fields.getStringField(name);
426     }
427 
428     /* ------------------------------------------------------------ */
429     /**
430      * @param name
431      * @return the header values
432      * @see org.eclipse.jetty.http.HttpFields#getValues(java.lang.String)
433      */
434     public Enumeration getHeaderValues(String name)
435     {
436         return _fields.getValues(name);
437     }
438 
439     /* ------------------------------------------------------------ */
440     /**
441      * @param name
442      * @param value
443      * @see org.eclipse.jetty.http.HttpFields#put(java.lang.String, java.lang.String)
444      */
445     public void setHeader(String name, String value)
446     {
447         _fields.put(name,value);
448     }
449 
450     /* ------------------------------------------------------------ */
451     /**
452      * @param name
453      * @param date
454      * @see org.eclipse.jetty.http.HttpFields#putDateField(java.lang.String, long)
455      */
456     public void setDateHeader(String name, long date)
457     {
458         _fields.putDateField(name,date);
459     }
460 
461     /* ------------------------------------------------------------ */
462     /**
463      * @param name
464      * @param value
465      * @see org.eclipse.jetty.http.HttpFields#putLongField(java.lang.String, long)
466      */
467     public void setLongHeader(String name, long value)
468     {
469         _fields.putLongField(name,value);
470     }
471 
472     /* ------------------------------------------------------------ */
473     /**
474      * @param name
475      * @see org.eclipse.jetty.http.HttpFields#remove(java.lang.String)
476      */
477     public void removeHeader(String name)
478     {
479         _fields.remove(name);
480     }
481     
482     /* ------------------------------------------------------------ */
483     public String getContent()
484     {
485         if (_parsedContent!=null)
486             return getString(_parsedContent.toByteArray());
487         if (_genContent!=null)
488             return getString(_genContent);
489         return null;
490     }
491     
492     /* ------------------------------------------------------------ */
493     public byte[] getContentBytes()
494     {
495         if (_parsedContent!=null)
496             return _parsedContent.toByteArray();
497         if (_genContent!=null)
498             return _genContent;
499         return null;
500     }
501     
502     /* ------------------------------------------------------------ */
503     public void setContent(String content)
504     {
505         _parsedContent=null;
506         if (content!=null)
507         {
508             _genContent=getByteArray(content);
509             setLongHeader(HttpHeaders.CONTENT_LENGTH,_genContent.length);
510         }
511         else
512         {
513             removeHeader(HttpHeaders.CONTENT_LENGTH);
514             _genContent=null;
515         }
516     }
517 
518     /* ------------------------------------------------------------ */
519     private class PH extends HttpParser.EventHandler
520     {
521         private volatile boolean _earlyEOF;
522         
523         @Override
524         public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException
525         {
526             reset();
527             _method=getString(method);
528             _uri=getString(url);
529             _version=getString(version);
530         }
531 
532         @Override
533         public void startResponse(Buffer version, int status, Buffer reason) throws IOException
534         {
535             reset();
536             _version=getString(version);
537             _status=status;
538             _reason=getString(reason);
539         }
540         
541         @Override
542         public void parsedHeader(Buffer name, Buffer value) throws IOException
543         {
544             _fields.add(name,value);
545         }
546 
547         @Override
548         public void headerComplete() throws IOException
549         {
550             _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER);
551             if(_contentType!=null)
552             {
553                 String charset = MimeTypes.getCharsetFromContentType(_contentType);
554                 if(charset!=null)
555                     _charset = charset;
556             }
557         }
558 
559         @Override
560         public void messageComplete(long contextLength) throws IOException
561         {
562         }
563         
564         @Override
565         public void content(Buffer ref) throws IOException
566         {
567             if (_parsedContent==null)
568                 _parsedContent=new ByteArrayOutputStream2();
569             _parsedContent.write(ref.asArray());
570         }
571 
572         @Override
573         public void earlyEOF() 
574         {
575             _earlyEOF = true;
576         }
577         
578         public boolean isEarlyEOF()
579         {
580             return _earlyEOF;
581         }
582         
583     }
584 
585 }