@@ -45,6 +45,16 @@ if sys.version_info[0] == 3:
4545logger = logger.getChild('search')
4646
4747number_of_searches = 0
48+max_request_timeout = settings.get('outgoing', {}).get('max_request_timeout' or None)
49+if max_request_timeout is None:
50+ logger.info('max_request_timeout={0}'.format(max_request_timeout))
51+else:
52+ if isinstance(max_request_timeout, float):
53+ logger.info('max_request_timeout={0} second(s)'.format(max_request_timeout))
54+ else:
55+ logger.critical('outgoing.max_request_timeout if defined has to be float')
56+ from sys import exit
57+ exit(1)
4858
4959
5060def send_http_request(engine, request_params):
@@ -265,6 +275,15 @@ def get_search_query_from_webapp(preferences, form):
265275 # query_engines
266276 query_engines = raw_text_query.engines
267277
278+ # timeout_limit
279+ query_timeout = raw_text_query.timeout_limit
280+ if query_timeout is None and 'timeout_limit' in form:
281+ raw_time_limit = form.get('timeout_limit')
282+ try:
283+ query_timeout = float(raw_time_limit)
284+ except ValueError:
285+ raise SearxParameterException('timeout_limit', raw_time_limit)
286+
268287 # query_categories
269288 query_categories = []
270289
@@ -338,7 +357,8 @@ def get_search_query_from_webapp(preferences, form):
338357 query_engines = deduplicate_query_engines(query_engines)
339358
340359 return (SearchQuery(query, query_engines, query_categories,
341− query_lang, query_safesearch, query_pageno, query_time_range),
360+ query_lang, query_safesearch, query_pageno,
361+ query_time_range, query_timeout),
342362 raw_text_query)
343363
344364
@@ -351,6 +371,7 @@ class Search(object):
351371 super(Search, self).__init__()
352372 self.search_query = search_query
353373 self.result_container = ResultContainer()
374+ self.actual_timeout = None
354375
355376 # do search-request
356377 def search(self):
@@ -380,7 +401,7 @@ class Search(object):
380401 search_query = self.search_query
381402
382403 # max of all selected engine timeout
383− timeout_limit = 0
404+ default_timeout = 0
384405
385406 # start search-reqest for all selected engines
386407 for selected_engine in search_query.engines:
@@ -420,12 +441,32 @@ class Search(object):
420441 # append request to list
421442 requests.append((selected_engine['name'], search_query.query, request_params))
422443
423− # update timeout_limit
424− timeout_limit = max(timeout_limit, engine.timeout)
425−
444+ # update default_timeout
445+ default_timeout = max(default_timeout, engine.timeout)
446+
447+ # adjust timeout
448+ self.actual_timeout = default_timeout
449+ query_timeout = self.search_query.timeout_limit
450+
451+ if max_request_timeout is None and query_timeout is None:
452+ # No max, no user query: default_timeout
453+ pass
454+ elif max_request_timeout is None and query_timeout is not None:
455+ # No max, but user query: From user query except if above default
456+ self.actual_timeout = min(default_timeout, query_timeout)
457+ elif max_request_timeout is not None and query_timeout is None:
458+ # Max, no user query: Default except if above max
459+ self.actual_timeout = min(default_timeout, max_request_timeout)
460+ elif max_request_timeout is not None and query_timeout is not None:
461+ # Max & user query: From user query except if above max
462+ self.actual_timeout = min(query_timeout, max_request_timeout)
463+
464+ logger.debug("actual_timeout={0} (default_timeout={1}, ?timeout_limit={2}, max_request_timeout={3})"
465+ .format(self.actual_timeout, default_timeout, query_timeout, max_request_timeout))
466+
467+ # send all search-request
426468 if requests:
427− # send all search-request
428− search_multiple_requests(requests, self.result_container, start_time, timeout_limit)
469+ search_multiple_requests(requests, self.result_container, start_time, self.actual_timeout)
429470 start_new_thread(gc.collect, tuple())
430471
431472 # return results, suggestions, answers and infoboxes