--- /dev/null
+/* 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