Я пытаюсь сделать эту криптографическую задачу Matasano, которая включает в себя временную атаку на сервер с искусственно замедленной функцией сравнения строк. В нем говорится использовать "веб-структуру по вашему выбору", но мне не хотелось устанавливать веб-фреймворк, поэтому я решил использовать класс HTTPServer, встроенный в http.server
.
Я придумал что-то, что сработало, но оно было очень медленным, поэтому я попытался ускорить его с использованием (плохо документированного) потока пула, встроенного в multiprocessing.dummy
. Это было намного быстрее, но я заметил что-то странное: если я делаю 8 или меньше запросов одновременно, он работает нормально. Если у меня есть нечто большее, это работает некоторое время и дает мне ошибки в кажущиеся случайными временами. Ошибки кажутся непоследовательными и не всегда одинаковыми, но обычно они имеют Connection refused, invalid argument
, OSError: [Errno 22] Invalid argument
, urllib.error.URLError: <urlopen error [Errno 22] Invalid argument>
, BrokenPipeError: [Errno 32] Broken pipe
или urllib.error.URLError: <urlopen error [Errno 61] Connection refused>
.
Есть ли ограничение на количество соединений, которые может обрабатывать сервер? Я не думаю, что количество потоков как таковых является проблемой, потому что я написал простую функцию, которая выполняла замедленное сравнение строк без запуска веб-сервера и называла ее с 500 одновременными потоками, и она работала нормально. Я не думаю, что просто делать запросы из многих потоков - проблема, потому что я сделал сканеры, которые использовали более 100 потоков (все одновременные запросы на один и тот же сайт), и они отлично работали. Похоже, что HTTPServer не предназначен для надежного размещения производственных веб-сайтов, которые получают большой объем трафика, но я удивлен, что это легко заставить его сбой.
Я попытался постепенно удалить материал из моего кода, который выглядел не связанным с проблемой, как я обычно делаю, когда я диагностирую таинственные ошибки, подобные этому, но в этом случае это было не очень полезно. Похоже, что когда я удалял, по-видимому, несвязанный код, количество соединений, которые сервер мог обрабатывать, постепенно увеличивался, но не было явной причины сбоев.
Кто-нибудь знает, как увеличить количество запросов, которые я могу сделать сразу, или, по крайней мере, почему это происходит?
Мой код сложный, но я придумал эту простую программу, которая демонстрирует проблему:
#!/usr/bin/env python3
import os
import random
from http.server import BaseHTTPRequestHandler, HTTPServer
from multiprocessing.dummy import Pool as ThreadPool
from socketserver import ForkingMixIn, ThreadingMixIn
from threading import Thread
from time import sleep
from urllib.error import HTTPError
from urllib.request import urlopen
class FancyHTTPServer(ThreadingMixIn, HTTPServer):
pass
class MyRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
sleep(random.uniform(0, 2))
self.send_response(200)
self.end_headers()
self.wfile.write(b"foo")
def log_request(self, code=None, size=None):
pass
def request_is_ok(number):
try:
urlopen("http://localhost:31415/test" + str(number))
except HTTPError:
return False
else:
return True
server = FancyHTTPServer(("localhost", 31415), MyRequestHandler)
try:
Thread(target=server.serve_forever).start()
with ThreadPool(200) as pool:
for i in range(10):
numbers = [random.randint(0, 99999) for j in range(20000)]
for j, result in enumerate(pool.imap(request_is_ok, numbers)):
if j % 20 == 0:
print(i, j)
finally:
server.shutdown()
server.server_close()
print("done testing server")
По какой-то причине программа выше работает отлично, если у нее не более 100 потоков или около того, но мой реальный код для вызова может обрабатывать только 8 потоков. Если я запускаю его с 9, я обычно получаю ошибки соединения, и с 10 я всегда получаю ошибки подключения. Я попытался использовать concurrent.futures.ThreadPoolExecutor
, concurrent.futures.ProcessPoolExecutor
и multiprocessing.pool
вместо multiprocessing.dummy.pool
, и ни один из них не помог. Я попытался использовать простой объект HTTPServer
(без ThreadingMixIn
), и это просто заставило вещи работать очень медленно и не устранило проблему. Я попытался использовать ForkingMixIn
, и это тоже не исправило.
Что я должен делать? Я запускаю Python 3.5.1 на MacBook Pro конца 2013 года под управлением OS X 10.11.3.
EDIT: Я пробовал еще несколько вещей, включая запуск сервера в процессе вместо потока, как простой HTTPServer
, с ForkingMixIn
и с ThreadingMixIn
, Ни один из них не помог.
EDIT: Эта проблема незнакома, чем я думал. Я попытался сделать один script с сервером, а другой с большим количеством потоков, делающих запросы, и запускал их на разных вкладках моего терминала. Процесс с сервером прошел нормально, но один из запросов делался с ошибкой. Исключениями были сочетание ConnectionResetError: [Errno 54] Connection reset by peer
, urllib.error.URLError: <urlopen error [Errno 54] Connection reset by peer>
, OSError: [Errno 41] Protocol wrong type for socket
, urllib.error.URLError: <urlopen error [Errno 41] Protocol wrong type for socket>
, urllib.error.URLError: <urlopen error [Errno 22] Invalid argument>
.
Я попробовал это с фиктивным сервером, как выше, и если бы я ограничил количество одновременных запросов до 5 или менее, он работал нормально, но с 6 запросами клиентский процесс разбился. Были ошибки на сервере, но он продолжал идти. Клиент разбился, независимо от того, использовал ли я потоки или процессы для выполнения запросов. Затем я попытался помещать замедленную функцию на сервер, и он мог обрабатывать 60 одновременных запросов, но он разбился с 70. Это похоже на то, что это может противоречить доказательствам, что проблема связана с сервером.
РЕДАКТИРОВАТЬ: Я пробовал большую часть вещей, которые я описал, используя requests
вместо urllib.request
и столкнулся с аналогичными проблемами.
EDIT: Теперь я запускаю OS X 10.11.4 и сталкиваюсь с теми же проблемами.