-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
WebServer.swift
88 lines (72 loc) · 3.81 KB
/
WebServer.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
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import GCDWebServers
import Shared
class WebServer {
private let log = Logger.browserLogger
static let WebServerSharedInstance = WebServer()
class var sharedInstance: WebServer {
return WebServerSharedInstance
}
let server: GCDWebServer = GCDWebServer()
var base: String {
return "http://localhost:\(server.port)"
}
/// The private credentials for accessing resources on this Web server.
let credentials: URLCredential
/// A random, transient token used for authenticating requests.
/// Other apps are able to make requests to our local Web server,
/// so this prevents them from accessing any resources.
fileprivate let sessionToken = UUID().uuidString
init() {
credentials = URLCredential(user: sessionToken, password: "", persistence: .forSession)
}
@discardableResult func start() throws -> Bool {
if !server.isRunning {
try server.start(options: [
GCDWebServerOption_Port: 6571,
GCDWebServerOption_BindToLocalhost: true,
GCDWebServerOption_AutomaticallySuspendInBackground: true,
GCDWebServerOption_AuthenticationMethod: GCDWebServerAuthenticationMethod_Basic,
GCDWebServerOption_AuthenticationAccounts: [sessionToken: ""]
])
}
return server.isRunning
}
/// Convenience method to register a dynamic handler. Will be mounted at $base/$module/$resource
func registerHandlerForMethod(_ method: String, module: String, resource: String, handler: @escaping (_ request: GCDWebServerRequest?) -> GCDWebServerResponse!) {
// Prevent serving content if the requested host isn't a whitelisted local host.
let wrappedHandler = {(request: GCDWebServerRequest?) -> GCDWebServerResponse? in
guard let request = request, request.url.isLocal else {
return GCDWebServerResponse(statusCode: 403)
}
return handler(request)
}
server.addHandler(forMethod: method, path: "/\(module)/\(resource)", request: GCDWebServerRequest.self, processBlock: wrappedHandler)
}
/// Convenience method to register a resource in the main bundle. Will be mounted at $base/$module/$resource
func registerMainBundleResource(_ resource: String, module: String) {
if let path = Bundle.main.path(forResource: resource, ofType: nil) {
server.addGETHandler(forPath: "/\(module)/\(resource)", filePath: path, isAttachment: false, cacheAge: UInt.max, allowRangeRequests: true)
}
}
/// Convenience method to register all resources in the main bundle of a specific type. Will be mounted at $base/$module/$resource
func registerMainBundleResourcesOfType(_ type: String, module: String) {
for path: String in Bundle.paths(forResourcesOfType: type, inDirectory: Bundle.main.bundlePath) {
if let resource = NSURL(string: path)?.lastPathComponent {
server.addGETHandler(forPath: "/\(module)/\(resource)", filePath: path as String, isAttachment: false, cacheAge: UInt.max, allowRangeRequests: true)
} else {
log.warning("Unable to locate resource at path: '\(path)'")
}
}
}
/// Return a full url, as a string, for a resource in a module. No check is done to find out if the resource actually exist.
func URLForResource(_ resource: String, module: String) -> String {
return "\(base)/\(module)/\(resource)"
}
func baseReaderModeURL() -> String {
return WebServer.sharedInstance.URLForResource("page", module: "reader-mode")
}
}