http server样例
阅读原文时间:2021年04月23日阅读:1
// TopdeskSvr.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <http.h>
#include <Windows.h>
#include <assert.h>
#include <iostream>
#include <conio.h>
#include <process.h>
#include <atlbase.h>
#include <string>
#include <atlstr.h>
using namespace std;

#pragma comment(lib, "httpapi.lib")
#define CPK 1
#define CK_EXIT 0
#define TRDCOUNT 10
#define BUFFERLEN 16*1024
#define ITEM(x) {x, _T(#x)}
#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 CHECKRETUL(ul) if (ul!=NO_ERROR)\
{\
    cout<<"ur="<<ul<<endl;\
}

void _send_http_response(HTTP_REQUEST* HttpRequest,HANDLE ReqQueueHandle, BOOL bAnsyc=FALSE);

enum IO_OPERATION
{
    ClientIORead,
    ClientIOWrite
};

typedef struct _TRDCONTEXT
{
    HANDLE hCompletionPort;
    HANDLE ReqQueueHandle;
}TRDCONTEXT, *PTRDCONTEXT;

struct OVERLAPPEDEX : OVERLAPPED 
{
    CHAR   buffer[BUFFERLEN];
    HTTP_REQUEST_ID HttpRequestId;
    IO_OPERATION IOType;
    HANDLE hFile;
};

struct HTTP_HEADER_DESC 
{
    HTTP_HEADER_ID id;
    PCTSTR desc;
};

HTTP_HEADER_DESC gvRequestHeaderDesc[]={
    ITEM(HttpHeaderCacheControl),
    ITEM(HttpHeaderConnection),
    ITEM(HttpHeaderDate),
    ITEM(HttpHeaderKeepAlive),
    ITEM(HttpHeaderPragma),
    ITEM(HttpHeaderTrailer),
    ITEM(HttpHeaderTransferEncoding),
    ITEM(HttpHeaderUpgrade),
    ITEM(HttpHeaderVia),
    ITEM(HttpHeaderWarning),
    ITEM(HttpHeaderAllow),
    ITEM(HttpHeaderContentLength),
    ITEM(HttpHeaderContentType),
    ITEM(HttpHeaderContentEncoding),
    ITEM(HttpHeaderContentLanguage),
    ITEM(HttpHeaderContentLocation),
    ITEM(HttpHeaderContentMd5),
    ITEM(HttpHeaderContentRange),
    ITEM(HttpHeaderExpires),
    ITEM(HttpHeaderLastModified),
    ITEM(HttpHeaderAccept),
    ITEM(HttpHeaderAcceptCharset),
    ITEM(HttpHeaderAcceptEncoding),
    ITEM(HttpHeaderAcceptLanguage),
    ITEM(HttpHeaderAuthorization),
    ITEM(HttpHeaderCookie),
    ITEM(HttpHeaderExpect),
    ITEM(HttpHeaderFrom),
    ITEM(HttpHeaderHost),
    ITEM(HttpHeaderIfMatch),
    ITEM(HttpHeaderIfModifiedSince),
    ITEM(HttpHeaderIfNoneMatch),
    ITEM(HttpHeaderIfRange),
    ITEM(HttpHeaderIfUnmodifiedSince),
    ITEM(HttpHeaderMaxForwards),
    ITEM(HttpHeaderProxyAuthorization),
    ITEM(HttpHeaderReferer),
    ITEM(HttpHeaderRange),
    ITEM(HttpHeaderTe),
    ITEM(HttpHeaderTranslate),
    ITEM(HttpHeaderUserAgent),
    ITEM(HttpHeaderRequestMaximum)
};

