/****************************************************************** * * CyberHTTP for Java * * Copyright (C) Satoshi Konno 2002-2004 * * File: HTTPConnection.java * * Revision; * * 11/18/02 * - first revision. * 09/02/03 * - Giordano Sassaroli * - 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" * - 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 (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 * - Changed hasContentRange() to check Content-Range and Range header. * - Added support for Range header to getContentRange(). * 02/02/05 * - Mark Retallack * - 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. * *******************************************************************/ package org.cybergarage.http; import java.io.*; import java.util.*; import org.cybergarage.net.*; import org.cybergarage.util.*; import java.util.Calendar; public class HTTPPacket { //////////////////////////////////////////////// // Constructor //////////////////////////////////////////////// public HTTPPacket() { setVersion(HTTP.VERSION); setContentInputStream(null); } public HTTPPacket(HTTPPacket httpPacket) { setVersion(HTTP.VERSION); set(httpPacket); setContentInputStream(null); } public HTTPPacket(InputStream in) { setVersion(HTTP.VERSION); set(in); setContentInputStream(null); } //////////////////////////////////////////////// // init //////////////////////////////////////////////// public void init() { setFirstLine(""); clearHeaders(); setContent(new byte[0], false); setContentInputStream(null); } //////////////////////////////////////////////// // Version //////////////////////////////////////////////// private String version; public void setVersion(String ver) { version = ver; } public String getVersion() { return version; } //////////////////////////////////////////////// // set //////////////////////////////////////////////// protected boolean set(InputStream in, boolean onlyHeaders) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String firstLine = reader.readLine(); if (firstLine == null || firstLine.length() <= 0) return false; setFirstLine(firstLine); // Thanks for Giordano Sassaroli (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 = reader.readLine(); while ((headerLine != null) && (0 < headerLine.length()) ) { HTTPHeader header = new HTTPHeader(headerLine); if (header.hasName() == true) setHeader(header); headerLine = reader.readLine(); } //look forward another first line String actualFirstLine = reader.readLine(); if ((actualFirstLine != null) && (0 < actualFirstLine.length()) ) { //this is the actual first line setFirstLine(actualFirstLine); }else{ return true; } } String headerLine = reader.readLine(); while ((headerLine != null) && (0 < headerLine.length()) ) { HTTPHeader header = new HTTPHeader(headerLine); if (header.hasName() == true) setHeader(header); headerLine = reader.readLine(); } if (onlyHeaders == true) { setContent("", false); return true; } boolean isChunkedRequest = isChunked(); long contentLen = 0; if (isChunkedRequest == true) { try { String chunkSizeLine = reader.readLine(); contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(), 0, chunkSizeLine.length()-2)); } catch (Exception e) {}; } else contentLen = getContentLength(); StringBuilder contentBuf = new StringBuilder(); while (0 < contentLen) { int chunkSize = HTTP.getChunkSize(); char readBuf[] = new char[chunkSize]; 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.append(new String(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 = reader.readLine(); contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(), 0, chunkSizeLine.length()-2)); } catch (Exception e) { contentLen = 0; }; } else contentLen = 0; } // Thanks for Ralf G. R. Bergs (02/09/04) String contentStr = contentBuf.toString(); setContent(contentStr.getBytes(), false); } catch (Exception e) { Debug.warning(e); return false; } return true; } 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 0) ? true : false; } //////////////////////////////////////////////// // Contents (InputStream) //////////////////////////////////////////////// private InputStream contentInput = null; public void setContentInputStream(InputStream in) { contentInput = in; } public InputStream getContentInputStream() { return contentInput; } public boolean hasContentInputStream() { return (contentInput != null) ? true : false; } //////////////////////////////////////////////// // ContentType //////////////////////////////////////////////// public void setContentType(String type) { setHeader(HTTP.CONTENT_TYPE, type); } public String getContentType() { return getHeaderValue(HTTP.CONTENT_TYPE); } //////////////////////////////////////////////// // ContentLength //////////////////////////////////////////////// public void setContentLength(long len) { setLongHeader(HTTP.CONTENT_LENGTH, len); } public long getContentLength() { return getLongHeaderValue(HTTP.CONTENT_LENGTH); } //////////////////////////////////////////////// // Connection //////////////////////////////////////////////// public boolean hasConnection() { return hasHeader(HTTP.CONNECTION); } public void setConnection(String value) { setHeader(HTTP.CONNECTION, value); } public String getConnection() { return getHeaderValue(HTTP.CONNECTION); } 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; 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 void setCacheControl(int value) { setCacheControl(HTTP.MAX_AGE, value); } public String getCacheControl() { return getHeaderValue(HTTP.CACHE_CONTROL); } //////////////////////////////////////////////// // Server //////////////////////////////////////////////// public void setServer(String name) { setHeader(HTTP.SERVER, name); } public String getServer() { return getHeaderValue(HTTP.SERVER); } //////////////////////////////////////////////// // 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); } //////////////////////////////////////////////// // Date //////////////////////////////////////////////// public void setDate(Calendar cal) { Date date = new Date(cal); setHeader(HTTP.DATE, date.getDateString()); } public String getDate() { return getHeaderValue(HTTP.DATE); } //////////////////////////////////////////////// // Connection //////////////////////////////////////////////// public boolean hasTransferEncoding() { return hasHeader(HTTP.TRANSFER_ENCODING); } public void setTransferEncoding(String value) { setHeader(HTTP.TRANSFER_ENCODING, value); } public String getTransferEncoding() { return getHeaderValue(HTTP.TRANSFER_ENCODING); } 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; } */ }