{"id":13737123,"url":"https://github.com/olliNiinivaara/GuildenStern","last_synced_at":"2025-05-08T13:33:05.026Z","repository":{"id":40620127,"uuid":"261455072","full_name":"olliNiinivaara/GuildenStern","owner":"olliNiinivaara","description":"Modular multithreading Linux HTTP+WebSocket server","archived":false,"fork":false,"pushed_at":"2024-05-28T11:50:04.000Z","size":2592,"stargazers_count":78,"open_issues_count":3,"forks_count":6,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-05-29T03:17:03.184Z","etag":null,"topics":["http","server","web","websockets"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olliNiinivaara.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-05T12:10:07.000Z","updated_at":"2024-06-10T20:01:03.738Z","dependencies_parsed_at":"2023-11-24T20:27:28.425Z","dependency_job_id":"b59aa5ec-9d5f-4e6d-95b7-342bf7b47d6d","html_url":"https://github.com/olliNiinivaara/GuildenStern","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olliNiinivaara%2FGuildenStern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olliNiinivaara%2FGuildenStern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olliNiinivaara%2FGuildenStern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olliNiinivaara%2FGuildenStern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olliNiinivaara","download_url":"https://codeload.github.com/olliNiinivaara/GuildenStern/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224737431,"owners_count":17361345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","server","web","websockets"],"created_at":"2024-08-03T03:01:35.623Z","updated_at":"2025-05-08T13:33:05.016Z","avatar_url":"https://github.com/olliNiinivaara.png","language":"Nim","funding_links":[],"categories":["Web"],"sub_categories":["HTTP Servers"],"readme":"# GuildenStern\n\nModular multithreading HTTP/1.1 + WebSocket upstream server framework for POSIXy OSs (Linux, BSD, MacOS).\n\n## Documentation\n\nhttps://olliniinivaara.github.io/GuildenStern/theindex.html\n\n## Example 1: hello world\n\n```nim\nimport guildenstern/[dispatcher, httpserver]\nlet server = newHttpServer(proc() = reply \"hello world\")\nif server.start(8080): joinThread(server.thread)\n```\n\n## Example 2: Partitioning work to fine-tuned servers\n\n```nim\n# nim r --d:release --mm:atomicArc thisexample\n\nimport cgi, guildenstern/[dispatcher, epolldispatcher, httpserver]\n     \nproc handleGet() =\n  echo \"method: \", getMethod()\n  echo \"uri: \", getUri()\n  if isUri(\"/favicon.ico\"): reply(Http204)\n  else:\n    reply \"\"\"\n      \u003c!doctype html\u003e\u003ctitle\u003eGuildenStern Example\u003c/title\u003e\u003cbody\u003e\n      \u003cform action=\"http://localhost:5051\" method=\"post\" accept-charset=\"utf-8\"\u003e\n      \u003cinput name=\"say\" value=\"Hi\"\u003e\u003cbutton\u003eSend\"\"\"\n\nproc handlePost() =\n  echo \"client said: \", readData(getBody()).getOrDefault(\"say\")\n  reply(Http303, [\"location: \" \u0026 http.headers.getOrDefault(\"origin\")])\n  \nlet getserver = newHttpServer(handleGet, contenttype = NoBody)\nlet postserver = newHttpServer(handlePost, loglevel = INFO, headerfields = [\"origin\"])\nif not dispatcher.start(getserver, 5050): quit()\nif not epolldispatcher.start(postserver, 5051, threadpoolsize = 20): quit()\njoinThreads(getserver.thread, postserver.thread)\n```\n\n## Example 3: Websocket server and multiple clients discussing\n\n```nim\nconst ClientCount = 10\nfrom os import sleep\n\n#-----------------\n\nimport guildenstern/websocketserver\nwhen epollSupported(): import guildenstern/epolldispatcher\nelse: import guildenstern/dispatcher\n\nproc serverReceive() =\n  let message = getMessage()\n  echo \"server got: \", message\n  if message == \"close?\": wsserver.send(thesocket, \"close!\")\n  else: wsserver.send(thesocket, \"Ok!\")\n\nlet server = newWebsocketServer(receive = serverReceive)\nif not server.start(8080): quit()\n\n#-----------------\n\nimport guildenstern/websocketclient\n\nproc clientReceive(client: WebsocketClient) =\n  let message = getMessage()\n  echo \"client \", $client.id, \" got: \",message\n  if message == \"close!\": shutdown()\n\nlet clientele = newWebsocketClientele()\n\nproc run() =\n  for i in 1..ClientCount:\n    let client = clientele.newWebsocketClient(\"ws://0.0.0.0:8080\", clientReceive)\n    if not client.connect(): quit()\n    client.send(\"this comes from client \" \u0026 $client.id)\n  sleep(100)\n  clientele.clients[1].send(\"close?\")\n\n#-----------------\n\nif clientele.start():\n  run()\n  joinThread(server.thread)\n```\n\n## Release notes, 8.1.0 (2025-02-10)\n- Servers are now pointers to objects instead of references, as a workaround to a race condition inside Nim's default memory manager\n- servers now have a name field, for better logging in multi-server configurations\n- websocketserver regression fix: canceling upgrade by returning false works again   \n\n## Release notes, 8.0.1 (2025-01-22)\n- epolldispatcher bug fix: always unregister sockets on close\n- websocketserver bug fix: do not mess the statuscode of *sendClose*\n- websocketserver improvement: remove a memory allocation bottleneck by casting instead of converting bytes in *maskMessage* proc    \n\n## Release notes, 8.0.0 (2025-01-17)\n\n### breaking changes\n- dispatcher's *start* proc now returns *bool* that has to be handled\n- *LogCallback* takes also source as parameter (breaking only if you have been using a custom logger procedure)\n- socketdata *flags* parameter is not directly accessible anymore, but there are new *getFlags* and *setFlags* procs (only affects those who created new server components)\n- 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*)\n\n### major changes\n- new robust *epolldispatcher* available for platforms that support epoll (e.g. Linux)\n- 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)\n- 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\n- *OnCloseSocketCallback* that offers socketdata as parameter is deprecated (but works). Switch to new *OnCloseSocketCallback* that offers server and socket directly as parameters\n- new *threadFinalizerCallback* that is triggered for every worker thread just before they stop running\n- various stability improvements\n\n### minor changes\n- the --d:threadsafe compiler switch is not needed anymore\n- all-around better logging\n- log messages also include server id and thread id\n- servers can work in client mode when port number 0 is used\n- new dispatcher proc *registerSocket* for adding sockets to servers working in client mode\n- dispatchers close themselves more gracefully, waiting up to 10 seconds for workerthreads to finish\n- if dispatcher fails to start, returns false instead of shutting down everything\n- various internal improvements for those who write new server components\n- new error code EFault for detecting memory corruption (faulty pointer inputs to posix procs)\n- new static func *epollSupported* in guildenserver for checking if epoll is supported\n- *suspend* proc now needs also the server as parameter. The old suspend exists for backward compatibility, but it always only sleeps\n- socket closing is always logged, with suitable log level depending on cause\n- *closeOtherSocket* renamed to *closeSocket* (closeOthersocket is deprecated, and just redirects to closeSocket)\n- HttpServers do not close the socket, if an empty request is received (because it might be a keep-alive packet)\n- reply messages do not need to have and address anymore (constants accepted)\n- WebsocketServer supports running in client mode (triggered, when new *clientmaskkey* parameter is set in *initWebsocketServer* proc)\n- WebsocketServer now hails the *sockettimeoutms* parameter when receiving messages\n- 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\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FolliNiinivaara%2FGuildenStern","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FolliNiinivaara%2FGuildenStern","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FolliNiinivaara%2FGuildenStern/lists"}