PyJSONProxy

simple proxy and scraper
git clone https://git.ce9e.org/PyJSONProxy.git

commit
474da8ad2523a3329021bde04a437c4984c085b7
parent
d28b945f1a8c255237304178d9cf9d3a4933a18c
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2017-07-28 21:30
use aiohttp directly instead of deprecated fakes

we losse some minor functionality like

-	automatic redirects on missing trailing slash
-	automatic HEAD/OPTIONS routes
-	ordered json response

Diffstat

M jsonproxy/__init__.py 96 +++++++++++++++++++++++++++++++++++++++++++------------------
M setup.py 2 +-

2 files changed, 69 insertions, 29 deletions


diff --git a/jsonproxy/__init__.py b/jsonproxy/__init__.py

@@ -1,13 +1,13 @@
   -1     1 from functools import lru_cache
   -1     2 from pkg_resources import resource_filename
    1     3 import argparse
   -1     4 import asyncio
    2     5 import os
    3     6 import sys
    4     7 
   -1     8 from aiohttp import web
    5     9 import aiohttp
    6    -1 
    7    -1 from fakes import Fakes
    8    -1 from fakes import jsonify
    9    -1 from fakes import make_response
   10    -1 from fakes import abort
   -1    10 import jinja2
   11    11 
   12    12 from .lib import check_config
   13    13 from .lib import _doc
@@ -16,14 +16,32 @@ from .lib import scrape
   16    16 
   17    17 __version__ = '2.0.0'
   18    18 
   19    -1 app = Fakes(__name__)
   -1    19 CONFIG = {}
   20    20 
   21    21 
   22    22 def get_config(endpoint):
   23    23 	try:
   24    -1 		return app.config[ENDPOINTS][endpoint]
   -1    24 		return CONFIG[ENDPOINTS][endpoint]
   25    25 	except KeyError:
   26    -1 		abort(404)
   -1    26 		raise aiohttp.web_exceptions.HTTPNotFound
   -1    27 
   -1    28 
   -1    29 @lru_cache()
   -1    30 def get_template(name):
   -1    31 	local_path = os.path.join('templates', name)
   -1    32 	path = resource_filename(__name__, local_path)
   -1    33 	with open(path) as fh:
   -1    34 		s = fh.read()
   -1    35 		return jinja2.Template(s)
   -1    36 
   -1    37 
   -1    38 def render_template(name, **kwargs):
   -1    39 	"""Shortcut for rendering a jinja template to a response."""
   -1    40 	template = get_template(name)
   -1    41 	text = template.render(**kwargs)
   -1    42 	return web.Response(
   -1    43 		body=text.encode('utf8'),
   -1    44 		content_type='text/html')
   27    45 
   28    46 
   29    47 def async_cache(maxsize=128):
@@ -43,17 +61,16 @@ def async_cache(maxsize=128):
   43    61 
   44    62 @async_cache()
   45    63 async def _request(method, url):
   46    -1 	app.logger.info('{}:{}'.format(method, url))
   -1    64 	print('{}:{}'.format(method, url))
   47    65 	async with aiohttp.request(method, url) as response:
   48    -1 		if response.status != 200:
   49    -1 			abort(response.status)
   50    -1 		else:
   51    -1 			# get response before closing the connection
   52    -1 			await response.read()
   53    -1 			return response
   -1    66 		if response.status == 404:
   -1    67 			raise aiohttp.web_exceptions.HTTPNotFound
   -1    68 		response.raise_for_status()
   -1    69 		# get response before closing the connection
   -1    70 		await response.read()
   -1    71 		return response
   54    72 
   55    73 
   56    -1 @app.route('/{endpoint}/{path:.+}')
   57    74 async def handle(request):
   58    75 	endpoint = request.match_info['endpoint']
   59    76 
@@ -70,27 +87,26 @@ async def handle(request):
   70    87 	body = await remote.read()
   71    88 
   72    89 	if 'fields' in config:
   73    -1 		response = jsonify(scrape(url, body, config), status=remote.status)
   -1    90 		data = scrape(url, body, config)
   -1    91 		response = web.json_response(data, status=remote.status)
   74    92 
   75    -1 	if app.config.get('ALLOW_CORS', False):
   -1    93 	if CONFIG.get('ALLOW_CORS', False):
   76    94 		response.headers['Access-Control-Allow-Origin'] = '*'
   77    95 
   78    96 	return response
   79    97 
   80    98 
   81    -1 @app.route('/')
   82    99 def index(request):
   83    -1 	config = app.config[ENDPOINTS]
   -1   100 	config = CONFIG[ENDPOINTS]
   84   101 	data = [_doc(config[endpoint], endpoint) for endpoint in config]
   85    -1 	return app.render_template('index.html', endpoints=data)
   -1   102 	return render_template('index.html', endpoints=data)
   86   103 
   87   104 
   88    -1 @app.route('/{endpoint}/')
   89   105 def doc(request):
   90   106 	endpoint = request.match_info['endpoint']
   91   107 	config = get_config(endpoint)
   92   108 	data = [_doc(config, endpoint)]
   93    -1 	return app.render_template('index.html', endpoints=data)
   -1   109 	return render_template('index.html', endpoints=data)
   94   110 
   95   111 
   96   112 def parse_args():
@@ -106,16 +122,40 @@ def parse_args():
  106   122 def main():
  107   123 	args = parse_args()
  108   124 
  109    -1 	app.config_from_file(os.path.abspath(args.config))
  110    -1 	app.debug = args.debug
   -1   125 	config_path = os.path.abspath(args.config)
   -1   126 	with open(config_path) as fh:
   -1   127 		exec(compile(fh.read(), config_path, 'exec'), CONFIG)  # nosec
  111   128 
  112    -1 	errors = check_config(app.config)
   -1   129 	errors = check_config(CONFIG)
  113   130 	if errors:
  114   131 		for error in errors:
  115    -1 			app.logger.error(error)
   -1   132 			print(error)
  116   133 		sys.exit(1)
  117   134 
  118    -1 	app.run(host=args.host, port=args.port)
   -1   135 	loop = asyncio.get_event_loop()
   -1   136 	app = web.Application(loop=loop)
   -1   137 
   -1   138 	app.router.add_route('GET', '/', index)
   -1   139 	app.router.add_route('GET', '/{endpoint}/', doc)
   -1   140 	app.router.add_route('GET', '/{endpoint}/{path:.+}', handle)
   -1   141 
   -1   142 	h = app.make_handler()
   -1   143 	f = loop.create_server(h, args.host, args.port)
   -1   144 	srv = loop.run_until_complete(f)
   -1   145 	msg = "Running on http://{}:{}/ (Press CTRL+C to quit)"
   -1   146 	print(msg.format(args.host, args.port))
   -1   147 
   -1   148 	try:
   -1   149 		loop.run_forever()
   -1   150 	except KeyboardInterrupt:
   -1   151 		pass
   -1   152 	finally:
   -1   153 		loop.run_until_complete(h.finish_connections(1.0))
   -1   154 		srv.close()
   -1   155 		loop.run_until_complete(srv.wait_closed())
   -1   156 		loop.run_until_complete(app.cleanup())
   -1   157 
   -1   158 	loop.close()
  119   159 
  120   160 
  121   161 if __name__ == '__main__':

diff --git a/setup.py b/setup.py

@@ -23,7 +23,7 @@ setup(
   23    23     packages=['jsonproxy'],
   24    24     include_package_data=True,
   25    25     install_requires=[
   26    -1         'Fakes',
   -1    26         'aiohttp',
   27    27         'beautifulsoup4',
   28    28     ],
   29    29     entry_points={'console_scripts': [