##// END OF EJS Templates
wrapfunction: use sysstr instead of bytes as argument in "zeroconf"...
marmoute -
r51687:c3d7a082 default
parent child Browse files
Show More
@@ -1,242 +1,242 b''
1 1 # zeroconf.py - zeroconf support for Mercurial
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 '''discover and advertise repositories on the local network
8 8
9 9 The zeroconf extension will advertise :hg:`serve` instances over
10 10 DNS-SD so that they can be discovered using the :hg:`paths` command
11 11 without knowing the server's IP address.
12 12
13 13 To allow other people to discover your repository using run
14 14 :hg:`serve` in your repository::
15 15
16 16 $ cd test
17 17 $ hg serve
18 18
19 19 You can discover Zeroconf-enabled repositories by running
20 20 :hg:`paths`::
21 21
22 22 $ hg paths
23 23 zc-test = http://example.com:8000/test
24 24 '''
25 25
26 26 import os
27 27 import socket
28 28 import time
29 29
30 30 from . import Zeroconf
31 31 from mercurial import (
32 32 dispatch,
33 33 encoding,
34 34 extensions,
35 35 hg,
36 36 pycompat,
37 37 rcutil,
38 38 ui as uimod,
39 39 )
40 40 from mercurial.hgweb import server as servermod
41 41
42 42 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
43 43 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
44 44 # be specifying the version(s) of Mercurial they are tested with, or
45 45 # leave the attribute unspecified.
46 46 testedwith = b'ships-with-hg-core'
47 47
48 48 # publish
49 49
50 50 server = None
51 51 localip = None
52 52
53 53
54 54 def getip():
55 55 # finds external-facing interface without sending any packets (Linux)
56 56 try:
57 57 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
58 58 s.connect(('1.0.0.1', 0))
59 59 ip = s.getsockname()[0]
60 60 return ip
61 61 except socket.error:
62 62 pass
63 63
64 64 # Generic method, sometimes gives useless results
65 65 try:
66 66 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
67 67 if ':' in dumbip:
68 68 dumbip = '127.0.0.1'
69 69 if not dumbip.startswith('127.'):
70 70 return dumbip
71 71 except (socket.gaierror, socket.herror):
72 72 dumbip = '127.0.0.1'
73 73
74 74 # works elsewhere, but actually sends a packet
75 75 try:
76 76 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
77 77 s.connect(('1.0.0.1', 1))
78 78 ip = s.getsockname()[0]
79 79 return ip
80 80 except socket.error:
81 81 pass
82 82
83 83 return dumbip
84 84
85 85
86 86 def publish(name, desc, path, port):
87 87 global server, localip
88 88 if not server:
89 89 ip = getip()
90 90 if ip.startswith('127.'):
91 91 # if we have no internet connection, this can happen.
92 92 return
93 93 localip = socket.inet_aton(ip)
94 94 server = Zeroconf.Zeroconf(ip)
95 95
96 96 hostname = socket.gethostname().split('.')[0]
97 97 host = hostname + ".local"
98 98 name = "%s-%s" % (hostname, name)
99 99
100 100 # advertise to browsers
101 101 svc = Zeroconf.ServiceInfo(
102 102 b'_http._tcp.local.',
103 103 pycompat.bytestr(name + '._http._tcp.local.'),
104 104 server=host,
105 105 port=port,
106 106 properties={b'description': desc, b'path': b"/" + path},
107 107 address=localip,
108 108 weight=0,
109 109 priority=0,
110 110 )
111 111 server.registerService(svc)
112 112
113 113 # advertise to Mercurial clients
114 114 svc = Zeroconf.ServiceInfo(
115 115 b'_hg._tcp.local.',
116 116 pycompat.bytestr(name + '._hg._tcp.local.'),
117 117 server=host,
118 118 port=port,
119 119 properties={b'description': desc, b'path': b"/" + path},
120 120 address=localip,
121 121 weight=0,
122 122 priority=0,
123 123 )
124 124 server.registerService(svc)
125 125
126 126
127 127 def zc_create_server(create_server, ui, app):
128 128 httpd = create_server(ui, app)
129 129 port = httpd.port
130 130
131 131 try:
132 132 repos = app.repos
133 133 except AttributeError:
134 134 # single repo
135 135 with app._obtainrepo() as repo:
136 136 name = app.reponame or os.path.basename(repo.root)
137 137 path = repo.ui.config(b"web", b"prefix", b"").strip(b'/')
138 138 desc = repo.ui.config(b"web", b"description")
139 139 if not desc:
140 140 desc = name
141 141 publish(name, desc, path, port)
142 142 else:
143 143 # webdir
144 144 prefix = app.ui.config(b"web", b"prefix", b"").strip(b'/') + b'/'
145 145 for repo, path in repos:
146 146 u = app.ui.copy()
147 147 if rcutil.use_repo_hgrc():
148 148 u.readconfig(os.path.join(path, b'.hg', b'hgrc'))
149 149 name = os.path.basename(repo)
150 150 path = (prefix + repo).strip(b'/')
151 151 desc = u.config(b'web', b'description')
152 152 if not desc:
153 153 desc = name
154 154 publish(name, desc, path, port)
155 155 return httpd
156 156
157 157
158 158 # listen
159 159
160 160
161 161 class listener:
162 162 def __init__(self):
163 163 self.found = {}
164 164
165 165 def removeService(self, server, type, name):
166 166 if repr(name) in self.found:
167 167 del self.found[repr(name)]
168 168
169 169 def addService(self, server, type, name):
170 170 self.found[repr(name)] = server.getServiceInfo(type, name)
171 171
172 172
173 173 def getzcpaths():
174 174 ip = getip()
175 175 if ip.startswith('127.'):
176 176 return
177 177 server = Zeroconf.Zeroconf(ip)
178 178 l = listener()
179 179 Zeroconf.ServiceBrowser(server, b"_hg._tcp.local.", l)
180 180 time.sleep(1)
181 181 server.close()
182 182 for value in l.found.values():
183 183 name = value.name[: value.name.index(b'.')]
184 184 url = "http://%s:%s%s" % (
185 185 socket.inet_ntoa(value.address),
186 186 value.port,
187 187 value.properties.get("path", "/"),
188 188 )
189 189 yield b"zc-" + name, pycompat.bytestr(url)
190 190
191 191
192 192 def config(orig, self, section, key, *args, **kwargs):
193 193 if section == b"paths" and key.startswith(b"zc-"):
194 194 for name, path in getzcpaths():
195 195 if name == key:
196 196 return path
197 197 return orig(self, section, key, *args, **kwargs)
198 198
199 199
200 200 def configitems(orig, self, section, *args, **kwargs):
201 201 repos = orig(self, section, *args, **kwargs)
202 202 if section == b"paths":
203 203 repos += getzcpaths()
204 204 return repos
205 205
206 206
207 207 def configsuboptions(orig, self, section, name, *args, **kwargs):
208 208 opt, sub = orig(self, section, name, *args, **kwargs)
209 209 if section == b"paths" and name.startswith(b"zc-"):
210 210 # We have to find the URL in the zeroconf paths. We can't cons up any
211 211 # suboptions, so we use any that we found in the original config.
212 212 for zcname, zcurl in getzcpaths():
213 213 if zcname == name:
214 214 return zcurl, sub
215 215 return opt, sub
216 216
217 217
218 218 def defaultdest(orig, source):
219 219 for name, path in getzcpaths():
220 220 if path == source:
221 221 return name.encode(encoding.encoding)
222 222 return orig(source)
223 223
224 224
225 225 def cleanupafterdispatch(orig, ui, options, cmd, cmdfunc):
226 226 try:
227 227 return orig(ui, options, cmd, cmdfunc)
228 228 finally:
229 229 # we need to call close() on the server to notify() the various
230 230 # threading Conditions and allow the background threads to exit
231 231 global server
232 232 if server:
233 233 server.close()
234 234
235 235
236 extensions.wrapfunction(dispatch, b'_runcommand', cleanupafterdispatch)
236 extensions.wrapfunction(dispatch, '_runcommand', cleanupafterdispatch)
237 237
238 extensions.wrapfunction(uimod.ui, b'config', config)
239 extensions.wrapfunction(uimod.ui, b'configitems', configitems)
240 extensions.wrapfunction(uimod.ui, b'configsuboptions', configsuboptions)
241 extensions.wrapfunction(hg, b'defaultdest', defaultdest)
242 extensions.wrapfunction(servermod, b'create_server', zc_create_server)
238 extensions.wrapfunction(uimod.ui, 'config', config)
239 extensions.wrapfunction(uimod.ui, 'configitems', configitems)
240 extensions.wrapfunction(uimod.ui, 'configsuboptions', configsuboptions)
241 extensions.wrapfunction(hg, 'defaultdest', defaultdest)
242 extensions.wrapfunction(servermod, 'create_server', zc_create_server)
General Comments 0
You need to be logged in to leave comments. Login now