Python: Реализация простого web-сервера.
Не для собственных нужд, но как-то понадобилось написать простой web-сервер для обработки на нём загруженных данных. В общем-то должен был получиться простой вёб-интерфейс к программе. Он и получился, местами даже слишком простой. В общем суть такова — необходимо получить от пользователя экзешник, на сервере его обработать нужными программами и выдать результат, т.е. обработанный файл для скачивания. Можно было бы написать скрипт на php, что было бы проще, но тогда необходимо с собой таскать какой-нибудь вёб-сервер (apache, denwer и т.п.). На питоне можно его реализовать достаточно просто в самом скрипте используя стандартные заготовки и библиотеки, такие как BaseHTTPServer. Ниже следует листинг с краткими комментариями.
# -*- coding: utf-8 -*- import cgi from os import curdir, sep from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer import subprocess class MyHandler(BaseHTTPRequestHandler): def do_GET(self): try: if self.path != "/output.exe": f = open(curdir+sep+"upload.html") self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(f.read()) f.close() else: self.send_response(200) self.send_header("Content-type", "application/octet-stream") self.end_headers() self.wfile.write(open(curdir+sep+"output.exe", "rb").read()) except IOError: self.send_error(404,"File Not Found: %s" % self.path) def do_POST(self): try: ctype, pdict = cgi.parse_header(self.headers.getheader("content-type")) if ctype == "multipart/form-data": query = cgi.parse_multipart(self.rfile, pdict) self.send_response(200) self.end_headers() upfile = query.get("file") f = open(curdir+sep+"output.exe", "wb") f.write(upfile[0]) f.close() params = " np output.exe" p = query.get("encryption") if p[0] == "aes": params += " sf 1" elif p[0] == "rc5": params += " sf 2" elif p[0] == "xor": params += " sf 3" else: params += " sf 0" p = query.get("hw_bind") if p[0] == "yes": p = query.get("hw_bind_serial") assert len(p[0]) == 8 params += " sn " + p[0] else: params += " sn 0" p = query.get("passwd") assert len(p[0]) > 0 params += " pass " + p[0] p = query.get("pack") if p[0] == "yes": params += " pack 1" else: params += " pack 0" pipe = subprocess.Popen("processor.exe "+params, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) pipe.stdin.close() pipe.wait() self.wfile.write('<a href="/output.exe">Download results</a>.'); except : pass if __name__ == "__main__": try: server = HTTPServer(("", 8080), MyHandler) print "started httpserver..." server.serve_forever() except KeyboardInterrupt: print "^C received, shutting down server" server.socket.close()
Итак, для начала импортируем из библиотеки BaseHTTPServer два класса: HTTPserver — собственно, сам вёб-сервер и BaseHTTPRequestHandler — класс, который служит для обработки запросов от пользователей.
Объявляем свой класс MyHandler, который наследуется от BaseHTTPRequestHandler. И перегружаем в нём два метода — do_GET и do_POST для обработки данных, переданных методом GET и POST, соответственно.
В первом обработчике проверяем запрос от пользователя (содержится в self.path), если запрашиваемый документ не является /output.exe, то выдаём форму для загрузки файла, иначе — считываем и выдаём output.exe. Так же, перед этим, посылается ответ от сервера с кодом 200, что соответствует успешной обработке запроса от клиента и передаём заголовок, в котором указан тип содержимого (text/html для html-код или application/octet-stream для бинарного файла).
В обработке POST-запроса проверяем какие данные были переданы. Сначала получаем файл и записываем его под именем output.exe. Далее формируем строку параметров на основе переданных от пользователя данных, запускаем хранящуюся на сервере программу для обработки полученных файлов, ждём пока она завершится и выдаём ссылку на скачивание результата.
Далее, в самой программе создаём вёб-сервер, указав при этом порт, на котором он будет работать (в примере это 8080) и класс, который необходимо использовать для обработки запросов от пользователей.
Недостаток заключается в том, что данная реализация не сможет обработать одновременную загрузку файлов от разных пользователей, т.к. в лучшем случае просто один из запросов затрёт файл output.exe, полученный в другом запросе. Но такая задача не стояла. На мой взгляд хороший пример для демонстрации возможностей python’а.
Так же код html-файла upload.html, который должен располагаться в той же директории, что и сервер:
<form action="/" enctype="multipart/form-data" method="post"> <table border="1"> <tbody> <tr> <td>Encryption:</td> <td> <input checked="checked" name="encryption" type="radio" value="none" />None <input name="encryption" type="radio" value="aes" />AES <input name="encryption" type="radio" value="rc5" />RC5 <input name="encryption" type="radio" value="xor" />XOR</td> </tr> <tr> <td>Hardware binding:</td> <td> <input checked="checked" name="hw_bind" type="radio" value="no" />No <input name="hw_bind" type="radio" value="yes" />Yes <input name="hw_bind_serial" value="media serial number" /></td> </tr> <tr> <td>Password:</td> <td> <input name="passwd" type="password" /></td> </tr> <tr> <td>Pack:</td> <td> <input checked="checked" name="pack" type="radio" value="no" />No <input name="pack" type="radio" value="yes" />Yes</td> </tr> <tr> <td colspan="2"> <input name="file" type="file" /> <input type="submit" value="Отправить" /></td> </tr> </tbody></table> </form>
Ссылки к статье:
http://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP — Список кодов состояния HTTP.



(2 голосов, средний: 3,50 из 5)
Последние комментарии