Не для собственных нужд, но как-то понадобилось написать простой web-сервер для обработки на нём загруженных данных. В общем-то должен был получиться простой вёб-интерфейс к программе. Он и получился, местами даже слишком простой. В общем суть такова – необходимо получить от пользователя экзешник, на сервере его обработать нужными программами и выдать результат, т.е. обработанный файл для скачивания.
Можно было бы написать скрипт на php, что было бы проще, но тогда необходимо с собой таскать какой-нибудь вёб-сервер (apache, denwer и т.п.). На питоне можно его реализовать достаточно просто в самом скрипте используя стандартные заготовки и библиотеки, такие как BaseHTTPServer. Ниже следует листинг с краткими комментариями.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | # -*- 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('Download results.') except Exception: pass if __name__ == "__main__": try: server = HTTPServer(("", 8080), MyHandler) print "started httperver..." 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, который должен располагаться в той же директории, что и сервер:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <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" />XO </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> |