/******************************************************************
*
* CyberHTTP for Java
*
* Copyright (C) Satoshi Konno 2002-2004
*
* File: HTTPConnection.java
*
* Revision;
*
* 11/18/02 - first revision. 09/02/03 - Giordano Sassaroli
* <sassarol@cefriel.it> - Problem : The API is unable to receive responses from
* the Microsoft UPnP stack - Error : the Microsoft UPnP stack is based on ISAPI
* on IIS, and whenever IIS receives a post request, it answers with two
* responses: the first one has no body and it is a code 100 (continue)
* response, which has to be ignored. The second response is the actual one and
* should be parsed as the response. 02/09/04 - Ralf G. R. Bergs" <Ralf@Ber.gs>
* - Why do you strip leading and trailing white space from the response body? -
* Disabled to trim the content string. 03/11/04 - Added some methods about
* InputStream content. setContentInputStream(), getContentInputStream() and
* hasContentInputStream(). 03/16/04 - Thanks for Darrell Young - Added
* setVersion() and getVersion(); 03/17/04 - Added hasFirstLine(); 05/26/04 -
* Jan Newmarch <jan.newmarch@infotech.monash.edu.au> (05/26/04) - Changed
* setCacheControl() and getChcheControl(); 08/25/04 - Added the following
* methods. hasContentRange(), setContentRange(), getContentRange(),
* getContentRangeFirstPosition(), getContentRangeLastPosition() and
* getContentRangeInstanceLength() 08/26/04 - Added the following methods.
* hasConnection(), setConnection(), getConnection(), isCloseConnection() and
* isKeepAliveConnection() 08/27/04 - Added a updateWithContentLength paramger
* to setContent(). - Changed to HTTPPacket::set() not to change the header of
* Content-Length. 08/28/04 - Added init() and read(). 09/19/04 - Added a
* onlyHeaders parameter to set(). 10/20/04 - Brent Hills
* <bhills@openshores.com> - Changed hasContentRange() to check Content-Range
* and Range header. - Added support for Range header to getContentRange().
* 02/02/05 - Mark Retallack <mretallack@users.sourceforge.net> - Fixed set()
* not to read over the content length when the stream is keep alive. 02/28/05 -
* Added the following methods for chunked stream support.
* hasTransferEncoding(), setTransferEncoding(), getTransferEncoding(),
* isChunked(). 03/02/05 - Changed post() to suppot chunked stream. 06/11/05 -
* Added setHost(). 07/07/05 - Lee Peik Feng <pflee@users.sourceforge.net> -
* Andrey Ovchar <AOvchar@consultitnow.com> - Fixed set() to parse the chunk
* size as a hex string. 11/02/05 - Changed set() to use BufferedInputStream
* instead of BufferedReader to get the content as a byte stream. 11/06/05 -
* Added getCharSet(). - Changed getContentString() to return the content string
* using the charset.
*
*******************************************************************/
package com.guo.duoduo.airplayreceiver.http;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.util.Calendar;
import java.util.StringTokenizer;
import java.util.Vector;
import com.guo.duoduo.airplayreceiver.utils.Debug;
import com.guo.duoduo.airplayreceiver.utils.HostInterface;
import com.guo.duoduo.airplayreceiver.utils.StringUtil;
public class HTTPPacket
{
////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////
private String version;
private String firstLine = "";
private Vector httpHeaderList = new Vector();
////////////////////////////////////////////////
// init
////////////////////////////////////////////////
private byte content[] = new byte[0];
////////////////////////////////////////////////
// Version
////////////////////////////////////////////////
private InputStream contentInput = null;
public HTTPPacket()
{
setVersion(HTTP.VERSION);
setContentInputStream(null);
}
public HTTPPacket(HTTPPacket httpPacket)
{
setVersion(HTTP.VERSION);
set(httpPacket);
setContentInputStream(null);
}
////////////////////////////////////////////////
// set
////////////////////////////////////////////////
public HTTPPacket(InputStream in)
{
setVersion(HTTP.VERSION);
set(in);
setContentInputStream(null);
}
public void init()
{
setFirstLine("");
clearHeaders();
setContent(new byte[0], false);
setContentInputStream(null);
}
public String getVersion()
{
return version;
}
public void setVersion(String ver)
{
version = ver;
}
private String readLine(BufferedInputStream in)
{
ByteArrayOutputStream lineBuf = new ByteArrayOutputStream();
byte readBuf[] = new byte[1];
try
{
int readLen = in.read(readBuf);
while (0 < readLen)
{
if (readBuf[0] == HTTP.LF)
break;
if (readBuf[0] != HTTP.CR)
lineBuf.write(readBuf[0]);
readLen = in.read(readBuf);
}
}
catch (InterruptedIOException e)
{
//Ignoring warning because it's a way to break the HTTP connecttion
//TODO Create a new level of Logging and log the event
}
catch (IOException e)
{
Debug.warning(e);
}
return lineBuf.toString();
}
////////////////////////////////////////////////
// read
////////////////////////////////////////////////
protected boolean set(InputStream in, boolean onlyHeaders)
{
try
{
BufferedInputStream reader = new BufferedInputStream(in);
String firstLine = readLine(reader);
if (firstLine == null || firstLine.length() <= 0)
return false;
setFirstLine(firstLine);
// Thanks for Giordano Sassaroli <sassarol@cefriel.it> (09/03/03)
HTTPStatus httpStatus = new HTTPStatus(firstLine);
int statCode = httpStatus.getStatusCode();
if (statCode == HTTPStatus.CONTINUE)
{
//ad hoc code for managing iis non-standard behaviour
//iis sends 100 code response and a 200 code response in the same
//stream, so the code should check the presence of the actual
//response in the stream.
//skip all header lines
String headerLine = readLine(reader);
while ((headerLine != null) && (0 < headerLine.length()))
{
HTTPHeader header = new HTTPHeader(headerLine);
if (header.hasName() == true)
setHeader(header);
headerLine = readLine(reader);
}
//look forward another first line
String actualFirstLine = readLine(reader);
if ((actualFirstLine != null) && (0 < actualFirstLine.length()))
{
//this is the actual first line
setFirstLine(actualFirstLine);
}
else
{
return true;
}
}
String headerLine = readLine(reader);
while ((headerLine != null) && (0 < headerLine.length()))
{
HTTPHeader header = new HTTPHeader(headerLine);
if (header.hasName() == true)
setHeader(header);
headerLine = readLine(reader);
}
if (onlyHeaders == true)
{
setContent("", false);
return true;
}
boolean isChunkedRequest = isChunked();
long contentLen = 0;
if (isChunkedRequest == true)
{
try
{
String chunkSizeLine = readLine(reader);
// Thanks for Lee Peik Feng <pflee@users.sourceforge.net> (07/07/05)
//contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(), 0, chunkSizeLine.length()-2), 16);
contentLen = (chunkSizeLine != null) ? Long.parseLong(
chunkSizeLine.trim(), 16) : 0;
}
catch (Exception e)
{
};
}
else
contentLen = getContentLength();
ByteArrayOutputStream contentBuf = new ByteArrayOutputStream();
while (0 < contentLen)
{
int chunkSize = HTTP.getChunkSize();
/* Thanks for Stephan Mehlhase (2010-10-26) */
byte readBuf[] = new byte[(int) (contentLen > chunkSize
? chunkSize
: contentLen)];
long readCnt = 0;
while (readCnt < contentLen)
{
try
{
// Thanks for Mark Retallack (02/02/05)
long bufReadLen = contentLen - readCnt;
if (chunkSize < bufReadLen)
bufReadLen = chunkSize;
int readLen = reader.read(readBuf, 0, (int) bufReadLen);
if (readLen < 0)
break;
contentBuf.write(readBuf, 0, readLen);
readCnt += readLen;
}
catch (Exception e)
{
Debug.warning(e);
break;
}
}
if (isChunkedRequest == true)
{
// skip CRLF
long skipLen = 0;
do
{
long skipCnt = reader.skip(HTTP.CRLF.length() - skipLen);
if (skipCnt < 0)
break;
skipLen += skipCnt;
} while (skipLen < HTTP.CRLF.length());
// read next chunk size
try
{
String chunkSizeLine = readLine(reader);
// Thanks for Lee Peik Feng <pflee@users.sourceforge.net> (07/07/05)
contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(),
0, chunkSizeLine.length() - 2), 16);
}
catch (Exception e)
{
contentLen = 0;
};
}
else
contentLen = 0;
}
setContent(contentBuf.toByteArray(), false);
}
catch (Exception e)
{
Debug.warning(e);
return false;
}
return true;
}
////////////////////////////////////////////////
// String
////////////////////////////////////////////////
protected boolean set(InputStream in)
{
return set(in, false);
}
protected boolean set(HTTPSocket httpSock)
{
return set(httpSock.getInputStream());
}
protected void set(HTTPPacket httpPacket)
{
setFirstLine(httpPacket.getFirstLine());
clearHeaders();
int nHeaders = httpPacket.getNHeaders();
for (int n = 0; n < nHeaders; n++)
{
HTTPHeader header = httpPacket.getHeader(n);
addHeader(header);
}
setContent(httpPacket.getContent());
}
public boolean read(HTTPSocket httpSock)
{
init();
return set(httpSock);
}
protected String getFirstLine()
{
return firstLine;
}
////////////////////////////////////////////////
// Header
////////////////////////////////////////////////
private void setFirstLine(String value)
{
firstLine = value;
}
protected String getFirstLineToken(int num)
{
StringTokenizer st = new StringTokenizer(firstLine, HTTP.REQEST_LINE_DELIM);
String lastToken = "";
for (int n = 0; n <= num; n++)
{
if (st.hasMoreTokens() == false)
return "";
lastToken = st.nextToken();
}
return lastToken;
}
public boolean hasFirstLine()
{
return (0 < firstLine.length()) ? true : false;
}
public int getNHeaders()
{
return httpHeaderList.size();
}
public void addHeader(HTTPHeader header)
{
httpHeaderList.add(header);
}
public void addHeader(String name, String value)
{
HTTPHeader header = new HTTPHeader(name, value);
httpHeaderList.add(header);
}
public HTTPHeader getHeader(int n)
{
return (HTTPHeader) httpHeaderList.get(n);
}
public HTTPHeader getHeader(String name)
{
int nHeaders = getNHeaders();
for (int n = 0; n < nHeaders; n++)
{
HTTPHeader header = getHeader(n);
String headerName = header.getName();
if (headerName.equalsIgnoreCase(name) == true)
return header;
}
return null;
}
public void clearHeaders()
{
httpHeaderList.clear();
httpHeaderList = new Vector();
}
public boolean hasHeader(String name)
{
return (getHeader(name) != null) ? true : false;
}
public void setHeader(String name, String value)
{
HTTPHeader header = getHeader(name);
if (header != null)
{
header.setValue(value);
return;
}
addHeader(name, value);
}
public void setHeader(String name, int value)
{
setHeader(name, Integer.toString(value));
}
public void setHeader(String name, long value)
{
setHeader(name, Long.toString(value));
}
////////////////////////////////////////////////
// set*Value
////////////////////////////////////////////////
public void setHeader(HTTPHeader header)
{
setHeader(header.getName(), header.getValue());
}
public String getHeaderValue(String name)
{
HTTPHeader header = getHeader(name);
if (header == null)
return "";
return header.getValue();
}
public void setStringHeader(String name, String value, String startWidth,
String endWidth)
{
String headerValue = value;
if (headerValue.startsWith(startWidth) == false)
headerValue = startWidth + headerValue;
if (headerValue.endsWith(endWidth) == false)
headerValue = headerValue + endWidth;
setHeader(name, headerValue);
}
public void setStringHeader(String name, String value)
{
setStringHeader(name, value, "\"", "\"");
}
public String getStringHeaderValue(String name, String startWidth, String endWidth)
{
String headerValue = getHeaderValue(name);
if (headerValue.startsWith(startWidth) == true)
headerValue = headerValue.substring(1, headerValue.length());
if (headerValue.endsWith(endWidth) == true)
headerValue = headerValue.substring(0, headerValue.length() - 1);
return headerValue;
}
public String getStringHeaderValue(String name)
{
return getStringHeaderValue(name, "\"", "\"");
}
public void setIntegerHeader(String name, int value)
{
setHeader(name, Integer.toString(value));
}
public void setLongHeader(String name, long value)
{
setHeader(name, Long.toString(value));
}
////////////////////////////////////////////////
// getHeader
////////////////////////////////////////////////
public int getIntegerHeaderValue(String name)
{
HTTPHeader header = getHeader(name);
if (header == null)
return 0;
return StringUtil.toInteger(header.getValue());
}
////////////////////////////////////////////////
// Contents
////////////////////////////////////////////////
public long getLongHeaderValue(String name)
{
HTTPHeader header = getHeader(name);
if (header == null)
return 0;
return StringUtil.toLong(header.getValue());
}
public String getHeaderString()
{
StringBuffer str = new StringBuffer();
int nHeaders = getNHeaders();
for (int n = 0; n < nHeaders; n++)
{
HTTPHeader header = getHeader(n);
str.append(header.getName() + ": " + header.getValue() + HTTP.CRLF);
}
return str.toString();
}
public void setContent(byte data[], boolean updateWithContentLength)
{
content = data;
if (updateWithContentLength == true)
setContentLength(data.length);
}
public void setContent(byte data[])
{
setContent(data, true);
}
public void setContent(String data, boolean updateWithContentLength)
{
setContent(data.getBytes(), updateWithContentLength);
}
public byte[] getContent()
{
return content;
}
public void setContent(String data)
{
setContent(data, true);
}
public String getContentString()
{
String charSet = getCharSet();
if (charSet == null || charSet.length() <= 0)
return new String(content);
try
{
return new String(content, charSet);
}
catch (Exception e)
{
Debug.warning(e);
}
return new String(content);
}
////////////////////////////////////////////////
// Contents (InputStream)
////////////////////////////////////////////////
public boolean hasContent()
{
return (content.length > 0) ? true : false;
}
public InputStream getContentInputStream()
{
return contentInput;
}
public void setContentInputStream(InputStream in)
{
contentInput = in;
}
public boolean hasContentInputStream()
{
return (contentInput != null) ? true : false;
}
////////////////////////////////////////////////
// ContentType
////////////////////////////////////////////////
public String getContentType()
{
return getHeaderValue(HTTP.CONTENT_TYPE);
}
public void setContentType(String type)
{
setHeader(HTTP.CONTENT_TYPE, type);
}
////////////////////////////////////////////////
// ContentLanguage
////////////////////////////////////////////////
public String getContentLanguage()
{
return getHeaderValue(HTTP.CONTENT_LANGUAGE);
}
public void setContentLanguage(String code)
{
setHeader(HTTP.CONTENT_LANGUAGE, code);
}
////////////////////////////////////////////////
// Charset
////////////////////////////////////////////////
public String getCharSet()
{
String contentType = getContentType();
if (contentType == null)
return "";
contentType = contentType.toLowerCase();
int charSetIdx = contentType.indexOf(HTTP.CHARSET);
if (charSetIdx < 0)
return "";
int charSetEndIdx = charSetIdx + HTTP.CHARSET.length() + 1;
String charSet = new String(contentType.getBytes(), charSetEndIdx,
(contentType.length() - charSetEndIdx));
if (charSet.length() < 0)
return "";
if (charSet.charAt(0) == '\"')
charSet = charSet.substring(1, (charSet.length() - 1));
if (charSet.length() < 0)
return "";
if (charSet.charAt((charSet.length() - 1)) == '\"')
charSet = charSet.substring(0, (charSet.length() - 1));
return charSet;
}
////////////////////////////////////////////////
// ContentLength
////////////////////////////////////////////////
public long getContentLength()
{
return getLongHeaderValue(HTTP.CONTENT_LENGTH);
}
public void setContentLength(long len)
{
setLongHeader(HTTP.CONTENT_LENGTH, len);
}
////////////////////////////////////////////////
// Connection
////////////////////////////////////////////////
public boolean hasConnection()
{
return hasHeader(HTTP.CONNECTION);
}
public String getConnection()
{
return getHeaderValue(HTTP.CONNECTION);
}
public void setConnection(String value)
{
setHeader(HTTP.CONNECTION, value);
}
public boolean isCloseConnection()
{
if (hasConnection() == false)
return false;
String connection = getConnection();
if (connection == null)
return false;
return connection.equalsIgnoreCase(HTTP.CLOSE);
}
public boolean isKeepAliveConnection()
{
if (hasConnection() == false)
return false;
String connection = getConnection();
if (connection == null)
return false;
return connection.equalsIgnoreCase(HTTP.KEEP_ALIVE);
}
////////////////////////////////////////////////
// ContentRange
////////////////////////////////////////////////
public boolean hasContentRange()
{
return (hasHeader(HTTP.CONTENT_RANGE) || hasHeader(HTTP.RANGE));
}
public void setContentRange(long firstPos, long lastPos, long length)
{
String rangeStr = "";
rangeStr += HTTP.CONTENT_RANGE_BYTES + " ";
rangeStr += Long.toString(firstPos) + "-";
rangeStr += Long.toString(lastPos) + "/";
rangeStr += ((0 < length) ? Long.toString(length) : "*");
setHeader(HTTP.CONTENT_RANGE, rangeStr);
}
public long[] getContentRange()
{
long range[] = new long[3];
range[0] = range[1] = range[2] = 0;
if (hasContentRange() == false)
return range;
String rangeLine = getHeaderValue(HTTP.CONTENT_RANGE);
// Thanks for Brent Hills (10/20/04)
if (rangeLine.length() <= 0)
rangeLine = getHeaderValue(HTTP.RANGE);
if (rangeLine.length() <= 0)
return range;
// Thanks for Brent Hills (10/20/04)
StringTokenizer strToken = new StringTokenizer(rangeLine, " =");
// Skip bytes
if (strToken.hasMoreTokens() == false)
return range;
String bytesStr = strToken.nextToken(" ");
// Get first-byte-pos
if (strToken.hasMoreTokens() == false)
return range;
String firstPosStr = strToken.nextToken(" -");
try
{
range[0] = Long.parseLong(firstPosStr);
}
catch (NumberFormatException e)
{
};
if (strToken.hasMoreTokens() == false)
return range;
String lastPosStr = strToken.nextToken("-/");
try
{
range[1] = Long.parseLong(lastPosStr);
}
catch (NumberFormatException e)
{
};
if (strToken.hasMoreTokens() == false)
return range;
String lengthStr = strToken.nextToken("/");
try
{
range[2] = Long.parseLong(lengthStr);
}
catch (NumberFormatException e)
{
};
return range;
}
public long getContentRangeFirstPosition()
{
long range[] = getContentRange();
return range[0];
}
public long getContentRangeLastPosition()
{
long range[] = getContentRange();
return range[1];
}
public long getContentRangeInstanceLength()
{
long range[] = getContentRange();
return range[2];
}
////////////////////////////////////////////////
// CacheControl
////////////////////////////////////////////////
public void setCacheControl(String directive)
{
setHeader(HTTP.CACHE_CONTROL, directive);
}
public void setCacheControl(String directive, int value)
{
String strVal = directive + "=" + Integer.toString(value);
setHeader(HTTP.CACHE_CONTROL, strVal);
}
public String getCacheControl()
{
return getHeaderValue(HTTP.CACHE_CONTROL);
}
public void setCacheControl(int value)
{
setCacheControl(HTTP.MAX_AGE, value);
}
////////////////////////////////////////////////
// Server
////////////////////////////////////////////////
public String getServer()
{
return getHeaderValue(HTTP.SERVER);
}
public void setServer(String name)
{
setHeader(HTTP.SERVER, name);
}
////////////////////////////////////////////////
// Host
////////////////////////////////////////////////
public void setHost(String host, int port)
{
String hostAddr = host;
if (HostInterface.isIPv6Address(host) == true)
hostAddr = "[" + host + "]";
setHeader(HTTP.HOST, hostAddr + ":" + Integer.toString(port));
}
public String getHost()
{
return getHeaderValue(HTTP.HOST);
}
public void setHost(String host)
{
String hostAddr = host;
if (HostInterface.isIPv6Address(host) == true)
hostAddr = "[" + host + "]";
setHeader(HTTP.HOST, hostAddr);
}
////////////////////////////////////////////////
// Date
////////////////////////////////////////////////
public String getDate()
{
return getHeaderValue(HTTP.DATE);
}
public void setDate(Calendar cal)
{
Date date = new Date(cal);
setHeader(HTTP.DATE, date.getDateString());
}
////////////////////////////////////////////////
// Connection
////////////////////////////////////////////////
public boolean hasTransferEncoding()
{
return hasHeader(HTTP.TRANSFER_ENCODING);
}
public String getTransferEncoding()
{
return getHeaderValue(HTTP.TRANSFER_ENCODING);
}
public void setTransferEncoding(String value)
{
setHeader(HTTP.TRANSFER_ENCODING, value);
}
public boolean isChunked()
{
if (hasTransferEncoding() == false)
return false;
String transEnc = getTransferEncoding();
if (transEnc == null)
return false;
return transEnc.equalsIgnoreCase(HTTP.CHUNKED);
}
////////////////////////////////////////////////
// set
////////////////////////////////////////////////
/*
* public final static boolean parse(HTTPPacket httpPacket, InputStream in)
* { try { BufferedReader reader = new BufferedReader(new
* InputStreamReader(in)); return parse(httpPacket, reader); } catch
* (Exception e) { Debug.warning(e); } return false; }
*/
}