mirror of
https://github.com/falk-werner/webfuse
synced 2024-10-27 20:34:10 +00:00
add javascript example
This commit is contained in:
parent
17d6275d96
commit
acbcbc1d97
@ -3,15 +3,35 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Webfuse Example</title>
|
<title>Webfuse Example</title>
|
||||||
<script type="module" src="js/startup.js"></script>
|
<script type="module" src="js/startup.js"></script>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Webfuse</h1>
|
<h1>Webfuse Example</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This example provides a single file "README.md", which
|
||||||
|
contents can be altered below.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Connection</h2>
|
||||||
<p>
|
<p>
|
||||||
<label for="url">Url:</label>
|
<label for="url">Url:</label>
|
||||||
<input type="text" id="url" value="ws://localhost:8081"/>
|
<input type="text" id="url" value="ws://localhost:8081"/>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="token">Token:</label>
|
||||||
|
<input type="text" id="token" value="" />
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input type="button" id="connect" value="Connect"/>
|
<input type="button" id="connect" value="Connect"/>
|
||||||
|
<span id="state">disconnected</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>README.md</h2>
|
||||||
|
<p>
|
||||||
|
<textarea id="contents"># Webfuse
|
||||||
|
This is a sample text.
|
||||||
|
</textarea>
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
104
example/provider/javascript/js/filesystem.js
Normal file
104
example/provider/javascript/js/filesystem.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
|
||||||
|
import { BaseFileSystem, ERRNO, Mode, AccessMode, OpenFlags } from "./webfuse/webfuse.js"
|
||||||
|
|
||||||
|
class FileSystem extends BaseFileSystem {
|
||||||
|
|
||||||
|
constructor(tokenProvider, stateListener, files) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.tokenProvider = tokenProvider;
|
||||||
|
this.stateListener = stateListener
|
||||||
|
this.files = new Map();
|
||||||
|
for(const file of files) {
|
||||||
|
this.files.set("/" + file.name, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
access(path, mode) {
|
||||||
|
// we do not allow write or execute
|
||||||
|
if ((mode & AccessMode.W_OK) || (mode & AccessMode.X_OK)) {
|
||||||
|
return ERRNO.EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((path = "/") || (this.files.has(path))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERRNO.ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
getattr(path) {
|
||||||
|
if (path == "/") {
|
||||||
|
return {
|
||||||
|
nlink: 2,
|
||||||
|
mode: Mode.DIR | 0o555
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (this.files.has(path)) {
|
||||||
|
const file = this.files.get(path);
|
||||||
|
const contents = file.contents();
|
||||||
|
return {
|
||||||
|
nlink: 1,
|
||||||
|
mode: Mode.REG | 0o444,
|
||||||
|
size: contents.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERRNO.ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
readdir(path) {
|
||||||
|
if (path == "/") {
|
||||||
|
const list = [];
|
||||||
|
for(const file of this.files.values()) {
|
||||||
|
list.push(file.name);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERRNO.ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
open(path, flags) {
|
||||||
|
if (this.files.has(path)) {
|
||||||
|
const accessMode = flags & OpenFlags.ACCESS_MODE;
|
||||||
|
if (accessMode == OpenFlags.RDONLY) {
|
||||||
|
return [0, 0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [ERRNO.EPERM, 0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ERRNO.ENOENT, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
read(path, size, offset, fd) {
|
||||||
|
if (this.files.has(path)) {
|
||||||
|
const file = this.files.get(path);
|
||||||
|
const contents = file.contents();
|
||||||
|
if (offset < contents.length) {
|
||||||
|
const available = contents.length - offset;
|
||||||
|
const length = (size < available) ? size : available;
|
||||||
|
const data = contents.slice(offset, offset + length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ERRNO.EBADF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getcreds() {
|
||||||
|
const token = this.tokenProvider();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionstatechanged(state) {
|
||||||
|
this.stateListener(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { FileSystem }
|
@ -1,19 +1,40 @@
|
|||||||
|
|
||||||
import { Webfuse } from "./webfuse/webfuse.js";
|
import { Webfuse } from "./webfuse/webfuse.js";
|
||||||
import { StaticFileSystem } from "./static_filesystem.js";
|
import { FileSystem } from "./filesystem.js";
|
||||||
|
|
||||||
|
|
||||||
|
function encode(value) {
|
||||||
|
const encoder = new TextEncoder('utf-8');
|
||||||
|
return encoder.encode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_contents() {
|
||||||
|
const contentTextArea = document.querySelector("#contents");
|
||||||
|
const contents = contentTextArea.value;
|
||||||
|
return encode(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_token() {
|
||||||
|
const tokenTextfield = document.querySelector('#token');
|
||||||
|
const token = tokenTextfield.value;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_state(state) {
|
||||||
|
const stateTextField = document.querySelector("#state");
|
||||||
|
stateTextField.textContent = (state == "connected") ? "connected" : "disconnected";
|
||||||
|
}
|
||||||
|
|
||||||
let webfuse = null;
|
let webfuse = null;
|
||||||
const filesystem = new StaticFileSystem(new Map([
|
const filesystem = new FileSystem(get_token, update_state, [
|
||||||
["/foo", "foo"],
|
{name: "README.md", contents: get_contents }
|
||||||
["/bar", "foo"]
|
]);
|
||||||
]));
|
|
||||||
|
|
||||||
function onConnectButtonClicked() {
|
function onConnectButtonClicked() {
|
||||||
if (webfuse) { webfuse.close(); }
|
if (webfuse) { webfuse.close(); }
|
||||||
|
|
||||||
const urlTextfield = document.querySelector('#url');
|
const urlTextfield = document.querySelector('#url');
|
||||||
const url = urlTextfield.value;
|
const url = urlTextfield.value;
|
||||||
console.log(url);
|
|
||||||
|
|
||||||
webfuse = new Webfuse(url, filesystem);
|
webfuse = new Webfuse(url, filesystem);
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
|
|
||||||
import { BaseFileSystem, ERRNO, Mode } from "./webfuse/webfuse.js"
|
|
||||||
|
|
||||||
class StaticFileSystem extends BaseFileSystem {
|
|
||||||
|
|
||||||
constructor(files) {
|
|
||||||
super();
|
|
||||||
this.files = files;
|
|
||||||
}
|
|
||||||
|
|
||||||
getattr(path) {
|
|
||||||
console.log("getattr", path);
|
|
||||||
|
|
||||||
if (path == "/") {
|
|
||||||
return {
|
|
||||||
nlink: 2,
|
|
||||||
mode: Mode.DIR | 0o555
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (this.files.has(path)) {
|
|
||||||
const contents = this.files.get(path);
|
|
||||||
return {
|
|
||||||
nlink: 1,
|
|
||||||
mode: Mode.REG | 0o444,
|
|
||||||
size: contents.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERRNO.ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
readdir(path) {
|
|
||||||
if (path == "/") {
|
|
||||||
return ["foo", "bar"]
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERRNO.ENOENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { StaticFileSystem }
|
|
@ -27,15 +27,19 @@ class BaseFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
chmod(path, mode) {
|
chmod(path, mode) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
chown(path, uid, gid) {
|
chown(path, uid, gid) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
truncate(path, size, fd) {
|
truncate(path, size, fd) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsync(path, isDataSync, fd) {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
open(path, flags) {
|
open(path, flags) {
|
||||||
@ -43,31 +47,31 @@ class BaseFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mknod(path, mode, rdev) {
|
mknod(path, mode, rdev) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
create(path, mode) {
|
create(path, mode) {
|
||||||
return [ERNNO.ENOEND, 0];
|
return [ERRNO.EPERM, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
release(path, fd) {
|
release(path, fd) {
|
||||||
return ERRNO.ENOENT;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink(path) {
|
unlink(path) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
read(path, size, offset, fd) {
|
read(path, size, offset, fd) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
write(path, data, offset, fd) {
|
write(path, data, offset, fd) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir(path, mode) {
|
mkdir(path, mode) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
readdir(path) {
|
readdir(path) {
|
||||||
@ -75,16 +79,24 @@ class BaseFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rmdir(path) {
|
rmdir(path) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
statfs(path) {
|
statfs(path) {
|
||||||
return ERRNO.ENOENT;
|
return ERRNO.ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
utimens(path, atime, mtime) {
|
||||||
|
return ERRNO.ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
getcreds() {
|
getcreds() {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectionstatechanged(state) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { BaseFileSystem }
|
export { BaseFileSystem }
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
class MessageReader {
|
class MessageReader {
|
||||||
|
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
// console.log(new Uint8Array(data));
|
|
||||||
this.raw = data;
|
this.raw = data;
|
||||||
this.data = new DataView(data);
|
this.data = new DataView(data);
|
||||||
this.pos = 0;
|
this.pos = 0;
|
||||||
|
@ -67,7 +67,6 @@ class MessageWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_data() {
|
get_data() {
|
||||||
// console.log(this.data)
|
|
||||||
return new Uint8Array(this.data);
|
return new Uint8Array(this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
example/provider/javascript/js/webfuse/openflags.js
Normal file
28
example/provider/javascript/js/webfuse/openflags.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
const OpenFlags = {
|
||||||
|
ACCESS_MODE: 0x03,
|
||||||
|
RDONLY : 0o00,
|
||||||
|
WRONLY : 0o01,
|
||||||
|
RDWR : 0o02,
|
||||||
|
|
||||||
|
APPEND : 0o00002000,
|
||||||
|
ASYNC : 0o00020000,
|
||||||
|
CLOEXEC : 0o02000000,
|
||||||
|
CREAT : 0o00000100,
|
||||||
|
DIRECT : 0o00040000,
|
||||||
|
DIRECTORY : 0o00200000,
|
||||||
|
DSYNC : 0o00010000,
|
||||||
|
EXCL : 0o00000200,
|
||||||
|
LARGEFILE : 0o00100000,
|
||||||
|
NOATIME : 0o01000000,
|
||||||
|
NOCTTY : 0o00000400,
|
||||||
|
NOFOLLOW : 0o00400000,
|
||||||
|
NONBLOCK : 0o00004000,
|
||||||
|
NDELAY : 0o00004000,
|
||||||
|
PATH : 0o10000000,
|
||||||
|
SYNC : 0o04010000,
|
||||||
|
TMPFILE : 0o20200000,
|
||||||
|
TRUNC : 0o00001000
|
||||||
|
};
|
||||||
|
|
||||||
|
export { OpenFlags }
|
@ -2,6 +2,7 @@ import { MessageWriter } from "./messagewriter.js";
|
|||||||
import { MessageReader } from "./messagereader.js";
|
import { MessageReader } from "./messagereader.js";
|
||||||
import { ERRNO } from "./errno.js";
|
import { ERRNO } from "./errno.js";
|
||||||
import { AccessMode } from "./accessmode.js";
|
import { AccessMode } from "./accessmode.js";
|
||||||
|
import { OpenFlags } from "./openflags.js";
|
||||||
import { BaseFileSystem } from "./basefilesystem.js";
|
import { BaseFileSystem } from "./basefilesystem.js";
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ const Mode = {
|
|||||||
function fs_access(reader, writer, filesystem) {
|
function fs_access(reader, writer, filesystem) {
|
||||||
const path = reader.read_str();
|
const path = reader.read_str();
|
||||||
const mode = reader.read_u8();
|
const mode = reader.read_u8();
|
||||||
result = filesystem.access(path, mode);
|
const result = filesystem.access(path, mode);
|
||||||
writer.write_i32(result);
|
writer.write_i32(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +157,7 @@ function fs_read(reader, writer, filesystem) {
|
|||||||
const fd = reader.read_u64();
|
const fd = reader.read_u64();
|
||||||
const result = filesystem.read(path, size, offset, fd);
|
const result = filesystem.read(path, size, offset, fd);
|
||||||
if (typeof(result) != "number") {
|
if (typeof(result) != "number") {
|
||||||
writer.write_i32(0);
|
writer.write_i32(result.length);
|
||||||
writer.write_bytes(result);
|
writer.write_bytes(result);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -259,10 +260,9 @@ const commands = new Map([
|
|||||||
class Webfuse {
|
class Webfuse {
|
||||||
|
|
||||||
constructor(url, filesystem) {
|
constructor(url, filesystem) {
|
||||||
console.log('webfuse: ctor')
|
|
||||||
|
|
||||||
this.ws = new WebSocket(url, ["webfuse2"]);
|
this.ws = new WebSocket(url, ["webfuse2"]);
|
||||||
this.ws.binaryType = 'arraybuffer';
|
this.ws.binaryType = 'arraybuffer';
|
||||||
|
this.ws.addEventListener('open', (event) => this.on_connected(event));
|
||||||
this.ws.addEventListener('close', (event) => this.on_closed(event));
|
this.ws.addEventListener('close', (event) => this.on_closed(event));
|
||||||
this.ws.addEventListener('error', (event) => this.on_error(event));
|
this.ws.addEventListener('error', (event) => this.on_error(event));
|
||||||
this.ws.addEventListener('message', (event) => this.on_message(event));
|
this.ws.addEventListener('message', (event) => this.on_message(event));
|
||||||
@ -285,21 +285,25 @@ class Webfuse {
|
|||||||
command(reader, writer, this.filesystem);
|
command(reader, writer, this.filesystem);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.error(`unknow message type: ${message_type}`);
|
console.warn(`unknow message type: ${message_type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ws.send(writer.get_data());
|
this.ws.send(writer.get_data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on_connected(event) {
|
||||||
|
this.filesystem.connectionstatechanged("connected");
|
||||||
|
}
|
||||||
|
|
||||||
on_error(event) {
|
on_error(event) {
|
||||||
console.log('error', event);
|
console.info("connection error");
|
||||||
this.ws.close();
|
this.ws.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
on_closed(event) {
|
on_closed(event) {
|
||||||
console.log('closed', event);
|
this.filesystem.connectionstatechanged("closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Webfuse, BaseFileSystem, ERRNO, Mode, AccessMode }
|
export { Webfuse, BaseFileSystem, ERRNO, Mode, AccessMode, OpenFlags }
|
||||||
|
28
example/provider/javascript/style.css
Normal file
28
example/provider/javascript/style.css
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
html, body {
|
||||||
|
background-color: #c0c0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stats {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user