-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathJSONHubProtocol.swift
106 lines (93 loc) · 3.94 KB
/
JSONHubProtocol.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//
// JSONHubProtocol.swift
// SignalRClient
//
// Created by Pawel Kadluczka on 8/27/17.
// Copyright © 2017 Pawel Kadluczka. All rights reserved.
//
import Foundation
public class JSONHubProtocol: HubProtocol {
private static let recordSeparator = UInt8(0x1e)
private let encoder: JSONEncoder
private let decoder: JSONDecoder
private let logger: Logger
public let name = "json"
public let version = 1
public let type = ProtocolType.Text
public init(logger: Logger,
encoder: JSONEncoder = JSONEncoder(),
decoder: JSONDecoder = JSONDecoder()) {
self.logger = logger
self.encoder = encoder
self.decoder = decoder
}
public func parseMessages(input: Data) throws -> [HubMessage] {
let payloads = input.split(separator: JSONHubProtocol.recordSeparator)
// do not try to parse the last payload if it is not terminated with record separator
var count = payloads.count
if count > 0 && input.last != JSONHubProtocol.recordSeparator {
logger.log(logLevel: .warning, message: "Partial message received. Here be dragons...")
count = count - 1
}
logger.log(logLevel: .debug, message: "Payload contains \(count) message(s)")
return try payloads[0..<count].map{ try createHubMessage(payload: $0) }
}
public func createHubMessage(payload: Data) throws -> HubMessage {
logger.log(logLevel: .debug, message: "Message received: \(String(data: payload, encoding: .utf8) ?? "(empty)")")
do {
let messageType = try getMessageType(payload: payload)
switch messageType {
case .Invocation:
return try decoder.decode(ClientInvocationMessage.self, from: payload)
case .StreamItem:
return try decoder.decode(StreamItemMessage.self, from: payload)
case .Completion:
return try decoder.decode(CompletionMessage.self, from: payload)
case .Ping:
return PingMessage.instance
case .Close:
return try decoder.decode(CloseMessage.self, from: payload)
default:
logger.log(logLevel: .error, message: "Unsupported messageType: \(messageType)")
throw SignalRError.unknownMessageType
}
} catch {
throw SignalRError.protocolViolation(underlyingError: error)
}
}
private func getMessageType(payload: Data) throws -> MessageType {
struct MessageTypeHelper: Decodable {
let type: MessageType
private enum CodingKeys: String, CodingKey { case type }
}
do {
return try decoder.decode(MessageTypeHelper.self, from: payload).type
} catch {
logger.log(logLevel: .error, message: "Getting messageType failed: \(error)")
throw SignalRError.protocolViolation(underlyingError: error)
}
}
public func writeMessage(message: HubMessage) throws -> Data {
var payload = try createMessageData(message: message)
payload.append(JSONHubProtocol.recordSeparator)
return payload
}
private func createMessageData(message: HubMessage) throws -> Data {
switch message.type {
case .Invocation:
return try encoder.encode(message as! ServerInvocationMessage)
case .StreamItem:
return try encoder.encode(message as! StreamItemMessage)
case .StreamInvocation:
return try encoder.encode(message as! StreamInvocationMessage)
case .CancelInvocation:
return try encoder.encode(message as! CancelInvocationMessage)
case .Completion:
return try encoder.encode(message as! CompletionMessage)
case .Ping:
return try encoder.encode(message as! PingMessage)
default:
throw SignalRError.invalidOperation(message: "Unexpected MessageType.")
}
}
}