Merge lp:~openerp-dev/openobject-server/trunk-bug-794287-xrg into lp:openobject-server

Proposed by xrg
Status: Rejected
Rejected by: Vo Minh Thu
Proposed branch: lp:~openerp-dev/openobject-server/trunk-bug-794287-xrg
Merge into: lp:openobject-server
Diff against target: 247 lines (+120/-37)
2 files modified
openerp/service/http_server.py (+108/-36)
openerp/service/websrv_lib.py (+12/-1)
To merge this branch: bzr merge lp:~openerp-dev/openobject-server/trunk-bug-794287-xrg
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+63781@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Vo Minh Thu (thu) wrote :

We are currently switching the HTTP stack from the websrv_lib layer to a WSGI approach. This means that handling IPv6 is done at an upper level, namely the WSGI server. For this reason I will reject this merge proposal.

Unmerged revisions

3452. By xrg

http_server: refactor IPv6 support for easier configuration

Previously, we would have the "httpd", the "httpsd" and then the "http6d"
etc, each of those able to listen on one interface/port.
With the introduction of IPv6, we may have the need to specify a set
of interfaces to listen to (as opposed to listening to all), and these
would also conflict with IPv4 ones on the same port.

So, have a "multi-interface" ability in the "interface" setting of two
httpds, accepting both IPv4 and IPv6 syntax.
Example:
   [httpd]
   interface = [::72ff:fee2], 192.168.1.2, 10.0.4.1:8169

This shall be compatible with the previous (single i/f) syntax, but
also allow configurations where we really want to restrict access to
trusted interfaces (networks).

3451. By xrg

websrv_lib: repr() for HTTPDir, helps logs

3450. By xrg

http_server: Initial IPv6 support

Easily, add one more http server at an IPv6 interface.

But wait, shall we only add one port, different than the main one?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openerp/service/http_server.py'
2--- openerp/service/http_server.py 2011-02-07 12:57:23 +0000
3+++ openerp/service/http_server.py 2011-06-07 22:28:29 +0000
4@@ -40,6 +40,7 @@
5 import os
6 import select
7 import socket
8+import re
9 import xmlrpclib
10 import logging
11
12@@ -102,6 +103,12 @@
13 def _get_next_name(self):
14 self.__threadno += 1
15 return 'http-client-%d' % self.__threadno
16+
17+class Threaded6HTTPServer(ThreadedHTTPServer):
18+ """A variant of ThreadedHTTPServer for IPv6
19+ """
20+ address_family = socket.AF_INET6
21+
22 class HttpLogHandler:
23 """ helper class for uniform log handling
24 Please define self._logger at each class that is derived from this
25@@ -136,17 +143,18 @@
26
27 class BaseHttpDaemon(threading.Thread, netsvc.Server):
28 _RealProto = '??'
29+ _IsSecure = False
30
31- def __init__(self, interface, port, handler):
32- threading.Thread.__init__(self, name='%sDaemon-%d'%(self._RealProto, port))
33+ def __init__(self, address, handler, server_class=ThreadedHTTPServer):
34+ threading.Thread.__init__(self, name='%sDaemon-%d'%(self._RealProto, address[1]))
35 netsvc.Server.__init__(self)
36- self.__port = port
37- self.__interface = interface
38+ self.__address = address
39
40 try:
41- self.server = ThreadedHTTPServer((interface, port), handler, proto=self._RealProto)
42+ self.server = server_class(address, handler, proto=self._RealProto)
43 self.server.vdirs = []
44 self.server.logRequests = True
45+ interface, port = address[:2]
46 self.server.timeout = self._busywait_timeout
47 logging.getLogger("web-services").info(
48 "starting %s service at %s port %d" %
49@@ -208,57 +216,121 @@
50 class HttpDaemon(BaseHttpDaemon):
51 _RealProto = 'HTTP'
52 def __init__(self, interface, port):
53- super(HttpDaemon, self).__init__(interface, port,
54+ super(HttpDaemon, self).__init__(address=(interface, port),
55 handler=MultiHandler2)
56
57 class HttpSDaemon(BaseHttpDaemon):
58 _RealProto = 'HTTPS'
59+ _IsSecure = True
60 def __init__(self, interface, port):
61 try:
62- super(HttpSDaemon, self).__init__(interface, port,
63+ super(HttpSDaemon, self).__init__(address=(interface, port),
64 handler=SecureMultiHandler2)
65 except SSLError, e:
66 logging.getLogger('httpsd').exception( \
67 "Can not load the certificate and/or the private key files")
68 raise
69
70-httpd = None
71-httpsd = None
72+class Http6Daemon(BaseHttpDaemon):
73+ _RealProto = 'HTTP6'
74+ def __init__(self, interface, port):
75+ super(Http6Daemon, self).__init__(address=(interface, port, 0, 0),
76+ handler=MultiHandler2,
77+ server_class=Threaded6HTTPServer)
78+ self.daemon = True
79+
80+class Http6SDaemon(BaseHttpDaemon):
81+ _RealProto = 'HTTP6S'
82+ def __init__(self, interface, port):
83+ try:
84+ super(Http6SDaemon, self).__init__(address=(interface, port),
85+ handler=SecureMultiHandler2,
86+ server_class=Threaded6HTTPServer)
87+ self.daemon = True
88+
89+ except SSLError, e:
90+ logging.getLogger('httpsd').exception( \
91+ "Can not load the certificate and/or the private key files")
92+ raise
93+
94+http_daemons = []
95
96 def init_servers():
97- global httpd, httpsd
98+ global http_daemons
99+ ipv4_re = re.compile('^([0-9]{1,3}(?:\.(?:[0-9]{1,3})){3})(?::(\d{1,5}))?$')
100+ ipv6_re = re.compile('^\[([0-9a-f:]+)\](?::(\d{1,5}))?$')
101+
102+ ipv6_missing = False
103 if tools.config.get('xmlrpc'):
104- httpd = HttpDaemon(tools.config.get('xmlrpc_interface', ''),
105- int(tools.config.get('xmlrpc_port', 8069)))
106+ ifaces = tools.config.get('xmlrpc_interface', '')
107+ if not ifaces:
108+ ifaces = '0.0.0.0' # By default, IPv4 only
109+ ifaces = map(str.strip, ifaces.split(','))
110+ base_port = tools.config.get('xmlrpc_port',8069)
111+ for iface in ifaces:
112+ m = ipv4_re.match(iface)
113+ if m:
114+ httpd = HttpDaemon( m.group(1), int(m.group(2) or base_port))
115+ http_daemons.append(httpd)
116+ continue
117+ m = ipv6_re.match(iface)
118+ if m:
119+ if not socket.has_ipv6:
120+ ipv6_missing = True
121+ continue
122+ httpd = Http6Daemon( m.group(1), int(m.group(2) or base_port))
123+ http_daemons.append(httpd)
124+ continue
125+ logging.getLogger('httpd').error("Cannot understand address \"%s\" to launch http daemon on!", iface)
126
127 if tools.config.get('xmlrpcs'):
128- httpsd = HttpSDaemon(tools.config.get('xmlrpcs_interface', ''),
129- int(tools.config.get('xmlrpcs_port', 8071)))
130-
131-def reg_http_service(hts, secure_only = False):
132+ ifaces = tools.config.get('xmlrpcs_interface', '')
133+ if not ifaces:
134+ ifaces = '0.0.0.0' # By default, IPv4 only
135+ ifaces = map(str.strip, ifaces.split(','))
136+ base_port = tools.config.get('xmlrpcs_port', 8071)
137+ for iface in ifaces:
138+ m = ipv4_re.match(iface)
139+ if m:
140+ httpd = HttpSDaemon( m.group(1), int(m.group(2) or base_port))
141+ http_daemons.append(httpd)
142+ continue
143+ m = ipv6_re.match(iface)
144+ if m:
145+ if not socket.has_ipv6:
146+ ipv6_missing = True
147+ continue
148+ httpd = Http6SDaemon( m.group(1), int(m.group(2) or base_port))
149+ http_daemons.append(httpd)
150+ continue
151+ logging.getLogger('httpd').error("Cannot understand address \"%s\" to launch http daemon on!", iface)
152+
153+ if ipv6_missing:
154+ logging.getLogger('http6d').warning("HTTPd servers for IPv6 specified, but not supported at this platform.")
155+
156+def reg_http_service(hts, secure_only=False):
157 """ Register some handler to httpd.
158 hts must be an HTTPDir
159 """
160- global httpd, httpsd
161-
162- if httpd and not secure_only:
163+ global http_daemons
164+
165+ if not http_daemons:
166+ logging.getLogger('httpd').warning("No httpd available to register service %s" % hts.path)
167+ return False
168+
169+ for httpd in http_daemons:
170+ if secure_only and not httpd._IsSecure:
171+ continue
172 httpd.append_svc(hts)
173-
174- if httpsd:
175- httpsd.append_svc(hts)
176-
177- if (not httpd) and (not httpsd):
178- logging.getLogger('httpd').warning("No httpd available to register service %s" % hts.path)
179- return
180+ return True
181
182 def list_http_services(protocol=None):
183- global httpd, httpsd
184- if httpd and (protocol == 'http' or protocol == None):
185+ global http_daemons
186+ for httpd in http_daemons:
187+ if protocol is not None and protocol != httpd._RealProto.lower():
188+ continue
189 return httpd.list_services()
190- elif httpsd and (protocol == 'https' or protocol == None):
191- return httpsd.list_services()
192- else:
193- raise Exception("Incorrect protocol or no http services")
194+ raise Exception("Incorrect protocol or no http services")
195
196 import SimpleXMLRPCServer
197 class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,HttpLogHandler,SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
198@@ -288,14 +360,14 @@
199 if tools.config.get('xmlrpc', False):
200 # Example of http file serving:
201 # reg_http_service(HTTPDir('/test/',HTTPHandler))
202- reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler))
203- logging.getLogger("web-services").info("Registered XML-RPC over HTTP")
204+ if reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler)):
205+ logging.getLogger("web-services").info("Registered XML-RPC over HTTP")
206
207 if tools.config.get('xmlrpcs', False) \
208 and not tools.config.get('xmlrpc', False):
209 # only register at the secure server
210- reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler), True)
211- logging.getLogger("web-services").info("Registered XML-RPC over HTTPS only")
212+ if reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler), True):
213+ logging.getLogger("web-services").info("Registered XML-RPC over HTTPS only")
214
215 class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler):
216 _logger = logging.getLogger('httpd')
217
218=== modified file 'openerp/service/websrv_lib.py'
219--- openerp/service/websrv_lib.py 2011-01-18 23:50:33 +0000
220+++ openerp/service/websrv_lib.py 2011-06-07 22:28:29 +0000
221@@ -135,6 +135,9 @@
222 if request.startswith(self.path):
223 return self.path
224 return False
225+
226+ def __repr__(self):
227+ return "<http %r on %s>" %(self.handler, self.path)
228
229 class noconnection(object):
230 """ a class to use instead of the real connection
231@@ -465,7 +468,15 @@
232 ssl_version=ssl.PROTOCOL_SSLv23)
233 self.rfile = self.connection.makefile('rb', self.rbufsize)
234 self.wfile = self.connection.makefile('wb', self.wbufsize)
235- self.log_message("Secure %s connection from %s",self.connection.cipher(),self.client_address)
236+ ciph_str = 'N/A'
237+ addr_str = '?'
238+ if self.connection.cipher():
239+ ciph_str = self.connection.cipher()[0]
240+ if self.client_address and len(self.client_address) == 4:
241+ addr_str = '[%s]:%s' % self.client_address[:2]
242+ elif self.client_address:
243+ addr_str = '%s:%s' % self.client_address
244+ self.log_message("Secure %s connection from %s",ciph_str,addr_str)
245 except Exception:
246 self.request.shutdown(socket.SHUT_RDWR)
247 raise