Skip to content

Instantly share code, notes, and snippets.

@imba-tjd
Last active November 3, 2024 14:00
Show Gist options
  • Save imba-tjd/523b377a0d36d40ae5109826e9eb9a0f to your computer and use it in GitHub Desktop.
Save imba-tjd/523b377a0d36d40ae5109826e9eb9a0f to your computer and use it in GitHub Desktop.
Windows httpapi server example.
// https://learn.microsoft.com/zh-cn/windows/win32/Http
// gcc server.c -Os -Wall -lhttpapi -o server
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <http.h>
#include <windows.h>
#include <signal.h>
static HANDLE hReqQueue;
static HTTP_SERVER_SESSION_ID serverSessionId;
static HTTP_URL_GROUP_ID urlGroupId;
static void cleanup(int) {
HttpCloseRequestQueue(hReqQueue);
hReqQueue = NULL; // notify receive loop
HttpCloseUrlGroup(urlGroupId);
HttpCloseServerSession(serverSessionId);
HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
puts("Server terminated");
}
int main() {
ULONG ret;
// Initialize HTTP Server API
ret = HttpInitialize((HTTPAPI_VERSION)HTTPAPI_VERSION_2, HTTP_INITIALIZE_SERVER, NULL);
if (ret != NO_ERROR) {
printf("HttpInitialize failed with %lu\n", ret);
return ret;
}
// Create HTTP Server Session
ret = HttpCreateServerSession((HTTPAPI_VERSION)HTTPAPI_VERSION_2, &serverSessionId, 0);
if (ret != NO_ERROR) {
printf("HttpCreateServerSession failed with %lu\n", ret);
return ret;
}
// Create URL Group
ret = HttpCreateUrlGroup(serverSessionId, &urlGroupId, 0);
if (ret != NO_ERROR) {
printf("HttpCreateUrlGroup failed with %lu\n", ret);
return ret;
}
// Create Request Queue
ret = HttpCreateRequestQueue((HTTPAPI_VERSION)HTTPAPI_VERSION_2, NULL, NULL, 0, &hReqQueue);
if (ret != NO_ERROR) {
printf("HttpCreateRequestQueue failed with %lu\n", ret);
return ret;
}
// Bind URL Group to Request Queue
HTTP_BINDING_INFO bindingInfo = {
.Flags.Present = 1,
.RequestQueueHandle = hReqQueue
};
ret = HttpSetUrlGroupProperty(urlGroupId, HttpServerBindingProperty, &bindingInfo, sizeof(bindingInfo));
if (ret != NO_ERROR) {
printf("HttpSetUrlGroupProperty failed with %lu\n", ret);
return ret;
}
// Set URL
ret = HttpAddUrlToUrlGroup(urlGroupId, L"http://localhost:8080/", 0, 0);
if (ret != NO_ERROR) {
printf("HttpAddUrlToUrlGroup failed with %lu\n", ret);
return ret;
}
signal(SIGINT, cleanup);
puts("Server started on http://localhost:8080");
// Main loop to handle requests
while (1) {
if (!hReqQueue) return 0; // cleaned in SIGINT
// Initialize request structure
#define requestBufferLength (sizeof(HTTP_REQUEST) + 1024)
char requestBuffer[requestBufferLength] = {0};
HTTP_REQUEST *request = (HTTP_REQUEST*) requestBuffer;
// Receive a request
DWORD bytesRead;
ret = HttpReceiveHttpRequest(hReqQueue, HTTP_NULL_ID, 0, request, requestBufferLength, &bytesRead, NULL);
if (ret != NO_ERROR) {
printf("HttpReceiveHttpRequest failed with %lu\n", ret);
continue;
}
// Log remote addr
PSOCKADDR_IN r = (PSOCKADDR_IN)request->Address.pRemoteAddress;
#define SIN_ADDR_B(s) (s->sin_addr.S_un.S_un_b)
printf("Received from %hhu.%hhu.%hhu.%hhu:%hu\n",
SIN_ADDR_B(r).s_b1, SIN_ADDR_B(r).s_b2, SIN_ADDR_B(r).s_b3, SIN_ADDR_B(r).s_b4, r->sin_port);
// Initialize the response
HTTP_RESPONSE response = {
.StatusCode = 200,
.pReason = "OK",
};
response.ReasonLength = (USHORT)strlen(response.pReason);
response.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = "text/plain";
response.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen(response.Headers.KnownHeaders[HttpHeaderContentType].pRawValue);
// Response body
const char* responseBody = "Hello, World!";
HTTP_DATA_CHUNK dataChunk = {
.DataChunkType = HttpDataChunkFromMemory,
.FromMemory = {
.pBuffer = (PVOID)responseBody,
.BufferLength = (ULONG)strlen(responseBody)
}
};
response.EntityChunkCount = 1;
response.pEntityChunks = &dataChunk;
// Send the response
DWORD bytesSent;
ret = HttpSendHttpResponse(hReqQueue, request->RequestId, HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA, &response, NULL, &bytesSent, NULL, 0, NULL, NULL);
if (ret != NO_ERROR) {
printf("HttpSendHttpResponse failed with %lu\n", ret);
}
}
}
@imba-tjd
Copy link
Author

imba-tjd commented Nov 3, 2024

Unfinished pyx binding with some notes:

HTTP_REQUEST_V2“继承”了HTTP_REQUEST_V1。Vista后HTTP_REQUEST就是V2

