Skip to content

Commit

Permalink
Merge pull request #90 from kzahel/serve-local
Browse files Browse the repository at this point in the history
support serving local folders using web-server-chrome
  • Loading branch information
matt-cook committed Mar 2, 2016
2 parents 07557ea + 32eb104 commit de09d80
Show file tree
Hide file tree
Showing 4 changed files with 7,029 additions and 184 deletions.
48 changes: 48 additions & 0 deletions src/js/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
function AdminDataHandler(request) {
WSC.BaseHandler.prototype.constructor.call(this)
}
_.extend(AdminDataHandler.prototype, {
put: function() {

var newData = this.request.bodyparams

chrome.storage.local.get(null, function(data) {

var saveData = {}
var restart = false;
for(var key in newData){
var value = newData[key]
if(data.hasOwnProperty(key)){
data[key] = value;
saveData[key] = value;
if(key == "url"){
chrome.runtime.sendMessage({url: value});
}
}else if(key == "restart"){
restart = true;
}
}
chrome.storage.local.set(saveData);
this.setHeader('content-type','text/json')
var buf = new TextEncoder('utf-8').encode(JSON.stringify(data)).buffer
this.write(buf)
this.finish()

if(restart) setTimeout( function() {
// allow writing out response first.
chrome.runtime.reload();
}, 1000 )


}.bind(this))

},
get: function() {
chrome.storage.local.get(null, function(data) {
this.setHeader('content-type','text/json')
var buf = new TextEncoder('utf-8').encode(JSON.stringify(data)).buffer
this.write(buf)
this.finish()
}.bind(this))
}
}, WSC.BaseHandler.prototype)
209 changes: 26 additions & 183 deletions src/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ chrome.app.runtime.onLaunched.addListener(init);
chrome.app.runtime.onRestarted.addListener(init);

function init() {
var directoryServer;
var adminServer;

var win, basePath, socketInfo, data;
var filesMap = {};

Expand All @@ -20,12 +23,12 @@ function init() {
var port = data.servelocalport;
console.log(host,port,entry);
//TODO: Start web server with local directry entry
//startWebserver(host,port,entry)
startWebserverDirectoryEntry(host,port,entry)
});
}
if(data.host && data.port){
//make setup page available remotely via HTTP
startWebserver(data.host,data.port,'www');
startWebserver(data.host,data.port,'www',data);
}
openWindow("windows/browser.html");
}else{
Expand Down Expand Up @@ -61,191 +64,31 @@ function init() {
});
}

function startWebserverDirectoryEntry(host,port,entry) {
directoryServer = new WSC.WebApplication({host:host,
port:port,
renderIndex:true,
entry:entry
})
directoryServer.start()
}

