1
0
Fork 0

HTTP connections aren't kept alive unless explicitly enabled.

Only the client can decide that the connection can be kept alive, we must close the socket if the client doesn't indicate keepalive support.
This will provide a fix for #390 when #560 is fixed; until then the issue remains, just it's no longer HTTPServer's fault.
This commit is contained in:
madmaxoft 2014-01-18 20:20:56 +01:00
parent e68521deac
commit fab726282c
4 changed files with 58 additions and 37 deletions

View File

@ -205,6 +205,12 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
{
m_State = wcsRecvIdle;
m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
if (!m_CurrentRequest->DoesAllowKeepAlive())
{
m_State = wcsInvalid;
m_HTTPServer.CloseConnection(*this);
return;
}
delete m_CurrentRequest;
m_CurrentRequest = NULL;
}

View File

@ -41,49 +41,52 @@ public:
cHTTPConnection(cHTTPServer & a_HTTPServer);
virtual ~cHTTPConnection();
/// Sends HTTP status code together with a_Reason (used for HTTP errors)
/** Sends HTTP status code together with a_Reason (used for HTTP errors) */
void SendStatusAndReason(int a_StatusCode, const AString & a_Reason);
/// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm
/** Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm */
void SendNeedAuth(const AString & a_Realm);
/// Sends the headers contained in a_Response
/** Sends the headers contained in a_Response */
void Send(const cHTTPResponse & a_Response);
/// Sends the data as the response (may be called multiple times)
/** Sends the data as the response (may be called multiple times) */
void Send(const void * a_Data, int a_Size);
/// Sends the data as the response (may be called multiple times)
/** Sends the data as the response (may be called multiple times) */
void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); }
/// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive)
/** Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) */
void FinishResponse(void);
/// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd"
/** Resets the internal connection state for a new request.
Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" */
void AwaitNextRequest(void);
/// Terminates the connection; finishes any request being currently processed
/** Terminates the connection; finishes any request being currently processed */
void Terminate(void);
protected:
typedef std::map<AString, AString> cNameValueMap;
/// The parent webserver that is to be notified of events on this connection
/** The parent webserver that is to be notified of events on this connection */
cHTTPServer & m_HTTPServer;
/// All the incoming data until the entire request header is parsed
/** All the incoming data until the entire request header is parsed */
AString m_IncomingHeaderData;
/// Status in which the request currently is
/** Status in which the request currently is */
eState m_State;
/// Data that is queued for sending, once the socket becomes writable
/** Data that is queued for sending, once the socket becomes writable */
AString m_OutgoingData;
/// The request being currently received (valid only between having parsed the headers and finishing receiving the body)
/** The request being currently received
Valid only between having parsed the headers and finishing receiving the body. */
cHTTPRequest * m_CurrentRequest;
/// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody
/** Number of bytes that remain to read for the complete body of the message to be received.
Valid only in wcsRecvBody */
int m_CurrentRequestBodyRemaining;

View File

@ -72,7 +72,8 @@ cHTTPRequest::cHTTPRequest(void) :
m_EnvelopeParser(*this),
m_IsValid(true),
m_UserData(NULL),
m_HasAuth(false)
m_HasAuth(false),
m_AllowKeepAlive(false)
{
}
@ -236,6 +237,10 @@ void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value)
m_HasAuth = true;
}
}
if ((a_Key == "Connection") && (NoCaseCompare(a_Value, "keep-alive") == 0))
{
m_AllowKeepAlive = true;
}
AddHeader(a_Key, a_Value);
}

View File

@ -35,7 +35,7 @@ public:
// Force a virtual destructor in all descendants
virtual ~cHTTPMessage() {};
/// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length
/** Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length */
void AddHeader(const AString & a_Key, const AString & a_Value);
void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; }
@ -51,10 +51,10 @@ protected:
cNameValueMap m_Headers;
/// Type of the content; parsed by AddHeader(), set directly by SetContentLength()
/** Type of the content; parsed by AddHeader(), set directly by SetContentLength() */
AString m_ContentType;
/// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength()
/** Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength() */
int m_ContentLength;
} ;
@ -76,64 +76,71 @@ public:
*/
int ParseHeaders(const char * a_Data, int a_Size);
/// Returns true if the request did contain a Content-Length header
/** Returns true if the request did contain a Content-Length header */
bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); }
/// Returns the method used in the request
/** Returns the method used in the request */
const AString & GetMethod(void) const { return m_Method; }
/// Returns the URL used in the request
/** Returns the URL used in the request */
const AString & GetURL(void) const { return m_URL; }
/// Returns the URL used in the request, without any parameters
/** Returns the URL used in the request, without any parameters */
AString GetBareURL(void) const;
/// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)!
/** Sets the UserData pointer that is stored within this request.
The request doesn't touch this data (doesn't delete it)! */
void SetUserData(void * a_UserData) { m_UserData = a_UserData; }
/// Retrieves the UserData pointer that has been stored within this request.
/** Retrieves the UserData pointer that has been stored within this request. */
void * GetUserData(void) const { return m_UserData; }
/// Returns true if more data is expected for the request headers
/** Returns true if more data is expected for the request headers */
bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); }
/// Returns true if the request did present auth data that was understood by the parser
/** Returns true if the request did present auth data that was understood by the parser */
bool HasAuth(void) const { return m_HasAuth; }
/// Returns the username that the request presented. Only valid if HasAuth() is true
/** Returns the username that the request presented. Only valid if HasAuth() is true */
const AString & GetAuthUsername(void) const { return m_AuthUsername; }
/// Returns the password that the request presented. Only valid if HasAuth() is true
/** Returns the password that the request presented. Only valid if HasAuth() is true */
const AString & GetAuthPassword(void) const { return m_AuthPassword; }
bool DoesAllowKeepAlive(void) const { return m_AllowKeepAlive; }
protected:
/// Parser for the envelope data
/** Parser for the envelope data */
cEnvelopeParser m_EnvelopeParser;
/// True if the data received so far is parsed successfully. When false, all further parsing is skipped
/** True if the data received so far is parsed successfully. When false, all further parsing is skipped */
bool m_IsValid;
/// Bufferred incoming data, while parsing for the request line
/** Bufferred incoming data, while parsing for the request line */
AString m_IncomingHeaderData;
/// Method of the request (GET / PUT / POST / ...)
/** Method of the request (GET / PUT / POST / ...) */
AString m_Method;
/// Full URL of the request
/** Full URL of the request */
AString m_URL;
/// Data that the HTTPServer callbacks are allowed to store.
/** Data that the HTTPServer callbacks are allowed to store. */
void * m_UserData;
/// Set to true if the request contains auth data that was understood by the parser
/** Set to true if the request contains auth data that was understood by the parser */
bool m_HasAuth;
/// The username used for auth
/** The username used for auth */
AString m_AuthUsername;
/// The password used for auth
/** The password used for auth */
AString m_AuthPassword;
/** Set to true if the request indicated that it supports keepalives.
If false, the server will close the connection once the request is finished */
bool m_AllowKeepAlive;
/** Parses the incoming data for the first line (RequestLine)
Returns the number of bytes consumed, or -1 for an error