bool kul::http::Server::get(PHTTP_REQUEST req){ HTTP_RESPONSE response; initialiseReponse(response, 200, "OK"); addKnownHeader(response, HttpHeaderContentType, "text/html"); HTTP_DATA_CHUNK dataChunk; DWORD result; DWORD bytesSent; std::string s(req->pRawUrl); std::string a; if(s.find("?") != std::string::npos){ a = s.substr(s.find("?") + 1); s = s.substr(0, s.find("?")); } const std::pair<kul::hash::set::String, std::string>& p(handle(s, asAttributes(a))); if(p.second.size()){ dataChunk.DataChunkType = HttpDataChunkFromMemory; dataChunk.FromMemory.pBuffer = (PVOID) p.second.c_str(); dataChunk.FromMemory.BufferLength = p.second.size(); response.EntityChunkCount = 1; response.pEntityChunks = &dataChunk; } result = HttpSendHttpResponse(this->q, req->RequestId, 0, &response, NULL, &bytesSent, NULL, 0, NULL, NULL); if(result != NO_ERROR) KEXCEPT(Exception, "HttpSendHttpResponse failed with: " + std::to_string(result)); return result; }
/* * SendHttpResponse sends a text/html content type back with * the user specified status code and reason. Used for returning * errors to clients. */ DWORD SendHttpResponse( IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest, IN USHORT StatusCode, IN PSTR pReason, IN PSTR pEntityString ) { HTTP_RESPONSE response; HTTP_DATA_CHUNK dataChunk; DWORD result; DWORD bytesSent; CHAR szServerHeader[20]; // Initialize the HTTP response structure. INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason); ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html"); StringCchPrintfA(szServerHeader, sizeof(szServerHeader), "Xmlrpc-c/%s", XMLRPC_C_VERSION); ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader); if(pEntityString) { // Add an entity chunk dataChunk.DataChunkType = HttpDataChunkFromMemory; dataChunk.FromMemory.pBuffer = pEntityString; dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString); response.EntityChunkCount = 1; response.pEntityChunks = &dataChunk; } // Since we are sending all the entity body in one call, we don't have // to specify the Content-Length. result = HttpSendHttpResponse( hReqQueue, // ReqQueueHandle pRequest->RequestId, // Request ID 0, // Flags &response, // HTTP response NULL, // pReserved1 &bytesSent, // bytes sent (OPTIONAL) NULL, // pReserved2 (must be NULL) 0, // Reserved3 (must be 0) NULL, // LPOVERLAPPED (OPTIONAL) NULL // pReserved4 (must be NULL) ); if(result != NO_ERROR) { TraceW(L"HttpSendHttpResponse failed with %lu", result); } return result; }
DWORD SendHttpResponseAuthRequired( IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest ) { HTTP_RESPONSE response; DWORD result; DWORD bytesSent; CHAR szServerHeader[20]; // Initialize the HTTP response structure. INITIALIZE_HTTP_RESPONSE(&response, 401, "Authentication Required"); // Add the WWW_Authenticate header. ADD_KNOWN_HEADER(response, HttpHeaderWwwAuthenticate, "Basic realm=\"xmlrpc\""); StringCchPrintfA(szServerHeader, sizeof(szServerHeader), "Xmlrpc-c/%s", XMLRPC_C_VERSION); ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader); // Since we are sending all the entity body in one call, we don't have // to specify the Content-Length. result = HttpSendHttpResponse( hReqQueue, // ReqQueueHandle pRequest->RequestId, // Request ID 0, // Flags &response, // HTTP response NULL, // pReserved1 &bytesSent, // bytes sent (OPTIONAL) NULL, // pReserved2 (must be NULL) 0, // Reserved3 (must be 0) NULL, // LPOVERLAPPED (OPTIONAL) NULL // pReserved4 (must be NULL) ); if(result != NO_ERROR) { TraceW(L"SendHttpResponseAuthRequired failed with %lu", result); } return result; }
DWORD SendHttpResponse( IN PHTTP_REQUEST pRequest, IN PHTTP_IO_CONTEXT pContext, IN USHORT StatusCode, IN PSTR pReason, IN PSTR pEntityString, IN PSTR pContentLength ) { HTTP_DATA_CHUNK dataChunk; DWORD result; PHTTP_LISTENER listener = pContext->listener; PHTTP_IO_CONTEXT pResponseContext = GetIOContext(); // // Initialize the HTTP response structure. // pResponseContext->Reponse.StatusCode = (StatusCode); pResponseContext->Reponse.pReason = (pReason); pResponseContext->Reponse.ReasonLength = (USHORT) strlen(pReason); // // Add a known header. // //ADD_KNOWN_HEADER pResponseContext->Reponse.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = "text/html"; pResponseContext->Reponse.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen("text/html"); DEBUG_ASSERT(pEntityString); dataChunk.DataChunkType = HttpDataChunkFromMemory; dataChunk.FromMemory.pBuffer = pEntityString; dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString); pResponseContext->Reponse.EntityChunkCount = 1; pResponseContext->Reponse.pEntityChunks = &dataChunk; // // Since we are sending all the entity body in one call, we don't have // to specify the Content-Length. // pResponseContext->operationState = HTTP_LISTENER_STATE_RESPONSE; // Enqueue async IO Request. StartThreadpoolIo(listener->pthreadPoolIO); result = HttpSendHttpResponse( listener->hRequestQueue, // ReqQueueHandle pRequest->RequestId, // Request ID 0, // Flags &pResponseContext->Reponse, // HTTP response NULL, // cache policy NULL, // bytes sent (OPTIONAL) NULL, // pReserved2 (must be NULL) 0, // Reserved3 (must be 0) pResponseContext, // LPOVERLAPPED (OPTIONAL) NULL // pReserved4 (must be NULL) ); 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"\nSynchronous completion response processing error - %lu", result); return result; } else if(result == NO_ERROR) { // Synchronous completion CancelThreadpoolIo(listener->pthreadPoolIO); HttpInputQueueEnqueue(listener, pResponseContext); } else { DEBUG_ASSERT(result == ERROR_IO_PENDING) } return NO_ERROR; }
/* * 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; }