Files
handleaiuinew/env/LMI/include/kApi/Io/kHttpServer.cpp
T
2024-05-18 23:53:26 +08:00

315 lines
8.2 KiB
C++

/**
* @file kHttpServer.cpp
*
* @internal
* Copyright (C) 2013-2021 by LMI Technologies Inc.
* Licensed under the MIT License.
* Redistributed files must retain the above copyright notice.
*/
#include <kApi/Io/kHttpServer.h>
#include <kApi/Data/kList.h>
#include <kApi/Io/kHttpServerChannel.h>
#include <kApi/Io/kTcpServer.h>
#include <kApi/Threads/kThread.h>
kBeginClassEx(k, kHttpServer)
kAddPrivateVMethod(kHttpServer, kObject, VRelease)
kEndClassEx()
kFx(kStatus) kHttpServer_Construct(kHttpServer* server, kAlloc allocator)
{
kAlloc alloc = kAlloc_Fallback(allocator);
kType type = kTypeOf(kHttpServer);
kStatus status;
kCheck(kAlloc_GetObject(alloc, type, server));
if (!kSuccess(status = xkHttpServer_Init(*server, type, alloc)))
{
kAlloc_FreeRef(alloc, server);
}
return status;
}
kFx(kStatus) xkHttpServer_Init(kHttpServer server, kType type, kAlloc allocator)
{
kObjR(kHttpServer, server);
kCheck(kObject_Init(server, type, allocator));
obj->localEndPoint.address = kIpAddress_AnyV4();
obj->localEndPoint.port = xkHTTP_SERVER_DEFAULT_PORT;
obj->channelCapacity = xkHTTP_SERVER_DEFAULT_CHANNEL_CAPACITY;
obj->socketWriteBufferSize = xkHTTP_SERVER_DEFAULT_SOCKET_WRITE_BUFFER;
obj->clientWriteBufferSize = xkHTTP_SERVER_DEFAULT_CLIENT_WRITE_BUFFER;
obj->socketReadBufferSize = xkHTTP_SERVER_DEFAULT_SOCKET_READ_BUFFER;
obj->clientReadBufferSize = xkHTTP_SERVER_DEFAULT_CLIENT_READ_BUFFER;
obj->handler.function = kNULL;
obj->handler.receiver = kNULL;
obj->listener = kNULL;
obj->thread = kNULL;
obj->shouldQuit = 0;
obj->channels = kNULL;
obj->backlog = xkHTTP_SERVER_DEFAULT_LISTEN_BACKLOG;
return kOK;
}
kFx(kStatus) xkHttpServer_VRelease(kHttpServer server)
{
kHttpServer_Stop(server);
kCheck(kObject_VRelease(server));
return kOK;
}
kFx(kStatus) kHttpServer_SetAddress(kHttpServer server, kIpAddress address)
{
kObj(kHttpServer, server);
kCheckState(kIsNull(obj->thread));
obj->localEndPoint.address = address;
return kOK;
}
kFx(kStatus) kHttpServer_SetPort(kHttpServer server, k32u port)
{
kObj(kHttpServer, server);
kCheckState(kIsNull(obj->thread));
obj->localEndPoint.port = port;
return kOK;
}
kFx(kStatus) kHttpServer_SetMaxConnections(kHttpServer server, kSize capacity)
{
kObj(kHttpServer, server);
kCheckState(kIsNull(obj->thread));
obj->channelCapacity = capacity;
return kOK;
}
kFx(kStatus) kHttpServer_SetBacklog(kHttpServer server, kSize backlog)
{
kObj(kHttpServer, server);
kCheckState(kIsNull(obj->thread));
obj->backlog = backlog;
return kOK;
}
kFx(kStatus) kHttpServer_SetWriteBuffers(kHttpServer server, kSSize socketSize, kSSize clientSize)
{
kObj(kHttpServer, server);
kCheckState(kIsNull(obj->thread));
if (socketSize >= 0)
{
obj->socketWriteBufferSize = socketSize;
}
if (clientSize >= 0)
{
obj->clientWriteBufferSize = clientSize;
}
return kOK;
}
kFx(kStatus) kHttpServer_SetReadBuffers(kHttpServer server, kSSize socketSize, kSSize clientSize)
{
kObj(kHttpServer, server);
kCheckState(kIsNull(obj->thread));
if (socketSize >= 0)
{
obj->socketReadBufferSize = socketSize;
}
if (clientSize >= 0)
{
obj->clientReadBufferSize = clientSize;
}
return kOK;
}
kFx(kStatus) kHttpServer_SetHandler(kHttpServer server, kCallbackFx function, kPointer receiver)
{
kObj(kHttpServer, server);
kCheckState(kIsNull(obj->thread));
obj->handler.function = function;
obj->handler.receiver = receiver;
return kOK;
}
kFx(kStatus) kHttpServer_Start(kHttpServer server)
{
kObj(kHttpServer, server);
kStatus status;
kCheckState(kIsNull(obj->thread));
kCheckState(!kIsNull(obj->handler.function));
kAtomic32s_Exchange(&obj->shouldQuit, kFALSE);
kTry
{
kTest(kList_Construct(&obj->channels, kTypeOf(kObject), obj->channelCapacity, kObject_Alloc(server)));
//configure server socket
kTest(kTcpServer_Construct(&obj->listener, obj->localEndPoint.address.version, kObject_Alloc(server)));
kTest(kTcpServer_SetReadBuffers(obj->listener, obj->socketReadBufferSize, obj->clientReadBufferSize));
kTest(kTcpServer_SetWriteBuffers(obj->listener, obj->socketWriteBufferSize, obj->clientWriteBufferSize));
kTest(kTcpServer_EnableReuseAddress(obj->listener, kTRUE));
kTest(kTcpServer_Listen(obj->listener, obj->localEndPoint.address, obj->localEndPoint.port, obj->backlog));
//start thread to accept incoming connections
kTest(kThread_Construct(&obj->thread, kObject_Alloc(server)));
kTest(kThread_StartEx(obj->thread, xkHttpServer_ThreadEntry, server, 0, "kHttpServer", 0));
}
kCatch(&status)
{
kHttpServer_Stop(server);
kEndCatch(status);
}
return kOK;
}
kFx(kStatus) kHttpServer_Stop(kHttpServer server)
{
kObj(kHttpServer, server);
if (!kIsNull(obj->thread))
{
kAtomic32s_Exchange(&obj->shouldQuit, kTRUE);
kCheck(kDestroyRef(&obj->thread));
}
kCheck(kDestroyRef(&obj->listener));
kCheck(kDisposeRef(&obj->channels));
return kOK;
}
kFx(kStatus) xkHttpServer_ThreadEntry(kHttpServer server)
{
kObj(kHttpServer, server);
//run until the server is stopped
while (!kAtomic32s_Get(&obj->shouldQuit))
{
kTcpClient client = kNULL;
kHttpServerChannel channel = kNULL;
//wait to accept a new connection; the wait time is limited to xkHTTP_SERVER_QUIT_QUERY_PERIOD, so that the
//'shouldQuit' flag is periodically polled
if (kSuccess(kTcpServer_Accept(obj->listener, xkHTTP_SERVER_QUIT_QUERY_PERIOD, &client, kObject_Alloc(server))))
{
if (!kIsNull(client))
{
//construct a new channel object to represent the connection, and add it to the list of active channels
if (!kSuccess(xkHttpServerChannel_Construct(&channel, server, client, kObject_Alloc(server))))
{
kObject_Destroy(client);
}
else
{
xkHttpServer_AddChannel(server, channel);
}
}
}
}
return kOK;
}
kFx(kStatus) xkHttpServer_AddChannel(kHttpServer server, kHttpServerChannel channel)
{
kObj(kHttpServer, server);
kStatus status;
//add new channel to channel list
if (!kSuccess(status = kList_AddT(obj->channels, &channel, kNULL)))
{
kObject_Destroy(channel);
return status;
}
//remove any closed channels, or channels in excess of max connections
kCheck(xkHttpServer_CullChannels(server));
return kOK;
}
kFx(kStatus) xkHttpServer_CullChannels(kHttpServer server)
{
kObj(kHttpServer, server);
kListItem channelIt = 0;
//remove any inactive channels
channelIt = kList_First(obj->channels);
while (!kIsNull(channelIt))
{
kListItem current = channelIt;
kHttpServerChannel channel = kList_AsT(obj->channels, current, kObject);
channelIt = kList_Next(obj->channels, channelIt);
if (xkHttpServerChannel_IsClosed(channel))
{
kCheck(kList_Remove(obj->channels, current));
kCheck(kObject_Destroy(channel));
}
}
//remove oldest remaining channels until less than capacity
while (kList_Count(obj->channels) > obj->channelCapacity)
{
kListItem current = kList_First(obj->channels);
kHttpServerChannel channel = kList_AsT(obj->channels, current, kObject);
kCheck(kList_Remove(obj->channels, current));
kCheck(kObject_Destroy(channel));
}
return kOK;
}
kFx(kStatus) kHttpServer_LocalEndPoint(kHttpServer server, kIpEndPoint* endPoint)
{
kObj(kHttpServer, server);
kCheckState(!kIsNull(obj->listener));
kCheck(kTcpServer_LocalEndPoint(obj->listener, endPoint));
return kOK;
}
kFx(kCallback) xkHttpServer_Handler(kHttpServer server)
{
kObj(kHttpServer, server);
return obj->handler;
}