apr_status_t WriteResponseCallback(request_rec *r, char *buf, unsigned int length) { REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r); if(rsc == NULL || rsc->m_pRequestRec == NULL || rsc->m_pResponseBuffer == NULL) return APR_SUCCESS; IHttpContext *pHttpContext = rsc->m_pHttpContext; IHttpResponse *pHttpResponse = pHttpContext->GetResponse(); HTTP_RESPONSE *pRawHttpResponse = pHttpResponse->GetRawHttpResponse(); HTTP_DATA_CHUNK *pDataChunk = (HTTP_DATA_CHUNK *)apr_palloc(rsc->m_pRequestRec->pool, sizeof(HTTP_DATA_CHUNK)); pRawHttpResponse->EntityChunkCount = 0; // since we clean the APR pool at the end of OnSendRequest, we must get IIS-managed memory chunk // void *reqbuf = pHttpContext->AllocateRequestMemory(length); memcpy(reqbuf, buf, length); pDataChunk->DataChunkType = HttpDataChunkFromMemory; pDataChunk->FromMemory.pBuffer = reqbuf; pDataChunk->FromMemory.BufferLength = length; CHAR szLength[21]; //Max length for a 64 bit int is 20 ZeroMemory(szLength, sizeof(szLength)); HRESULT hr = StringCchPrintfA( szLength, sizeof(szLength) / sizeof(CHAR) - 1, "%d", length); if(FAILED(hr)) { // not possible } hr = pHttpResponse->SetHeader( HttpHeaderContentLength, szLength, (USHORT)strlen(szLength), TRUE); if(FAILED(hr)) { // possible, but there's nothing we can do } pHttpResponse->WriteEntityChunkByReference(pDataChunk); return APR_SUCCESS; }
apr_status_t WriteBodyCallback(request_rec *r, char *buf, unsigned int length) { REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r); if(rsc == NULL || rsc->m_pRequestRec == NULL) return APR_SUCCESS; IHttpContext *pHttpContext = rsc->m_pHttpContext; IHttpRequest *pHttpRequest = pHttpContext->GetRequest(); CHAR szLength[21]; //Max length for a 64 bit int is 20 ZeroMemory(szLength, sizeof(szLength)); HRESULT hr = StringCchPrintfA( szLength, sizeof(szLength) / sizeof(CHAR) - 1, "%d", length); if(FAILED(hr)) { // not possible } hr = pHttpRequest->SetHeader( HttpHeaderContentLength, szLength, (USHORT)strlen(szLength), TRUE); if(FAILED(hr)) { // possible, but there's nothing we can do } // since we clean the APR pool at the end of OnSendRequest, we must get IIS-managed memory chunk // void *reqbuf = pHttpContext->AllocateRequestMemory(length); memcpy(reqbuf, buf, length); pHttpRequest->InsertEntityBody(reqbuf, length); return APR_SUCCESS; }
BOOL CProtocolBridge::SendDevError(CNodeHttpStoredContext* context, USHORT status, PCTSTR reason, HRESULT hresult, BOOL disableCache) { HRESULT hr; DWORD rawLogSize, htmlLogSize, htmlTotalSize; IHttpContext* ctx; char* rawLog; char* templ1; char* html; char* current; if (500 <= status && CModuleConfiguration::GetDevErrorsEnabled(context->GetHttpContext()) && NULL != context->GetNodeProcess()) { // return a friendly HTTP 200 response with HTML entity body that contains error details // and the content of node.exe stdout/err, if available ctx = context->GetHttpContext(); rawLog = context->GetNodeProcess()->TryGetLog(ctx, &rawLogSize); templ1 = "<p>iisnode encountered an error when processing the request.</p><pre style=\"background-color: eeeeee\">" "HRESULT: 0x%x\n" "HTTP status: %d\n" "HTTP reason: %s</pre>" "<p>You are receiving this HTTP 200 response because <a href=""https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config"">" "system.webServer/iisnode/@devErrorsEnabled</a> configuration setting is 'true'.</p>" "<p>In addition to the log of stdout and stderr of the node.exe process, consider using " "<a href=""http://tomasz.janczuk.org/2011/11/debug-nodejs-applications-on-windows.html"">debugging</a> " "and <a href=""http://tomasz.janczuk.org/2011/09/using-event-tracing-for-windows-to.html"">ETW traces</a> to further diagnose the problem.</p>"; // calculate HTML encoded size of the log htmlLogSize = 0; for (int i = 0; i < rawLogSize; i++) { if (rawLog[i] >= 0 && rawLog[i] <= 0x8 || rawLog[i] >= 0xb && rawLog[i] <= 0xc || rawLog[i] >= 0xe && rawLog[i] <= 0x1f || rawLog[i] >= 0x80 && rawLog[i] <= 0x9f) { // characters disallowed in HTML will be converted to [xAB] notation, thus taking 5 bytes htmlLogSize += 5; } else { switch (rawLog[i]) { default: htmlLogSize++; break; case '&': htmlLogSize += 5; // & break; case '<': case '>': htmlLogSize += 4; // < > break; case '"': case '\'': htmlLogSize += 6; // " ' break; }; } } // calculate total size of HTML and allocate memory htmlTotalSize = strlen(templ1) + 20 /* hresult */ + 20 /* status code */ + strlen(reason) + 300 /* log content heading */ + htmlLogSize; ErrorIf(NULL == (html = (char*)ctx->AllocateRequestMemory(htmlTotalSize)), ERROR_NOT_ENOUGH_MEMORY); RtlZeroMemory(html, htmlTotalSize); // construct the HTML sprintf(html, templ1, hresult, status, reason); if (0 == rawLogSize) { if (CModuleConfiguration::GetLoggingEnabled(ctx)) { strcat(html, "<p>The node.exe process has not written any information to the stdout or stderr.</p>"); } else { strcat(html, "<p>You may get additional information about this error condition by logging stdout and stderr of the node.exe process." "To enable logging, set the <a href=""https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config"">" "system.webServer/iisnode/@loggingEnabled</a> configuration setting to 'true' (current value is 'false').</p>"); } } else { strcat(html, "<p>The last 64k of the output generated by the node.exe process to stdout and stderr is shown below:</p><pre style=\"background-color: eeeeee\">"); current = html + strlen(html); if (htmlLogSize == rawLogSize) { // no HTML encoding is necessary memcpy(current, rawLog, rawLogSize); } else { // HTML encode the log for (int i = 0; i < rawLogSize; i++) { if (rawLog[i] >= 0 && rawLog[i] <= 0x8 || rawLog[i] >= 0xb && rawLog[i] <= 0xc || rawLog[i] >= 0xe && rawLog[i] <= 0x1f || rawLog[i] >= 0x80 && rawLog[i] <= 0x9f) { // characters disallowed in HTML are converted to [xAB] notation *current++ = '['; *current++ = 'x'; *current = rawLog[i] >> 4; *current++ += *current > 9 ? 'A' - 10 : '0'; *current = rawLog[i] & 15; *current++ += *current > 9 ? 'A' - 10 : '0'; *current++ = ']'; } else { switch (rawLog[i]) { default: *current++ = rawLog[i]; break; case '&': *current++ = '&'; *current++ = 'a'; *current++ = 'm'; *current++ = 'p'; *current++ = ';'; break; case '<': *current++ = '&'; *current++ = 'l'; *current++ = 't'; *current++ = ';'; break; case '>': *current++ = '&'; *current++ = 'g'; *current++ = 't'; *current++ = ';'; break; case '"': *current++ = '&'; *current++ = 'q'; *current++ = 'u'; *current++ = 'o'; *current++ = 't'; *current++ = ';'; break; case '\'': *current++ = '&'; *current++ = 'a'; *current++ = 'p'; *current++ = 'o'; *current++ = 's'; *current++ = ';'; break; }; } } }
HRESULT CHttpProtocol::SerializeRequestHeaders(CNodeHttpStoredContext* ctx, void** result, DWORD* resultSize, DWORD* resultLength) { HRESULT hr; PCSTR originalUrl = NULL; USHORT originalUrlLength; DWORD remoteHostSize = INET6_ADDRSTRLEN + 1; char remoteHost[INET6_ADDRSTRLEN + 1]; BOOL addXFF; char** serverVars; int serverVarCount; CheckNull(ctx); CheckNull(result); CheckNull(resultSize); CheckNull(resultLength); IHttpContext* context = ctx->GetHttpContext(); DWORD bufferLength = CModuleConfiguration::GetInitialRequestBufferSize(context); DWORD offset = 0; IHttpRequest* request = context->GetRequest(); HTTP_REQUEST* raw = request->GetRawHttpRequest(); USHORT major, minor; const int tmpSize = 256; char tmp[tmpSize]; PCSTR method = request->GetHttpMethod(); ErrorIf(NULL == (*result = context->AllocateRequestMemory(bufferLength)), ERROR_NOT_ENOUGH_MEMORY); // Determine whether response entity body is to be expected if (method && 0 == strcmpi("HEAD", method)) { // HEAD requests do not have response entity body // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 ctx->SetExpectResponseBody(FALSE); } // Request-Line CheckError(CHttpProtocol::Append(context, method, 0, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, " ", 1, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, ctx->GetTargetUrl(), ctx->GetTargetUrlLength(), result, &bufferLength, &offset)); request->GetHttpVersion(&major, &minor); if (1 == major && 1 == minor) { CheckError(CHttpProtocol::Append(context, " HTTP/1.1\r\n", 11, result, &bufferLength, &offset)); } else if (1 == major && 0 == minor) { CheckError(CHttpProtocol::Append(context, " HTTP/1.0\r\n", 11, result, &bufferLength, &offset)); } else { sprintf(tmp, " HTTP/%d.%d\r\n", major, minor); CheckError(CHttpProtocol::Append(context, tmp, 0, result, &bufferLength, &offset)); } // Known headers for (int i = 0; i < HttpHeaderRequestMaximum; i++) { if (raw->Headers.KnownHeaders[i].RawValueLength > 0) { CheckError(CHttpProtocol::Append(context, CHttpProtocol::httpRequestHeaders[i], 0, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, raw->Headers.KnownHeaders[i].pRawValue, raw->Headers.KnownHeaders[i].RawValueLength, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); } } // Unknown headers if (TRUE == (addXFF = CModuleConfiguration::GetEnableXFF(context))) { PSOCKADDR addr = request->GetRemoteAddress(); DWORD addrSize = addr->sa_family == AF_INET ? sizeof SOCKADDR_IN : sizeof SOCKADDR_IN6; ErrorIf(0 != WSAAddressToString(addr, addrSize, NULL, remoteHost, &remoteHostSize), GetLastError()); } for (int i = 0; i < raw->Headers.UnknownHeaderCount; i++) { CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pName, raw->Headers.pUnknownHeaders[i].NameLength, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pRawValue, raw->Headers.pUnknownHeaders[i].RawValueLength, result, &bufferLength, &offset)); if (addXFF && 15 == raw->Headers.pUnknownHeaders[i].NameLength && 0 == strcmpi("X-Forwarded-For", raw->Headers.pUnknownHeaders[i].pName)) { // augment existing X-Forwarded-For header CheckError(CHttpProtocol::Append(context, ", ", 2, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, remoteHost, remoteHostSize - 1, result, &bufferLength, &offset)); addXFF = FALSE; } CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); } if (addXFF) { // add a new X-Forwarded-For header CheckError(CHttpProtocol::Append(context, "X-Forwarded-For: ", 17, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, remoteHost, remoteHostSize - 1, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); } // promote server variables CheckError(CModuleConfiguration::GetPromoteServerVars(context, &serverVars, &serverVarCount)); while (serverVarCount) { serverVarCount--; PCSTR varValue; DWORD varValueLength; if (S_OK == context->GetServerVariable(serverVars[serverVarCount], &varValue, &varValueLength)) { CheckError(CHttpProtocol::Append(context, "X-iisnode-", 10, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, serverVars[serverVarCount], 0, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, varValue, varValueLength, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); } } // CRLF CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); *resultSize = bufferLength; *resultLength = offset; return S_OK; Error: if (NULL != result) { *result = NULL; } if (NULL != resultLength) { *resultLength = 0; } if (NULL != resultSize) { *resultSize = 0; } return hr; }
HRESULT CHttpProtocol::SerializeRequestHeaders(CNodeHttpStoredContext* ctx, void** result, DWORD* resultSize, DWORD* resultLength) { HRESULT hr; PCSTR originalUrl = NULL; USHORT originalUrlLength; CheckNull(ctx); CheckNull(result); CheckNull(resultSize); CheckNull(resultLength); IHttpContext* context = ctx->GetHttpContext(); DWORD bufferLength = CModuleConfiguration::GetInitialRequestBufferSize(context); DWORD offset = 0; IHttpRequest* request = context->GetRequest(); HTTP_REQUEST* raw = request->GetRawHttpRequest(); USHORT major, minor; char tmp[256]; PCSTR method = request->GetHttpMethod(); ErrorIf(NULL == (*result = context->AllocateRequestMemory(bufferLength)), ERROR_NOT_ENOUGH_MEMORY); // Determine whether response entity body is to be expected if (method && 0 == strcmpi("HEAD", method)) { // HEAD requests do not have response entity body // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 ctx->SetExpectResponseBody(FALSE); } // Request-Line CheckError(CHttpProtocol::Append(context, method, 0, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, " ", 1, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, ctx->GetTargetUrl(), ctx->GetTargetUrlLength(), result, &bufferLength, &offset)); request->GetHttpVersion(&major, &minor); if (1 == major && 1 == minor) { CheckError(CHttpProtocol::Append(context, " HTTP/1.1\r\n", 11, result, &bufferLength, &offset)); } else if (1 == major && 0 == minor) { CheckError(CHttpProtocol::Append(context, " HTTP/1.0\r\n", 11, result, &bufferLength, &offset)); } else { sprintf(tmp, " HTTP/%d.%d\r\n", major, minor); CheckError(CHttpProtocol::Append(context, tmp, 0, result, &bufferLength, &offset)); } // Known headers for (int i = 0; i < HttpHeaderRequestMaximum; i++) { if (raw->Headers.KnownHeaders[i].RawValueLength > 0) { CheckError(CHttpProtocol::Append(context, CHttpProtocol::httpRequestHeaders[i], 0, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, raw->Headers.KnownHeaders[i].pRawValue, raw->Headers.KnownHeaders[i].RawValueLength, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); } } // Unknown headers for (int i = 0; i < raw->Headers.UnknownHeaderCount; i++) { CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pName, raw->Headers.pUnknownHeaders[i].NameLength, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pRawValue, raw->Headers.pUnknownHeaders[i].RawValueLength, result, &bufferLength, &offset)); CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); } // CRLF CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset)); *resultSize = bufferLength; *resultLength = offset; return S_OK; Error: if (NULL != result) { *result = NULL; } if (NULL != resultLength) { *resultLength = 0; } if (NULL != resultSize) { *resultSize = 0; } return hr; }