version 1.2, 2015/04/20 18:10:12
|
version 1.3, 2015/04/20 18:19:52
|
|
|
| |
#include <assert.h> | #include <assert.h> |
#include <ctype.h> | #include <ctype.h> |
#include "http.h" |
#include "httpcommon.h" |
#include <sock/addr.h> | #include <sock/addr.h> |
#include <sock/sock.h> | #include <sock/sock.h> |
#include <sock/selector.h> | #include <sock/selector.h> |
#include <base/time.h> |
#include <pal/sleep.h> |
#include <base/buf.h> | #include <base/buf.h> |
#include <base/log.h> | #include <base/log.h> |
#include <base/result.h> | #include <base/result.h> |
#include <base/strings.h> |
#include <pal/strings.h> |
#include <base/io.h> |
#include <pal/format.h> |
#include <base/paths.h> | #include <base/paths.h> |
#include <base/base64.h> |
#include <base/Strand.h> |
| |
#ifdef CONFIG_POSIX | #ifdef CONFIG_POSIX |
#include <openssl/ssl.h> | #include <openssl/ssl.h> |
|
|
| |
#endif | #endif |
| |
#define T MI_T |
#define FORCE_TRACING 0 |
| |
/* #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 |
|
| |
|
#define HTTPSOCKET_STRANDAUX_NEWREQUEST 0 |
| |
|
STRAND_DEBUGNAME1( HttpSocket, NewRequest ); |
| |
/* | /* |
**============================================================================== | **============================================================================== |
|
|
MI_Uint32 magic; | MI_Uint32 magic; |
Selector internalSelector; | Selector internalSelector; |
Selector* selector; | Selector* selector; |
HttpCallbackOnNewConnection callbackOnNewConnection; |
OpenCallback callbackOnNewConnection; |
HttpCallbackOnCloseConnection callbackOnCloseConnection; |
|
HttpCallbackOnRequest callbackOnRequest; |
|
void* callbackData; | void* callbackData; |
SSL_CTX* sslContext; | SSL_CTX* sslContext; |
/* options: timeouts etc */ | /* options: timeouts etc */ |
|
|
| |
typedef struct _Http_SR_SocketData | typedef struct _Http_SR_SocketData |
{ | { |
/* based member*/ |
Strand strand; |
Handler base; |
|
|
Handler handler; // Used on selector |
|
|
|
Http* http; |
| |
/* ssl part */ | /* ssl part */ |
SSL* ssl; | SSL* ssl; |
|
|
(to disbale timeout) */ | (to disbale timeout) */ |
MI_Boolean requestIsBeingProcessed; | MI_Boolean requestIsBeingProcessed; |
| |
/* link to next stack layer */ |
|
void* connectionData; |
|
|
|
/* receiving data */ | /* receiving data */ |
char* recvBuffer; | char* recvBuffer; |
size_t recvBufferSize; | size_t recvBufferSize; |
size_t recvievedSize; |
size_t receivedSize; |
Http_RecvState recvingState; | Http_RecvState recvingState; |
HttpHeaders recvHeaders; | HttpHeaders recvHeaders; |
Page* recvPage; | Page* recvPage; |
|
HttpRequestMsg* request; // request msg with the request page |
| |
/* sending part */ | /* sending part */ |
Page* sendPage; | Page* sendPage; |
|
|
Http_RecvState sendingState; | Http_RecvState sendingState; |
int httpErrorCode; | int httpErrorCode; |
| |
|
/* pending send message */ |
|
Message* savedSendMsg; |
|
|
/* Enable tracing */ | /* Enable tracing */ |
MI_Boolean enableTracing; | MI_Boolean enableTracing; |
} | } |
|
|
| |
#define _HashCode(first,last,len) ( (((MI_Uint8)first) << 16) | (((MI_Uint8)last) << 8) | (((MI_Uint16)len)) ) | #define _HashCode(first,last,len) ( (((MI_Uint8)first) << 16) | (((MI_Uint8)last) << 8) | (((MI_Uint16)len)) ) |
| |
|
_Return_type_success_(return == MI_TRUE) |
static int _Base64DecCallback( |
|
const void* data, |
|
size_t size, |
|
void* callbackData) |
|
{ |
|
char** str = (char**)callbackData; |
|
size_t i; |
|
|
|
for (i = 0; i < size; i++) |
|
{ |
|
**str = ((unsigned char*)data)[i]; |
|
(*str)++; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static MI_Boolean _DecodeBasicAuth( |
|
HttpHeaders* recvHeaders, |
|
const char * src, |
|
char* tgt) |
|
{ |
|
char* startBuffer = tgt; |
|
|
|
/* skip spaces in value */ |
|
|
|
while (src[0] == ' ' || src[0] == '\t') |
|
src++; |
|
|
|
/* Decode the password */ |
|
|
|
if (Base64Dec(src, strlen(src), _Base64DecCallback, &tgt) != 0) |
|
{ |
|
return MI_FALSE; |
|
} |
|
|
|
*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( | static MI_Boolean _getNameValuePair( |
char ** line, |
_Inout_ CharPtr* line, |
char ** value, |
_Out_ CharPtr* value, |
int* nameHashCode ) | int* nameHashCode ) |
{ | { |
int len = 0; | int len = 0; |
char* p; | char* p; |
|
*value = 0; |
/* find name end /hash-code */ | /* find name end /hash-code */ |
| |
|
if ((*line)[0] == 0) |
|
{ |
|
trace_GetNameValuePair_Failed(); |
|
return MI_FALSE; |
|
} |
|
|
*nameHashCode = _ToLower((MI_Uint8)(*line)[0])<<16; | *nameHashCode = _ToLower((MI_Uint8)(*line)[0])<<16; |
| |
for (len = 1; (*line)[len] != ':' && (*line)[len] != '\r'; len++ ) |
for (len = 1; (*line)[len] && (*line)[len] != ':' && (*line)[len] != '\r'; len++ ) |
; | ; |
| |
if ((*line)[len] != ':') | if ((*line)[len] != ':') |
|
{ |
|
trace_GetNameValuePair_Failed(); |
return MI_FALSE; | return MI_FALSE; |
|
} |
| |
*nameHashCode |= (len) | _ToLower((MI_Uint8)(*line)[len-1])<<8; | *nameHashCode |= (len) | _ToLower((MI_Uint8)(*line)[len-1])<<8; |
(*line)[len] = 0; | (*line)[len] = 0; |
|
|
*value = p; | *value = p; |
| |
/* skip to end of line */ | /* skip to end of line */ |
for ( ; ; ) |
for ( ; p[0]; ) |
{ | { |
if (p[0] == '\r' && p[1] == '\n' && | if (p[0] == '\r' && p[1] == '\n' && |
(p[2] != ' ' && p[2] != '\t') ) | (p[2] != ' ' && p[2] != '\t') ) |
|
|
| |
/* remove trailing spaces */ | /* remove trailing spaces */ |
p--; | p--; |
|
#ifdef _PREFAST_ |
|
#pragma prefast (push) |
|
#pragma prefast (disable: 26001) |
|
#endif |
|
/* disabling IPv6 OACR warnings - D3M bug 56 */ |
while (p[0] == ' ' || p[0] == '\t') | while (p[0] == ' ' || p[0] == '\t') |
p--; | p--; |
|
#ifdef _PREFAST_ |
|
#pragma prefast (pop) |
|
#endif |
| |
p[1] = 0; | p[1] = 0; |
| |
return MI_TRUE; | 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( | static MI_Boolean _getHeaderField( |
Http_SR_SocketData* handler, | Http_SR_SocketData* handler, |
char ** line) |
_Inout_ CharPtr* line) |
{ | { |
char* name = *line; | char* name = *line; |
char* value = NULL; | char* value = NULL; |
int nameHashCode; | int nameHashCode; |
| |
if (!_getNameValuePair(line, &value, &nameHashCode)) | if (!_getNameValuePair(line, &value, &nameHashCode)) |
|
{ |
|
trace_GetNameValuePair_Failed(); |
return MI_FALSE; | return MI_FALSE; |
|
} |
|
|
| |
|
#if defined(CONFIG_ENABLE_HTTPHEADERS) |
|
|
|
/* Inject name-value into HTTP header array */ |
|
|
|
if (handler->recvHeaders.headersSize < HTTP_MAX_HEADERS) |
|
{ |
|
handler->recvHeaders.headers[handler->recvHeaders.headersSize].name = |
|
name; |
|
handler->recvHeaders.headers[handler->recvHeaders.headersSize].value = |
|
value; |
|
handler->recvHeaders.headersSize++; |
|
} |
|
|
|
#endif /* defined(CONFIG_ENABLE_HTTPHEADERS) */ |
|
|
|
/* Convert specified headers to static fields */ |
| |
switch (nameHashCode) | switch (nameHashCode) |
{ | { |
case (_HashCode('c','e',12)): /*Content-Type*/ | case (_HashCode('c','e',12)): /*Content-Type*/ |
|
{ |
if (Strcasecmp(name,"Content-Type") == 0) | if (Strcasecmp(name,"Content-Type") == 0) |
_ParseContentType(&handler->recvHeaders, value); |
ParseContentType(&handler->recvHeaders, value); |
| |
break; | break; |
|
} |
case (_HashCode('c','h',14)): /*Content-Length*/ | case (_HashCode('c','h',14)): /*Content-Length*/ |
|
{ |
if (Strcasecmp(name,"Content-Length") == 0) | if (Strcasecmp(name,"Content-Length") == 0) |
{ | { |
handler->recvHeaders.contentLength = (size_t)Strtoull(value, NULL, 10); |
handler->recvHeaders.contentLength = (size_t)Strtoull(value, |
|
NULL, 10); |
|
|
if ( handler->recvHeaders.contentLength > HTTP_MAX_CONTENT ) | if ( handler->recvHeaders.contentLength > HTTP_MAX_CONTENT ) |
|
{ |
|
trace_ContentLength_MaxCheck_Failed(); |
return MI_FALSE; | return MI_FALSE; |
} | } |
|
} |
break; | break; |
|
} |
case (_HashCode('a','n',13)): /*Authorization*/ | case (_HashCode('a','n',13)): /*Authorization*/ |
if (Strcasecmp(name,"Authorization") == 0) |
|
{ | { |
if (Strncasecmp(value,"Basic",5) == 0) |
if (Strcasecmp(name,"Authorization") == 0) |
{ | { |
/* since decoded string is smaller, performing decoding inplace; |
if( !ParseAuthorization(&handler->recvHeaders,value) ) |
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; | return MI_FALSE; |
} | } |
} | } |
else /* unknown authorization type */ |
break; |
handler->recvHeaders.authorization = value; |
} |
|
case (_HashCode('u','t',10)): |
|
{ |
|
/* Remember the User-Agent for later use */ |
|
if (Strcasecmp(name, "User-Agent") == 0) |
|
{ |
|
handler->recvHeaders.userAgent = value; |
} | } |
break; | break; |
|
} |
default: | default: |
break; | break; |
| |
|
|
| |
static MI_Boolean _getRequestLine( | static MI_Boolean _getRequestLine( |
Http_SR_SocketData* handler, | Http_SR_SocketData* handler, |
char ** line) |
_Inout_ CharPtr* line) |
{ | { |
size_t index; | size_t index; |
/* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF | /* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF |
Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv | Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv |
*/ | */ |
| |
|
if ((*line)[0] == 0) |
|
{ |
|
trace_GetRequestLine_failed(); |
|
return MI_FALSE; |
|
} |
|
|
/* skip to end of line */ | /* skip to end of line */ |
for ( index = 1; index < handler->recvievedSize; index++ ) |
for ( index = 1; (*line)[index] && index < handler->receivedSize; index++ ) |
{ | { |
if ((*line)[index-1] == '\r' && (*line)[index] == '\n' ) | if ((*line)[index-1] == '\r' && (*line)[index] == '\n' ) |
{ | { |
|
|
} | } |
} | } |
| |
|
trace_GetRequestLine_failed(); |
|
|
return MI_FALSE; | return MI_FALSE; |
} | } |
| |
|
|
int res; | int res; |
| |
if (!handler->ssl) | if (!handler->ssl) |
return Sock_Read(handler->base.sock, buf, buf_size, sizeRead); |
return Sock_Read(handler->handler.sock, buf, buf_size, sizeRead); |
| |
handler->base.mask &= ~SELECTOR_WRITE; |
handler->handler.mask &= ~SELECTOR_WRITE; |
handler->base.mask |= SELECTOR_READ; |
handler->handler.mask |= SELECTOR_READ; |
handler->reverseOperations = MI_FALSE; | handler->reverseOperations = MI_FALSE; |
| |
*sizeRead = 0; | *sizeRead = 0; |
|
|
if (handler->acceptDone) | if (handler->acceptDone) |
{ | { |
res = SSL_read(handler->ssl, buf, buf_size); | res = SSL_read(handler->ssl, buf, buf_size); |
PRINTF(("ssl read %d\n", res)); |
|
} | } |
else | else |
{ | { |
res = SSL_accept(handler->ssl); | res = SSL_accept(handler->ssl); |
PRINTF(("ssl accept %d\n", res)); |
|
if ( res > 0 ) | if ( res > 0 ) |
{ | { |
/* we are done with accpet */ | /* we are done with accpet */ |
|
|
{ | { |
case SSL_ERROR_WANT_WRITE: | case SSL_ERROR_WANT_WRITE: |
handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ | handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ |
handler->base.mask |= SELECTOR_WRITE; |
handler->handler.mask |= SELECTOR_WRITE; |
handler->base.mask &= ~SELECTOR_READ; |
handler->handler.mask &= ~SELECTOR_READ; |
PRINTF(("ssl read/accept WANT_WRITE\n")); |
|
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
case SSL_ERROR_WANT_READ: | case SSL_ERROR_WANT_READ: |
PRINTF(("ssl read/accept WANT_READ\n")); |
|
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
case SSL_ERROR_SYSCALL: | case SSL_ERROR_SYSCALL: |
|
|
EINPROGRESS == errno) | EINPROGRESS == errno) |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
LOGW_CHAR(("ssl-read: unexpected sys error %d\n", errno)); |
trace_SSLRead_UnexpectedSysError(errno); |
break; | break; |
| |
default: | default: |
|
|
char err_txt[200]; | char err_txt[200]; |
ERR_error_string_n(err, err_txt, sizeof(err_txt)); | ERR_error_string_n(err, err_txt, sizeof(err_txt)); |
| |
LOGW_CHAR(("ssl-read error: %d [%s]\n", (int)err, err_txt)); |
trace_SSLRead_Error((int)err, scs(err_txt)); |
|
|
err = ERR_get_error(); | err = ERR_get_error(); |
} | } |
} | } |
|
|
int res; | int res; |
| |
if (!handler->ssl) | if (!handler->ssl) |
return Sock_Write(handler->base.sock, buf, buf_size, sizeWritten); |
return Sock_Write(handler->handler.sock, buf, buf_size, sizeWritten); |
| |
/* Do not clear READ flag, since 'close' notification | /* Do not clear READ flag, since 'close' notification |
delivered as READ event*/ | delivered as READ event*/ |
handler->base.mask &= ~SELECTOR_READ; |
handler->handler.mask &= ~SELECTOR_READ; |
handler->base.mask |= SELECTOR_WRITE; |
handler->handler.mask |= SELECTOR_WRITE; |
handler->reverseOperations = MI_FALSE; | handler->reverseOperations = MI_FALSE; |
| |
*sizeWritten = 0; | *sizeWritten = 0; |
res = SSL_write(handler->ssl, buf, buf_size); | res = SSL_write(handler->ssl, buf, buf_size); |
PRINTF(("ssl write %d\n", res)); |
|
| |
if ( res == 0 ) | if ( res == 0 ) |
return MI_RESULT_OK; /* connection closed */ | return MI_RESULT_OK; /* connection closed */ |
|
|
| |
case SSL_ERROR_WANT_READ: | case SSL_ERROR_WANT_READ: |
handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ | handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ |
handler->base.mask |= SELECTOR_READ; |
handler->handler.mask |= SELECTOR_READ; |
handler->base.mask &= ~SELECTOR_WRITE; |
handler->handler.mask &= ~SELECTOR_WRITE; |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
case SSL_ERROR_SYSCALL: | case SSL_ERROR_SYSCALL: |
|
|
EINPROGRESS == errno) | EINPROGRESS == errno) |
return MI_RESULT_WOULD_BLOCK; | return MI_RESULT_WOULD_BLOCK; |
| |
LOGW_CHAR(("ssl-write: unexpected sys error %d\n", errno)); |
trace_SSLWrite_UnexpectedSysError(errno); |
break; | break; |
| |
default: | default: |
|
|
| |
static void _WriteTraceFile(PathID id, void* data, size_t size) | static void _WriteTraceFile(PathID id, void* data, size_t size) |
{ | { |
|
#ifdef CONFIG_POSIX |
static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER; | static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
#else |
|
/* TODO: How to synchronize logging */ |
|
#endif |
const char* path; | const char* path; |
| |
if (!(path = GetPath(id))) |
if (!(path = OMI_GetPath(id))) |
return; | return; |
| |
|
#ifdef CONFIG_POSIX |
pthread_mutex_lock(&s_mutex); | pthread_mutex_lock(&s_mutex); |
|
#else |
|
/* TODO: How to synchronize logging */ |
|
#endif |
{ | { |
FILE* out = fopen(path, "a"); | FILE* out = fopen(path, "a"); |
| |
if (out) | if (out) |
|
{ |
fwrite(data, 1, size, out); | fwrite(data, 1, size, out); |
|
|
fclose(out); | fclose(out); |
} | } |
|
else |
|
{ |
|
trace_CannotOpenHttptraceFile(path, errno); |
|
} |
|
} |
|
#ifdef CONFIG_POSIX |
pthread_mutex_unlock(&s_mutex); | pthread_mutex_unlock(&s_mutex); |
|
#else |
|
/* TODO: How to synchronize logging */ |
|
#endif |
} | } |
| |
INLINE MI_Result _Sock_Read( | INLINE MI_Result _Sock_Read( |
|
|
{ | { |
MI_Result r = _Sock_ReadAux(handler, buf, buf_size, sizeRead); | MI_Result r = _Sock_ReadAux(handler, buf, buf_size, sizeRead); |
| |
if (r == MI_RESULT_OK && handler->enableTracing) |
if (FORCE_TRACING || (r == MI_RESULT_OK && handler->enableTracing)) |
{ | { |
_WriteTraceFile(ID_HTTPRECVTRACEFILE, buf, *sizeRead); | _WriteTraceFile(ID_HTTPRECVTRACEFILE, buf, *sizeRead); |
} | } |
|
|
{ | { |
MI_Result r = _Sock_WriteAux(handler, buf, buf_size, sizeWritten); | MI_Result r = _Sock_WriteAux(handler, buf, buf_size, sizeWritten); |
| |
if (r == MI_RESULT_OK && handler->enableTracing) |
if (FORCE_TRACING || (r == MI_RESULT_OK && handler->enableTracing)) |
{ | { |
_WriteTraceFile(ID_HTTPSENDTRACEFILE, buf, *sizeWritten); | _WriteTraceFile(ID_HTTPSENDTRACEFILE, buf, *sizeWritten); |
} | } |
|
|
if (handler->recvingState == RECV_STATE_CONTENT) | if (handler->recvingState == RECV_STATE_CONTENT) |
return PRT_CONTINUE; | return PRT_CONTINUE; |
| |
buf = handler->recvBuffer + handler->recvievedSize; |
buf = handler->recvBuffer + handler->receivedSize; |
buf_size = handler->recvBufferSize - handler->recvievedSize; |
buf_size = handler->recvBufferSize - handler->receivedSize; |
received = 0; | received = 0; |
| |
r = _Sock_Read(handler, buf, buf_size, &received); | 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 ) | if ( r == MI_RESULT_OK && 0 == received ) |
return PRT_RETURN_FALSE; /* conection closed */ | return PRT_RETURN_FALSE; /* conection closed */ |
|
|
if (!received) | if (!received) |
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
| |
handler->recvievedSize += received; |
handler->receivedSize += received; |
|
|
/* check header */ |
|
PRINTF_2(("%s\n",buf)); |
|
| |
/* did we get full header? */ | /* did we get full header? */ |
buf = handler->recvBuffer; | buf = handler->recvBuffer; |
for ( index = 3; index < handler->recvievedSize; index++ ) |
for ( index = 3; index < handler->receivedSize; index++ ) |
{ | { |
if (buf[index-3] == '\r' && buf[index-1] == '\r' && | if (buf[index-3] == '\r' && buf[index-1] == '\r' && |
buf[index-2] == '\n' && buf[index] == '\n' ) | buf[index-2] == '\n' && buf[index] == '\n' ) |
|
|
| |
if (!fullHeaderReceived ) | if (!fullHeaderReceived ) |
{ | { |
if ( handler->recvievedSize < handler->recvBufferSize ) |
if ( handler->receivedSize < handler->recvBufferSize ) |
return PRT_RETURN_TRUE; /* continue reading */ | return PRT_RETURN_TRUE; /* continue reading */ |
| |
if ( handler->recvBufferSize < MAX_HEADER_SIZE ) | if ( handler->recvBufferSize < MAX_HEADER_SIZE ) |
{ | { |
buf = realloc(handler->recvBuffer, handler->recvBufferSize * 2); |
buf = PAL_Realloc(handler->recvBuffer, handler->recvBufferSize * 2); |
| |
if (!buf) | if (!buf) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
|
else | else |
{ | { |
/* http header is too big - drop connection */ | /* http header is too big - drop connection */ |
LOGW((T("http header is too big; dropping connection\n"))); |
trace_HttpHeaderIsTooBig(); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
} | } |
} | } |
|
|
} | } |
| |
/* Allocate zero-terminated buffer */ | /* Allocate zero-terminated buffer */ |
handler->recvPage = (Page*)malloc(sizeof(Page) + handler->recvHeaders.contentLength + 1); |
handler->recvPage = (Page*)PAL_Malloc(sizeof(Page) + handler->recvHeaders.contentLength + 1); |
| |
if (!handler->recvPage) | if (!handler->recvPage) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
|
|
handler->recvPage->u.s.size = (unsigned int)handler->recvHeaders.contentLength; | handler->recvPage->u.s.size = (unsigned int)handler->recvHeaders.contentLength; |
handler->recvPage->u.s.next = 0; | handler->recvPage->u.s.next = 0; |
| |
handler->recvievedSize -= index + 1; |
handler->receivedSize -= index + 1; |
| |
/* Verify that we have not more than 'content-length' bytes in buffer left | /* 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 we hvae more, assuming http client is invalid and drop connection */ |
if (handler->recvievedSize > handler->recvHeaders.contentLength) |
if (handler->receivedSize > handler->recvHeaders.contentLength) |
{ | { |
LOGW((T("http payload is bigger than content-length\n"))); |
trace_HttpPayloadIsBiggerThanContentLength(); |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
} | } |
| |
memcpy( handler->recvPage + 1, data, handler->recvievedSize ); |
memcpy( handler->recvPage + 1, data, handler->receivedSize ); |
handler->recvingState = RECV_STATE_CONTENT; | 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; | return PRT_CONTINUE; |
} | } |
| |
static Http_CallbackResult _ReadData( | static Http_CallbackResult _ReadData( |
Http_SR_SocketData* handler) | Http_SR_SocketData* handler) |
{ | { |
Http* self = (Http*)handler->base.data; |
|
char* buf; | char* buf; |
size_t buf_size, received; | size_t buf_size, received; |
MI_Result r; | MI_Result r; |
|
HttpRequestMsg* msg; |
| |
/* are we in the right state? */ | /* are we in the right state? */ |
if (handler->recvingState != RECV_STATE_CONTENT) | if (handler->recvingState != RECV_STATE_CONTENT) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
buf = ((char*)(handler->recvPage + 1)) + handler->recvievedSize; |
buf = ((char*)(handler->recvPage + 1)) + handler->receivedSize; |
buf_size = handler->recvHeaders.contentLength - handler->recvievedSize; |
buf_size = handler->recvHeaders.contentLength - handler->receivedSize; |
received = 0; | received = 0; |
| |
if (buf_size) | if (buf_size) |
{ | { |
r = _Sock_Read(handler, buf, buf_size, &received); | 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 ) | if ( r == MI_RESULT_OK && 0 == received ) |
return PRT_RETURN_FALSE; /* conection closed */ | return PRT_RETURN_FALSE; /* conection closed */ |
| |
if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) | if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) |
return PRT_RETURN_FALSE; | return PRT_RETURN_FALSE; |
| |
handler->recvievedSize += received; |
handler->receivedSize += received; |
} | } |
| |
/* did we get all data? */ | /* 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 ) |
if ( handler->receivedSize != handler->recvHeaders.contentLength ) |
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
| |
handler->requestIsBeingProcessed = MI_TRUE; |
msg = HttpRequestMsg_New(handler->recvPage, &handler->recvHeaders); |
(*self->callbackOnRequest)( self, self->callbackData, handler->connectionData, handler, &handler->recvHeaders, |
|
&handler->recvPage ); |
if( NULL == msg ) |
|
{ |
|
trace_HTTP_RequestAllocFailed( handler ); |
| |
if (handler->recvPage) | if (handler->recvPage) |
free(handler->recvPage); |
{ |
|
PAL_Free(handler->recvPage); |
|
handler->recvPage = NULL; /* clearing this out so that caller does not double-free it */ |
|
} |
|
|
|
return PRT_RETURN_FALSE; |
|
} |
|
|
|
handler->requestIsBeingProcessed = MI_TRUE; |
|
|
|
// the page will be owned by receiver of this message |
|
DEBUG_ASSERT( NULL == handler->request ); |
|
handler->request = msg; |
|
Strand_ScheduleAux( &handler->strand, HTTPSOCKET_STRANDAUX_NEWREQUEST ); |
| |
handler->recvPage = 0; | handler->recvPage = 0; |
handler->recvievedSize = 0; |
handler->receivedSize = 0; |
memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); | memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); |
handler->recvingState = RECV_STATE_HEADER; | handler->recvingState = RECV_STATE_HEADER; |
return PRT_CONTINUE; | return PRT_CONTINUE; |
|
|
return "Error"; | return "Error"; |
} | } |
| |
|
/* |
|
* Common clean up function that reverts the changes made when preparing |
|
* the strand for a write. |
|
*/ |
|
static void _ResetWriteState( |
|
Http_SR_SocketData* socketData ) |
|
{ |
|
if (socketData->sendPage) |
|
{ |
|
PAL_Free(socketData->sendPage); |
|
socketData->sendPage = 0; |
|
} |
|
socketData->httpErrorCode = 0; |
|
socketData->sentSize = 0; |
|
socketData->sendingState = RECV_STATE_HEADER; |
|
socketData->handler.mask &= ~SELECTOR_WRITE; |
|
socketData->handler.mask |= SELECTOR_READ; |
|
} |
|
|
static Http_CallbackResult _WriteHeader( | static Http_CallbackResult _WriteHeader( |
Http_SR_SocketData* handler) | Http_SR_SocketData* handler) |
{ | { |
|
|
"Content-Type: application/soap+xml;charset=UTF-8\r\n"\ | "Content-Type: application/soap+xml;charset=UTF-8\r\n"\ |
"\r\n" | "\r\n" |
| |
|
#define RESPONSE_HEADER_NO_AUTH_FMT "HTTP/1.1 %d %s\r\n\r\n" |
|
|
|
#define RESPONSE_HEADER_401_ERROR_FMT \ |
|
"HTTP/1.1 %d %s \r\n" \ |
|
HTTP_WWWAUTHENTICATE_BASIC\ |
|
"\r\n"\ |
|
"Content-Length: 0\r\n"\ |
|
"\r\n" |
|
|
/* "SOAPAction: http://schemas.xmlsoap.org/ws/2004/08/addressing/fault\r\n"\ */ | /* "SOAPAction: http://schemas.xmlsoap.org/ws/2004/08/addressing/fault\r\n"\ */ |
| |
char currentLine[sizeof(RESPONSE_HEADER_FMT) + | char currentLine[sizeof(RESPONSE_HEADER_FMT) + |
|
|
} | } |
else | else |
{ | { |
|
int httpErrorCode = (int)handler->httpErrorCode; |
|
/* |
|
Check the error code and in case it is "HTTP_ERROR_CODE_UNAUTHORIZED" (401), then we need to send the |
|
"WWW-Authenticate" header field. Since right now we only support "Basic" auth so the header will contains |
|
"WWW-Authenticate: Basic realm=\"WSMAN\". |
|
*/ |
|
char * response = (httpErrorCode == HTTP_ERROR_CODE_UNAUTHORIZED) ? RESPONSE_HEADER_401_ERROR_FMT : RESPONSE_HEADER_NO_AUTH_FMT; |
|
|
buf_size = (size_t)Snprintf( | buf_size = (size_t)Snprintf( |
currentLine, | currentLine, |
sizeof(currentLine), | sizeof(currentLine), |
"HTTP/1.1 %d %s\r\n\r\n", |
response, |
(int)handler->httpErrorCode, |
httpErrorCode, |
_GetHttpErrorCodeDescription(handler->httpErrorCode)); | _GetHttpErrorCodeDescription(handler->httpErrorCode)); |
} | } |
| |
|
|
| |
r = _Sock_Write(handler, buf, buf_size - handler->sentSize, &sent); | 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 ) | if ( r == MI_RESULT_OK && 0 == sent ) |
return PRT_RETURN_FALSE; /* conection closed */ | return PRT_RETURN_FALSE; /* conection closed */ |
| |
|
|
| |
if (!handler->sendPage) | if (!handler->sendPage) |
{ /* no content*/ | { /* no content*/ |
handler->httpErrorCode = 0; |
_ResetWriteState( handler ); |
handler->sentSize = 0; |
|
handler->sendingState = RECV_STATE_HEADER; |
|
handler->base.mask &= ~SELECTOR_WRITE; |
|
handler->base.mask |= SELECTOR_READ; |
|
return PRT_CONTINUE; | return PRT_CONTINUE; |
} | } |
| |
|
|
| |
r = _Sock_Write(handler, buf, buf_size, &sent); | 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 ) | if ( r == MI_RESULT_OK && 0 == sent ) |
return PRT_RETURN_FALSE; /* conection closed */ | return PRT_RETURN_FALSE; /* conection closed */ |
| |
|
|
if ( handler->sentSize != handler->sendPage->u.s.size ) | if ( handler->sentSize != handler->sendPage->u.s.size ) |
return PRT_RETURN_TRUE; | return PRT_RETURN_TRUE; |
| |
free(handler->sendPage); |
_ResetWriteState( handler ); |
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; | return PRT_CONTINUE; |
} | } |
|
|
{ | { |
case PRT_CONTINUE: break; | case PRT_CONTINUE: break; |
case PRT_RETURN_TRUE: return MI_TRUE; | case PRT_RETURN_TRUE: return MI_TRUE; |
case PRT_RETURN_FALSE: return MI_FALSE; |
case PRT_RETURN_FALSE: |
|
_ResetWriteState( handler ); |
|
return MI_FALSE; |
} | } |
| |
switch (_WriteData(handler)) | switch (_WriteData(handler)) |
{ | { |
case PRT_CONTINUE: break; | case PRT_CONTINUE: break; |
case PRT_RETURN_TRUE: return MI_TRUE; | case PRT_RETURN_TRUE: return MI_TRUE; |
case PRT_RETURN_FALSE: return MI_FALSE; |
case PRT_RETURN_FALSE: |
|
_ResetWriteState( handler ); |
|
return MI_FALSE; |
} | } |
return MI_TRUE; | return MI_TRUE; |
} | } |
|
|
MI_Uint32 mask, | MI_Uint32 mask, |
MI_Uint64 currentTimeUsec) | MI_Uint64 currentTimeUsec) |
{ | { |
Http_SR_SocketData* handler = (Http_SR_SocketData*)handlerIn; |
Http_SR_SocketData* handler = FromOffset( Http_SR_SocketData, handler, handlerIn ); |
sel=sel; | sel=sel; |
| |
if ( ((mask & SELECTOR_READ) != 0 && !handler->reverseOperations) || | if ( ((mask & SELECTOR_READ) != 0 && !handler->reverseOperations) || |
((mask & SELECTOR_WRITE) != 0 && handler->reverseOperations) ) | ((mask & SELECTOR_WRITE) != 0 && handler->reverseOperations) ) |
{ | { |
if (!_RequestCallbackRead(handler)) | if (!_RequestCallbackRead(handler)) |
|
{ |
|
trace_RequestCallbackRead_Failed(handler); |
return MI_FALSE; | return MI_FALSE; |
} | } |
|
} |
| |
if ( ((mask & SELECTOR_WRITE) != 0 && !handler->reverseOperations) || | if ( ((mask & SELECTOR_WRITE) != 0 && !handler->reverseOperations) || |
((mask & SELECTOR_READ) != 0 && handler->reverseOperations) ) | ((mask & SELECTOR_READ) != 0 && handler->reverseOperations) ) |
{ | { |
if (!_RequestCallbackWrite(handler)) | if (!_RequestCallbackWrite(handler)) |
|
{ |
|
trace_RequestCallbackWrite_Failed(); |
return MI_FALSE; | return MI_FALSE; |
} | } |
|
} |
| |
/* re-set timeout - if we performed R/W operation, set timeout depending where we are in communication */ | /* re-set timeout - if we performed R/W operation, set timeout depending where we are in communication */ |
if (mask & (SELECTOR_READ | SELECTOR_WRITE)) | if (mask & (SELECTOR_READ | SELECTOR_WRITE)) |
{ | { |
Http* self = (Http*)handler->base.data; |
Http* self = (Http*)handler->handler.data; |
| |
if (handler->requestIsBeingProcessed) | if (handler->requestIsBeingProcessed) |
{ | { |
/* since request is processed by server, disable timeout for this period */ | /* since request is processed by server, disable timeout for this period */ |
handler->base.fireTimeoutAt = TIME_NEVER; |
handler->handler.fireTimeoutAt = TIME_NEVER; |
} | } |
else | else |
{ | { |
/* Use configuration timeout */ | /* Use configuration timeout */ |
handler->base.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec; |
handler->handler.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec; |
} | } |
} | } |
| |
/* Close conenction by timeout */ | /* Close conenction by timeout */ |
if (mask & SELECTOR_TIMEOUT) | if (mask & SELECTOR_TIMEOUT) |
|
{ |
|
trace_ConnectionClosed_Timeout(); |
return MI_FALSE; | return MI_FALSE; |
|
} |
| |
if ((mask & SELECTOR_REMOVE) != 0 || | if ((mask & SELECTOR_REMOVE) != 0 || |
(mask & SELECTOR_DESTROY) != 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) | if (handler->ssl) |
SSL_free(handler->ssl); | SSL_free(handler->ssl); |
| |
PRINTF(("%d: close\n", (int)handler->base.sock)); |
trace_SocketClose_REMOVEDESTROY(); |
| |
Sock_Close(handler->base.sock); |
Sock_Close(handler->handler.sock); |
|
|
|
// Free the savedSendMsg and ACK it to prevent leaks when a non-io thread |
|
// writes to the socket, but it cannot be read because of an error or a |
|
// ECONNRESET. |
|
if (handler->savedSendMsg) |
|
{ |
|
Message_Release(handler->savedSendMsg); |
|
handler->savedSendMsg = NULL; |
|
DEBUG_ASSERT(handler->strand.info.otherAckPending); |
|
Strand_ScheduleAck( &handler->strand ); |
|
} |
| |
if (handler->recvPage) | if (handler->recvPage) |
free(handler->recvPage); |
PAL_Free(handler->recvPage); |
| |
if (handler->sendPage) | if (handler->sendPage) |
free(handler->sendPage); |
PAL_Free(handler->sendPage); |
|
|
|
PAL_Free(handler->recvBuffer); |
|
// handler deleted on its own strand |
| |
free(handler->recvBuffer); |
// notify next stack layer |
free(handler); |
// (only after internal data has been deleted as this may delete the object) |
|
Strand_ScheduleClose( &handler->strand ); |
} | } |
| |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
|
/* |
|
**============================================================================== |
|
*/ |
|
|
|
static void _SendIN_IO_thread_HttpSocket(void* self_, Message* message) |
|
{ |
|
Http_SR_SocketData* sendSock = (Http_SR_SocketData*)self_; |
|
HttpResponseMsg * response = (HttpResponseMsg *)message; |
|
|
|
DEBUG_ASSERT( sendSock ); |
|
DEBUG_ASSERT( HttpResponseMsgTag == message->tag ); |
|
|
|
/* validate handler */ |
|
|
|
if (MI_RESULT_OK != Selector_ContainsHandler( |
|
sendSock->http->selector, &sendSock->handler ) ) |
|
{ |
|
trace_SendIN_IO_thread_HttpSocket_InvalidHandler(sendSock); |
|
return; |
|
} |
|
|
|
sendSock->requestIsBeingProcessed = MI_FALSE; |
|
|
|
sendSock->handler.mask |= SELECTOR_WRITE; |
|
sendSock->handler.mask &= ~SELECTOR_READ; |
|
|
|
// Now we take ownership of the page |
|
sendSock->sendPage = response->page; |
|
response->page = NULL; |
|
sendSock->httpErrorCode = response->httpErrorCode; |
|
|
|
sendSock->sentSize = 0; |
|
sendSock->sendingState = RECV_STATE_HEADER; |
|
|
|
// Done after response->page is NULL'd to prevent early release |
|
// of that memory in the message's destructor |
|
DEBUG_ASSERT( sendSock->savedSendMsg ); |
|
Message_Release(sendSock->savedSendMsg); |
|
sendSock->savedSendMsg = NULL; |
|
|
|
if( !_RequestCallbackWrite(sendSock) ) |
|
{ |
|
trace_SendIN_IO_thread_HttpSocket_WriteFailed(); |
|
} |
|
|
|
Strand_ScheduleAck( &sendSock->strand ); |
|
} |
|
|
|
void _HttpSocket_Post( _In_ Strand* self_, _In_ Message* msg) |
|
{ |
|
Http_SR_SocketData* self = (Http_SR_SocketData*)self_; |
|
DEBUG_ASSERT( NULL != self_ ); |
|
trace_HttpSocketPosting(&self->strand.info.interaction, self->strand.info.interaction.other); |
|
|
|
// Preserve the message in case there is an error while writing it to the socket during |
|
// non-IO thread calls to Selector_CallInIOThread. |
|
// A ref to the message is held until the message is ACK'd. |
|
DEBUG_ASSERT( NULL == self->savedSendMsg ); |
|
Message_AddRef(msg); |
|
self->savedSendMsg = msg; |
|
|
|
if( MI_RESULT_OK != Selector_CallInIOThread( |
|
self->http->selector, _SendIN_IO_thread_HttpSocket, self, msg ) ) |
|
{ |
|
// We also need to release the page (if any) |
|
HttpResponseMsg * response = (HttpResponseMsg *)msg; |
|
DEBUG_ASSERT( HttpResponseMsgTag == msg->tag ); |
|
|
|
trace_HttpSocket_CannotPostMessage( self, msg, &self->strand.info.interaction, self->strand.info.interaction.other ); |
|
|
|
HttpResponseMsg_Release( response ); // same message as savedSendMsg |
|
self->savedSendMsg = NULL; |
|
Strand_ScheduleAck( &self->strand ); |
|
} |
|
} |
|
|
|
void _HttpSocket_PostControl( _In_ Strand* self, _In_ Message* msg) |
|
{ |
|
DEBUG_ASSERT( MI_FALSE ); // not used yet |
|
} |
|
|
|
void _HttpSocket_Ack( _In_ Strand* self) |
|
{ |
|
trace_HttpSocketAck( &self->info.interaction, self->info.interaction.other ); |
|
// No need to do anything for now |
|
} |
|
|
|
void _HttpSocket_Cancel( _In_ Strand* self) |
|
{ |
|
// no need to do anything here (upper layer should send proper error response) |
|
} |
|
|
|
void _HttpSocket_Finish( _In_ Strand* self_) |
|
{ |
|
Http_SR_SocketData* self = (Http_SR_SocketData*)self_; |
|
DEBUG_ASSERT( NULL != self_ ); |
|
|
|
trace_HttpSocketFinish( self_ ); |
|
Strand_Delete( &self->strand ); |
|
} |
|
|
|
// HTTPSOCKET_STRANDAUX_NEWREQUEST |
|
void _HttpSocket_Aux_NewRequest( _In_ Strand* self_) |
|
{ |
|
Http_SR_SocketData* self = (Http_SR_SocketData*)self_; |
|
HttpRequestMsg* msg; |
|
|
|
DEBUG_ASSERT( NULL != self_ ); |
|
msg = self->request; |
|
DEBUG_ASSERT( NULL != msg ); |
|
|
|
trace_HttpSocketAuxNewRequest( self, msg ); |
|
self->request = NULL; |
|
|
|
if( !self_->info.thisClosedOther ) |
|
{ |
|
// Leave the strand for the case where the new request provider |
|
// is in-proc and takes over the thread |
|
Strand_PostAndLeaveStrand( &self->strand, &msg->base ); |
|
} |
|
|
|
HttpRequestMsg_Release( msg ); |
|
} |
|
|
|
/* |
|
Object that implements the HTTP protocol endpoint on a TCP Socket. |
|
Usually connects to WSMAN interaction object. |
|
|
|
Behaviour: |
|
- Post tries to schedule the operation on the IO thread (thru selector) |
|
if that fails it sends the Ack immediately. Note that the response message |
|
is delivered from WSMAN inside a HttpResponseMsg |
|
- Post control is not implemented |
|
- Both Cancel, Close and Ack do nothing (in case of cancelation upper layer should |
|
send an actual response) |
|
- Shutdown: |
|
Once the connection is closed by the client that is notified to _RequestCallback |
|
which calls Strand_ScheduleClose on component to the right. |
|
From there normal Strand logic applies: once the upper layer |
|
also closes the interaction the object is deleted. |
|
|
|
Unique features and special Behavour: |
|
- When a complete message has been read instead of scheduling a post |
|
the auxiliary function HTTPSOCKET_STRANDAUX_NEWREQUEST is |
|
scheduled instead. That function takes care of posting using |
|
Strand_PostAndLeaveStrand (which avoids holding the strand in case the thread |
|
is going to be hijacked by the provider in the processing of that post). |
|
*/ |
|
static StrandFT _HttpSocket_FT = { |
|
_HttpSocket_Post, |
|
_HttpSocket_PostControl, |
|
_HttpSocket_Ack, |
|
_HttpSocket_Cancel, |
|
NULL, |
|
_HttpSocket_Finish, |
|
NULL, |
|
_HttpSocket_Aux_NewRequest, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL }; |
|
|
|
/* |
|
**============================================================================== |
|
*/ |
|
|
static MI_Boolean _ListenerCallback( | static MI_Boolean _ListenerCallback( |
Selector* sel, | Selector* sel, |
Handler* handler_, | Handler* handler_, |
|
|
/* Accept the incoming connection */ | /* Accept the incoming connection */ |
r = Sock_Accept(handler->base.sock, &s, &addr); | r = Sock_Accept(handler->base.sock, &s, &addr); |
| |
PRINTF(("%d: accept r = %d\n", (int)s, (int)r )); |
|
|
|
if (MI_RESULT_WOULD_BLOCK == r) | if (MI_RESULT_WOULD_BLOCK == r) |
return MI_TRUE; | return MI_TRUE; |
| |
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
LOGW((T("Sock_Accept() failed; err %d\n"), Sock_GetLastError())); |
trace_SockAccept_Failed(Sock_GetLastError()); |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
r = Sock_SetBlocking(s, MI_FALSE); | r = Sock_SetBlocking(s, MI_FALSE); |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
LOGW((T("Sock_SetBlocking() failed\n"))); |
trace_SockSetBlocking_Failed(); |
Sock_Close(s); | Sock_Close(s); |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
/* Create handler */ | /* Create handler */ |
h = (Http_SR_SocketData*)calloc(1, sizeof(Http_SR_SocketData)); |
h = (Http_SR_SocketData*)Strand_New( STRAND_DEBUG( HttpSocket ) &_HttpSocket_FT, sizeof(Http_SR_SocketData), STRAND_FLAG_ENTERSTRAND, NULL ); |
| |
if (!h) | if (!h) |
{ | { |
|
trace_SocketClose_Http_SR_SocketDataAllocFailed(); |
Sock_Close(s); | Sock_Close(s); |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
|
h->http = self; |
h->recvBufferSize = INITIAL_BUFFER_SIZE; | h->recvBufferSize = INITIAL_BUFFER_SIZE; |
h->recvBuffer = (char*)calloc(1, h->recvBufferSize); |
h->recvBuffer = (char*)PAL_Calloc(1, h->recvBufferSize); |
if (!h->recvBuffer) | if (!h->recvBuffer) |
{ | { |
free(h); |
Strand_Delete(&h->strand); |
|
trace_SocketClose_recvBuffer_AllocFailed(); |
Sock_Close(s); | Sock_Close(s); |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
h->base.sock = s; |
h->handler.sock = s; |
h->base.mask = SELECTOR_READ | SELECTOR_EXCEPTION; |
h->handler.mask = SELECTOR_READ | SELECTOR_EXCEPTION; |
h->base.callback = _RequestCallback; |
h->handler.callback = _RequestCallback; |
h->base.data = self; |
h->handler.data = self; |
h->base.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec; |
h->handler.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec; |
h->enableTracing = self->options.enableTracing; | h->enableTracing = self->options.enableTracing; |
| |
/* ssl support */ | /* ssl support */ |
|
|
| |
if (!h->ssl) | if (!h->ssl) |
{ | { |
LOGW((T("ssl_new() failed\n"))); |
trace_SSLNew_Failed(); |
free(h); |
Strand_Delete(&h->strand); |
Sock_Close(s); | Sock_Close(s); |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
if (!(SSL_set_fd(h->ssl, s) )) | if (!(SSL_set_fd(h->ssl, s) )) |
{ | { |
LOGW((T("ssl_set_fd() failed\n"))); |
trace_SSL_setfd_Failed(); |
SSL_free(h->ssl); | SSL_free(h->ssl); |
free(h); |
Strand_Delete(&h->strand); |
Sock_Close(s); | Sock_Close(s); |
return MI_TRUE; | return MI_TRUE; |
} | } |
|
|
} | } |
| |
/* Watch for read events on the incoming connection */ | /* Watch for read events on the incoming connection */ |
r = Selector_AddHandler(self->selector, &h->base); |
r = Selector_AddHandler(self->selector, &h->handler); |
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
LOGW((T("Selector_AddHandler() failed\n"))); |
trace_SelectorAddHandler_Failed(); |
if (handler->secure) | if (handler->secure) |
SSL_free(h->ssl); | SSL_free(h->ssl); |
free(h); |
Strand_Delete(&h->strand); |
Sock_Close(s); | Sock_Close(s); |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
/* notify next stack layer about new connection */ |
// notify next stack layer about new connection |
(*self->callbackOnNewConnection)( |
// (open the interaction) |
self, |
Strand_Open( |
|
&h->strand, |
|
self->callbackOnNewConnection, |
self->callbackData, | self->callbackData, |
h, |
NULL, |
&h->connectionData ); |
MI_TRUE ); |
} | } |
} | } |
| |
if ((mask & SELECTOR_REMOVE) != 0 || | if ((mask & SELECTOR_REMOVE) != 0 || |
(mask & SELECTOR_DESTROY) != 0) | (mask & SELECTOR_DESTROY) != 0) |
{ | { |
|
trace_SocketClose_REMOVEDESTROY(); |
Sock_Close(handler->base.sock); | Sock_Close(handler->base.sock); |
free(handler); |
PAL_Free(handler); |
} | } |
| |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
static MI_Result _New_Http( | static MI_Result _New_Http( |
Http** selfOut, |
_Out_ Http** selfOut, |
Selector* selector, /*optional, maybe NULL*/ |
_In_ Selector* selector, /*optional, maybe NULL*/ |
HttpCallbackOnNewConnection callbackOnNewConnection, |
_In_ OpenCallback callbackOnNewConnection, |
HttpCallbackOnCloseConnection callbackOnCloseConnection, |
_In_opt_ void* callbackData) |
HttpCallbackOnRequest callbackOnRequest, |
|
void* callbackData) |
|
{ | { |
Http* self; | Http* self; |
| |
|
|
| |
/* Allocate structure */ | /* Allocate structure */ |
{ | { |
self = (Http*)calloc(1, sizeof(Http)); |
self = (Http*)PAL_Calloc(1, sizeof(Http)); |
| |
if (!self) | if (!self) |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
|
|
/* Initialize the selector */ | /* Initialize the selector */ |
if (Selector_Init(&self->internalSelector) != MI_RESULT_OK) | if (Selector_Init(&self->internalSelector) != MI_RESULT_OK) |
{ | { |
free(self); |
PAL_Free(self); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
self->selector = &self->internalSelector; | self->selector = &self->internalSelector; |
|
|
} | } |
| |
/* Save the callback and callbackData */ | /* Save the callback and callbackData */ |
self->callbackOnRequest = callbackOnRequest; |
|
self->callbackOnCloseConnection = callbackOnCloseConnection; |
|
self->callbackOnNewConnection = callbackOnNewConnection; | self->callbackOnNewConnection = callbackOnNewConnection; |
self->callbackData = callbackData; | self->callbackData = callbackData; |
| |
/* Set the magic number */ | /* Set the magic number */ |
self->magic = _MAGIC; | self->magic = _MAGIC; |
| |
/* options */ |
|
{ |
|
HttpOptions options = DEFAULT_HTTP_OPTIONS; |
|
self->options = options; |
|
} |
|
|
|
/* Set output parameter */ | /* Set output parameter */ |
*selfOut = self; | *selfOut = self; |
return MI_RESULT_OK; | return MI_RESULT_OK; |
|
|
| |
if (!is) | if (!is) |
{ | { |
LOGE_CHAR(( |
trace_SSL_FailedToOpenPrivateKeyFile(scs(keyPath)); |
"---> SSL: failed to open private key file: %s", |
|
keyPath)); |
|
return MI_FALSE; | return MI_FALSE; |
} | } |
| |
|
|
| |
if (!pkey) | if (!pkey) |
{ | { |
LOGE_CHAR(("---> SSL: failed to create private key")); |
trace_SSL_FailedToCreatePrivateKey(); |
return MI_FALSE; | return MI_FALSE; |
} | } |
| |
|
|
if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) | if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) |
{ | { |
EVP_PKEY_free(pkey); | EVP_PKEY_free(pkey); |
LOGE_CHAR(( |
trace_SSL_NoPrivateKeyFound(scs(keyPath)); |
"---> SSL: no private key found in %s", |
|
keyPath)); |
|
return MI_FALSE; | return MI_FALSE; |
} | } |
| |
|
|
| |
if (!SSL_CTX_check_private_key(ctx)) | if (!SSL_CTX_check_private_key(ctx)) |
{ | { |
LOGE_CHAR(( |
trace_SSL_PrivateAndPublicKeyDonotMatch(); |
"---> SSL: Private and public key do not match")); |
|
return MI_FALSE; | return MI_FALSE; |
} | } |
| |
return MI_TRUE; | return MI_TRUE; |
} | } |
| |
static MI_Result _CreateSSLContext(Http* self) |
static MI_Result _CreateSSLContext(Http* self, const char* sslCipherSuite, Server_SSL_Options sslOptions) |
{ | { |
SSL_CTX * sslContext = 0; | SSL_CTX * sslContext = 0; |
|
long options = 0; |
| |
sslContext = SSL_CTX_new(SSLv23_method()); | sslContext = SSL_CTX_new(SSLv23_method()); |
| |
if (!sslContext) | if (!sslContext) |
{ | { |
LOGE_CHAR(( |
trace_SSL_CannotCreateContext(); |
"---> SSL: cannot create ssl context")); |
return MI_RESULT_FAILED; |
|
} |
|
|
|
if (sslCipherSuite != NULL) |
|
{ |
|
// Set the cipher list to the user specified cipher list. |
|
if (SSL_CTX_set_cipher_list(sslContext, sslCipherSuite) == 0) |
|
{ |
|
trace_SSL_BadCipherList(scs(sslCipherSuite)); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
} |
|
|
|
// Disable SSL_v2 and/or SSL_v3 if requested |
|
if ( sslOptions & DISABLE_SSL_V2 ) |
|
{ |
|
options |= SSL_OP_NO_SSLv2; |
|
} |
|
if ( sslOptions & DISABLE_SSL_V3 ) |
|
{ |
|
options |= SSL_OP_NO_SSLv3; |
|
} |
|
if ( SSL_CTX_set_options(sslContext, options) == 0 ) |
|
{ |
|
trace_SSL_CannotSetOptions( options ); |
|
return MI_RESULT_FAILED; |
|
} |
|
|
SSL_CTX_set_quiet_shutdown(sslContext, 1); | SSL_CTX_set_quiet_shutdown(sslContext, 1); |
SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); | SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); |
SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE); | SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE); |
|
|
** certificate. | ** certificate. |
*/ | */ |
{ | { |
|
char errorBuf[256]; |
|
|
/* load the specified server certificates */ | /* load the specified server certificates */ |
LOGI_CHAR(("---> SSL: Loading server certificate from: %s", |
trace_SSL_LoadingServerCert(scs(OMI_GetPath(ID_PEMFILE))); |
GetPath(ID_PEMFILE))); |
|
| |
if (SSL_CTX_use_certificate_file(sslContext, | if (SSL_CTX_use_certificate_file(sslContext, |
GetPath(ID_PEMFILE), SSL_FILETYPE_PEM) <=0) |
OMI_GetPath(ID_PEMFILE), SSL_FILETYPE_PEM) <=0) |
{ | { |
LOGE_CHAR(("---> SSL: No server certificate found in %s", |
trace_SSL_NoServerCertFound(OMI_GetPath(ID_PEMFILE), GetSslErrorString(errorBuf, 256)); |
GetPath(ID_PEMFILE))); |
|
SSL_CTX_free(sslContext); | SSL_CTX_free(sslContext); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
** If specified, validate and load the key. | ** If specified, validate and load the key. |
*/ | */ |
{ | { |
|
char errorBuf[256]; |
|
|
/* load the specified server certificates */ | /* load the specified server certificates */ |
LOGI_CHAR(("---> SSL: Loading certificate's private key from: %s", |
trace_SSL_LoadingCertPrivateKey(scs(OMI_GetPath(ID_KEYFILE))); |
GetPath(ID_KEYFILE))); |
|
| |
// |
/* load given private key and check for validity |
// load given private key and check for validity |
*/ |
// |
if (!_verifyPrivateKey(sslContext, OMI_GetPath(ID_KEYFILE))) |
if (!_verifyPrivateKey(sslContext, GetPath(ID_KEYFILE))) |
{ |
{ |
trace_SSL_NoServerCertFound(scs(OMI_GetPath(ID_KEYFILE)), GetSslErrorString(errorBuf, sizeof errorBuf)); |
LOGE_CHAR(( |
|
"---> SSL: No server certificate found in %s", |
|
GetPath(ID_KEYFILE))); |
|
SSL_CTX_free(sslContext); | SSL_CTX_free(sslContext); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
|
trace_SocketClose_SetBlockingFailed(); |
Sock_Close(listener); | Sock_Close(listener); |
return r; | return r; |
} | } |
|
|
| |
/* Watch for read events on the listener socket (client connections) */ | /* Watch for read events on the listener socket (client connections) */ |
{ | { |
Http_Listener_SocketData* h = (Http_Listener_SocketData*)calloc(1, sizeof(Http_Listener_SocketData)); |
Http_Listener_SocketData* h = (Http_Listener_SocketData*)PAL_Calloc(1, sizeof(Http_Listener_SocketData)); |
| |
if (!h) | if (!h) |
{ | { |
|
trace_SocketClose_Http_Listener_SocketDataAllocFailed(); |
Sock_Close(listener); | Sock_Close(listener); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
|
|
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
|
trace_SocketClose_Selector_AddHandlerFailed(); |
Sock_Close(listener); | Sock_Close(listener); |
free(h); |
PAL_Free(h); |
return r; | return r; |
} | } |
} | } |
|
|
} | } |
| |
MI_Result Http_New_Server( | MI_Result Http_New_Server( |
Http** selfOut, |
_Out_ Http** selfOut, |
Selector* selector, /*optional, maybe NULL*/ |
_In_ Selector* selector, /* optional, maybe NULL*/ |
unsigned short http_port, /* 0 to disable */ |
_In_ unsigned short http_port, /* 0 to disable */ |
unsigned short https_port, /* 0 to disable */ |
_In_ unsigned short https_port, /* 0 to disable */ |
HttpCallbackOnNewConnection callbackOnNewConnection, |
_In_opt_z_ const char* sslCipherSuite, /* NULL to disable */ |
HttpCallbackOnCloseConnection callbackOnCloseConnection, |
_In_ Server_SSL_Options sslOptions, /* 0 for default options */ |
HttpCallbackOnRequest callbackOnRequest, |
_In_ OpenCallback callbackOnNewConnection, |
void* callbackData) |
_In_opt_ void* callbackData, |
|
_In_opt_ const HttpOptions* options) |
{ | { |
Http* self; | Http* self; |
MI_Result r; | MI_Result r; |
| |
/* allocate this, inits selector */ | /* allocate this, inits selector */ |
r = _New_Http(selfOut, selector, callbackOnNewConnection, |
r = _New_Http(selfOut, selector, callbackOnNewConnection, callbackData); |
callbackOnCloseConnection, callbackOnRequest, callbackData); |
|
| |
if (MI_RESULT_OK != r) | if (MI_RESULT_OK != r) |
return r; | return r; |
|
|
SSL_library_init(); | SSL_library_init(); |
| |
/* create context */ | /* create context */ |
r = _CreateSSLContext(self); |
r = _CreateSSLContext(self, sslCipherSuite, sslOptions); |
| |
if (r != MI_RESULT_OK) | if (r != MI_RESULT_OK) |
{ | { |
|
|
MI_UNUSED(https_port); | MI_UNUSED(https_port); |
#endif | #endif |
| |
|
// options |
|
if( NULL == options ) |
|
{ |
|
HttpOptions tmpOptions = DEFAULT_HTTP_OPTIONS; |
|
self->options = tmpOptions; |
|
} |
|
else |
|
{ |
|
self->options = *options; |
|
} |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
|
if (self->internalSelectorUsed) | if (self->internalSelectorUsed) |
{ | { |
/* Release selector; | /* Release selector; |
Note: selector-destory closes all sockects in a list including connector and listener */ |
Note: selector-destory closes all sockets in a list including connector and listener */ |
Selector_Destroy(self->selector); | Selector_Destroy(self->selector); |
| |
/* Shutdown the network */ | /* Shutdown the network */ |
|
|
self->magic = 0xDDDDDDDD; | self->magic = 0xDDDDDDDD; |
| |
/* Free self pointer */ | /* Free self pointer */ |
free(self); |
PAL_Free(self); |
| |
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
|
|
MI_Uint64 timeoutUsec) | MI_Uint64 timeoutUsec) |
{ | { |
/* Run the selector */ | /* Run the selector */ |
return Selector_Run(self->selector, timeoutUsec); |
return Selector_Run(self->selector, timeoutUsec, MI_FALSE); |
} |
|
|
|
/* 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; |
|
} |
|