#ifndef PACKETPP_HTTP_LAYER
#define PACKETPP_HTTP_LAYER

#include "TextBasedProtocol.h"
#include <string>
#include <exception>

/// @file

/**
 * \namespace pcpp
 * \brief The main namespace for the PcapPlusPlus lib
 */
namespace pcpp
{

	/**
	 * An enum for HTTP version
	 */
	enum HttpVersion
	{
		/** HTTP/0.9 */
		ZeroDotNine,
		/** HTTP/1.0 */
		OneDotZero,
		/** HTTP/1.1 */
		OneDotOne,
		/** Unknown HTTP version */
		HttpVersionUnknown
	};

	// some popular HTTP fields

	/** Host field */
#define PCPP_HTTP_HOST_FIELD 				"Host"
	/** Connection field */
#define PCPP_HTTP_CONNECTION_FIELD 			"Connection"
	/** User-Agent field */
#define PCPP_HTTP_USER_AGENT_FIELD			"User-Agent"
	/** Referer field */
#define PCPP_HTTP_REFERER_FIELD				"Referer"
	/** Accept field */
#define PCPP_HTTP_ACCEPT_FIELD 				"Accept"
	/** Accept-Encoding field */
#define PCPP_HTTP_ACCEPT_ENCODING_FIELD		"Accept-Encoding"
	/** Accept-Language field */
#define PCPP_HTTP_ACCEPT_LANGUAGE_FIELD		"Accept-Language"
	/** Cookie field */
#define PCPP_HTTP_COOKIE_FIELD 				"Cookie"
	/** Content-Length field */
#define PCPP_HTTP_CONTENT_LENGTH_FIELD		"Content-Length"
	/** Content-Encoding field */
#define PCPP_HTTP_CONTENT_ENCODING_FIELD 	"Content-Encoding"
	/** Content-Type field */
#define PCPP_HTTP_CONTENT_TYPE_FIELD		"Content-Type"
	/** Transfer-Encoding field */
#define PCPP_HTTP_TRANSFER_ENCODING_FIELD	"Transfer-Encoding"
	/** Server field */
#define PCPP_HTTP_SERVER_FIELD				"Server"



	// -------- Class HttpMessage -----------------


	/**
	 * @class HttpMessage
	 * Represents a general HTTP message. It's an abstract class and cannot be instantiated. It's inherited by HttpRequestLayer and HttpResponseLayer
	 */
	class HttpMessage : public TextBasedProtocolMessage
	{
	public:

		virtual ~HttpMessage() {}

		/**
		 * A static method that checks whether the port is considered as HTTP
		 * @param[in] port The port number to be checked
		 * @return True if the port matches those associated with the HTTP protocol
		 */
		static bool isHttpPort(uint16_t port) { return port == 80 || port == 8080; }

		// overridden methods

		virtual HeaderField* addField(const std::string& fieldName, const std::string& fieldValue);
		virtual HeaderField* addField(const HeaderField& newField);
		virtual HeaderField* insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue);
		virtual HeaderField* insertField(HeaderField* prevField, const HeaderField& newField);

		OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; }

	protected:
		HttpMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {}
		HttpMessage() : TextBasedProtocolMessage() {}
		HttpMessage(const HttpMessage& other) : TextBasedProtocolMessage(other) {}
		HttpMessage& operator=(const HttpMessage& other) { TextBasedProtocolMessage::operator=(other); return *this; }

		// implementation of abstract methods
		char getHeaderFieldNameValueSeparator() const { return ':'; }
		bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; }
	};




	class HttpRequestFirstLine;



	// -------- Class HttpRequestLayer -----------------

	/**
	 * @class HttpRequestLayer
	 * Represents an HTTP request header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage.
	 * The functionality that is added for this class is the HTTP first line concept. An HTTP request has the following first line:
	 * <i>GET /bla/blabla.asp HTTP/1.1</i>
	 * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpRequestFirstLine.
	 * Unlike most L2-4 protocols, an HTTP request header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header
	 * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpRequestLayer (the other packets
	 * won't be recognized as HttpRequestLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following
	 * packets), this why PcapPlusPlus can indicate that HTTP request header is complete or not(doesn't end with "\r\n\r\n" or "\n\n") using
	 * HttpMessage#isHeaderComplete()
	 */
	class HttpRequestLayer : public HttpMessage
	{
		friend class HttpRequestFirstLine;
	public:
		/**
		 * HTTP request methods
		 */
		enum HttpMethod
		{
			/** GET */
			HttpGET,
			/** HEAD */
			HttpHEAD,
			/** POST */
			HttpPOST,
			/** PUT */
			HttpPUT,
			/** DELETE */
			HttpDELETE,
			/** TRACE */
			HttpTRACE,
			/** OPTIONS */
			HttpOPTIONS,
			/** CONNECT */
			HttpCONNECT,
			/** PATCH */
			HttpPATCH,
			/** Unknown HTTP method */
			HttpMethodUnknown
		};

		 /** A constructor that creates the layer from an existing packet raw data
		 * @param[in] data A pointer to the raw data
		 * @param[in] dataLen Size of the data in bytes
		 * @param[in] prevLayer A pointer to the previous layer
		 * @param[in] packet A pointer to the Packet instance where layer will be stored in
		 */
		HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);

		/**
		 * A constructor that allocates a new HTTP request header with only the first line filled. Object will be created without further fields.
		 * The user can then add fields using addField() methods
		 * @param[in] method The HTTP method used in this HTTP request
		 * @param[in] uri The URI of the first line
		 * @param[in] version HTTP version to be used in this request
		 */
		HttpRequestLayer(HttpMethod method, std::string uri, HttpVersion version);

		virtual ~HttpRequestLayer();

		/**
		 * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and add the functionality
		 * of copying the first line as well
		 * @param[in] other The instance to copy from
		 */
		HttpRequestLayer(const HttpRequestLayer& other);

		/**
		 * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and add the functionality
		 * of copying the first line as well
		 * @param[in] other The instance to copy from
		 * @return A reference to the assignee
		 */
		HttpRequestLayer& operator=(const HttpRequestLayer& other);

		/**
		 * @return A pointer to the first line instance for this message
		 */
		HttpRequestFirstLine* getFirstLine() const { return m_FirstLine; }

		/**
		 * The URL is hostname+uri. So given the following URL, for example: "www.cnn.com/main.html", the hostname is "www.cnn.com" and the URI
		 * is "/.main.html". URI and hostname are split to 2 different places inside the HTTP request packet: URI is in the first line and hostname
		 * is in "HOST" field.
		 * This methods concatenates the hostname and URI to the full URL
		 * @return The URL of the HTTP request message
		 */
		std::string getUrl() const;

		// implement Layer's abstract methods
		std::string toString() const;

	private:
		HttpRequestFirstLine* m_FirstLine;
	};





	// -------- Class HttpResponseLayer -----------------

	class HttpResponseFirstLine;


	/**
	 * @class HttpResponseLayer
	 * Represents an HTTP response header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage.
	 * The functionality that is added for this class is the HTTP first line concept. An HTTP response has the following first line:
	 * <i>200 OK HTTP/1.1</i>
	 * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpResponseFirstLine.
	 * Unlike most L2-4 protocols, an HTTP response header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header
	 * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpResponseLayer (the other packets
	 * won't be recognized as HttpResponseLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following
	 * packets), this why PcapPlusPlus can indicate that HTTP response header is complete or not (doesn't end with "\r\n\r\n" or "\n\n") using
	 * HttpMessage#isHeaderComplete()
	 */
	class HttpResponseLayer : public HttpMessage
	{
		friend class HttpResponseFirstLine;
	public:
		/**
		 * Enum for HTTP response status codes
		 */
		enum HttpResponseStatusCode
		{
			/** 100 Continue*/
			Http100Continue,
			/** 101 Switching Protocols*/
			Http101SwitchingProtocols,
			/** 102 Processing */
			Http102Processing,
			/** 200 OK */
			Http200OK,
			/** 201 Created */
			Http201Created,
			/** 202 Accepted */
			Http202Accepted,
			/** 203 Non-Authoritative Information */
			Http203NonAuthoritativeInformation,
			/** 204 No Content*/
			Http204NoContent,
			/** 205 Reset Content*/
			Http205ResetContent,
			/** 206 Partial Content */
			Http206PartialContent,
			/** 207 Multi-Status */
			Http207MultiStatus,
			/** 208 Already Reported */
			Http208AlreadyReported,
			/** 226 IM Used */
			Http226IMUsed,
			/** 300 Multiple Choices */
			Http300MultipleChoices,
			/** 301 Moved Permanently */
			Http301MovedPermanently,
			/** 302 (various messages) */
			Http302,
			/** 303 See Other */
			Http303SeeOther,
			/** 304 Not Modified */
			Http304NotModified,
			/** 305 Use Proxy */
			Http305UseProxy,
			/** 306 Switch Proxy */
			Http306SwitchProxy,
			/** 307 Temporary Redirect */
			Http307TemporaryRedirect,
			/** 308 Permanent Redirect, */
			Http308PermanentRedirect,
			/** 400 Bad Request */
			Http400BadRequest,
			/** 401 Unauthorized */
			Http401Unauthorized,
			/** 402 Payment Required */
			Http402PaymentRequired,
			/** 403 Forbidden */
			Http403Forbidden,
			/** 404 Not Found */
			Http404NotFound,
			/** 405 Method Not Allowed */
			Http405MethodNotAllowed,
			/** 406 Not Acceptable */
			Http406NotAcceptable,
			/** 407 Proxy Authentication Required */
			Http407ProxyAuthenticationRequired,
			/** 408 Request Timeout */
			Http408RequestTimeout,
			/** 409 Conflict */
			Http409Conflict,
			/** 410 Gone */
			Http410Gone,
			/** 411 Length Required */
			Http411LengthRequired,
			/** 412 Precondition Failed */
			Http412PreconditionFailed,
			/** 413 RequestEntity Too Large */
			Http413RequestEntityTooLarge,
			/** 414 Request-URI Too Long */
			Http414RequestURITooLong,
			/** 415 Unsupported Media Type */
			Http415UnsupportedMediaType,
			/** 416 Requested Range Not Satisfiable */
			Http416RequestedRangeNotSatisfiable,
			/** 417 Expectation Failed */
			Http417ExpectationFailed,
			/** 418 I'm a teapot */
			Http418Imateapot,
			/** 419 Authentication Timeout */
			Http419AuthenticationTimeout,
			/** 420 (various messages) */
			Http420,
			/** 422 Unprocessable Entity */
			Http422UnprocessableEntity,
			/** 423 Locked */
			Http423Locked,
			/** 424 Failed Dependency */
			Http424FailedDependency,
			/** 426 Upgrade Required */
			Http426UpgradeRequired,
			/** 428 Precondition Required */
			Http428PreconditionRequired,
			/** 429 Too Many Requests */
			Http429TooManyRequests,
			/** 431 Request Header Fields Too Large */
			Http431RequestHeaderFieldsTooLarge,
			/** 440 Login Timeout */
			Http440LoginTimeout,
			/** 444 No Response */
			Http444NoResponse,
			/** 449 Retry With */
			Http449RetryWith,
			/** 450 Blocked by Windows Parental Controls */
			Http450BlockedByWindowsParentalControls,
			/** 451 (various messages) */
			Http451,
			/** 494 Request Header Too Large */
			Http494RequestHeaderTooLarge,
			/** 495 Cert Error */
			Http495CertError,
			/** 496 No Cert */
			Http496NoCert,
			/** 497 HTTP to HTTPS */
			Http497HTTPtoHTTPS,
			/** 498 Token expired/invalid */
			Http498TokenExpiredInvalid,
			/** 499 (various messages) */
			Http499,
			/** 500 Internal Server Error */
			Http500InternalServerError,
			/** 501 Not Implemented */
			Http501NotImplemented,
			/** 502 Bad Gateway */
			Http502BadGateway,
			/** 503 Service Unavailable */
			Http503ServiceUnavailable,
			/** 504 Gateway Timeout */
			Http504GatewayTimeout,
			/** 505 HTTP Version Not Supported */
			Http505HTTPVersionNotSupported,
			/** 506 Variant Also Negotiates */
			Http506VariantAlsoNegotiates,
			/** 507 Insufficient Storage */
			Http507InsufficientStorage,
			/** 508 Loop Detected */
			Http508LoopDetected,
			/** 509 Bandwidth Limit Exceeded */
			Http509BandwidthLimitExceeded,
			/** 510 Not Extended */
			Http510NotExtended,
			/** 511 Network Authentication Required */
			Http511NetworkAuthenticationRequired,
			/** 520 Origin Error */
			Http520OriginError,
			/** 521 Web server is down */
			Http521WebServerIsDown,
			/** 522 Connection timed out */
			Http522ConnectionTimedOut,
			/** 523 Proxy Declined Request */
			Http523ProxyDeclinedRequest,
			/** 524 A timeout occurred */
			Http524aTimeoutOccurred,
			/** 598 Network read timeout error */
			Http598NetworkReadTimeoutError,
			/** 599 Network connect timeout error */
			Http599NetworkConnectTimeoutError,
			/** Unknown status code */
			HttpStatusCodeUnknown
		};


		 /** A constructor that creates the layer from an existing packet raw data
		 * @param[in] data A pointer to the raw data
		 * @param[in] dataLen Size of the data in bytes
		 * @param[in] prevLayer A pointer to the previous layer
		 * @param[in] packet A pointer to the Packet instance where layer will be stored in
		 */
		HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);

		/**
		 * A constructor that allocates a new HTTP response header with only the first line filled. Object will be created without further fields.
		 * The user can then add fields using addField() methods
		 * @param[in] version HTTP version to be used
		 * @param[in] statusCode Status code to be used
		 * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK", 404 is usually "Not Found", etc.
		 * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the
		 * default status code string
		 */
		HttpResponseLayer(HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString = "");

		virtual ~HttpResponseLayer();

		/**
		 * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and adds the functionality
		 * of copying the first line as well
		 * @param[in] other The instance to copy from
		 */
		HttpResponseLayer(const HttpResponseLayer& other);

		/**
		 * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and adds the functionality
		 * of copying the first line as well
		 * @param[in] other The instance to copy from
		 * @return A reference to the assignee
		 */
		HttpResponseLayer& operator=(const HttpResponseLayer& other);

		/**
		 * @return A pointer to the first line instance for this message
		 */
		HttpResponseFirstLine* getFirstLine() const { return m_FirstLine; }

		/**
		 * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method sets
		 * The content-length field value. The method supports several cases:
		 * - If the "Content-Length" field exists - the method will only replace the existing value with the new value
		 * - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it. Here are also 2 cases:
		 * 		- If prevFieldName is specified - the new "Content-Length" field will be created after it
		 * 		- If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as the last field before
		 * 		  end-of-header field
		 * @param[in] contentLength The content length value to set
		 * @param[in] prevFieldName Optional field, if specified and "Content-Length" field doesn't exist, it will be created after it
		 * @return A pointer to the "Content-Length" field, or NULL if creation failed for some reason
		 */
		HeaderField* setContentLength(int contentLength, const std::string prevFieldName = "");

		/**
		 * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method
		 * parses this field, extracts its value and return it. If this field doesn't exist the method will return 0
		 * @return HTTP response body length determined by "Content-Length" field
		 */
		int getContentLength() const;

		// implement Layer's abstract methods

		std::string toString() const;

	private:
		HttpResponseFirstLine* m_FirstLine;

	};





	// -------- Class HttpRequestFirstLine -----------------

	/**
	 * @class HttpRequestFirstLine
	 * Represents an HTTP request header first line. The first line includes 3 parameters: HTTP method (e.g GET, POST, etc.),
	 * URI (e.g /main/index.html) and HTTP version (e.g HTTP/1.1). All these parameters are included in this class, and the user
	 * can retrieve or set them.
	 * This class cannot be instantiated by users, it's created inside HttpRequestLayer and user can get a pointer to an instance of it. All "get"
	 * methods of this class will retrieve the actual data of the HTTP request and the "set" methods will change the packet data.
	 * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods
	 * of this class need in most cases to shorten or extend the data in HttpRequestLayer. These methods will return a false value if this
	 * action failed
	 */
	class HttpRequestFirstLine
	{
		friend class HttpRequestLayer;
	public:
		/**
		 * @return The HTTP method
		 */
		HttpRequestLayer::HttpMethod getMethod() const { return m_Method; }

		/**
		 * Set the HTTP method
		 * @param[in] newMethod The method to set
		 * @return False if newMethod is HttpRequestLayer#HttpMethodUnknown or if shortening/extending the HttpRequestLayer data failed. True otherwise
		 */
		bool setMethod(HttpRequestLayer::HttpMethod newMethod);

		/**
		 * @return A copied version of the URI (notice changing the return value won't change the actual data of the packet)
		 */
		std::string getUri() const;

		/**
		 * Set the URI
		 * @param[in] newUri The URI to set
		 * @return False if shortening/extending the HttpRequestLayer data failed. True otherwise
		 */
		bool setUri(std::string newUri);

		/**
		 * @return The HTTP version
		 */
		HttpVersion getVersion() const { return m_Version; }

		/**
		 * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size
		 * (HTTP/0.9, HTTP/1.0, HTTP/1.1)
		 * @param[in] newVersion The HTTP version to set
		 */
		void setVersion(HttpVersion newVersion);

		/**
		 * A static method for parsing the HTTP method out of raw data
		 * @param[in] data The raw data
		 * @param[in] dataLen The raw data length
		 * @return The parsed HTTP method
		 */
		static HttpRequestLayer::HttpMethod parseMethod(char* data, size_t dataLen);

		/**
		 * @return The size in bytes of the HTTP first line
		 */
		int getSize() const { return m_FirstLineEndOffset; }

		/**
		 * As explained in HttpRequestLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet
		 * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication
		 * whether the first line is partial
		 * @return False if the first line is partial, true if it's complete
		 */
		bool isComplete() const { return m_IsComplete; }

		/**
		 * @class HttpRequestFirstLineException
		 * This exception can be thrown while constructing HttpRequestFirstLine (the constructor is private, so the construction happens
		 * only in HttpRequestLayer). This kind of exception will be thrown if trying to construct with HTTP method of
		 * HttpRequestLayer#HttpMethodUnknown or with undefined HTTP version ::HttpVersionUnknown
		 */
		class HttpRequestFirstLineException : public std::exception
		{
		public:
			~HttpRequestFirstLineException() throw() {}
			void setMessage(std::string message) { m_Message = message; }
			virtual const char* what() const throw()
			{
				return m_Message.c_str();
			}
		private:
			std::string m_Message;
		};
	private:
		HttpRequestFirstLine(HttpRequestLayer* httpRequest);
		HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, std::string uri = "/");
			//throw(HttpRequestFirstLineException); // Deprecated in C++17

		void parseVersion();

		HttpRequestLayer* m_HttpRequest;
		HttpRequestLayer::HttpMethod m_Method;
		HttpVersion m_Version;
		int m_VersionOffset;
		int m_UriOffset;
		int m_FirstLineEndOffset;
		bool m_IsComplete;
		HttpRequestFirstLineException m_Exception;
	};





	// -------- Class HttpResponseFirstLine -----------------

	/**
	 * @class HttpResponseFirstLine
	 * Represents an HTTP response header first line. The first line includes 2 parameters: status code (e.g 200 OK, 404 Not Found, etc.),
	 * and HTTP version (e.g HTTP/1.1). These 2 parameters are included in this class, and the user can retrieve or set them.
	 * This class cannot be instantiated by users, it's created inside HttpResponseLayer and user can get a pointer to an instance of it. The "get"
	 * methods of this class will retrieve the actual data of the HTTP response and the "set" methods will change the packet data.
	 * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods
	 * of this class need in most cases to shorten or extend the data in HttpResponseLayer. These methods will return a false value if this
	 * action failed
	 */
	class HttpResponseFirstLine
	{
		friend class HttpResponseLayer;
	public:
		/**
		 * @return The status code as HttpResponseLayer::HttpResponseStatusCode enum
		 */
		HttpResponseLayer::HttpResponseStatusCode getStatusCode() const { return m_StatusCode; }

		/**
		 * @return The status code number as integer (e.g 200, 404, etc.)
		 */
		int getStatusCodeAsInt() const;

		/**
		 * @return The status code message (e.g "OK", "Not Found", etc.)
		 */
		std::string getStatusCodeString() const;

		/**
		 * Set the status code
		 * @param[in] newStatusCode The new status code to set
		 * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If
		 * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set
		 * @return True if setting the status code was completed successfully, false otherwise
		 */
		bool setStatusCode(HttpResponseLayer::HttpResponseStatusCode newStatusCode, std::string statusCodeString = "");

		/**
		 * @return The HTTP version
		 */
		HttpVersion getVersion() const { return m_Version; }

		/**
		 * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size
		 * (HTTP/0.9, HTTP/1.0, HTTP/1.1)
		 * @param[in] newVersion The HTTP version to set
		 */
		void setVersion(HttpVersion newVersion);

		/**
		 * A static method for parsing the HTTP status code out of raw data
		 * @param[in] data The raw data
		 * @param[in] dataLen The raw data length
		 * @return The parsed HTTP status code as enum
		 */
		static HttpResponseLayer::HttpResponseStatusCode parseStatusCode(char* data, size_t dataLen);

		/**
		 * @return The size in bytes of the HTTP first line
		 */
		int getSize() const { return m_FirstLineEndOffset; }

		/**
		 * As explained in HttpResponseLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet
		 * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication
		 * whether the first line is partial
		 * @return False if the first line is partial, true if it's complete
		 */
		bool isComplete() const { return m_IsComplete; }

		/**
		 * @class HttpResponseFirstLineException
		 * This exception can be thrown while constructing HttpResponseFirstLine (the constructor is private, so the construction happens
		 * only in HttpResponseLayer). This kind of exception will be thrown if trying to construct with HTTP status code of
		 * HttpResponseLayer#HttpStatusCodeUnknown or with undefined HTTP version ::HttpVersionUnknown
		 */
		class HttpResponseFirstLineException : public std::exception
		{
		public:
			~HttpResponseFirstLineException() throw() {}
			void setMessage(std::string message) { m_Message = message; }
			virtual const char* what() const throw()
			{
				return m_Message.c_str();
			}
		private:
			std::string m_Message;
		};

	private:
		HttpResponseFirstLine(HttpResponseLayer* httpResponse);
		HttpResponseFirstLine(HttpResponseLayer* httpResponse,  HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString = "");

		static HttpVersion parseVersion(char* data, size_t dataLen);
		static HttpResponseLayer::HttpResponseStatusCode validateStatusCode(char* data, size_t dataLen, HttpResponseLayer::HttpResponseStatusCode potentialCode);


		HttpResponseLayer* m_HttpResponse;
		HttpVersion m_Version;
		HttpResponseLayer::HttpResponseStatusCode m_StatusCode;
		int m_FirstLineEndOffset;
		bool m_IsComplete;
		HttpResponseFirstLineException m_Exception;
	};

} // namespace pcpp

#endif /* PACKETPP_HTTP_LAYER */
