Changeset - ccbe369ce439
[Not reviewed]
default
0 6 1
Laman - 7 years ago 2018-01-19 01:17:36

checking version compatibility
7 files changed with 28 insertions and 10 deletions:
0 comments (0 inline, 0 general)
protocol.md
Show inline comments
 
# Protocol #
 

	
 
[Communication protocol proposal. Not yet fully implemented and expected to change.]
 
[Communication protocol proposal. Not yet fully implemented and still expected to change.]
 

	
 
Nodes communicate by exchanging messages through network sockets. Basic message format is simple:
 

	
 
		json-length: ABC
 
		bin-length: DEF
 
		{... JSON payload ...}
 
@@ -16,13 +16,13 @@ The client sends a request (a message) a
 

	
 
## Commands ##
 
### <a name="init"></a>init ###
 
Initiates communication between the client and the server. Checks that the nodes can meaningfully talk to each other.
 

	
 
#### Params ####
 
- (str) version: program version. Both the client and the server are to check for compatibility with their partner.
 
- [(int) major, (int) minor, (int) tiny] version version: program version. Both the client and the server are to check for compatibility with their partner.
 
- {push, pull} action: what the client desires to do
 
- (str) filename: name of the file to be synchronized
 
- (int) blockSize
 
- (int) blockCount
 

	
 
#### Responses ####
 
@@ -71,20 +71,21 @@ Cease talking and close the connection.
 

	
 
## Responses ##
 
### <a name="r-init"></a>init ###
 
See [the init command](#init). If the client doesn't like server's protocol version, it is free to [end](#end) the connection.
 

	
 
#### Params ####
 
- (str) version
 
- [(int) major, (int) minor, (int) tiny] version
 

	
 

	
 
### <a name="deny"></a>deny ###
 
The server refuses connection. It might be busy, corrupted or incompatible.
 

	
 
#### Params ####
 
- (int) retry: suggested delay in seconds before another attempt at connection
 
- (int) status
 
- (str) msg
 

	
 

	
 
### <a name="ack"></a>ack ###
 
Everything is alright, but the server has nothing better to say.
 

	
src/client.py
Show inline comments
 
@@ -2,16 +2,17 @@ import collections
 
import socket
 
import ssl
 
import logging as log
 
from datetime import datetime
 

	
 
import config as conf
 
import status
 
import stats
 
from util import Progress
 
from hashtree import HashTree,hashBlock
 
from netnode import BaseConnection,NetNode,FailedConnection,LockedException
 
from netnode import BaseConnection,NetNode,FailedConnection,LockedException,IncompatibleException
 

	
 

	
 
class DeniedConnection(Exception): pass
 

	
 

	
 
class Connection(BaseConnection):
 
@@ -47,14 +48,18 @@ class Client(NetNode):
 

	
 
	def init(self,action):
 
		jsonData={"command":"init", "blockSize":self._tree.BLOCK_SIZE, "blockCount":self._tree.leafCount, "version":conf.version, "action":action}
 
		self._outcoming.writeMsg(jsonData)
 
		jsonData,binData=self._incoming.readMsg()
 
		if jsonData["command"]=="deny":
 
			if jsonData["status"]==status.incompatible.version:
 
				raise DeniedConnection("Incompatible client version. Consider upgrading it.")
 
			raise DeniedConnection()
 
		assert jsonData["command"]=="init"
 
		if jsonData["version"]<conf.lowestCompatible:
 
			raise IncompatibleException("Incompatible server version. Consider upgrading it.")
 

	
 
	## Asks server for node hashes to determine which are to be transferred.
 
	#
 
	# Uses a binary HashTree, where item at k is hash of items at 2k+1, 2k+2.
 
	#
 
	# Requests nodes in order of a batch DFS. Needs stack of size O(treeDepth*batchSize). Nodes in each tree level are accessed in order.
src/config.py
Show inline comments
 
@@ -18,12 +18,13 @@ peers=os.path.join(directory,"certs/peer
 

	
 
configFile=os.path.join(directory,"config.json")
 
conf=dict()
 
if os.path.isfile(configFile):
 
	with open(configFile) as f: conf=json.load(f)
 

	
 
version=(0,0,0)
 
version=[0,0,1]
 
lowestCompatible=[0,0,0] # tuple is more fitting but json conversion transforms it into a list anyway
 

	
 
hosts=conf.get("hosts",["127.0.0.1"])
 
port=conf.get("port",9901)
 

	
 
batchSize=conf.get("batchSize",256)
src/morevna.py
Show inline comments
 
@@ -47,14 +47,15 @@ def push(args):
 
				blocksToTransfer=c.negotiate()
 
				c.sendData(blocksToTransfer)
 
			print()
 
			print(stats.report())
 
			print()
 
		except FailedConnection: pass
 
		except DeniedConnection:
 
		except DeniedConnection as e:
 
			print("Server {0}:{1} denied connection.".format(*host))
 
			print(e)
 

	
 
def pull(args):
 
	_checkFile(args.datafile)
 
	if args.tree:
 
		_checkFile(args.tree)
 
	if args.host: conf.hosts=[args.host]
 
@@ -68,14 +69,15 @@ def pull(args):
 
			c.init("pull")
 
			blocksToTransfer=c.negotiate()
 
			c.pullData(blocksToTransfer,args.force)
 
		print()
 
		print(stats.report())
 
	except FailedConnection: pass
 
	except DeniedConnection:
 
	except DeniedConnection as e:
 
		print("Server {0}:{1} denied connection.".format(*host))
 
		print(e)
 

	
 
def serve(args):
 
	_checkFile(args.datafile)
 
	if args.tree:
 
		_checkFile(args.tree)
 
	if args.host: conf.hosts.insert(0,args.host)
src/netnode.py
Show inline comments
 
@@ -9,12 +9,13 @@ from hashtree import HashTree
 

	
 
lockFile=os.path.join(conf.directory,"dirty.lock")
 

	
 

	
 
class FailedConnection(Exception): pass
 
class LockedException(Exception): pass
 
class IncompatibleException(Exception): pass
 

	
 

	
 
class BaseConnection: # abstract
 
	def __init__(self):
 
		self._socket=None
 
		self.incoming=None
src/server.py
Show inline comments
 
@@ -3,12 +3,13 @@ import ssl
 
import multiprocessing
 
import logging as log
 

	
 
from hashtree import hashBlock
 
from netnode import BaseConnection,NetNode
 
import config as conf
 
import status
 

	
 

	
 
class Connection(BaseConnection):
 
	def __init__(self,serverSocket,sslContext):
 
		super().__init__()
 

	
 
@@ -37,13 +38,13 @@ class Miniserver:
 
		with self._ss:
 
			while True:
 
				connection=Connection(self._ss,self._ssl)
 
				if p and p.is_alive():
 
					with connection as c:
 
						c[0].readMsg()
 
						c[1].writeMsg({"command":"deny"})
 
						c[1].writeMsg({"command":"deny","status":status.locked})
 
					continue
 
				p=multiprocessing.Process(target=Server.run,args=(connection,self._filename,self._treeFile))
 
				p.start()
 

	
 

	
 
class Server(NetNode):
 
@@ -76,15 +77,17 @@ class Server(NetNode):
 

	
 
	def _serveOne(self):
 
		jsonData,binData=self._incoming.readMsg()
 

	
 
		if jsonData["command"]=="init":
 
			if jsonData["blockSize"]!=self.BLOCK_SIZE or jsonData["blockCount"]!=self._tree.leafCount:
 
				self._outcoming.writeMsg({"command":"deny"})
 
				self._outcoming.writeMsg({"command":"deny","status":status.incompatible.parameters})
 
			if jsonData["version"]<conf.lowestCompatible:
 
				self._outcoming.writeMsg({"command":"deny","status":status.incompatible.version})
 
			if jsonData["action"]=="pull" and self.isLocked():
 
				self._outcoming.writeMsg({"command":"deny"})
 
				self._outcoming.writeMsg({"command":"deny","status":status.locked})
 
			if jsonData["action"]=="push" and not self.isLocked():
 
				self._lock()
 

	
 
			self._outcoming.writeMsg({"command":"init", "version":conf.version})
 

	
 
		elif jsonData["command"]=="req":
src/status.py
Show inline comments
 
new file 100644
 
class incompatible:
 
	version=100
 
	parameters=101
 

	
 
locked=102
0 comments (0 inline, 0 general)