~openerp/openobject-server/trunk-speedup-need_action-tde

« back to all changes in this revision

Viewing changes to bin/service/http_server.py

  • Committer: Harry (Open ERP)
  • Author(s): xrg
  • Date: 2009-11-20 14:31:04 UTC
  • mfrom: (1119.1.208)
  • mto: (1898.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 1900.
  • Revision ID: hmo@tinyerp.com-20091120143104-tb6136unkdw7yfy9
[merge] merge from lp:~xrg/openobject-server/optimize-5.0 and removed some confilts

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- encoding: utf-8 -*-
 
2
 
 
3
#
 
4
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
 
5
#
 
6
#
 
7
# WARNING: This program as such is intended to be used by professional
 
8
# programmers who take the whole responsability of assessing all potential
 
9
# consequences resulting from its eventual inadequacies and bugs
 
10
# End users who are looking for a ready-to-use solution with commercial
 
11
# garantees and support are strongly adviced to contract a Free Software
 
12
# Service Company
 
13
#
 
14
# This program is Free Software; you can redistribute it and/or
 
15
# modify it under the terms of the GNU General Public License
 
16
# as published by the Free Software Foundation; either version 2
 
17
# of the License, or (at your option) any later version.
 
18
#
 
19
# This program is distributed in the hope that it will be useful,
 
20
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
22
# GNU General Public License for more details.
 
23
#
 
24
# You should have received a copy of the GNU General Public License
 
25
# along with this program; if not, write to the Free Software
 
26
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
27
###############################################################################
 
28
 
 
29
""" This file contains instance of the http server.
 
30
 
 
31
    
 
32
"""
 
33
from websrv_lib import *
 
34
import netsvc
 
35
import threading
 
36
import tools
 
37
import os
 
38
import socket
 
39
import xmlrpclib
 
40
 
 
41
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
 
42
 
 
43
try:
 
44
    import fcntl
 
45
except ImportError:
 
46
    fcntl = None
 
47
    
 
48
try:
 
49
        from ssl import SSLError
 
50
except ImportError:
 
51
        class SSLError(Exception): pass
 
52
 
 
53
class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer):
 
54
    """ A threaded httpd server, with all the necessary functionality for us.
 
55
    
 
56
        It also inherits the xml-rpc dispatcher, so that some xml-rpc functions
 
57
        will be available to the request handler
 
58
    """
 
59
    encoding = None
 
60
    allow_none = False
 
61
    allow_reuse_address = 1
 
62
    _send_traceback_header = False
 
63
    i = 0
 
64
 
 
65
    def __init__(self, addr, requestHandler,
 
66
                 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
 
67
        self.logRequests = logRequests
 
68
 
 
69
        SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
 
70
        HTTPServer.__init__(self, addr, requestHandler)
 
71
 
 
72
        # [Bug #1222790] If possible, set close-on-exec flag; if a
 
73
        # method spawns a subprocess, the subprocess shouldn't have
 
74
        # the listening socket open.
 
75
        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
 
76
            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
 
77
            flags |= fcntl.FD_CLOEXEC
 
78
            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
 
79
 
 
80
    def handle_error(self, request, client_address):
 
81
        """ Override the error handler
 
82
        """
 
83
        import traceback
 
84
        netsvc.Logger().notifyChannel("init", netsvc.LOG_ERROR,"Server error in request from %s:\n%s" %
 
85
                (client_address,traceback.format_exc()))
 
86
 
 
87
class MultiHandler2(MultiHTTPHandler):
 
88
    def log_message(self, format, *args):
 
89
        netsvc.Logger().notifyChannel('http',netsvc.LOG_DEBUG,format % args)
 
90
 
 
91
    def log_error(self, format, *args):
 
92
        netsvc.Logger().notifyChannel('http',netsvc.LOG_ERROR,format % args)
 
93
 
 
94
 
 
95
class SecureMultiHandler2(SecureMultiHTTPHandler):
 
96
    def log_message(self, format, *args):
 
97
        netsvc.Logger().notifyChannel('https',netsvc.LOG_DEBUG,format % args)
 
98
 
 
99
    def getcert_fnames(self):
 
100
        tc = tools.config
 
101
        fcert = tc.get_misc('httpsd','sslcert', 'ssl/server.cert')
 
102
        fkey = tc.get_misc('httpsd','sslkey', 'ssl/server.key')
 
103
        return (fcert,fkey)
 
104
 
 
105
    def log_message(self, format, *args):
 
106
        netsvc.Logger().notifyChannel('http',netsvc.LOG_DEBUG,format % args)
 
107
 
 
108
    def log_error(self, format, *args):
 
109
        netsvc.Logger().notifyChannel('http',netsvc.LOG_ERROR,format % args)
 
110
 
 
111
class HttpDaemon(threading.Thread, netsvc.Server):
 
112
    def __init__(self, interface, port):
 
113
        threading.Thread.__init__(self)
 
114
        netsvc.Server.__init__(self)
 
115
        self.__port = port
 
116
        self.__interface = interface
 
117
 
 
118
        try:
 
119
            self.server = ThreadedHTTPServer((interface, port), MultiHandler2)
 
120
            self.server.vdirs = []
 
121
            self.server.logRequests = True
 
122
            netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, 
 
123
                        "starting HTTP service at %s port %d" % (interface or '0.0.0.0', port,))
 
124
        except Exception, e:
 
125
            netsvc.Logger().notifyChannel('httpd', netsvc.LOG_CRITICAL, "Error occur when starting the server daemon: %s" % (e,))
 
126
            raise
 
127
 
 
128
 
 
129
    def attach(self, path, gw):
 
130
        pass
 
131
 
 
132
    def stop(self):
 
133
        self.running = False
 
134
        if os.name != 'nt':
 
135
            self.server.socket.shutdown( hasattr(socket, 'SHUT_RDWR') and socket.SHUT_RDWR or 2 )
 
136
        self.server.socket.close()
 
137
 
 
138
    def run(self):
 
139
        #self.server.register_introspection_functions()
 
140
 
 
141
        self.running = True
 
142
        while self.running:
 
143
            self.server.handle_request()
 
144
        return True
 
145
 
 
146
class HttpSDaemon(threading.Thread, netsvc.Server):
 
147
    def __init__(self, interface, port):
 
148
        threading.Thread.__init__(self)
 
149
        netsvc.Server.__init__(self)
 
150
        self.__port = port
 
151
        self.__interface = interface
 
152
 
 
153
        try:
 
154
            self.server = ThreadedHTTPServer((interface, port), SecureMultiHandler2)
 
155
            self.server.vdirs = []
 
156
            self.server.logRequests = True
 
157
            netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, 
 
158
                        "starting HTTPS service at %s port %d" % (interface or '0.0.0.0', port,))
 
159
        except SSLError, e:
 
160
            netsvc.Logger().notifyChannel('httpd-ssl', netsvc.LOG_CRITICAL, "Can not load the certificate and/or the private key files")
 
161
            raise
 
162
        except Exception, e:
 
163
            netsvc.Logger().notifyChannel('httpd-ssl', netsvc.LOG_CRITICAL, "Error occur when starting the server daemon: %s" % (e,))
 
164
            raise
 
165
 
 
166
    def attach(self, path, gw):
 
167
        pass
 
168
 
 
169
    def stop(self):
 
170
        self.running = False
 
171
        if os.name != 'nt':
 
172
            self.server.socket.shutdown( hasattr(socket, 'SHUT_RDWR') and socket.SHUT_RDWR or 2 )
 
173
        self.server.socket.close()
 
174
 
 
175
    def run(self):
 
176
        #self.server.register_introspection_functions()
 
177
 
 
178
        self.running = True
 
179
        while self.running:
 
180
            self.server.handle_request()
 
181
        return True
 
182
 
 
183
httpd = None
 
184
httpsd = None
 
185
 
 
186
def init_servers():
 
187
        global httpd, httpsd
 
188
        if tools.config.get_misc('httpd','enable', True):
 
189
                httpd = HttpDaemon(tools.config.get_misc('httpd','interface', ''), \
 
190
                        tools.config.get_misc('httpd','port', 8069))
 
191
 
 
192
        if tools.config.get_misc('httpsd','enable', False):
 
193
                httpsd = HttpSDaemon(tools.config.get_misc('httpsd','interface', ''), \
 
194
                        tools.config.get_misc('httpsd','port', 8071))
 
195
 
 
196
def reg_http_service(hts, secure_only = False):
 
197
        """ Register some handler to httpd.
 
198
            hts must be an HTTPDir
 
199
        """
 
200
        global httpd, httpsd
 
201
        if not isinstance(hts, HTTPDir):
 
202
                raise Exception("Wrong class for http service")
 
203
        
 
204
        if httpd and not secure_only:
 
205
                httpd.server.vdirs.append(hts)
 
206
        
 
207
        if httpsd:
 
208
                httpsd.server.vdirs.append(hts)
 
209
        
 
210
        if (not httpd) and (not httpsd):
 
211
                netsvc.Logger().notifyChannel('httpd',netsvc.LOG_WARNING,"No httpd available to register service %s" % hts.path)
 
212
        return
 
213
 
 
214
import SimpleXMLRPCServer
 
215
class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
 
216
    rpc_paths = [] 
 
