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

org.xsocket.connection.http.RequestHeader 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.InetSocketAddress;
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 RequestHeader extends AbstractMessageHeader {
	
	private Logger LOG = Logger.getLogger(RequestHeader.class.getName());

	private static final String HOST = "Host";

	
	private ByteBuffer[] rawHeader = null;

	
	private IConnection connection = null;
	private Boolean isSecure = null;

	private String method = null;
	private String hostname = null;
	private Integer port = null;
	
	private String requestUri = null;
	private String path = null;
	private String query = null;

	private String protocol = null;
	private String scheme = null;
	private String schemeVersion = "1.1";

	private Map parameterMap = null;
	private boolean isParametersModified = false;

	
	private boolean isFirstLineModified = false;
	
	// performance optimization
	private String userAgent = null;
	private String host = null;

	

	
	
	/**
	 * constructor 
	 * 
	 * @param uri  the uri
	 */
	public RequestHeader(String uri) {
		this(null, uri);
	}
	
	
	/**
	 * constructor 
	 * 
	 * @param method   the method 
	 * @param uri      the uri
	 */
	public RequestHeader(String method, String uri) {
		this(method, uri, null, "1.1");
	}
	
	
	
	/**
	 * constructor
	 * 
	 * @param method       the method
	 * @param uri          the uri
	 * @param contentType  the content type
	 */
	public RequestHeader(String method, String uri, String contentType) {
		this(method, uri, contentType, "1.1");
	}
	
	
	private RequestHeader(String method, String uri, String contentType, String schemeVersion) {
		try {
			this.method = method;
			this.schemeVersion = schemeVersion;
			
			if (contentType != null) {
				setContentType(contentType);
			}
			
			
			URI u = new URI(uri);
			scheme = u.getScheme();
			path = u.getRawPath();
			query = u.getRawQuery();
	
			hostname = u.getHost();
			port = u.getPort();
			
			scheme = u.getScheme();
			if (scheme != null) {
				if (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) {
					throw new RuntimeException("unsupported protocol " + scheme);
				}
				
				if (port == -1) {
					if (scheme.equalsIgnoreCase("http")) {
						port = 80;
						
					} else if (scheme.equalsIgnoreCase("https")) {
						port = 443;
					}
				}
			}
			
			if (scheme != null) {
				protocol = scheme + "/" + schemeVersion;
			}
			
			if (hostname != null) {
				setHost(hostname + ":" + port);
			}
		} catch (URISyntaxException se) {
			throw new RuntimeException("could not resolve url " + uri + " " + se.toString());
		}
	}
	
	
	/**
	 * constructor 
	 * 
	 * @param rawHeader    the raw header 
	 * @param connection   the underlying connection
	 */
	RequestHeader(ByteBuffer[] rawHeader, IConnection connection) {
		this.rawHeader = rawHeader;
		this.connection = connection;
		
		
		
		try {
			String header = DataConverter.toString(rawHeader, "ISO-8859-1");
			String[] headerlines = header.split("\r\n"); 

			// unfolding lines if necessary
			headerlines = unfoldingHeaderlines(headerlines);

			
			for (String line : headerlines) {
				
				if (line.length() == 0) {
					LOG.warning("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];
			
					// full request? 
					if (parts.length > 1) {
			
						// get pathAndQuery
						requestUri = parts[1];
			
						// get protocol
						if (parts.length > 2) {
							protocol = parts[2];
						} else {
							scheme = "HTTP";
							protocol = scheme + "/1.0";
						}
					}
					
					
				// read the remaining
				} else {
					addHeaderLineSilence(line);
				}
			}
			
			
		} catch (UnsupportedEncodingException use) {
			throw new RuntimeException(use.toString());
		}
	}
	


	/**
	 * {@inheritDoc}
	 */
	@Override
	void onHeaderAdded(String upperHeadername, String headervalue) {
		super.onHeaderAdded(upperHeadername, headervalue);
	
		if (upperHeadername.equals("USER-AGENT")) {
			userAgent = headervalue;

		} else if (upperHeadername.equals("HOST")) {
			host = headervalue;;
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	void onHeaderRemoved(String upperHeadername) {
		super.onHeaderRemoved(upperHeadername);
		
		if (upperHeadername.equals("USER-AGENT")) {
			userAgent = null;

		} else if (upperHeadername.equals("HOST")) {
			host = null;
		}
	}
	
	
	/**
	 * copy the headers 
	 * 
	 * @param otherHeader         the other request header
	 * @param upperExcludenames   the header names to exclude
	 */
	public void copyHeaderFrom(RequestHeader otherHeader, String... upperExcludenames) {
		super.copyHeaderFrom(otherHeader, upperExcludenames);
	}


	/**
	 * returns the user agent header
	 * @return
	 */
	public String getUserAgent() {
		return userAgent;
	}
	
	
	/**
	 * sets the user agent header
	 * 
	 * @param userAgent the user agent header
	 */
	public void setUserAgent(String userAgent) {
		setHeader(USER_AGENT, userAgent);
	}

	
	/**
	 * sets the host header
	 *  
	 * @param host the host header
	 */
	public void setHost(String host) {
		setHeader(HOST, host);
		
		this.host = host;
		
		int pos = host.lastIndexOf(":");
		hostname = host.substring(0, pos);
		port = Integer.parseInt(host.substring(pos + 1 , host.length()));
	}

	
	
	/**
	 * returns the host header
	 * 
	 * @return the host header
	 */
	public String getHost() {
		return host;
	}
	
	
	
	/**
	 * sets the remote address (updates the uri) 
	 * 
	 * @param address the remote address
	 */
    public void setRemoteSocketAddress(InetSocketAddress address) {
    	this.hostname = address.getHostName();
    	this.port = address.getPort();
    	
    	setHost(hostname + ":" + port);
    }


	/**
	 * returns the remote host
	 * 
	 * @return the remote host
	 */
	public String getRemoteHost() {
		if (hostname == null) {
			if (connection != null) {
				hostname = connection.getLocalAddress().getHostName();
			}
		}
		return hostname;
	}
	
	
	
	
	/**
	 * returns the remote port
	 * 
	 * @return the remote port
	 */
	public int getRemotePort() {
		if (port == null) {
			if (connection != null) {
				port = connection.getLocalPort();
			} else {
				return -1;
			}
		}
		return port;
	}
	
	
	/**
	 * returns the protocol 
	 * 
	 * @return the protocol
	 */
	public String getProtocol() {
		return protocol;
	}
	
	
	/**
	 * returns the query string
	 * 
	 * @return the query string
	 */
	public String getQueryString() {
		splitRequestURI();
		return query;
	}
	
	
	/**
	 * returns the request uri
	 * 
	 * @return the request uri
	 */
	public String getRequestURI() {
		splitRequestURI();
		return path;
	}
	

	/**
	 * returns the full request uri
	 * 
	 * @return the full request uri
	 */
	public String getFullRequestURI() {
		return requestUri;
	}
	
	
	
	/**
	 * returns true if is secured
	 *   
	 * @return true, if is secured
	 */
	public boolean isSecure() {
		if (isSecure == null) {
			if (connection != null) {
				isSecure = connection.isSecure();
			} else {
				isSecure = scheme.equalsIgnoreCase("HTTPS");
			}
		}
		return isSecure;
	}
	
	
	
	/**
	 * returns the method
	 * 
	 * @return the method 
	 */
	public String getMethod() {
		return method;
	}
	
	
	
	/**
	 * set the method 
	 * 
	 * @param method  the method
	 */
	public void setMethod(String method) {
		this.method = method;
		isFirstLineModified = true;
	}

	
	
	/**
	 * returns the scheme
	 * 
	 * @return the scheme
	 */
	public String getScheme() {
		if (scheme == null) {
			if (protocol != null) {
				int startPos = protocol.indexOf("/"); 
				if (startPos != -1) {
					scheme = protocol.substring(0, startPos);
				} 
			}
		}
		return scheme;
	}
	
	
	/**
	 * sets the scheme 
	 * 
	 * @param scheme the scheme 
	 */
	public void setScheme(String scheme) {
		this.scheme = scheme;
		protocol = scheme + "/" + schemeVersion;
		isFirstLineModified = true;
	}
	
	
	
	
	private void splitRequestURI() {
		if ((path == null) && (requestUri != null)) {
			int startPos = requestUri.indexOf("?"); 
			if (startPos != -1) {
				path = requestUri.substring(0, startPos);
				query = requestUri.substring(startPos + 1, requestUri.length());
				
			} else {
				path = requestUri;
			}
		}
	}
	
	
	

	/**
	 * 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());
	}
	
	
	/**
	 * gets a parameter
	 * 
	 * @param name the parameter name
	 * @return the parameter value or null
	 */
	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("erro occured by endcoding request params " + use.toString());
						}
					}
				}
			}
			String query = sb.toString();
			if (query.length() > 0) {
				query = query.substring(0, query.length() - 1);
				sb.append("?" + query);
				requestUri = path + "?" + query;
				
			} else {
				requestUri = path;
			}
		}


		StringBuilder sb = new StringBuilder(method + " ");

		if (requestUri != null) {
			sb.append(requestUri);
			
		} else {
			sb.append(path);
			if (query != null) {
				sb.append("?" + query);
			}
		}
		
		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());
		}
	}	
	
	
	public static RequestHeader readFrom(INonBlockingConnection connection, int maxLength) throws BufferUnderflowException, IOException {
		ByteBuffer[] rawHeader = connection.readByteBufferByDelimiter("\r\n\r\n", "US-ASCII", maxLength);
		
		return new RequestHeader(rawHeader, connection);
	}
}

 




© 2015 - 2025 Weber Informatics LLC | Privacy Policy