mirror of https://github.com/osnr/TabFS.git
safari: TabFSServer subprocess that can live long. fixes bug where fs would die after a minute or two
This commit is contained in:
parent
9b4abc40ee
commit
2f639e2a02
|
@ -20,3 +20,12 @@ sites. It should be running now! (?)
|
|||
|
||||
Check the `fs/mnt` folder of the TabFS repo on your computer to see if
|
||||
it's mounted.
|
||||
|
||||
### tips
|
||||
|
||||
- To open Web inspector: Safari -> Develop menu -> Web Extension
|
||||
Background Pages -> TabFS
|
||||
|
||||
- You need to rebuild if you change background.js. This is pretty
|
||||
annoying.
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
|
|||
guard let message = item.userInfo?["message"] as? [AnyHashable: Any] else { return }
|
||||
|
||||
guard message["op"] as! String == "safari_did_connect" else { return }
|
||||
|
||||
|
||||
// The XPC service is a subprocess that lives outside the macOS App Sandbox.
|
||||
// (Safari extension native code, including this file, has to live in the sandbox.)
|
||||
// It can do forbidden things like spawn tabfs filesystem and set up WebSocket server.
|
||||
|
|
|
@ -9,10 +9,11 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
F028D2B625D0B7370095C2D5 /* TabFSService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028D2B525D0B7370095C2D5 /* TabFSService.swift */; };
|
||||
F028D2B825D0B7370095C2D5 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028D2B725D0B7370095C2D5 /* main.swift */; };
|
||||
F028D2BC25D0B7370095C2D5 /* TabFSService.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = F028D2B125D0B7370095C2D5 /* TabFSService.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
F028D2D725D0B8500095C2D5 /* TabFSServiceProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028D2B325D0B7370095C2D5 /* TabFSServiceProtocols.swift */; };
|
||||
F028D2DE25D0B8590095C2D5 /* TabFSServiceProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028D2B325D0B7370095C2D5 /* TabFSServiceProtocols.swift */; };
|
||||
F028D2ED25D106F10095C2D5 /* TabFSService.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = F028D2B125D0B7370095C2D5 /* TabFSService.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
F028D30125D17B080095C2D5 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = F028D30025D17B080095C2D5 /* main.swift */; };
|
||||
F028D34525D17D6A0095C2D5 /* TabFSServer in CopyFiles */ = {isa = PBXBuildFile; fileRef = F028D2FE25D17B080095C2D5 /* TabFSServer */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
F04429F625C7507200D998A5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04429F525C7507200D998A5 /* AppDelegate.swift */; };
|
||||
F04429F925C7507200D998A5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F04429F725C7507200D998A5 /* Main.storyboard */; };
|
||||
F04429FB25C7507200D998A5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04429FA25C7507200D998A5 /* ViewController.swift */; };
|
||||
|
@ -27,12 +28,12 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
F028D2BA25D0B7370095C2D5 /* PBXContainerItemProxy */ = {
|
||||
F028D33725D17D100095C2D5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = F04429E925C7507200D998A5 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = F028D2B025D0B7370095C2D5;
|
||||
remoteInfo = TabFSService;
|
||||
remoteGlobalIDString = F028D2FD25D17B080095C2D5;
|
||||
remoteInfo = TabFSServer;
|
||||
};
|
||||
F0442A0525C7507400D998A5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
|
@ -44,17 +45,6 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
F028D2BD25D0B7370095C2D5 /* Embed XPC Services */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
F028D2BC25D0B7370095C2D5 /* TabFSService.xpc in Embed XPC Services */,
|
||||
);
|
||||
name = "Embed XPC Services";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F028D2E525D106BB0095C2D5 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -65,6 +55,25 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F028D2FC25D17B080095C2D5 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
F028D30E25D17BD20095C2D5 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 7;
|
||||
files = (
|
||||
F028D34525D17D6A0095C2D5 /* TabFSServer in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F0442A1425C7507400D998A5 /* Embed App Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -84,6 +93,8 @@
|
|||
F028D2B525D0B7370095C2D5 /* TabFSService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabFSService.swift; sourceTree = "<group>"; };
|
||||
F028D2B725D0B7370095C2D5 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
F028D2B925D0B7370095C2D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
F028D2FE25D17B080095C2D5 /* TabFSServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TabFSServer; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F028D30025D17B080095C2D5 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
F04429F125C7507200D998A5 /* TabFS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabFS.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F04429F425C7507200D998A5 /* TabFS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TabFS.entitlements; sourceTree = "<group>"; };
|
||||
F04429F525C7507200D998A5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -110,6 +121,13 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F028D2FB25D17B080095C2D5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F04429EE25C7507200D998A5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -139,12 +157,21 @@
|
|||
path = TabFSService;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F028D2FF25D17B080095C2D5 /* TabFSServer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F028D30025D17B080095C2D5 /* main.swift */,
|
||||
);
|
||||
path = TabFSServer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F04429E825C7507200D998A5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F04429F325C7507200D998A5 /* TabFS */,
|
||||
F0442A0A25C7507400D998A5 /* TabFS Extension */,
|
||||
F028D2B225D0B7370095C2D5 /* TabFSService */,
|
||||
F028D2FF25D17B080095C2D5 /* TabFSServer */,
|
||||
F0442A0725C7507400D998A5 /* Frameworks */,
|
||||
F04429F225C7507200D998A5 /* Products */,
|
||||
);
|
||||
|
@ -156,6 +183,7 @@
|
|||
F04429F125C7507200D998A5 /* TabFS.app */,
|
||||
F0442A0325C7507400D998A5 /* TabFS Extension.appex */,
|
||||
F028D2B125D0B7370095C2D5 /* TabFSService.xpc */,
|
||||
F028D2FE25D17B080095C2D5 /* TabFSServer */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -214,16 +242,35 @@
|
|||
F028D2AD25D0B7370095C2D5 /* Sources */,
|
||||
F028D2AE25D0B7370095C2D5 /* Frameworks */,
|
||||
F028D2AF25D0B7370095C2D5 /* Resources */,
|
||||
F028D30E25D17BD20095C2D5 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
F028D33825D17D100095C2D5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = TabFSService;
|
||||
productName = TabFSService;
|
||||
productReference = F028D2B125D0B7370095C2D5 /* TabFSService.xpc */;
|
||||
productType = "com.apple.product-type.xpc-service";
|
||||
};
|
||||
F028D2FD25D17B080095C2D5 /* TabFSServer */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F028D30425D17B090095C2D5 /* Build configuration list for PBXNativeTarget "TabFSServer" */;
|
||||
buildPhases = (
|
||||
F028D2FA25D17B080095C2D5 /* Sources */,
|
||||
F028D2FB25D17B080095C2D5 /* Frameworks */,
|
||||
F028D2FC25D17B080095C2D5 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = TabFSServer;
|
||||
productName = TabFSServer;
|
||||
productReference = F028D2FE25D17B080095C2D5 /* TabFSServer */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
F04429F025C7507200D998A5 /* TabFS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F0442A1525C7507400D998A5 /* Build configuration list for PBXNativeTarget "TabFS" */;
|
||||
|
@ -232,13 +279,11 @@
|
|||
F04429EE25C7507200D998A5 /* Frameworks */,
|
||||
F04429EF25C7507200D998A5 /* Resources */,
|
||||
F0442A1425C7507400D998A5 /* Embed App Extensions */,
|
||||
F028D2BD25D0B7370095C2D5 /* Embed XPC Services */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
F0442A0625C7507400D998A5 /* PBXTargetDependency */,
|
||||
F028D2BB25D0B7370095C2D5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = TabFS;
|
||||
productName = TabFS;
|
||||
|
@ -275,6 +320,9 @@
|
|||
F028D2B025D0B7370095C2D5 = {
|
||||
CreatedOnToolsVersion = 12.1;
|
||||
};
|
||||
F028D2FD25D17B080095C2D5 = {
|
||||
CreatedOnToolsVersion = 12.1;
|
||||
};
|
||||
F04429F025C7507200D998A5 = {
|
||||
CreatedOnToolsVersion = 12.1;
|
||||
};
|
||||
|
@ -299,6 +347,7 @@
|
|||
F04429F025C7507200D998A5 /* TabFS */,
|
||||
F0442A0225C7507400D998A5 /* TabFS Extension */,
|
||||
F028D2B025D0B7370095C2D5 /* TabFSService */,
|
||||
F028D2FD25D17B080095C2D5 /* TabFSServer */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -344,6 +393,14 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F028D2FA25D17B080095C2D5 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F028D30125D17B080095C2D5 /* main.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F04429ED25C7507200D998A5 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -365,10 +422,10 @@
|
|||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
F028D2BB25D0B7370095C2D5 /* PBXTargetDependency */ = {
|
||||
F028D33825D17D100095C2D5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = F028D2B025D0B7370095C2D5 /* TabFSService */;
|
||||
targetProxy = F028D2BA25D0B7370095C2D5 /* PBXContainerItemProxy */;
|
||||
target = F028D2FD25D17B080095C2D5 /* TabFSServer */;
|
||||
targetProxy = F028D33725D17D100095C2D5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
F0442A0625C7507400D998A5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
|
@ -421,6 +478,28 @@
|
|||
};
|
||||
name = Release;
|
||||
};
|
||||
F028D30225D17B080095C2D5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 75YA78K5AM;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F028D30325D17B080095C2D5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 75YA78K5AM;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F0442A0F25C7507400D998A5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
@ -628,6 +707,15 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
F028D30425D17B090095C2D5 /* Build configuration list for PBXNativeTarget "TabFSServer" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
F028D30225D17B080095C2D5 /* Debug */,
|
||||
F028D30325D17B080095C2D5 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
F04429EC25C7507200D998A5 /* Build configuration list for PBXProject "TabFS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
|
Binary file not shown.
|
@ -7,13 +7,18 @@
|
|||
<key>TabFS.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>TabFSService.xcscheme_^#shared#^_</key>
|
||||
<key>TabFSServer.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>TabFSService.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// main.swift
|
||||
// TabFSServer
|
||||
//
|
||||
// Created by Omar Rizwan on 2/8/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import os.log
|
||||
|
||||
class TabFSServer {
|
||||
|
||||
var fs: Process!
|
||||
var fsInput: FileHandle!
|
||||
var fsOutput: FileHandle!
|
||||
func startFs() {
|
||||
let fileURL = URL(fileURLWithPath: #filePath)
|
||||
let repoURL = fileURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
|
||||
|
||||
fs = Process()
|
||||
fs.executableURL = repoURL.appendingPathComponent("fs").appendingPathComponent("tabfs")
|
||||
fs.currentDirectoryURL = fs.executableURL?.deletingLastPathComponent()
|
||||
|
||||
fs.arguments = []
|
||||
|
||||
let inputPipe = Pipe(), outputPipe = Pipe()
|
||||
fs.standardInput = inputPipe
|
||||
fs.standardOutput = outputPipe
|
||||
|
||||
fsInput = inputPipe.fileHandleForWriting
|
||||
fsOutput = outputPipe.fileHandleForReading
|
||||
|
||||
try! fs.run()
|
||||
}
|
||||
|
||||
var ws: NWListener!
|
||||
func startWs() {
|
||||
// TODO: randomly generate port and report back to caller?
|
||||
let port = NWEndpoint.Port(rawValue: 9991)!
|
||||
|
||||
let parameters = NWParameters(tls: nil)
|
||||
parameters.allowLocalEndpointReuse = true
|
||||
parameters.includePeerToPeer = true
|
||||
// for security ? so people outside your computer can't hijack TabFS at least
|
||||
parameters.requiredInterfaceType = .loopback
|
||||
|
||||
let opts = NWProtocolWebSocket.Options()
|
||||
opts.autoReplyPing = true
|
||||
parameters.defaultProtocolStack.applicationProtocols.insert(opts, at: 0)
|
||||
|
||||
ws = try! NWListener(using: parameters, on: port)
|
||||
ws.start(queue: .main)
|
||||
}
|
||||
|
||||
init() {
|
||||
startFs()
|
||||
startWs()
|
||||
|
||||
var handleRequest: ((_ req: Data) -> Void)?
|
||||
ws.newConnectionHandler = { conn in
|
||||
conn.start(queue: .main)
|
||||
|
||||
handleRequest = { req in
|
||||
let metaData = NWProtocolWebSocket.Metadata(opcode: .text)
|
||||
let context = NWConnection.ContentContext(identifier: "context", metadata: [metaData])
|
||||
conn.send(content: req, contentContext: context, completion: .contentProcessed({ err in
|
||||
if err != nil {
|
||||
os_log(.default, "req %{public}@ error: %{public}@", String(data: req, encoding: .utf8)!, err!.debugDescription as CVarArg)
|
||||
// FIXME: ERROR
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func read() {
|
||||
conn.receiveMessage { (resp, context, isComplete, err) in
|
||||
guard let resp = resp else {
|
||||
// FIXME err
|
||||
os_log(.default, "resp error: %{public}@", err!.debugDescription as CVarArg)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the response back to tabfs.c.
|
||||
self.fsInput.write(withUnsafeBytes(of: UInt32(resp.count)) { Data($0) })
|
||||
self.fsInput.write(resp)
|
||||
read()
|
||||
}
|
||||
}
|
||||
read()
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
while true {
|
||||
// Blocking read from the tabfs process.
|
||||
let length = self.fsOutput.readData(ofLength: 4).withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
let req = self.fsOutput.readData(ofLength: Int(length))
|
||||
|
||||
if let handleRequest = handleRequest {
|
||||
// Send the request over the WebSocket connection to background.js in browser.
|
||||
handleRequest(req)
|
||||
} else {
|
||||
// FIXME: ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: notify
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let server = TabFSServer()
|
||||
dispatchMain()
|
|
@ -10,105 +10,15 @@ import Network
|
|||
import os.log
|
||||
|
||||
class TabFSService: NSObject, TabFSServiceProtocol {
|
||||
var fs: Process!
|
||||
var fsInput: FileHandle!
|
||||
var fsOutput: FileHandle!
|
||||
func startFs() {
|
||||
let fileURL = URL(fileURLWithPath: #filePath)
|
||||
let repoURL = fileURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
|
||||
|
||||
fs = Process()
|
||||
fs.executableURL = repoURL.appendingPathComponent("fs").appendingPathComponent("tabfs")
|
||||
fs.currentDirectoryURL = fs.executableURL?.deletingLastPathComponent()
|
||||
|
||||
fs.arguments = []
|
||||
|
||||
let inputPipe = Pipe(), outputPipe = Pipe()
|
||||
fs.standardInput = inputPipe
|
||||
fs.standardOutput = outputPipe
|
||||
|
||||
fsInput = inputPipe.fileHandleForWriting
|
||||
fsOutput = outputPipe.fileHandleForReading
|
||||
|
||||
try! fs.run()
|
||||
}
|
||||
|
||||
var ws: NWListener!
|
||||
func startWs() {
|
||||
// TODO: randomly generate port and report back to caller?
|
||||
let port = NWEndpoint.Port(rawValue: 9991)!
|
||||
|
||||
let parameters = NWParameters(tls: nil)
|
||||
parameters.allowLocalEndpointReuse = true
|
||||
parameters.includePeerToPeer = true
|
||||
// for security ? so people outside your computer can't hijack TabFS at least
|
||||
parameters.requiredInterfaceType = .loopback
|
||||
|
||||
let opts = NWProtocolWebSocket.Options()
|
||||
opts.autoReplyPing = true
|
||||
parameters.defaultProtocolStack.applicationProtocols.insert(opts, at: 0)
|
||||
|
||||
ws = try! NWListener(using: parameters, on: port)
|
||||
ws.start(queue: .main)
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
startFs()
|
||||
startWs()
|
||||
|
||||
var handleRequest: ((_ req: Data) -> Void)?
|
||||
ws.newConnectionHandler = { conn in
|
||||
conn.start(queue: .main)
|
||||
|
||||
handleRequest = { req in
|
||||
let metaData = NWProtocolWebSocket.Metadata(opcode: .text)
|
||||
let context = NWConnection.ContentContext(identifier: "context", metadata: [metaData])
|
||||
conn.send(content: req, contentContext: context, completion: .contentProcessed({ err in
|
||||
if err != nil {
|
||||
os_log(.default, "req %{public}@ error: %{public}@", String(data: req, encoding: .utf8)!, err!.debugDescription as CVarArg)
|
||||
// FIXME: ERROR
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func read() {
|
||||
conn.receiveMessage { (resp, context, isComplete, err) in
|
||||
guard let resp = resp else {
|
||||
// FIXME err
|
||||
os_log(.default, "resp error: %{public}@", err!.debugDescription as CVarArg)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the response back to tabfs.c.
|
||||
self.fsInput.write(withUnsafeBytes(of: UInt32(resp.count)) { Data($0) })
|
||||
self.fsInput.write(resp)
|
||||
read()
|
||||
}
|
||||
}
|
||||
read()
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
while true {
|
||||
// Blocking read from the tabfs process.
|
||||
let length = self.fsOutput.readData(ofLength: 4).withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
let req = self.fsOutput.readData(ofLength: Int(length))
|
||||
|
||||
if let handleRequest = handleRequest {
|
||||
// Send the request over the WebSocket connection to background.js in browser.
|
||||
handleRequest(req)
|
||||
} else {
|
||||
// FIXME: ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME: disable auto termination
|
||||
}
|
||||
|
||||
func start(withReply reply: @escaping () -> Void) {
|
||||
// This XPC call is enough to just force the XPC service to be started.
|
||||
os_log("HELLO")
|
||||
let server = Process()
|
||||
os_log("HOW ARE YOU?")
|
||||
server.executableURL = Bundle.main.url(forResource: "TabFSServer", withExtension: "")!
|
||||
os_log("I AM GOOD")
|
||||
server.launch()
|
||||
os_log("GREAT")
|
||||
reply()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue