// ======================================================================== // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.testing; import java.io.IOException; import java.util.Enumeration; import javax.servlet.http.Cookie; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.SimpleBuffers; import org.eclipse.jetty.io.View; import org.eclipse.jetty.io.bio.StringEndPoint; import org.eclipse.jetty.util.ByteArrayOutputStream2; /* ------------------------------------------------------------ */ /** Test support class. * Assist with parsing and generating HTTP requests and responses. * * <pre> * HttpTester tester = new HttpTester(); * * tester.parse( * "GET /uri HTTP/1.1\r\n"+ * "Host: fakehost\r\n"+ * "Content-Length: 10\r\n" + * "\r\n"); * * System.err.println(tester.getMethod()); * System.err.println(tester.getURI()); * System.err.println(tester.getVersion()); * System.err.println(tester.getHeader("Host")); * System.err.println(tester.getContent()); * </pre> * * * @see org.eclipse.jetty.testing.ServletTester */ public class HttpTester { protected HttpFields _fields=new HttpFields(); protected String _method; protected String _uri; protected String _version; protected int _status; protected String _reason; protected ByteArrayOutputStream2 _parsedContent; protected byte[] _genContent; private String _charset, _defaultCharset; private Buffer _contentType; public HttpTester() { this("UTF-8"); } public HttpTester(String charset) { _defaultCharset = charset; } public void reset() { _fields.clear(); _method=null; _uri=null; _version=null; _status=0; _reason=null; _parsedContent=null; _genContent=null; } private String getString(Buffer buffer) { return getString(buffer.asArray()); } private String getString(byte[] b) { if(_charset==null) return new String(b); try { return new String(b, _charset); } catch(Exception e) { return new String(b); } } private byte[] getByteArray(String str) { if(_charset==null) return str.getBytes(); try { return str.getBytes(_charset); } catch(Exception e) { return str.getBytes(); } } /* ------------------------------------------------------------ */ /** * Parse one HTTP request or response * @param rawHTTP Raw HTTP to parse * @return Any unparsed data in the rawHTTP (eg pipelined requests) * @throws IOException */ public String parse(String rawHTTP, boolean isHeadResponse) throws IOException { _charset = _defaultCharset; ByteArrayBuffer buf = new ByteArrayBuffer(getByteArray(rawHTTP)); View view = new View(buf); PH ph = new PH(); HttpParser parser = new HttpParser(view,ph); parser.setHeadResponse(isHeadResponse); parser.parse(); if (ph.isEarlyEOF()) throw new EofException(); return getString(view.asArray()); } /* ------------------------------------------------------------ */ /** * Parse one HTTP request or response * @param rawHTTP Raw HTTP to parse * @return Any unparsed data in the rawHTTP (eg pipelined requests) * @throws IOException */ public String parse(String rawHTTP) throws IOException { return parse(rawHTTP, false); } /* ------------------------------------------------------------ */ /** * Parse one HTTP request or response * @param rawHTTP Raw HTTP to parse * @return Any unparsed data in the rawHTTP (eg pipelined requests) * @throws IOException */ public byte[] parse(byte[] rawHTTP, boolean isHeadResponse) throws IOException { _charset = _defaultCharset; ByteArrayBuffer buf = new ByteArrayBuffer(rawHTTP); View view = new View(buf); PH ph = new PH(); HttpParser parser = new HttpParser(view,ph); parser.setHeadResponse(isHeadResponse); parser.parse(); if (ph.isEarlyEOF()) throw new EofException(); return view.asArray(); } /* ------------------------------------------------------------ */ /** * Parse one HTTP request or response * @param rawHTTP Raw HTTP to parse * @return Any unparsed data in the rawHTTP (eg pipelined requests) * @throws IOException */ public byte[] parse(byte[] rawHTTP) throws IOException { return parse(rawHTTP, false); } /* ------------------------------------------------------------ */ public String generate() throws IOException { _charset = _defaultCharset; _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER); if(_contentType!=null) { String charset = MimeTypes.getCharsetFromContentType(_contentType); if(charset!=null) _charset = charset; } Buffer bb=new ByteArrayBuffer(32*1024 + (_genContent!=null?_genContent.length:0)); Buffer sb=new ByteArrayBuffer(4*1024); StringEndPoint endp = new StringEndPoint(_charset); HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb,bb),endp); if (_method!=null) { generator.setRequest(getMethod(),getURI()); if (_version==null) generator.setVersion(HttpVersions.HTTP_1_1_ORDINAL); else generator.setVersion(HttpVersions.CACHE.getOrdinal(HttpVersions.CACHE.lookup(_version))); generator.completeHeader(_fields,false); if (_genContent!=null) generator.addContent(new View(new ByteArrayBuffer(_genContent)),false); else if (_parsedContent!=null) generator.addContent(new ByteArrayBuffer(_parsedContent.toByteArray()),false); } generator.complete(); generator.flushBuffer(); return endp.getOutput(); } /* ------------------------------------------------------------ */ /** * @return the method */ public String getMethod() { return _method; } /* ------------------------------------------------------------ */ /** * @param method the method to set */ public void setMethod(String method) { _method=method; } /* ------------------------------------------------------------ */ /** * @return the reason */ public String getReason() { return _reason; } /* ------------------------------------------------------------ */ /** * @param reason the reason to set */ public void setReason(String reason) { _reason=reason; } /* ------------------------------------------------------------ */ /** * @return the status */ public int getStatus() { return _status; } /* ------------------------------------------------------------ */ /** * @param status the status to set */ public void setStatus(int status) { _status=status; } /* ------------------------------------------------------------ */ /** * @return the uri */ public String getURI() { return _uri; } /* ------------------------------------------------------------ */ /** * @param uri the uri to set */ public void setURI(String uri) { _uri=uri; } /* ------------------------------------------------------------ */ /** * @return the version */ public String getVersion() { return _version; } /* ------------------------------------------------------------ */ /** * @param version the version to set */ public void setVersion(String version) { _version=version; } /* ------------------------------------------------------------ */ public String getContentType() { return getString(_contentType); } /* ------------------------------------------------------------ */ public String getCharacterEncoding() { return _charset; } /* ------------------------------------------------------------ */ /** * @param name * @param value * @throws IllegalArgumentException * @see org.eclipse.jetty.http.HttpFields#add(java.lang.String, java.lang.String) */ public void addHeader(String name, String value) throws IllegalArgumentException { _fields.add(name,value); } /* ------------------------------------------------------------ */ /** * @param name * @param date * @see org.eclipse.jetty.http.HttpFields#addDateField(java.lang.String, long) */ public void addDateHeader(String name, long date) { _fields.addDateField(name,date); } /* ------------------------------------------------------------ */ /** * @param name * @param value * @see org.eclipse.jetty.http.HttpFields#addLongField(java.lang.String, long) */ public void addLongHeader(String name, long value) { _fields.addLongField(name,value); } /* ------------------------------------------------------------ */ /** * @param cookie * @see org.eclipse.jetty.http.HttpFields#addSetCookie(org.eclipse.jetty.http.HttpCookie) */ public void addSetCookie(Cookie cookie) { _fields.addSetCookie( cookie.getName(), cookie.getValue(), cookie.getDomain(), cookie.getPath(), cookie.getMaxAge(), cookie.getComment(), cookie.getSecure(), false, cookie.getVersion()); } /* ------------------------------------------------------------ */ /** * @param name * @return the header value as a date * @see org.eclipse.jetty.http.HttpFields#getDateField(java.lang.String) */ public long getDateHeader(String name) { return _fields.getDateField(name); } /* ------------------------------------------------------------ */ /** * @return the header value names * @see org.eclipse.jetty.http.HttpFields#getFieldNames() */ public Enumeration getHeaderNames() { return _fields.getFieldNames(); } /* ------------------------------------------------------------ */ /** * @param name * @return the header value as a long * @throws NumberFormatException * @see org.eclipse.jetty.http.HttpFields#getLongField(java.lang.String) */ public long getLongHeader(String name) throws NumberFormatException { return _fields.getLongField(name); } /* ------------------------------------------------------------ */ /** * @param name * @return the header value * @see org.eclipse.jetty.http.HttpFields#getStringField(java.lang.String) */ public String getHeader(String name) { return _fields.getStringField(name); } /* ------------------------------------------------------------ */ /** * @param name * @return the header values * @see org.eclipse.jetty.http.HttpFields#getValues(java.lang.String) */ public Enumeration getHeaderValues(String name) { return _fields.getValues(name); } /* ------------------------------------------------------------ */ /** * @param name * @param value * @see org.eclipse.jetty.http.HttpFields#put(java.lang.String, java.lang.String) */ public void setHeader(String name, String value) { _fields.put(name,value); } /* ------------------------------------------------------------ */ /** * @param name * @param date * @see org.eclipse.jetty.http.HttpFields#putDateField(java.lang.String, long) */ public void setDateHeader(String name, long date) { _fields.putDateField(name,date); } /* ------------------------------------------------------------ */ /** * @param name * @param value * @see org.eclipse.jetty.http.HttpFields#putLongField(java.lang.String, long) */ public void setLongHeader(String name, long value) { _fields.putLongField(name,value); } /* ------------------------------------------------------------ */ /** * @param name * @see org.eclipse.jetty.http.HttpFields#remove(java.lang.String) */ public void removeHeader(String name) { _fields.remove(name); } /* ------------------------------------------------------------ */ public String getContent() { if (_parsedContent!=null) return getString(_parsedContent.toByteArray()); if (_genContent!=null) return getString(_genContent); return null; } /* ------------------------------------------------------------ */ public byte[] getContentBytes() { if (_parsedContent!=null) return _parsedContent.toByteArray(); if (_genContent!=null) return _genContent; return null; } /* ------------------------------------------------------------ */ public void setContent(String content) { _parsedContent=null; if (content!=null) { _genContent=getByteArray(content); setLongHeader(HttpHeaders.CONTENT_LENGTH,_genContent.length); } else { removeHeader(HttpHeaders.CONTENT_LENGTH); _genContent=null; } } /* ------------------------------------------------------------ */ private class PH extends HttpParser.EventHandler { private volatile boolean _earlyEOF; @Override public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException { reset(); _method=getString(method); _uri=getString(url); _version=getString(version); } @Override public void startResponse(Buffer version, int status, Buffer reason) throws IOException { reset(); _version=getString(version); _status=status; _reason=getString(reason); } @Override public void parsedHeader(Buffer name, Buffer value) throws IOException { _fields.add(name,value); } @Override public void headerComplete() throws IOException { _contentType = _fields.get(HttpHeaders.CONTENT_TYPE_BUFFER); if(_contentType!=null) { String charset = MimeTypes.getCharsetFromContentType(_contentType); if(charset!=null) _charset = charset; } } @Override public void messageComplete(long contextLength) throws IOException { } @Override public void content(Buffer ref) throws IOException { if (_parsedContent==null) _parsedContent=new ByteArrayOutputStream2(); _parsedContent.write(ref.asArray()); } @Override public void earlyEOF() { _earlyEOF = true; } public boolean isEarlyEOF() { return _earlyEOF; } } }