on master help sign in
Initial
commitac01fc5e47e592501fbc75a7ea7383213a2ebecf authorilyakooo0 <ilyakooo0@gmail.com> dateSat 24 Nov 2018, 01:19 (8 years ago) parent157faf0
4 files changed+240−2
Sources/App/Models/Post.swift+147 Sources/App/boot.swift+62−1 Sources/App/configure.swift+26 Sources/App/routes.swift+5−1
Sources/App/Models/Post.swiftnew file+147
@@ -0,0 +1,147 @@
1+//
2+// Post.swift
3+// App
4+//
5+// Created by Ilya on 22/11/2018.
6+//
7+
8+import Foundation
9+import Vapor
10+
11+class Model {
12+ var posts: [Int: Post] = [:]
13+
14+ enum Error: AbortError {
15+ var status: HTTPResponseStatus {return .notFound}
16+
17+ var reason: String {return "User not found."}
18+
19+ var identifier: String {return "userNotFound"}
20+
21+ case notFound
22+ }
23+
24+ func getPost(for id: Post.Id, on worker: Worker) -> Future<Post> {
25+ let out = worker.eventLoop.newPromise(Post.self)
26+
27+ queue.async {
28+ if let post = self.posts[id] {
29+ out.succeed(result: post)
30+ } else {
31+ out.fail(error: Error.notFound)
32+ }
33+ }
34+
35+ return out.futureResult
36+ }
37+
38+ func deleteRandom(on worker: Worker) -> Future<Post.Id> {
39+ let out = worker.eventLoop.newPromise(Post.Id.self)
40+
41+ if let post = self.posts.randomElement() {
42+ self.queue.sync(flags: .barrier) {
43+ self.posts[post.key] = nil
44+ out.succeed(result: post.key)
45+ }
46+ } else {
47+ out.fail(error: Error.notFound)
48+ }
49+
50+ return out.futureResult
51+
52+ }
53+
54+ func insert(post: Post) {
55+ queue.sync(flags: .barrier) {
56+ posts[post.id] = post
57+ }
58+ }
59+
60+ private let queue = DispatchQueue(label: "soy.iko.Model")
61+}
62+
63+let postModel = Model()
64+
65+enum Event: Encodable {
66+ func encode(to encoder: Encoder) throws {
67+ var container = encoder.container(keyedBy: CodingKeys.self)
68+ switch self {
69+ case .delete(let id):
70+ try container.encode(id, forKey: .id)
71+ try container.encode("delete", forKey: .type)
72+ case .add(let id):
73+ try container.encode(id, forKey: .id)
74+ try container.encode("add", forKey: .type)
75+ }
76+ }
77+
78+ private enum CodingKeys: String, CodingKey {
79+ case type
80+ case id
81+ }
82+
83+ case delete(Post.Id)
84+ case add(Post.Id)
85+}
86+
87+struct CyclicBuffer<T> {
88+ var buffer: [T?]
89+
90+ var index = 0
91+ let size: Int
92+ init(of size: Int) {
93+ self.size = size
94+ buffer = Array(repeating: nil, count: size)
95+ }
96+
97+ mutating func append(element: T?) {
98+ queue.sync(flags: .barrier) {
99+ buffer[index] = element
100+ index = (index+1)%size
101+ }
102+ }
103+
104+ func get(last number: Int, loop: Worker) -> Future<ArraySlice<T?>> {
105+
106+ let out = loop.eventLoop.newPromise(ArraySlice<T?>.self)
107+
108+// var out: ArraySlice<T?>!
109+ queue.async {
110+ out.succeed(result: (self.buffer[self.index+1..<self.size] + self.buffer[0...max(number-1, self.index)]))
111+ }
112+
113+ return out.futureResult
114+ }
115+
116+ private let queue = DispatchQueue(label: "soy.iko.Buffer")
117+}
118+
119+var eventBuffer = CyclicBuffer<Event>(of: 100)
120+
121+struct Events: Encodable {
122+ let error: String?
123+ let events: [Event]?
124+}
125+
126+struct Post: Content {
127+ typealias Id = Int
128+
129+ let user: User
130+ let text: String
131+
132+ let id: Id = {
133+ lastID += 1;
134+ return lastID
135+ }()
136+
137+ static private var lastID: Int = 0
138+}
139+
140+struct User: Content {
141+ let name: Name
142+}
143+
144+struct Name: Content {
145+ let first: String
146+ let second: String
147+}
Sources/App/boot.swift+62−1
@@ -1,6 +1,67 @@
11import Vapor
22
3+import Dispatch
4+
5+struct OpenSockets {
6+ private(set) static var sockets: [Int: WebSocket] = [:]
7+
8+ static func insert(_ socket: WebSocket) -> Int {
9+ let out = id
10+ id += 1
11+ queue.sync {
12+ sockets[out] = socket
13+ }
14+ return out
15+ }
16+
17+ static func remove(at id: Int) {
18+ queue.async {
19+ sockets[id] = nil
20+ }
21+ }
22+
23+// static func get(for id: Int) {
24+// var out: WebSocket!
25+// queue.sync {
26+// out = sockets[id]
27+// }
28+// }
29+
30+ static let queue = DispatchQueue.init(label: "WS")
31+
32+ private static var id = 0
33+}
34+
35+let enc = JSONEncoder()
36+
337/// Called after your application has initialized.
438public func boot(_ app: Application) throws {
5− // your code here
39+
40+ app.eventLoop.scheduleRepeatedTask(initialDelay: .seconds(10), delay: .seconds(10)) { (task: RepeatedTask) in
41+ let _ = try app.client().get("https://baconipsum.com/api/?type=all-meat&paras=1&start-with-lorem=0")
42+ .flatMap { try $0.content.decode([String].self) }
43+ .do { (texts) in
44+ guard let text = texts.first else { return }
45+
46+ let p = Post.init(user: User(name: Name(first: "Акакий", second: "Программистович")), text: text)
47+
48+ postModel.insert(post: p)
49+
50+ eventBuffer.append(element: .add(p.id))
51+
52+
53+ OpenSockets.sockets.values.forEach { ws in
54+ ws.send(try! enc.encode(Events(error: nil, events: [.add(p.id)])))
55+ }
56+ }
57+ }
58+
59+ app.eventLoop.scheduleRepeatedTask(initialDelay: .seconds(205), delay: .seconds(10)) { (task: RepeatedTask) in
60+ postModel.deleteRandom(on: app)
61+ .whenSuccess({ (id) in
62+ eventBuffer.append(element: .delete(id))
63+
64+ OpenSockets.sockets.values.forEach {$0.send(try! enc.encode(Events(error: nil, events: [.delete(id)])))}
65+ })
66+ }
667}
Sources/App/configure.swift+26
@@ -29,5 +29,31 @@ public func configure(_ config: inout Config, _ env: inout Environment, _ servic
2929 var migrations = MigrationConfig()
3030 migrations.add(model: Todo.self, database: .sqlite)
3131 services.register(migrations)
32+
33+ let ws = NIOWebSocketServer.default()
34+
35+
36+ ws.get("postUpdates", Int.parameter) { (ws, req) in
37+ if let lastTry = try? req.parameters.next(Int.self) {
38+ eventBuffer.get(last: max(0, eventBuffer.index - lastTry), loop: req)
39+ .map {$0.compactMap {$0}}
40+ .map {Events.init(error: nil, events: $0)}
41+ .map {try JSONEncoder().encode($0)}
42+ .do { ws.send($0) }
43+ }
44+
45+ let socketID = OpenSockets.insert(ws)
46+
47+
48+// ws.onText({ (ws, text) in
49+// ws.send("\(id): \(text)")
50+// })
51+ _ = ws.onClose.always {
52+ print("closing")
53+ OpenSockets.remove(at: socketID)
54+ }
55+ }
56+
57+ services.register(ws, as: WebSocketServer.self)
3258
3359}
Sources/App/routes.swift+5−1
@@ -11,7 +11,11 @@ public func routes(_ router: Router) throws {
1111 router.get("hello") { req in
1212 return "Hello, world!"
1313 }
14−
14+
15+ router.get("post", Int.parameter) { req -> Future<Post> in
16+ return postModel.getPost(for: try req.parameters.next(Int.self), on: req)
17+ }
18+
1519 // Example of configuring a controller
1620 let todoController = TodoController()
1721 router.get("todos", use: todoController.index)