这是微软提供的示例程序,原文地址在此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] ... \n", 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 \n", retCode);
return retCode;
} //
// 创建请求队列句柄
//
retCode = HttpCreateHttpHandle(
&hReqQueue, // Req Queue
0 // Reserved
); if (retCode != NO_ERROR)
{
wprintf(L"HttpCreateHttpHandle failed with %lu \n", 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\n", argv[i]); retCode = HttpAddUrl(
hReqQueue, // Req Queue
argv[i], // Fully qualified URL
NULL // Reserved
); if (retCode != NO_ERROR)
{
wprintf(L"HttpAddUrl failed with %lu \n", 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 \n",
pRequest->CookedUrl.pFullUrl); result = SendHttpResponse(
hReqQueue,
pRequest,
200,
"OK",
"Hey! You hit the server \r\n"
);
break; /* POST 请求处理 */
case HttpVerbPOST: wprintf(L"Got a POST request for %ws \n",
pRequest->CookedUrl.pFullUrl); result= SendHttpPostResponse(hReqQueue, pRequest);
break; default:
wprintf(L"Got a unknown request for %ws \n",
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 \n", 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 \n");
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 \n", 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 \n",
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 \n",
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\n",
result
);
} goto Done; break; default:
wprintf(
L"HttpReceiveRequestEntityBody failed with %lu \n",
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 \n",
result);
}
} Done: if(pEntityBuffer)
{
FREE_MEM(pEntityBuffer);
} if(INVALID_HANDLE_VALUE != hTempFile)
{
CloseHandle(hTempFile);
DeleteFile(szTempName);
} return result;
}

WinHttp编写HTTP服务器示例代码的更多相关文章

  1. 程序员笔记|如何编写高性能的Java代码

    一.并发 Unable to create new native thread …… 问题1:Java中创建一个线程消耗多少内存? 每个线程有独自的栈内存,共享堆内存 问题2:一台机器可以创建多少线程 ...

  2. WebRTC 音频采样算法 附完整C++示例代码

    之前有大概介绍了音频采样相关的思路,详情见<简洁明了的插值音频重采样算法例子 (附完整C代码)>. 音频方面的开源项目很多很多. 最知名的莫过于谷歌开源的WebRTC, 其中的音频模块就包 ...

  3. 推荐Java五大微服务器及其代码示例教程

    来源素文宅博客:http://blog.yoodb.com/yoodb/article/detail/1339 微服务越来越多地用于开发领域,因为开发人员致力于创建更大,更复杂的应用程序,这些应用程序 ...

  4. python开源项目及示例代码

    本页面是俺收集的各种 Python 资源,不定期更新. 下面列出的各种 Python 库/模块/工具,如果名称带超链接,说明是第三方的:否则是 Python 语言内置的. 1 算法 1.1 字符串处理 ...

  5. C/C++ 开源库及示例代码

    C/C++ 开源库及示例代码 Table of Contents 说明 1 综合性的库 2 数据结构 & 算法 2.1 容器 2.1.1 标准容器 2.1.2 Lockfree 的容器 2.1 ...

  6. iOS App集成Apple Pay教程(附示例代码)

    苹果在本周一发布了iOS 8.1版本,并正式开放了Apple Pay支付系统.Apple Pay是一个基于NFC的支付系统,不久将被数以万计的线下零售商店予以支持.即便这项科技并不是彻底的突破性进展, ...

  7. 实战WEB 服务器(JAVA编写WEB服务器)

    实战WEB 服务器(JAVA编写WEB服务器) 标签: web服务服务器javawebsockethttp服务器 2010-04-21 17:09 11631人阅读 评论(24) 收藏 举报  分类: ...

  8. python开源项目及示例代码(转)

    本页面是俺收集的各种 Python 资源,不定期更新. 下面列出的各种 Python 库/模块/工具,如果名称带超链接,说明是第三方的:否则是 Python 语言内置的. 1 算法 1.1 字符串处理 ...

  9. 如何在Android上编写高效的Java代码

    转自:http://www.ituring.com.cn/article/177180 作者/ Erik Hellman Factor10咨询公司资深移动开发顾问,曾任索尼公司Android团队首席架 ...

随机推荐

  1. ASP.NET Core开发-读取配置文件Configuration appsettings.json

    https://www.cnblogs.com/linezero/p/Configuration.html ASP.NET Core 是如何读取配置文件,今天我们来学习. ASP.NET Core的配 ...

  2. RateLimiter

    RateLimiter是Guava的concurrent包下的一个用于限制访问频率的类. 限流算法 常用的更平滑的限流算法有两种:漏桶算法和令牌桶算法. 很多传统的服务提供商如华为中兴都有类似的专利, ...

  3. The Art of Deception

    前言 一些黑客毁坏别人的文件甚至整个硬盘,他们被称为电脑狂人(crackers)或计算机破坏者(vandals).另一些新手省去学习技术的麻烦,直接下载黑客工具侵入别人的计算机,这些人被称为脚本小子( ...

  4. [leetcode]Valid Palindrome @ Python

    原题地址:https://oj.leetcode.com/problems/valid-palindrome/ 题意: Given a string, determine if it is a pal ...

  5. 如何修改Windows上某块网卡的MTU的值

    先用如下命令查看所有的网卡以及他们的MTU的值. netsh interface ipv4 show interfaces 使用如下的命令修改他们的MTU为9000.        netsh int ...

  6. Everything 使用技巧 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  7. Openstack中为虚拟机使用CDROM光驱设备

    在Libvirt里处理 在nova里处理 实际效果 怎么卸载 在Libvirt里处理 尝试了下面有几种方法,为虚拟机载入光盘文件: 1.使用ide方式挂载: virsh attach-disk {in ...

  8. 机器学习中的损失函数 (着重比较:hinge loss vs softmax loss)

    https://blog.csdn.net/u010976453/article/details/78488279 1. 损失函数 损失函数(Loss function)是用来估量你模型的预测值 f( ...

  9. Python防止sql注入

    看了网上文章,说的都挺好的,给cursor.execute传递格式串和参数,就能防止注入,但是我写了代码,却死活跑不通,怀疑自己用了一个假的python 最后,发现原因可能是不同的数据库,对于字符串的 ...

  10. moodle安装体验

    EasyPHP-5.3.8.0+moodle 2.6.2 安装过程要耐心等待. 成功安装后.网页又訪问不了. 不知道为什么? 2015/04/12 EasyPHP-DevServer-14.1VC11 ...