Last active
March 21, 2016 21:08
-
-
Save truetug/aa1fa38253e9d10c4839 to your computer and use it in GitHub Desktop.
Тестовая задачка от ivelum.com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# encoding: utf-8 | |
# habraproxy.py — это простейший http-прокси-сервер, запускаемый локально (порт на ваше | |
# усмотрение), который показывает содержимое страниц Хабра. С одним исключением: после | |
# каждого слова из шести букв должен стоять значок «™». Примерно так: | |
# | |
# http://habrahabr.ru/company/yandex/blog/258673/ | |
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
# Сейчас на фоне уязвимости Logjam все в индустрии в очередной раз обсуждают проблемы и | |
# особенности TLS. Я хочу воспользоваться этой возможностью, чтобы поговорить об одной из | |
# них, а именно — о настройке ciphersiutes. | |
# | |
# http://127.0.0.1:8232/company/yandex/blog/258673/ | |
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
# Сейчас™ на фоне уязвимости Logjam™ все в индустрии в очередной раз обсуждают проблемы и | |
# особенности TLS. Я хочу воспользоваться этой возможностью, чтобы поговорить об одной из | |
# них, а именно™ — о настройке ciphersiutes. | |
# | |
# Условия: | |
# * Python 2.x | |
# * можно использовать любые общедоступные библиотеки, которые сочтёте нужным | |
# * чем меньше кода, тем лучше. PEP8 — обязательно | |
# * в случае, если не хватает каких-то данных, следует опираться на здравый смысл | |
# | |
# Если задача кажется слишом простой, можно добавить следующее: | |
# * параметры командной строки (порт, хост, сайт, отличный от хабра и т.п.) | |
# * после старта локального сервера автоматически запускается браузер с открытой | |
# обработанной™ главной страницей | |
import re | |
import os | |
import imp | |
import signal | |
import logging | |
import webbrowser | |
try: | |
from tornado import gen | |
from tornado.ioloop import IOLoop | |
from tornado.options import define, options, \ | |
parse_command_line, parse_config_file | |
from tornado.web import Application, RequestHandler | |
from tornado.httpclient import AsyncHTTPClient | |
import bs4 | |
imp.find_module('html5lib') | |
except ImportError: | |
requirements = ( | |
'tornado==4.3', | |
'html5lib==0.9999999', | |
'beautifulsoup4==4.4.1' | |
) | |
msg = ( | |
'Please install requirements:', | |
'virtualenv env && ' | |
'. env/bin/activate && ' | |
'pip install {0} && python {1}', | |
) | |
print '\n'.join(msg).format(' '.join(requirements), __file__) | |
exit(1) | |
NAME = 'xa6pokcug' | |
WORD_RE = re.compile( | |
r'(?:^|(?<=\s))(\w{6})(?:(?=\s)|$)', | |
flags=(re.MULTILINE | re.UNICODE) | |
) | |
IGNORE_TAGS = ('script', 'style', 'pre', 'code', 'iframe') | |
logger = logging.getLogger(NAME) | |
logger.setLevel(logging.INFO) | |
ch = logging.StreamHandler() | |
ch.setLevel(logging.INFO) | |
logger.addHandler(ch) | |
define('scheme', default='http', help="") | |
define('address', default='127.0.0.1', help="address to listen on") | |
define('port', default=8888, help="port to listen on") | |
define('config_file', default='config.cfg', | |
help='filename for additional configuration') | |
define('debug', default=False, group='application', | |
help="run in debug mode (with automatic reloading)") | |
define('proxy_host', default='habrahabr.ru', group='application', | |
help="host proxy to") | |
def on_start(): | |
""" | |
Opens browser after app start | |
""" | |
url = "{scheme}://{address}:{port}".format( | |
scheme=options.scheme, | |
address=options.address, | |
port=options.port | |
) | |
webbrowser.open(url, new=2) | |
def on_signal(signum, frame): | |
""" | |
Handles signals | |
""" | |
logger.info('%s shutdowned because of %s', | |
NAME, | |
'{0} signal was recieved'.format(on_signal.signals.get(signum, signum)) | |
) | |
exit(0) | |
on_signal.signals = {2: 'INT', 15: 'TERM'} | |
class MainHandler(RequestHandler): | |
@gen.coroutine | |
def get(self): | |
url = '{protocol}://{host}{path}'.format( | |
protocol=self.request.protocol, host=options.proxy_host, | |
path=self.request.uri | |
) | |
try: | |
response = yield AsyncHTTPClient().fetch(url) | |
content = response.body | |
except Exception as error: | |
response = error.response | |
content = response.body | |
content = self.process(content) | |
self.set_status(response.code) | |
self.write(content) | |
def process(self, content): | |
soup = bs4.BeautifulSoup(content, 'html5lib') | |
domain_re = self.application.settings.get('domain_re') | |
for tag in domain_re and soup.find_all('a', href=domain_re) or (): | |
tag.attrs['href'] = domain_re.sub(u'', tag.attrs['href']) | |
for node in soup.find_all(text=True): | |
if node.string.strip() \ | |
and not isinstance(node, bs4.element.PreformattedString) \ | |
and node.parent.name not in IGNORE_TAGS: | |
node.string.replace_with(WORD_RE.sub(u'\\1™', node.string)) | |
return u'{0}'.format(soup) | |
def main(): | |
# Catch signals | |
signal.signal(signal.SIGINT, on_signal) | |
signal.signal(signal.SIGTERM, on_signal) | |
parse_command_line(final=False) | |
if os.path.isfile(options.config_file): | |
parse_config_file(options.config_file) | |
domain_re = re.compile(r'^https?://{0}'.format(options.proxy_host)) | |
logger.info('Come along tornado %s:%s...', options.address, options.port) | |
if options.debug: | |
logger.info( | |
u'\nOptions\n===\n%s\n', | |
u'\n'.join([u'{0}: {1}'.format(k, v) for k, v in options.items()]) | |
) | |
app = Application([ | |
(r".+", MainHandler), | |
], domain_re=domain_re, **options.group_dict('application')) | |
app.listen(options.port, options.address) | |
ioloop = IOLoop.current() | |
if not options.debug: | |
ioloop.add_callback(on_start) | |
ioloop.start() | |
if __name__ == "__main__": | |
main() | |
else: | |
logger.info('Running %s', __name__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# encoding: utf-8 | |
import re | |
import os | |
import imp | |
import signal | |
import webbrowser | |
from tornado import gen | |
from tornado.ioloop import IOLoop | |
from tornado.options import define, options, parse_command_line, \ | |
parse_config_file | |
from tornado.web import Application, RequestHandler | |
from tornado.httpclient import AsyncHTTPClient | |
import bs4 | |
NAME = 'xa6pokcug' | |
WORD_RE = re.compile( | |
r'(?:^|(?<=\s))(\w{6})(?:(?=\s)|$)', flags=(re.MULTILINE | re.UNICODE) | |
) | |
IGNORE_TAGS = ('script', 'style', 'pre', 'code', 'iframe') | |
define('scheme', default='http', help="") | |
define('address', default='127.0.0.1', help="address to listen on") | |
define('port', default=8888, help="port to listen on") | |
define('config_file', default='config.cfg', | |
help='filename for additional configuration') | |
define('debug', default=False, group='application', | |
help="run in debug mode (with automatic reloading)") | |
define('proxy_host', default='habrahabr.ru', group='application', | |
help="host proxy to") | |
def on_start(): | |
url = "{scheme}://{address}:{port}".format( | |
scheme=options.scheme, address=options.address, port=options.port | |
) | |
webbrowser.open(url, new=2) | |
class MainHandler(RequestHandler): | |
@gen.coroutine | |
def get(self): | |
url = '{protocol}://{host}{path}'.format( | |
protocol=self.request.protocol, host=options.proxy_host, | |
path=self.request.uri | |
) | |
try: | |
response = yield AsyncHTTPClient().fetch(url) | |
content = response.body | |
except Exception as error: | |
response = error.response | |
content = response.body | |
content = self.process(content) | |
self.set_status(response.code) | |
self.write(content) | |
def process(self, content): | |
soup = bs4.BeautifulSoup(content, 'html5lib') | |
domain_re = self.application.settings.get('domain_re') | |
for tag in domain_re and soup.find_all('a', href=domain_re) or (): | |
tag.attrs['href'] = domain_re.sub(u'', tag.attrs['href']) | |
for node in soup.find_all(text=True): | |
if node.string.strip() \ | |
and not isinstance(node, bs4.element.PreformattedString) \ | |
and node.parent.name not in IGNORE_TAGS: | |
node.string.replace_with(WORD_RE.sub(u'\\1™', node.string)) | |
return u'{0}'.format(soup) | |
def main(): | |
parse_command_line(final=False) | |
if os.path.isfile(options.config_file): | |
parse_config_file(options.config_file) | |
domain_re = re.compile(r'^https?://{0}'.format(options.proxy_host)) | |
app = Application([ | |
(r".+", MainHandler), | |
], domain_re=domain_re, **options.group_dict('application')) | |
app.listen(options.port, options.address) | |
ioloop = IOLoop.current() | |
if not options.debug: | |
ioloop.add_callback(on_start) | |
ioloop.start() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment