/*
* This file is part of the OWASP Proxy, a free intercepting proxy library.
* Copyright (C) 2008-2010 Rogan Dawes <rogan@dawes.za.net>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.owasp.proxy.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.owasp.proxy.util.AsciiString;
/**
* The MessageHeader class is the base class for the HTTP Message, Request and Response classes.
*
* It attempts to be binary clean when the "lowest-level" methods are used, namely:
* <ul>
* <li>{@link #getHeader()}</li>
* <li>{@link #setHeader(byte[])}</li>
* </ul>
*
* Getting more convenient, there are methods that parse the "header" into lines and allow manipulation
* thereof:
* <ul>
* <li>{@link #getHeaders()}</li>
* <li>{@link #setHeaders(NamedValue[])}</li>
* <li>{@link #addHeader(String, String)}</li>
* <li>{@link #deleteHeader(String)}</li>
* </ul>
*
*/
public interface MutableMessageHeader extends MessageHeader {
void setId(int id);
void setHeader(byte[] header);
void setStartLine(String line) throws MessageFormatException;
void setHeaders(NamedValue[] headers) throws MessageFormatException;
void setHeader(String name, String value) throws MessageFormatException;
void addHeader(String name, String value) throws MessageFormatException;
String deleteHeader(String name) throws MessageFormatException;
public static class Impl implements MutableMessageHeader {
private static final byte[] CRLF = { '\r', '\n' };
private int id = -1;
protected byte[] header = null;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setHeader(byte[] header) {
this.header = header;
}
public byte[] getHeader() {
return header;
}
/**
* Finds the first occurrence of separator, starting at start
*
* @param separator
* @param start
* @return
*/
protected int indexOf(byte[] separator, int start) {
if (header == null)
throw new NullPointerException("array is null");
if (header.length - start < separator.length)
return -1;
int sep = start;
int i = 0;
while (sep <= header.length - separator.length
&& i < separator.length) {
if (header[sep + i] == separator[i]) {
i++;
} else {
i = 0;
sep++;
}
}
if (i == separator.length)
return sep;
return -1;
}
/**
* Breaks the header byte[] up into lines, separated by \r\n. The lines will include the trailing blank line
* expected as part of the header. The lines do NOT include the actual \r\n separator
*
* @return
* @throws MessageFormatException
*/
protected String[] getHeaderLines() throws MessageFormatException {
if (header == null)
return null;
List<String> lines = new LinkedList<String>();
int sep, start = 0;
while ((sep = indexOf(CRLF, start)) > -1) {
lines.add(AsciiString.create(header, start, sep - start));
start = sep + CRLF.length;
}
return lines.toArray(new String[lines.size()]);
}
/**
* Converts an array of String to a suitable byte[], by separating each with \r\n. The provided lines should
* include the trailing blank line
*
* @param lines
* including the trailing empty line
* @throws MessageFormatException
*/
protected void setHeaderLines(String[] lines)
throws MessageFormatException {
ByteArrayOutputStream buff = new ByteArrayOutputStream();
try {
for (int i = 0; i < lines.length; i++) {
buff.write(AsciiString.getBytes(lines[i]));
buff.write(CRLF);
}
} catch (IOException ioe) {
// impossible for ByteArrayOutputStream
}
setHeader(buff.toByteArray());
}
/**
* @return
* @throws MessageFormatException
*/
protected String[] getStartParts() throws MessageFormatException {
String startLine = getStartLine();
if (startLine == null)
return new String[0];
return startLine.split("[ \t]+", 3);
}
/**
* @param parts
* @throws MessageFormatException
*/
protected void setStartParts(String[] parts)
throws MessageFormatException {
if (parts == null || parts.length == 0) {
setStartLine(null);
} else {
StringBuilder b = new StringBuilder(parts[0] == null ? ""
: parts[0]);
for (int i = 1; i < parts.length; i++)
b.append(" ").append(parts[i] == null ? "" : parts[i]);
setStartLine(b.toString());
}
}
/**
* @return
* @throws MessageFormatException
*/
public String getStartLine() throws MessageFormatException {
String[] lines = getHeaderLines();
if (lines == null || lines.length == 0)
return null;
return lines[0];
}
/**
* @param line
* @throws MessageFormatException
*/
public void setStartLine(String line) throws MessageFormatException {
if (line == null)
line = "";
String[] lines = getHeaderLines();
if (lines == null || lines.length <= 1) {
lines = new String[2];
lines[1] = "";
}
lines[0] = line;
setHeaderLines(lines);
}
/**
* @return
* @throws MessageFormatException
*/
public NamedValue[] getHeaders() throws MessageFormatException {
String[] lines = getHeaderLines();
if (lines == null || lines.length <= 1)
return null;
NamedValue[] headers = new NamedValue[lines.length - 2];
for (int i = 0; i < headers.length; i++)
headers[i] = NamedValue.parse(lines[i + 1], " *: *");
return headers;
}
/**
* @param headers
* @throws MessageFormatException
*/
public void setHeaders(NamedValue[] headers)
throws MessageFormatException {
String[] lines = new String[(headers == null ? 0 : headers.length) + 2];
lines[0] = getStartLine();
if (lines[0] == null)
throw new MessageFormatException(
"No start line found, can't set headers without one!",
header);
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
lines[i + 1] = headers[i].toString();
}
}
lines[lines.length - 1] = "";
setHeaderLines(lines);
}
/**
* @param name
* @return
* @throws MessageFormatException
*/
public String getHeader(String name) throws MessageFormatException {
NamedValue[] headers = getHeaders();
if (headers == null || headers.length == 0)
return null;
for (int i = 0; i < headers.length; i++)
if (name.equalsIgnoreCase(headers[i].getName()))
return headers[i].getValue();
return null;
}
/**
* @param name
* @param value
* @throws MessageFormatException
*/
public void setHeader(String name, String value)
throws MessageFormatException {
NamedValue[] headers = getHeaders();
if (headers != null && headers.length != 0) {
for (int i = 0; i < headers.length; i++)
if (name.equalsIgnoreCase(headers[i].getName())) {
headers[i] = new NamedValue(name, headers[i]
.getSeparator(), value);
setHeaders(headers);
return;
}
}
addHeader(headers, name, value);
}
/**
* @param name
* @param value
* @throws MessageFormatException
*/
public void addHeader(String name, String value)
throws MessageFormatException {
addHeader(getHeaders(), name, value);
}
/**
* @param headers
* @param name
* @param value
* @throws MessageFormatException
*/
private void addHeader(NamedValue[] headers, String name, String value)
throws MessageFormatException {
if (headers == null) {
headers = new NamedValue[1];
} else {
NamedValue[] nh = new NamedValue[headers.length + 1];
System.arraycopy(headers, 0, nh, 0, headers.length);
headers = nh;
}
headers[headers.length - 1] = new NamedValue(name, ": ", value);
setHeaders(headers);
}
/**
* @param name
* @return
* @throws MessageFormatException
*/
public String deleteHeader(String name) throws MessageFormatException {
NamedValue[] headers = getHeaders();
if (headers == null || headers.length == 0)
return null;
for (int i = 0; i < headers.length; i++)
if (name.equalsIgnoreCase(headers[i].getName())) {
String ret = headers[i].getValue();
NamedValue[] nh = new NamedValue[headers.length - 1];
if (i > 0)
System.arraycopy(headers, 0, nh, 0, i);
if (i < headers.length - 1)
System.arraycopy(headers, i + 1, nh, i, headers.length
- i - 1);
setHeaders(nh);
return ret;
}
return null;
}
@Override
public String toString() {
return AsciiString.create(header);
}
}
}