org.xsocket.connection.http.AbstractMessageHeader Maven / Gradle / Ivy
/*
* Copyright (c) xsocket.org, 2006 - 2008. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xsocket.org/
*/
package org.xsocket.connection.http;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.xsocket.IDataSink;
/**
* Implementation base for a message header
*
* @author [email protected]
*/
abstract class AbstractMessageHeader implements IHttpMessageHeader {
static final String HOST = "Host";
static final String USER_AGENT = "User-Agent";
static final String SERVER = "Server";
static final String CONNECTION = "connection";
static final String KEEP_ALIVE = "Keep-Alive";
static final String CONTENT_LENGTH = "Content-Length";
static final String CONTENT_TYPE = "Content-Type";
static final String TRANSFER_ENCODING = "Transfer-Encoding";
static final String HEADER_ENCODING = "iso-8859-1";
static final String DEFAULT_BODY_ENCODING = "iso-8859-1";
//static final String DEFAULT_BODY_ENCODING = "UTF-8";
private String characterEncoding = DEFAULT_BODY_ENCODING;
private final Map headers = new HashMap();
private boolean isModified = false;
// performance optimization
private int contentLength = -1;
private String contentType = null;
private String transferEncoding = null;
private String connection = null;
private String keepAlive = null;
/**
* returns if the header has been modified
*
* @return true, if the header has been modified
*/
final boolean isHeadersModified() {
return isModified;
}
/**
* {@inheritDoc}
*/
public final int getContentLength() {
return contentLength;
}
/**
* {@inheritDoc}
*/
public final void setContentLength(int length) {
if (contentLength == length) {
return;
}
setHeader(CONTENT_LENGTH, Integer.toString(length));
}
/**
* {@inheritDoc}
*/
public final void setContentType(String type) {
setHeader(CONTENT_TYPE, type);
// contains charset encoding?
String encoding = parseEncoding(type);
if (encoding != null) {
characterEncoding = encoding;
}
}
/**
* {@inheritDoc}
*/
public final String getCharacterEncoding() {
return characterEncoding;
}
/**
* {@inheritDoc}
*/
public final String getContentType() {
return contentType;
}
/**
* {@inheritDoc}
*/
public final String getTransferEncoding() {
return transferEncoding;
}
/**
* {@inheritDoc}
*/
public final void setTransferEncoding(String transferEncoding) {
setHeader(TRANSFER_ENCODING, transferEncoding);
}
/**
* adds a header line in a silence mode
*
* @param line the header line
*/
final void addHeaderLineSilence(String line) {
HeaderLine headerline = new HeaderLine(line);
addHeaderSilence(headerline.getOriginalHeader().toUpperCase(), headerline);
}
/**
* adds a header line
*
* @param line the header line
*/
final void addHeaderLine(String line) {
HeaderLine headerline = new HeaderLine(line);
addHeader(headerline.getOriginalHeader().toUpperCase(), headerline);
}
/**
* copy the headers
*
* @param otherHeader the other header
* @param upperExcludenames the header names to exclude
*/
final void copyHeaderFrom(AbstractMessageHeader otherHeader, String... upperExcludenames) {
ol : for (Entry entry : otherHeader.headers.entrySet()) {
for (String upperExcludename : upperExcludenames) {
if (entry.getKey().equals(upperExcludename)) {
continue ol;
}
}
HeaderLine headerline = entry.getValue();
while (headerline != null) {
addHeader(entry.getKey(), headerline);
headerline = headerline.getMoreLine();
}
}
}
/**
* {@inheritDoc}
*/
public final void addHeader(String headername, String headervalue) {
addHeader(headername.toUpperCase(), new HeaderLine(headername, headervalue));
}
private void addHeader(String upperHeadername, HeaderLine headerLine) {
addHeaderSilence(upperHeadername, headerLine);
isModified = true;
}
private void addHeaderSilence(String upperHeadername, HeaderLine headerLine) {
HeaderLine existingHeaderline = headers.get(upperHeadername);
if (existingHeaderline == null) {
headers.put(upperHeadername, headerLine);
} else {
while (true) {
if (existingHeaderline.getMoreLine() == null) {
existingHeaderline.addMoreLine(headerLine);
break;
} else {
existingHeaderline = existingHeaderline.getMoreLine();
}
}
}
onHeaderAdded(upperHeadername, headerLine.getValue());
}
/**
* call back if a header has been added
*
* @param upperHeadername the header name
* @param headervalue the header value
*/
void onHeaderAdded(String upperHeadername, String headervalue) {
if (upperHeadername.equals("CONTENT-TYPE")) {
contentType = headervalue;
String encoding = parseEncoding(headervalue);
if (encoding != null) {
characterEncoding = encoding;
}
} else if (upperHeadername.equals("CONTENT-LENGTH")) {
contentLength = Integer.parseInt(headervalue);
} else if (upperHeadername.equals("TRANSFER-ENCODING")) {
transferEncoding = headervalue;
} else if (upperHeadername.equals("CONNECTION")) {
connection = headervalue;
} else if (upperHeadername.equals("KEEP-ALIVE")) {
keepAlive = headervalue;
}
}
/**
* {@inheritDoc}
*/
public final void setHeader(String headername, String headervalue) {
removeHeader(headername);
addHeader(headername, headervalue);
}
/**
* {@inheritDoc}
*/
public final void removeHeader(String headername) {
String upperHeadername = headername.toUpperCase();
HeaderLine removedValue = headers.remove(upperHeadername);
if (removedValue != null) {
onHeaderRemoved(upperHeadername);
isModified = true;
}
}
/**
* call back if a header has been removed
*
* @param upperHeadername the header name
*/
void onHeaderRemoved(String upperHeadername) {
if (upperHeadername.equals("CONTENT-TYPE")) {
contentType = null;
characterEncoding = DEFAULT_BODY_ENCODING;
} else if (upperHeadername.equals("CONTENT-LENGTH")) {
contentLength = -1;
} else if (upperHeadername.equals("TRANSFER-ENCODING")) {
transferEncoding = null;
} else if (upperHeadername.equals("CONNECTION")) {
connection = null;
} else if (upperHeadername.equals("KEEP-ALIVE")) {
keepAlive = null;
}
}
/**
* {@inheritDoc}
*/
public final void removeHopByHopHeaders() {
removeHopByHopHeaders("Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "TE",
"Trailers", "Transfer-Encoding", "Upgrade", "Proxy-Connection");
}
/**
* {@inheritDoc}
*/
public final void removeHopByHopHeaders(String... headernames) {
for (String headername : headernames) {
if (headername.equalsIgnoreCase("Connection")) {
String connectionHeader = getHeader("Connection");
if (connectionHeader != null) {
String[] values = connectionHeader.split(",");
for (String value : values) {
value = value.trim();
removeHeader(value);
}
}
}
removeHeader(headername);
}
}
/**
* {@inheritDoc}
*/
public final boolean containsHeader(String headername) {
return headers.containsKey(headername.toUpperCase());
}
/**
* {@inheritDoc}
*/
public final boolean containsHeaderValue(String headername, String headervalue) {
HeaderLine headerline = headers.get(headername.toUpperCase());
while (headerline != null) {
if (headerline.getValue().equalsIgnoreCase(headervalue)) {
return true;
}
headerline = headerline.getMoreLine();
}
return false;
}
/**
* {@inheritDoc}
*/
public final Set getHeaderNameSet() {
Set headernames = new HashSet();
for (HeaderLine headerline : headers.values()) {
headernames.add(headerline.getOriginalHeader());
}
return Collections.unmodifiableSet(headernames);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public final Enumeration getHeaderNames() {
return Collections.enumeration(getHeaderNameSet());
}
/**
* {@inheritDoc}
*/
public final List getHeaderList(String headername) {
List result = new ArrayList();
HeaderLine headerline = headers.get(headername.toUpperCase());
if (headerline != null) {
do {
result.add(headerline.getValue());
headerline = headerline.getMoreLine();
} while(headerline != null);
}
return result;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public final Enumeration getHeaders(String headername) {
return Collections.enumeration(getHeaderList(headername));
}
/**
* {@inheritDoc}
*/
public String getHeader(String headername) {
String upperHeaderName = headername.toUpperCase();
// performance optimization
if (upperHeaderName.equals("CONNECTION") && (connection != null)) {
return connection;
}
if (upperHeaderName.equals("KEEP-ALIVE") && (keepAlive != null)) {
return keepAlive;
}
HeaderLine headerline = headers.get(upperHeaderName);
if (headerline != null) {
return headerline.getValue();
} else {
return null;
}
}
/**
* return the header as string
* @return the headers as string
*/
@SuppressWarnings("unchecked")
final String printHeaders() {
StringBuilder sb = new StringBuilder();
for (HeaderLine headerline : headers.values()) {
sb.append(headerline.toString() + "\r\n");
}
return sb.toString();
}
/**
* writes the header to the given data sink
*
* @param dataSink the data sink
* @return the number of written bytes
* @throws IOException if an exception occurs
*/
public abstract int writeTo(IDataSink dataSink) throws IOException;
/**
* returns if a message body is chunked
*
* @param messageHeader the message header
* @return true, if the body is chunked
*/
static boolean hasChunkedBody(AbstractMessageHeader messageHeader) {
String transferEncoding = messageHeader.getTransferEncoding();
if (transferEncoding == null) {
return false;
} else {
return (transferEncoding.equalsIgnoreCase("chunked"));
}
}
/**
* returns if a message body is defined by length
*
* @param messageHeader the message header
* @return true, if the body is defined by length
*/
static boolean hasBoundBody(AbstractMessageHeader messageHeader) {
return (messageHeader.getContentLength() != -1);
}
/**
* returns if a message body is connection terminated
*
* @param messageHeader the message header
* @return true, if the body is connection terminated
*/
static boolean hasConnectionTerminatedBody(AbstractMessageHeader messageHeader) {
return !hasBoundBody(messageHeader) && !hasChunkedBody(messageHeader) && (messageHeader.getContentType() != null);
}
/**
* parse the encoding of a given content type
* @param contentType the content type
* @return the encoding
*/
static String parseEncoding(String contentType) {
// contains charset encoding?
int pos = contentType.indexOf(';');
if (pos > 0) {
int pos2 = contentType.indexOf("charset=", pos + 1);
if (pos2 > 0) {
String encoding = contentType.substring(pos2 + "charset=".length(), contentType.length()).trim();
return encoding;
}
}
return null;
}
/**
* unfolds header lines
*
* @param headerlines the header lines to unfold
* @return the unfloded header lines
*/
static String[] unfoldingHeaderlines(String[] headerlines) {
String[] unfoldedLines = headerlines;
int emptyLines = 0;
lineLoop : for (int i = (headerlines.length - 1); i > 1; i--) {
for (int j = 0; j < headerlines[i].length(); j++) {
// folded line ?
if ((headerlines[i].charAt(j) == ' ') || (headerlines[i].charAt(j) == '\t')) {
do {
j++;
} while ((headerlines[i].charAt(j) == ' ') || (headerlines[i].charAt(j) == '\t'));
String line = headerlines[i].substring(j, headerlines[i].length());
headerlines[i-1] = headerlines[i-1] + " " + line;
headerlines[i] = null;
emptyLines++;
continue lineLoop;
// no
} else {
continue lineLoop;
}
}
}
// compact header line array
if (emptyLines > 0) {
String[] newHeaderlines = new String[headerlines.length - emptyLines];
int cursor = 0;
for (int i = 0; i < headerlines.length; i++) {
if (headerlines[i] != null) {
newHeaderlines[cursor] = headerlines[i];
cursor++;
}
}
unfoldedLines = newHeaderlines;
}
return unfoldedLines;
}
private static final class HeaderLine {
private String originalHeader = null;
private String value = null;
private String line = null;
private HeaderLine moreLine = null;
public HeaderLine(String line) {
this.line = line;
int idx = line.indexOf(":");
originalHeader = line.substring(0, idx);
value = line.substring(idx + 1, line.length()).trim();
}
public HeaderLine(String originalHeader, String value) {
this.line = originalHeader + ": " + value;
this.originalHeader = originalHeader;
this.value = value;
}
public HeaderLine(String line, String originalHeader, String value) {
this.line = line;
this.originalHeader = originalHeader;
this.value = value;
}
String getOriginalHeader() {
return originalHeader;
}
String getValue() {
return value;
}
public void addMoreLine(HeaderLine moreLine) {
this.moreLine = moreLine;
}
public HeaderLine getMoreLine() {
return moreLine;
}
@Override
public String toString() {
if (moreLine == null) {
return line;
} else {
return line + "\r\n" + moreLine;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy