50 sec send timeout, force activate keepalive, reworked websocket api to make it unified and allow named and unnamed parameters, reload on content.json fileWrite
This commit is contained in:
parent
ac211229a7
commit
655c104130
6 changed files with 78 additions and 60 deletions
|
@ -38,12 +38,12 @@ Linux (Debian):
|
||||||
- `pip install pyzmq` (if it drops a compile error then run `apt-get install python-dev` and try again)
|
- `pip install pyzmq` (if it drops a compile error then run `apt-get install python-dev` and try again)
|
||||||
- `pip install gevent`
|
- `pip install gevent`
|
||||||
- `pip install msgpack-python`
|
- `pip install msgpack-python`
|
||||||
- start using `python zeronet.py`
|
- start using `python start.py`
|
||||||
|
|
||||||
Linux (Without root access):
|
Linux (Without root access):
|
||||||
- `wget https://bootstrap.pypa.io/get-pip.py`
|
- `wget https://bootstrap.pypa.io/get-pip.py`
|
||||||
- `python get-pip.py --user pyzmq gevent msgpack-python`
|
- `python get-pip.py --user pyzmq gevent msgpack-python`
|
||||||
- start using `python zeronet.py`
|
- start using `python start.py`
|
||||||
|
|
||||||
|
|
||||||
## Current limitations
|
## Current limitations
|
||||||
|
@ -54,7 +54,7 @@ Linux (Without root access):
|
||||||
|
|
||||||
|
|
||||||
## How can I create a ZeroNet site?
|
## How can I create a ZeroNet site?
|
||||||
Shut down zeronet.py if you are running it already
|
Shut down zeronet if you are running it already
|
||||||
```
|
```
|
||||||
$ zeronet.py siteCreate
|
$ zeronet.py siteCreate
|
||||||
...
|
...
|
||||||
|
|
|
@ -158,6 +158,7 @@ class FileServer:
|
||||||
socket = self.context.socket(zmq.REP)
|
socket = self.context.socket(zmq.REP)
|
||||||
self.socket = socket
|
self.socket = socket
|
||||||
self.socket.setsockopt(zmq.RCVTIMEO, 5000) # Wait for data receive
|
self.socket.setsockopt(zmq.RCVTIMEO, 5000) # Wait for data receive
|
||||||
|
self.socket.setsockopt(zmq.SNDTIMEO, 50000) # Wait for data send
|
||||||
self.log.info("Binding to tcp://%s:%s" % (self.ip, self.port))
|
self.log.info("Binding to tcp://%s:%s" % (self.ip, self.port))
|
||||||
try:
|
try:
|
||||||
self.socket.bind('tcp://%s:%s' % (self.ip, self.port))
|
self.socket.bind('tcp://%s:%s' % (self.ip, self.port))
|
||||||
|
|
|
@ -33,12 +33,13 @@ class Peer:
|
||||||
if self.socket: self.socket.close()
|
if self.socket: self.socket.close()
|
||||||
|
|
||||||
self.socket = context.socket(zmq.REQ)
|
self.socket = context.socket(zmq.REQ)
|
||||||
|
self.socket.setsockopt(zmq.RCVTIMEO, 50000) # Wait for data arrive
|
||||||
self.socket.setsockopt(zmq.SNDTIMEO, 5000) # Wait for data send
|
self.socket.setsockopt(zmq.SNDTIMEO, 5000) # Wait for data send
|
||||||
self.socket.setsockopt(zmq.LINGER, 500) # Wait for socket close
|
self.socket.setsockopt(zmq.LINGER, 500) # Wait for socket close
|
||||||
#self.socket.setsockopt(zmq.TCP_KEEPALIVE, 1) # Enable keepalive
|
self.socket.setsockopt(zmq.TCP_KEEPALIVE, 1) # Enable keepalive
|
||||||
#self.socket.setsockopt(zmq.TCP_KEEPALIVE_IDLE, 4*60) # Send after 4 minute idle
|
self.socket.setsockopt(zmq.TCP_KEEPALIVE_IDLE, 4*60) # Send after 4 minute idle
|
||||||
#self.socket.setsockopt(zmq.TCP_KEEPALIVE_INTVL, 15) # Wait 15 sec to response
|
self.socket.setsockopt(zmq.TCP_KEEPALIVE_INTVL, 15) # Wait 15 sec to response
|
||||||
#self.socket.setsockopt(zmq.TCP_KEEPALIVE_CNT, 4) # 4 Probes
|
self.socket.setsockopt(zmq.TCP_KEEPALIVE_CNT, 4) # 4 Probes
|
||||||
self.socket.connect('tcp://%s:%s' % (self.ip, self.port))
|
self.socket.connect('tcp://%s:%s' % (self.ip, self.port))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,48 +77,60 @@ class UiWebsocket:
|
||||||
# Handle incoming messages
|
# Handle incoming messages
|
||||||
def handleRequest(self, data):
|
def handleRequest(self, data):
|
||||||
req = json.loads(data)
|
req = json.loads(data)
|
||||||
cmd = req.get("cmd", None)
|
|
||||||
|
cmd = req.get("cmd")
|
||||||
|
params = req.get("params")
|
||||||
permissions = self.site.settings["permissions"]
|
permissions = self.site.settings["permissions"]
|
||||||
if cmd == "response":
|
|
||||||
self.actionResponse(req)
|
if cmd == "response": # It's a response to a command
|
||||||
|
return self.actionResponse(req["to"], req["result"])
|
||||||
elif cmd == "ping":
|
elif cmd == "ping":
|
||||||
self.actionPing(req["id"])
|
func = self.actionPing
|
||||||
elif cmd == "channelJoin":
|
elif cmd == "channelJoin":
|
||||||
self.actionChannelJoin(req["id"], req["params"])
|
func = self.actionChannelJoin
|
||||||
elif cmd == "siteInfo":
|
elif cmd == "siteInfo":
|
||||||
self.actionSiteInfo(req["id"], req["params"])
|
func = self.actionSiteInfo
|
||||||
elif cmd == "serverInfo":
|
elif cmd == "serverInfo":
|
||||||
self.actionServerInfo(req["id"], req["params"])
|
func = self.actionServerInfo
|
||||||
elif cmd == "siteUpdate":
|
elif cmd == "siteUpdate":
|
||||||
self.actionSiteUpdate(req["id"], req["params"])
|
func = self.actionSiteUpdate
|
||||||
elif cmd == "sitePublish":
|
elif cmd == "sitePublish":
|
||||||
self.actionSitePublish(req["id"], req["params"])
|
func = self.actionSitePublish
|
||||||
elif cmd == "fileWrite":
|
elif cmd == "fileWrite":
|
||||||
self.actionFileWrite(req["id"], req["params"])
|
func = self.actionFileWrite
|
||||||
# Admin commands
|
# Admin commands
|
||||||
elif cmd == "sitePause" and "ADMIN" in permissions:
|
elif cmd == "sitePause" and "ADMIN" in permissions:
|
||||||
self.actionSitePause(req["id"], req["params"])
|
func = self.actionSitePause
|
||||||
elif cmd == "siteResume" and "ADMIN" in permissions:
|
elif cmd == "siteResume" and "ADMIN" in permissions:
|
||||||
self.actionSiteResume(req["id"], req["params"])
|
func = self.actionSiteResume
|
||||||
elif cmd == "siteDelete" and "ADMIN" in permissions:
|
elif cmd == "siteDelete" and "ADMIN" in permissions:
|
||||||
self.actionSiteDelete(req["id"], req["params"])
|
func = self.actionSiteDelete
|
||||||
elif cmd == "siteList" and "ADMIN" in permissions:
|
elif cmd == "siteList" and "ADMIN" in permissions:
|
||||||
self.actionSiteList(req["id"], req["params"])
|
func = self.actionSiteList
|
||||||
elif cmd == "channelJoinAllsite" and "ADMIN" in permissions:
|
elif cmd == "channelJoinAllsite" and "ADMIN" in permissions:
|
||||||
self.actionChannelJoinAllsite(req["id"], req["params"])
|
func = self.actionChannelJoinAllsite
|
||||||
# Unknown command
|
# Unknown command
|
||||||
else:
|
else:
|
||||||
self.response(req["id"], "Unknown command: %s" % cmd)
|
self.response(req["id"], "Unknown command: %s" % cmd)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Support calling as named, unnamed paramters and raw first argument too
|
||||||
|
if type(params) is dict:
|
||||||
|
func(req["id"], **params)
|
||||||
|
elif type(params) is list:
|
||||||
|
func(req["id"], *params)
|
||||||
|
else:
|
||||||
|
func(req["id"], params)
|
||||||
|
|
||||||
|
|
||||||
# - Actions -
|
# - Actions -
|
||||||
|
|
||||||
# Do callback on response {"cmd": "response", "to": message_id, "result": result}
|
# Do callback on response {"cmd": "response", "to": message_id, "result": result}
|
||||||
def actionResponse(self, req):
|
def actionResponse(self, to, result):
|
||||||
if req["to"] in self.waiting_cb:
|
if to in self.waiting_cb:
|
||||||
self.waiting_cb(req["result"]) # Call callback function
|
self.waiting_cb(result) # Call callback function
|
||||||
else:
|
else:
|
||||||
self.log.error("Websocket callback not found: %s" % req)
|
self.log.error("Websocket callback not found: %s, %s" % (to, result))
|
||||||
|
|
||||||
|
|
||||||
# Send a simple pong answer
|
# Send a simple pong answer
|
||||||
|
@ -151,19 +163,19 @@ class UiWebsocket:
|
||||||
|
|
||||||
|
|
||||||
# Send site details
|
# Send site details
|
||||||
def actionSiteInfo(self, to, params):
|
def actionSiteInfo(self, to):
|
||||||
ret = self.formatSiteInfo(self.site)
|
ret = self.formatSiteInfo(self.site)
|
||||||
self.response(to, ret)
|
self.response(to, ret)
|
||||||
|
|
||||||
|
|
||||||
# Join to an event channel
|
# Join to an event channel
|
||||||
def actionChannelJoin(self, to, params):
|
def actionChannelJoin(self, to, channel):
|
||||||
if params["channel"] not in self.channels:
|
if channel not in self.channels:
|
||||||
self.channels.append(params["channel"])
|
self.channels.append(channel)
|
||||||
|
|
||||||
|
|
||||||
# Server variables
|
# Server variables
|
||||||
def actionServerInfo(self, to, params):
|
def actionServerInfo(self, to):
|
||||||
ret = {
|
ret = {
|
||||||
"ip_external": bool(config.ip_external),
|
"ip_external": bool(config.ip_external),
|
||||||
"platform": sys.platform,
|
"platform": sys.platform,
|
||||||
|
@ -177,13 +189,13 @@ class UiWebsocket:
|
||||||
self.response(to, ret)
|
self.response(to, ret)
|
||||||
|
|
||||||
|
|
||||||
def actionSitePublish(self, to, params):
|
def actionSitePublish(self, to, privatekey):
|
||||||
site = self.site
|
site = self.site
|
||||||
if not site.settings["own"]: return self.response(to, "Forbidden, you can only modify your own sites")
|
if not site.settings["own"]: return self.response(to, "Forbidden, you can only modify your own sites")
|
||||||
|
|
||||||
# Signing
|
# Signing
|
||||||
site.loadContent(True) # Reload content.json, ignore errors to make it up-to-date
|
site.loadContent(True) # Reload content.json, ignore errors to make it up-to-date
|
||||||
signed = site.signContent(params[0]) # Sign using private key sent by user
|
signed = site.signContent(privatekey) # Sign using private key sent by user
|
||||||
if signed:
|
if signed:
|
||||||
self.cmd("notification", ["done", "Private key correct, site signed!", 5000]) # Display message for 5 sec
|
self.cmd("notification", ["done", "Private key correct, site signed!", 5000]) # Display message for 5 sec
|
||||||
else:
|
else:
|
||||||
|
@ -217,15 +229,18 @@ class UiWebsocket:
|
||||||
|
|
||||||
|
|
||||||
# Write a file to disk
|
# Write a file to disk
|
||||||
def actionFileWrite(self, to, params):
|
def actionFileWrite(self, to, inner_path, content_base64):
|
||||||
if not self.site.settings["own"]: return self.response(to, "Forbidden, you can only modify your own sites")
|
if not self.site.settings["own"]: return self.response(to, "Forbidden, you can only modify your own sites")
|
||||||
try:
|
try:
|
||||||
import base64
|
import base64
|
||||||
content = base64.b64decode(params[1])
|
content = base64.b64decode(content_base64)
|
||||||
open(self.site.getPath(params[0]), "wb").write(content)
|
open(self.site.getPath(inner_path), "wb").write(content)
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
return self.response(to, "Write error: %s" % err)
|
return self.response(to, "Write error: %s" % err)
|
||||||
|
|
||||||
|
if inner_path == "content.json":
|
||||||
|
self.site.loadContent(True)
|
||||||
|
|
||||||
return self.response(to, "ok")
|
return self.response(to, "ok")
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,7 +249,7 @@ class UiWebsocket:
|
||||||
# - Admin actions -
|
# - Admin actions -
|
||||||
|
|
||||||
# List all site info
|
# List all site info
|
||||||
def actionSiteList(self, to, params):
|
def actionSiteList(self, to):
|
||||||
ret = []
|
ret = []
|
||||||
SiteManager.load() # Reload sites
|
SiteManager.load() # Reload sites
|
||||||
for site in self.server.sites.values():
|
for site in self.server.sites.values():
|
||||||
|
@ -244,9 +259,9 @@ class UiWebsocket:
|
||||||
|
|
||||||
|
|
||||||
# Join to an event channel on all sites
|
# Join to an event channel on all sites
|
||||||
def actionChannelJoinAllsite(self, to, params):
|
def actionChannelJoinAllsite(self, to, channel):
|
||||||
if params["channel"] not in self.channels: # Add channel to channels
|
if channel not in self.channels: # Add channel to channels
|
||||||
self.channels.append(params["channel"])
|
self.channels.append(channel)
|
||||||
|
|
||||||
for site in self.server.sites.values(): # Add websocket to every channel
|
for site in self.server.sites.values(): # Add websocket to every channel
|
||||||
if self not in site.websockets:
|
if self not in site.websockets:
|
||||||
|
@ -254,8 +269,7 @@ class UiWebsocket:
|
||||||
|
|
||||||
|
|
||||||
# Update site content.json
|
# Update site content.json
|
||||||
def actionSiteUpdate(self, to, params):
|
def actionSiteUpdate(self, to, address):
|
||||||
address = params.get("address")
|
|
||||||
site = self.server.sites.get(address)
|
site = self.server.sites.get(address)
|
||||||
if site and (site.address == self.site.address or "ADMIN" in self.site.settings["permissions"]):
|
if site and (site.address == self.site.address or "ADMIN" in self.site.settings["permissions"]):
|
||||||
gevent.spawn(site.update)
|
gevent.spawn(site.update)
|
||||||
|
@ -264,8 +278,7 @@ class UiWebsocket:
|
||||||
|
|
||||||
|
|
||||||
# Pause site serving
|
# Pause site serving
|
||||||
def actionSitePause(self, to, params):
|
def actionSitePause(self, to, address):
|
||||||
address = params.get("address")
|
|
||||||
site = self.server.sites.get(address)
|
site = self.server.sites.get(address)
|
||||||
if site:
|
if site:
|
||||||
site.settings["serving"] = False
|
site.settings["serving"] = False
|
||||||
|
@ -277,8 +290,7 @@ class UiWebsocket:
|
||||||
|
|
||||||
|
|
||||||
# Resume site serving
|
# Resume site serving
|
||||||
def actionSiteResume(self, to, params):
|
def actionSiteResume(self, to, address):
|
||||||
address = params.get("address")
|
|
||||||
site = self.server.sites.get(address)
|
site = self.server.sites.get(address)
|
||||||
if site:
|
if site:
|
||||||
site.settings["serving"] = True
|
site.settings["serving"] = True
|
||||||
|
@ -290,8 +302,7 @@ class UiWebsocket:
|
||||||
self.response(to, {"error": "Unknown site: %s" % address})
|
self.response(to, {"error": "Unknown site: %s" % address})
|
||||||
|
|
||||||
|
|
||||||
def actionSiteDelete(self, to, params):
|
def actionSiteDelete(self, to, address):
|
||||||
address = params.get("address")
|
|
||||||
site = self.server.sites.get(address)
|
site = self.server.sites.get(address)
|
||||||
if site:
|
if site:
|
||||||
site.settings["serving"] = False
|
site.settings["serving"] = False
|
||||||
|
|
|
@ -23,7 +23,8 @@ class Wrapper
|
||||||
@site_error = null # Latest failed file download
|
@site_error = null # Latest failed file download
|
||||||
|
|
||||||
window.onload = @onLoad # On iframe loaded
|
window.onload = @onLoad # On iframe loaded
|
||||||
$(window).on "hashchange", -> # On hash change
|
$(window).on "hashchange", => # On hash change
|
||||||
|
@log "Hashchange", window.location.hash
|
||||||
src = $("#inner-iframe").attr("src").replace(/#.*/, "")+window.location.hash
|
src = $("#inner-iframe").attr("src").replace(/#.*/, "")+window.location.hash
|
||||||
$("#inner-iframe").attr("src", src)
|
$("#inner-iframe").attr("src", src)
|
||||||
@
|
@
|
||||||
|
|
|
@ -535,8 +535,8 @@ jQuery.extend( jQuery.easing,
|
||||||
__slice = [].slice;
|
__slice = [].slice;
|
||||||
|
|
||||||
Notifications = (function() {
|
Notifications = (function() {
|
||||||
function Notifications(elem) {
|
function Notifications(_at_elem) {
|
||||||
this.elem = elem;
|
this.elem = _at_elem;
|
||||||
this;
|
this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,7 +684,8 @@ jQuery.extend( jQuery.easing,
|
||||||
});
|
});
|
||||||
|
|
||||||
/*$(".fixbutton-bg").on "click", ->
|
/*$(".fixbutton-bg").on "click", ->
|
||||||
return false */
|
return false
|
||||||
|
*/
|
||||||
$(".fixbutton-bg").on("mousedown", function() {
|
$(".fixbutton-bg").on("mousedown", function() {
|
||||||
return $(".fixbutton-burger").stop().animate({
|
return $(".fixbutton-burger").stop().animate({
|
||||||
"scale": 0.7,
|
"scale": 0.7,
|
||||||
|
@ -743,11 +744,14 @@ jQuery.extend( jQuery.easing,
|
||||||
this.wrapperWsInited = false;
|
this.wrapperWsInited = false;
|
||||||
this.site_error = null;
|
this.site_error = null;
|
||||||
window.onload = this.onLoad;
|
window.onload = this.onLoad;
|
||||||
$(window).on("hashchange", function() {
|
$(window).on("hashchange", (function(_this) {
|
||||||
var src;
|
return function() {
|
||||||
src = $("#inner-iframe").attr("src").replace(/#.*/, "") + window.location.hash;
|
var src;
|
||||||
return $("#inner-iframe").attr("src", src);
|
_this.log("Hashchange", window.location.hash);
|
||||||
});
|
src = $("#inner-iframe").attr("src").replace(/#.*/, "") + window.location.hash;
|
||||||
|
return $("#inner-iframe").attr("src", src);
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
this;
|
this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,9 +943,9 @@ jQuery.extend( jQuery.easing,
|
||||||
Wrapper.prototype.setSiteInfo = function(site_info) {
|
Wrapper.prototype.setSiteInfo = function(site_info) {
|
||||||
if (site_info.event != null) {
|
if (site_info.event != null) {
|
||||||
if (site_info.event[0] === "file_added" && site_info.bad_files) {
|
if (site_info.event[0] === "file_added" && site_info.bad_files) {
|
||||||
this.loading.printLine("" + site_info.bad_files + " files needs to be downloaded");
|
this.loading.printLine(site_info.bad_files + " files needs to be downloaded");
|
||||||
} else if (site_info.event[0] === "file_done") {
|
} else if (site_info.event[0] === "file_done") {
|
||||||
this.loading.printLine("" + site_info.event[1] + " downloaded");
|
this.loading.printLine(site_info.event[1] + " downloaded");
|
||||||
if (site_info.event[1] === window.inner_path) {
|
if (site_info.event[1] === window.inner_path) {
|
||||||
this.loading.hideScreen();
|
this.loading.hideScreen();
|
||||||
if (!this.site_info) {
|
if (!this.site_info) {
|
||||||
|
@ -953,7 +957,7 @@ jQuery.extend( jQuery.easing,
|
||||||
}
|
}
|
||||||
} else if (site_info.event[0] === "file_failed") {
|
} else if (site_info.event[0] === "file_failed") {
|
||||||
this.site_error = site_info.event[1];
|
this.site_error = site_info.event[1];
|
||||||
this.loading.printLine("" + site_info.event[1] + " download failed", "error");
|
this.loading.printLine(site_info.event[1] + " download failed", "error");
|
||||||
} else if (site_info.event[0] === "peers_added") {
|
} else if (site_info.event[0] === "peers_added") {
|
||||||
this.loading.printLine("Peers found: " + site_info.peers);
|
this.loading.printLine("Peers found: " + site_info.peers);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue