View Javadoc

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