An open API service indexing awesome lists of open source software.

https://github.com/olliNiinivaara/GuildenStern

Modular multithreading Linux HTTP+WebSocket server
https://github.com/olliNiinivaara/GuildenStern

http server web websockets

Last synced: 21 days ago
JSON representation

Modular multithreading Linux HTTP+WebSocket server

Awesome Lists containing this project

README

        

# GuildenStern

Modular multithreading HTTP/1.1 + WebSocket upstream server framework for POSIXy OSs (Linux, BSD, MacOS).

## Documentation

https://olliniinivaara.github.io/GuildenStern/theindex.html

## Example 1: hello world

```nim
import guildenstern/[dispatcher, httpserver]
let server = newHttpServer(proc() = reply "hello world")
if server.start(8080): joinThread(server.thread)
```

## Example 2: Partitioning work to fine-tuned servers

```nim
# nim r --d:release --mm:atomicArc thisexample

import cgi, guildenstern/[dispatcher, epolldispatcher, httpserver]

proc handleGet() =
echo "method: ", getMethod()
echo "uri: ", getUri()
if isUri("/favicon.ico"): reply(Http204)
else:
reply """
GuildenStern Example

Send"""

proc handlePost() =
echo "client said: ", readData(getBody()).getOrDefault("say")
reply(Http303, ["location: " & http.headers.getOrDefault("origin")])

let getserver = newHttpServer(handleGet, contenttype = NoBody)
let postserver = newHttpServer(handlePost, loglevel = INFO, headerfields = ["origin"])
if not dispatcher.start(getserver, 5050): quit()
if not epolldispatcher.start(postserver, 5051, threadpoolsize = 20): quit()
joinThreads(getserver.thread, postserver.thread)
```

## Example 3: Websocket server and multiple clients discussing

```nim
const ClientCount = 10
from os import sleep

#-----------------

import guildenstern/websocketserver
when epollSupported(): import guildenstern/epolldispatcher
else: import guildenstern/dispatcher

proc serverReceive() =
let message = getMessage()
echo "server got: ", message
if message == "close?": wsserver.send(thesocket, "close!")
else: wsserver.send(thesocket, "Ok!")

let server = newWebsocketServer(receive = serverReceive)
if not server.start(8080): quit()

#-----------------

import guildenstern/websocketclient

proc clientReceive(client: WebsocketClient) =
let message = getMessage()
echo "client ", $client.id, " got: ",message
if message == "close!": shutdown()

let clientele = newWebsocketClientele()

proc run() =
for i in 1..ClientCount:
let client = clientele.newWebsocketClient("ws://0.0.0.0:8080", clientReceive)
if not client.connect(): quit()
client.send("this comes from client " & $client.id)
sleep(100)
clientele.clients[1].send("close?")

#-----------------

if clientele.start():
run()
joinThread(server.thread)
```

## Release notes, 8.1.0 (2025-02-10)
- Servers are now pointers to objects instead of references, as a workaround to a race condition inside Nim's default memory manager
- servers now have a name field, for better logging in multi-server configurations
- websocketserver regression fix: canceling upgrade by returning false works again

## Release notes, 8.0.1 (2025-01-22)
- epolldispatcher bug fix: always unregister sockets on close
- websocketserver bug fix: do not mess the statuscode of *sendClose*
- websocketserver improvement: remove a memory allocation bottleneck by casting instead of converting bytes in *maskMessage* proc

## Release notes, 8.0.0 (2025-01-17)

### breaking changes
- dispatcher's *start* proc now returns *bool* that has to be handled
- *LogCallback* takes also source as parameter (breaking only if you have been using a custom logger procedure)
- socketdata *flags* parameter is not directly accessible anymore, but there are new *getFlags* and *setFlags* procs (only affects those who created new server components)
- new global convenience template *thesocket*, so you don't need write *socketcontext.socket*, *http.socket* or *ws.socket* (breaking only if you were already using a variable named *thesocket*)

### major changes
- new robust *epolldispatcher* available for platforms that support epoll (e.g. Linux)
- new *websocketclient* module available, that let's you test your websocket servers easily (for inspiration, check the new *wsclienttest* and *wsmulticasttest* files in the examples folder)
- SocketData is not anymore available in socketcontext. Instead, *server*, *socket* and *customdata* are directly available in the socketcontext.There is a convenience *socketdata* proc that makes the redirection, so existing code should not break
- *OnCloseSocketCallback* that offers socketdata as parameter is deprecated (but works). Switch to new *OnCloseSocketCallback* that offers server and socket directly as parameters
- new *threadFinalizerCallback* that is triggered for every worker thread just before they stop running
- various stability improvements

### minor changes
- the --d:threadsafe compiler switch is not needed anymore
- all-around better logging
- log messages also include server id and thread id
- servers can work in client mode when port number 0 is used
- new dispatcher proc *registerSocket* for adding sockets to servers working in client mode
- dispatchers close themselves more gracefully, waiting up to 10 seconds for workerthreads to finish
- if dispatcher fails to start, returns false instead of shutting down everything
- various internal improvements for those who write new server components
- new error code EFault for detecting memory corruption (faulty pointer inputs to posix procs)
- new static func *epollSupported* in guildenserver for checking if epoll is supported
- *suspend* proc now needs also the server as parameter. The old suspend exists for backward compatibility, but it always only sleeps
- socket closing is always logged, with suitable log level depending on cause
- *closeOtherSocket* renamed to *closeSocket* (closeOthersocket is deprecated, and just redirects to closeSocket)
- HttpServers do not close the socket, if an empty request is received (because it might be a keep-alive packet)
- reply messages do not need to have and address anymore (constants accepted)
- WebsocketServer supports running in client mode (triggered, when new *clientmaskkey* parameter is set in *initWebsocketServer* proc)
- WebsocketServer now hails the *sockettimeoutms* parameter when receiving messages
- WebsocketServer has new *send* proc for sending a message to many clients simultaneously, that takes *failedsockets: var seq[SocketHandle]* parameter, and returns in it all sockets that failed to receive the message. Consult *serverHandler* proc in wsmulticasttest as an example