1 # Copyright (C) 2008, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 from logging
import info
, debug
7 from BaseHTTPServer
import HTTPServer
, BaseHTTPRequestHandler
9 from zeroinstall
.zerostore
import Stores
, BadDigest
, NotStored
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
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
)
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()
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.")
41 digests
= data
.split('\n')[1:]
45 if not d
: raise BadDigest("Empty digest!")
48 info("Yes, we have %s", d
)
51 info("No, we don't have %s", d
)
53 info("Bad request: %s", str(ex
))
56 info("Sending reply...")
57 ss
.sendto('0share-reply\n' + '\n'.join(got
), addr
)
59 class FetchRequestHandler(BaseHTTPRequestHandler
):
61 info("GET %s", self
.path
)
62 bits
= self
.path
.split('/')
63 if len(bits
) == 3 and bits
[0] == '' and bits
[1] == 'implementation':
66 self
.send_error(404, "Not an implementation")
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
)
76 self
.send_response(200, "OK")
77 self
.send_header('Content-Type', 'application/x-compressed-tar')
81 archive
= tarfile
.open(mode
= 'w|gz', fileobj
= self
.wfile
)
82 for x
in os
.listdir(path
):
85 archive
.add(os
.path
.join(path
, x
), x
)