# distutils: libraries=httpapi
# distutils: define_macros=WIN32_LEAN_AND_MEAN

# TODO:HTTP_SERVER_SESSION_ID定义错了,是ULONGLONG,不是struct

cdef extern from "<windows.h>":
    ctypedef unsigned short USHORT
    ctypedef unsigned long ULONG
    ctypedef unsigned long long ULONGLONG
    ctypedef void* PVOID
    ctypedef PVOID HANDLE
    ctypedef HANDLE* PHANDLE
    ctypedef Py_UNICODE WCHAR
    ctypedef const WCHAR* PCWSTR


cdef extern from "<http.h>":
    ctypedef struct HTTPAPI_VERSION:
        USHORT HttpApiMajorVersion
        USHORT HttpApiMinorVersion
    cdef HTTPAPI_VERSION HTTPAPI_VERSION_2

    ctypedef enum HTTP_SERVER_PROPERTY: # 一种多态,本enum指定选项类型,用给 队列、会话、URL组 设置属性
        HttpServerBindingProperty # HTTP_BINDING_INFO 仅用于把队列绑给URL组
        HttpServerTimeoutsProperty # HTTP_TIMEOUT_LIMIT_INFO 多个部分的超时配置,默认120秒。用于会话和URL组
        HttpServerLoggingProperty # HTTP_LOGGING_INFO 默认无日志。用于会话和URL组
        HttpServerQueueLengthProperty # ULONG 默认100,用于队列
        HttpServer503VerbosityProperty # HTTP_503_RESPONSE_VERBOSITY 队列满了应该怎么响应。默认重置,可选发送503后关闭,可选包含详细信息


    cdef ULONG HTTP_INITIALIZE_SERVER # Flags
    cdef ULONG NO_ERROR, ERROR_INVALID_PARAMETER # 返回值
    ULONG HttpInitialize(
        HTTPAPI_VERSION Version,
        ULONG Flags,
        PVOID pReserved # NULL
    )
    ULONG HttpTerminate( # 必须传与init相同的参数
        ULONG Flags,
        PVOID pReserved
    )


    ctypedef struct HTTP_SERVER_SESSION_ID: pass
    ctypedef HTTP_SERVER_SESSION_ID* PHTTP_SERVER_SESSION_ID
    ULONG HttpCreateServerSession(
        HTTPAPI_VERSION Version,
        PHTTP_SERVER_SESSION_ID ServerSessionId, # out
        ULONG Reserved # 0
    )
    ULONG HttpCloseServerSession(
        HTTP_SERVER_SESSION_ID ServerSessionId
    )

    ULONG HttpSetServerSessionProperty(
        HTTP_SERVER_SESSION_ID ServerSessionId,
        HTTP_SERVER_PROPERTY Property,
        PVOID PropertyInformation,
        ULONG PropertyInformationLength
    )


    ctypedef struct HTTP_URL_GROUP_ID: pass
    ctypedef HTTP_URL_GROUP_ID* PHTTP_URL_GROUP_ID
    ULONG HttpCreateUrlGroup(
        HTTP_SERVER_SESSION_ID ServerSessionId,
        PHTTP_URL_GROUP_ID pUrlGroupId, # out
        ULONG Reserved
    )
    ULONG HttpCloseUrlGroup( # 会自动删除与其关联的URL,不用手动HttpRemoveUrlFromUrlGroup
        HTTP_URL_GROUP_ID UrlGroupId
    )

    ctypedef ULONGLONG HTTP_URL_CONTEXT
    ULONG HttpAddUrlToUrlGroup(
        HTTP_URL_GROUP_ID UrlGroupId,
        PCWSTR pFullyQualifiedUrl,
        HTTP_URL_CONTEXT UrlContext, # optional,允许用户自定义的一个数字,之后每次HTTP_REQUEST收到时会加上
        ULONG Reserved
    )

    ctypedef struct HTTP_PROPERTY_FLAGS:
        bint Present
        bint _[31]
    ctypedef struct HTTP_BINDING_INFO: # 将队列绑定到URL组里。设计上二者独立,但实际都只会创建一个
        HTTP_PROPERTY_FLAGS Flags
        HANDLE RequestQueueHandle
    ULONG HttpSetUrlGroupProperty(
        HTTP_URL_GROUP_ID UrlGroupId,
        HTTP_SERVER_PROPERTY Property, # 要设置的属性类型
        PVOID PropertyInformation, # 属性的值
        ULONG PropertyInformationLength # sizeof(值)
    )


    ctypedef PVOID PSECURITY_ATTRIBUTES
    ULONG HttpCreateRequestQueue(
        HTTPAPI_VERSION Version,
        PCWSTR Name, # optional,用于其它进程按名称访问Q
        PSECURITY_ATTRIBUTES SecurityAttributes, # optional,不考虑使用
        ULONG Flags, # optional,用于按名称打开而非创建
        PHANDLE RequestQueueHandle # out
    )
    ULONG HttpCloseRequestQueue(
        HANDLE RequestQueueHandle
    )

    ULONG HttpSetRequestQueueProperty(
        HANDLE RequestQueueHandle,
        HTTP_SERVER_PROPERTY Property,
        PVOID PropertyInformation,
        ULONG PropertyInformationLength,
        ULONG Reserved1,
        PVOID Reserved2
    )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment