initial load of upstream version 1.06.32
[xmlrpc-c] / src / xmlrpc_server_w32httpsys.c
diff --git a/src/xmlrpc_server_w32httpsys.c b/src/xmlrpc_server_w32httpsys.c
new file mode 100644 (file)
index 0000000..f899d1f
--- /dev/null
@@ -0,0 +1,924 @@
+/* Copyright (C) 2005 by Steven A. Bone, sbone@pobox.com. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission. 
+**  
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE. */
+
+/* COMPILATION NOTE:
+   Note that the Platform SDK headers and
+   link libraries for Windows XP SP2 or newer are required to compile
+   xmlrpc-c for this module.  If you are not using this server, it is 
+   safe to exclude the xmlrpc_server_w32httpsys.c file from the xmlrpc
+   project and these dependencies will not be required.  You can get the 
+   latest platform SDK at 
+   http://www.microsoft.com/msdownload/platformsdk/sdkupdate/
+   Be sure after installation to choose the program to "register the PSDK
+   directories with Visual Studio" so the newer headers are found.
+*/
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#ifndef _UNICODE
+#define _UNICODE
+#endif
+
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/server.h"
+#include "xmlrpc-c/server_w32httpsys.h"
+#include "version.h"
+
+#if MUST_BUILD_HTTP_SYS_SERVER > 0
+
+/* See compilation note above if this header is not found! */
+#include <http.h>
+#include <windows.h>
+#include <strsafe.h>
+
+#pragma comment( lib, "httpapi" )
+
+
+/* XXX - This variable is *not* currently threadsafe. Once the server has
+** been started, it must be treated as read-only. */
+static xmlrpc_registry *global_registryP;
+
+//set TRUE if you want a log
+static BOOL g_bDebug;
+//set log filename
+static char g_fLogFile[MAX_PATH];
+//do you want OutputDebugString() to be called?
+static BOOL g_bDebugString;
+
+//
+// Macros.
+//
+#define INITIALIZE_HTTP_RESPONSE( resp, status, reason )                    \
+    do                                                                      \
+    {                                                                       \
+        RtlZeroMemory( (resp), sizeof(*(resp)) );                           \
+        (resp)->StatusCode = (status);                                      \
+        (resp)->pReason = (reason);                                         \
+        (resp)->ReasonLength = (USHORT) strlen(reason);                     \
+    } while (FALSE)
+
+
+#define ADD_KNOWN_HEADER(Response, HeaderId, RawValue)                      \
+    do                                                                      \
+    {                                                                       \
+        (Response).Headers.KnownHeaders[(HeaderId)].pRawValue = (RawValue); \
+        (Response).Headers.KnownHeaders[(HeaderId)].RawValueLength =        \
+            (USHORT) strlen(RawValue);                                      \
+    } while(FALSE)
+
+#define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb))
+#define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr))
+
+//
+// Prototypes for Internal Functions.
+//
+DWORD
+DoReceiveRequests(
+    HANDLE hReqQueue,
+       const xmlrpc_server_httpsys_parms * const parmsP
+    );
+
+DWORD
+SendHttpResponse(
+    IN HANDLE        hReqQueue,
+    IN PHTTP_REQUEST pRequest,
+    IN USHORT        StatusCode,
+    IN PSTR          pReason,
+    IN PSTR          pEntity
+    );
+
+DWORD
+SendHttpResponseAuthRequired(
+    IN HANDLE        hReqQueue,
+    IN PHTTP_REQUEST pRequest
+    );
+
+void
+processRPCCall(
+    xmlrpc_env *     const envP,
+    IN HANDLE        hReqQueue,
+    IN PHTTP_REQUEST pRequest
+    );
+
+__inline void TraceA(const char *format, ...);
+__inline void TraceW(const wchar_t *format, ...);
+
+
+//
+// External Function Implementation.
+//
+
+void
+xmlrpc_server_httpsys(
+       xmlrpc_env *                        const envP,
+    const xmlrpc_server_httpsys_parms * const parmsP,
+    unsigned int                        const parm_size
+       )
+{
+       ULONG           retCode;
+    HANDLE          hReqQueue      = NULL;
+    HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
+       WCHAR           wszURL[35];
+
+       XMLRPC_ASSERT_ENV_OK(envP);
+
+    if (parm_size < XMLRPC_HSSIZE(authfn))
+       {
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR,
+            "You must specify members at least up through "
+            "'authfn' in the server parameters argument.  "
+            "That would mean the parameter size would be >= %u "
+            "but you specified a size of %u",
+            XMLRPC_HSSIZE(authfn), parm_size);
+               return;
+       }
+
+       //Set logging options
+       if (parmsP->logLevel>0)
+               g_bDebug=TRUE;
+       else
+               g_bDebug=FALSE;
+
+       if (parmsP->logLevel>1)
+               g_bDebugString=TRUE;
+       else
+               g_bDebugString=FALSE;
+
+       if (!parmsP->logFile)
+               g_bDebug=FALSE;
+       else
+               StringCchPrintfA(g_fLogFile,MAX_PATH,parmsP->logFile);
+
+       //construct the URL we are listening on
+       if (parmsP->useSSL!=0)
+               StringCchPrintf(wszURL,35,L"https://+:%u/RPC2",parmsP->portNum);
+       else
+               StringCchPrintf(wszURL,35,L"http://+:%u/RPC2",parmsP->portNum);
+
+       global_registryP = parmsP->registryP;
+
+       // Initialize HTTP APIs.
+       retCode = HttpInitialize( 
+                               HttpApiVersion,
+                               HTTP_INITIALIZE_SERVER,    // Flags
+                               NULL                       // Reserved
+                               );
+       if (retCode != NO_ERROR)
+       {
+               xmlrpc_env_set_fault_formatted(
+                       envP, XMLRPC_INTERNAL_ERROR,
+                       "HttpInitialize failed with %lu \n ",
+                       retCode);
+               return;
+       }
+
+       // Create a Request Queue Handle
+       retCode = HttpCreateHttpHandle(
+                               &hReqQueue,        // Req Queue
+                               0                  // Reserved
+                               );
+       if (retCode != NO_ERROR)
+       { 
+               xmlrpc_env_set_fault_formatted(
+                       envP, XMLRPC_INTERNAL_ERROR,
+                       "HttpCreateHttpHandle failed with %lu \n ",
+                       retCode);
+               goto CleanUp;
+       }
+
+       retCode = HttpAddUrl(
+                               hReqQueue,    // Req Queue
+                               wszURL,      // Fully qualified URL
+                               NULL          // Reserved
+                               );
+
+       if (retCode != NO_ERROR)
+       {
+               xmlrpc_env_set_fault_formatted(
+                       envP, XMLRPC_INTERNAL_ERROR,
+                       "HttpAddUrl failed with %lu \n ",
+                       retCode);
+               goto CleanUp;
+       }
+
+       TraceW( L"we are listening for requests on the following url: %ws\n", wszURL);
+
+       // Loop while receiving requests
+       for(;;)
+       {
+               TraceW( L"Calling DoReceiveRequests()\n");
+               retCode = DoReceiveRequests(hReqQueue, parmsP);
+               if(NO_ERROR == retCode)
+               {
+                       TraceW( L"DoReceiveRequests() returned NO_ERROR, breaking");
+                       break;
+               }
+       }
+
+CleanUp:
+
+       TraceW( L"Tearing down the server.\n", wszURL);
+
+       // Call HttpRemoveUrl for the URL that we added.
+       HttpRemoveUrl( hReqQueue, wszURL );
+
+       // Close the Request Queue handle.
+       if(hReqQueue)
+               CloseHandle(hReqQueue);
+
+       // Call HttpTerminate.
+       HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
+       return;
+}
+
+//
+// Internal Function Implementations.
+//
+
+__inline void TraceA(const char *format, ...)
+{
+       if(g_bDebug)
+       {
+               if (format)
+               {
+                       va_list arglist;
+                       char str[4096];
+
+                       va_start(arglist, format);
+                       if (g_fLogFile)
+                       {
+                               FILE *fout = fopen(g_fLogFile, "a+t");
+                               if (fout)
+                               {
+                                       vfprintf(fout, format, arglist);
+                                       fclose(fout);
+                               }
+                       }
+
+                       StringCchVPrintfA(str,4096, format, arglist);
+                       printf(str);
+
+                       if (g_bDebugString)
+                       {
+                               
+                               OutputDebugStringA(str);
+                       }
+
+                       va_end(arglist);
+               }
+       }
+}
+
+__inline void TraceW(const wchar_t *format, ...)
+{
+       if(g_bDebug)
+       {
+               if (format)
+               {
+                       va_list arglist;
+                       wchar_t str[4096];
+
+                       va_start(arglist, format);
+                       if (g_fLogFile)
+                       {
+                               FILE *fout = fopen(g_fLogFile, "a+t");
+                               if (fout)
+                               {
+                                       vfwprintf(fout, format, arglist);
+                                       fclose(fout);
+                               }
+                       }
+                       
+                       StringCchVPrintfW(str, 4096, format, arglist);
+                       wprintf(str);
+                       
+                       if (g_bDebugString)
+                       {                               
+                               OutputDebugStringW(str);
+                       }
+
+                       va_end(arglist);
+               }
+       }
+}
+
+/*
+ * 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 \n",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,6)!=0)
+                                                       {
+                                                               xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, 
+                                                                       "Authorization header is not of 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 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 only handle text/xml data.  Anything else is not valid.
+                                               TraceW(L"POST request had an unsupported content-type: %s \n", 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 content-length \n", 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 for us to handle: %d bytes \n", 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 only handle POST data.  Anything else is not valid.
+                    TraceW(L"Got an unsupported Verb request for URI %ws \n", 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(;;)
+Cleanup:
+
+    if(pRequestBuffer)
+    {
+        FREE_MEM( pRequestBuffer );
+    }
+
+    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,20, "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 \n", result);
+    }
+
+    return result;
+}
+
+/*
+ * SendHttpResponseAuthRequired sends a 401 status code requesting authorization
+ */
+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,20, "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 \n", result);
+    }
+
+    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_env_set_fault_formatted(
+                               envP, XMLRPC_INTERNAL_ERROR,
+                               "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_TYPED_MEM_BLOCK_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_TYPED_MEM_BLOCK_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. For supporting large entity
+                    //       bodies, we would have to use a ULONGLONG.
+                                       TraceA("xmlrpc_server RPC2 handler processing RPC request.\n");
+                                                                               
+                                       // Process the RPC.
+                                       output = xmlrpc_registry_process_call(
+                                                                               envP, global_registryP, NULL, 
+                                                                               XMLRPC_MEMBLOCK_CONTENTS(char, body),
+                                                                               XMLRPC_MEMBLOCK_SIZE(char, body));
+                                       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 \n", 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 with %lu \n", 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 \n", result);
+                                       xmlrpc_env_set_fault_formatted(
+                                                               envP, XMLRPC_NETWORK_ERROR,
+                                                               "HttpReceiveRequestEntityBody failed with %lu", result);
+                    goto Done;
+            }
+        } while(TRUE);
+    }
+    else
+    {
+               // This request does not have an entity body. 
+               TraceA("Received a bad request (no body in HTTP post).\n");
+               xmlrpc_env_set_fault_formatted(
+                               envP, XMLRPC_PARSE_ERROR,
+                               "Bad POST request (no body)");
+        goto Done;
+    }
+
+Done:
+
+    if(pEntityBuffer)
+        FREE_MEM(pEntityBuffer);
+
+       if(output)
+               XMLRPC_MEMBLOCK_FREE(char, output);
+
+       if(body)
+               XMLRPC_MEMBLOCK_FREE(char, body);
+
+    return;
+}
+
+#endif /* #if MUST_BUILD_HTTP_SYS_SERVER <> 0 */
\ No newline at end of file