All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xsocket.connection.http.HttpRequestHeader Maven / Gradle / Ivy

There is a newer version: 2.0-beta-1
Show newest version
/*
 *  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