/** \file HTTPSocket.h 	Class HTTPSocket definition.
 **	\date  2004-04-06
 **	\author grymse@alhem.net
**/
/*
Copyright (C) 2004-2011  Anders Hedstrom

This library is made available under the terms of the GNU GPL, with
the additional exemption that compiling, linking, and/or using OpenSSL 
is allowed.

If you would like to use this library in a closed-source application,
a separate license agreement is available. For information about 
the closed-source license agreement for the C++ sockets library,
please visit http://www.alhem.net/Sockets/license.html and/or
email license@alhem.net.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#ifndef _SOCKETS_HTTPSocket_H
#define _SOCKETS_HTTPSocket_H

#include "sockets-config.h"
#include "TcpSocket.h"
#include "Utility.h"


#ifdef SOCKETS_NAMESPACE
namespace SOCKETS_NAMESPACE {
#endif

/** \defgroup http HTTP Sockets */
/** HTTP request/response base class. 
	\ingroup http */
class HTTPSocket : public TcpSocket
{
	/** map to hold http header values. */
	typedef Utility::ncmap<std::string> string_m;
public:
	HTTPSocket(ISocketHandler& );
	~HTTPSocket();

	void OnRawData(const char *buf,size_t len);
	void OnLine(const std::string& line);

	/** Callback executes when first line has been received.
		GetMethod, GetUrl/GetUri, and GetHttpVersion are valid when this callback is executed. */
	virtual void OnFirst() = 0;
	/** For each header line this callback is executed.
		\param key Http header name
		\param value Http header value */
	virtual void OnHeader(const std::string& key,const std::string& value) = 0;
	/** Callback fires when all http headers have been received. */
	virtual void OnHeaderComplete() = 0;
	/** Chunk of http body data recevied. */
	virtual void OnData(const char *,size_t) = 0;
	/** The full request/response body has been received. */
	virtual void OnDataComplete() {}

	/** Get http method from incoming request, ie GET/POST/PUT etc */
	const std::string& GetMethod();
	/** Set http method to be used in request. */
	void SetMethod(const std::string& x);
	/** Get url from request. */
	const std::string& GetUrl();
	/** Set url to be used in outgoing request. */
	void SetUrl(const std::string& x);
	/** Get part of url before '?' character. */
	const std::string& GetUri();
	/** Now why would I need this when there is a SetUrl method? */
	void SetUri(const std::string& x);
	/** Get part of url after '?' character. */
	const std::string& GetQueryString();
	/** Get http version from incoming request/response. */
	const std::string& GetHttpVersion();
	/** Get http status from incoming response. */
	const std::string& GetStatus();
	/** Get http statustext from incoming response. */
	const std::string& GetStatusText();
	/** Incoming header has been identified as a request (method url http_version\r\n). */
	bool IsRequest();
	/** Incoming header has been identified as a response (http_version status status_text\r\n). */
	bool IsResponse();
	/** Set http version to be used in outgoing request/response. */
	void SetHttpVersion(const std::string& x);
	/** Set http status for outgoing response. */
	void SetStatus(const std::string& x);
	/** Set http statustext for outgoing response. */
	void SetStatusText(const std::string& x);
	/** Add (and replace if exists) http header. */
	void AddResponseHeader(const std::string& x,const std::string& y);
	/** Add (and replace if exists) http header. */
	void AddResponseHeader(const std::string& x,const char *format, ...);
	/** Add http header. */
	void AppendResponseHeader(const std::string& x,const std::string& y);
	/** See if http header 'name' has been set. */
	bool ResponseHeaderIsSet(const std::string& name);
	/** Send response prepared with calls to methods SetHttpVersion, SetStatus, SetStatusText,
		and AddResponseHeader. */
	void SendResponse();
	/** Send request prepared with calls to methods SetMethod, SetUrl, SetHttpVersion,
		and AddResponseHeader. */
	void SendRequest();

	/** Implement this to return your own User-agent string. */
	virtual std::string MyUseragent();

	/** Parse url. If protocol is https, EnableSSL() will be called. */
	void url_this(const std::string& url_in,std::string& protocol,std::string& host,port_t& port,std::string& url,std::string& file);

	/** Transfer coding 'chunked' */
	bool IsChunked() { return m_b_chunked; }

protected:
	HTTPSocket(const HTTPSocket& s) : TcpSocket(s) {}
	/** Reset state of socket to sucessfully implement keep-alive. */
	virtual void Reset();

	void SetMaxHeaderCount(int x) { m_max_header_count = x; }

private:
	HTTPSocket& operator=(const HTTPSocket& ) { return *this; }
	bool m_first;
	bool m_header;
	std::string m_line;
	std::string m_method;
	std::string m_url;
	std::string m_uri;
	std::string m_query_string;
	std::string m_http_version;
	std::string m_status;
	std::string m_status_text;
	bool m_request;
	bool m_response;
	string_m m_response_header;
	size_t m_body_size_left;
	bool m_b_http_1_1;
	bool m_b_keepalive;
	std::list<std::pair<std::string, std::string> > m_response_header_append;
	bool m_b_chunked;
	size_t m_chunk_size;
	int m_chunk_state;
	std::string m_chunk_line;
	int m_header_count;
	int m_max_header_count;
};




#ifdef SOCKETS_NAMESPACE
}
#endif

#endif // _SOCKETS_HTTPSocket_H

