From 687a6a734de9a27f04563feb11aa1112eb8a21d8 Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Thu, 5 Feb 2015 15:25:00 +0100 Subject: [PATCH] Added support for transport adapter plugins #276, #298 --- httpie/client.py | 19 ++++++++++++++++--- httpie/plugins/__init__.py | 10 +++++++++- httpie/plugins/base.py | 21 ++++++++++++++++++++- httpie/plugins/manager.py | 7 +++++++ httpie/sessions.py | 19 ++++++++++--------- 5 files changed, 62 insertions(+), 14 deletions(-) diff --git a/httpie/client.py b/httpie/client.py index 1073c63c..79e01ba6 100644 --- a/httpie/client.py +++ b/httpie/client.py @@ -15,16 +15,29 @@ JSON = 'application/json; charset=utf-8' DEFAULT_UA = 'HTTPie/%s' % __version__ +def get_requests_session(): + + requests_session = requests.Session() + for adapter_plugin_cls in plugin_manager.get_adapter_plugins(): + adapter_plugin = adapter_plugin_cls() + requests_session.mount(prefix=adapter_plugin.prefix, + adapter=adapter_plugin.get_adapter()) + return requests_session + + def get_response(args, config_dir): """Send the request and return a `request.Response`.""" + requests_session = get_requests_session() + if not args.session and not args.session_read_only: - requests_kwargs = get_requests_kwargs(args) + kwargs = get_requests_kwargs(args) if args.debug: - dump_request(requests_kwargs) - response = requests.request(**requests_kwargs) + dump_request(kwargs) + response = requests_session.request(**kwargs) else: response = sessions.get_response( + requests_session=requests_session, args=args, config_dir=config_dir, session_name=args.session or args.session_read_only, diff --git a/httpie/plugins/__init__.py b/httpie/plugins/__init__.py index d822de18..4be81e7a 100644 --- a/httpie/plugins/__init__.py +++ b/httpie/plugins/__init__.py @@ -1,4 +1,12 @@ -from httpie.plugins.base import AuthPlugin, FormatterPlugin, ConverterPlugin +""" +WARNING: The plugin API is still work in progress and will + probably be completely reworked by v1.0.0. + +""" +from httpie.plugins.base import ( + AuthPlugin, FormatterPlugin, + ConverterPlugin, TransportPlugin +) from httpie.plugins.manager import PluginManager from httpie.plugins.builtin import BasicAuthPlugin, DigestAuthPlugin from httpie.output.formatters.headers import HeadersFormatter diff --git a/httpie/plugins/base.py b/httpie/plugins/base.py index 187c874b..d1211540 100644 --- a/httpie/plugins/base.py +++ b/httpie/plugins/base.py @@ -15,7 +15,7 @@ class AuthPlugin(BasePlugin): """ Base auth plugin class. - See for an example auth plugin. + See for an example auth plugin. """ # The value that should be passed to --auth-type @@ -30,6 +30,25 @@ class AuthPlugin(BasePlugin): raise NotImplementedError() +class TransportPlugin(BasePlugin): + """ + + http://docs.python-requests.org/en/latest/user/advanced/#transport-adapters + + """ + + # The URL prefix the adapter should be mount to. + prefix = None + + def get_adapter(self): + """ + Return a ``requests.adapters.BaseAdapter`` subclass instance to be + mounted to ``self.prefix``. + + """ + raise NotImplementedError() + + class ConverterPlugin(object): def __init__(self, mime): diff --git a/httpie/plugins/manager.py b/httpie/plugins/manager.py index 82bfcb81..957b8a6c 100644 --- a/httpie/plugins/manager.py +++ b/httpie/plugins/manager.py @@ -1,12 +1,14 @@ from itertools import groupby from pkg_resources import iter_entry_points from httpie.plugins import AuthPlugin, FormatterPlugin, ConverterPlugin +from httpie.plugins.base import TransportPlugin ENTRY_POINT_NAMES = [ 'httpie.plugins.auth.v1', 'httpie.plugins.formatter.v1', 'httpie.plugins.converter.v1', + 'httpie.plugins.transport.v1', ] @@ -56,3 +58,8 @@ class PluginManager(object): def get_converters(self): return [plugin for plugin in self if issubclass(plugin, ConverterPlugin)] + + # Adapters + def get_adapter_plugins(self): + return [plugin for plugin in self + if issubclass(plugin, TransportPlugin)] diff --git a/httpie/sessions.py b/httpie/sessions.py index 4c42a085..5280146c 100644 --- a/httpie/sessions.py +++ b/httpie/sessions.py @@ -4,7 +4,6 @@ import re import os -import requests from requests.cookies import RequestsCookieJar, create_cookie from httpie.compat import urlsplit @@ -21,7 +20,8 @@ VALID_SESSION_NAME_PATTERN = re.compile('^[a-zA-Z0-9_.-]+$') SESSION_IGNORED_HEADER_PREFIXES = ['Content-', 'If-'] -def get_response(session_name, config_dir, args, read_only=False): +def get_response(requests_session, session_name, + config_dir, args, read_only=False): """Like `client.get_response`, but applies permanent aspects of the session to the request. @@ -32,7 +32,9 @@ def get_response(session_name, config_dir, args, read_only=False): else: hostname = (args.headers.get('Host', None) or urlsplit(args.url).netloc.split('@')[-1]) - assert re.match('^[a-zA-Z0-9_.:-]+$', hostname) + if not hostname: + # HACK/FIXME: httpie-unixsocket's URLs have no hostname. + hostname = 'localhost' # host:port => host_port hostname = hostname.replace(':', '_') @@ -44,10 +46,10 @@ def get_response(session_name, config_dir, args, read_only=False): session = Session(path) session.load() - requests_kwargs = get_requests_kwargs(args, base_headers=session.headers) + kwargs = get_requests_kwargs(args, base_headers=session.headers) if args.debug: - dump_request(requests_kwargs) - session.update_headers(requests_kwargs['headers']) + dump_request(kwargs) + session.update_headers(kwargs['headers']) if args.auth: session.auth = { @@ -56,13 +58,12 @@ def get_response(session_name, config_dir, args, read_only=False): 'password': args.auth.value, } elif session.auth: - requests_kwargs['auth'] = session.auth + kwargs['auth'] = session.auth - requests_session = requests.Session() requests_session.cookies = session.cookies try: - response = requests_session.request(**requests_kwargs) + response = requests_session.request(**kwargs) except Exception: raise else: