package com.grendelscan.commons.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.NoHttpResponseException;
import org.apache.http.RequestLine;
import org.apache.http.StatusLine;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.impl.io.AbstractMessageParser;
import org.apache.http.message.BasicLineParser;
import org.apache.http.message.ParserCursor;
import org.apache.http.util.ByteArrayBuffer;
import org.apache.http.util.CharArrayBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.grendelscan.commons.StringUtils;
import com.grendelscan.commons.formatting.encoding.UrlEncodingUtils;
import com.grendelscan.commons.http.apache_overrides.serializable.SerializableBasicCookie;
import com.grendelscan.commons.http.apache_overrides.serializable.SerializableHttpHeader;
import com.grendelscan.commons.http.apache_overrides.serializable.SerializableStatusLine;
import com.grendelscan.commons.http.dataHandling.containers.DataContainerUtils;
import com.grendelscan.commons.http.dataHandling.containers.NameValuePairDataContainer;
import com.grendelscan.commons.http.dataHandling.data.DataUtils;
import com.grendelscan.commons.http.transactions.HttpTransactionFields;
import com.grendelscan.commons.http.transactions.StandardHttpTransaction;
import com.grendelscan.commons.http.wrappers.HttpHeadersWrapper;
import com.grendelscan.commons.http.wrappers.HttpMessageWrapper;
import com.grendelscan.commons.http.wrappers.HttpRequestWrapper;
import com.grendelscan.commons.http.wrappers.HttpResponseWrapper;
public class HttpUtils
{
private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
static private Pattern httpVersionPattern = Pattern.compile("^(\\w+)/(\\d+)\\.(\\d+)$");
static private Pattern headerPattern = Pattern.compile("([^ \r\n\t]+): ([^\r\n]+)");
/**
*
* @param entity
* @param maxSize
* Set to zero for no limit
* @return
* @throws IOException
*/
public static byte[] entityToByteArray(final HttpEntity entity, int maxSize) throws IOException
{
if (maxSize <= 0)
{
maxSize = Integer.MAX_VALUE;
}
if (entity == null)
{
throw new IllegalArgumentException("HTTP entity may not be null");
}
InputStream instream = entity.getContent();
if (instream == null)
{
return new byte[] {};
}
int i = (int) entity.getContentLength();
if (entity.getContentLength() > maxSize)
{
i = maxSize;
}
else if (i < 0)
{
i = 4096;
}
ByteArrayBuffer buffer = new ByteArrayBuffer(i);
try
{
byte[] tmp = new byte[4096];
int l;
while ((l = instream.read(tmp)) != -1 && buffer.length() <= maxSize)
{
buffer.append(tmp, 0, l);
}
}
finally
{
instream.close();
}
return buffer.toByteArray();
}
/**
* It might seem strange to say that a 302 (or other redirect) isn't a file found. Keep in mind two things. First, many file not found messages are in redirects. Second, the redirect's destination
* will be tested as a file found (if it is found).
*
* @param httpStatusCode
* @return
*/
public static boolean fileExists(final int httpStatusCode)
{
boolean exists = false;
// 12/10/10 - changed this to include all 3xx codes
if (httpStatusCode >= 100 && httpStatusCode < 400 || httpStatusCode >= 401 && httpStatusCode <= 403)
{
exists = true;
}
return exists;
}
public static List<String> getAllQueryParameterNames(final StandardHttpTransaction transaction)
{
List<String> names = new ArrayList<String>(1);
for (NameValuePairDataContainer param : DataContainerUtils.getAllNamedContaners(transaction.getTransactionContainer()))
{
names.add(new String(DataUtils.getBytes(param.getNameData())));
}
return names;
}
public static Cookie getSetCookie(final StandardHttpTransaction transaction, final String cookieName)
{
for (Header setCookie : transaction.getResponseWrapper().getHeaders().getHeaders("Set-Cookie"))
{
try
{
for (Cookie cookie : transaction.getCookieJar().getCookieSpec().parse(setCookie, transaction.getCookieOrigin()))
{
if (cookie.getName().equalsIgnoreCase(cookieName))
{
return cookie;
}
}
}
catch (MalformedCookieException e)
{
LOGGER.error("Problem with set-cookie header in HttpUtils.getSetCookies: " + e.toString(), e);
}
}
return null;
}
public static Set<String> getSetCookieNames(final StandardHttpTransaction transaction)
{
Set<String> names = new HashSet<String>();
for (Cookie cookie : getSetCookies(transaction))
{
names.add(cookie.getName());
}
return names;
}
public static List<SerializableBasicCookie> getSetCookies(final HttpTransactionFields transaction)
{
List<SerializableBasicCookie> cookies = new ArrayList<SerializableBasicCookie>();
for (Header setCookie : transaction.getResponseWrapper().getHeaders().getHeaders("Set-Cookie"))
{
try
{
for (Cookie cookie : CookieJar.getCookieSpec().parse(setCookie, transaction.getCookieOrigin()))
{
cookies.add(new SerializableBasicCookie(cookie));
}
}
catch (MalformedCookieException e)
{
LOGGER.error("Problem with set-cookie header in HttpUtils.getSetCookies: " + e.toString(), e);
}
}
return cookies;
}
public static boolean isRedirectCode(final int httpStatusCode)
{
return httpStatusCode == 301 || httpStatusCode == 302 || httpStatusCode == 303 || httpStatusCode == 307;
}
public static StandardHttpTransaction parseIntoHttpRequest(final TransactionSource source, final byte[] rawRequest, final int testJobId) throws HttpFormatException, URISyntaxException
{
String requestString = new String(rawRequest, StringUtils.getDefaultCharset());
String method = "";
String uri = "";
String protocolVersion = "";
String bodyString = "";
Pattern requestPattern = Pattern.compile("^([^ \t\r\n]+) ([^ \t\r\n]+) ([^ \t\r\n]+)\r?\n((?:[^ \r\n\t]+: [^\r\n]+\r?\n)*+)\r?\n(.*)$", Pattern.DOTALL);
Matcher requestMatcher = requestPattern.matcher(requestString);
if (requestMatcher.matches())
{
method = requestMatcher.group(1);
uri = requestMatcher.group(2);
protocolVersion = requestMatcher.group(3);
String rawHeaders = requestMatcher.group(4);
bodyString = requestMatcher.group(5);
if (bodyString == null)
{
bodyString = "";
}
if (rawHeaders == null)
{
rawHeaders = "";
}
int major, minor;
String protocol = "";
Matcher versionMatcher = httpVersionPattern.matcher(protocolVersion);
if (versionMatcher.find())
{
protocol = versionMatcher.group(1);
major = Integer.valueOf(versionMatcher.group(2));
minor = Integer.valueOf(versionMatcher.group(3));
}
else
{
throw new HttpFormatException("Invalid protocol version format. It should look something like \"HTTP/1.0\".");
}
String host = URIStringUtils.getHost(uri);
Matcher headerMatcher = headerPattern.matcher(rawHeaders);
List<SerializableHttpHeader> headers = new ArrayList<SerializableHttpHeader>();
while (headerMatcher.find())
{
String name = UrlEncodingUtils.decodeUrl(headerMatcher.group(1));
String value = UrlEncodingUtils.decodeUrl(headerMatcher.group(2));
if (name.equalsIgnoreCase("Host"))
{
host = value;
}
headers.add(new SerializableHttpHeader(name, value));
}
int port = URIStringUtils.getPort(uri);
boolean ssl = URIStringUtils.getScheme(uri).equalsIgnoreCase("https") ? true : false;
byte[] body = bodyString.getBytes(StringUtils.getDefaultCharset());
StandardHttpTransaction transaction = new StandardHttpTransaction(source, testJobId);
if (body.length > 0)
{
transaction.getRequestWrapper().setBody(body);
}
transaction.getRequestWrapper().setMethod(method);
transaction.getRequestWrapper().setURI(uri, true);
transaction.getRequestWrapper().setVersion(protocol, major, minor);
transaction.getRequestWrapper().setSecure(ssl);
transaction.getRequestWrapper().setNetworkHost(host);
transaction.getRequestWrapper().setNetworkPort(port);
transaction.getRequestWrapper().getHeaders().addHeaders(headers.toArray(new Header[0]));
return transaction;
}
throw new HttpFormatException("Unparsable request format.");
}
private static void parseMessage(final HttpMessageWrapper wrapper, final HttpTransactionByteInputBuffer inputBuffer, final BasicLineParser lineParser) throws IOException, HttpException
{
Header headers[] = AbstractMessageParser.parseHeaders(inputBuffer, 0, 0, lineParser);
ByteArrayOutputStream bodyStream = new ByteArrayOutputStream();
int b;
while ((b = inputBuffer.read()) >= 0)
{
bodyStream.write(b);
}
wrapper.setBody(bodyStream.toByteArray());
wrapper.setHeaders(new HttpHeadersWrapper(headers));
}
public static HttpRequestWrapper parseRequest(final byte[] data) throws IOException, HttpException
{
HttpTransactionByteInputBuffer inputBuffer = new HttpTransactionByteInputBuffer(data);
CharArrayBuffer lineBuf = new CharArrayBuffer(128);
if (inputBuffer.readLine(lineBuf) == -1)
{
throw new NoHttpResponseException("No CRLFs found. Try again.");
}
ParserCursor cursor = new ParserCursor(0, lineBuf.length());
BasicLineParser lineParser = BasicLineParser.DEFAULT;
RequestLine requestLine = lineParser.parseRequestLine(lineBuf, cursor);
HttpRequestWrapper wrapper = new HttpRequestWrapper(-123);
wrapper.setMethod(requestLine.getMethod());
wrapper.setURI(requestLine.getUri(), false);
wrapper.setVersion(requestLine.getProtocolVersion());
parseMessage(wrapper, inputBuffer, lineParser);
return wrapper;
}
public static HttpResponseWrapper parseResponse(final byte[] data) throws IOException, HttpException
{
HttpTransactionByteInputBuffer inputBuffer = new HttpTransactionByteInputBuffer(data);
CharArrayBuffer lineBuf = new CharArrayBuffer(128);
if (inputBuffer.readLine(lineBuf) == -1)
{
throw new NoHttpResponseException("No CRLFs found. Try again.");
}
ParserCursor cursor = new ParserCursor(0, lineBuf.length());
BasicLineParser lineParser = BasicLineParser.DEFAULT;
StatusLine status = lineParser.parseStatusLine(lineBuf, cursor);
HttpResponseWrapper wrapper = new HttpResponseWrapper(-123);
wrapper.setStatusLine(new SerializableStatusLine(status));
parseMessage(wrapper, inputBuffer, lineParser);
return wrapper;
}
}