📄 httpconnection.cpp
字号:
* sending them. If not, the message must be queued up until a complete flag * has arrived. */Boolean HTTPConnection::_handleWriteEvent(Message &message){ static const char func[] = "HTTPConnection::_handleWriteEvent"; String httpStatus; HTTPMessage& httpMessage = *(HTTPMessage*)&message; Buffer& buffer = httpMessage.message; Boolean isFirst = message.isFirst(); Boolean isLast = message.isComplete(); Sint32 totalBytesWritten = 0; Uint32 messageLength = buffer.size(); try { // delivery behavior: // 1 handler.processing() : header + optional body? // 2 handler.deliver() : 1+ fully XML encoded object(s) // 3 handler.complete() : deliver() + isLast = true Uint32 bytesRemaining = messageLength; char *messageStart = (char *) buffer.getData(); Uint32 bytesToWrite = httpTcpBufferSize; Uint32 messageIndex = message.getIndex(); Boolean isChunkResponse = false; Boolean isChunkRequest = false; Boolean isFirstException = false; if (_isClient() == false) { // for null termination buffer.reserveCapacity(messageLength + 1); messageStart = (char *) buffer.getData(); messageStart[messageLength] = 0; Tracer::trace( TRC_XML_IO, Tracer::LEVEL2, "<!-- Response: queue id: %u -->\n%s", getQueueId(), buffer.getData()); if (isFirst == true) { _incomingBuffer.clear(); // tracks the message coming from above _transferEncodingChunkOffset = 0; _mpostPrefix.clear(); cimException = CIMException(); _responsePending = true; } else { // this is coming from our own internal code, therefore it is an // internal error. somehow the chunks came out of order. if (_transferEncodingChunkOffset+1 != messageIndex) _throwEventFailure( httpStatusInternal, "chunk sequence mismatch"); _transferEncodingChunkOffset++; } // save the first error if (httpMessage.cimException.getCode() != CIM_ERR_SUCCESS) { httpStatus = httpMessage.cimException.getMessage(); if (cimException.getCode() == CIM_ERR_SUCCESS) { cimException = httpMessage.cimException; // set language to first error language (overriding // anything there) contentLanguages = cimException.getContentLanguages(); isFirstException = true; } } else if (cimException.getCode() == CIM_ERR_SUCCESS) { if (isFirst == true) contentLanguages = httpMessage.contentLanguages; else if (httpMessage.contentLanguages != contentLanguages) contentLanguages.clear(); else contentLanguages = httpMessage.contentLanguages; } // check to see if the client requested chunking OR trailers. // trailers are tightly integrated with chunking, so it can also // be used. if (isChunkRequested() == true) { isChunkRequest = true; } else { // we are not sending chunks because the client did not // request it // save the entire FIRST error response for non-chunked error // responses this will be used as the error message if (isFirstException == true) { // this shouldnt happen, but this is defensive ... if (messageLength == 0) { CIMStatusCode code = httpMessage.cimException.getCode(); String httpDetail(cimStatusCodeToString(code)); char s[21]; sprintf(s, "%u", code); String httpStatus(s); Buffer message = XmlWriter::formatHttpErrorRspMessage (httpStatus, String(), httpDetail); messageLength = message.size(); message.reserveCapacity(messageLength+1); messageStart = (char *) message.getData(); messageStart[messageLength] = 0; } cimException = CIMException(cimException.getCode(), String(messageStart, messageLength)); } if (isFirst == false) { // subsequent chunks from the server, just append messageLength += _incomingBuffer.size(); _incomingBuffer.reserveCapacity(messageLength+1); _incomingBuffer.append(buffer.getData(), buffer.size()); buffer.clear(); // null terminate messageStart = (char *) _incomingBuffer.getData(); messageStart[messageLength] = 0; // put back in buffer, so the httpMessage parser can work // below _incomingBuffer.swap(buffer); } if (isLast == false) { // this tells the send loop below to do nothing until we // are at the last response bytesRemaining = 0; } else { if (cimException.getCode() != CIM_ERR_SUCCESS) { buffer.clear(); // discard all data collected to this point _incomingBuffer.clear(); String messageS = cimException.getMessage(); CString messageC = messageS.getCString(); messageStart = (char *) (const char *) messageC; messageLength = strlen(messageStart); buffer.reserveCapacity(messageLength+1); buffer.append(messageStart, messageLength); // null terminate messageStart = (char *) buffer.getData(); messageStart[messageLength] = 0; } bytesRemaining = messageLength; } } // if not sending chunks // We now need to adjust the contentLength line. // If chunking was requested and this is the first chunk, then // we need to enter this block so we can adjust the header and // send to the client the first set of bytes right away. // If chunking was NOT requested, we have to wait for the last // chunk of the message to get (and set) the size of the content // because we are going to send it the traditional (i.e // non-chunked) way if (isChunkRequest == true && isFirst == true || isChunkRequest == false && isLast == true) { // need to find the end of the header String startLine; Array<HTTPHeader> headers; Uint32 contentLength = 0; // Note: this gets the content length from subtracting the // header length from the messageLength, not by parsing the // content length header field httpMessage.parse(startLine, headers, contentLength); Uint32 httpStatusCode = 0; String httpVersion; String reasonPhrase; Boolean isValid = httpMessage.parseStatusLine( startLine, httpVersion, httpStatusCode, reasonPhrase); Uint32 headerLength = messageLength - contentLength; char save = messageStart[headerLength]; messageStart[headerLength] = 0; char *contentLengthStart = strstr(messageStart, headerNameContentLength); char* contentLengthEnd = contentLengthStart ? strstr(contentLengthStart, headerLineTerminator) : 0; messageStart[headerLength] = save; // the message may or may not have the content length specified // depending on the type of request it is if (contentLengthStart) { // the message has the content length specified. // If we are NOT sending a chunked response, then we need // to overlay the contentLength number to reflect the // actual byte count of the content (i.e message body). // If we ARE sending a chunked response, then we will // overlay the transferEncoding keyword name and value // on top of the contentLength keyword and value. // Important note: // for performance reasons, the contentLength and/or // transferEncoding strings are being overlayed // DIRECTLY inside the message buffer WITHOUT changing // the actual length in bytes of the message. // The XmlWriter has been modified to pad out the // maximum number in zeros to accomodate any number. // The maximum contentLength name and value is identical // to the transferEncoding name and value and can // be easily interchanged. By doing this, we do not have // to piece together the header (and more importantly, // the lengthy body) all over again! // This is why the http line lengths are validated below Uint32 transferEncodingLineLengthExpected = headerNameTransferEncodingLength + headerNameTerminatorLength + headerValueTransferEncodingChunkedLength; Uint32 contentLengthLineLengthExpected = headerNameContentLengthLength + headerNameTerminatorLength + numberAsStringLength; Uint32 contentLengthLineLengthFound = contentLengthEnd - contentLengthStart; if (isValid == false || ! contentLengthEnd || contentLengthLineLengthFound != transferEncodingLineLengthExpected || transferEncodingLineLengthExpected != contentLengthLineLengthExpected) { // these should match up since this is coming // directly from our code in XmlWriter! If not, // some code changes have got out of sync _throwEventFailure(httpStatusInternal, "content length was incorrectly formatted"); } // we will be sending a chunk response if: // 1. chunking has been requested AND // 2. contentLength has been set // (meaning a non-bodyless message has come in) OR // 3. this is not the last message // (meaning the data is coming in pieces and we should // send chunked) if (isChunkRequest == true && (contentLength > 0 || isLast == false)) { isChunkResponse = true; } save = contentLengthStart[contentLengthLineLengthExpected]; contentLengthStart[contentLengthLineLengthExpected] = 0; // overlay the contentLength value if (isChunkResponse == false) { // overwrite the content length number with the actual // byte count char *contentLengthNumberStart = contentLengthStart + headerNameContentLengthLength + headerNameTerminatorLength; char format[6]; sprintf (format, "%%.%uu", numberAsStringLength); // overwrite the bytes in buffer with the content //encoding length sprintf(contentLengthNumberStart, format, contentLength); contentLengthStart[contentLengthLineLengthExpected] = save; } else { // overlay the contentLength name and value with the // transferEncoding name and value sprintf(contentLengthStart, "%s%s%s", headerNameTransferEncoding, headerNameTerminator, headerValueTransferEncodingChunked); bytesToWrite = messageLength - contentLength; contentLengthStart[contentLengthLineLengthExpected] = save; String operationName = headerNameOperation; // look for 2-digit prefix (if mpost was use) HTTPMessage::lookupHeaderPrefix(headers, operationName, _mpostPrefix); } // else chunk response is true } // if content length was found if (isChunkRequest == false) { if (isLast == true) { if (contentLanguages.size() != 0) { // we must insert the content-language into the // header Buffer contentLanguagesString; // this is the keyword:value(s) + header line // terminator contentLanguagesString << headerNameContentLanguage << headerNameTerminator << LanguageParser::buildContentLanguageHeader( contentLanguages).getCString() << headerLineTerminator; Uint32 insertOffset = headerLength - headerLineTerminatorLength; messageLength = contentLanguagesString.size() + buffer.size(); buffer.reserveCapacity(messageLength+1); messageLength = contentLanguagesString.size();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -