Add GeminiDataTask
This commit is contained in:
parent
befccfc38a
commit
87917687bf
|
@ -43,6 +43,8 @@
|
||||||
D664673624BD07F700B0B741 /* RenderingBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673524BD07F700B0B741 /* RenderingBlock.swift */; };
|
D664673624BD07F700B0B741 /* RenderingBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673524BD07F700B0B741 /* RenderingBlock.swift */; };
|
||||||
D664673824BD086F00B0B741 /* RenderingBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673724BD086F00B0B741 /* RenderingBlockView.swift */; };
|
D664673824BD086F00B0B741 /* RenderingBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673724BD086F00B0B741 /* RenderingBlockView.swift */; };
|
||||||
D664673A24BD0B8E00B0B741 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673924BD0B8E00B0B741 /* Fonts.swift */; };
|
D664673A24BD0B8E00B0B741 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673924BD0B8E00B0B741 /* Fonts.swift */; };
|
||||||
|
D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */; };
|
||||||
|
D69F00AE24BEA29100E37622 /* GeminiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AD24BEA29100E37622 /* GeminiResponse.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -186,6 +188,8 @@
|
||||||
D664673524BD07F700B0B741 /* RenderingBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlock.swift; sourceTree = "<group>"; };
|
D664673524BD07F700B0B741 /* RenderingBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlock.swift; sourceTree = "<group>"; };
|
||||||
D664673724BD086F00B0B741 /* RenderingBlockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlockView.swift; sourceTree = "<group>"; };
|
D664673724BD086F00B0B741 /* RenderingBlockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingBlockView.swift; sourceTree = "<group>"; };
|
||||||
D664673924BD0B8E00B0B741 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; };
|
D664673924BD0B8E00B0B741 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; };
|
||||||
|
D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiDataTask.swift; sourceTree = "<group>"; };
|
||||||
|
D69F00AD24BEA29100E37622 /* GeminiResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiResponse.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -309,7 +313,9 @@
|
||||||
D626649924BBF24100DF9B88 /* GeminiProtocol.swift */,
|
D626649924BBF24100DF9B88 /* GeminiProtocol.swift */,
|
||||||
D626649A24BBF24100DF9B88 /* GeminiRequest.swift */,
|
D626649A24BBF24100DF9B88 /* GeminiRequest.swift */,
|
||||||
D626649524BBF24000DF9B88 /* GeminiResponseHeader.swift */,
|
D626649524BBF24000DF9B88 /* GeminiResponseHeader.swift */,
|
||||||
|
D69F00AD24BEA29100E37622 /* GeminiResponse.swift */,
|
||||||
D626649724BBF24100DF9B88 /* GeminiConnection.swift */,
|
D626649724BBF24100DF9B88 /* GeminiConnection.swift */,
|
||||||
|
D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */,
|
||||||
);
|
);
|
||||||
path = GeminiProtocol;
|
path = GeminiProtocol;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -671,10 +677,12 @@
|
||||||
files = (
|
files = (
|
||||||
D62664A024BBF24100DF9B88 /* GeminiProtocol.swift in Sources */,
|
D62664A024BBF24100DF9B88 /* GeminiProtocol.swift in Sources */,
|
||||||
D626649E24BBF24100DF9B88 /* GeminiConnection.swift in Sources */,
|
D626649E24BBF24100DF9B88 /* GeminiConnection.swift in Sources */,
|
||||||
|
D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */,
|
||||||
D62664A124BBF24100DF9B88 /* GeminiRequest.swift in Sources */,
|
D62664A124BBF24100DF9B88 /* GeminiRequest.swift in Sources */,
|
||||||
D626649F24BBF24100DF9B88 /* NWParameters+Gemini.swift in Sources */,
|
D626649F24BBF24100DF9B88 /* NWParameters+Gemini.swift in Sources */,
|
||||||
D626649D24BBF24100DF9B88 /* Message+Gemini.swift in Sources */,
|
D626649D24BBF24100DF9B88 /* Message+Gemini.swift in Sources */,
|
||||||
D626649C24BBF24100DF9B88 /* GeminiResponseHeader.swift in Sources */,
|
D626649C24BBF24100DF9B88 /* GeminiResponseHeader.swift in Sources */,
|
||||||
|
D69F00AE24BEA29100E37622 /* GeminiResponse.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
var window: NSWindow!
|
var window: NSWindow!
|
||||||
|
|
||||||
var connection: GeminiConnection!
|
var task: GeminiDataTask!
|
||||||
let url = URL(string: "gemini://localhost:1965/overview.gmi")!
|
let url = URL(string: "gemini://localhost:1965/overview.gmi")!
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
|
@ -33,42 +33,21 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
window.title = "Gemini"
|
window.title = "Gemini"
|
||||||
window.makeKeyAndOrderFront(nil)
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
|
||||||
connection = GeminiConnection(endpoint: .url(url), delegate: self)
|
task = try! GeminiDataTask(url: url, completion: { (result) in
|
||||||
|
switch result {
|
||||||
|
case let .success(response):
|
||||||
|
print("Received response header: \(response.header)")
|
||||||
|
print("Received data: \(String(describing: response.body))")
|
||||||
|
print(response.bodyText.debugDescription)
|
||||||
|
case let .failure(error):
|
||||||
|
print("Error: \(error)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
task.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
// Insert code here to tear down your application
|
// Insert code here to tear down your application
|
||||||
}
|
}
|
||||||
|
|
||||||
var alreadyReceived = false
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate: GeminiConnectionDelegate {
|
|
||||||
func connectionReady(_ connection: GeminiConnection) {
|
|
||||||
print("!! Ready")
|
|
||||||
let req = try! GeminiRequest(url: url)
|
|
||||||
connection.sendRequest(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func connection(_ connection: GeminiConnection, receivedData data: Data?, header: GeminiResponseHeader) {
|
|
||||||
if !alreadyReceived {
|
|
||||||
alreadyReceived = true
|
|
||||||
print("!! Status: \(header.status)")
|
|
||||||
print("!! Meta: '\(header.meta)'")
|
|
||||||
}
|
|
||||||
if let data = data {
|
|
||||||
print("Received: \(data)")
|
|
||||||
print(String(data: data, encoding: .utf8)!.debugDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func connection(_ connection: GeminiConnection, handleError error: GeminiConnection.Error) {
|
|
||||||
print("!! connection error: \(error)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectionCompleted(_ connection: GeminiConnection) {
|
|
||||||
print("!! completed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -21,26 +21,22 @@ public class GeminiConnection {
|
||||||
|
|
||||||
public weak var delegate: GeminiConnectionDelegate?
|
public weak var delegate: GeminiConnectionDelegate?
|
||||||
|
|
||||||
private var connection: NWConnection?
|
private let connection: NWConnection
|
||||||
|
|
||||||
public init(endpoint: NWEndpoint, delegate: GeminiConnectionDelegate? = nil) {
|
public init(endpoint: NWEndpoint, delegate: GeminiConnectionDelegate? = nil) {
|
||||||
self.connection = NWConnection(to: endpoint, using: .gemini)
|
self.connection = NWConnection(to: endpoint, using: .gemini)
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
|
|
||||||
startConnection()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startConnection() {
|
public func startConnection() {
|
||||||
guard let connection = connection else { return }
|
|
||||||
|
|
||||||
connection.stateUpdateHandler = { (newState) in
|
connection.stateUpdateHandler = { (newState) in
|
||||||
switch newState {
|
switch newState {
|
||||||
case .ready:
|
case .ready:
|
||||||
print("\(connection) established")
|
print("\(self.connection) established")
|
||||||
self.delegate?.connectionReady(self)
|
self.delegate?.connectionReady(self)
|
||||||
case let .failed(error):
|
case let .failed(error):
|
||||||
print("\(connection) failed: \(error)")
|
print("\(self.connection) failed: \(error)")
|
||||||
connection.cancel()
|
self.connection.cancel()
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -50,22 +46,21 @@ public class GeminiConnection {
|
||||||
connection.start(queue: .main)
|
connection.start(queue: .main)
|
||||||
}
|
}
|
||||||
|
|
||||||
var report: NWConnection.PendingDataTransferReport!
|
public func cancelConnection() {
|
||||||
|
if connection.state != .cancelled {
|
||||||
|
connection.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func sendRequest(_ request: GeminiRequest) {
|
public func sendRequest(_ request: GeminiRequest) {
|
||||||
guard let connection = connection else { return }
|
|
||||||
|
|
||||||
let message = NWProtocolFramer.Message(geminiRequest: request)
|
let message = NWProtocolFramer.Message(geminiRequest: request)
|
||||||
let context = NWConnection.ContentContext(identifier: "GeminiRequest", metadata: [message])
|
let context = NWConnection.ContentContext(identifier: "GeminiRequest", metadata: [message])
|
||||||
// todo: should this really always be idempotent?
|
// todo: should this really always be idempotent?
|
||||||
connection.send(content: nil, contentContext: context, isComplete: true, completion: .contentProcessed({ (_) in }))
|
connection.send(content: nil, contentContext: context, isComplete: true, completion: .contentProcessed({ (_) in }))
|
||||||
receiveNextMessage()
|
receiveNextMessage()
|
||||||
report = connection.startDataTransferReport()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func receiveNextMessage() {
|
private func receiveNextMessage() {
|
||||||
guard let connection = connection else { return }
|
|
||||||
|
|
||||||
connection.receiveMessage { (data, context, isComplete, error) in
|
connection.receiveMessage { (data, context, isComplete, error) in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
self.delegate?.connection(self, handleError: error)
|
self.delegate?.connection(self, handleError: error)
|
||||||
|
@ -75,12 +70,9 @@ public class GeminiConnection {
|
||||||
delegate.connection(self, receivedData: data, header: header)
|
delegate.connection(self, receivedData: data, header: header)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isComplete {
|
guard isComplete else { fatalError("Connection should complete immediately") }
|
||||||
self.delegate?.connectionCompleted(self)
|
self.delegate?.connectionCompleted(self)
|
||||||
connection.cancel()
|
self.connection.cancel()
|
||||||
} else {
|
|
||||||
self.receiveNextMessage()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// GeminiDataTask.swift
|
||||||
|
// GeminiProtocol
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 7/14/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
|
||||||
|
public class GeminiDataTask {
|
||||||
|
public let request: GeminiRequest
|
||||||
|
private let completion: (Result<GeminiResponse, Error>) -> Void
|
||||||
|
|
||||||
|
private let connection: GeminiConnection
|
||||||
|
|
||||||
|
public private(set) var completed: Bool = false
|
||||||
|
|
||||||
|
public init(request: GeminiRequest, completion: @escaping (Result<GeminiResponse, Error>) -> Void) {
|
||||||
|
self.request = request
|
||||||
|
self.completion = completion
|
||||||
|
self.connection = GeminiConnection(endpoint: .url(request.url))
|
||||||
|
self.connection.delegate = self
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init(url: URL, completion: @escaping (Result<GeminiResponse, Error>) -> Void) throws {
|
||||||
|
self.init(request: try GeminiRequest(url: url), completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func resume() {
|
||||||
|
self.connection.startConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cancel() {
|
||||||
|
connection.cancelConnection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension GeminiDataTask {
|
||||||
|
enum Error: Swift.Error {
|
||||||
|
case noData
|
||||||
|
case connectionError(NWError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GeminiDataTask: GeminiConnectionDelegate {
|
||||||
|
public func connectionReady(_ connection: GeminiConnection) {
|
||||||
|
connection.sendRequest(self.request)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func connection(_ connection: GeminiConnection, receivedData data: Data?, header: GeminiResponseHeader) {
|
||||||
|
guard !completed else {
|
||||||
|
print("GeminiRequestTask(\(self)) has already completed, shouldn't be receiving any data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completed = true
|
||||||
|
if let data = data {
|
||||||
|
let response = GeminiResponse(header: header, body: data)
|
||||||
|
self.completion(.success(response))
|
||||||
|
} else {
|
||||||
|
self.completion(.failure(.noData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func connection(_ connection: GeminiConnection, handleError error: GeminiConnection.Error) {
|
||||||
|
guard !completed else {
|
||||||
|
print("GeminiRequestTask(\(self)) has already completed, shouldn't be receiving any data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completed = true
|
||||||
|
self.completion(.failure(.connectionError(error)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func connectionCompleted(_ connection: GeminiConnection) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,10 +11,18 @@ public struct GeminiRequest {
|
||||||
public let url: URL
|
public let url: URL
|
||||||
|
|
||||||
public init(url: URL) throws {
|
public init(url: URL) throws {
|
||||||
if url.absoluteString.count > 1024 {
|
guard var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { throw Error.invalidURL }
|
||||||
|
guard components.scheme == "gemini" else { throw Error.wrongProtocol }
|
||||||
|
|
||||||
|
if components.port == nil {
|
||||||
|
components.port = 1965
|
||||||
|
}
|
||||||
|
|
||||||
|
self.url = components.url!
|
||||||
|
|
||||||
|
if self.url.absoluteString.count > 1024 {
|
||||||
throw Error.urlTooLong
|
throw Error.urlTooLong
|
||||||
}
|
}
|
||||||
self.url = url
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var data: Data {
|
var data: Data {
|
||||||
|
@ -26,6 +34,8 @@ public struct GeminiRequest {
|
||||||
|
|
||||||
public extension GeminiRequest {
|
public extension GeminiRequest {
|
||||||
enum Error: Swift.Error {
|
enum Error: Swift.Error {
|
||||||
|
case invalidURL
|
||||||
|
case wrongProtocol
|
||||||
case urlTooLong
|
case urlTooLong
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// GeminiResponse.swift
|
||||||
|
// GeminiProtocol
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 7/14/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
public struct GeminiResponse {
|
||||||
|
public let header: GeminiResponseHeader
|
||||||
|
public let body: Data?
|
||||||
|
|
||||||
|
public var status: GeminiResponseHeader.StatusCode { header.status }
|
||||||
|
public var meta: String { header.meta }
|
||||||
|
|
||||||
|
public var rawMimeType: String? {
|
||||||
|
guard status.isSuccess else { return nil }
|
||||||
|
return meta.trimmingCharacters(in: .whitespaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var mimeType: String? {
|
||||||
|
guard let rawMimeType = rawMimeType else { return nil }
|
||||||
|
return rawMimeType.split(separator: ";").first?.trimmingCharacters(in: .whitespaces)
|
||||||
|
}
|
||||||
|
public var mimeTypeParameters: [String: String]? {
|
||||||
|
guard let rawMimeType = rawMimeType else { return nil }
|
||||||
|
return rawMimeType.split(separator: ";").dropFirst().reduce(into: [String: String]()) { (parameters, parameter) in
|
||||||
|
let parts = parameter.split(separator: "=").map { $0.trimmingCharacters(in: .whitespaces) }
|
||||||
|
precondition(parts.count == 2)
|
||||||
|
parameters[parts[0].lowercased()] = parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@available(macOS 10.16, *)
|
||||||
|
public var utiType: UTType? {
|
||||||
|
guard let mimeType = mimeType else { return nil }
|
||||||
|
return UTType.types(tag: mimeType, tagClass: .mimeType, conformingTo: nil).first
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bodyText: String? {
|
||||||
|
guard let body = body, let parameters = mimeTypeParameters else { return nil }
|
||||||
|
let encoding: String.Encoding
|
||||||
|
switch parameters["charset"]?.lowercased() {
|
||||||
|
case nil, "utf-8":
|
||||||
|
// The Gemini spec defines UTF-8 to be the default charset.
|
||||||
|
encoding = .utf8
|
||||||
|
case "us-ascii":
|
||||||
|
encoding = .ascii
|
||||||
|
default:
|
||||||
|
// todo: log warning
|
||||||
|
encoding = .utf8
|
||||||
|
}
|
||||||
|
return String(data: body, encoding: encoding)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,33 +6,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
public struct GeminiResponseHeader {
|
public struct GeminiResponseHeader {
|
||||||
public let status: StatusCode
|
public let status: StatusCode
|
||||||
public let meta: String
|
public let meta: String
|
||||||
|
|
||||||
public var rawMimeType: String? {
|
|
||||||
guard status.isSuccess else { return nil }
|
|
||||||
return meta.trimmingCharacters(in: .whitespaces)
|
|
||||||
}
|
|
||||||
var mimeType: String? {
|
|
||||||
guard let rawMimeType = rawMimeType else { return nil }
|
|
||||||
return rawMimeType.split(separator: ";").first?.trimmingCharacters(in: .whitespaces)
|
|
||||||
}
|
|
||||||
var mimeTypeParameters: [String: String]? {
|
|
||||||
guard let rawMimeType = rawMimeType else { return nil }
|
|
||||||
return rawMimeType.split(separator: ";").dropFirst().reduce(into: [String: String]()) { (parameters, parameter) in
|
|
||||||
let parts = parameter.split(separator: "=").map { $0.trimmingCharacters(in: .whitespaces) }
|
|
||||||
precondition(parts.count == 2)
|
|
||||||
parameters[parts[0].lowercased()] = parts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@available(macOS 10.16, *)
|
|
||||||
var utiType: UTType? {
|
|
||||||
guard let mimeType = mimeType else { return nil }
|
|
||||||
return UTType.types(tag: mimeType, tagClass: .mimeType, conformingTo: nil).first
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension GeminiResponseHeader {
|
public extension GeminiResponseHeader {
|
||||||
|
@ -74,41 +51,41 @@ extension GeminiResponseHeader.StatusCode: CustomStringConvertible {
|
||||||
public var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .input:
|
case .input:
|
||||||
return "input"
|
return "\(rawValue): input"
|
||||||
case .sensitiveInput:
|
case .sensitiveInput:
|
||||||
return "sensitiveInput"
|
return "\(rawValue): sensitiveInput"
|
||||||
case .success:
|
case .success:
|
||||||
return "success"
|
return "\(rawValue): success"
|
||||||
case .temporaryRedirect:
|
case .temporaryRedirect:
|
||||||
return "temporaryRedirect"
|
return "\(rawValue): temporaryRedirect"
|
||||||
case .permanentRedirect:
|
case .permanentRedirect:
|
||||||
return "permanentRedirect"
|
return "\(rawValue): permanentRedirect"
|
||||||
case .temporaryFailure:
|
case .temporaryFailure:
|
||||||
return "temporaryFailure"
|
return "\(rawValue): temporaryFailure"
|
||||||
case .serverUnavailable:
|
case .serverUnavailable:
|
||||||
return "serverUnavailable"
|
return "\(rawValue): serverUnavailable"
|
||||||
case .cgiError:
|
case .cgiError:
|
||||||
return "cgiError"
|
return "\(rawValue): cgiError"
|
||||||
case .proxyError:
|
case .proxyError:
|
||||||
return "proxyError"
|
return "\(rawValue): proxyError"
|
||||||
case .slowDown:
|
case .slowDown:
|
||||||
return "slowDown"
|
return "\(rawValue): slowDown"
|
||||||
case .permanentFailure:
|
case .permanentFailure:
|
||||||
return "permanentFailure"
|
return "\(rawValue): permanentFailure"
|
||||||
case .notFound:
|
case .notFound:
|
||||||
return "notFound"
|
return "\(rawValue): notFound"
|
||||||
case .gone:
|
case .gone:
|
||||||
return "gone"
|
return "\(rawValue): gone"
|
||||||
case .proxyRequestRefused:
|
case .proxyRequestRefused:
|
||||||
return "proxyRequestRefused"
|
return "\(rawValue): proxyRequestRefused"
|
||||||
case .badRequest:
|
case .badRequest:
|
||||||
return "badRequest"
|
return "\(rawValue): badRequest"
|
||||||
case .clientCertificateRequested:
|
case .clientCertificateRequested:
|
||||||
return "clientCertificateRequested"
|
return "\(rawValue): clientCertificateRequested"
|
||||||
case .certificateNotAuthorised:
|
case .certificateNotAuthorised:
|
||||||
return "certificateNotAuthorised"
|
return "\(rawValue): certificateNotAuthorised"
|
||||||
case .certificateNotValid:
|
case .certificateNotValid:
|
||||||
return "certificateNotValid"
|
return "\(rawValue): certificateNotValid"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue