{"id":19881553,"url":"https://github.com/chifisource/toolipsudp.jl","last_synced_at":"2026-03-05T01:35:36.908Z","repository":{"id":107336480,"uuid":"517986459","full_name":"ChifiSource/ToolipsUDP.jl","owner":"ChifiSource","description":"UDP Servers for Toolips.jl","archived":false,"fork":false,"pushed_at":"2025-07-11T18:25:06.000Z","size":113,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-21T15:42:48.786Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Julia","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/ChifiSource.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,"zenodo":null},"funding":{"github":["emmaccode","UnformalPenguin"]}},"created_at":"2022-07-26T08:59:28.000Z","updated_at":"2025-07-11T00:46:10.000Z","dependencies_parsed_at":"2025-07-09T03:24:56.244Z","dependency_job_id":"41c252bf-e35f-4979-bee3-fec0d79ec418","html_url":"https://github.com/ChifiSource/ToolipsUDP.jl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ChifiSource/ToolipsUDP.jl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FToolipsUDP.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FToolipsUDP.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FToolipsUDP.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FToolipsUDP.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChifiSource","download_url":"https://codeload.github.com/ChifiSource/ToolipsUDP.jl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FToolipsUDP.jl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30104746,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T01:06:53.091Z","status":"ssl_error","status_checked_at":"2026-03-05T01:02:35.679Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-12T17:14:36.407Z","updated_at":"2026-03-05T01:35:31.899Z","avatar_url":"https://github.com/ChifiSource.png","language":"Julia","funding_links":["https://github.com/sponsors/emmaccode","https://github.com/sponsors/UnformalPenguin"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/raw/main/toolips/toolipsudp.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\n`ToolipsUDP` provides high-level [toolips](https://github.com/ChifiSource/Toolips.jl)-style functionality to UDP networking projects.\n- Follows `Toolips` `0.3` conventions.\n- Servers are modules.\n- Extensible server framework.\n- Streamlined UDP API.\n```julia\nusing Pkg; Pkg.add(\"ToolipsUDP\")\n```\n```julia\n# create a new project from template:\nusing ToolipsUDP; ToolipsUDP.new_app(UDP, \"MyApp\")\n```\n```julia\nmodule MyUDPExample\n\nmain = handler() do c::AbstractUDPConnection\n    if contains(c.packet, \"emmy\")\n         respond!(c, \"you are indeed me!\")\n         return\n    end\n    respond!(c, \"you aren't me... ? How are you on my network? Who are you ?!\")\nend\n\n# routes and extensions are loaded by exporting them. We will need `start!` and `UDP` to start the server.\nexport start!, UDP, main\nend\n```\n```julia\n# start your server\nusing MyUDPExample; start!(UDP, MyUDPExample)\n\n# start with threads. Any minimum below `1` will recurring select the main-thread, allowing for more requests to be distributed to the main thread than the accompanying threads.\n#                                                                                             (*similar to the router_threads argument from `Toolips`*)\n                                      #     serves once on main thread, then 4 times on other threads before returning.\nusing MyUDPExample; start!(UDP, MyUDPExample, threads = 1:5)\n                                                     # serves on the main thread 7 times, -5-1,then serves 7 times on threads before returning to -5.\nusing MyUDPExample; start!(UDP, MyUDPExample, threads = -5:8)\n```\nNote that for multi-threading you will want to alias your functions as an `AbstractConnection` -- this also will not work with certain forms of `send`.\n```julia\n# client\nusing ToolipsUDP\n# send with no response.\nsend(\"127.0.0.1\":7009, \"hello, my name is emmy\")\n# send with response.\nsock = send(\"127.0.0.1\":7009, \"hello, my name is emmy\", keep_open = true)\nclose(sock)\n```\n###### map\n- [get started](#get-started)\n- [getters](#getters)\n- [responding](#responding)\n- [extensions](#extensions)\n- [multi-threading](#multi-threading)\n###### get started\nThe intention with `ToolipsUDP` is to replicate the typical `Toolips` web-development format in `UDP`. The server system closely mirrors that of `Toolips` itself:\n```julia\n# hello world in toolips (TCP HTTP Server)\nmodule HelloWorld\nusing Toolips\n\nhome = route(\"/\") do c::Connection\n    write!(c, \"hello world!\")\nend\n\nexport start!, home\nend # module\n\nusing HelloWorld: start!(HelloWorld)\n\n# hello world in toolipsUDP (UDP Server)\nmodule HelloUDP\nusing ToolipsUDP\n\nhome = handler() do c::UDPConnection\n    respond!(c, \"hello world!\")\nend\nend # module\n\n\nusing HelloUDP; start!(UDP, HelloUDP)\n# toolips 0.3.4 +:\nusing HelloUDP; start!(UDP, HelloUDP)\n```\n**note that we will provide `UDP` to both `start!` and `new_app`**\nThe `Toolips.Route` is replaced with the `ToolipsUDP.handler`. To write a `handler`, we use the `handler` function as seen above. We can also provide a `String` to this function to make a `NamedHandler`, which is used by some extensions. The `handler`(s) are stored in the `AbstractUDPConnection.handlers` field. There is also the `packet` and an `ip` field. \n```julia\n?UDPConnection\n```\n### abstract type AbstractUDPConnection \u003c: Toolips.AbstractConnection\n The `AbstractUDPConnection` fills the same role as the `Toolips.AbstractConnection` -- \nbeing a mutable type that is passed into a response handler.\n- See also: `UDPConnection`, `UDPIOConnection`, `start!`\n##### consistencies\n- `ip`**::String**\n- `port`**::Int64**\n- `packet`**::String**\n- `data`**::Dict{Symbol, Any}**\n\n`ToolipsUDP` is also a bit more simplified; there is no 'default header' to process, and the only data we can receive is the packet itself and the IP of the client, so things are a bit more simplified. \n#### getters\nThe following functions are used to retrieve data from an `AstractUDPConnection`.\n- `Toolips.get_ip(c::UDPConnection)`\n- `ToolipsUDP.get_ip4(c::UDPConnection)`\n\nFor accessing the data in the incoming packet, we simply utilize the `c.packet` field.\n#### responding\nWe are able to respond, as well as send data, using the `send` and `respond` functions.\n- `respond!(c::UDPConnection, data::String)` is the most essential, as it instantly sends `data` back to the client.\n- `send(data::String, to::IP4 = \"127.0.0.1\":2000; from::Int64 = to.port - 5)` For sending a packet through a 'cursor', useful to test an initial response via a temporary server on the port in `from`, but note that it will not get a response as the server is immediately stopped after sending.\n- `send(c::UDPConnection, data::String, to::IP4 = \"127.0.0.1\":2000)` allows us to send data to any server, regardless as to whether or not it has sent to us or not or it is the current client, from a `handler`. A use-case for this would be multi-user chat, for example, where we want to call a `Function` on a certain client and not another. We store the IP of all clients alongside their names, when a client elects to send a user to the name we retrieve the associated IP and send the data.\n- `send(c::Module, data::String, to::IP4 = \"127.0.0.1\":2000)` is similar to sending data from a `handler`, but it allows us to send data from the REPL using the server.\n#### extensions\nUDP extensions are handled nearly identically to regular `Toolips` extensions. First, we create a new type that is a `\u003c:` of `AbstractUDPExtension`.\n```julia\npages = Dict(\"page1\" =\u003e \"once upon a time\", \"page2\" =\u003e \"there was a person\")\n\nmutable struct MySampleExtension \u003c: ToolipsUDP.UDPExtension\n     client_page::Dict{IP4,  String}\n     config_path::String\n    MySampleExtension(uri::String) = new(Dict{IP4, String}(), uri)::MySampleExtension\nend\n```\nNext, this extension may be bound to `route!` and `on_start`:\n```julia\nimport ToolipsUDP: on_start, route!\n\nfunction on_start(data::Dict{Symbol, Any}, ext::MySampleExtension)\n    raw_config = read(ext.config_path, String)\n    for client in split(raw_config, \";\")\n        value_splits = split(client, \"|\")\n        ip4_splits = split(ip4_str[1], \":\")\n        ip4 = string(ip4_splits[1]):parse(Int64, ip4_splits[2])\n        push!(ext.client_page, ip4 =\u003e string(value_splits[2]))\n   end\nend\n# use `false` to stop routing\nfunction route!(c::UDPConnection, ext::MySampleExtension)\n    if get_ip4(c) in keys(ext.client_page)\n       respond!(c, \"you're loaded on page \" * ext.client_page[get_ip4(c)])\n       return(false)\n    end\nend\n```\n#### multi-threading\nMulti-threading is done by simply adding a **range** of threads to utilize. In this range, `1` represents your base thread -- anything about `1` will be served on an additional thread. Anything below `1` provided as the `minimum` will perform an extra response on the base thread. In other words, `0:3` would serve twice on the base thread, `0` and `1`, before serving `2` and `3` on workers and returning to `0` and the base thread. Note that both a `UDPIOConnection` and a `UDPConnection` will be sent through a multi-threaded server's handlers, so this must be annotated as an `AbstractUDPConnection`.\n```julia\nmodule MultiThreadedServer\nusing ToolipsUDP\ncount = 0\n\nmain = handler() do c::AbstractUDPConnection\n    global count += 1\nend\n\nexport main, UDP, start!\nend # module MultiThreadedServer\n\nusing MultiThreadedServer\nstart!(UDP, MultiThreadedServer, threads = -1:5)\n```\nThe `ProcessManager` is also, like `Toolips`, the return of `start!`. Considering this, we could feasibly add workers and distribute our tasks -- though `threads` and `router_threads` aren't *both* available as they are in `Toolips`. \nWhile this aspect of multi-threading is relatively straightforward, not all servers will be compatible with this form of multi-threading. For starters, your handlers will need to be annotated as `AbstractUDPConnection`, rather than a regular `UDPConnection` -- as a different `Connection` type is used when not on the base thread.\n\nDownsides to multi-threading with `ToolipsUDP`:\n- You cannot use `send` -- in the future, there might be a place to store sent data inside of a `UDPIOConnection`, for now this is not a reality and the `UDPIOConnection` is relegated exclusively to `respond!` for sending data. In other words, we can send data back to the client but not really anywhere else.\n- A multi-threaded project **MUST BE** an established project with its own environment, modules created under `Main` or in the REPL will not be able to load on additional threads.\n- Inevitably, loading your server across several threads leads to a higher memory cost. The `threads` argument allows you to balance the performance of the threads with the loss in performance that occurs from translating data into each thread.\n- Every `Handler` function argument must be annotated as an `AbstractUDPConnection`, or have no annotation at all.\n### contributing\nYou can help out with this project by...\n- using `ToolipsUDP` in your own project 🌷\n- creating extensions for the toolips ecosystem 💐\n- forking this project [contributing guidelines](#guidelines)\n- submitting issues\n- contributing to other [chifi](https://github.com/ChifiSource) projects\n- supporting chifi creators\n\nI thank you for all of your help with our project, or just for considering contributing! I want to stress further that we are not picky -- allowing us all to express ourselves in different ways is part of the key methodology behind the entire [chifi](https://github.com/ChifiSource) ecosystem. Feel free to contribute, we would **love** to see your art! Issues marked with `good first issue` might be a great place to start!\n#### guidelines\nWe are not super strict, but making sure of these few things will be helpful for maintainers!\n1. You have replicated the issue on **Unstable**\n2. The issue does not currently exist... or does not have a planned implementation different to your own. In these cases, please collaborate on the issue, express your idea and we will select the best choice.\n3. **Pull Request TO UNSTABLE**\n4. Be **specific** about your issue -- if you are experiencing multiple issues, open multiple issues. It is better to have a high quantity of issues that specifically describe things than a low quantity of issues that describe multiple things.\n5. If you have a new issue, **open a new issue**. It is not best to comment your issue under an unrelated issue; even a case where you are experiencing that issue, if you want to mention **another issue**, open a **new issue**.\n6. Questions are fine, but preferably **not** questions answered inside of this `README`.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchifisource%2Ftoolipsudp.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchifisource%2Ftoolipsudp.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchifisource%2Ftoolipsudp.jl/lists"}