|
43 |
import tempfile |
43 |
import tempfile |
44 |
import time |
44 |
import time |
45 |
import threading |
45 |
import threading |
46 |
import urllib |
|
|
47 |
import urlparse |
48 |
import uuid |
46 |
import uuid |
49 |
|
47 |
|
50 |
import notifier |
48 |
import notifier |
|
328 |
msg = '%s (%s:%s) %s' % (self.name, self.get_ip_address(), remote.port, _msg) |
326 |
msg = '%s (%s:%s) %s' % (self.name, self.get_ip_address(), remote.port, _msg) |
329 |
self._logOptions.get(loglevel, CORE.info)(msg) |
327 |
self._logOptions.get(loglevel, CORE.info)(msg) |
330 |
|
328 |
|
331 |
def get_request(self, request, json): |
329 |
def get_request(self, path, args): |
332 |
req = umcp.Request( [ 'generic' ], opts = {} ) |
330 |
return umcp.Request( [ 'generic' ], opts = {} ) |
333 |
return req |
|
|
334 |
|
331 |
|
335 |
def get_ip_address(self): |
332 |
def get_ip_address(self): |
336 |
"""get the IP address of client by last entry in X-FORWARDED-FOR header""" |
333 |
"""get the IP address of client by last entry in X-FORWARDED-FOR header""" |
|
353 |
self._log(99, 'sessionid="%s"' % (sessionid)) |
350 |
self._log(99, 'sessionid="%s"' % (sessionid)) |
354 |
return sessionid |
351 |
return sessionid |
355 |
|
352 |
|
|
|
353 |
def load_json(self, body): |
354 |
try: |
355 |
json = simplejson.loads(body) |
356 |
if not isinstance(json, dict): |
357 |
raise cherrypy.HTTPError(httplib.BAD_REQUEST, 'JSON document have to be object') |
358 |
except ValueError: |
359 |
self._log('error', 'cannot parse JSON body') |
360 |
raise cherrypy.HTTPError(httplib.BAD_REQUEST, 'Invalid JSON document') |
361 |
return json |
362 |
|
356 |
@cherrypy.expose |
363 |
@cherrypy.expose |
357 |
def default( self, *args, **kwargs ): |
364 |
def default( self, *args, **kwargs ): |
358 |
remote = cherrypy.request.remote |
|
|
359 |
self._log('info', 'got new request') |
365 |
self._log('info', 'got new request') |
360 |
|
366 |
|
361 |
# get the session id from the request |
367 |
# get the session id from the request |
362 |
sessionid = self.get_session_id() |
368 |
sessionid = self.get_session_id() |
363 |
req = None |
|
|
364 |
|
369 |
|
365 |
if cherrypy.request.headers.get( 'Content-Type', '' ).startswith( 'application/json' ): # normal request |
370 |
if cherrypy.request.headers.get( 'Content-Type', '' ).startswith( 'application/json' ): # normal (json) request |
366 |
if not cherrypy.request.headers.get(u"Content-Length", u""): |
371 |
# get body and parse json |
367 |
json = '' |
372 |
body = '{}' |
368 |
self._log('warn', 'missing Content-Length header') |
373 |
if cherrypy.request.method in cherrypy.request.methods_with_bodies: |
369 |
else: |
374 |
if not cherrypy.request.headers.get(u"Content-Length"): |
370 |
# get body and parse json |
375 |
self._log('warn', 'missing Content-Length header') |
371 |
body = '' |
376 |
raise cherrypy.HTTPError(httplib.LENGTH_REQUIRED, 'Missing Content-Length header') |
372 |
if cherrypy.request.body: |
377 |
body = cherrypy.request.body.read() |
373 |
body = cherrypy.request.body.read() |
|
|
374 |
|
378 |
|
375 |
try: |
379 |
json = self.load_json(body) |
376 |
json = simplejson.loads(body) |
|
|
377 |
except ValueError: |
378 |
self._log('error', 'cannot parse JSON body') |
379 |
raise cherrypy.HTTPError(httplib.BAD_REQUEST, 'Invalid JSON document') |
380 |
else: |
380 |
else: |
381 |
json = '' |
381 |
# request is not json |
|
|
382 |
json = kwargs |
382 |
|
383 |
|
|
|
384 |
return self.get_response(sessionid, args, json) |
385 |
|
386 |
def get_response(self, sessionid, path, args): |
383 |
# create new UMCP request |
387 |
# create new UMCP request |
384 |
req = self.get_request( cherrypy.request, json ) |
388 |
req = self.get_request( '/'.join(path), args ) |
385 |
|
389 |
|
386 |
# create new response queue |
390 |
# create new response queue |
387 |
response_queue = Queue.Queue() |
391 |
response_queue = Queue.Queue() |
|
394 |
response = response_queue.get() |
398 |
response = response_queue.get() |
395 |
self._log('info', 'got response(0x%x) from queue(0x%x): status=%s (sessionid="%s")' % (id(response), id(response_queue), response.status, sessionid)) |
399 |
self._log('info', 'got response(0x%x) from queue(0x%x): status=%s (sessionid="%s")' % (id(response), id(response_queue), response.status, sessionid)) |
396 |
|
400 |
|
397 |
if response.status == umcp.SUCCESS: |
401 |
if 200 <= response.status < 300: |
398 |
update_session(cherrypy.response, sessionid) |
402 |
update_session(cherrypy.response, sessionid) |
399 |
return simplejson.dumps(response.body) |
403 |
cherrypy.response.status = response.status |
400 |
elif response.mimetype != umcp.MIMETYPE_JSON: |
|
|
401 |
update_session(cherrypy.response, sessionid) |
402 |
cherrypy.response.headers[ 'Content-Type' ] = response.mimetype |
404 |
cherrypy.response.headers[ 'Content-Type' ] = response.mimetype |
|
|
405 |
if response.mimetype == umcp.MIMETYPE_JSON: |
406 |
return simplejson.dumps(response.body) |
403 |
return response.body |
407 |
return response.body |
404 |
|
408 |
|
|
|
409 |
# TODO: 3xx handling |
410 |
|
405 |
# something bad happened |
411 |
# something bad happened |
406 |
self._log('error', 'response status code: %s' % response.status) |
412 |
self._log('error', 'response status code: %s' % response.status) |
407 |
self._log('error', 'response message: %s' % response.message) |
413 |
self._log('error', 'response message: %s' % response.message) |
408 |
message = response.message |
414 |
raise cherrypy.HTTPError(response.status, response.message) |
409 |
if isinstance(message, basestring): |
|
|
410 |
message = cherrypy._cperror._escape(message.replace('"', "'")) |
411 |
raise cherrypy.HTTPError(response.status, message) |
412 |
|
415 |
|
413 |
|
416 |
|
414 |
class CPGet(CPgeneric): |
417 |
class CPGet(CPgeneric): |
415 |
requestprefix = '/get' |
418 |
def get_request(self, path, args): |
416 |
|
419 |
if not path: |
417 |
def get_request(self, request, json): |
420 |
self._log('error', 'get_request: path is empty') |
418 |
args = request.path_info |
|
|
419 |
|
420 |
if args.startswith( self.requestprefix ): |
421 |
args = args[ len(self.requestprefix)+1 : ] |
422 |
|
423 |
if not args: |
424 |
self._log('error', 'get_request: args is empty') |
425 |
raise cherrypy.HTTPError(httplib.NOT_FOUND) |
421 |
raise cherrypy.HTTPError(httplib.NOT_FOUND) |
426 |
|
422 |
|
427 |
req = umcp.Request( 'GET', arguments = [ args ], options = json.get( 'options', {} ) ) |
423 |
return umcp.Request( 'GET', arguments = [ path ], options = args.get( 'options', {} ) ) |
428 |
|
424 |
|
429 |
return req |
|
|
430 |
|
425 |
|
431 |
|
|
|
432 |
class CPSet(CPgeneric): |
426 |
class CPSet(CPgeneric): |
433 |
requestprefix = '/set' |
427 |
def get_request(self, path, args): |
|
|
428 |
return umcp.Request( 'SET', options = args.get( 'options', {} ) ) |
434 |
|
429 |
|
435 |
def get_request(self, request, json): |
|
|
436 |
return umcp.Request( 'SET', options = json.get( 'options', {} ) ) |
437 |
|
430 |
|
438 |
|
|
|
439 |
class CPUpload( CPgeneric ): |
431 |
class CPUpload( CPgeneric ): |
440 |
requestprefix = '/upload' |
432 |
requestprefix = '/upload' |
441 |
|
433 |
|
442 |
def get_request(self, request, fields ): |
434 |
def get_request(self, custom_command, fields ): |
443 |
self._log( 'info', 'Handle upload command' ) |
435 |
self._log( 'info', 'Handle upload command' ) |
444 |
global _upload_manager |
436 |
global _upload_manager |
445 |
custom_command = request.path_info[ len( self.requestprefix ) + 1 : ] |
|
|
446 |
if custom_command: |
437 |
if custom_command: |
447 |
self._log( 'info', 'Send upload to module function' ) |
438 |
self._log( 'info', 'Send upload to module function' ) |
448 |
req = umcp.Request( 'COMMAND', arguments = [ custom_command ] ) |
439 |
req = umcp.Request( 'COMMAND', arguments = [ custom_command ] ) |
|
485 |
|
476 |
|
486 |
@cherrypy.expose |
477 |
@cherrypy.expose |
487 |
def default( self, *args, **kwargs ): |
478 |
def default( self, *args, **kwargs ): |
488 |
remote = cherrypy.request.remote |
|
|
489 |
self._log('info', 'got new request') |
479 |
self._log('info', 'got new request') |
490 |
|
480 |
|
491 |
# check for a valid session key |
481 |
# check for a valid session key in GET/POST data |
492 |
if 'X-UMC-Session-Id' in kwargs: |
482 |
if 'X-UMC-Session-Id' in kwargs: |
493 |
# allow for X-UMC-Sesssion-Id as entry in a multi-form (=upload) request |
483 |
# allow for X-UMC-Sesssion-Id as entry in a multi-form (=upload) request |
494 |
sessionid = kwargs['X-UMC-Session-Id'] |
484 |
sessionid = kwargs['X-UMC-Session-Id'] |
|
500 |
if not cherrypy.request.headers.get( 'Content-Type', '' ).startswith( 'multipart/form-data' ): |
490 |
if not cherrypy.request.headers.get( 'Content-Type', '' ).startswith( 'multipart/form-data' ): |
501 |
raise cherrypy.HTTPError(httplib.BAD_REQUEST, 'Content type and URL do not match') |
491 |
raise cherrypy.HTTPError(httplib.BAD_REQUEST, 'Content type and URL do not match') |
502 |
|
492 |
|
|
|
493 |
return self.get_response(sessionid, args, kwargs) |
494 |
|
503 |
# check if the request is a iframe upload |
495 |
# check if the request is a iframe upload |
504 |
iframe = 'iframe' in kwargs and (kwargs['iframe'] not in ('false', False, 0, '0')) |
496 |
if 'iframe' in kwargs and (kwargs['iframe'] not in ('false', False, 0, '0')): |
|
|
497 |
# this is a workaround to make iframe uploads work, they need the textarea field |
498 |
cherrypy.response.headers[ 'Content-Type' ] = umcp.MIMETYPE_HTML |
499 |
return '<html><body><textarea>%s</textarea></body></html>' % (response.body) |
505 |
|
500 |
|
506 |
# create new UMCP request |
501 |
return response |
507 |
req = self.get_request( cherrypy.request, kwargs ) |
|
|
508 |
|
502 |
|
509 |
# create new response queue |
|
|
510 |
response_queue = Queue.Queue() |
511 |
|
512 |
# send request to UMC server |
513 |
request = QueueRequest(sessionid, req, response_queue, self.get_ip_address()) |
514 |
UMCP_Dispatcher._queue_send.put(request) |
515 |
|
516 |
self._log('info', 'pushed request(0x%x) to queue(0x%x) - waiting for response (sessionid="%s")' % (id(req), id(response_queue), sessionid)) |
517 |
response = response_queue.get() |
518 |
self._log('info', 'got response(0x%x) from queue(0x%x): status=%s (sessionid="%s")' % (id(response), id(response_queue), response.status, sessionid)) |
519 |
|
520 |
if response.status == umcp.SUCCESS: |
521 |
update_session(cherrypy.response, sessionid) |
522 |
if iframe: |
523 |
# this is a workaround to make iframe uploads work, they need the textarea field |
524 |
cherrypy.response.headers[ 'Content-Type' ] = umcp.MIMETYPE_HTML |
525 |
return '<html><body><textarea>%s</textarea></body></html>' % (simplejson.dumps(response.body)) |
526 |
|
527 |
return simplejson.dumps(response.body) |
528 |
|
529 |
# something bad happened |
530 |
self._log('error', 'response status code: %s' % response.status) |
531 |
self._log('error', 'response message: %s' % response.message) |
532 |
message = response.message |
533 |
if isinstance(message, basestring): |
534 |
message = cherrypy._cperror._escape(message.replace('"', "'")) |
535 |
raise cherrypy.HTTPError(response.status, message) |
536 |
|
537 |
class CPCommand(CPgeneric): |
503 |
class CPCommand(CPgeneric): |
538 |
requestprefix = '/command' |
504 |
def get_request(self, path, args): |
539 |
|
505 |
if not path: |
540 |
def get_request(self, request, json): |
506 |
self._log('error', 'get_request: path is empty') |
541 |
parse_result = urlparse.urlparse( request.path_info ) |
|
|
542 |
args = parse_result.path |
543 |
|
544 |
if args.startswith( self.requestprefix ): |
545 |
args = args[ len(self.requestprefix)+1 : ] |
546 |
|
547 |
if not args: |
548 |
self._log('error', 'get_request: args is empty') |
549 |
raise cherrypy.HTTPError(httplib.NOT_FOUND) |
507 |
raise cherrypy.HTTPError(httplib.NOT_FOUND) |
550 |
|
508 |
|
551 |
if cherrypy.request.method == 'POST': |
509 |
req = umcp.Command( [ path ], options = args.get( 'options', {} ) ) |
552 |
req = umcp.Command( [ args ], options = json.get( 'options', {} ) ) |
510 |
if 'flavor' in args: |
553 |
if 'flavor' in json: |
511 |
req.flavor = args[ 'flavor' ] |
554 |
req.flavor = json[ 'flavor' ] |
|
|
555 |
elif cherrypy.request.method == 'GET': |
556 |
req = umcp.Command( [ args ], options = {} ) |
557 |
for key, value in urlparse.parse_qsl( cherrypy.request.query_string ): |
558 |
req.options[ key ] = urllib.unquote( value ) |
559 |
|
512 |
|
560 |
return req |
513 |
return req |
561 |
|
514 |
|
|
566 |
remote = cherrypy.request.remote |
519 |
remote = cherrypy.request.remote |
567 |
CORE.info('CPRoot/auth: got new auth request (%s:%s <=> %s)' % (self.get_ip_address(), remote.port, remote.name)) |
520 |
CORE.info('CPRoot/auth: got new auth request (%s:%s <=> %s)' % (self.get_ip_address(), remote.port, remote.name)) |
568 |
|
521 |
|
569 |
if not cherrypy.request.headers.get(u"Content-Length", u""): |
522 |
content_length = cherrypy.request.headers.get(u"Content-Length") |
|
|
523 |
if not content_length: |
570 |
CORE.process('CPRoot/auth: missing Content-Length header') |
524 |
CORE.process('CPRoot/auth: missing Content-Length header') |
571 |
raise cherrypy.HTTPError(httplib.LENGTH_REQUIRED) |
525 |
raise cherrypy.HTTPError(httplib.LENGTH_REQUIRED) |
572 |
|
526 |
|
573 |
# TODO FIXME check body length and stop here if too much data has been sent by client |
|
|
574 |
|
575 |
# get body and parse json |
527 |
# get body and parse json |
576 |
body = '' |
528 |
body = '' |
577 |
if cherrypy.request.body: |
529 |
if cherrypy.request.method in cherrypy.request.methods_with_bodies: |
|
|
530 |
max_length = 512 |
531 |
if content_length <= max_length: |
532 |
raise cherrypy.HTTPError(httplib.REQUEST_ENTITY_TOO_LARGE, 'Request data is to large, allowed length is %d' % max_length) |
578 |
body = cherrypy.request.body.read() |
533 |
body = cherrypy.request.body.read() |
579 |
|
534 |
|
580 |
try: |
535 |
json = self.load_json(body) |
581 |
json = simplejson.loads(body) |
|
|
582 |
except ValueError: |
583 |
CORE.process('CPRoot/auth: cannot parse JSON body') |
584 |
raise cherrypy.HTTPError(httplib.BAD_REQUEST, 'Invalid JSON document') |
585 |
|
536 |
|
586 |
CORE.info('CPRoot/command: request: command=%s' % cherrypy.request.path_info ) |
537 |
CORE.info('CPRoot/command: request: command=%s' % cherrypy.request.path_info ) |
587 |
|
538 |
|
|
608 |
return "" |
559 |
return "" |
609 |
|
560 |
|
610 |
CORE.process('CPRoot/auth: username: %s, status code: %s' % (json.get('username'), response.status)) |
561 |
CORE.process('CPRoot/auth: username: %s, status code: %s' % (json.get('username'), response.status)) |
611 |
message = response.message |
562 |
raise cherrypy.HTTPError(response.status, response.message) |
612 |
if isinstance(message, basestring): |
|
|
613 |
message = cherrypy._cperror._escape(message.replace('"', "'")) |
614 |
raise cherrypy.HTTPError(response.status, message) |
615 |
|
563 |
|
616 |
class CPRoot(object): |
564 |
class CPRoot(object): |
617 |
def index(self): |
565 |
@cherrypy.expose |
|
|
566 |
def index(self, **kw): |
618 |
""" |
567 |
""" |
619 |
http://localhost:<ucr:umc/http/port>/ |
568 |
http://localhost:<ucr:umc/http/port>/ |
620 |
""" |
569 |
""" |
621 |
raise cherrypy.HTTPError(httplib.NOT_FOUND) |
570 |
raise cherrypy.HTTPError(httplib.NOT_FOUND) |
622 |
index.exposed = True |
|
|
623 |
|
571 |
|
|
|
572 |
def default_error_page(**kwargs): |
573 |
cherrypy.response.headers['Content-type'] = umcp.MIMETYPE_JSON |
574 |
# escape json and html |
575 |
for key, value in kwargs.items(): |
576 |
kwargs[key] = cherrypy._cperror._escape(str(value), True) |
577 |
return cherrypy._cperror._HTTPErrorTemplate % kwargs |
578 |
|
624 |
def run_cherrypy(): |
579 |
def run_cherrypy(): |
625 |
# TODO FIXME Folgenden Configeintrag einbauen, wenn loglevel in (0,1,2) |
580 |
# TODO FIXME Folgenden Configeintrag einbauen, wenn loglevel in (0,1,2) |
626 |
# 'server.environment': 'production', |
581 |
# 'server.environment': 'production', |
|
631 |
'engine.autoreload_on': False, |
586 |
'engine.autoreload_on': False, |
632 |
'tools.response_headers.on': True, |
587 |
'tools.response_headers.on': True, |
633 |
'tools.response_headers.headers': [ |
588 |
'tools.response_headers.headers': [ |
634 |
('Content-Type', umcp.MIMETYPE_PLAIN) |
589 |
('Content-Type', umcp.MIMETYPE_JSON) |
635 |
] |
590 |
], |
|
|
591 |
'error_page.default': default_error_page |
636 |
} ) |
592 |
} ) |
637 |
cherrypy.tools.proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For', scheme='X-Forwarded-Proto') |
593 |
cherrypy.tools.proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For', scheme='X-Forwarded-Proto') |
638 |
|
594 |
|