HTTP_HEADER_DESC gvResponseHeaderDesc[]={
    ITEM(HttpHeaderCacheControl),
    ITEM(HttpHeaderConnection),
    ITEM(HttpHeaderDate),
    ITEM(HttpHeaderKeepAlive),
    ITEM(HttpHeaderPragma),
    ITEM(HttpHeaderTrailer),
    ITEM(HttpHeaderTransferEncoding),
    ITEM(HttpHeaderUpgrade),
    ITEM(HttpHeaderVia),
    ITEM(HttpHeaderWarning),
    ITEM(HttpHeaderAllow),
    ITEM(HttpHeaderContentLength),
    ITEM(HttpHeaderContentType),
    ITEM(HttpHeaderContentEncoding),
    ITEM(HttpHeaderContentLanguage),
    ITEM(HttpHeaderContentLocation),
    ITEM(HttpHeaderContentMd5),
    ITEM(HttpHeaderContentRange),
    ITEM(HttpHeaderExpires),
    ITEM(HttpHeaderLastModified),
    ITEM(HttpHeaderAcceptRanges),
    ITEM(HttpHeaderAge),
    ITEM(HttpHeaderEtag),
    ITEM(HttpHeaderLocation),
    ITEM(HttpHeaderProxyAuthenticate),
    ITEM(HttpHeaderRetryAfter),
    ITEM(HttpHeaderServer),
    ITEM(HttpHeaderSetCookie),
    ITEM(HttpHeaderVary),
    ITEM(HttpHeaderWwwAuthenticate),
    ITEM(HttpHeaderResponseMaximum)
};

void InitialOverlappedex(OVERLAPPEDEX* pOverlappedex)
{
    ZeroMemory(pOverlappedex,sizeof(OVERLAPPEDEX));
    pOverlappedex->HttpRequestId=HTTP_NULL_ID;
    pOverlappedex->IOType=ClientIORead;
    pOverlappedex->hFile=INVALID_HANDLE_VALUE;
}

void _parse_http_request(HTTP_REQUEST* pHttpRequest)
{
    USES_CONVERSION;
    int headerCount = sizeof(gvRequestHeaderDesc) / sizeof(HTTP_HEADER_DESC);
    CHAR buffer[MAX_PATH];
    for (int i=0;i<headerCount;i++)
    {
        int HeaderID=gvRequestHeaderDesc[i].id;
        if (pHttpRequest->Headers.KnownHeaders[HeaderID].RawValueLength!=0)
        {
            ZeroMemory(buffer,MAX_PATH);
            memcpy_s(buffer,MAX_PATH,pHttpRequest->Headers.KnownHeaders[HeaderID].pRawValue,pHttpRequest->Headers.KnownHeaders[HeaderID].RawValueLength);
            cout<<T2A(gvRequestHeaderDesc[i].desc)<<":"<<buffer<<endl;
        }
    }
}

std::wstring get_response_xml()
{
    std::wstring xml=L"<person><name>毕向阳</name></person>";
    return xml;
}

