Beispiel #1
0
// 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);
    }
Beispiel #3
0
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;
}