def _safe_serve(params, client_ip, client_hostname, cookie_data): # Note: Only logging imports here from config import WORK_DIR from logging import basicConfig as log_basic_config # Enable logging try: from config import LOG_LEVEL log_level = _convert_log_level(LOG_LEVEL) except ImportError: from logging import WARNING as LOG_LEVEL_WARNING log_level = LOG_LEVEL_WARNING log_basic_config(filename=path_join(WORK_DIR, 'server.log'), level=log_level) # Do the necessary imports after enabling the logging, order critical try: from common import ProtocolError, ProtocolArgumentError, NoPrintJSONError from dispatch import dispatch from jsonwrap import dumps from message import Messager from session import get_session, init_session, close_session, NoSessionError except ImportError: # Note: Heisenbug trap for #612, remove after resolved from logging import critical as log_critical from sys import path as sys_path log_critical('Heisenbug trap reports: ' + str(sys_path)) raise init_session(client_ip, cookie_data=cookie_data) try: # Unpack the arguments into something less obscure than the # Python FieldStorage object (part dictonary, part list, part FUBAR) http_args = DefaultNoneDict() for k in params: # Also take the opportunity to convert Strings into Unicode, # according to HTTP they should be UTF-8 try: http_args[k] = unicode(params.getvalue(k), encoding='utf-8') except TypeError: Messager.error('protocol argument error: expected string argument %s, got %s' % (k, type(params.getvalue(k)))) raise ProtocolArgumentError # Dispatch the request json_dic = dispatch(http_args, client_ip, client_hostname) response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) except ProtocolError, e: # Internal error, only reported to client not to log json_dic = {} e.json(json_dic) # Add a human-readable version of the error err_str = str(e) if err_str != '': Messager.error(err_str) response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic)))
def serve(params, client_ip, client_hostname, cookie_data): # The session relies on the config, wait-for-it cookie_hdrs = None # Do we have a Python version compatibly with our libs? if (version_info < REQUIRED_PY_VERSION): # Bail with hand-written JSON, this is very fragile to protocol changes return cookie_hdrs, ((JSON_HDR, ), (''' { "messages": [ [ "Incompatible Python version (%s), %s or above is supported", "error", -1 ] ] } ''' % (PY_VER_STR, REQUIRED_PY_VERSION_STR)).strip()) # We can now safely use json and Messager from jsonwrap import dumps from message import Messager try: # We need to lock here since flup uses threads for each request and # can thus manipulate each other's global variables try: CONFIG_CHECK_LOCK.acquire() _config_check() finally: CONFIG_CHECK_LOCK.release() except ConfigurationError as e: json_dic = {} e.json(json_dic) return cookie_hdrs, ((JSON_HDR, ), dumps( Messager.output_json(json_dic))) # We can now safely read the config from config import DEBUG try: _permission_check() except PermissionError as e: json_dic = {} e.json(json_dic) return cookie_hdrs, ((JSON_HDR, ), dumps( Messager.output_json(json_dic))) try: # Safe region, can throw any exception, has verified installation return _safe_serve(params, client_ip, client_hostname, cookie_data) except BaseException as e: # Handle the server crash return _server_crash(cookie_hdrs, e)
def _server_crash(cookie_hdrs, e): from config import ADMIN_CONTACT_EMAIL, DEBUG from jsonwrap import dumps from message import Messager stack_trace = _get_stack_trace() if DEBUG: # Send back the stack-trace as json error_msg = '\n'.join(('Server Python crash, stack-trace is:\n', stack_trace)) Messager.error(error_msg, duration=-1) else: # Give the user an error message # Use the current time since epoch as an id for later log look-up error_msg = ('The server encountered a serious error, ' 'please contact the administrators at %s ' 'and give the id #%d' ) % (ADMIN_CONTACT_EMAIL, int(time())) Messager.error(error_msg, duration=-1) # Print to stderr so that the exception is logged by the webserver print(stack_trace, file=stderr) json_dic = { 'exception': 'serverCrash', } return (cookie_hdrs, ((JSON_HDR, ), dumps(Messager.output_json(json_dic))))
def serve(params, client_ip, client_hostname, cookie_data): # The session relies on the config, wait-for-it cookie_hdrs = None # Do we have a Python version compatibly with our libs? if (version_info[0] != REQUIRED_PY_VERSION[0] or version_info < REQUIRED_PY_VERSION): # Bail with hand-writen JSON, this is very fragile to protocol changes return cookie_hdrs, ((JSON_HDR, ), (''' { "messages": [ [ "Incompatible Python version (%s), %s or above is supported", "error", -1 ] ] } ''' % (PY_VER_STR, REQUIRED_PY_VERSION_STR)).strip()) # We can now safely use json and Messager from jsonwrap import dumps from message import Messager try: # We need to lock here since flup uses threads for each request and # can thus manipulate each other's global variables with CONFIG_CHECK_LOCK: _config_check() except ConfigurationError, e: json_dic = {} e.json(json_dic) return cookie_hdrs, ((JSON_HDR, ), dumps(Messager.output_json(json_dic)))
def _server_crash(cookie_hdrs, e): from config import ADMIN_CONTACT_EMAIL, DEBUG from jsonwrap import dumps from message import Messager stack_trace = _get_stack_trace() if DEBUG: # Send back the stack-trace as json error_msg = '\n'.join(('Server Python crash, stack-trace is:\n', stack_trace)) Messager.error(error_msg, duration=-1) else: # Give the user an error message # Use the current time since epoch as an id for later log look-up error_msg = ('The server encountered a serious error, ' 'please contact the administrators at %s ' 'and give the id #%d' ) % (ADMIN_CONTACT_EMAIL, int(time())) Messager.error(error_msg, duration=-1) # Print to stderr so that the exception is logged by the webserver print(stack_trace, file=sys.stderr) json_dic = { 'exception': 'serverCrash', } return (cookie_hdrs, ((JSON_HDR, ), dumps(Messager.output_json(json_dic))))
def _safe_serve(params, client_ip, client_hostname, cookie_data): # Note: Only logging imports here from config import WORK_DIR from logging import basicConfig as log_basic_config # Enable logging try: from config import LOG_LEVEL log_level = _convert_log_level(LOG_LEVEL) except ImportError: from logging import WARNING as LOG_LEVEL_WARNING log_level = LOG_LEVEL_WARNING log_basic_config(filename=path_join(WORK_DIR, 'server.log'), level=log_level) # Do the necessary imports after enabling the logging, order critical from common import ProtocolError, NoPrintJSONError from dispatch import dispatch from jsonwrap import dumps from message import Messager from session import get_session, init_session, close_session, NoSessionError init_session(client_ip, cookie_data=cookie_data) try: # Unpack the arguments into something less obscure than the # Python FieldStorage object (part dictonary, part list, part FUBAR) http_args = DefaultNoneDict() for k in params: # Also take the opportunity to convert Strings into Unicode, # according to HTTP they should be UTF-8 http_args[k] = unicode(params.getvalue(k), encoding='utf-8') # Dispatch the request json_dic = dispatch(http_args, client_ip, client_hostname) response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) except ProtocolError, e: # Internal error, only reported to client not to log json_dic = {} e.json(json_dic) # Add a human-readable version of the error err_str = str(e) if err_str != '': Messager.error(err_str) response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic)))
def serve(params, client_ip, client_hostname): # At this stage we can not get any cookie data, wait-for-it cookie_hdrs = None # Do we have a Python version compatibly with our libs? if (version_info[0] != REQUIRED_PY_VERSION_MAJOR or version_info[1] < REQUIRED_PY_VERSION_MINOR): # Bail with hand-writen JSON, this is very fragile to protocol changes return cookie_hdrs, ((JSON_HDR, ), (''' { "messages": [ [ "Incompatible Python version (%s), %d.%d or above is supported", "error", -1 ] ] } ''' % (PY_VER_STR, REQUIRED_PY_VERSION_MAJOR, REQUIRED_PY_VERSION_MINOR)).strip()) # We can now safely use json and Messager from jsonwrap import dumps from message import Messager try: _config_check() except ConfigurationError: return cookie_hdrs, ((JSON_HDR, ), dumps(Messager.output_json({}))) # We can now safely read the config from config import DEBUG try: _permission_check() except PermissionError: return cookie_hdrs, ((JSON_HDR, ), dumps(Messager.output_json({}))) try: # Safe region, can throw any exception, has verified installation return _safe_serve(params, client_ip, client_hostname) except BaseException, e: # Handle the server crash return _server_crash(cookie_hdrs, e)
def _safe_serve(params, client_ip, client_hostname): from common import ProtocolError, NoPrintJSONError from config import WORK_DIR from dispatch import dispatch from jsonwrap import dumps from logging import basicConfig as log_basic_config from message import Messager from session import get_session # Enable logging try: from config import LOG_LEVEL log_level = _convert_log_level(LOG_LEVEL) except ImportError: from logging import WARNING as LOG_LEVEL_WARNING log_level = LOG_LEVEL_WARNING log_basic_config(filename=path_join(WORK_DIR, 'server.log'), level=log_level) # Session information is now available cookie_hdrs = get_session().get_cookie_hdrs() try: # Dispatch the request json_dic = dispatch(params, client_ip, client_hostname) response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) except ProtocolError, e: # Internal error, only reported to client not to log json_dic = {} e.json(json_dic) # Add a human-readable version of the error err_str = str(e) if err_str != '': Messager.error(err_str) response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic)))
def _safe_serve(params, client_ip, client_hostname, cookie_data): # Note: Only logging imports here from config import WORK_DIR from logging import basicConfig as log_basic_config # Enable logging try: from config import LOG_LEVEL log_level = _convert_log_level(LOG_LEVEL) except ImportError: from logging import WARNING as LOG_LEVEL_WARNING log_level = LOG_LEVEL_WARNING log_basic_config(filename=path_join(WORK_DIR, 'server.log'), level=log_level) # Do the necessary imports after enabling the logging, order critical try: from common import ProtocolError, ProtocolArgumentError, NoPrintJSONError from dispatch import dispatch from jsonwrap import dumps from message import Messager from session import get_session, init_session, close_session, NoSessionError, SessionStoreError except ImportError: # Note: Heisenbug trap for #612, remove after resolved from logging import critical as log_critical from sys import path as sys_path log_critical('Heisenbug trap reports: ' + str(sys_path)) raise init_session(client_ip, cookie_data=cookie_data) response_is_JSON = True try: # Unpack the arguments into something less obscure than the # Python FieldStorage object (part dictonary, part list, part FUBAR) http_args = DefaultNoneDict() for k in params: # Also take the opportunity to convert Strings into Unicode, # according to HTTP they should be UTF-8 try: http_args[k] = params.getvalue(k) except TypeError as e: # Messager.error(e) Messager.error( 'protocol argument error: expected string argument %s, got %s' % (k, type( params.getvalue(k)))) raise ProtocolArgumentError # Dispatch the request json_dic = dispatch(http_args, client_ip, client_hostname) except ProtocolError as e: # Internal error, only reported to client not to log json_dic = {} e.json(json_dic) # Add a human-readable version of the error err_str = str(e) if err_str != '': Messager.error(err_str, duration=-1) except NoPrintJSONError as e: # Terrible hack to serve other things than JSON response_data = (e.hdrs, e.data) response_is_JSON = False # Get the potential cookie headers and close the session (if any) try: cookie_hdrs = get_session().cookie.hdrs() close_session() except SessionStoreError: Messager.error( "Failed to store cookie (missing write permission to brat work directory)?", -1) except NoSessionError: cookie_hdrs = None if response_is_JSON: response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) return (cookie_hdrs, response_data)
except NoPrintJSONError, e: # Terrible hack to serve other things than JSON response_data = (e.hdrs, e.data) response_is_JSON = False # Get the potential cookie headers and close the session (if any) try: cookie_hdrs = get_session().cookie.hdrs() close_session() except SessionStoreError: Messager.error("Failed to store cookie (missing write permission to brat work directory)?", -1) except NoSessionError: cookie_hdrs = None if response_is_JSON: response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) return (cookie_hdrs, response_data) # Programmatically access the stack-trace def _get_stack_trace(): from traceback import print_exc try: from cStringIO import StringIO except ImportError: from StringIO import StringIO # Getting the stack-trace requires a small trick buf = StringIO() print_exc(file=buf)
def link(collection, document, tagger): pconf = ProjectConfiguration(real_directory(collection)) for linker_token, _, _, linker_service_url in pconf.get_linker_config(): if tagger == linker_token: break else: raise UnknownTaggerError(tagger) with TextAnnotations(path_join(real_directory(collection), document)) as ann_obj: url_soup = urlparse(linker_service_url) if url_soup.scheme == 'http': Connection = HTTPConnection elif url_soup.scheme == 'https': # Delayed HTTPS import since it relies on SSL which is commonly # missing if you roll your own Python, for once we should not # fail early since tagging is currently an edge case and we # can't allow it to bring down the whole server. from httplib import HTTPSConnection Connection = HTTPSConnection else: raise InvalidConnectionSchemeError(linker_token, url_soup.scheme) conn = None try: conn = Connection(url_soup.netloc) req_headers = { 'Content-type': 'text/plain; charset=utf-8', 'Accept': 'application/json', } # Build a new service URL since the request method doesn't accept # a parameters argument service_url = url_soup.path + ('?' + url_soup.query if url_soup.query else '') try: entities = list() for e in ann_obj.get_entities(): s = "{}\t{} {} {}\t{}\n".format(e.id, e.type, e.start, e.end, e.text.encode('utf-8')) entities.append(s) data = { 'document': ann_obj.get_document_text().encode('utf-8'), 'entities': entities } # req_headers['Content-length'] = len(data) # Note: Trout slapping for anyone sending Unicode objects here conn.request( 'POST', # As per: http://bugs.python.org/issue11898 # Force the url to be an ascii string str(service_url), dumps(data), headers=req_headers) except SocketError, e: raise TaggerConnectionError(linker_token, e) resp = conn.getresponse() # Did the request succeed? if resp.status != 200: raise TaggerConnectionError( linker_token, '%s %s' % (resp.status, resp.reason)) # Finally, we can read the response data resp_data = resp.read() finally:
response_data = (e.hdrs, e.data) response_is_JSON = False # Get the potential cookie headers and close the session (if any) try: cookie_hdrs = get_session().cookie.hdrs() close_session() except SessionStoreError: Messager.error( "Failed to store cookie (missing write permission to brat work directory)?", -1) except NoSessionError: cookie_hdrs = None if response_is_JSON: response_data = ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) return (cookie_hdrs, response_data) # Programmatically access the stack-trace def _get_stack_trace(): from traceback import print_exc try: from cStringIO import StringIO except ImportError: from StringIO import StringIO # Getting the stack-trace requires a small trick buf = StringIO()
from jsonwrap import dumps from message import Messager try: # We need to lock here since flup uses threads for each request and # can thus manipulate each other's global variables with CONFIG_CHECK_LOCK: _config_check() except ConfigurationError, e: json_dic = {} e.json(json_dic) return cookie_hdrs, ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) # We can now safely read the config from config import DEBUG try: _permission_check() except PermissionError, e: json_dic = {} e.json(json_dic) return cookie_hdrs, ((JSON_HDR, ), dumps(Messager.output_json(json_dic))) try: # Safe region, can throw any exception, has verified installation return _safe_serve(params, client_ip, client_hostname, cookie_data) except BaseException, e: # Handle the server crash return _server_crash(cookie_hdrs, e)