void _send_http_response_xml(HTTP_REQUEST* HttpRequest,HANDLE ReqQueueHandle, BOOL bAnsyc)
{
    HTTP_RESPONSE HttpResponse;
    //call ZeroMemory is required, or HttpSendHttpResponse returns ERROR_INVALID_PARAMETER
    ZeroMemory(&HttpResponse,sizeof(HTTP_RESPONSE));
    PCSTR Reason="OK";
    HttpResponse.StatusCode=200;
    HttpResponse.pReason=Reason;
    HttpResponse.ReasonLength=strlen(Reason);

    ADD_KNOWN_HEADER(HttpResponse,HttpHeaderContentType,"text/xml;charset=utf-8");

    std::wstring xml = get_response_xml();
    std::string strbuf = CW2AEX<256>(xml.c_str(),CP_UTF8);

    HTTP_DATA_CHUNK dataChunk[2];
    dataChunk[0].DataChunkType           = HttpDataChunkFromMemory;
    dataChunk[0].FromMemory.pBuffer      = (PVOID)strbuf.c_str();
    dataChunk[0].FromMemory.BufferLength = strbuf.length();
    //dataChunk[1].DataChunkType           = HttpDataChunkFromMemory;
    //dataChunk[1].FromMemory.pBuffer      = (PVOID)xml.c_str();
    //dataChunk[1].FromMemory.BufferLength = xml.length();

    HttpResponse.EntityChunkCount         = 1;
    HttpResponse.pEntityChunks            = dataChunk;

    PHTTP_LOG_FIELDS_DATA pLogFieldsData=NULL;
    HTTP_LOG_FIELDS_DATA LogFieldsData;
    //call ZeroMemory is required, or HttpSendHttpResponse returns ERROR_INVALID_PARAMETER
    ZeroMemory(&LogFieldsData,sizeof(HTTP_LOG_FIELDS_DATA));

    OSVERSIONINFO osversion = {0};
    osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if (GetVersionEx(&osversion))
    {
        //windows vista and later
        if (osversion.dwMajorVersion >= 6)
        {
            LogFieldsData.Base.Type=HttpLogDataTypeFields;
            LogFieldsData.ClientIp="127.0.0.1";
            LogFieldsData.ClientIpLength=strlen("127.0.0.1");
            pLogFieldsData=&LogFieldsData;
        }
    }

    if (!bAnsyc)
    {
        ULONG BytesSent;
        ULONG result = HttpSendHttpResponse(
            ReqQueueHandle,     // ReqQueueHandle
            HttpRequest->RequestId,   // Request ID
            0,       // Flags
            &HttpResponse,           // HTTP response
            NULL,                // pReserved1
            &BytesSent,          // bytes sent  (OPTIONAL)
            NULL,                // pReserved2  (must be NULL)
            0,                   // Reserved3   (must be 0)
            NULL,                // LPOVERLAPPED(OPTIONAL)
            (PHTTP_LOG_DATA)pLogFieldsData                 // pReserved4  (must be NULL)
            ); 

        if(result != NO_ERROR)
        {
            wprintf(L"HttpSendHttpResponse failed with %lu \n", result);
        }
    }
    else
    {
        OVERLAPPEDEX* pOverlapped = new OVERLAPPEDEX();
        InitialOverlappedex(pOverlapped);
        pOverlapped->IOType=ClientIOWrite;
        ULONG result = HttpSendHttpResponse(
            ReqQueueHandle,     // ReqQueueHandle
            HttpRequest->RequestId,   // Request ID
            0,       // Flags
            &HttpResponse,           // HTTP response
            NULL,      // pReserved1
            NULL,      // bytes sent  (OPTIONAL)
            NULL,      // pReserved2  (must be NULL)
            0,       // Reserved3   (must be 0)
            pOverlapped,                // LPOVERLAPPED(OPTIONAL)
            (PHTTP_LOG_DATA)pLogFieldsData       // pReserved4  (must be NULL)
            ); 

        if(result != NO_ERROR && result!=ERROR_IO_PENDING)
        {
            wprintf(L"HttpSendHttpResponse failed with %lu \n", result);
        }
    }
}
void _send_http_response_file(HTTP_REQUEST* HttpRequest,HANDLE ReqQueueHandle, BOOL bAnsyc)
{
    HTTP_RESPONSE HttpResponse;
    //call ZeroMemory is required, or HttpSendHttpResponse returns ERROR_INVALID_PARAMETER
    ZeroMemory(&HttpResponse,sizeof(HTTP_RESPONSE));
    PCSTR Reason="OK";
    HttpResponse.StatusCode=200;
    HttpResponse.pReason=Reason;
    HttpResponse.ReasonLength=strlen(Reason);

    ADD_KNOWN_HEADER(HttpResponse,HttpHeaderContentType,"text/xml;charset=utf-8");

    std::wstring xml = get_response_xml();
    std::string strbuf = CW2AEX<256>(xml.c_str(),CP_UTF8);

    HTTP_DATA_CHUNK dataChunk[2];
    dataChunk[0].DataChunkType           = HttpDataChunkFromFragmentCache;
    dataChunk[0].FromFragmentCache.pFragmentName = L"https://+:8089/Topsec";
    dataChunk[0].FromFragmentCache.FragmentNameLength = wcslen(L"https://+:8089/Topsec") * 2;
    HttpResponse.EntityChunkCount         = 1;
    HttpResponse.pEntityChunks            = dataChunk;

    OVERLAPPEDEX* pOverlapped = new OVERLAPPEDEX();
    InitialOverlappedex(pOverlapped);
    pOverlapped->IOType=ClientIOWrite;

    ULONG result = HttpSendHttpResponse(
        ReqQueueHandle,     // ReqQueueHandle
        HttpRequest->RequestId,   // Request ID
        0,       // Flags
        &HttpResponse,           // HTTP response
        NULL,      // pReserved1
        NULL,      // bytes sent  (OPTIONAL)
        NULL,      // pReserved2  (must be NULL)
        0,       // Reserved3   (must be 0)
        pOverlapped,              // LPOVERLAPPED(OPTIONAL)
        NULL       // Windows Server 2003 and Windows XP with SP2:  This parameter is reserved and must be NULL.
        //Windows Vista and Windows Server 2008:  This parameter is new for Windows Vista, and Windows Server 2008
        ); 
    if(result != NO_ERROR && result!=ERROR_IO_PENDING)
    {
        wprintf(L"HttpSendHttpResponse failed with %lu \n", result);
    }
}

