{"id":13635118,"url":"https://github.com/noppoMan/Prorsum","last_synced_at":"2025-04-19T03:34:22.859Z","repository":{"id":63919324,"uuid":"74495385","full_name":"noppoMan/Prorsum","owner":"noppoMan","description":"A Go like concurrent system + networking/http library for Swift that works on Linux and Mac","archived":false,"fork":false,"pushed_at":"2019-09-11T06:52:26.000Z","size":608,"stargazers_count":71,"open_issues_count":7,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-09T23:28:46.239Z","etag":null,"topics":["channel","event-driven","go","goroutine","http","server-side-swift","socket","swift3","tcp","udp","websocket"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/noppoMan.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}},"created_at":"2016-11-22T17:08:13.000Z","updated_at":"2023-12-24T20:30:07.000Z","dependencies_parsed_at":"2023-01-14T14:00:23.953Z","dependency_job_id":null,"html_url":"https://github.com/noppoMan/Prorsum","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FProrsum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FProrsum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FProrsum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FProrsum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noppoMan","download_url":"https://codeload.github.com/noppoMan/Prorsum/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249600508,"owners_count":21297708,"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":["channel","event-driven","go","goroutine","http","server-side-swift","socket","swift3","tcp","udp","websocket"],"created_at":"2024-08-02T00:00:40.926Z","updated_at":"2025-04-19T03:34:22.614Z","avatar_url":"https://github.com/noppoMan.png","language":"Swift","readme":"# Prorsum\nA Go like concurrent system + networking/http libraries for Swift that works on Linux and Mac.\n\n[\u003cimg src=\"https://travis-ci.org/noppoMan/Prorsum.svg?branch=master\"\u003e](https://travis-ci.org/noppoMan/Prorsum)\n### Why Prorsum?\nThe reason why I started this project is because I felt it was very difficult to handle asynchronous io with Swift in the project called [Slimane](https://github.com/noppoMan/Slimane) which I had previously made. In the Asynchronous paradigm in Swift, We need to often use the capture list well for closures and sometimes retain the object(Connection etc..) to avoid to release by ARC.\nThen I thought Go's concurrent/parallel and synchronous mecanism is suitable model for the present stage of Swift(If you want to write Server on the MultiCore Machine). Because we can easy to make async operations wituhout callback chains, can use Full Cores with the simple syntax and easy to share the memory via Channel between a Thread and a Thread.\n\n**(Prorsum is not Goroutine. It doesn't have Corotuines and Context Switch is done on the OS side. It just has thread safe  shared memory mechanism(It works on the GCD) that is heavily inspired by Go.)**\n\n### VS C10K Problem\nProrsum's HTTP Server architecure is Event Driven master + Multithreading Request Handler.\nIn a DispatchQueue, you can write asynchronous I/O with synchronous syntax with `go()` + `Channel\u003cElement\u003e`.  \nEasy to make codes solve C10K without callbacks.\n\n```\n                                                 +-----------------+\n                                             |-- | Request Handler |\n                                             |   +-----------------+\n               +--------+                    |   +-----------------+\n----- TCP ---- | master |---Dispatch Queue---|-- | Request Handler |\n               +--------+                    |   +-----------------+               \n                                             |   +-----------------+\n                                             |-- | Request Handler |\n                                                 +-----------------+\n```\n\n## Features\n\n#### Go like equipments\n- [x] GCD based Concurrent System\n- [x] WaitGroup\n- [x] Once\n- [x] Channels\n- [ ] Channel Iteration\n- [x] Select\n- [ ] Timers\n\n#### Networking/HTTP\n- [x] DNS ipv6/v4\n- [x] TCP Server\n- [x] TCP Client\n- [x] UDP Socket\n- [ ] QUIC\n- [x] HTTP Server\n- [x] HTTP Client\n- [x] HTTPS Client\n- [ ] HTTP2.0\n- [x] WebSocket\n\n## Installation\n\nCurrenty Prorsum supports only SPM.\n\n### SPM\n\n```swift\nimport PackageDescription\n\nlet package = Package(\n    name: \"MyApp\",\n    dependencies: [\n        .Package(url: \"https://github.com/noppoMan/Prorsum.git\", majorVersion: 0, minor: 1)\n    ]\n)\n```\n\n### Cocoapods\n\nNot supported yet\n\n### Carthage\n\nNot supported yet\n\n## Usage\n\n### `go`\ngo is an alias of `DispatchQueue().async { }`\n\n```swift\nfunc asyncTask(){\n    print(Thread.current)\n}\n\ngo(asyncTask())\n\ngo {\n    print(Thread.current)\n}\n\ngomain {\n    print(Thread.current) // back to the main thread\n}\n```\n\n\n### `WaitGroup`\n\nA WaitGroup waits for a collection of GCD operations to finish. The main GCD operation calls Add to set the number of GCD operations to wait for. Then each of the GCD operations runs and calls Done when finished. At the same time, Wait can be used to block until all GCD operations have finished.\n\n\n```swift\nlet wg = WaitGroup()\n\nwg.add(1)\ngo {\n    sleep(1)\n    print(\"wg: 1\")\n    wg.done()\n}\n\nwg.add(1)\ngo {\n    sleep(1)\n    print(\"wg: 2\")\n    wg.done()\n}\n\nwg.wait() // block unitle twice wg.done() is called.\n\nprint(\"wg done\")\n```\n\n\n### `Channel\u003cElement\u003e`\n\nChannels are the pipes that connect concurrent operation. You can send values into channels from one GCD operation and receive those values into another GCD operation.\n\n```swift\nlet ch = Channel\u003cString\u003e.make(capacity: 1)\n\nfunc asyncSend(){\n    try! ch.send(\"Expecto patronum!\")\n}\n\ngo(asyncSend()) // =\u003e Expecto patronum!\n\ngo {\n    try! ch.send(\"Accio!\")\n}\n\ntry! ch.receive() // =\u003e Accio!\n\nch.close()\n```\n\n\n### `select`\n\nThe select statement lets a `BlockOperation` wait on multiple communication operations.\n\n```swift\nlet magicCh = Channel\u003cString\u003e.make(capacity: 1)\n\ngo {\n  try! magicCh.send(\"Obliviate\")\n}\n\nselect {\n    when(magicCh) {\n        print($0)\n    }\n\n    otherwise {\n        print(\"otherwise\")\n    }\n}\n```\n\n\n### `forSelect`\n\nGenerally You need to wrap the select inside a while loop. To make it easier to work with this pattern You can use `forSelect`. forSelect will loop until `done()` is called.\n\n```swift\nlet magicCh = Channel\u003cString\u003e.make(capacity: 1)\nlet doneCh = Channel\u003cString\u003e.make(capacity: 1)\n\ngo {\n    try! magicCh.send(\"Crucio\")\n    try! magicCh.send(\"Imperio\")\n}\n\ngo {\n    try! doneCh.send(\"Avada Kedavra!\")\n}\n\nforSelect { done in\n    when(magicCh) {\n        print($0)\n    }\n\n    when(doneCh) {\n        done() // break current loop\n    }\n\n    otherwise {\n        print(\"otherwise\")\n    }\n}\n```\n\n\n# Networking\n\n## HTTP Server\n\n```swift\nimport Prorsum\nimport Foundation\n\nlet server = try! HTTPServer { (request, writer) in\n    do {\n        let response = Response(\n            headers: [\"Server\": \"Prorsum Micro HTTP Server\"],\n            body: .buffer(\"hello\".data)\n        )\n\n        try writer.serialize(response)\n\n        writer.close()\n    } catch {\n        fatalError(\"\\(error)\")\n    }\n}\n\ntry! server.bind(host: \"0.0.0.0\", port: 3000)\nprint(\"Server listening at 0.0.0.0:3000\")\ntry! server.listen()\n\nRunLoop.main.run() //start run loop\n```\n\n## HTTP/HTTPS Client\n\n```swift\nimport Prorsum\n\nlet url = URL(string: \"https://google.com\")\nlet client = try! HTTPClient(url: url!)\ntry! client.open()\nlet response = try! client.request()\n\nprint(response)\n// HTTP/1.1 200 OK\n// Set-Cookie: NID=91=CPfJo7FsoC_HXmq7kLrs-e0DhR0lAaHcYc8GFxhazE5OXdc3uPvs22oz_UP3Bcd2mZDczDgtW80OrjC6JigVCGIhyhXSD7e1RA7rkinF3zxUNsDnAtagvs5pbZSjXuZE; expires=Sun, 04-Jun-2017 16:21:39 GMT; path=/; domain=.google.co.jp; HttpOnly\n// Transfer-Encoding: chunked\n// Accept-Ranges: none\n// Date: Sat, 03 Dec 2016 16:21:39 GMT\n// Content-Type: text/html; charset=Shift_JIS\n// Expires: -1\n// Alt-Svc: quic=\":443\"; ma=2592000; v=\"36,35,34\"\n// Cache-Control: private, max-age=0\n// Server: gws\n// X-XSS-Protection: 1; mode=block\n// Vary: Accept-Encoding\n// X-Frame-Options: SAMEORIGIN\n// P3P: CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\"\n```\n\n## TCP\n\n```swift\n#if os(Linux)\n    import Glibc\n#else\n    import Darwin.C\n#endif\n\nimport Prorsum\nimport Foundation\n\nlet server = try! TCPServer { clientStream in\n    while !clientStream.isClosed {\n        let bytes = try! clientStream.read()\n        try! clientStream.write(bytes)\n        clientStream.close()\n    }\n}\n\n// setup client\ngo {\n    sleep(1)\n    let client = try! TCPSocket()\n    try! client.connect(host: \"0.0.0.0\", port: 3000)\n    while !client.isClosed {\n        try! client.write(Array(\"hello\".utf8))\n        let bytes = try! client.recv()\n        if !bytes.isEmpty {\n            print(String(bytes: bytes, encoding: .utf8))\n        }\n    }\n    server.terminate() // terminate server\n}\n\ntry! server.bind(host: \"0.0.0.0\", port: 3000)\ntry! server.listen() //start run loop\n\nRunLoop.main.run() //start run loop\n```\n\n## Websocket\n\nHere is a Websocket Echo Server Example.\n\n```swift\n#if os(Linux)\n    import Glibc\n#else\n    import Darwin.C\n#endif\n\nimport Foundation\nimport Prorsum\n\nlet server = try! HTTPServer { (request, writer) in\n    do {\n        let response: Response\n        if request.isWebSocket {\n            response = try request.upgradeToWebSocket { request, websocket in\n                websocket.onText {\n                    print(\"received: \\($0)\")\n                    try! websocket.send($0)\n                }\n            }\n        } else {\n            response = Response(\n                headers: [\"Server\": \"Prorsum Micro HTTP Server\"],\n                body: .buffer(\"hello\".data)\n            )\n        }\n\n        try writer.serialize(response)\n\n        try response.upgradeConnection?(request, writer.stream)\n\n        writer.close()\n    } catch {\n        fatalError(\"\\(error)\")\n    }\n}\n\ntry! server.bind(host: \"0.0.0.0\", port: 8080)\nprint(\"Server listening at 0.0.0.0:8080\")\ntry! server.listen()\n\nRunLoop.main.run()\n```\n\n## Related Articles\n* [Rethink Appropriate Server Architecture For Swift](https://medium.com/@yukitakei/rethink-appropriate-server-architecture-for-swift-7c8513944db8#.9ii5n3yuz)\n\n## License\nProrsum is released under the MIT license. See LICENSE for details.\n","funding_links":[],"categories":["Network"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FnoppoMan%2FProrsum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FnoppoMan%2FProrsum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FnoppoMan%2FProrsum/lists"}