//directory must be a subdirectory of the package
function startWebserver(host,port,directory){
function startWebserver(host,port,directory,settings){
chrome.runtime.getPackageDirectoryEntry(function(packageDirectory){
packageDirectory.getDirectory(directory,{create: false},function(webroot){

basePath = webroot.fullPath;
readDirectory(webroot);

chrome.sockets.tcpServer.create({}, function(_socketInfo) {
socketInfo = _socketInfo;
chrome.sockets.tcpServer.listen(socketInfo.socketId, host, port, null, function(result){
chrome.sockets.tcpServer.onAccept.addListener(onAccept);
chrome.sockets.tcp.onReceive.addListener(onReceive);
});
});

var fs = new WSC.FileSystem(webroot)
var handlers = [['/data.*', AdminDataHandler],
['.*', WSC.DirectoryEntryHandler.bind(null, fs)]]
adminServer = new WSC.WebApplication({host:host,
port:port,
handlers:handlers,
renderIndex:true,
auth:{ username: settings.username,
password: settings.password }
})
adminServer.start()
});
});
}

var readDirectory = function(directory){
var r = directory.createReader();
r.readEntries(function(entries) {
entries.forEach(function(entry, i) {
if(entry.isDirectory)
readDirectory(entry);
else {
entry.file(function(file){
filesMap[entry.fullPath.replace(basePath,'')] = file;
});
}
});
});
}

var stringToUint8Array = function(string) {
var buffer = new ArrayBuffer(string.length);
var view = new Uint8Array(buffer);
for(var i = 0; i < string.length; i++) {
view[i] = string.charCodeAt(i);
}
return view;
};

var arrayBufferToString = function(buffer) {
var str = '';
var uArrayVal = new Uint8Array(buffer);
for(var s = 0; s < uArrayVal.length; s++) {
str += String.fromCharCode(uArrayVal[s]);
}
return str;
};

var writeErrorResponse = function(socketId, errorCode, keepAlive) {
var file = { size: 0 };
var contentType = "text/plain"; //(file.type === "") ? "text/plain" : file.type;
var contentLength = file.size;
var header = stringToUint8Array("HTTP/1.0 " + errorCode + "\nWWW-Authenticate: Basic\nContent-length: " + file.size + "\nContent-type:" + contentType + ( keepAlive ? "\nConnection: keep-alive" : "") + "\n\n");
var outputBuffer = new ArrayBuffer(header.byteLength + file.size);
var view = new Uint8Array(outputBuffer)
view.set(header, 0);
chrome.sockets.tcp.send(socketId, outputBuffer, function(writeInfo) {
if (!keepAlive) {
chrome.sockets.tcp.close(socketId);
}
});
};

var write200FileResponse = function(socketId, file, keepAlive) {
var contentType = (file.type === "") ? "text/plain" : file.type;
var header = stringToUint8Array("HTTP/1.0 200 OK\nContent-length: " + file.size + "\nContent-type:" + contentType + ( keepAlive ? "\nConnection: keep-alive" : "") + "\n\n");
var outputBuffer = new ArrayBuffer(header.byteLength + file.size);
var view = new Uint8Array(outputBuffer)
view.set(header, 0);

var fileReader = new FileReader();
fileReader.onload = function(e) {
view.set(new Uint8Array(e.target.result), header.byteLength);
chrome.sockets.tcp.send(socketId, outputBuffer, function(writeInfo) {
if (!keepAlive) {
chrome.sockets.tcp.close(socketId);
}
});
};

fileReader.readAsArrayBuffer(file);
};

var write200JSONResponse = function (socketId, json, keepAlive){
var content = stringToUint8Array(json);
var header = stringToUint8Array("HTTP/1.0 200 OK\nContent-length: " + content.byteLength + "\nContent-type:text/json" + ( keepAlive ? "\nConnection: keep-alive" : "") + "\n\n");
var outputBuffer = new ArrayBuffer(header.byteLength + content.byteLength);
var view = new Uint8Array(outputBuffer)
view.set(header, 0);
view.set(content,header.byteLength);
chrome.sockets.tcp.send(socketId, outputBuffer, function(writeInfo) {
if (!keepAlive) {
chrome.sockets.tcp.close(socketId);
}
});
}

var onAccept = function(info) {
chrome.sockets.tcp.setPaused(info.clientSocketId,false);
};

var onReceive = function(info) {

// parse the request.
var request = arrayBufferToString(info.data);

// check keep-alive
var keepAlive = false;
if (request.indexOf("Connection: keep-alive") != -1) {
keepAlive = true;
}

// verify authorization
var auth = request.indexOf("Authorization: Basic");
if (auth >= 0){
auth = request.substring(auth,request.indexOf('\n',auth)).split(' ');
auth = window.atob(auth[auth.length-1]).split(':');
if(auth.length == 2 && auth[0] == data["username"] && auth[1] == data["password"]){
//request is authorized, continue
}else{
writeErrorResponse(info.socketId, "401 Not Authorized", keepAlive);
return;
}
}else{
writeErrorResponse(info.socketId, "401 Not Authorized", keepAlive);
return;
}

// parse the request
if(request.indexOf("PUT ") == 0){
//REST request to set data
var newData = request.split("\n\r");
newData = newData[newData.length-1];
newData = newData.trim().split('&');
var saveData = {};
var restart = false;
for(var i = 0; i < newData.length; i++){
var d = newData[i].split('=');
var key = decodeURIComponent(d[0]);
var value = decodeURIComponent(d[1]);
if(data.hasOwnProperty(key)){
data[key] = value;
saveData[key] = value;
if(key == "url"){
chrome.runtime.sendMessage({url: value});
}
}else if(key == "restart"){
restart = true;
}
}
chrome.storage.local.set(saveData);
write200JSONResponse(info.socketId, JSON.stringify(data), keepAlive);
if(restart) chrome.runtime.reload();
}else if(request.indexOf("GET ") == 0) {
var uriEnd = request.indexOf(" ", 4);
if(uriEnd < 0) { /* throw a wobbler */ return; }
var uri = request.substring(4, uriEnd);
// strip query string
var q = uri.indexOf("?");
if (q != -1) {
uri = uri.substring(0, q);
}
if(uri.substr(0,5) == '/data'){
//this is a REST request for data
write200JSONResponse(info.socketId, JSON.stringify(data), keepAlive);
}else{
//treat as a file request
if(uri == "/") uri = "/index.html";
var file = filesMap[uri];
if(!!file == false) {
console.warn("File does not exist..." + uri);
writeErrorResponse(info.socketId, "404 Not Found", keepAlive);
return;
}
write200FileResponse(info.socketId, file, keepAlive);
}
}else{
// Throw an error
chrome.sockets.tcp.close(info.socketId);
}
};

}
Loading

0 comments on commit de09d80

Please sign in to comment.