217
    protocol_version = 'HTTP/1.1'
 
218
    def _dispatch(self, method, params):
 
219
        try:
 
220
            service_name = self.path.split("/")[-1]
 
221
            return self.dispatch(service_name, method, params)
 
222
        except netsvc.OpenERPDispatcherException, e:
 
223
            raise xmlrpclib.Fault(tools.exception_to_unicode(e.exception), e.traceback)
 
224
 
 
225
    def log_message(self, format, *args):
 
226
        netsvc.Logger().notifyChannel('xmlrpc',netsvc.LOG_DEBUG_RPC,format % args)
 
227
 
 
228
    def handle(self):
 
229
        pass
 
230
 
 
231
    def finish(self):
 
232
        pass
 
233
 
 
234
    def setup(self):
 
235
        self.connection = dummyconn()
 
236
        if not len(XMLRPCRequestHandler.rpc_paths):
 
237
                XMLRPCRequestHandler.rpc_paths = map(lambda s: '/%s' % s, netsvc.ExportService._services.keys())
 
238
        pass
 
239
 
 
240
 
 
241
def init_xmlrpc():
 
242
        if not tools.config.get_misc('xmlrpc','enable', True):
 
243
                return
 
244
        reg_http_service(HTTPDir('/xmlrpc/',XMLRPCRequestHandler))
 
245
        # Example of http file serving:
 
246
        # reg_http_service(HTTPDir('/test/',HTTPHandler))
 
247
        netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, 
 
248
                        "Registered XML-RPC over HTTP")
 
249
                        
 
250
 
 
251
class OerpAuthProxy(AuthProxy):
 
252
        """ Require basic authentication..
 
253
        
 
254
            This is a copy of the BasicAuthProxy, which however checks/caches the db
 
255
            as well.
 
256
        """
 
257
        def __init__(self,provider):
 
258
                AuthProxy.__init__(self,provider)
 
259
                self.auth_creds = {}
 
260
                self.auth_tries = 0
 
261
                self.last_auth = None
 
262
 
 
263
        def checkRequest(self,handler,path = '/'):
 
264
                if self.auth_creds:
 
265
                        return True
 
266
                auth_str = handler.headers.get('Authorization',False)
 
267
                try:
 
268
                        db = handler.get_db_from_path(path)
 
269
                        print "Got db:",db
 
270
                except:
 
271
                        if path.startswith('/'):
 
272
                                path = path[1:]
 
273
                        psp= path.split('/')
 
274
                        if len(psp)>1:
 
275
                                db = psp[0]
 
276
                        else:
 
277
                                #FIXME!
 
278
                                self.provider.log("Wrong path: %s, failing auth" %path)
 
279
                                raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
 
280
 
 
281
                if auth_str and auth_str.startswith('Basic '):
 
282
                        auth_str=auth_str[len('Basic '):]
 
283
                        (user,passwd) = base64.decodestring(auth_str).split(':')
 
284
                        self.provider.log("Found user=\"%s\", passwd=\"***\" for db=\"%s\"" %(user,db))
 
285
                        acd = self.provider.authenticate(db,user,passwd,handler.client_address)
 
286
                        if acd != False:
 
287
                                self.auth_creds[db] = acd
 
288
                                self.last_auth=db
 
289
                                return True
 
290
                if self.auth_tries > 5:
 
291
                        self.provider.log("Failing authorization after 5 requests w/o password")
 
292
                        raise AuthRejectedExc("Authorization failed.")
 
293
                self.auth_tries += 1
 
294
                raise AuthRequiredExc(atype = 'Basic', realm=self.provider.realm)
 
295
 
 
296
import security
 
297
class OpenERPAuthProvider(AuthProvider):
 
298
        def __init__(self,realm = 'OpenERP User'):
 
299
                self.realm = realm
 
300
 
 
301
        def setupAuth(self, multi, handler):
 
302
                if not multi.sec_realms.has_key(self.realm):
 
303
                        multi.sec_realms[self.realm] = OerpAuthProxy(self)
 
304
                handler.auth_proxy = multi.sec_realms[self.realm]
 
305
 
 
306
        def authenticate(self, db, user, passwd, client_address):
 
307
                try:
 
308
                        uid = security.login(db,user,passwd)
 
309
                        if uid is False:
 
310
                                return False
 
311
                        return (user, passwd, db, uid)
 
312
                except Exception,e:
 
313
                        netsvc.Logger().notifyChannel("auth",netsvc.LOG_DEBUG,"Fail auth:"+ str(e))
 
314
                        return False
 
315
                
 
316
        def log(self, msg):
 
317
                netsvc.Logger().notifyChannel("auth",netsvc.LOG_INFO,msg)
 
318
 
 
319
#eof