34
34
from mig .shared import returnvalues
35
35
from mig .shared .bailout import bailout_helper , crash_helper , compact_string
36
36
from mig .shared .base import requested_backend , allow_script , \
37
- is_default_str_coding , force_default_str_coding_rec
37
+ is_default_str_coding , force_default_str_coding_rec , force_utf8
38
38
from mig .shared .defaults import download_block_size , default_fs_coding
39
39
from mig .shared .conf import get_configuration_object
40
40
from mig .shared .objecttypes import get_object_type_info
43
43
from mig .shared .scriptinput import fieldstorage_to_dict
44
44
45
45
46
+ def _import_backend (backend ):
47
+ import_path = 'mig.shared.functionality.%s' % backend
48
+ module_handle = importlib .import_module (import_path )
49
+ return module_handle .main
50
+
51
+
46
52
def object_type_info (object_type ):
47
53
"""Lookup object type"""
48
54
49
55
return get_object_type_info (object_type )
50
56
51
57
52
- def stub (configuration , client_id , import_path , backend , user_arguments_dict ,
53
- environ ):
58
+ def stub (configuration , client_id , user_arguments_dict , environ , _retrieve_handler ):
54
59
"""Run backend on behalf of client_id with supplied user_arguments_dict.
55
60
I.e. import main from import_path and execute it with supplied arguments.
56
61
"""
@@ -61,6 +66,7 @@ def stub(configuration, client_id, import_path, backend, user_arguments_dict,
61
66
before_time = time .time ()
62
67
63
68
output_objects = []
69
+ backend = 'UNKNOWN'
64
70
main = dummy_main
65
71
66
72
# _logger.debug("stub for backend %r" % backend)
@@ -69,10 +75,12 @@ def stub(configuration, client_id, import_path, backend, user_arguments_dict,
69
75
# NEVER print/output it verbatim before it is validated below.
70
76
71
77
try :
78
+ default_page = configuration .site_landing_page # TODO: avoid doing this work a second time
79
+ backend = requested_backend (environ , fallback = default_page )
72
80
valid_backend_name (backend )
73
81
except InputException as iex :
74
- _logger .error ("%s refused to import invalid backend %r (%s) : %s" %
75
- (_addr , backend , import_path , iex ))
82
+ _logger .error ("%s refused to import invalid backend %r: %s" %
83
+ (_addr , backend , iex ))
76
84
bailout_helper (configuration , backend , output_objects ,
77
85
header_text = 'User Error' )
78
86
output_objects .extend ([
@@ -81,41 +89,40 @@ def stub(configuration, client_id, import_path, backend, user_arguments_dict,
81
89
{'object_type' : 'link' , 'text' : 'Go to default interface' ,
82
90
'destination' : configuration .site_landing_page }
83
91
])
84
- return (output_objects , returnvalues .CLIENT_ERROR )
92
+ return backend , (output_objects , returnvalues .CLIENT_ERROR )
85
93
86
94
try :
87
95
# Import main from backend module
88
96
89
97
# _logger.debug("import main from %r" % import_path)
90
98
# NOTE: dynamic module loading to find corresponding main function
91
- module_handle = importlib .import_module (import_path )
92
- main = module_handle .main
99
+ main = _retrieve_handler (backend )
93
100
except Exception as err :
94
- _logger .error ("%s could not import %r (%s) : %s" %
95
- (_addr , backend , import_path , err ))
101
+ _logger .error ("%s could not import %r: %s" %
102
+ (_addr , backend , err ))
96
103
bailout_helper (configuration , backend , output_objects )
97
104
output_objects .extend ([
98
105
{'object_type' : 'error_text' , 'text' :
99
106
'Could not load backend: %s' % html_escape (backend )},
100
107
{'object_type' : 'link' , 'text' : 'Go to default interface' ,
101
108
'destination' : configuration .site_landing_page }
102
109
])
103
- return (output_objects , returnvalues .SYSTEM_ERROR )
110
+ return backend , (output_objects , returnvalues .SYSTEM_ERROR )
104
111
105
112
# _logger.debug("imported main %s" % main)
106
113
107
114
# Now backend value is validated to be safe for output
108
115
109
116
if not isinstance (user_arguments_dict , dict ):
110
- _logger .error ("%s invalid user args %s for %s " % (_addr ,
117
+ _logger .error ("%s invalid user args %s for backend %r " % (_addr ,
111
118
user_arguments_dict ,
112
- import_path ))
119
+ backend ))
113
120
bailout_helper (configuration , backend , output_objects ,
114
121
header_text = 'Input Error' )
115
122
output_objects .append (
116
123
{'object_type' : 'error_text' , 'text' :
117
124
'User input is not on expected format!' })
118
- return (output_objects , returnvalues .INVALID_ARGUMENT )
125
+ return backend , (output_objects , returnvalues .INVALID_ARGUMENT )
119
126
120
127
try :
121
128
(output_objects , (ret_code , ret_msg )) = main (client_id ,
@@ -125,7 +132,7 @@ def stub(configuration, client_id, import_path, backend, user_arguments_dict,
125
132
_logger .error ("%s script crashed:\n %s" % (_addr ,
126
133
traceback .format_exc ()))
127
134
crash_helper (configuration , backend , output_objects )
128
- return (output_objects , returnvalues .ERROR )
135
+ return backend , (output_objects , returnvalues .ERROR )
129
136
130
137
(val_ret , val_msg ) = validate (output_objects )
131
138
if not val_ret :
@@ -138,7 +145,7 @@ def stub(configuration, client_id, import_path, backend, user_arguments_dict,
138
145
after_time = time .time ()
139
146
output_objects .append ({'object_type' : 'timing_info' , 'text' :
140
147
"done in %.3fs" % (after_time - before_time )})
141
- return (output_objects , (ret_code , ret_msg ))
148
+ return backend , (output_objects , (ret_code , ret_msg ))
142
149
143
150
144
151
def wrap_wsgi_errors (environ , configuration , max_line_len = 100 ):
@@ -193,6 +200,14 @@ def application(environ, start_response):
193
200
*start_response* is a helper function used to deliver the client response.
194
201
"""
195
202
203
+ def _set_os_environ (value ):
204
+ os .environ = value
205
+
206
+ return _application (None , environ , start_response , _set_environ = _set_os_environ , _wrap_wsgi_errors = wrap_wsgi_errors )
207
+
208
+
209
+ def _application (configuration , environ , start_response , _set_environ , _format_output = format_output , _retrieve_handler = _import_backend , _wrap_wsgi_errors = True , _config_file = None , _skip_log = False ):
210
+
196
211
# NOTE: pass app environ including apache and query args on to sub handlers
197
212
# through the usual 'os.environ' channel expected in functionality
198
213
# handlers. Special care is needed to avoid various sub-interpreter
@@ -235,18 +250,20 @@ def application(environ, start_response):
235
250
os_env_value ))
236
251
237
252
# Assign updated environ to LOCAL os.environ for the rest of this session
238
- os . environ = environ
253
+ _set_environ ( environ )
239
254
240
255
# NOTE: redirect stdout to stderr in python 2 only. It breaks logger in 3
241
256
# and stdout redirection apparently is already handled there.
242
257
if sys .version_info [0 ] < 3 :
243
258
sys .stdout = sys .stderr
244
259
245
- configuration = get_configuration_object ()
260
+ if configuration is None :
261
+ configuration = get_configuration_object (_config_file , _skip_log )
262
+
246
263
_logger = configuration .logger
247
264
248
265
# NOTE: replace default wsgi errors to apache error log with our own logs
249
- wrap_wsgi_errors (environ , configuration )
266
+ _wrap_wsgi_errors (environ , configuration )
250
267
251
268
for line in env_sync_status :
252
269
_logger .debug (line )
@@ -298,7 +315,6 @@ def application(environ, start_response):
298
315
default_page = configuration .site_landing_page
299
316
script_name = requested_backend (environ , fallback = default_page ,
300
317
strip_ext = False )
301
- backend = requested_backend (environ , fallback = default_page )
302
318
# _logger.debug('DEBUG: wsgi found backend %s and script %s' %
303
319
# (backend, script_name))
304
320
fieldstorage = cgi .FieldStorage (fp = environ ['wsgi.input' ],
@@ -307,13 +323,12 @@ def application(environ, start_response):
307
323
if 'output_format' in user_arguments_dict :
308
324
output_format = user_arguments_dict ['output_format' ][0 ]
309
325
310
- module_path = 'mig.shared.functionality.%s' % backend
311
326
(allow , msg ) = allow_script (configuration , script_name , client_id )
312
327
if allow :
313
328
# _logger.debug("wsgi handling script: %s" % script_name)
314
- (output_objs , ret_val ) = stub (configuration , client_id ,
315
- module_path , backend ,
316
- user_arguments_dict , environ )
329
+ backend , (output_objs , ret_val ) = stub (configuration , client_id ,
330
+ user_arguments_dict , environ ,
331
+ _retrieve_handler )
317
332
else :
318
333
_logger .warning ("wsgi handling refused script:%s" % script_name )
319
334
(output_objs , ret_val ) = reject_main (client_id ,
@@ -363,7 +378,7 @@ def application(environ, start_response):
363
378
output_objs .append (wsgi_entry )
364
379
365
380
_logger .debug ("call format %r output to %s" % (backend , output_format ))
366
- output = format_output (configuration , backend , ret_code , ret_msg ,
381
+ output = _format_output (configuration , backend , ret_code , ret_msg ,
367
382
output_objs , output_format )
368
383
# _logger.debug("formatted %s output to %s" % (backend, output_format))
369
384
# _logger.debug("output:\n%s" % [output])
@@ -372,7 +387,7 @@ def application(environ, start_response):
372
387
_logger .error (
373
388
"Formatted output is NOT on default str coding: %s" % [output [:100 ]])
374
389
err_mark = '__****__'
375
- output = format_output (configuration , backend , ret_code , ret_msg ,
390
+ output = _format_output (configuration , backend , ret_code , ret_msg ,
376
391
force_default_str_coding_rec (
377
392
output_objs , highlight = err_mark ),
378
393
output_format )
@@ -396,7 +411,14 @@ def application(environ, start_response):
396
411
# NOTE: send response to client but don't crash e.g. on closed connection
397
412
try :
398
413
start_response (status , response_headers )
414
+ except IOError as ioe :
415
+ _logger .warning ("WSGI %s for %s could not deliver output: %s" %
416
+ (backend , client_id , ioe ))
417
+ except Exception as exc :
418
+ _logger .error ("WSGI %s for %s crashed during response: %s" %
419
+ (backend , client_id , exc ))
399
420
421
+ try :
400
422
# NOTE: we consistently hit download error for archive files reaching ~2GB
401
423
# with showfreezefile.py on wsgi but the same on cgi does NOT suffer
402
424
# the problem for the exact same files. It seems wsgi has a limited
@@ -410,12 +432,12 @@ def application(environ, start_response):
410
432
_logger .info ("WSGI %s yielding %d output parts (%db)" %
411
433
(backend , chunk_parts , content_length ))
412
434
# _logger.debug("send chunked %r response to client" % backend)
413
- for i in xrange ( chunk_parts ):
435
+ for i in list ( range ( chunk_parts ) ):
414
436
# _logger.debug("WSGI %s yielding part %d / %d output parts" %
415
437
# (backend, i+1, chunk_parts))
416
438
# end index may be after end of content - but no problem
417
439
part = output [i * download_block_size :(i + 1 )* download_block_size ]
418
- yield part
440
+ yield force_utf8 ( part )
419
441
if chunk_parts > 1 :
420
442
_logger .info ("WSGI %s finished yielding all %d output parts" %
421
443
(backend , chunk_parts ))
0 commit comments