void handle_http_request(HTTP_REQUEST* HttpRequest, HANDLE ReqQueueHandle)
{
    _parse_http_request(HttpRequest);
    if (_strcmpi("/topdesk",HttpRequest->pRawUrl) == 0)
    {
        _send_http_response_xml(HttpRequest, ReqQueueHandle,TRUE);
    }
    else if(_strcmpi("/topsec",HttpRequest->pRawUrl) == 0)
    {
        _send_http_response_file(HttpRequest, ReqQueueHandle,TRUE);
    }
}

unsigned int __stdcall _workthread(void *p)
{
    PTRDCONTEXT pTrdContext=(PTRDCONTEXT)p;
    DWORD NumberOfBytesTransferred;
    ULONG_PTR CompletionKey;
    LPOVERLAPPED pOverlapped;
    DWORD tid=GetCurrentThreadId();
    while (TRUE)
    {
        BOOL bOK=GetQueuedCompletionStatus(
            pTrdContext->hCompletionPort,
            &NumberOfBytesTransferred,
            &CompletionKey,
            &pOverlapped,
            5000);
        if (bOK)
        {
            if (pOverlapped!=NULL)
            {
                OVERLAPPEDEX* pOverlappedex=(OVERLAPPEDEX*)pOverlapped;
                if (pOverlappedex->IOType==ClientIORead)
                {
                    handle_http_request((HTTP_REQUEST*)pOverlappedex->buffer, pTrdContext->ReqQueueHandle);InitialOverlappedex(pOverlappedex);HttpReceiveHttpRequest(pTrdContext->ReqQueueHandle,pOverlappedex->HttpRequestId,0,(PHTTP_REQUEST)pOverlappedex->buffer,BUFFERLEN,NULL,pOverlappedex);
                }
                else
                {
                    cout<<"send response successfully"<<endl;
                    delete pOverlappedex;
                }
            }
            else
            {
                if (CompletionKey==CK_EXIT)
                {
                    cout<<"thread "<<tid<<" exit."<<endl;
                    break;
                }
            }
        }
    }
    return 0;
}

CStringW GetLoggingDirectory()
{
    WCHAR buffer[MAX_PATH];
    GetModuleFileName(NULL,buffer,MAX_PATH);
    CStringW strFilename=buffer;
    int index=strFilename.ReverseFind(_T('\\'));
    return strFilename.Left(index);
}

