123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- from xmlrpc.client import ServerProxy
- from os.path import join, isfile
- from xmlrpc.server import SimpleXMLRPCServer
- from urllib.parse import urlparse
- import sys
- MAX_HISTORY_LENGTH = 6
- OK = 1
- FAIL = 2
- EMPTY = ''
- def get_port(url):
- 'Extracts the port from a URL'
- name = urlparse(url)[1]
- parts = name.split(':')
- return int(parts[-1])
- class Node:
- """
- A node in a peer-to-peer network.
- """
- def __init__(self, url, dirname, secret):
- self.url = url
- self.dirname = dirname
- self.secret = secret
- self.known = set()
- def query(self, query, history=[]):
- """
- Performs a query for a file, possibly asking other known Nodes for
- help. Returns the file as a string.
- """
- code, data = self._handle(query)
- if code == OK:
- return code, data
- else:
- history = history + [self.url]
- if len(history) >= MAX_HISTORY_LENGTH:
- return FAIL, EMPTY
- return self._broadcast(query, history)
- def hello(self, other):
- """
- Used to introduce the Node to other Nodes.
- """
- self.known.add(other)
- return OK
- def fetch(self, query, secret):
- """
- Used to make the Node find a file and download it.
- """
- if secret != self.secret: return FAIL
- code, data = self.query(query)
- if code == OK:
- f = open(join(self.dirname, query), 'w')
- f.write(data)
- f.close()
- return OK
- else:
- return FAIL
- def _start(self):
- """
- Used internally to start the XML-RPC server.
- """
- s = SimpleXMLRPCServer(("", get_port(self.url)), logRequests=False)
- s.register_instance(self)
- s.serve_forever()
- def _handle(self, query):
- """
- Used internally to handle queries.
- """
- dir = self.dirname
- name = join(dir, query)
- if not isfile(name): return FAIL, EMPTY
- return OK, open(name).read()
- def _broadcast(self, query, history):
- """
- Used internally to broadcast a query to all known Nodes.
- """
- for other in self.known.copy():
- if other in history: continue
- try:
- s = ServerProxy(other)
- code, data = s.query(query, history)
- if code == OK:
- return code, data
- except:
- self.known.remove(other)
- return FAIL, EMPTY
- def main():
- url, directory, secret = sys.argv[1:]
- n = Node(url, directory, secret)
- n._start()
- if __name__ == '__main__': main()
|