DWORD EnqueueReceive ( IN PHTTP_LISTENER listener ) { DWORD result; // Create the listener overlapped. PHTTP_IO_CONTEXT pListenerRequest = GetIOContext(); if (pListenerRequest == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } // Initalize the overlapped fields. pListenerRequest->RequestSize = REQUEST_BUFFER_SIZE; pListenerRequest->listener = listener; pListenerRequest->RequestSize = REQUEST_BUFFER_SIZE; HTTP_SET_NULL_ID(&pListenerRequest->requestId); pListenerRequest->operationState = HTTP_LISTENER_STATE_REQUEST; // Enqueue async IO Request. StartThreadpoolIo(listener->pthreadPoolIO); result = HttpReceiveHttpRequest( listener->hRequestQueue, // Req Queue pListenerRequest->requestId, // Req ID 0, // Flags &pListenerRequest->Request, // HTTP request buffer pListenerRequest->RequestSize, // req buffer length NULL, // bytes received pListenerRequest // LPOVERLAPPED ); if(result != NO_ERROR && result != ERROR_IO_PENDING) { // need to call this whenever an async I/O operation fails synchronously CancelThreadpoolIo(listener->pthreadPoolIO); LOG_ERROR(L"\nSycnhronous request processing completion error - %lu", result); return result; } else if(result == NO_ERROR) { // Synchronous completion CancelThreadpoolIo(listener->pthreadPoolIO); HttpInputQueueEnqueue(listener, pListenerRequest); } else { DEBUG_ASSERT(result == ERROR_IO_PENDING) } return NO_ERROR; }
void kul::http::Server::start() throw(kul::http::Exception){ ULONG RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; PCHAR pRequestBuffer = (PCHAR) wAlloc(RequestBufferLength); if(pRequestBuffer == NULL) KEXCEPT(Exception, "Buffer allocation failed: " + std::to_string(ERROR_NOT_ENOUGH_MEMORY)); PHTTP_REQUEST req = (PHTTP_REQUEST) pRequestBuffer; HTTP_REQUEST_ID requestId; HTTP_SET_NULL_ID(&requestId); DWORD bytesRead; ULONG r = 0; while(1){ RtlZeroMemory(req, RequestBufferLength); r = HttpReceiveHttpRequest(this->q, requestId, 0, req, RequestBufferLength, &bytesRead, NULL); if(r == NO_ERROR){ if(req->Verb == HttpVerbGET){ get(req); }else if(req->Verb == HttpVerbPOST){ post(req); }else if(req->Verb == HttpVerbHEAD){ // head(req); }else KLOG(INF) << "Unrecognised http method type"; }else if(r == ERROR_MORE_DATA){ requestId = req->RequestId; RequestBufferLength = bytesRead; wFreeM(pRequestBuffer); pRequestBuffer = (PCHAR) wAlloc(RequestBufferLength); }else if(r == ERROR_CONNECTION_INVALID && !HTTP_IS_NULL_ID(&requestId)){ HTTP_SET_NULL_ID(&requestId); }else KEXCEPT(Exception, "HttpReceiveHttpRequest failed: " + std::to_string(r)); } }
/* * This is a blocking function that merely sits on the request queue * for our URI and processes them one at a time. Once a request comes * in, we check it for content-type, content-length, and verb. As long * as the initial validations are done, we pass the request to the * processRPCCall() function, which collects the body of the request * and processes it. If we get an error back other than network type, * we are responsible for notifing the client. */ DWORD DoReceiveRequests( IN HANDLE hReqQueue, const xmlrpc_server_httpsys_parms * const parmsP ) { ULONG result; HTTP_REQUEST_ID requestId; DWORD bytesRead; PHTTP_REQUEST pRequest; PCHAR pRequestBuffer; ULONG RequestBufferLength; xmlrpc_env env; char szHeaderBuf[255]; long lContentLength; // Allocate a 2K buffer. Should be good for most requests, we'll grow // this if required. We also need space for a HTTP_REQUEST structure. RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } pRequest = (PHTTP_REQUEST)pRequestBuffer; // Wait for a new request -- This is indicated by a NULL request ID. HTTP_SET_NULL_ID( &requestId ); for(;;) { RtlZeroMemory(pRequest, RequestBufferLength); result = HttpReceiveHttpRequest( hReqQueue, // Req Queue requestId, // Req ID 0, // Flags pRequest, // HTTP request buffer RequestBufferLength,// req buffer length &bytesRead, // bytes received NULL // LPOVERLAPPED ); if(NO_ERROR == result) { // Got a request with a filled buffer. switch(pRequest->Verb) { case HttpVerbPOST: TraceW(L"Got a POST request for %ws", pRequest->CookedUrl.pFullUrl); //Check if we need use authorization. if(parmsP->authfn) { xmlrpc_env_init(&env); if(pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization].RawValueLength < 6) { xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, "Authorization header too short."); } else { //unencode the headers if(_strnicmp( "basic", pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization].pRawValue,5) !=0) { #ifndef NDEBUG PCHAR pTmp = (PCHAR) ALLOC_MEM(pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength + 1 ); if( pTmp ) { strncpy(pTmp, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].pRawValue, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength ); pTmp[pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength] = 0; TraceA("Got HEADER [%s]",pTmp); FREE_MEM(pTmp); } #endif /* #ifndef NDEBUG */ xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, "Authorization header does not start " "with type 'basic'!"); } else { xmlrpc_mem_block * decoded; decoded = xmlrpc_base64_decode( &env, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].pRawValue+6, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength-6); if(!env.fault_occurred) { char *pDecodedStr; char *pUser; char *pPass; char *pColon; pDecodedStr = (char*) malloc(decoded->_size+1); memcpy(pDecodedStr, decoded->_block, decoded->_size); pDecodedStr[decoded->_size]='\0'; pUser = pPass = pDecodedStr; pColon=strchr(pDecodedStr,':'); if(pColon) { *pColon='\0'; pPass=pColon+1; //The authfn should set env to //fail if auth is denied. parmsP->authfn(&env,pUser,pPass); } else { xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, "Decoded auth not of the correct " "format."); } free(pDecodedStr); } if(decoded) XMLRPC_MEMBLOCK_FREE(char, decoded); } } if(env.fault_occurred) { //request basic authorization, as the user //did not provide it. xmlrpc_env_clean(&env); TraceW(L"POST request did not provide valid " L"authorization header."); result = SendHttpResponseAuthRequired( hReqQueue, pRequest); break; } xmlrpc_env_clean(&env); } //Check content type to make sure it is text/xml. memcpy(szHeaderBuf, pRequest->Headers.KnownHeaders[ HttpHeaderContentType ].pRawValue, pRequest->Headers.KnownHeaders[ HttpHeaderContentType ].RawValueLength); szHeaderBuf[pRequest->Headers.KnownHeaders[ HttpHeaderContentType ].RawValueLength] = '\0'; if (_stricmp(szHeaderBuf,"text/xml")!=0) { //We handle only text/xml data. Anything else //is not valid. TraceW(L"POST request had an unrecognized " L"content-type: %s", szHeaderBuf); result = SendHttpResponse( hReqQueue, pRequest, 400, "Bad Request", NULL ); break; } //Check content length to make sure it exists and //is not too big. memcpy(szHeaderBuf, pRequest->Headers.KnownHeaders[ HttpHeaderContentLength ].pRawValue, pRequest->Headers.KnownHeaders[ HttpHeaderContentLength ].RawValueLength); szHeaderBuf[pRequest->Headers.KnownHeaders[ HttpHeaderContentLength ].RawValueLength]='\0'; lContentLength = atol(szHeaderBuf); if (lContentLength<=0) { //Make sure a content length was supplied. TraceW(L"POST request did not include a " L"content-length", szHeaderBuf); result = SendHttpResponse( hReqQueue, pRequest, 411, "Length Required", NULL ); break; } if((size_t) lContentLength > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) { //Content-length is too big for us to handle TraceW(L"POST request content-length is too big " L"for us to handle: %d bytes", lContentLength); result = SendHttpResponse( hReqQueue, pRequest, 500, "content-length too large", NULL ); break; } //our initial validations of POST, content-type, //and content-length all check out. Collect and //pass the complete buffer to the XMLRPC-C library xmlrpc_env_init(&env); processRPCCall(&env,hReqQueue, pRequest); if (env.fault_occurred) { //if we fail and it is anything other than a //network error, we should return a failure //response to the client. if (env.fault_code != XMLRPC_NETWORK_ERROR) { if (env.fault_string) result = SendHttpResponse( hReqQueue, pRequest, 500, env.fault_string, NULL ); else result = SendHttpResponse( hReqQueue, pRequest, 500, "Unknown Error", NULL ); } } xmlrpc_env_clean(&env); break; default: //We handle only POST data. Anything else is not valid. TraceW(L"Got an unrecognized Verb request for URI %ws", pRequest->CookedUrl.pFullUrl); result = SendHttpResponse( hReqQueue, pRequest, 405, "Method Not Allowed", NULL ); break; } if(result != NO_ERROR) { break; } // Reset the Request ID so that we pick up the next request. HTTP_SET_NULL_ID( &requestId ); } else if(result == ERROR_MORE_DATA) { // The input buffer was too small to hold the request headers // We have to allocate more buffer & call the API again. // // When we call the API again, we want to pick up the request // that just failed. This is done by passing a RequestID. // This RequestID is picked from the old buffer. requestId = pRequest->RequestId; // Free the old buffer and allocate a new one. RequestBufferLength = bytesRead; FREE_MEM( pRequestBuffer ); pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; break; } pRequest = (PHTTP_REQUEST)pRequestBuffer; } else if(ERROR_CONNECTION_INVALID == result && !HTTP_IS_NULL_ID(&requestId)) { // The TCP connection got torn down by the peer when we were // trying to pick up a request with more buffer. We'll just move // onto the next request. HTTP_SET_NULL_ID( &requestId ); } else { break; } } // for(;;) if(pRequestBuffer) { FREE_MEM( pRequestBuffer ); } return result; }