int _tmain(int argc, _TCHAR* argv[])
{
    HTTPAPI_VERSION HttpApiVersion=HTTPAPI_VERSION_2;
    ULONG ul;
    ul=HttpInitialize(HttpApiVersion,HTTP_INITIALIZE_CONFIG|HTTP_INITIALIZE_SERVER,NULL);
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    HTTP_SERVER_SESSION_ID ServerSessionID;
    ul=HttpCreateServerSession(HttpApiVersion,&ServerSessionID,0);
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    //enable logging
    CStringW strLogDir = GetLoggingDirectory();
    HTTP_LOGGING_INFO LogginInfo;
    ZeroMemory(&LogginInfo,sizeof(HTTP_LOGGING_INFO));
    LogginInfo.Flags.Present=1;
    LogginInfo.Format=HttpLoggingTypeW3C;
    LogginInfo.Fields=HTTP_LOG_FIELD_TIME|HTTP_LOG_FIELD_CLIENT_IP;
    LogginInfo.DirectoryName=(LPCWSTR)strLogDir;
    LogginInfo.DirectoryNameLength=strLogDir.GetLength()*2;
    LogginInfo.RolloverType=HttpLoggingRolloverDaily;
    ul=HttpSetServerSessionProperty(ServerSessionID,HttpServerLoggingProperty,&LogginInfo,sizeof(HTTP_LOGGING_INFO));
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    HTTP_URL_GROUP_ID UrlGroupID;
    ul=HttpCreateUrlGroup(ServerSessionID,&UrlGroupID,0);
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    HTTP_URL_CONTEXT UrlContext=0;
    LPCWSTR TopdeskQualifiedUrl=L"http://+:8088/Topdesk";
    ul=HttpAddUrlToUrlGroup(UrlGroupID,TopdeskQualifiedUrl,UrlContext,0);
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    LPCWSTR TopsecQualifiedUrl=L"https://+:8089/Topsec";
    ul=HttpAddUrlToUrlGroup(UrlGroupID,TopsecQualifiedUrl,UrlContext,0);
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    HANDLE ReqQueueHandle;
    ul=HttpCreateRequestQueue(HttpApiVersion,NULL,NULL,0,&ReqQueueHandle);
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    HTTP_BINDING_INFO Binding;
    Binding.Flags.Present=1;
    Binding.RequestQueueHandle=ReqQueueHandle;
    ul=HttpSetUrlGroupProperty(UrlGroupID,HttpServerBindingProperty,&Binding,sizeof(HTTP_BINDING_INFO));
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    std::wstring xml = get_response_xml();
    std::string strbuf = CW2AEX<256>(xml.c_str(),CP_UTF8);
    HTTP_DATA_CHUNK cacheChunk;
    cacheChunk.DataChunkType           = HttpDataChunkFromMemory;
    cacheChunk.FromMemory.pBuffer      = (PVOID)strbuf.c_str();
    cacheChunk.FromMemory.BufferLength = strbuf.length();
    HTTP_CACHE_POLICY CachePolicy;
    CachePolicy.Policy=HttpCachePolicyTimeToLive;
    CachePolicy.SecondsToLive=1000*60;
    ul=HttpAddFragmentToCache(ReqQueueHandle,L"https://+:8089/Topsec",&cacheChunk,&CachePolicy,NULL);
    assert(ul==NO_ERROR);
    CHECKRETUL(ul);

    HANDLE hCompletionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,CPK,0);
    HANDLE hNewCompletionPort=CreateIoCompletionPort(ReqQueueHandle,hCompletionPort,CPK,0);
    assert(hCompletionPort==hNewCompletionPort);
    HANDLE hTread[TRDCOUNT];
    for (int i=0;i<TRDCOUNT;i++)
    {
        PTRDCONTEXT trdContext = new TRDCONTEXT();
        trdContext->hCompletionPort=hCompletionPort;
        trdContext->ReqQueueHandle=ReqQueueHandle;
        hTread[i]=(HANDLE)_beginthreadex(NULL,0,_workthread,trdContext,0,NULL);
    }
    OVERLAPPEDEX* pOverlappedex=new OVERLAPPEDEX[TRDCOUNT];
    for (int i=0;i<TRDCOUNT;i++)
    {
        InitialOverlappedex(pOverlappedex+i);
        ul=HttpReceiveHttpRequest(
            ReqQueueHandle,
            pOverlappedex->HttpRequestId,
            0, //Only the request headers are retrieved; the entity body is not copied.
            (PHTTP_REQUEST)pOverlappedex->buffer,
            BUFFERLEN,
            NULL,
            pOverlappedex);
        assert(ul==NO_ERROR || ul==ERROR_IO_PENDING);
    }

    cout<<"Press any key to exit."<<endl;
    _getch();

    for(int i=0;i<TRDCOUNT;i++)
    {
        PostQueuedCompletionStatus(hCompletionPort,0,CK_EXIT,0);
    }

    DWORD dwRet=WaitForMultipleObjects(TRDCOUNT,hTread,TRUE,INFINITE);
    cout<<"WaitForMultipleObjects returned, code = "<<dwRet<<endl;

    HttpRemoveUrlFromUrlGroup(UrlGroupID,TopdeskQualifiedUrl,0);
    HttpRemoveUrlFromUrlGroup(UrlGroupID,TopsecQualifiedUrl,0);
    //or HttpRemoveUrlFromUrlGroup(UrlGroupID,NULL,HTTP_URL_FLAG_REMOVE_ALL);

    HttpCloseUrlGroup(UrlGroupID);
    HttpCloseServerSession(ServerSessionID);
    HttpCloseRequestQueue(ReqQueueHandle);
    HttpTerminate(HTTP_INITIALIZE_CONFIG|HTTP_INITIALIZE_SERVER,NULL);

    return 0;
}