version 1.125.4.15, 2008/02/01 17:50:16
|
version 1.126, 2007/01/11 16:21:54
|
|
|
const char *file , Uint32 line) | const char *file , Uint32 line) |
{ | { |
String message = status + httpDetailDelimiter + detail; | String message = status + httpDetailDelimiter + detail; |
PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2, message); |
Tracer::trace(file, line, TRC_HTTP, Tracer::LEVEL2, message); |
if (status == httpStatusInternal) | if (status == httpStatusInternal) |
throw AssertionFailureException(file, line, message); | throw AssertionFailureException(file, line, message); |
else throw Exception(message); | else throw Exception(message); |
|
|
#define _socketWriteError() \ | #define _socketWriteError() \ |
do \ | do \ |
{ \ | { \ |
PEG_TRACE((__FILE__, __LINE__, TRC_DISCARDED_DATA, Tracer::LEVEL2, \ |
Tracer::trace(__FILE__, __LINE__, TRC_DISCARDED_DATA, Tracer::LEVEL2, \ |
"Socket write failed with error %d; could not write response " \ | "Socket write failed with error %d; could not write response " \ |
"to client. (Client may have timed out.)", \ | "to client. (Client may have timed out.)", \ |
getSocketError())); \ |
getSocketError()); \ |
throw Exception("socket write error"); \ | throw Exception("socket write error"); \ |
} \ | } \ |
while (0) | while (0) |
|
|
return x < y ? x : y; | return x < y ? x : y; |
} | } |
| |
|
static char* _FindSeparator(const char* data, Uint32 size) |
|
{ |
|
const char* p = data; |
|
const char* end = p + size; |
|
|
|
while (p != end) |
|
{ |
|
if (*p == '\r') |
|
{ |
|
size_t n = end - p; |
|
|
|
if (n >= 2 && p[1] == '\n') |
|
return (char*)p; |
|
} |
|
else if (*p == '\n') |
|
return (char*)p; |
|
|
|
p++; |
|
} |
|
|
|
return 0; |
|
} |
|
|
// Used to test signal handling | // Used to test signal handling |
void * sigabrt_generator(void * parm) | void * sigabrt_generator(void * parm) |
{ | { |
|
|
_contentOffset(-1), | _contentOffset(-1), |
_contentLength(-1), | _contentLength(-1), |
_connectionClosePending(false), | _connectionClosePending(false), |
_acceptPending(false), |
_acceptPending(false) |
_firstRead(true), |
|
_idleConnectionTimeoutSeconds(0) |
|
{ | { |
PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::HTTPConnection"); | PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::HTTPConnection"); |
| |
|
_socket->disableBlocking(); |
_authInfo.reset(new AuthenticationInfo(true)); | _authInfo.reset(new AuthenticationInfo(true)); |
| |
// Add SSL verification information to the authentication information | // Add SSL verification information to the authentication information |
|
|
if (_socket->isPeerVerificationEnabled() && | if (_socket->isPeerVerificationEnabled() && |
_socket->isCertificateVerified()) | _socket->isCertificateVerified()) |
{ | { |
_authInfo->setConnectionAuthenticated(true); |
_authInfo->setAuthStatus(AuthenticationInfoRep::AUTHENTICATED); |
_authInfo->setAuthType(AuthenticationInfoRep::AUTH_TYPE_SSL); | _authInfo->setAuthType(AuthenticationInfoRep::AUTH_TYPE_SSL); |
_authInfo->setClientCertificateChain( | _authInfo->setClientCertificateChain( |
_socket->getPeerCertificateChain()); | _socket->getPeerCertificateChain()); |
|
|
#else | #else |
if (_socket->isClientAuthenticated()) | if (_socket->isClientAuthenticated()) |
{ | { |
_authInfo->setConnectionAuthenticated(true); |
_authInfo->setAuthStatus(AuthenticationInfoRep::AUTHENTICATED); |
_authInfo->setAuthenticatedUser(_socket->getAuthenticatedUser()); | _authInfo->setAuthenticatedUser(_socket->getAuthenticatedUser()); |
} | } |
#endif | #endif |
|
|
{ | { |
PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::~HTTPConnection"); | PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::~HTTPConnection"); |
| |
// We need to acquire this mutex in order to give handleEnqueue() |
|
// a chance to finish processing. If we don't, we may run into a |
|
// situation where the connection is marked to be closed by the |
|
// idle connection timeout mechanism and there are no pending |
|
// responses (the _responsePending flag is cleared in |
|
// _handleWriteEvent()). This causes the monitor to clean up the |
|
// connection. But if processing is not out of |
|
// HTTPConnection::handleEnqueue(), we are running a risk of |
|
// accessing a deleted object and crashing cimserver. |
|
AutoMutex connectionLock(_connection_mut); |
|
_socket->close(); | _socket->close(); |
| |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
|
|
{ | { |
case SOCKET_MESSAGE: | case SOCKET_MESSAGE: |
{ | { |
PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL4, |
"HTTPConnection::handleEnqueue - SOCKET_MESSAGE"); | "HTTPConnection::handleEnqueue - SOCKET_MESSAGE"); |
SocketMessage* socketMessage = (SocketMessage*)message; | SocketMessage* socketMessage = (SocketMessage*)message; |
if (socketMessage->events & SocketMessage::READ) | if (socketMessage->events & SocketMessage::READ) |
|
|
| |
case HTTP_MESSAGE: | case HTTP_MESSAGE: |
{ | { |
PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL4, |
"HTTPConnection::handleEnqueue - HTTP_MESSAGE"); | "HTTPConnection::handleEnqueue - HTTP_MESSAGE"); |
| |
_handleWriteEvent(*message); | _handleWriteEvent(*message); |
|
|
} | } |
| |
/* | /* |
* Used on Server side to close outstanding connections waiting for SSL |
|
* handshake to complete if timeout expired or to close idle connections if |
|
* idleConnectionTimeout config property value has specified. |
|
* Returns 'true' if connection is closed (or is closePending). |
|
* timeNow will be updated to current time if connection's _idleStartTime |
|
* is greater than timeNow. |
|
*/ |
|
|
|
Boolean HTTPConnection::closeConnectionOnTimeout(struct timeval* timeNow) |
|
{ |
|
// if SSL Handshake is not complete. |
|
if (_acceptPending) |
|
{ |
|
PEGASUS_ASSERT(!_isClient()); |
|
if ((timeNow->tv_sec - _acceptPendingStartTime.tv_sec > |
|
PEGASUS_SSL_ACCEPT_TIMEOUT_SECONDS) && |
|
(timeNow->tv_sec > _acceptPendingStartTime.tv_sec)) |
|
{ |
|
PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"HTTPConnection: close acceptPending connection for timeout"); |
|
_closeConnection(); |
|
return true; // return 'true' to indicate connection was closed |
|
} |
|
} |
|
// else if connection timeout is active |
|
else if (_idleConnectionTimeoutSeconds) |
|
{ |
|
// For performance reasons timeNow is calculated only once in Monitor. |
|
// Update timeNow if connection's _idleStartTime has more recent time. |
|
if (timeNow->tv_sec < _idleStartTime.tv_sec) |
|
{ |
|
Time::gettimeofday(timeNow); |
|
} |
|
else if ((Uint32)(timeNow->tv_sec - _idleStartTime.tv_sec) > |
|
_idleConnectionTimeoutSeconds) |
|
{ |
|
PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"HTTPConnection: close idle connection for timeout " |
|
"of %d seconds\n", _idleConnectionTimeoutSeconds)); |
|
_closeConnection(); |
|
return true; // return 'true' to indicate connection was closed |
|
} |
|
} |
|
return false; // connection was not closed |
|
} |
|
|
|
/* |
|
* handle the message coming down from the above. This is shared client and | * handle the message coming down from the above. This is shared client and |
* server code. If the message is coming in chunks, then validate the chunk | * server code. If the message is coming in chunks, then validate the chunk |
* sequence number. If the message is being processed on the server side, | * sequence number. If the message is being processed on the server side, |
|
|
Boolean isFirst = message.isFirst(); | Boolean isFirst = message.isFirst(); |
Boolean isLast = message.isComplete(); | Boolean isLast = message.isComplete(); |
Sint32 totalBytesWritten = 0; | Sint32 totalBytesWritten = 0; |
Uint32 messageLength = buffer.size(); |
Uint32 messageLength = (Uint32) buffer.size(); |
| |
try | try |
{ | { |
|
|
buffer.reserveCapacity(messageLength + 1); | buffer.reserveCapacity(messageLength + 1); |
messageStart = (char *) buffer.getData(); | messageStart = (char *) buffer.getData(); |
messageStart[messageLength] = 0; | messageStart[messageLength] = 0; |
PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2, |
Tracer::trace( |
"<!-- Response: queue id: %u -->\n%s", |
TRC_XML_IO, |
|
Tracer::LEVEL2, "<!-- Response: queue id: %u -->\n%s", |
getQueueId(), | getQueueId(), |
buffer.getData())); |
buffer.getData()); |
if (isFirst == true) | if (isFirst == true) |
{ | { |
_incomingBuffer.clear(); | _incomingBuffer.clear(); |
|
|
_transferEncodingChunkOffset = 0; | _transferEncodingChunkOffset = 0; |
_mpostPrefix.clear(); | _mpostPrefix.clear(); |
cimException = CIMException(); | cimException = CIMException(); |
|
_responsePending = true; |
} | } |
else | else |
{ | { |
|
|
String httpStatus(s); | String httpStatus(s); |
Buffer message = XmlWriter::formatHttpErrorRspMessage | Buffer message = XmlWriter::formatHttpErrorRspMessage |
(httpStatus, String(), httpDetail); | (httpStatus, String(), httpDetail); |
messageLength = message.size(); |
messageLength = (Uint32)message.size(); |
message.reserveCapacity(messageLength+1); | message.reserveCapacity(messageLength+1); |
messageStart = (char *) message.getData(); | messageStart = (char *) message.getData(); |
messageStart[messageLength] = 0; | messageStart[messageLength] = 0; |
|
|
{ | { |
// subsequent chunks from the server, just append | // subsequent chunks from the server, just append |
| |
messageLength += _incomingBuffer.size(); |
messageLength += (Uint32)(_incomingBuffer.size()); |
_incomingBuffer.reserveCapacity(messageLength+1); | _incomingBuffer.reserveCapacity(messageLength+1); |
_incomingBuffer.append(buffer.getData(), buffer.size()); | _incomingBuffer.append(buffer.getData(), buffer.size()); |
buffer.clear(); | buffer.clear(); |
|
|
String messageS = cimException.getMessage(); | String messageS = cimException.getMessage(); |
CString messageC = messageS.getCString(); | CString messageC = messageS.getCString(); |
messageStart = (char *) (const char *) messageC; | messageStart = (char *) (const char *) messageC; |
messageLength = strlen(messageStart); |
messageLength = (Uint32)strlen(messageStart); |
buffer.reserveCapacity(messageLength+1); | buffer.reserveCapacity(messageLength+1); |
buffer.append(messageStart, messageLength); | buffer.append(messageStart, messageLength); |
// null terminate | // null terminate |
|
|
headerNameTerminatorLength + numberAsStringLength; | headerNameTerminatorLength + numberAsStringLength; |
| |
Uint32 contentLengthLineLengthFound = | Uint32 contentLengthLineLengthFound = |
contentLengthEnd - contentLengthStart; |
(Uint32)(contentLengthEnd - contentLengthStart); |
| |
if (isValid == false || ! contentLengthEnd || | if (isValid == false || ! contentLengthEnd || |
contentLengthLineLengthFound != | contentLengthLineLengthFound != |
|
|
Uint32 insertOffset = | Uint32 insertOffset = |
headerLength - headerLineTerminatorLength; | headerLength - headerLineTerminatorLength; |
messageLength = | messageLength = |
contentLanguagesString.size() + buffer.size(); |
(Uint32)(contentLanguagesString.size() + |
|
buffer.size()); |
buffer.reserveCapacity(messageLength+1); | buffer.reserveCapacity(messageLength+1); |
messageLength = contentLanguagesString.size(); |
messageLength = |
|
(Uint32)contentLanguagesString.size(); |
messageStart = | messageStart = |
(char *)contentLanguagesString.getData(); | (char *)contentLanguagesString.getData(); |
// insert the content language line before end | // insert the content language line before end |
|
|
// note: this can be expensive on large payloads | // note: this can be expensive on large payloads |
buffer.insert( | buffer.insert( |
insertOffset, messageStart, messageLength); | insertOffset, messageStart, messageLength); |
messageLength = buffer.size(); |
messageLength = (Uint32)buffer.size(); |
// null terminate | // null terminate |
messageStart = (char *) buffer.getData(); | messageStart = (char *) buffer.getData(); |
messageStart[messageLength] = 0; | messageStart[messageLength] = 0; |
|
|
| |
} // if not a client | } // if not a client |
| |
PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4, |
|
"HTTPConnection::_handleWriteEvent: Server write event."); |
|
|
|
SignalHandler::ignore(PEGASUS_SIGPIPE); | SignalHandler::ignore(PEGASUS_SIGPIPE); |
| |
// use the next four lines to test the SIGABRT handler | // use the next four lines to test the SIGABRT handler |
|
|
// dont include header terminator yet | // dont include header terminator yet |
Uint32 headerLength = bytesToWrite; | Uint32 headerLength = bytesToWrite; |
bytesToWrite -= headerLineTerminatorLength; | bytesToWrite -= headerLineTerminatorLength; |
PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4, |
|
"HTTPConnection::_handleWriteEvent: " |
|
"Sending header for chunked reponses."); |
|
| |
bytesWritten = _socket->write(sendStart, bytesToWrite); | bytesWritten = _socket->write(sendStart, bytesToWrite); |
if (bytesWritten < 0) | if (bytesWritten < 0) |
|
|
_mpostPrefix << headerNameDescription << headerValueSeparator << | _mpostPrefix << headerNameDescription << headerValueSeparator << |
headerNameContentLanguage << headerLineTerminator; | headerNameContentLanguage << headerLineTerminator; |
sendStart = (char *) trailer.getData(); | sendStart = (char *) trailer.getData(); |
bytesToWrite = trailer.size(); |
bytesToWrite = (Uint32)trailer.size(); |
|
|
PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4, |
|
"HTTPConnection::_handleWriteEvent: " |
|
"Sending trailer header for chunked responses."); |
|
|
|
bytesWritten = _socket->write(sendStart, bytesToWrite); | bytesWritten = _socket->write(sendStart, bytesToWrite); |
| |
if (bytesWritten < 0) | if (bytesWritten < 0) |
|
|
// now send header terminator | // now send header terminator |
bytesToWrite = headerLineTerminatorLength; | bytesToWrite = headerLineTerminatorLength; |
sendStart = messageStart + headerLength - bytesToWrite; | sendStart = messageStart + headerLength - bytesToWrite; |
|
|
PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4, |
|
"HTTPConnection::_handleWriteEvent: " |
|
"Sending header terminator for chunked responses."); |
|
|
|
bytesWritten = _socket->write(sendStart, bytesToWrite); | bytesWritten = _socket->write(sendStart, bytesToWrite); |
if (bytesWritten < 0) | if (bytesWritten < 0) |
_socketWriteError(); | _socketWriteError(); |
|
|
// terminator | // terminator |
sprintf(chunkLine, "%x%s", bytesToWrite, chunkLineTerminator); | sprintf(chunkLine, "%x%s", bytesToWrite, chunkLineTerminator); |
sendStart = chunkLine; | sendStart = chunkLine; |
Sint32 chunkBytesToWrite = strlen(sendStart); |
Sint32 chunkBytesToWrite = (Sint32)strlen(sendStart); |
|
|
PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4, |
|
"HTTPConnection::_handleWriteEvent: " |
|
"Sending chunk with chunk line terminator."); |
|
|
|
bytesWritten = _socket->write(sendStart, chunkBytesToWrite); | bytesWritten = _socket->write(sendStart, chunkBytesToWrite); |
if (bytesWritten < 0) | if (bytesWritten < 0) |
_socketWriteError(); | _socketWriteError(); |
|
|
{ | { |
sendStart = messageStart + messageLength - bytesRemaining; | sendStart = messageStart + messageLength - bytesRemaining; |
bytesToWrite = _Min(httpTcpBufferSize, bytesRemaining); | bytesToWrite = _Min(httpTcpBufferSize, bytesRemaining); |
|
|
PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4, |
|
"HTTPConnection::_handleWriteEvent: " |
|
"Sending non-chunked data."); |
|
|
|
bytesWritten = _socket->write(sendStart, bytesToWrite); | bytesWritten = _socket->write(sendStart, bytesToWrite); |
if (bytesWritten < 0) | if (bytesWritten < 0) |
_socketWriteError(); | _socketWriteError(); |
|
|
| |
if (traceTrailer) | if (traceTrailer) |
{ | { |
PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2, |
Tracer::trace(TRC_XML_IO,Tracer::LEVEL2, |
"<!-- Trailer: queue id: %u -->\n%s", |
"<!-- Trailer: queue id: %u -->\n%s \n", |
getQueueId(), | getQueueId(), |
trailer.getData())); |
trailer.getData()); |
} | } |
sendStart = (char *) trailer.getData(); | sendStart = (char *) trailer.getData(); |
Sint32 chunkBytesToWrite = (Sint32) trailer.size(); | Sint32 chunkBytesToWrite = (Sint32) trailer.size(); |
|
|
PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4, |
|
"HTTPConnection::_handleWriteEvent: " |
|
"Sending the last chunk with chunk body terminator "); |
|
|
|
bytesWritten = _socket->write(sendStart, chunkBytesToWrite); | bytesWritten = _socket->write(sendStart, chunkBytesToWrite); |
if (bytesWritten < 0) | if (bytesWritten < 0) |
_socketWriteError(); | _socketWriteError(); |
|
|
catch (...) | catch (...) |
{ | { |
httpStatus = HTTP_STATUS_INTERNALSERVERERROR; | httpStatus = HTTP_STATUS_INTERNALSERVERERROR; |
PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL2, "Unknown internal error"); |
String message("Unknown internal error"); |
|
Tracer::trace(__FILE__, __LINE__, TRC_HTTP, Tracer::LEVEL2, message); |
} | } |
| |
if (isLast == true) | if (isLast == true) |
|
|
_incomingBuffer.clear(); | _incomingBuffer.clear(); |
_transferEncodingTEValues.clear(); | _transferEncodingTEValues.clear(); |
| |
// Reset the transfer encoding chunk offset. If it is not reset here, |
|
// then a request sent with chunked encoding may not be properly read |
|
// off of a connection in which a chunked response has been sent. |
|
_transferEncodingChunkOffset = 0; |
|
// | // |
// decrement request count | // decrement request count |
// | // |
| |
_requestCount--; | _requestCount--; |
_responsePending = false; |
|
| |
if (httpStatus.size() == 0) | if (httpStatus.size() == 0) |
{ | { |
|
|
"A total of %d requests have been processed on this " | "A total of %d requests have been processed on this " |
"connection."; | "connection."; |
| |
PEG_TRACE((TRC_HTTP, Tracer::LEVEL4, msg, totalBytesWritten, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL4, msg, totalBytesWritten, |
messageLength, _requestCount.get(), _connectionRequestCount)); |
messageLength, _requestCount.get(), _connectionRequestCount); |
} | } |
| |
// | // |
|
|
// Check for message to close | // Check for message to close |
if (message.getCloseConnect()== true) | if (message.getCloseConnect()== true) |
{ | { |
PEG_TRACE(( |
Tracer::trace( |
TRC_HTTP, | TRC_HTTP, |
Tracer::LEVEL3, | Tracer::LEVEL3, |
"HTTPConnection::_handleWriteEvent - Connection: Close " | "HTTPConnection::_handleWriteEvent - Connection: Close " |
"in client message.")); |
"in client message."); |
_closeConnection(); | _closeConnection(); |
} | } |
else | else |
{ | { |
// Update the connection idle time. |
Tracer::trace (TRC_HTTP, Tracer::LEVEL2, |
if (_idleConnectionTimeoutSeconds) |
"Now setting state to %d", _MonitorEntry::IDLE); |
{ |
|
Time::gettimeofday(&_idleStartTime); |
|
} |
|
PEG_TRACE((TRC_HTTP, Tracer::LEVEL2, |
|
"Now setting state to %d", _MonitorEntry::IDLE)); |
|
_monitor->setState (_entry_index, _MonitorEntry::IDLE); | _monitor->setState (_entry_index, _MonitorEntry::IDLE); |
_monitor->tickle(); | _monitor->tickle(); |
} | } |
|
_responsePending = false; |
cimException = CIMException(); | cimException = CIMException(); |
} | } |
} | } |
|
|
| |
for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++) | for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++) |
{ | { |
Uint32 n = strlen(METHOD_NAMES[i]); |
Uint32 n = (Uint32)strlen(METHOD_NAMES[i]); |
| |
if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n])) | if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n])) |
return true; | return true; |
|
|
| |
for (Uint32 i = 0; i < RESPONSE_CODES_SIZE; i++) | for (Uint32 i = 0; i < RESPONSE_CODES_SIZE; i++) |
{ | { |
Uint32 n = strlen(RESPONSE_CODES[i]); |
Uint32 n = (Uint32)strlen(RESPONSE_CODES[i]); |
| |
if (strncmp(line, RESPONSE_CODES[i], n - 2) == 0 && isspace(line[n])) | if (strncmp(line, RESPONSE_CODES[i], n - 2) == 0 && isspace(line[n])) |
return true; | return true; |
|
|
{ | { |
static const char func[] = | static const char func[] = |
"HTTPConnection::_getContentLengthAndContentOffset"; | "HTTPConnection::_getContentLengthAndContentOffset"; |
Uint32 size = _incomingBuffer.size(); |
Uint32 size = (Uint32)_incomingBuffer.size(); |
if (size == 0) | if (size == 0) |
return; | return; |
char* data = (char*)_incomingBuffer.getData(); | char* data = (char*)_incomingBuffer.getData(); |
|
|
Boolean gotContentLanguage = false; | Boolean gotContentLanguage = false; |
Boolean gotTransferTE = false; | Boolean gotTransferTE = false; |
| |
while ((sep = HTTPMessage::findSeparator(line, size - (line - data)))) |
while ((sep = _FindSeparator(line, (Uint32)(size - (line - data))))) |
{ | { |
char save = *sep; | char save = *sep; |
*sep = '\0'; | *sep = '\0'; |
|
|
{ | { |
*sep = save; | *sep = save; |
line = sep + ((save == '\r') ? 2 : 1); | line = sep + ((save == '\r') ? 2 : 1); |
_contentOffset = line - _incomingBuffer.getData(); |
_contentOffset = (Sint32)(line - _incomingBuffer.getData()); |
| |
// reserve space for entire non-chunked message | // reserve space for entire non-chunked message |
if (_contentLength > 0) | if (_contentLength > 0) |
|
|
// note: if this is a chunked header, then this will be | // note: if this is a chunked header, then this will be |
// ignored later | // ignored later |
String contentLanguagesString( | String contentLanguagesString( |
valueStart, valueEnd - valueStart + 1); |
valueStart, (Uint32)(valueEnd - valueStart + 1)); |
try | try |
{ | { |
ContentLanguageList contentLanguagesValue = | ContentLanguageList contentLanguagesValue = |
|
|
} | } |
catch (...) | catch (...) |
{ | { |
PEG_TRACE((TRC_HTTP, Tracer::LEVEL2, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL2, |
"HTTPConnection: ERROR: contentLanguages had " | "HTTPConnection: ERROR: contentLanguages had " |
"parsing failure. clearing languages. error " | "parsing failure. clearing languages. error " |
"data=%s", | "data=%s", |
(const char *)contentLanguagesString.getCString())); |
(const char *)contentLanguagesString.getCString()); |
contentLanguages.clear(); | contentLanguages.clear(); |
} | } |
} | } |
|
|
{ | { |
if (_responsePending == true) | if (_responsePending == true) |
{ | { |
PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL2, |
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
"HTTPConnection::_closeConnection - Close connection " | "HTTPConnection::_closeConnection - Close connection " |
"requested while responses are still expected on this " | "requested while responses are still expected on this " |
"connection. connection=0x%p, socket=%d\n", | "connection. connection=0x%p, socket=%d\n", |
(void*)this, getSocket())); |
(void*)this, getSocket()); |
| |
} | } |
| |
// still set to DYING | // still set to DYING |
PEG_TRACE((TRC_HTTP, Tracer::LEVEL2, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL2, |
"Now setting state to %d", _MonitorEntry::DYING)); |
"Now setting state to %d", _MonitorEntry::DYING); |
_monitor->setState (_entry_index, _MonitorEntry::DYING); | _monitor->setState (_entry_index, _MonitorEntry::DYING); |
_monitor->tickle(); | _monitor->tickle(); |
} | } |
| |
if (_connectionRequestCount == 0) | if (_connectionRequestCount == 0) |
{ | { |
PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL4, |
"HTTPConnection::_closeConnection - Connection being closed " | "HTTPConnection::_closeConnection - Connection being closed " |
"without receiving any requests."); | "without receiving any requests."); |
} | } |
|
|
"HTTPConnection::_handleReadEventTransferEncoding"; | "HTTPConnection::_handleReadEventTransferEncoding"; |
PEG_METHOD_ENTER(TRC_HTTP, func); | PEG_METHOD_ENTER(TRC_HTTP, func); |
| |
Uint32 messageLength = _incomingBuffer.size(); |
Uint32 messageLength = (Uint32)_incomingBuffer.size(); |
Uint32 headerLength = (Uint32) _contentOffset; | Uint32 headerLength = (Uint32) _contentOffset; |
| |
// return immediately under these conditions: | // return immediately under these conditions: |
|
|
} | } |
| |
chunkLineEnd += chunkLineTerminatorLength; | chunkLineEnd += chunkLineTerminatorLength; |
Uint32 chunkLineLength = chunkLineEnd - chunkLineStart; |
Uint32 chunkLineLength = (Uint32)(chunkLineEnd - chunkLineStart); |
Uint32 chunkMetaLength = chunkLineLength; | Uint32 chunkMetaLength = chunkLineLength; |
if (chunkLengthParsed > 0) | if (chunkLengthParsed > 0) |
chunkMetaLength += chunkTerminatorLength; | chunkMetaLength += chunkTerminatorLength; |
|
|
| |
// remove the chunk length line | // remove the chunk length line |
_incomingBuffer.remove(_transferEncodingChunkOffset, chunkLineLength); | _incomingBuffer.remove(_transferEncodingChunkOffset, chunkLineLength); |
messageLength = _incomingBuffer.size(); |
messageLength = (Uint32)_incomingBuffer.size(); |
// always keep the byte after the last data byte null for easy string | // always keep the byte after the last data byte null for easy string |
// processing. | // processing. |
messageStart[messageLength] = 0; | messageStart[messageLength] = 0; |
|
|
trailerStart[trailerLength] = save; | trailerStart[trailerLength] = save; |
| |
_incomingBuffer.remove(trailerOffset, trailerLength); | _incomingBuffer.remove(trailerOffset, trailerLength); |
messageLength = _incomingBuffer.size(); |
messageLength = (Uint32)_incomingBuffer.size(); |
messageStart[messageLength] = 0; | messageStart[messageLength] = 0; |
remainderLength -= trailerLength; | remainderLength -= trailerLength; |
| |
|
|
} | } |
catch (...) | catch (...) |
{ | { |
PEG_TRACE((TRC_HTTP, Tracer::LEVEL2, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL2, |
"HTTPConnection: ERROR: contentLanguages had " | "HTTPConnection: ERROR: contentLanguages had " |
"parsing failure. clearing languages. " | "parsing failure. clearing languages. " |
"error data=%s", | "error data=%s", |
(const char *)contentLanguagesString. | (const char *)contentLanguagesString. |
getCString())); |
getCString()); |
contentLanguages.clear(); | contentLanguages.clear(); |
} | } |
} | } |
|
|
| |
// now remove the chunk terminator | // now remove the chunk terminator |
_incomingBuffer.remove(chunkTerminatorOffset, chunkTerminatorLength); | _incomingBuffer.remove(chunkTerminatorOffset, chunkTerminatorLength); |
messageLength = _incomingBuffer.size(); |
messageLength = (Uint32)_incomingBuffer.size(); |
messageStart[messageLength] = 0; | messageStart[messageLength] = 0; |
| |
// jump to the start of the next chunk (which may not have been | // jump to the start of the next chunk (which may not have been |
|
|
delimiterFound + httpDetailDelimiter.size()); | delimiterFound + httpDetailDelimiter.size()); |
} | } |
| |
PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2, |
Tracer::trace(__FILE__, __LINE__, TRC_HTTP, Tracer::LEVEL2, |
httpStatus + httpDetailDelimiter + httpDetail + | httpStatus + httpDetailDelimiter + httpDetail + |
httpDetailDelimiter + cimError); | httpDetailDelimiter + cimError); |
| |
|
|
message = XmlWriter::formatHttpErrorRspMessage(httpStatus, cimError, | message = XmlWriter::formatHttpErrorRspMessage(httpStatus, cimError, |
httpDetail); | httpDetail); |
HTTPMessage* httpMessage = new HTTPMessage(message); | HTTPMessage* httpMessage = new HTTPMessage(message); |
|
Tracer::traceBuffer(TRC_XML_IO, Tracer::LEVEL2, |
|
httpMessage->message.getData(), |
|
(Uint32)(httpMessage->message.size())); |
| |
// this is common error code. If we are the server side, we want to send | // this is common error code. If we are the server side, we want to send |
// back the error to the client, but if we are the client side, then we | // back the error to the client, but if we are the client side, then we |
|
|
else | else |
{ | { |
// else server side processing error - send back to client | // else server side processing error - send back to client |
PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2, |
|
"<!-- Error response: queue id: %u -->\n%s", |
|
getQueueId(), |
|
httpMessage->message.getData())); |
|
handleEnqueue(httpMessage); | handleEnqueue(httpMessage); |
} | } |
_closeConnection(); | _closeConnection(); |
|
|
| |
if (socketAcceptStatus < 0) | if (socketAcceptStatus < 0) |
{ | { |
PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
"HTTPConnection: SSL_accept() failed"); | "HTTPConnection: SSL_accept() failed"); |
_closeConnection(); | _closeConnection(); |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
|
|
else if (socketAcceptStatus == 0) | else if (socketAcceptStatus == 0) |
{ | { |
// Not enough data yet to complete the SSL handshake | // Not enough data yet to complete the SSL handshake |
PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL2, |
PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2, |
"HTTPConnection: SSL_accept() pending"); | "HTTPConnection: SSL_accept() pending"); |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
return; | return; |
|
|
if (_socket->isPeerVerificationEnabled() && | if (_socket->isPeerVerificationEnabled() && |
_socket->isCertificateVerified()) | _socket->isCertificateVerified()) |
{ | { |
_authInfo->setConnectionAuthenticated(true); |
_authInfo->setAuthStatus( |
|
AuthenticationInfoRep::AUTHENTICATED); |
_authInfo->setAuthType( | _authInfo->setAuthType( |
AuthenticationInfoRep::AUTH_TYPE_SSL); | AuthenticationInfoRep::AUTH_TYPE_SSL); |
_authInfo->setClientCertificateChain( | _authInfo->setClientCertificateChain( |
|
|
#else | #else |
if (_socket->isClientAuthenticated()) | if (_socket->isClientAuthenticated()) |
{ | { |
_authInfo->setConnectionAuthenticated(true); |
_authInfo->setAuthStatus( |
|
AuthenticationInfoRep::AUTHENTICATED); |
_authInfo->setAuthenticatedUser( | _authInfo->setAuthenticatedUser( |
_socket->getAuthenticatedUser()); | _socket->getAuthenticatedUser()); |
} | } |
|
|
| |
// -- Append all data waiting on socket to incoming buffer: | // -- Append all data waiting on socket to incoming buffer: |
| |
|
String httpStatus; |
Sint32 bytesRead = 0; | Sint32 bytesRead = 0; |
Boolean incompleteSecureReadOccurred = false; | Boolean incompleteSecureReadOccurred = false; |
| |
for (;;) | for (;;) |
{ | { |
char buffer[httpTcpBufferSize]; |
// save one for null |
|
char buffer[httpTcpBufferSize+1]; |
|
buffer[sizeof(buffer)-1] = 0; |
| |
Sint32 n = _socket->read(buffer, sizeof(buffer)-1); | Sint32 n = _socket->read(buffer, sizeof(buffer)-1); |
| |
// Check if this was the first read of a connection to the server. |
|
// This has to happen inside the read loop, because there can be |
|
// an incomplete SSL read. |
|
if (_firstRead && n > 5 && !_isClient()) |
|
{ |
|
// The first bytes of a connection to the server have to contain |
|
// a valid cim-over-http HTTP Method (M-POST or POST). |
|
if ((strncmp(buffer, "POST", 4) != 0) && |
|
(strncmp(buffer, "M-POST", 6) != 0)) |
|
{ |
|
_clearIncoming(); |
|
|
|
PEG_TRACE((TRC_HTTP, Tracer::LEVEL4, |
|
"This Request has non-valid CIM-HTTP Method: " |
|
"%02X %02X %02X %02X %02X %02X", |
|
buffer[0],buffer[1],buffer[2], |
|
buffer[3],buffer[4],buffer[5])); |
|
|
|
// Try to send message to client. |
|
// This function also closes the connection. |
|
_handleReadEventFailure(HTTP_STATUS_NOTIMPLEMENTED); |
|
|
|
PEG_METHOD_EXIT(); |
|
return; |
|
} |
|
_firstRead = false; |
|
} |
|
|
|
if (n <= 0) | if (n <= 0) |
{ | { |
|
if (_socket->isSecure()) |
|
{ |
// It is possible that SSL_read was not able to | // It is possible that SSL_read was not able to |
// read the entire SSL record. This could happen | // read the entire SSL record. This could happen |
// if the record was send in multiple packets | // if the record was send in multiple packets |
|
|
// disconnect and partial read of an SSL record. | // disconnect and partial read of an SSL record. |
// | // |
incompleteSecureReadOccurred = | incompleteSecureReadOccurred = |
_socket->incompleteSecureReadOccurred(n); |
_socket->incompleteReadOccurred(n); |
|
} |
break; | break; |
} | } |
| |
try | try |
{ | { |
_incomingBuffer.reserveCapacity(_incomingBuffer.size() + n); |
buffer[n] = 0; |
|
// important: always keep message buffer null terminated for easy |
|
// string parsing! |
|
Uint32 size = (Uint32)(_incomingBuffer.size() + n); |
|
_incomingBuffer.reserveCapacity(size + 1); |
_incomingBuffer.append(buffer, n); | _incomingBuffer.append(buffer, n); |
|
// put a null on it. This is safe sice we have reserved an |
|
// extra byte |
|
char *data = (char *)_incomingBuffer.getData(); |
|
data[size] = 0; |
} | } |
catch (...) | catch (...) |
{ | { |
static const char detailP[] = | static const char detailP[] = |
"Unable to append the request to the input buffer"; | "Unable to append the request to the input buffer"; |
String httpStatus = |
httpStatus = |
HTTP_STATUS_REQUEST_TOO_LARGE + httpDetailDelimiter + detailP; | HTTP_STATUS_REQUEST_TOO_LARGE + httpDetailDelimiter + detailP; |
_handleReadEventFailure(httpStatus); | _handleReadEventFailure(httpStatus); |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
|
|
#endif | #endif |
} | } |
| |
PEG_TRACE((TRC_HTTP, Tracer::LEVEL4, |
Tracer::trace(TRC_HTTP, Tracer::LEVEL4, |
"Total bytesRead = %d; Bytes read this iteration = %d", | "Total bytesRead = %d; Bytes read this iteration = %d", |
_incomingBuffer.size(), bytesRead)); |
_incomingBuffer.size(), bytesRead); |
| |
try | try |
{ | { |
|
|
} | } |
catch (Exception& e) | catch (Exception& e) |
{ | { |
_handleReadEventFailure(e.getMessage()); |
httpStatus = e.getMessage(); |
|
} |
|
|
|
if (httpStatus.size() > 0) |
|
{ |
|
_handleReadEventFailure(httpStatus); |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
return; | return; |
} | } |
|
|
(_contentLength != -1 && _contentOffset != -1 && | (_contentLength != -1 && _contentOffset != -1 && |
(Sint32(_incomingBuffer.size()) >= _contentLength + _contentOffset))) | (Sint32(_incomingBuffer.size()) >= _contentLength + _contentOffset))) |
{ | { |
// If no message was received, just close the connection |
|
if (_incomingBuffer.size() == 0) |
|
{ |
|
_clearIncoming(); |
|
|
|
PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2, |
|
"<!-- No request message received; connection closed: " |
|
"queue id: %u -->", |
|
getQueueId())); |
|
_closeConnection(); |
|
|
|
PEG_TRACE((TRC_HTTP, Tracer::LEVEL4, |
|
"_requestCount = %d", _requestCount.get())); |
|
|
|
// |
|
// If we are executing on the server side, the connection |
|
// is closed, return. Do not forward an empty HTTP message. |
|
// |
|
if (!_isClient()) |
|
{ |
|
PEG_METHOD_EXIT(); |
|
return; |
|
} |
|
} |
|
|
|
// If the connection was closed and we are executing on the client |
|
// side send an empty HTTP message. Otherwise, a message was |
|
// received, so process it. |
|
|
|
HTTPMessage* message = new HTTPMessage(_incomingBuffer, getQueueId()); | HTTPMessage* message = new HTTPMessage(_incomingBuffer, getQueueId()); |
message->authInfo = _authInfo.get(); | message->authInfo = _authInfo.get(); |
message->ipAddress = _ipAddress; | message->ipAddress = _ipAddress; |
message->contentLanguages = contentLanguages; |
|
message->dest = _outputMessageQueue->getQueueId(); |
|
|
|
// |
|
// The _closeConnection method sets the _connectionClosePending flag. |
|
// If we are executing on the client side and the |
|
// _connectionClosePending flag is set, send an empty HTTP message. |
|
// |
|
if (_connectionClosePending) |
|
{ |
|
_outputMessageQueue->enqueue(message); |
|
PEG_METHOD_EXIT(); |
|
return; |
|
} |
|
| |
if (_isClient() == false) |
// add any content languages |
{ |
message->contentLanguages = contentLanguages; |
#ifndef PEGASUS_REMOVE_TRACE |
|
if (Tracer::isTraceOn()) |
|
{ |
|
AutoArrayPtr<char> requestTrace( |
|
Tracer::getHTTPRequestMessage(_incomingBuffer)); |
|
PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2, |
|
"<!-- Request: queue id: %u -->\n%s", |
|
getQueueId(), |
|
requestTrace.get())); |
|
} |
|
#endif |
|
} |
|
| |
// | // |
// increment request count | // increment request count |
// | // |
|
if (bytesRead > 0) |
|
{ |
_requestCount++; | _requestCount++; |
_connectionRequestCount++; | _connectionRequestCount++; |
_responsePending = true; |
} |
|
Tracer::trace(TRC_HTTP, Tracer::LEVEL4, |
PEG_TRACE((TRC_HTTP, Tracer::LEVEL4, |
"_requestCount = %d", _requestCount.get()); |
"_requestCount = %d", _requestCount.get())); |
message->dest = _outputMessageQueue->getQueueId(); |
|
// SendForget(message); |
| |
// | // |
// Set the entry status to BUSY. | // Set the entry status to BUSY. |
// | // |
if (_isClient() == false) |
if (_isClient() == false && !_connectionClosePending) |
{ | { |
PEG_TRACE((TRC_HTTP, Tracer::LEVEL2, |
Tracer::trace (TRC_HTTP, Tracer::LEVEL2, |
"Now setting state to %d", _MonitorEntry::BUSY)); |
"Now setting state to %d", _MonitorEntry::BUSY); |
_monitor->setState (_entry_index, _MonitorEntry::BUSY); | _monitor->setState (_entry_index, _MonitorEntry::BUSY); |
_monitor->tickle(); | _monitor->tickle(); |
} | } |
|
|
try | try |
{ | { |
_outputMessageQueue->enqueue(message); | _outputMessageQueue->enqueue(message); |
|
|
} | } |
| |
_clearIncoming(); | _clearIncoming(); |
|
|
|
if (bytesRead == 0) |
|
{ |
|
Tracer::trace(TRC_HTTP, Tracer::LEVEL3, |
|
"HTTPConnection::_handleReadEvent - bytesRead == 0 - " |
|
"Connection being closed."); |
|
_closeConnection(); |
|
|
|
// |
|
// decrement request count |
|
// |
|
Tracer::trace(TRC_HTTP, Tracer::LEVEL4, |
|
"_requestCount = %d", _requestCount.get()); |
|
|
|
PEG_METHOD_EXIT(); |
|
return; |
|
} |
} | } |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
} | } |
|
|
{ | { |
Boolean handled_events = false; | Boolean handled_events = false; |
int events = 0; | int events = 0; |
fd_set fdread; |
fd_set fdread; // , fdwrite; |
struct timeval tv = { 0, 1 }; | struct timeval tv = { 0, 1 }; |
FD_ZERO(&fdread); | FD_ZERO(&fdread); |
FD_SET(getSocket(), &fdread); | FD_SET(getSocket(), &fdread); |
|
|
} | } |
catch (...) | catch (...) |
{ | { |
PEG_TRACE_CSTRING( |
Tracer::trace( |
TRC_DISCARDED_DATA, | TRC_DISCARDED_DATA, |
Tracer::LEVEL2, | Tracer::LEVEL2, |
"HTTPConnection::run handleEnqueue(msg) failure"); | "HTTPConnection::run handleEnqueue(msg) failure"); |