org.browsermob.proxy.jetty.http.HttpMessage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of browsermob-proxy Show documentation
Show all versions of browsermob-proxy Show documentation
A programmatic HTTP/S designed for performance and functional testing
// ========================================================================
// $Id: HttpMessage.java,v 1.41 2006/04/04 22:28:02 gregwilkins Exp $
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.browsermob.proxy.jetty.http;
import org.apache.commons.logging.Log;
import org.browsermob.proxy.jetty.log.LogFactory;
import org.browsermob.proxy.jetty.util.LogSupport;
import org.browsermob.proxy.jetty.util.QuotedStringTokenizer;
import org.browsermob.proxy.jetty.util.TypeUtil;
import java.io.*;
import java.util.*;
/* ------------------------------------------------------------ */
/** HTTP Message base.
* This class forms the basis of HTTP requests and replies. It provides
* header fields, content and optional trailer fields, while managing the
* state of the message.
*
* @version $Id: HttpMessage.java,v 1.41 2006/04/04 22:28:02 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public abstract class HttpMessage
{
private static Log log = LogFactory.getLog(HttpMessage.class);
/* ------------------------------------------------------------ */
public final static String __SCHEME ="http";
public final static String __SSL_SCHEME ="https";
/* ------------------------------------------------------------ */
public final static String __HTTP_0_9 ="HTTP/0.9";
public final static String __HTTP_1_0 ="HTTP/1.0";
public final static String __HTTP_1_1 ="HTTP/1.1";
public final static String __HTTP_1_X ="HTTP/1.";
/* ------------------------------------------------------------ */
public interface HeaderWriter
{
void writeHeader(HttpMessage httpMessage)
throws IOException;
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/** Message States.
*/
public final static int
__MSG_EDITABLE=0, // Created locally, all set methods enabled
__MSG_BAD=1, // Bad message/
__MSG_RECEIVED=2, // Received from connection.
__MSG_SENDING=3, // Headers sent.
__MSG_SENT=4; // Entity and trailers sent.
public final static String[] __state =
{
"EDITABLE",
"BAD",
"RECEIVED",
"SENDING",
"SENT"
};
/* ------------------------------------------------------------ */
protected int _state=__MSG_EDITABLE;
protected String _version;
protected int _dotVersion;
protected HttpFields _header=new HttpFields();
protected HttpConnection _connection;
protected String _characterEncoding;
protected String _mimeType;
protected Object _wrapper;
protected Map _attributes;
/* ------------------------------------------------------------ */
/** Constructor.
*/
protected HttpMessage()
{}
/* ------------------------------------------------------------ */
/** Constructor.
*/
protected HttpMessage(HttpConnection connection)
{
_connection=connection;
}
/* ------------------------------------------------------------ */
/** Set a wrapper object.
* A wrapper object is an object associated with this message and
* presents it with an different interface. The
* primary example of a HttpRequest facade is ServletHttpRequest.
* A single facade object may be associated with the message with
* this call and retrieved with the getFacade method.
*/
public void setWrapper(Object wrapper)
{
_wrapper=wrapper;
}
/* ------------------------------------------------------------ */
/** Get an associated wrapper object.
* @return Wrapper message or null.
*/
public Object getWrapper()
{
return _wrapper;
}
/* ------------------------------------------------------------ */
protected void reset()
{
_state=__MSG_EDITABLE;
_header.clear();
}
/* ------------------------------------------------------------ */
public HttpConnection getHttpConnection()
{
return _connection;
}
/* ------------------------------------------------------------ */
public InputStream getInputStream()
{
if (_connection==null)
return null;
return _connection.getInputStream();
}
/* ------------------------------------------------------------ */
public OutputStream getOutputStream()
{
if (_connection==null)
return null;
return _connection.getOutputStream();
}
/* ------------------------------------------------------------ */
/** Get the message state.
*
* __MSG_EDITABLE = 0 - Created locally, all set methods enabled
* __MSG_BAD = 1 - Bad message or send failure.
* __MSG_RECEIVED = 2 - Received from connection.
* __MSG_SENDING = 3 - Headers sent.
* __MSG_SENT = 4 - Entity and trailers sent.
*
* @return the state.
*/
public int getState()
{
return _state;
}
/* ------------------------------------------------------------ */
/** Set the message state.
* This method should be used by experts only as it can prevent
* normal handling of a request/response.
* @param state The new state
* @return the last state.
*/
public int setState(int state)
{
int last=_state;
_state=state;
return last;
}
/* ------------------------------------------------------------ */
/** Get the protocol version.
* @return return the version.
*/
public String getVersion()
{
return _version;
}
/* ------------------------------------------------------------ */
/** Get the protocol version.
* @return return the version dot (0.9=-1 1.0=0 1.1=1)
*/
public int getDotVersion()
{
return _dotVersion;
}
/* ------------------------------------------------------------ */
/** Get field names.
* @return Enumeration of Field Names
*/
public Enumeration getFieldNames()
{
return _header.getFieldNames();
}
/* ------------------------------------------------------------ */
/** Does the header or trailer contain a field?
* @param name Name of the field
* @return True if contained in header or trailer.
*/
public boolean containsField(String name)
{
return _header.containsKey(name);
}
/* ------------------------------------------------------------ */
/** Get a message field.
* Get a field from a message header. If no header field is found,
* trailer fields are searched.
* @param name The field name
* @return field value or null
*/
public String getField(String name)
{
return _header.get(name);
}
/* ------------------------------------------------------------ */
/** Get a multi valued message field.
* Get a field from a message header.
* @param name The field name
* @return Enumeration of field values or null
*/
public Enumeration getFieldValues(String name)
{
return _header.getValues(name);
}
/* ------------------------------------------------------------ */
/** Get a multi valued message field.
* Get a field from a message header.
* @param name The field name
* @param separators String of separators.
* @return Enumeration of field values or null
*/
public Enumeration getFieldValues(String name,String separators)
{
return _header.getValues(name,separators);
}
/* ------------------------------------------------------------ */
/** Set a field value.
* If the message is editable, then a header field is set. Otherwise
* if the message is sending and a HTTP/1.1 version, then a trailer
* field is set.
* @param name Name of field
* @param value New value of field
* @return Old value of field
*/
public String setField(String name, String value)
{
if (_state!=__MSG_EDITABLE)
return null;
if (HttpFields.__ContentType.equalsIgnoreCase(name))
{
String old=_header.get(name);
setContentType(value);
return old;
}
return _header.put(name,value);
}
/* ------------------------------------------------------------ */
/** Set a multi-value field value.
* If the message is editable, then a header field is set. Otherwise
* if the meesage is sending and a HTTP/1.1 version, then a trailer
* field is set.
* @param name Name of field
* @param value New values of field
*/
public void setField(String name, List value)
{
if (_state!=__MSG_EDITABLE)
return;
_header.put(name,value);
}
/* ------------------------------------------------------------ */
/** Add to a multi-value field value.
* If the message is editable, then a header field is set. Otherwise
* if the meesage is sending and a HTTP/1.1 version, then a trailer
* field is set.
* @param name Name of field
* @param value New value to add to the field
* @exception IllegalStateException Not editable or sending 1.1
* with trailers
*/
public void addField(String name, String value)
throws IllegalStateException
{
if (_state!=__MSG_EDITABLE)
return;
_header.add(name,value);
}
/* -------------------------------------------------------------- */
/** Get a field as an integer value.
* Look in header and trailer fields.
* Returns the value of an integer field, or -1 if not found.
* The case of the field name is ignored.
* @param name the case-insensitive field name
*/
public int getIntField(String name)
{
return _header.getIntField(name);
}
/* -------------------------------------------------------------- */
/** Sets the value of an integer field.
* Header or Trailer fields are set depending on message state.
* @param name the field name
* @param value the field integer value
*/
public void setIntField(String name, int value)
{
if (_state!=__MSG_EDITABLE)
return;
_header.put(name, TypeUtil.toString(value));
}
/* -------------------------------------------------------------- */
/** Adds the value of an integer field.
* Header or Trailer fields are set depending on message state.
* @param name the field name
* @param value the field integer value
*/
public void addIntField(String name, int value)
{
if (_state!=__MSG_EDITABLE)
return;
_header.add(name, TypeUtil.toString(value));
}
/* -------------------------------------------------------------- */
/** Get a header as a date value.
* Look in header and trailer fields.
* Returns the value of a date field, or -1 if not found.
* The case of the field name is ignored.
* @param name the case-insensitive field name
*/
public long getDateField(String name)
{
return _header.getDateField(name);
}
/* -------------------------------------------------------------- */
/** Sets the value of a date field.
* Header or Trailer fields are set depending on message state.
* @param name the field name
* @param date the field date value
*/
public void setDateField(String name, Date date)
{
if (_state!=__MSG_EDITABLE)
return;
_header.putDateField(name,date);
}
/* -------------------------------------------------------------- */
/** Adds the value of a date field.
* Header or Trailer fields are set depending on message state.
* @param name the field name
* @param date the field date value
*/
public void addDateField(String name, Date date)
{
if (_state!=__MSG_EDITABLE)
return;
_header.addDateField(name,date);
}
/* -------------------------------------------------------------- */
/** Sets the value of a date field.
* Header or Trailer fields are set depending on message state.
* @param name the field name
* @param date the field date value
*/
public void setDateField(String name, long date)
{
if (_state!=__MSG_EDITABLE)
return;
_header.putDateField(name,date);
}
/* -------------------------------------------------------------- */
/** Add the value of a date field.
* Header or Trailer fields are set depending on message state.
* @param name the field name
* @param date the field date value
* @exception IllegalStateException Not editable or sending 1.1
* with trailers
*/
public void addDateField(String name, long date)
{
if (_state!=__MSG_EDITABLE)
return;
_header.addDateField(name,date);
}
/* ------------------------------------------------------------ */
/** Remove a field.
* If the message is editable, then a header field is removed. Otherwise
* if the message is sending and a HTTP/1.1 version, then a trailer
* field is removed.
* @param name Name of field
* @return Old value of field
*/
public String removeField(String name)
throws IllegalStateException
{
if (_state!=__MSG_EDITABLE)
return null;
return _header.remove(name);
}
/* ------------------------------------------------------------ */
/** Set the request version
* @param version the HTTP version string (eg HTTP/1.1)
* @exception IllegalStateException message is not EDITABLE
*/
public void setVersion(String version)
{
if (_state!=__MSG_EDITABLE)
throw new IllegalStateException("Not EDITABLE");
if (version.equalsIgnoreCase(__HTTP_1_1))
{
_dotVersion=1;
_version=__HTTP_1_1;
}
else if (version.equalsIgnoreCase(__HTTP_1_0))
{
_dotVersion=0;
_version=__HTTP_1_0;
}
else if (version.equalsIgnoreCase(__HTTP_0_9))
{
_dotVersion=-1;
_version=__HTTP_0_9;
}
else
throw new IllegalArgumentException("Unknown version");
}
/* ------------------------------------------------------------ */
/** Get the HTTP header fields.
* @return Header or null
*/
public HttpFields getHeader()
{
if (_state!=__MSG_EDITABLE)
throw new IllegalStateException("Can't get header in "+__state[_state]);
return _header;
}
/* -------------------------------------------------------------- */
public int getContentLength()
{
return getIntField(HttpFields.__ContentLength);
}
/* ------------------------------------------------------------ */
public void setContentLength(int len)
{
setIntField(HttpFields.__ContentLength,len);
}
/* -------------------------------------------------------------- */
/** Character Encoding.
* The character encoding is extracted from the ContentType field
* when set.
* @return Character Encoding or null
*/
public String getCharacterEncoding()
{
return _characterEncoding;
}
/* ------------------------------------------------------------ */
/** Set Character Encoding.
* @param encoding An encoding that can override the encoding set
* from the ContentType field.
*/
public void setCharacterEncoding(String encoding,boolean setField)
{
if (isCommitted())
return;
if (encoding==null)
{
// Clear any encoding.
if (_characterEncoding!=null)
{
_characterEncoding=null;
if (setField)
_header.put(HttpFields.__ContentType,_mimeType);
}
}
else
{
// No, so just add this one to the mimetype
_characterEncoding=encoding;
if (setField && _mimeType!=null)
{
_header.put(HttpFields.__ContentType,
_mimeType+";charset="+
QuotedStringTokenizer.quote(_characterEncoding,";= "));
}
}
}
/* -------------------------------------------------------------- */
public String getContentType()
{
return getField(HttpFields.__ContentType);
}
/* ------------------------------------------------------------ */
public void setContentType(String contentType)
{
if (isCommitted())
return;
if (contentType==null)
{
_mimeType=null;
_header.remove(HttpFields.__ContentType);
}
else
{
// Look for encoding in contentType
int i0=contentType.indexOf(';');
if (i0>0)
{
// Strip params off mimetype
_mimeType=contentType.substring(0,i0).trim();
// Look for charset
int i1=contentType.indexOf("charset=",i0);
if (i1>=0)
{
i1+=8;
int i2 = contentType.indexOf(' ',i1);
_characterEncoding = (00)
{
// Strip params off mimetype
_mimeType=contentType.substring(0,i0).trim();
// Look for charset
int i1=contentType.indexOf("charset=",i0);
if (i1>=0)
{
i1+=8;
int i2 = contentType.indexOf(' ',i1);
_characterEncoding = (0