// Our workhorse. Implementations call this to get the next HTTP request ULONG RequestQueue::GetNextRequest(HTTP_REQUEST_ID RequestId ,ULONG Flags ,PHTTP_REQUEST RequestBuffer ,ULONG RequestBufferLength ,PULONG Bytes) { ULONG result = NO_ERROR; Request* request = nullptr; if(RequestId == 0) { // Get a new request from the incoming queue AutoCritSec lock(&m_lock); if(m_incoming.empty()) { lock.Unlock(); // Wait for event of incoming request DWORD result = WaitForSingleObject(m_event,INFINITE); lock.Relock(); // Still no request in the queue, or event interrupted if(m_incoming.empty() || result == WAIT_ABANDONED || result == WAIT_FAILED) { return ERROR_HANDLE_EOF; } } // Move request to servicing queue request = m_incoming.front(); m_incoming.pop_front(); m_servicing.push_back(request); request->SetStatus(RQ_READING); // BitBlitting our request! memcpy_s(RequestBuffer,RequestBufferLength,request->GetV2Request(),sizeof(HTTP_REQUEST_V2)); for(int index = 0;index < RequestBuffer->RequestInfoCount;++index) { if(RequestBuffer->pRequestInfo[index].InfoType == HttpRequestInfoTypeAuth) { PHTTP_REQUEST_AUTH_INFO info = (PHTTP_REQUEST_AUTH_INFO)RequestBuffer->pRequestInfo[index].pInfo; if(info->AccessToken) { // Taking a duplicate token HANDLE token = NULL; if(DuplicateTokenEx(request->GetAccessToken() ,TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_ALL_ACCESS | TOKEN_READ | TOKEN_WRITE ,NULL ,SecurityImpersonation ,TokenImpersonation ,&token) == FALSE) { token = NULL; } // Set cloned token in the request buffer info->AccessToken = token; } } } } else { // Check that we are restarting this request if(RequestId != RequestBuffer->RequestId) { return ERROR_INVALID_PARAMETER; } // This is our request. Flags will be set! request = reinterpret_cast<Request*>(RequestId); } // Reading chunks if(Flags) { // Read a one chunk from the stream LONG length = (LONG)RequestBufferLength - sizeof(HTTP_REQUEST_V2) - 16; PVOID buffer = (PUCHAR)RequestBuffer + sizeof(HTTP_REQUEST_V2) + 16; // Buffer must at least be capable to receive one standard message // Otherwise the caller must use "HttpReceiveRequestEntityBody" !! if(length < MESSAGE_BUFFER_LENGTH) { return ERROR_MORE_DATA; } // Get the buffer int result = HttpReceiveRequestEntityBody(this,RequestId,Flags,buffer,length,Bytes,NULL); if(result == NO_ERROR) { // If no problem, set as first chunk in the request structure request->ReceiveChunk(buffer,length); } } // Reflect total bytes read if(Bytes) { *Bytes = request->GetBytes(); } return result; }
/* * processRPCCall() is called after some validations. The assumption is that * the request is an HTTP post of content-type text/xml with a content-length * that is less than the maximum the library can handle. * * The caller should check the error status, and if the error was other than * a network type, respond back to the client to let them know the call failed. */ void processRPCCall( xmlrpc_env * const envP, IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest ) { HTTP_RESPONSE response; DWORD result; DWORD bytesSent; PUCHAR pEntityBuffer; ULONG EntityBufferLength; ULONG BytesRead; #define MAX_ULONG_STR ((ULONG) sizeof("4294967295")) CHAR szContentLength[MAX_ULONG_STR]; CHAR szServerHeader[20]; HTTP_DATA_CHUNK dataChunk; ULONG TotalBytesRead = 0; xmlrpc_mem_block * body; xmlrpc_mem_block * output; BytesRead = 0; body = NULL; output = NULL; // Allocate some space for an entity buffer. EntityBufferLength = 2048; pEntityBuffer = (PUCHAR) ALLOC_MEM( EntityBufferLength ); if (pEntityBuffer == NULL) { xmlrpc_faultf(envP, "Out of Memory"); goto Done; } // NOTE: If we had passed the HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY // flag with HttpReceiveHttpRequest(), the entity would have // been a part of HTTP_REQUEST (using the pEntityChunks field). // Since we have not passed that flag, we can be assured that // there are no entity bodies in HTTP_REQUEST. if(pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS) { //Allocate some space for an XMLRPC memory block. body = xmlrpc_mem_block_new(envP, 0); if (envP->fault_occurred) goto Done; // The entity body can be sent over multiple calls. Let's collect all // of these in a buffer and send the buffer to the xmlrpc-c library do { // Read the entity chunk from the request. BytesRead = 0; result = HttpReceiveRequestEntityBody( hReqQueue, pRequest->RequestId, 0, pEntityBuffer, EntityBufferLength, &BytesRead, NULL ); switch(result) { case NO_ERROR: if(BytesRead != 0) { XMLRPC_MEMBLOCK_APPEND(char, envP, body, pEntityBuffer, BytesRead); if(envP->fault_occurred) goto Done; } break; case ERROR_HANDLE_EOF: // We have read the last request entity body. We can now // process the suppossed XMLRPC data. if(BytesRead != 0) { XMLRPC_MEMBLOCK_APPEND(char, envP, body, pEntityBuffer, BytesRead); if(envP->fault_occurred) goto Done; } // We will send the response over multiple calls. // This is achieved by passing the // HTTP_SEND_RESPONSE_FLAG_MORE_DATA flag. // NOTE: Since we are accumulating the TotalBytesRead in // a ULONG, this will not work for entity bodies that // are larger than 4 GB. To work with large entity // bodies, we would have to use a ULONGLONG. TraceA("xmlrpc_server RPC2 handler processing " "RPC request."); // Process the RPC. xmlrpc_registry_process_call2( envP, global_registryP, XMLRPC_MEMBLOCK_CONTENTS(char, body), XMLRPC_MEMBLOCK_SIZE(char, body), NULL, &output); if (envP->fault_occurred) goto Done; // Initialize the HTTP response structure. INITIALIZE_HTTP_RESPONSE(&response, 200, "OK"); //Add the content-length StringCchPrintfA(szContentLength,MAX_ULONG_STR, "%lu", XMLRPC_MEMBLOCK_SIZE(char, output)); ADD_KNOWN_HEADER( response, HttpHeaderContentLength, szContentLength ); //Add the content-type ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/xml"); StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION); ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader); //send the response result = HttpSendHttpResponse( hReqQueue, // ReqQueueHandle pRequest->RequestId, // Request ID HTTP_SEND_RESPONSE_FLAG_MORE_DATA, &response, // HTTP response NULL, // pReserved1 &bytesSent, // bytes sent (optional) NULL, // pReserved2 0, // Reserved3 NULL, // LPOVERLAPPED NULL // pReserved4 ); if(result != NO_ERROR) { TraceW(L"HttpSendHttpResponse failed with %lu", result); xmlrpc_env_set_fault_formatted( envP, XMLRPC_NETWORK_ERROR, "HttpSendHttpResponse failed with %lu", result); goto Done; } // Send entity body from a memory chunk. dataChunk.DataChunkType = HttpDataChunkFromMemory; dataChunk.FromMemory.BufferLength = (ULONG)XMLRPC_MEMBLOCK_SIZE(char, output); dataChunk.FromMemory.pBuffer = XMLRPC_MEMBLOCK_CONTENTS(char, output); result = HttpSendResponseEntityBody( hReqQueue, pRequest->RequestId, 0, // This is the last send. 1, // Entity Chunk Count. &dataChunk, NULL, NULL, 0, NULL, NULL ); if(result != NO_ERROR) { TraceW(L"HttpSendResponseEntityBody failed " L"with %lu", result); xmlrpc_env_set_fault_formatted( envP, XMLRPC_NETWORK_ERROR, "HttpSendResponseEntityBody failed with %lu", result); goto Done; } goto Done; break; default: TraceW(L"HttpReceiveRequestEntityBody failed with %lu", result); xmlrpc_env_set_fault_formatted( envP, XMLRPC_NETWORK_ERROR, "HttpReceiveRequestEntityBody failed " "with %lu", result); goto Done; } } while(TRUE); }
bool kul::http::Server::post(PHTTP_REQUEST req){ HTTP_RESPONSE response; DWORD result; DWORD bytesSent; ULONG EntityBufferLength = 512; PUCHAR rstr = (PUCHAR) wAlloc(EntityBufferLength); ULONG bytes = 0; CHAR szContentLength[MAX_ULONG_STR]; HTTP_DATA_CHUNK dataChunk; if (rstr == NULL){ postClean(rstr); KEXCEPT(Exception, "Insufficient resources " + std::to_string(ERROR_NOT_ENOUGH_MEMORY)); } initialiseReponse(response, 200, "OK"); if(req->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS){ std::string atts; do{ bytes = 0; result = HttpReceiveRequestEntityBody(this->q, req->RequestId, 0, rstr, EntityBufferLength, &bytes, NULL); switch(result){ case NO_ERROR: for(ULONG i = 0; i < bytes; i++) atts += rstr[i]; break; case ERROR_HANDLE_EOF: { for(ULONG i = 0; i < bytes; i++) atts += rstr[i]; sprintf_s(szContentLength, MAX_ULONG_STR, "%lu", bytes); addKnownHeader(response, HttpHeaderContentLength, szContentLength); result = HttpSendHttpResponse(this->q, req->RequestId, HTTP_SEND_RESPONSE_FLAG_MORE_DATA, &response, NULL, &bytesSent, NULL, 0, NULL, NULL); if(result != NO_ERROR){ postClean(rstr); KEXCEPT(Exception, "HttpSendHttpResponse failed with: " + std::to_string(result)); } const std::pair<kul::hash::set::String, std::string>& p(handle(req->pRawUrl, asAttributes(atts))); dataChunk.DataChunkType = HttpDataChunkFromMemory; dataChunk.FromMemory.pBuffer = (PVOID) p.second.c_str(); dataChunk.FromMemory.BufferLength = p.second.size(); result = HttpSendResponseEntityBody(this->q, req->RequestId, 0, 1, &dataChunk, NULL, NULL, 0, NULL, NULL); if(result != NO_ERROR){ postClean(rstr); KEXCEPT(Exception, "HttpSendResponseEntityBody failed with: " + std::to_string(result)); } break; } default: postClean(rstr); return result; } }while(TRUE); }else{ // This request does not have an entity body. result = HttpSendHttpResponse(this->q, req->RequestId, 0, &response, NULL, &bytesSent, NULL,0, NULL, NULL); if(result != NO_ERROR){ postClean(rstr); KEXCEPT(Exception, "HttpSendHttpResponse failed with: " + std::to_string(result)); } } postClean(rstr); return result; }