/*******************************************************************************
* Copyright (c) 2011 Subgraph.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Subgraph - initial API and implementation
******************************************************************************/
package com.subgraph.vega.internal.http.proxy;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.ConnectionClosedException;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpMessage;
import org.apache.http.HttpRequestFactory;
import org.apache.http.ParseException;
import org.apache.http.ProtocolException;
import org.apache.http.RequestLine;
import org.apache.http.impl.io.AbstractMessageParser;
import org.apache.http.io.HttpMessageParser;
import org.apache.http.io.SessionInputBuffer;
import org.apache.http.message.BasicRequestLine;
import org.apache.http.message.LineParser;
import org.apache.http.message.ParserCursor;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.CharArrayBuffer;
/**
* HTTP request parser based on HttpRequestParser and AbstractMessageParser. Attaches scheme and host information to a
* request line URI when the connection is performing SSL MITM so the request still contains an absoluteURI as it would
* for an ordinary proxy request.
*/
public class VegaHttpRequestParser implements HttpMessageParser {
private final VegaHttpServerConnection conn;
private final SessionInputBuffer sessionBuffer;
private final HttpRequestFactory requestFactory;
private final CharArrayBuffer lineBuf;
private final LineParser lineParser;
private final int maxHeaderCount;
private final int maxLineLen;
public VegaHttpRequestParser(final VegaHttpServerConnection conn, final SessionInputBuffer buffer, final LineParser parser, final HttpRequestFactory requestFactory, final HttpParams params) {
if (requestFactory == null) {
throw new IllegalArgumentException("Request factory may not be null");
}
this.conn = conn;
this.sessionBuffer = buffer;
this.lineParser = parser;
this.requestFactory = requestFactory;
this.lineBuf = new CharArrayBuffer(128);
this.maxHeaderCount = params.getIntParameter(CoreConnectionPNames.MAX_HEADER_COUNT, -1);
this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1);
}
private RequestLine parseRequestLine(final SessionInputBuffer sessionBuffer) throws IOException, HttpException, ParseException {
lineBuf.clear();
int i = sessionBuffer.readLine(lineBuf);
if (i == -1) {
throw new ConnectionClosedException("Client closed connection");
}
ParserCursor cursor = new ParserCursor(0, lineBuf.length());
return lineParser.parseRequestLine(lineBuf, cursor);
}
@Override
public HttpMessage parse() throws IOException, HttpException {
RequestLine requestLine;
try {
requestLine = parseRequestLine(sessionBuffer);
} catch (ParseException px) {
throw new ProtocolException(px.getMessage(), px);
}
List<CharArrayBuffer> headerLines = new ArrayList<CharArrayBuffer>();
Header[] headers = AbstractMessageParser.parseHeaders(sessionBuffer, maxHeaderCount, maxLineLen, lineParser, headerLines);
if (conn.isSslConnection()) {
URI uri;
try {
uri = new URI(requestLine.getUri());
} catch (URISyntaxException e) {
throw new ProtocolException("Invalid URI: " + requestLine.getUri(), e);
}
if (uri.getScheme() == null) {
final Header hostHeader = getFirstHeader(headers, HTTP.TARGET_HOST);
final StringBuilder buf = new StringBuilder();
if (hostHeader != null) {
// REVISIT: does using the Host header value instead of the SSL host risk opening another connection?
buf.append("https://");
buf.append(hostHeader.getValue());
} else {
buf.append(conn.getSslHost().toURI());
}
buf.append(uri.getRawPath());
if (uri.getRawQuery() != null) {
buf.append("?");
buf.append(uri.getRawQuery());
}
requestLine = new BasicRequestLine(requestLine.getMethod(), buf.toString(), requestLine.getProtocolVersion());
}
}
HttpMessage message = requestFactory.newHttpRequest(requestLine);
message.setHeaders(headers);
return message;
}
private Header getFirstHeader(Header[] headers, String name) {
for (int i = 0; i < headers.length; i++) {
if (headers[i].getName().equalsIgnoreCase(name)) {
return headers[i];
}
}
return null;
}
}