org.xsocket.connection.http.HttpRequestHeader 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.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xsocket.DataConverter;
import org.xsocket.IDataSink;
import org.xsocket.connection.IConnection;
import org.xsocket.connection.INonBlockingConnection;
/**
* http request header representation
*
* @author [email protected]
*/
public final class HttpRequestHeader extends AbstractMessageHeader {
private Logger LOG = Logger.getLogger(HttpRequestHeader.class.getName());
private ByteBuffer[] rawHeader = null;
// connection properties
private IConnection connection = null;
private Boolean isSecure = null;
// method
private String method = null;
// request elements
private String schemeVersion = "";
private String scheme = null;
private String host = null;
private int port = -1;
private String rawPath = null;
private String rawQuery = null;
// resolved parameters
private Map parameterMap = null;
private boolean isParametersModified = false;
private boolean isFirstLineModified = false;
/**
* constructor
*
* @param method the method
* @param uri the uri
*/
public HttpRequestHeader(String method, String uri) throws URISyntaxException {
this(method, uri, null, "1.1");
}
/**
* constructor
*
* @param method the method
* @param uri the uri
* @param contentType the content type
*/
public HttpRequestHeader(String method, String uri, String contentType) throws URISyntaxException {
this(method, uri, contentType, "1.1");
}
private HttpRequestHeader(String method, String uri, String contentType, String schemeVersion) throws URISyntaxException {
this.method = method;
this.schemeVersion = schemeVersion;
setTargetURI(new URI(uri));
if (contentType != null) {
setContentType(contentType);
}
}
/**
* constructor
*
* @param rawHeader the raw header
* @param connection the underlying connection
*/
HttpRequestHeader(ByteBuffer[] rawHeader, IConnection connection) throws IOException {
this.rawHeader = rawHeader;
this.connection = connection;
try {
String header = DataConverter.toString(rawHeader, HEADER_ENCODING);
String[] headerlines = header.split("\r\n");
// unfolding lines if necessary
headerlines = unfoldingHeaderlines(headerlines);
for (String line : headerlines) {
if (line.length() == 0) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("got request header with empty line:\r\n" + header);
}
continue;
}
// read first line
if (method == null) {
// get method
String[] parts = line.split(" ");
method = parts[0];
if (parts.length > 1) {
// get pathAndQuery
String requestPath = parts[1];
// get protocol
String scheme = "http";
String schemeVersion = "1.0";
if (parts.length > 2) {
String protocol = parts[2];
String[] pair = protocol.split("/");
scheme = pair[0];
schemeVersion = pair[1];
}
setTargetURI(requestPath, scheme, schemeVersion);
}
// read the remaining
} else {
addHeaderLineSilence(line);
}
}
if (host == null) {
String hostHeader = getHeader("Host");
int pos = hostHeader.lastIndexOf(":");
if (pos != -1) {
host = hostHeader.substring(0, pos);
port = Integer.parseInt(hostHeader.substring(pos + 1, hostHeader.length()).trim());
} else {
host = hostHeader;
if (isSecure) {
port = 443;
} else {
port = 80;
}
}
}
} catch (UnsupportedEncodingException use) {
throw new RuntimeException(use.toString());
}
}
/**
* copy the headers
*
* @param otherHeader the other request header
* @param upperExcludenames the header names to exclude
*/
public void copyHeaderFrom(HttpRequestHeader otherHeader, String... upperExcludenames) {
super.copyHeaderFrom(otherHeader, upperExcludenames);
}
/**
* Returns the fully qualified name of the client
* or the last proxy that sent the request. If the
* engine cannot or chooses not to resolve the hostname
* (to improve performance), this method returns the
* dotted-string form of the IP address.
*
* @return a String containing the fully qualified name of the client
*/
public String getRemoteHost() {
if (connection != null) {
return connection.getRemoteAddress().getHostName();
} else {
return null;
}
}
/**
* Returns the Internet Protocol (IP) source
* port of the client or last proxy that sent the request.
*
* @return an integer specifying the port number
*/
public int getRemotePort() {
if (connection != null) {
return connection.getRemotePort();
} else {
return -1;
}
}
/**
* Returns the name and version of the protocol the request
* uses in the form protocol/majorVersion.minorVersion, for example, HTTP/1.1.
*
* @return a String containing the protocol name and version number
*/
public String getProtocol() {
return scheme + "/" + schemeVersion;
}
/**
* Returns the query string that is contained in the request URL
* after the path. This method returns null
* if the URL does not have a query string.
*
* @return a String containing the query string or null if the URL contains no query string.
*/
public String getQueryString() {
return rawQuery;
}
/**
* Returns the part of this request's URL from the protocol
* name up to the query string in the first line of the HTTP request.
*
* @return a String containing the part of the URL from the protocol
* name up to the query string
*/
public String getRequestURI() {
return rawPath;
}
/**
* returns the full request uri
*
* @return the full request uri
*/
public URI getTargetURI() {
try {
return new URI(scheme, null, host, port, rawPath, rawQuery, null);
} catch (URISyntaxException use) {
throw new RuntimeException(use.toString());
}
}
/**
* set the target uri
*
* @param uri the target uri
*/
public void setTargetURI(URI uri) {
if (uri.getScheme() != null) {
scheme = uri.getScheme();
isFirstLineModified = true;
} else {
scheme = "http";
isFirstLineModified = true;
}
rawPath = uri.getRawPath();
rawQuery = uri.getRawQuery();
host = uri.getHost();
port = uri.getPort();
String host = uri.getHost();
int port = uri.getPort();
if (port != -1) {
host += ":" + port;
setHeader("HOST", host);
}
}
private void setTargetURI(String requestPath, String scheme, String schemeVersion) {
try {
URI uri = new URI(requestPath);
setTargetURI(uri);
this.scheme = scheme;
this.schemeVersion = schemeVersion;
isFirstLineModified = true;
} catch (URISyntaxException use) {
throw new RuntimeException(use.toString());
}
}
/**
* updates the target uri
*
* @param host the host
* @param port the port
*/
public void updateTargetURI(String host, int port) {
this.host = host;
this.port = port;
if (port != -1) {
host += ":" + port;
setHeader("HOST", host);
}
}
/**
* Returns a boolean indicating whether this request
* was made using a secure channel, such as HTTPS.
*
* @return a boolean indicating if the request was made using a secure channel
*/
public boolean isSecure() {
if (isSecure == null) {
if (connection != null) {
isSecure = connection.isSecure();
} else {
if (getScheme() != null) {
isSecure = getScheme().equalsIgnoreCase("HTTPS");
} else {
throw new RuntimeException("error occured scheme is not defined");
}
}
}
return isSecure;
}
/**
* Returns the name of the HTTP method with
* which this request was made, for example, GET, POST, or PUT.
*
* @return a String specifying the name of the method
*/
public String getMethod() {
return method;
}
/**
* Sets the name of the HTTP method
*
* @param method a String specifying the name of the method
*/
public void setMethod(String method) {
this.method = method;
isFirstLineModified = true;
}
/**
* returns the scheme
*
* @return the scheme
*/
public String getScheme() {
return scheme;
}
/**
* sets the scheme
*
* @param scheme the scheme
*/
public void setScheme(String scheme) {
this.scheme = scheme;
isFirstLineModified = true;
}
/**
* Returns the host name of the server to which the request was sent.
* It is the value of the part before ":" in the Host header value,
* if any, or the resolved server name, or the server IP address.
*
* @return the server name
*/
public String getServerName() {
String host = getHeader("HOST");
if (host != null) {
int pos = host.lastIndexOf(":");
if (pos == -1) {
return host;
} else {
return host.substring(0, pos);
}
} else {
if (connection != null) {
return connection.getLocalAddress().getHostName();
}
}
return null;
}
/**
* Returns the port number to which the request was sent. It is the
* value of the part after ":" in the Host header value, if any,
* or the server port where the client connection was accepted on.
*
* @return the server port
*/
public int getServerPort() {
String host = getHeader("HOST");
if (host != null) {
int pos = host.lastIndexOf(":");
if (pos == -1) {
if (connection != null) {
connection.getLocalPort();
}
} else {
return Integer.parseInt(host.substring(pos + 1, host.length()));
}
} else {
if (connection != null) {
return connection.getLocalPort();
}
}
return -1;
}
/**
* removes a parameter
*
* @param parameterName the parameter name
*/
void removeParameter(String parameterName) {
rawHeader = null;
isParametersModified = true;
getParamMap().remove(parameterName);
}
/**
* sets a parameter
*
* @param parameterName the parameter name
* @param parameterValue the parameter value
*/
public void setParameter(String parameterName, String parameterValue) {
rawHeader = null;
isParametersModified = true;
getParamMap().put(parameterName, new String[] { parameterValue });
}
/**
* returns the parameter map
*
* @return the parameter map
*/
@SuppressWarnings("unchecked")
public final Map getParameterMap() {
return Collections.unmodifiableMap(getParamMap());
}
/**
* returns the parameter name set
*
* @return the parameter name set
*/
public final Set getParameterNameSet() {
return Collections.unmodifiableSet(getParamMap().keySet());
}
/**
* Returns the value of a request parameter as a String, or null
* if the parameter does not exist. Request parameters are extra
* information sent with the request.
*
* @param name a String specifying the name of the parameter
* @return a String representing the single value of the parameter
*/
public final String getParameter(String name) {
String[] values = getParamMap().get(name);
if (values == null) {
return null;
}
return values[0];
}
private final Map getParamMap() {
if (parameterMap == null) {
parameterMap = new HashMap();
String queryStr = getQueryString();
if (queryStr != null) {
String[] parameters = queryStr.split("&");
for (String parameter : parameters) {
String[] kvp = parameter.split("=");
String[] values = parameterMap.get(kvp[0]);
try {
// decode by using UTF-8 (see http://www.w3.org/TR/html401/appendix/notes.html#h-B.2.1)
String key = URLDecoder.decode(kvp[0], "UTF-8").trim();
String value = URLDecoder.decode(kvp[1], "UTF-8").trim();
if (values == null) {
parameterMap.put(key, new String[] { value });
} else {
String[] newValues = new String[values.length + 1];
System.arraycopy(values, 0, newValues, 0, values.length);
newValues[values.length] = value;
parameterMap.put(key, newValues);
}
} catch (UnsupportedEncodingException usce) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Error occured by decoding parameter " + kvp[0] + "=" + kvp[1] + " " + usce.toString());
}
}
}
}
}
return parameterMap;
}
/**
* return the request line
*
* @return the request line
*/
String getRequestLine() {
if (isParametersModified) {
StringBuilder sb = new StringBuilder();
for (Entry entry : getParamMap().entrySet()) {
for (String value : entry.getValue()) {
try {
sb.append(URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(value, "UTF-8") + "&");
} catch (UnsupportedEncodingException use) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by endcoding request params " + use.toString());
}
}
}
}
String q = sb.toString();
if (q.length() > 0) {
rawQuery = (q.substring(0, q.length() - 1));
isParametersModified = false;
}
}
StringBuilder sb = new StringBuilder(method + " ");
sb.append(rawPath);
if (rawQuery != null) {
sb.append("?" + rawQuery);
}
sb.append(" HTTP/" + schemeVersion);
return sb.toString();
}
/**
* signals if request is modified
*
* @return true, if the request is modified
*/
boolean isModified() {
return isParametersModified || isFirstLineModified || super.isHeadersModified();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getRequestLine() + "\r\n");
sb.append(printHeaders());
// write blank line
sb.append("\r\n");
return sb.toString();
}
/**
* {@inheritDoc}
*/
@Override
public int writeTo(IDataSink dataSink) throws IOException {
if (!isModified() && (rawHeader != null)) {
int written = (int) dataSink.write(rawHeader);
written += dataSink.write("\r\n\r\n");
rawHeader = null; // necessary, because byte buffers pointer will be modified by writing
return written;
} else {
return dataSink.write(toString().getBytes(HEADER_ENCODING));
}
}
public static HttpRequestHeader readFrom(INonBlockingConnection connection, int maxLength) throws BufferUnderflowException, IOException {
ByteBuffer[] rawHeader = connection.readByteBufferByDelimiter("\r\n\r\n", HEADER_ENCODING, maxLength);
return new HttpRequestHeader(rawHeader, connection);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy