Start development series 0.1-post
[0share.git] / server.py
blob7334ae4a23ae62f6b47126756139450da770eab3
1 # Copyright (C) 2008, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 from logging import info, debug
5 import socket
6 import select, os
7 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
9 from zeroinstall.zerostore import Stores, BadDigest, NotStored
11 stores = Stores()
13 def serve(host, port):
14 """Listen on (host, port) for broadcast queries and fetch requests."""
16 # The UDP socket listens for broadcast queries about who has
17 # particular digests
18 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
19 udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
20 udp_socket.bind((host or '<broadcast>', port))
22 # The TCP socket is used to send the requested implementations
23 http_server = HTTPServer((host or '', port), FetchRequestHandler)
25 info("0share started and listening for requests...")
26 for x in stores.stores:
27 info("Serving implementations from %s", x)
28 while True:
29 ready = select.select([udp_socket, http_server.socket], [], [])[0]
30 if udp_socket in ready:
31 handle_query(udp_socket)
32 if http_server.socket in ready:
33 http_server.handle_request()
35 def handle_query(ss):
36 data, addr = ss.recvfrom(128)
37 info("Request from %s: %s", addr, repr(data))
38 if not data.startswith('0share\n'):
39 info("Not our format. Ignoring.")
40 return
41 digests = data.split('\n')[1:]
42 got = []
43 try:
44 for d in digests:
45 if not d: raise BadDigest("Empty digest!")
46 try:
47 stores.lookup(d)
48 info("Yes, we have %s", d)
49 got.append(d)
50 except NotStored:
51 info("No, we don't have %s", d)
52 except BadDigest, ex:
53 info("Bad request: %s", str(ex))
54 return
55 if got:
56 info("Sending reply...")
57 ss.sendto('0share-reply\n' + '\n'.join(got), addr)
59 class FetchRequestHandler(BaseHTTPRequestHandler):
60 def do_GET(self):
61 info("GET %s", self.path)
62 bits = self.path.split('/')
63 if len(bits) == 3 and bits[0] == '' and bits[1] == 'implementation':
64 digest = bits[2]
65 else:
66 self.send_error(404, "Not an implementation")
67 return
69 try:
70 path = stores.lookup(digest)
71 except (BadDigest, NotStored), ex:
72 info("Lookup error: %s", str(ex))
73 self.send_error(404, "Implementation %s not found" % digest)
74 return
76 self.send_response(200, "OK")
77 self.send_header('Content-Type', 'application/x-compressed-tar')
78 self.end_headers()
80 import tarfile
81 archive = tarfile.open(mode = 'w|gz', fileobj = self.wfile)
82 for x in os.listdir(path):
83 if x != '.manifest':
84 debug("Adding %s", x)
85 archive.add(os.path.join(path, x), x)
86 archive.close()