noppoman/prorsum
A Go like concurrent system + networking/http libraries for Swift that works on Linux and Mac.
Features
Go like equipments
- [x] GCD based Concurrent System
- [x] WaitGroup
- [x] Once
- [x] Channels
- [ ] Channel Iteration
- [x] Select
- [ ] Timers
Networking/HTTP
- [x] DNS ipv6/v4
- [x] TCP Server
- [x] TCP Client
- [x] UDP Socket
- [ ] QUIC
- [x] HTTP Server
- [x] HTTP Client
- [x] HTTPS Client
- [ ] HTTP2.0
- [x] WebSocket
Installation
Currenty Prorsum supports only SPM.
SPM
import PackageDescription
let package = Package(
name: "MyApp",
dependencies: [
.Package(url: "https://github.com/noppoMan/Prorsum.git", majorVersion: 0, minor: 1)
]
)Cocoapods
Not supported yet
Carthage
Not supported yet
Usage
go
go is an alias of DispatchQueue().async { }
func asyncTask(){
print(Thread.current)
}
go(asyncTask())
go {
print(Thread.current)
}
gomain {
print(Thread.current) // back to the main thread
}WaitGroup
A 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.
let wg = WaitGroup()
wg.add(1)
go {
sleep(1)
print("wg: 1")
wg.done()
}
wg.add(1)
go {
sleep(1)
print("wg: 2")
wg.done()
}
wg.wait() // block unitle twice wg.done() is called.
print("wg done")Channel<Element>
Channels 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.
let ch = Channel<String>.make(capacity: 1)
func asyncSend(){
try! ch.send("Expecto patronum!")
}
go(asyncSend()) // => Expecto patronum!
go {
try! ch.send("Accio!")
}
try! ch.receive() // => Accio!
ch.close()select
The select statement lets a BlockOperation wait on multiple communication operations.
let magicCh = Channel<String>.make(capacity: 1)
go {
try! magicCh.send("Obliviate")
}
select {
when(magicCh) {
print($0)
}
otherwise {
print("otherwise")
}
}forSelect
Generally 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.
let magicCh = Channel<String>.make(capacity: 1)
let doneCh = Channel<String>.make(capacity: 1)
go {
try! magicCh.send("Crucio")
try! magicCh.send("Imperio")
}
go {
try! doneCh.send("Avada Kedavra!")
}
forSelect { done in
when(magicCh) {
print($0)
}
when(doneCh) {
done() // break current loop
}
otherwise {
print("otherwise")
}
}Networking
HTTP Server
import Prorsum
import Foundation
let server = try! HTTPServer { (request, writer) in
do {
let response = Response(
headers: ["Server": "Prorsum Micro HTTP Server"],
body: .buffer("hello".data)
)
try writer.serialize(response)
writer.close()
} catch {
fatalError("\(error)")
}
}
try! server.bind(host: "0.0.0.0", port: 3000)
print("Server listening at 0.0.0.0:3000")
try! server.listen()
RunLoop.main.run() //start run loopHTTP/HTTPS Client
import Prorsum
let url = URL(string: "https://google.com")
let client = try! HTTPClient(url: url!)
try! client.open()
let response = try! client.request()
print(response)
// HTTP/1.1 200 OK
// Set-Cookie: NID=91=CPfJo7FsoC_HXmq7kLrs-e0DhR0lAaHcYc8GFxhazE5OXdc3uPvs22oz_UP3Bcd2mZDczDgtW80OrjC6JigVCGIhyhXSD7e1RA7rkinF3zxUNsDnAtagvs5pbZSjXuZE; expires=Sun, 04-Jun-2017 16:21:39 GMT; path=/; domain=.google.co.jp; HttpOnly
// Transfer-Encoding: chunked
// Accept-Ranges: none
// Date: Sat, 03 Dec 2016 16:21:39 GMT
// Content-Type: text/html; charset=Shift_JIS
// Expires: -1
// Alt-Svc: quic=":443"; ma=2592000; v="36,35,34"
// Cache-Control: private, max-age=0
// Server: gws
// X-XSS-Protection: 1; mode=block
// Vary: Accept-Encoding
// X-Frame-Options: SAMEORIGIN
// P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info."TCP
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
import Prorsum
import Foundation
let server = try! TCPServer { clientStream in
while !clientStream.isClosed {
let bytes = try! clientStream.read()
try! clientStream.write(bytes)
clientStream.close()
}
}
// setup client
go {
sleep(1)
let client = try! TCPSocket()
try! client.connect(host: "0.0.0.0", port: 3000)
while !client.isClosed {
try! client.write(Array("hello".utf8))
let bytes = try! client.recv()
if !bytes.isEmpty {
print(String(bytes: bytes, encoding: .utf8))
}
}
server.terminate() // terminate server
}
try! server.bind(host: "0.0.0.0", port: 3000)
try! server.listen() //start run loop
RunLoop.main.run() //start run loopWebsocket
Here is a Websocket Echo Server Example.
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
import Foundation
import Prorsum
let server = try! HTTPServer { (request, writer) in
do {
let response: Response
if request.isWebSocket {
response = try request.upgradeToWebSocket { request, websocket in
websocket.onText {
print("received: \($0)")
try! websocket.send($0)
}
}
} else {
response = Response(
headers: ["Server": "Prorsum Micro HTTP Server"],
body: .buffer("hello".data)
)
}
try writer.serialize(response)
try response.upgradeConnection?(request, writer.stream)
writer.close()
} catch {
fatalError("\(error)")
}
}
try! server.bind(host: "0.0.0.0", port: 8080)
print("Server listening at 0.0.0.0:8080")
try! server.listen()
RunLoop.main.run()License
Prorsum is released under the MIT license. See LICENSE for details.
Package Metadata
Repository: noppoman/prorsum
Default branch: master
README: README.md