/*
**==============================================================================
**
** Open Management Infrastructure (OMI)
**
** Copyright (c) Microsoft Corporation
**
** Licensed under the Apache License, Version 2.0 (the "License"); you may not
** use this file except in compliance with the License. You may obtain a copy
** of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
** MERCHANTABLITY OR NON-INFRINGEMENT.
**
** See the Apache 2 License for the specific language governing permissions
** and limitations under the License.
**
**==============================================================================
*/
#include <assert.h>
#include <ctype.h>
#include "http.h"
#include "addr.h"
#include "sock.h"
#include "selector.h"
#include <base/time.h>
#include <base/buf.h>
#include <base/log.h>
#include <base/result.h>
#include <base/strings.h>
#include <base/io.h>
#include <base/paths.h>
#ifdef CONFIG_POSIX
#include <openssl/ssl.h>
#include <openssl/err.h>
#else
/* ssl not supported in this configuration; just make compiler happy */
typedef void SSL;
typedef void SSL_CTX;
#define SSL_CTX_free(c)
#define SSL_new(c) 0
#define SSL_free(c)
#define SSL_set_fd(c,a) (a==a)
#define SSL_read(c,a,b) 0
#define SSL_write(c,a,b) 0
#define SSL_get_error(c,e) e
#define SSL_ERROR_WANT_WRITE 0
#define SSL_ERROR_WANT_READ 1
#define SSL_ERROR_SYSCALL 2
#ifdef EWOULDBLOCK
# undef EWOULDBLOCK
#endif
#define EWOULDBLOCK 0
#ifdef EINPROGRESS
# undef EINPROGRESS
#endif
#define EINPROGRESS 0
#define ERR_get_error() 0
#define ERR_error_string_n(c,a,b) a[0]=0
#define SSL_accept(c) 0
#endif
#define T MI_T
/* #define ENABLE_TRACING // */
#ifdef ENABLE_TRACING
#define PRINTF(a) TIMESTAMP(); printf a
#define TIMESTAMP() \
{\
MI_Uint64 currentTimeUsec = 0;\
\
Time_Now(¤tTimeUsec);\
currentTimeUsec /= 1000; /* ms */ \
printf("%ds%03dms ", (int)(currentTimeUsec / 1000 % 1000), (int)(currentTimeUsec % 1000));\
}
#define PRINTF_2(a)
#else
#define PRINTF(a)
#define PRINTF_2(a)
#endif
/*
**==============================================================================
**
** Local definitions:
**
**==============================================================================
*/
static const MI_Uint32 _MAGIC = 0xE0BB5FD3;
static const MI_Uint32 MAX_HEADER_SIZE = 2 * 1024;
static const MI_Uint32 INITIAL_BUFFER_SIZE = 2 * 1024;
static const size_t HTTP_MAX_CONTENT = 1024 * 1024;
struct _Http
{
MI_Uint32 magic;
Selector internalSelector;
Selector* selector;
HttpCallbackOnNewConnection callbackOnNewConnection;
HttpCallbackOnCloseConnection callbackOnCloseConnection;
HttpCallbackOnRequest callbackOnRequest;
void* callbackData;
SSL_CTX* sslContext;
/* options: timeouts etc */
HttpOptions options;
MI_Boolean internalSelectorUsed;
};
typedef struct _Http_Listener_SocketData
{
/* based member*/
Handler base;
MI_Boolean secure;
}
Http_Listener_SocketData;
typedef enum _Http_RecvState
{
RECV_STATE_HEADER,
RECV_STATE_CONTENT
}
Http_RecvState;
typedef struct _Http_SR_SocketData
{
/* based member*/
Handler base;
/* ssl part */
SSL* ssl;
MI_Boolean reverseOperations; /*reverse read/write Events/Handlers*/
MI_Boolean acceptDone;
/* is server/provider is processing request
(to disbale timeout) */
MI_Boolean requestIsBeingProcessed;
/* link to next stack layer */
void* connectionData;
/* receiving data */
char* recvBuffer;
size_t recvBufferSize;
size_t recvievedSize;
Http_RecvState recvingState;
HttpHeaders recvHeaders;
Page* recvPage;
/* sending part */
Page* sendPage;
size_t sentSize;
Http_RecvState sendingState;
int httpErrorCode;
}
Http_SR_SocketData;
/* helper functions result */
typedef enum _Http_CallbackResult
{
PRT_CONTINUE,
PRT_RETURN_TRUE,
PRT_RETURN_FALSE
}
Http_CallbackResult;
MI_INLINE MI_Uint8 _ToLower(MI_Uint8 x)
{
return (MI_Uint8)tolower(x);
}
#define _HashCode(first,last,len) ( (((MI_Uint8)first) << 16) | (((MI_Uint8)last) << 8) | (((MI_Uint16)len)) )
static MI_Boolean _DecodeBasicAuth(
HttpHeaders* recvHeaders,
const char * src,
char* tgt)
{
/* decoding array 0-63 - valid value, 64-padding, 65-skip, -1-invalid */
static const char s_decodingArray[] =
{
/* 0 - 31 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 32 - 39 !"#$%&' */
-1, -1, -1, -1, -1, -1, -1, -1,
/* ()*+,-./ */
-1, -1, -1, 62, -1, -1, -1, 63,
/* 0 -9 */
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
/* :;<=>? */
-1,-1,-1,64,-1,-1,
/* @ A-Z */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
/* []\\^_` */
-1,-1,-1,-1,-1,-1,
/* a-z */
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
/* {|} ~ 127 */
-1, -1, -1, -1, -1
};
char* startBuffer = tgt;
/* skip spaces in value */
while (src[0] == ' ' || src[0] == '\t')
src++;
/* perform decoding - 4 input bytes are converted into 3 output,
using porvided pointers src/tgt
since decoding is done inplace and result is smaller than input,
no checks for buffer size is required */
while (*src)
{
signed char b1, b2, b3, b4;
b1 = s_decodingArray[(*src++) & 0x7f];
if (b1 < 0 )
return MI_FALSE;
b2 = s_decodingArray[(*src++) & 0x7f];
if (b2 < 0 )
return MI_FALSE;
b3 = s_decodingArray[(*src++) & 0x7f];
if (b3 < 0 )
return MI_FALSE;
b4 = s_decodingArray[(*src++) & 0x7f];
if (b4 < 0 )
return MI_FALSE;
*tgt++ = (b1 << 2) | (b2 >> 4);
if (b3 != 64)
*tgt++ = ((b2&0xF) << 4) | (b3 >> 2);
if (b4 != 64)
*tgt++ = ((b3&0x3) << 6) | (b4);
}
*tgt = 0;
/* decoded string has format:
<uersname>[:<password>] */
recvHeaders->username = startBuffer;
startBuffer = strchr(startBuffer, ':');
if ( startBuffer )
{
*startBuffer = 0;
recvHeaders->password = startBuffer + 1;
}
return MI_TRUE;
}
static MI_Boolean _getNameValuePair(
char ** line,
char ** value,
int* nameHashCode )
{
int len = 0;
char* p;
/* find name end /hash-code */
*nameHashCode = _ToLower((MI_Uint8)(*line)[0])<<16;
for (len = 1; (*line)[len] != ':' && (*line)[len] != '\r'; len++ )
;
if ((*line)[len] != ':')
return MI_FALSE;
*nameHashCode |= (len) | _ToLower((MI_Uint8)(*line)[len-1])<<8;
(*line)[len] = 0;
p = *line + len + 1;
/* skip spaces in value */
while (p[0] == ' ' || p[0] == '\t')
p++;
*value = p;
/* skip to end of line */
for ( ; ; )
{
if (p[0] == '\r' && p[1] == '\n' &&
(p[2] != ' ' && p[2] != '\t') )
{
p[0] = 0;
(*line) = p + 2;
break;
}
p ++;
}
/* remove trailing spaces */
p--;
while (p[0] == ' ' || p[0] == '\t')
p--;
p[1] = 0;
return MI_TRUE;
}
static void _ParseContentType(
HttpHeaders* recvHeaders,
char* value)
{
recvHeaders->contentType = value;
/* find attribute list */
while (value[0] != 0 && value[0] != ';')
value++;
/* Check if attribute list was provided */
if (value[0] == 0)
return;
/* terminate type/subtype; move to attributes list */
value[0] = 0;
value++;
/* skip spaces in value */
while (value[0] == ' ' || value[0] == '\t')
value++;
/* find charset attribute (if present) */
if (Strncasecmp(value,"charset=",8) != 0)
return;
value += 8;
recvHeaders->charset = value;
/* can be enclosed in quotes */
if (value[0] == '"')
{
/* skip '"' */
recvHeaders->charset++;
value++;
value = strchr(value, '"');
if (value)
*value = 0;
else
recvHeaders->charset = 0;
}
else
{
/*skip trialing spaces */
while (value[0] != 0 && value[0] != ' ' && value[0] != '\t')
value++;
*value = 0;
}
}
static MI_Boolean _getHeaderField(
Http_SR_SocketData* handler,
char ** line)
{
char* name = *line;
char* value = NULL;
int nameHashCode;
if (!_getNameValuePair(line, &value, &nameHashCode))
return MI_FALSE;
switch (nameHashCode)
{
case (_HashCode('c','e',12)): /*Content-Type*/
if (Strcasecmp(name,"Content-Type") == 0)
_ParseContentType(&handler->recvHeaders, value);
break;
case (_HashCode('c','h',14)): /*Content-Length*/
if (Strcasecmp(name,"Content-Length") == 0)
{
handler->recvHeaders.contentLength = (size_t)Strtoull(value, NULL, 10);
if ( handler->recvHeaders.contentLength > HTTP_MAX_CONTENT )
return MI_FALSE;
}
break;
case (_HashCode('a','n',13)): /*Authorization*/
if (Strcasecmp(name,"Authorization") == 0)
{
if (Strncasecmp(value,"Basic",5) == 0)
{
/* since decoded string is smaller, performing decoding inplace;
for source skip 'Basic ' part (6 chars) */
if (!_DecodeBasicAuth(&handler->recvHeaders, value + 6, value))
{
LOGW_CHAR(("base64 decoding error in Basic auth: [%s]\n", value));
return MI_FALSE;
}
}
else /* unknown authorization type */
handler->recvHeaders.authorization = value;
}
break;
default:
break;
}
return MI_TRUE;
}
static MI_Boolean _getRequestLine(
Http_SR_SocketData* handler,
char ** line)
{
size_t index;
/* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF
Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv
*/
/* skip to end of line */
for ( index = 1; index < handler->recvievedSize; index++ )
{
if ((*line)[index-1] == '\r' && (*line)[index] == '\n' )
{
(*line) = (*line) + index + 1;
return MI_TRUE;
}
}
return MI_FALSE;
}
static MI_Result _Sock_Read(
Http_SR_SocketData* handler,
void* buf,
size_t buf_size,
size_t* sizeRead)
{
int res;
if (!handler->ssl)
return Sock_Read(handler->base.sock, buf, buf_size, sizeRead);
handler->base.mask &= ~SELECTOR_WRITE;
handler->base.mask |= SELECTOR_READ;
handler->reverseOperations = MI_FALSE;
*sizeRead = 0;
if (handler->acceptDone)
{
res = SSL_read(handler->ssl, buf, buf_size);
PRINTF(("ssl read %d\n", res));
}
else
{
res = SSL_accept(handler->ssl);
PRINTF(("ssl accept %d\n", res));
if ( res > 0 )
{
/* we are done with accpet */
handler->acceptDone = MI_TRUE;
return _Sock_Read(handler,buf,buf_size,sizeRead);
}
/* perform regular error checking */
}
if ( res == 0 )
return MI_RESULT_OK; /* connection closed */
if ( res > 0 )
{
*sizeRead = res;
return MI_RESULT_OK; /* ok */
}
switch (SSL_get_error(handler->ssl, res))
{
case SSL_ERROR_WANT_WRITE:
handler->reverseOperations = MI_TRUE; /* wait until write is allowed */
handler->base.mask |= SELECTOR_WRITE;
handler->base.mask &= ~SELECTOR_READ;
PRINTF(("ssl read/accept WANT_WRITE\n"));
return MI_RESULT_WOULD_BLOCK;
case SSL_ERROR_WANT_READ:
PRINTF(("ssl read/accept WANT_READ\n"));
return MI_RESULT_WOULD_BLOCK;
case SSL_ERROR_SYSCALL:
if (EAGAIN == errno ||
EWOULDBLOCK == errno ||
EINPROGRESS == errno)
return MI_RESULT_WOULD_BLOCK;
LOGW_CHAR(("ssl-read: unexpected sys error %d\n", errno));
break;
default:
{
/* print error */
unsigned long err = ERR_get_error();
while (err)
{
char err_txt[200];
ERR_error_string_n(err, err_txt, sizeof(err_txt));
LOGW_CHAR(("ssl-read error: %d [%s]\n", (int)err, err_txt));
err = ERR_get_error();
}
}
break;
}
return MI_RESULT_FAILED;
}
static MI_Result _Sock_Write(
Http_SR_SocketData* handler,
void* buf,
size_t buf_size,
size_t* sizeWritten)
{
int res;
if (!handler->ssl)
return Sock_Write(handler->base.sock, buf, buf_size, sizeWritten);
/* Do not clear READ flag, since 'close' notification
delivered as READ event*/
handler->base.mask &= ~SELECTOR_READ;
handler->base.mask |= SELECTOR_WRITE;
handler->reverseOperations = MI_FALSE;
*sizeWritten = 0;
res = SSL_write(handler->ssl, buf, buf_size);
PRINTF(("ssl write %d\n", res));
if ( res == 0 )
return MI_RESULT_OK; /* connection closed */
if ( res > 0 )
{
*sizeWritten = res;
return MI_RESULT_OK; /* ok */
}
switch (SSL_get_error(handler->ssl, res))
{
case SSL_ERROR_WANT_WRITE:
return MI_RESULT_WOULD_BLOCK;
case SSL_ERROR_WANT_READ:
handler->reverseOperations = MI_TRUE; /* wait until write is allowed */
handler->base.mask |= SELECTOR_READ;
handler->base.mask &= ~SELECTOR_WRITE;
return MI_RESULT_WOULD_BLOCK;
case SSL_ERROR_SYSCALL:
if (EAGAIN == errno ||
EWOULDBLOCK == errno ||
EINPROGRESS == errno)
return MI_RESULT_WOULD_BLOCK;
LOGW_CHAR(("ssl-write: unexpected sys error %d\n", errno));
break;
default:
break;
}
return MI_RESULT_FAILED;
}
static Http_CallbackResult _ReadHeader(
Http_SR_SocketData* handler)
{
char* buf;
char* currentLine;
char* data;
size_t buf_size, received, index;
MI_Result r;
MI_Boolean fullHeaderReceived = MI_FALSE;
/* are we done with header? */
if (handler->recvingState == RECV_STATE_CONTENT)
return PRT_CONTINUE;
buf = handler->recvBuffer + handler->recvievedSize;
buf_size = handler->recvBufferSize - handler->recvievedSize;
received = 0;
r = _Sock_Read(handler, buf, buf_size, &received);
PRINTF(("%d: read r = %d, recv = %d; reverse %d\n", (int)handler->base.sock, (int)r, (int)received, (int)handler->reverseOperations ));
if ( r == MI_RESULT_OK && 0 == received )
return PRT_RETURN_FALSE; /* conection closed */
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK )
return PRT_RETURN_FALSE;
if (!received)
return PRT_RETURN_TRUE;
handler->recvievedSize += received;
/* check header */
PRINTF_2(("%s\n",buf));
/* did we get full header? */
buf = handler->recvBuffer;
for ( index = 3; index < handler->recvievedSize; index++ )
{
if (buf[index-3] == '\r' && buf[index-1] == '\r' &&
buf[index-2] == '\n' && buf[index] == '\n' )
{
fullHeaderReceived = MI_TRUE;
break;
}
}
if (!fullHeaderReceived )
{
if ( handler->recvievedSize < handler->recvBufferSize )
return PRT_RETURN_TRUE; /* continue reading */
if ( handler->recvBufferSize < MAX_HEADER_SIZE )
{
buf = realloc(handler->recvBuffer, handler->recvBufferSize * 2);
if (!buf)
return PRT_RETURN_FALSE;
handler->recvBufferSize *= 2;
handler->recvBuffer = buf;
return _ReadHeader(handler);
}
else
{
/* http header is too big - drop connection */
LOGW((T("http header is too big; dropping connection\n")));
return PRT_RETURN_FALSE;
}
}
/* consume data */
currentLine = buf;
data = buf + index + 1; /* pointer to data in case we got some */
if (!_getRequestLine(handler, ¤tLine))
return PRT_RETURN_FALSE;
while ((data-currentLine) > 3)
{
if (!_getHeaderField(handler, ¤tLine))
return PRT_RETURN_FALSE;
}
/* Allocate zero-terminated buffer */
handler->recvPage = (Page*)malloc(sizeof(Page) + handler->recvHeaders.contentLength + 1);
if (!handler->recvPage)
return PRT_RETURN_FALSE;
((char*)(handler->recvPage + 1))[handler->recvHeaders.contentLength] = 0;
handler->recvPage->u.s.size = (unsigned int)handler->recvHeaders.contentLength;
handler->recvPage->u.s.next = 0;
handler->recvievedSize -= index + 1;
/* Verify that we have not more than 'content-length' bytes in buffer left
If we hvae more, assuming http client is invalid and drop connection */
if (handler->recvievedSize > handler->recvHeaders.contentLength)
{
LOGW((T("http payload is bigger than content-length\n")));
return PRT_RETURN_FALSE;
}
memcpy( handler->recvPage + 1, data, handler->recvievedSize );
handler->recvingState = RECV_STATE_CONTENT;
PRINTF_2(("full header read; page size %d, position %d\n", handler->recvPage->u.s.size, handler->recvievedSize));
return PRT_CONTINUE;
}
static Http_CallbackResult _ReadData(
Http_SR_SocketData* handler)
{
Http* self = (Http*)handler->base.data;
char* buf;
size_t buf_size, received;
MI_Result r;
/* are we in the right state? */
if (handler->recvingState != RECV_STATE_CONTENT)
return PRT_RETURN_FALSE;
buf = ((char*)(handler->recvPage + 1)) + handler->recvievedSize;
buf_size = handler->recvHeaders.contentLength - handler->recvievedSize;
received = 0;
if (buf_size)
{
r = _Sock_Read(handler, buf, buf_size, &received);
PRINTF(("%d: read r = %d, recv = %d\n", (int)handler->base.sock, (int)r, (int)received ));
if ( r == MI_RESULT_OK && 0 == received )
return PRT_RETURN_FALSE; /* conection closed */
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK )
return PRT_RETURN_FALSE;
handler->recvievedSize += received;
}
/* did we get all data? */
PRINTF_2(("dt status - %d / %d\n", (int)handler->recvievedSize, (int)handler->recvHeaders.contentLength));
if ( handler->recvievedSize != handler->recvHeaders.contentLength )
return PRT_RETURN_TRUE;
handler->requestIsBeingProcessed = MI_TRUE;
(*self->callbackOnRequest)( self, self->callbackData, handler->connectionData, handler, &handler->recvHeaders,
&handler->recvPage );
if (handler->recvPage)
free(handler->recvPage);
handler->recvPage = 0;
handler->recvievedSize = 0;
memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders));
handler->recvingState = RECV_STATE_HEADER;
return PRT_CONTINUE;
}
static MI_Boolean _RequestCallbackRead(
Http_SR_SocketData* handler)
{
switch (_ReadHeader(handler))
{
case PRT_CONTINUE: break;
case PRT_RETURN_TRUE: return MI_TRUE;
case PRT_RETURN_FALSE: return MI_FALSE;
}
switch (_ReadData(handler))
{
case PRT_CONTINUE: break;
case PRT_RETURN_TRUE: return MI_TRUE;
case PRT_RETURN_FALSE: return MI_FALSE;
}
return MI_TRUE;
}
/* length of longest description - has to be updated if descriptions are updated */
#define HTTP_LONGEST_ERROR_DESCRIPTION 50
static const char* _GetHttpErrorCodeDescription(
int httpErrorCode )
{
switch (httpErrorCode)
{
case 200:
return "OK";
case 400:
return "Bad Request";
case 401:
return "Unauthorized";
case 500:
return "Internal Server Error";
}
return "Error";
}
static Http_CallbackResult _WriteHeader(
Http_SR_SocketData* handler)
{
#define RESPONSE_HEADER_FMT \
"HTTP/1.1 %d %s\r\n" \
"Content-Length: %d\r\n"\
"Connection: Keep-Alive\r\n"\
"Content-Type: application/soap+xml;charset=UTF-8\r\n"\
"\r\n"
/* "SOAPAction: http://schemas.xmlsoap.org/ws/2004/08/addressing/fault\r\n"\ */
char currentLine[sizeof(RESPONSE_HEADER_FMT) +
10 /* content length */ +
10 /*error code*/ +
HTTP_LONGEST_ERROR_DESCRIPTION /* code descirpiton */ ];
char* buf;
size_t buf_size, sent;
MI_Result r;
/* Do we have any data to send? */
if (!handler->sendPage && 0 == handler->httpErrorCode)
return PRT_RETURN_TRUE;
/* are we done with header? */
if (handler->sendingState == RECV_STATE_CONTENT)
return PRT_CONTINUE;
if (handler->sendPage)
{
buf_size = (size_t)Snprintf(
currentLine,
sizeof(currentLine),
RESPONSE_HEADER_FMT,
(int)handler->httpErrorCode,
_GetHttpErrorCodeDescription(handler->httpErrorCode),
(int)handler->sendPage->u.s.size );
}
else
{
buf_size = (size_t)Snprintf(
currentLine,
sizeof(currentLine),
"HTTP/1.1 %d %s\r\n\r\n",
(int)handler->httpErrorCode,
_GetHttpErrorCodeDescription(handler->httpErrorCode));
}
buf = currentLine + handler->sentSize;
sent = 0;
r = _Sock_Write(handler, buf, buf_size - handler->sentSize, &sent);
PRINTF(("%d: write r = %d, sent = %d; reverse %d\n", (int)handler->base.sock, (int)r, (int)sent, (int)handler->reverseOperations ));
if ( r == MI_RESULT_OK && 0 == sent )
return PRT_RETURN_FALSE; /* conection closed */
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK )
return PRT_RETURN_FALSE;
if (!sent)
return PRT_RETURN_TRUE;
handler->sentSize += sent;
if (handler->sentSize < buf_size)
return PRT_RETURN_TRUE;
handler->sentSize = 0;
handler->sendingState = RECV_STATE_CONTENT;
return PRT_CONTINUE;
}
static Http_CallbackResult _WriteData(
Http_SR_SocketData* handler)
{
char* buf;
size_t buf_size, sent;
MI_Result r;
/* are we in the right state? */
if (handler->sendingState != RECV_STATE_CONTENT)
return PRT_RETURN_FALSE;
if (!handler->sendPage)
{ /* no content*/
handler->httpErrorCode = 0;
handler->sentSize = 0;
handler->sendingState = RECV_STATE_HEADER;
handler->base.mask &= ~SELECTOR_WRITE;
handler->base.mask |= SELECTOR_READ;
return PRT_CONTINUE;
}
buf = ((char*)(handler->sendPage + 1)) + handler->sentSize;
buf_size = handler->sendPage->u.s.size - handler->sentSize;
sent = 0;
r = _Sock_Write(handler, buf, buf_size, &sent);
PRINTF(("%d: write r = %d, sent = %d\n", (int)handler->base.sock, (int)r, (int)sent ));
if ( r == MI_RESULT_OK && 0 == sent )
return PRT_RETURN_FALSE; /* conection closed */
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK )
return PRT_RETURN_FALSE;
handler->sentSize += sent;
/* did we get all data? */
if ( handler->sentSize != handler->sendPage->u.s.size )
return PRT_RETURN_TRUE;
free(handler->sendPage);
handler->sendPage = 0;
handler->httpErrorCode = 0;
handler->sentSize = 0;
handler->sendingState = RECV_STATE_HEADER;
handler->base.mask &= ~SELECTOR_WRITE;
handler->base.mask |= SELECTOR_READ;
return PRT_CONTINUE;
}
static MI_Boolean _RequestCallbackWrite(
Http_SR_SocketData* handler)
{
switch (_WriteHeader(handler))
{
case PRT_CONTINUE: break;
case PRT_RETURN_TRUE: return MI_TRUE;
case PRT_RETURN_FALSE: return MI_FALSE;
}
switch (_WriteData(handler))
{
case PRT_CONTINUE: break;
case PRT_RETURN_TRUE: return MI_TRUE;
case PRT_RETURN_FALSE: return MI_FALSE;
}
return MI_TRUE;
}
static MI_Boolean _RequestCallback(
Selector* sel,
Handler* handlerIn,
MI_Uint32 mask,
MI_Uint64 currentTimeUsec)
{
Http_SR_SocketData* handler = (Http_SR_SocketData*)handlerIn;
sel=sel;
if ( ((mask & SELECTOR_READ) != 0 && !handler->reverseOperations) ||
((mask & SELECTOR_WRITE) != 0 && handler->reverseOperations) )
{
if (!_RequestCallbackRead(handler))
return MI_FALSE;
}
if ( ((mask & SELECTOR_WRITE) != 0 && !handler->reverseOperations) ||
((mask & SELECTOR_READ) != 0 && handler->reverseOperations) )
{
if (!_RequestCallbackWrite(handler))
return MI_FALSE;
}
/* re-set timeout - if we performed R/W operation, set timeout depending where we are in communication */
if (mask & (SELECTOR_READ | SELECTOR_WRITE))
{
Http* self = (Http*)handler->base.data;
if (handler->requestIsBeingProcessed)
{
/* since request is processed by server, disable timeout for this period */
handler->base.fireTimeoutAt = TIME_NEVER;
}
else
{
/* Use configuration timeout */
handler->base.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec;
}
}
/* Close conenction by timeout */
if (mask & SELECTOR_TIMEOUT)
return MI_FALSE;
if ((mask & SELECTOR_REMOVE) != 0 ||
(mask & SELECTOR_DESTROY) != 0)
{
Http* self = (Http*)handler->base.data;
/* notify next stack layer */
(*self->callbackOnCloseConnection)(
self,
self->callbackData,
handler->connectionData );
if (handler->ssl)
SSL_free(handler->ssl);
PRINTF(("%d: close\n", (int)handler->base.sock));
Sock_Close(handler->base.sock);
if (handler->recvPage)
free(handler->recvPage);
if (handler->sendPage)
free(handler->sendPage);
free(handler->recvBuffer);
free(handler);
}
return MI_TRUE;
}
static MI_Boolean _ListenerCallback(
Selector* sel,
Handler* handler_,
MI_Uint32 mask,
MI_Uint64 currentTimeUsec)
{
Http_Listener_SocketData* handler = (Http_Listener_SocketData*)handler_;
Http* self = (Http*)handler->base.data;
MI_Result r;
Sock s;
Addr addr;
Http_SR_SocketData* h;
sel=sel;
mask=mask;
currentTimeUsec = currentTimeUsec;
if (mask & SELECTOR_READ)
{
int count;
for (count = 0; count < 5; count++)
{
/* Accept the incoming connection */
r = Sock_Accept(handler->base.sock, &s, &addr);
PRINTF(("%d: accept r = %d\n", (int)s, (int)r ));
if (MI_RESULT_WOULD_BLOCK == r)
return MI_TRUE;
if (r != MI_RESULT_OK)
{
LOGW((T("Sock_Accept() failed; err %d\n"), Sock_GetLastError()));
return MI_TRUE;
}
r = Sock_SetBlocking(s, MI_FALSE);
if (r != MI_RESULT_OK)
{
LOGW((T("Sock_SetBlocking() failed\n")));
Sock_Close(s);
return MI_TRUE;
}
/* Create handler */
h = (Http_SR_SocketData*)calloc(1, sizeof(Http_SR_SocketData));
if (!h)
{
Sock_Close(s);
return MI_TRUE;
}
h->recvBufferSize = INITIAL_BUFFER_SIZE;
h->recvBuffer = (char*)calloc(1, h->recvBufferSize);
if (!h->recvBuffer)
{
free(h);
Sock_Close(s);
return MI_TRUE;
}
h->base.sock = s;
h->base.mask = SELECTOR_READ | SELECTOR_EXCEPTION;
h->base.callback = _RequestCallback;
h->base.data = self;
h->base.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec;
/* ssl support */
if (handler->secure)
{
h->ssl = SSL_new(self->sslContext);
if (!h->ssl)
{
LOGW((T("ssl_new() failed\n")));
free(h);
Sock_Close(s);
return MI_TRUE;
}
if (!(SSL_set_fd(h->ssl, s) ))
{
LOGW((T("ssl_set_fd() failed\n")));
SSL_free(h->ssl);
free(h);
Sock_Close(s);
return MI_TRUE;
}
}
/* Watch for read events on the incoming connection */
r = Selector_AddHandler(self->selector, &h->base);
if (r != MI_RESULT_OK)
{
LOGW((T("Selector_AddHandler() failed\n")));
if (handler->secure)
SSL_free(h->ssl);
free(h);
Sock_Close(s);
return MI_TRUE;
}
/* notify next stack layer about new connection */
(*self->callbackOnNewConnection)(
self,
self->callbackData,
h,
&h->connectionData );
}
}
if ((mask & SELECTOR_REMOVE) != 0 ||
(mask & SELECTOR_DESTROY) != 0)
{
Sock_Close(handler->base.sock);
free(handler);
}
return MI_TRUE;
}
static MI_Result _New_Http(
Http** selfOut,
Selector* selector, /*optional, maybe NULL*/
HttpCallbackOnNewConnection callbackOnNewConnection,
HttpCallbackOnCloseConnection callbackOnCloseConnection,
HttpCallbackOnRequest callbackOnRequest,
void* callbackData)
{
Http* self;
/* Check parameters */
if (!selfOut)
return MI_RESULT_INVALID_PARAMETER;
/* Clear output parameter */
*selfOut = NULL;
/* Allocate structure */
{
self = (Http*)calloc(1, sizeof(Http));
if (!self)
return MI_RESULT_FAILED;
}
if (selector)
{ /* attach the exisiting selector */
self->selector = selector;
self->internalSelectorUsed = MI_FALSE;
}
else
{ /* creaet a new selector */
/* Initialize the network */
Sock_Start();
/* Initialize the selector */
if (Selector_Init(&self->internalSelector) != MI_RESULT_OK)
{
free(self);
return MI_RESULT_FAILED;
}
self->selector = &self->internalSelector;
self->internalSelectorUsed = MI_TRUE;
}
/* Save the callback and callbackData */
self->callbackOnRequest = callbackOnRequest;
self->callbackOnCloseConnection = callbackOnCloseConnection;
self->callbackOnNewConnection = callbackOnNewConnection;
self->callbackData = callbackData;
/* Set the magic number */
self->magic = _MAGIC;
/* options */
{
HttpOptions options = DEFAULT_HTTP_OPTIONS;
self->options = options;
}
/* Set output parameter */
*selfOut = self;
return MI_RESULT_OK;
}
#ifdef CONFIG_POSIX
static MI_Boolean _verifyPrivateKey(
SSL_CTX *ctx,
const char* keyPath)
{
// Open the private key file.
FILE* is = fopen(keyPath, "r");
if (!is)
{
LOGE_CHAR((
"---> SSL: failed to open private key file: %s",
keyPath));
return MI_FALSE;
}
// Read the private key from the input stream.
EVP_PKEY* pkey;
pkey = PEM_read_PrivateKey(is, NULL, NULL, NULL);
if (!pkey)
{
LOGE_CHAR(("---> SSL: failed to create private key"));
return MI_FALSE;
}
/* Close the input stream. */
fclose(is);
/* Associate the new private key with the SSL context object. */
if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0)
{
EVP_PKEY_free(pkey);
LOGE_CHAR((
"---> SSL: no private key found in %s",
keyPath));
return MI_FALSE;
}
EVP_PKEY_free(pkey);
/* Check private key for validity. */
if (!SSL_CTX_check_private_key(ctx))
{
LOGE_CHAR((
"---> SSL: Private and public key do not match"));
return MI_FALSE;
}
return MI_TRUE;
}
static MI_Result _CreateSSLContext(Http* self)
{
SSL_CTX * sslContext = 0;
sslContext = SSL_CTX_new(SSLv23_method());
if (!sslContext)
{
LOGE_CHAR((
"---> SSL: cannot create ssl context"));
return MI_RESULT_FAILED;
}
SSL_CTX_set_quiet_shutdown(sslContext, 1);
SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF);
/* Check if there is a certificate file (file containing server
** certificate) specified. If specified, validate and load the
** certificate.
*/
{
/* load the specified server certificates */
LOGI_CHAR(("---> SSL: Loading server certificate from: %s",
GetPath(ID_PEMFILE)));
if (SSL_CTX_use_certificate_file(sslContext,
GetPath(ID_PEMFILE), SSL_FILETYPE_PEM) <=0)
{
LOGE_CHAR(("---> SSL: No server certificate found in %s",
GetPath(ID_PEMFILE)));
SSL_CTX_free(sslContext);
return MI_RESULT_FAILED;
}
}
/*
** Check if there is a key file (file containing server
** private key) specified and the key was not already loaded.
** If specified, validate and load the key.
*/
{
/* load the specified server certificates */
LOGI_CHAR(("---> SSL: Loading certificate's private key from: %s",
GetPath(ID_KEYFILE)));
//
// load given private key and check for validity
//
if (!_verifyPrivateKey(sslContext, GetPath(ID_KEYFILE)))
{
LOGE_CHAR((
"---> SSL: No server certificate found in %s",
GetPath(ID_KEYFILE)));
SSL_CTX_free(sslContext);
return MI_RESULT_FAILED;
}
}
self->sslContext = sslContext;
return MI_RESULT_OK;
}
#endif
static MI_Result _CreateAddListenerSocket(
Http* self,
unsigned short port,
MI_Boolean secure
)
{
Addr addr;
Sock listener;
MI_Result r;
/* Create listener socket */
{
Addr_InitAny(&addr, port);
r = Sock_CreateListener(&listener, &addr);
if (r != MI_RESULT_OK)
{
return r;
}
r = Sock_SetBlocking(listener, MI_FALSE);
if (r != MI_RESULT_OK)
{
Sock_Close(listener);
return r;
}
}
/* Watch for read events on the listener socket (client connections) */
{
Http_Listener_SocketData* h = (Http_Listener_SocketData*)calloc(1, sizeof(Http_Listener_SocketData));
if (!h)
{
Sock_Close(listener);
return MI_RESULT_FAILED;
}
h->base.sock = listener;
h->base.mask = SELECTOR_READ | SELECTOR_EXCEPTION;
h->base.callback = _ListenerCallback;
h->base.data = self;
h->secure = secure;
r = Selector_AddHandler(self->selector, &h->base);
if (r != MI_RESULT_OK)
{
Sock_Close(listener);
free(h);
return r;
}
}
return MI_RESULT_OK;
}
MI_Result Http_New_Server(
Http** selfOut,
Selector* selector, /*optional, maybe NULL*/
unsigned short http_port, /* 0 to disable */
unsigned short https_port, /* 0 to disable */
HttpCallbackOnNewConnection callbackOnNewConnection,
HttpCallbackOnCloseConnection callbackOnCloseConnection,
HttpCallbackOnRequest callbackOnRequest,
void* callbackData)
{
Http* self;
MI_Result r;
/* allocate this, inits selector */
r = _New_Http(selfOut, selector, callbackOnNewConnection,
callbackOnCloseConnection, callbackOnRequest, callbackData);
if (MI_RESULT_OK != r)
return r;
self = *selfOut;
/* Create http listener socket */
if (http_port)
{
r = _CreateAddListenerSocket(self, http_port, MI_FALSE);
if (r != MI_RESULT_OK)
{
Http_Delete(self);
return r;
}
}
#ifdef CONFIG_POSIX
/* Create https listener socket */
if (https_port)
{
/* init ssl */
SSL_library_init();
/* create context */
r = _CreateSSLContext(self);
if (r != MI_RESULT_OK)
{
Http_Delete(self);
return r;
}
/* create a socket */
r = _CreateAddListenerSocket(self, https_port, MI_TRUE);
if (r != MI_RESULT_OK)
{
Http_Delete(self);
return r;
}
}
#else
MI_UNUSED(https_port);
#endif
return MI_RESULT_OK;
}
MI_Result Http_Delete(
Http* self)
{
/* Check parameters */
if (!self)
return MI_RESULT_INVALID_PARAMETER;
/* Check magic number */
if (self->magic != _MAGIC)
return MI_RESULT_INVALID_PARAMETER;
if (self->internalSelectorUsed)
{
/* Release selector;
Note: selector-destory closes all sockects in a list including connector and listener */
Selector_Destroy(self->selector);
/* Shutdown the network */
Sock_Stop();
}
if (self->sslContext)
SSL_CTX_free(self->sslContext);
/* Clear magic number */
self->magic = 0xDDDDDDDD;
/* Free self pointer */
free(self);
return MI_RESULT_OK;
}
MI_Result Http_Run(
Http* self,
MI_Uint64 timeoutUsec)
{
/* Run the selector */
return Selector_Run(self->selector, timeoutUsec);
}
/* sends 'ok' response with provided content;
if message is accepted to be sent, on return *data == null (taking memory ownership)*/
MI_Result Http_SendResponse(
Http* self,
void* httpConnectionHanlde,
int httpErrorCode,
Page** data)
{
Http_SR_SocketData* sendSock;
/* check params */
if (!self)
return MI_RESULT_INVALID_PARAMETER;
if (self->magic != _MAGIC)
{
LOGW((T("_SendIN_IO_thread: invalid magic!") ));
return MI_RESULT_INVALID_PARAMETER;
}
sendSock = (Http_SR_SocketData*)httpConnectionHanlde;
/* validate handler */
if (MI_RESULT_OK != Selector_ContainsHandler(
self->selector, (Handler*)sendSock ) )
{
LOGW((T("cannot send message: invalid handler (msg->clientID) %p\n"), sendSock));
return MI_RESULT_INVALID_PARAMETER;
}
sendSock->requestIsBeingProcessed = MI_FALSE;
sendSock->base.mask |= SELECTOR_WRITE;
sendSock->base.mask &= ~SELECTOR_READ;
if (data)
{
sendSock->sendPage = *data;
*data = 0;
}
else
{
sendSock->sendPage = 0;
}
sendSock->httpErrorCode = httpErrorCode;
sendSock->sentSize = 0;
sendSock->sendingState = RECV_STATE_HEADER;
_RequestCallbackWrite(sendSock);
return MI_RESULT_OK;
}
/* Sets http options (mostly unit-test support) */
MI_Result Http_SetOptions(
Http* self,
const HttpOptions* options)
{
/* check params */
if (!self || !options)
return MI_RESULT_INVALID_PARAMETER;
if (self->magic != _MAGIC)
{
LOGW((T("Http_SetOptions: invalid magic!") ));
return MI_RESULT_INVALID_PARAMETER;
}
self->options = *options;
return MI_RESULT_OK;
}