package org.apache.jmeter.protocol.http.proxy;
/****************************************
 * File HttpRequestHdr.java
 ***************************************/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.jmeter.protocol.http.config.MultipartUrlConfig;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui;
import org.apache.jmeter.protocol.http.gui.HeaderPanel;
import org.apache.jmeter.protocol.http.sampler.HTTPSampler;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.log.Logger;
import org.apache.jorphan.logging.LoggingManager;
//
// Class:     HttpRequestHdr
// Abstract:  The headers of the client HTTP request.
//
/****************************************
 * !ToDo (Class description)
 *
 *@author    $Author: mstover1 $
 *@created   $Date: 2002/10/17 19:47:19 $
 *@version   $Revision: 1.6 $
 ***************************************/
public class HttpRequestHdr
{
	Logger log =
		LoggingManager.getLoggerFor(JMeterUtils.HTTP);
	/****************************************
	 * Http Request method. Such as get or post.
	 ***************************************/
	public String method = new String();
	/****************************************
	 * The requested url. The universal resource locator that hopefully uniquely
	 * describes the object or service the client is requesting.
	 ***************************************/
	public String url = new String();
	/****************************************
	 * Version of http being used. Such as HTTP/1.0
	 ***************************************/
	public String version = new String();
	/****************************************
	 * !ToDo (Field description)
	 ***************************************/
	public String postData = "";
	static String CR = "\r\n";
	private Map headers = new HashMap();
	/****************************************
	 * Parses a http header from a stream.
	 *
	 *@param in  The stream to parse.
	 *@return    Array of bytes from client.
	 ***************************************/
	public byte[] parse(InputStream in) throws IOException
	{
		boolean inHeaders = true;
		int readLength = 0;
		int dataLength = 0;
		String CR = "\r\n";
		boolean first = true;
		ByteArrayOutputStream clientRequest = new ByteArrayOutputStream();
		ByteArrayOutputStream line = new ByteArrayOutputStream();
		int x;
		while ((inHeaders || readLength < dataLength) && ((x = in.read()) != -1))
		{
			line.write(x);
			clientRequest.write(x);
			if (inHeaders && (byte) x == (byte) '\n')
			{
				if (line.size() < 3)
				{
					inHeaders = false;
				}
				if (first)
				{
					parseFirstLine(line.toString());
					first = false;
				}
				else
				{
					dataLength = Math.max(parseLine(line.toString()), dataLength);
				}
				line.reset();
			}
			else if (!inHeaders)
			{
				readLength++;
			}
		}
		postData = line.toString().trim();
		return clientRequest.toByteArray();
	}
	public void parseFirstLine(String firstLine)
	{
		log.debug("browser request: " + firstLine);
		StringTokenizer tz = new StringTokenizer(firstLine);
		method = getToken(tz).toUpperCase();
		url = getToken(tz);
		log.debug("parsed url: " + url);
		version = getToken(tz);
	}
	public int parseLine(String nextLine)
	{
		StringTokenizer tz;
		tz = new StringTokenizer(nextLine);
		String token = getToken(tz);
		// look for termination of HTTP command
		if (0 == token.length())
		{
			return 0;
		}
		else
		{
			String name = token.trim().substring(0, token.trim().length() - 1);
			String value = getRemainder(tz);
			headers.put(name.toLowerCase(), new Header(name, value));
			if (name.equalsIgnoreCase("content-length"))
			{
				return Integer.parseInt(value);
			}
		}
		return 0;
	}
	public HeaderManager getHeaderManager()
	{
		HeaderManager manager = new HeaderManager();
		Iterator keys = headers.keySet().iterator();
		while (keys.hasNext())
		{
			String key = (String) keys.next();
			if (!key.equals("proxy-connection") && !key.equals("content-length"))
			{
				manager.add((Header) headers.get(key));
			}
		}
		manager.setName("Browser-derived headers");
		manager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName());
		manager.setProperty(TestElement.GUI_CLASS, HeaderPanel.class.getName());
		return manager;
	}
	public HTTPSampler getSampler()
		throws MalformedURLException, IOException, ProtocolException
	{
		HttpTestSampleGui tempGui = new HttpTestSampleGui();
		tempGui.configure(createSampler());
		HTTPSampler result = (HTTPSampler) tempGui.createTestElement();
		result.setFollowRedirects(false);
		result.setUseKeepAlive(true);
		return result;
	}
	public String getContentType()
	{
		Header contentTypeHeader = (Header) headers.get("content-type");
		if (contentTypeHeader != null)
		{
			return contentTypeHeader.getValue();
		}
		return "";
	}
	public static MultipartUrlConfig isMultipart(String contentType)
	{
		if (contentType != null
			&& contentType.startsWith(MultipartUrlConfig.MULTIPART_FORM))
		{
			return new MultipartUrlConfig(
				contentType.substring(contentType.indexOf("oundary=") + 8));
		}
		else
		{
			return null;
		}
	}
	private HTTPSampler createSampler()
	{
		MultipartUrlConfig urlConfig = null;
		HTTPSampler sampler = new HTTPSampler();
		sampler.setDomain(serverName());
		log.debug("Proxy: setting server: "+sampler.getDomain());
		sampler.setMethod(method);
		log.debug("Proxy: method server: "+sampler.getMethod());
		sampler.setPath(serverUrl());
		log.debug("Proxy: setting path: "+sampler.getPath());
		sampler.setName(sampler.getPath());
		sampler.setPort(serverPort());
		log.debug("Proxy: setting port: "+sampler.getPort());
		if (url.indexOf("//") > -1)
		{
			String protocol = url.substring(0, url.indexOf(":"));
			log.debug("Proxy: setting protocol to : "+protocol);
			sampler.setProtocol(protocol);
		}
		else if(sampler.getPort() == 443)
		{
			sampler.setProtocol("https");
			log.debug("Proxy: setting protocol to https");
		}
		else
		{
			log.debug("Proxy setting default protocol to: http");
			sampler.setProtocol("http");
		}
		if ((urlConfig = isMultipart(getContentType())) != null)
		{
			urlConfig.parseArguments(postData);
			sampler.setArguments(urlConfig.getArguments());
			sampler.setFileField(urlConfig.getFileFieldName());
			sampler.setFilename(urlConfig.getFilename());
			sampler.setMimetype(urlConfig.getMimeType());
		}
		else
		{
			sampler.parseArguments(postData);
		}
		return sampler;
	}
	//
	// Parsing Methods
	//
	/****************************************
	 * Find the //server.name from an url.
	 *
	 *@return   Servers internet name
	 ***************************************/
	public String serverName()
	{
		// chop to "server.name:x/thing"
		String str = url;
		int i = str.indexOf("//");
		if (i > 0)
		{
			str = str.substring(i + 2);
		}
		// chop to  server.name:xx
		i = str.indexOf("/");
		if (0 < i)
		{
			str = str.substring(0, i);
		}
		// chop to server.name
		i = str.indexOf(":");
		if (0 < i)
		{
			str = str.substring(0, i);
		}
		return str;
	}
	/****************************************
	 * Find the :PORT form http://server.ect:PORT/some/file.xxx
	 *
	 *@return   Servers internet name
	 ***************************************/
	public int serverPort()
	{
		String str = url;
		// chop to "server.name:x/thing"
		int i = str.indexOf("//");
		if (i > 0)
		{
			str = str.substring(i + 2);
		}		
		// chop to  server.name:xx
		i = str.indexOf("/");
		if (0 < i)
		{
			str = str.substring(0, i);
		}
		// chop XX
		i = str.indexOf(":");
		if (0 < i)
		{
			return Integer.parseInt(str.substring(i + 1).trim());
		}
		return 80;
	}
	/****************************************
	 * Find the /some/file.xxxx form http://server.ect:PORT/some/file.xxx
	 *
	 *@return   the deproxied url
	 ***************************************/
	public String serverUrl()
	{
		String str = url;
		int i = str.indexOf("//");
		if (i > 0)
		{
			str = str.substring(i + 2);
		}		
		i = str.indexOf("/");
		if (i < 0)
		{
			return "";
		}
		return str.substring(i);
	}
	/****************************************
	 * Returns the next token in a string
	 *
	 *@param tk  String that is partially tokenized.
	 *@return    !ToDo (Return description)
	 *@returns   The remainder
	 ***************************************/
	String getToken(StringTokenizer tk)
	{
		String str = "";
		if (tk.hasMoreTokens())
		{
			str = tk.nextToken();
		}
		return str;
	}
	/****************************************
	 * Returns the remainder of a tokenized string
	 *
	 *@param tk  String that is partially tokenized.
	 *@return    !ToDo (Return description)
	 *@returns   The remainder
	 ***************************************/
	String getRemainder(StringTokenizer tk)
	{
		String str = "";
		if (tk.hasMoreTokens())
		{
			str = tk.nextToken();
		}
		while (tk.hasMoreTokens())
		{
			str += " " + tk.nextToken();
		}
		return str;
	}
}
