zoukankan      html  css  js  c++  java
  • WinHttp编写HTTP服务器示例代码

    这是微软提供的示例程序,原文地址在此https://msdn.microsoft.com/en-us/library/windows/desktop/aa364640(v=vs.85).aspx

    HTTP Server示例程序

    以下示例应用程序展示如何使用HTTP Server API处理HTTP请求任务。第一个示例中包含的precomp.h文件包含示例所需的所有头文件,如下:

    #ifndef UNICODE
    #define UNICODE
    #endif
    
    #ifndef _WIN32_WINNT
    #define _WIN32_WINNT 0x0600
    #endif
    
    #ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
    #endif
    
    #include <windows.h>
    #include <http.h>
    #include <stdio.h>
    
    #pragma comment(lib, "httpapi.lib")
    

    Main and Preliminaries(main和准备工作)

    #include "precomp.h"
    
    //
    // Macros.初始化HTTP响应体宏
    //
    #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.原型
    //
    DWORD DoReceiveRequests(HANDLE hReqQueue);
    
    DWORD
    SendHttpResponse(
        IN HANDLE        hReqQueue,
        IN PHTTP_REQUEST pRequest,
        IN USHORT        StatusCode,
        IN PSTR          pReason,
        IN PSTR          pEntity
        );
    
    DWORD
    SendHttpPostResponse(
        IN HANDLE        hReqQueue,
        IN PHTTP_REQUEST pRequest
        );
    
    
    /*******************************************************************++
    
    函数说明:
        main函数
    
    参数:
        argc - 命令行参数个数.
        argv - 命令行参数.
    
    返回值:
        Success/Failure
    
    --*******************************************************************/
    int __cdecl wmain(
            int argc, 
            wchar_t * argv[]
            )
    {
        ULONG           retCode;
        HANDLE          hReqQueue      = NULL;
        int             UrlAdded       = 0;
        HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
        
        if (argc < 2)
        {
            wprintf(L"%ws: <Url1> [Url2] ... 
    ", argv[0]);
            return -1;
        }
    

    初始化HTTP Service

        //
        // 初始化HTTP Server APIs
        //
        retCode = HttpInitialize( 
                    HttpApiVersion,
                    HTTP_INITIALIZE_SERVER,    // Flags
                    NULL                       // Reserved
                    );
    
        if (retCode != NO_ERROR)
        {
            wprintf(L"HttpInitialize failed with %lu 
    ", retCode);
            return retCode;
        }
    
        //
        // 创建请求队列句柄
        //
        retCode = HttpCreateHttpHandle(
                    &hReqQueue,        // Req Queue
                    0                  // Reserved
                    );
    
        if (retCode != NO_ERROR)
        {    
            wprintf(L"HttpCreateHttpHandle failed with %lu 
    ", retCode);
            goto CleanUp;
        }
    

    注册URLs进行监听

        //
        // 命令行参数指定要监听的URI。为每个URI调用HttpAddUrl。
        //
        // URI是一个完全合格的URI,必须包含终止字符(/)
        //
        for (int i = 1; i < argc; i++)
        {
            wprintf(L"listening for requests on the following url: %s
    ", argv[i]);
    
            retCode = HttpAddUrl(
                        hReqQueue,    // Req Queue
                        argv[i],      // Fully qualified URL
                        NULL          // Reserved
                        );
    
            if (retCode != NO_ERROR)
            {
                wprintf(L"HttpAddUrl failed with %lu 
    ", retCode);
                goto CleanUp;
            }
            else
            {
                //
                // Track the currently added URLs.
                //
                UrlAdded ++;
            }
        }
    

    调用程序以接收请求

        DoReceiveRequests(hReqQueue);
    

    清理HTTP Server API

    CleanUp:
    
        //
        // 对所有添加的URI调用HttpRemoveUrl.
        //
        for(int i=1; i<=UrlAdded; i++)
        {
            HttpRemoveUrl(
                  hReqQueue,     // Req Queue
                  argv[i]        // Fully qualified URL
                  );
        }
    
        //
        // 关闭请求队列句柄.
        //
        if(hReqQueue)
        {
            CloseHandle(hReqQueue);
        }
    
        // 
        // 调用HttpTerminate.
        //
        HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
    
        return retCode;
    

    接收请求

    /*******************************************************************++
    
    函数说明:
        他的功能是接收一个请求。
    	该函数调用相应的函数来处理响应。
    
    参数:
        hReqQueue - 请求队列句柄
    
    返回值:
        Success/Failure.
    
    --*******************************************************************/
    DWORD DoReceiveRequests(
        IN HANDLE hReqQueue
        )
    {
        ULONG              result;
        HTTP_REQUEST_ID    requestId;
        DWORD              bytesRead;
        PHTTP_REQUEST      pRequest;
        PCHAR              pRequestBuffer;
        ULONG              RequestBufferLength;
    
        //
        // 分配一个2 KB缓冲区。 这个大小应该适用于大多数请求。 如果需要,
    	// 可以增加缓冲区大小。HTTP_REQUEST结构也需要空间。
        //
        RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
        pRequestBuffer      = (PCHAR) ALLOC_MEM( RequestBufferLength );
    
        if (pRequestBuffer == NULL)
        {
            return ERROR_NOT_ENOUGH_MEMORY;
        }
    
        pRequest = (PHTTP_REQUEST)pRequestBuffer;
    
        //
        // 等待一个新请求. 标记为一个NULL请求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
                        );
    

    处理HTTP请求

            if(NO_ERROR == result)
            {
                //
                // Worked! 
                // 
                switch(pRequest->Verb)
                {
    			    /* GET 请求处理 */
                    case HttpVerbGET:
                        wprintf(L"Got a GET request for %ws 
    ", 
                                pRequest->CookedUrl.pFullUrl);
    
                        result = SendHttpResponse(
                                    hReqQueue, 
                                    pRequest, 
                                    200,
                                    "OK",
                                    "Hey! You hit the server 
    "
                                    );
                        break;
    
    			    /* POST 请求处理 */
                    case HttpVerbPOST:
    
                        wprintf(L"Got a POST request for %ws 
    ", 
                                pRequest->CookedUrl.pFullUrl);
    
                        result= SendHttpPostResponse(hReqQueue, pRequest);
                        break;
    
                    default:
                        wprintf(L"Got a unknown request for %ws 
    ", 
                                pRequest->CookedUrl.pFullUrl);
    
                        result = SendHttpResponse(
                                    hReqQueue, 
                                    pRequest,
                                    503,
                                    "Not Implemented",
                                    NULL
                                    );
                        break;
                }
    
                if(result != NO_ERROR)
                {
                    break;
                }
    
                //
                // 重置请求ID用于处理下一个请求.
                //
                HTTP_SET_NULL_ID( &requestId );
            }
            else if(result == ERROR_MORE_DATA)
            {
                //
                // 输入缓冲区太小,无法容纳请求标头。增加缓冲区大小,再次调用API。
                //
                // 再次调用API时,通过传递RequestID来处理失败的请求。
                //
                // 该RequestID从旧缓冲区读取。
                //
                requestId = pRequest->RequestId;
    
                //
                // 释放旧的缓冲区并分配一个新的缓冲区。
                //
                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))
            {
                // 当尝试使用更多缓冲区来处理请求时,TCP连接被对方破坏
                // 继续下一个请求。
                
                HTTP_SET_NULL_ID( &requestId );
            }
            else
            {
                break;
            }
    
        }
    
        if(pRequestBuffer)
        {
            FREE_MEM( pRequestBuffer );
        }
    
        return result;
    }
    

    发送一个HTTP响应

    /*******************************************************************++
    
    函数说明:
        这个函数用于发送一个HTTP响应
    
    参数:
        hReqQueue     - 请求队列句柄
        pRequest      - 解析出的HTTP请求
        StatusCode    - Response状态码
        pReason       - Response原因短语
        pEntityString - Response实体主体
    
    返回值:
        Success/Failure.
    --*******************************************************************/
    
    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;
    
        //
        // 初始化HTTP response结构体
        //
        INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason);
    
        //
        // 添加一个known header.
        //
        ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html");
       
        if(pEntityString)
        {
            // 
            // 添加一个entity chunk.
            //
            dataChunk.DataChunkType           = HttpDataChunkFromMemory;
            dataChunk.FromMemory.pBuffer      = pEntityString;
            dataChunk.FromMemory.BufferLength = 
                                           (ULONG) strlen(pEntityString);
    
            response.EntityChunkCount         = 1;
            response.pEntityChunks            = &dataChunk;
        }
    
        // 
        // 因为entity body在一个调用中发送,所以不需要指定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)
        {
            wprintf(L"HttpSendHttpResponse failed with %lu 
    ", result);
        }
    
        return result;
    }
    

    发送一个HTTP POST响应

    #define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
    
    /*******************************************************************++
    
    函数说明:
        这个函数在读取entity body后发送HTTP响应
    
    参数:
        hReqQueue     - 请求队列句柄
        pRequest      - 解析出的HTTP request.
    
    返回值:
        Success/Failure.
    --*******************************************************************/
    
    DWORD SendHttpPostResponse(
        IN HANDLE        hReqQueue,
        IN PHTTP_REQUEST pRequest
        )
    {
        HTTP_RESPONSE   response;
        DWORD           result;
        DWORD           bytesSent;
        PUCHAR          pEntityBuffer;
        ULONG           EntityBufferLength;
        ULONG           BytesRead;
        ULONG           TempFileBytesWritten;
        HANDLE          hTempFile;
        TCHAR           szTempName[MAX_PATH + 1];
        CHAR            szContentLength[MAX_ULONG_STR];
        HTTP_DATA_CHUNK dataChunk;
        ULONG           TotalBytesRead = 0;
    
        BytesRead  = 0;
        hTempFile  = INVALID_HANDLE_VALUE;
    
        //
        // 为实体缓冲区分配空间。 缓冲区可按需增加。
        //
        EntityBufferLength = 2048;
        pEntityBuffer      = (PUCHAR) ALLOC_MEM( EntityBufferLength );
    
        if (pEntityBuffer == NULL)
        {
            result = ERROR_NOT_ENOUGH_MEMORY;
            wprintf(L"Insufficient resources 
    ");
            goto Done;
        }
    
        //
        // 初始化HTTP response结构体.
        //
        INITIALIZE_HTTP_RESPONSE(&response, 200, "OK");
    
        //
        // 对于POST,从客户端回显实体
        //
        // 注意: 如果HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY标识通过HttpReceiveHttpRequest()
    	//       传递,则entity将是HTTP_REQUEST的一部分(使用pEntityChunks字段).因为此标识
    	//       未被传递,则entity不在HTTP_REQUEST中.
        //
       
        if(pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
        {
            // 实体主体通过多个调用发送. 收集这些在一个文件并回发.创建一个临时文件
            //
    
            if(GetTempFileName(
                    L".", 
                    L"New", 
                    0, 
                    szTempName
                    ) == 0)
            {
                result = GetLastError();
                wprintf(L"GetTempFileName failed with %lu 
    ", result);
                goto Done;
            }
    
            hTempFile = CreateFile(
                            szTempName,
                            GENERIC_READ | GENERIC_WRITE, 
                            0,                  // Do not share.
                            NULL,               // No security descriptor.
                            CREATE_ALWAYS,      // Overrwrite existing.
                            FILE_ATTRIBUTE_NORMAL,    // Normal file.
                            NULL
                            );
    
            if(hTempFile == INVALID_HANDLE_VALUE)
            {
                result = GetLastError();
                wprintf(L"Cannot create temporary file. Error %lu 
    ",
                         result);
                goto Done;
            }
    
            do
            {
                //
                // 从请求中读取entity chunk.
                //
                BytesRead = 0; 
                result = HttpReceiveRequestEntityBody(
                            hReqQueue,
                            pRequest->RequestId,
                            0,
                            pEntityBuffer,
                            EntityBufferLength,
                            &BytesRead,
                            NULL 
                            );
    
                switch(result)
                {
                    case NO_ERROR:
    
                        if(BytesRead != 0)
                        {
                            TotalBytesRead += BytesRead;
                            WriteFile(
                                    hTempFile, 
                                    pEntityBuffer, 
                                    BytesRead,
                                    &TempFileBytesWritten,
                                    NULL
                                    );
                        }
                        break;
    
                    case ERROR_HANDLE_EOF:
    
                        //
                        // The last request entity body has been read.
                        // Send back a response. 
                        //
                        // To illustrate entity sends via 
                        // HttpSendResponseEntityBody, the response will 
                        // be sent over multiple calls. To do this,
                        // pass the HTTP_SEND_RESPONSE_FLAG_MORE_DATA
                        // flag.
                        
                        if(BytesRead != 0)
                        {
                            TotalBytesRead += BytesRead;
                            WriteFile(
                                    hTempFile, 
                                    pEntityBuffer, 
                                    BytesRead,
                                    &TempFileBytesWritten,
                                    NULL
                                    );
                        }
    
                        //
                        // Because the response is sent over multiple
                        // API calls, add a content-length.
                        //
                        // Alternatively, the response could have been
                        // sent using chunked transfer encoding, by  
                        // passimg "Transfer-Encoding: Chunked".
                        //
    
                        // NOTE: Because the TotalBytesread in a ULONG
                        //       are accumulated, this will not work
                        //       for entity bodies larger than 4 GB. 
                        //       For support of large entity bodies,
                        //       use a ULONGLONG.
                        // 
    
                      
                        sprintf_s(szContentLength, MAX_ULONG_STR, "%lu", TotalBytesRead);
    
                        ADD_KNOWN_HEADER(
                                response, 
                                HttpHeaderContentLength, 
                                szContentLength
                                );
    
                        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)
                        {
                            wprintf(
                               L"HttpSendHttpResponse failed with %lu 
    ", 
                               result
                               );
                            goto Done;
                        }
    
                        //
                        // Send entity body from a file handle.
                        //
                        dataChunk.DataChunkType = 
                            HttpDataChunkFromFileHandle;
    
                        dataChunk.FromFileHandle.
                            ByteRange.StartingOffset.QuadPart = 0;
    
                        dataChunk.FromFileHandle.
                            ByteRange.Length.QuadPart = 
                                              HTTP_BYTE_RANGE_TO_EOF;
    
                        dataChunk.FromFileHandle.FileHandle = hTempFile;
    
                        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)
                        {
                           wprintf(
                              L"HttpSendResponseEntityBody failed %lu
    ", 
                              result
                              );
                        }
    
                        goto Done;
    
                        break;
                           
    
                    default:
                      wprintf( 
                       L"HttpReceiveRequestEntityBody failed with %lu 
    ", 
                       result);
                      goto Done;
                }
    
            } while(TRUE);
        }
        else
        {
            // 此请求没有实体主体。
            //
            
            result = HttpSendHttpResponse(
                       hReqQueue,           // ReqQueueHandle
                       pRequest->RequestId, // Request ID
                       0,
                       &response,           // HTTP response
                       NULL,                // pReserved1
                       &bytesSent,          // bytes sent (optional)
                       NULL,                // pReserved2
                       0,                   // Reserved3
                       NULL,                // LPOVERLAPPED
                       NULL                 // pReserved4
                       );
            if(result != NO_ERROR)
            {
                wprintf(L"HttpSendHttpResponse failed with %lu 
    ",
                        result);
            }
        }
    
    Done:
    
        if(pEntityBuffer)
        {
            FREE_MEM(pEntityBuffer);
        }
    
        if(INVALID_HANDLE_VALUE != hTempFile)
        {
            CloseHandle(hTempFile);
            DeleteFile(szTempName);
        }
    
        return result;
    }
    
  • 相关阅读:
    01 React快速入门(一)——使用循环时对于‘key’报错处理
    01 div实现浮动效果
    17 制作热力图
    16 ArcGIS Server 10.6.1发布影像服务
    虚拟机上有关于Apache服务基于主机名@4域名访问网页
    虚拟机上有关于Apache服务基于IP地址@3IP访问网站
    Apache有关个人用户主页以及强制访问安全系统功能介绍@2
    Apache服务的安装以及服务文件参数内容的配置 @1
    WVware虚拟机linux环境下使用ssh服务以安全密钥的形式远程控制服务(本地客户端登录远程服务端)
    WVware虚拟机linux环境下RAID5 五块磁盘操作管理实例
  • 原文地址:https://www.cnblogs.com/oloroso/p/7684672.html
Copyright © 2011-2022 走看看