Compare commits

...

13 Commits
0.9.0 ... 0.9.1

11 changed files with 104 additions and 26 deletions

View File

@ -98,10 +98,10 @@ If the above fails, please use ``easy_install`` instead (``$ easy_install httpie
Development version Development version
------------------- -------------------
============= ============= =========== ============= =============
Mac/Linux Windows Tests Mac/Linux Windows
|unix| |windows| |coverage| |unix| |windows|
============= ============= =========== ============= =============
The **latest development version** can be installed directly from GitHub: The **latest development version** can be installed directly from GitHub:
@ -595,6 +595,7 @@ Auth Plugins
* `httpie-ntlm <https://github.com/jakubroztocil/httpie-ntlm>`_: NTLM (NT LAN Manager) * `httpie-ntlm <https://github.com/jakubroztocil/httpie-ntlm>`_: NTLM (NT LAN Manager)
* `httpie-negotiate <https://github.com/ndzou/httpie-negotiate>`_: SPNEGO (GSS Negotiate) * `httpie-negotiate <https://github.com/ndzou/httpie-negotiate>`_: SPNEGO (GSS Negotiate)
* `requests-hawk <https://github.com/mozilla-services/requests-hawk>`_: Hawk * `requests-hawk <https://github.com/mozilla-services/requests-hawk>`_: Hawk
* `httpie-api-auth <https://github.com/pd/httpie-api-auth>`_: ApiAuth
======= =======
@ -1312,7 +1313,13 @@ Changelog
*You can click a version name to see a diff with the previous one.* *You can click a version name to see a diff with the previous one.*
* `1.0.0-dev`_
* `0.9.1`_ (2015-02-07)
* Added support for Requests transport adapter plugins
to enable plugin-provided features such as
`unix socket <https://github.com/msabramo/httpie-unixsocket>`_
communication and
`HTTP/2 <https://github.com/jakubroztocil/httpie-http2>`_.
* `0.9.0`_ (2015-01-31) * `0.9.0`_ (2015-01-31)
* Added ``--cert`` and ``--cert-key`` parameters to specify a client side * Added ``--cert`` and ``--cert-key`` parameters to specify a client side
certificate and private key for SSL certificate and private key for SSL
@ -1322,7 +1329,7 @@ Changelog
now only escapes special characters (the ones that are used as key-value now only escapes special characters (the ones that are used as key-value
separators by HTTPie). separators by HTTPie).
* Switched from ``unittest`` to ``pytest``. * Switched from ``unittest`` to ``pytest``.
* Added Python `wheel` suppor. * Added Python `wheel` support.
* Various test suite improvements. * Various test suite improvements.
* Added `CONTRIBUTING`_. * Added `CONTRIBUTING`_.
* Fixed ``User-Agent`` overwriting when used within a session. * Fixed ``User-Agent`` overwriting when used within a session.
@ -1456,7 +1463,8 @@ Changelog
.. _0.6.0: https://github.com/jakubroztocil/httpie/compare/0.5.1...0.6.0 .. _0.6.0: https://github.com/jakubroztocil/httpie/compare/0.5.1...0.6.0
.. _0.7.1: https://github.com/jakubroztocil/httpie/compare/0.6.0...0.7.1 .. _0.7.1: https://github.com/jakubroztocil/httpie/compare/0.6.0...0.7.1
.. _0.8.0: https://github.com/jakubroztocil/httpie/compare/0.7.1...0.8.0 .. _0.8.0: https://github.com/jakubroztocil/httpie/compare/0.7.1...0.8.0
.. _0.9.0: https://github.com/jakubroztocil/httpie/compare/0.9.0...master .. _0.9.0: https://github.com/jakubroztocil/httpie/compare/0.8.0...0.9.0
.. _0.9.1: https://github.com/jakubroztocil/httpie/compare/0.9.0...0.9.1
.. _1.0.0-dev: https://github.com/jakubroztocil/httpie/compare/0.9.0...master .. _1.0.0-dev: https://github.com/jakubroztocil/httpie/compare/0.9.0...master
.. _LICENSE: https://github.com/jakubroztocil/httpie/blob/master/LICENSE .. _LICENSE: https://github.com/jakubroztocil/httpie/blob/master/LICENSE
.. _Tox: http://tox.testrun.org .. _Tox: http://tox.testrun.org
@ -1466,6 +1474,9 @@ Changelog
.. |version| image:: https://badge.fury.io/py/httpie.svg .. |version| image:: https://badge.fury.io/py/httpie.svg
:target: http://badge.fury.io/py/httpie :target: http://badge.fury.io/py/httpie
.. |coverage| image:: https://coveralls.io/repos/jakubroztocil/httpie/badge.svg?branch=master
:target: https://coveralls.io/r/jakubroztocil/httpie?branch=master
.. |unix| image:: https://api.travis-ci.org/jakubroztocil/httpie.svg .. |unix| image:: https://api.travis-ci.org/jakubroztocil/httpie.svg
:target: http://travis-ci.org/jakubroztocil/httpie :target: http://travis-ci.org/jakubroztocil/httpie
:alt: Build Status of the master branch on Mac/Linux :alt: Build Status of the master branch on Mac/Linux

View File

@ -3,7 +3,7 @@ HTTPie - a CLI, cURL-like tool for humans.
""" """
__author__ = 'Jakub Roztocil' __author__ = 'Jakub Roztocil'
__version__ = '0.9.0' __version__ = '0.9.1'
__licence__ = 'BSD' __licence__ = 'BSD'

View File

@ -15,16 +15,28 @@ JSON = 'application/json; charset=utf-8'
DEFAULT_UA = 'HTTPie/%s' % __version__ DEFAULT_UA = 'HTTPie/%s' % __version__
def get_requests_session():
requests_session = requests.Session()
for cls in plugin_manager.get_trasnsport_plugins():
transport_plugin = cls()
requests_session.mount(prefix=transport_plugin.prefix,
adapter=transport_plugin.get_adapter())
return requests_session
def get_response(args, config_dir): def get_response(args, config_dir):
"""Send the request and return a `request.Response`.""" """Send the request and return a `request.Response`."""
requests_session = get_requests_session()
if not args.session and not args.session_read_only: if not args.session and not args.session_read_only:
requests_kwargs = get_requests_kwargs(args) kwargs = get_requests_kwargs(args)
if args.debug: if args.debug:
dump_request(requests_kwargs) dump_request(kwargs)
response = requests.request(**requests_kwargs) response = requests_session.request(**kwargs)
else: else:
response = sessions.get_response( response = sessions.get_response(
requests_session=requests_session,
args=args, args=args,
config_dir=config_dir, config_dir=config_dir,
session_name=args.session or args.session_read_only, session_name=args.session or args.session_read_only,

View File

@ -21,6 +21,10 @@ from httpie.sessions import VALID_SESSION_NAME_PATTERN
from httpie.utils import load_json_preserve_order from httpie.utils import load_json_preserve_order
# ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
# <http://tools.ietf.org/html/rfc3986#section-3.1>
URL_SCHEME_RE = re.compile(r'^[a-z][a-z0-9.+-]*://', re.IGNORECASE)
HTTP_POST = 'POST' HTTP_POST = 'POST'
HTTP_GET = 'GET' HTTP_GET = 'GET'
HTTP = 'http://' HTTP = 'http://'
@ -132,7 +136,7 @@ class Parser(ArgumentParser):
self._parse_items() self._parse_items()
if not self.args.ignore_stdin and not env.stdin_isatty: if not self.args.ignore_stdin and not env.stdin_isatty:
self._body_from_file(self.env.stdin) self._body_from_file(self.env.stdin)
if not (self.args.url.startswith((HTTP, HTTPS))): if not URL_SCHEME_RE.match(self.args.url):
scheme = HTTP scheme = HTTP
# See if we're using curl style shorthand for localhost (:3000/foo) # See if we're using curl style shorthand for localhost (:3000/foo)

View File

@ -52,7 +52,14 @@ class HTTPResponse(HTTPMessage):
@property @property
def headers(self): def headers(self):
original = self._orig.raw._original_response original = self._orig.raw._original_response
version = {9: '0.9', 10: '1.0', 11: '1.1'}[original.version]
version = {
9: '0.9',
10: '1.0',
11: '1.1',
20: '2',
}[original.version]
status_line = 'HTTP/{version} {status} {reason}'.format( status_line = 'HTTP/{version} {status} {reason}'.format(
version=version, version=version,
status=original.status, status=original.status,

View File

@ -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.manager import PluginManager
from httpie.plugins.builtin import BasicAuthPlugin, DigestAuthPlugin from httpie.plugins.builtin import BasicAuthPlugin, DigestAuthPlugin
from httpie.output.formatters.headers import HeadersFormatter from httpie.output.formatters.headers import HeadersFormatter

View File

@ -15,7 +15,7 @@ class AuthPlugin(BasePlugin):
""" """
Base auth plugin class. Base auth plugin class.
See <https://github.com/jkbr/httpie-ntlm> for an example auth plugin. See <https://github.com/jakubroztocil/httpie-ntlm> for an example auth plugin.
""" """
# The value that should be passed to --auth-type # The value that should be passed to --auth-type
@ -30,6 +30,25 @@ class AuthPlugin(BasePlugin):
raise NotImplementedError() 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): class ConverterPlugin(object):
def __init__(self, mime): def __init__(self, mime):

View File

@ -1,12 +1,14 @@
from itertools import groupby from itertools import groupby
from pkg_resources import iter_entry_points from pkg_resources import iter_entry_points
from httpie.plugins import AuthPlugin, FormatterPlugin, ConverterPlugin from httpie.plugins import AuthPlugin, FormatterPlugin, ConverterPlugin
from httpie.plugins.base import TransportPlugin
ENTRY_POINT_NAMES = [ ENTRY_POINT_NAMES = [
'httpie.plugins.auth.v1', 'httpie.plugins.auth.v1',
'httpie.plugins.formatter.v1', 'httpie.plugins.formatter.v1',
'httpie.plugins.converter.v1', 'httpie.plugins.converter.v1',
'httpie.plugins.transport.v1',
] ]
@ -56,3 +58,8 @@ class PluginManager(object):
def get_converters(self): def get_converters(self):
return [plugin for plugin in self return [plugin for plugin in self
if issubclass(plugin, ConverterPlugin)] if issubclass(plugin, ConverterPlugin)]
# Adapters
def get_trasnsport_plugins(self):
return [plugin for plugin in self
if issubclass(plugin, TransportPlugin)]

View File

@ -4,7 +4,6 @@
import re import re
import os import os
import requests
from requests.cookies import RequestsCookieJar, create_cookie from requests.cookies import RequestsCookieJar, create_cookie
from httpie.compat import urlsplit 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-'] 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 """Like `client.get_response`, but applies permanent
aspects of the session to the request. aspects of the session to the request.
@ -32,7 +32,9 @@ def get_response(session_name, config_dir, args, read_only=False):
else: else:
hostname = (args.headers.get('Host', None) hostname = (args.headers.get('Host', None)
or urlsplit(args.url).netloc.split('@')[-1]) 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 # host:port => host_port
hostname = hostname.replace(':', '_') hostname = hostname.replace(':', '_')
@ -44,10 +46,10 @@ def get_response(session_name, config_dir, args, read_only=False):
session = Session(path) session = Session(path)
session.load() session.load()
requests_kwargs = get_requests_kwargs(args, base_headers=session.headers) kwargs = get_requests_kwargs(args, base_headers=session.headers)
if args.debug: if args.debug:
dump_request(requests_kwargs) dump_request(kwargs)
session.update_headers(requests_kwargs['headers']) session.update_headers(kwargs['headers'])
if args.auth: if args.auth:
session.auth = { session.auth = {
@ -56,13 +58,12 @@ def get_response(session_name, config_dir, args, read_only=False):
'password': args.auth.value, 'password': args.auth.value,
} }
elif session.auth: elif session.auth:
requests_kwargs['auth'] = session.auth kwargs['auth'] = session.auth
requests_session = requests.Session()
requests_session.cookies = session.cookies requests_session.cookies = session.cookies
try: try:
response = requests_session.request(**requests_kwargs) response = requests_session.request(**kwargs)
except Exception: except Exception:
raise raise
else: else:

View File

@ -2,9 +2,9 @@
import json import json
# noinspection PyCompatibility # noinspection PyCompatibility
import argparse import argparse
import os
import pytest import pytest
from requests.exceptions import InvalidSchema
from httpie import input from httpie import input
from httpie.input import KeyValue, KeyValueArgType, DataDict from httpie.input import KeyValue, KeyValueArgType, DataDict
@ -321,3 +321,12 @@ class TestIgnoreStdin:
error_exit_ok=True) error_exit_ok=True)
assert r.exit_status == ExitStatus.ERROR assert r.exit_status == ExitStatus.ERROR
assert 'because --ignore-stdin' in r.stderr assert 'because --ignore-stdin' in r.stderr
class TestSchemes:
def test_custom_scheme(self):
# InvalidSchema is expected because HTTPie
# shouldn't touch a formally valid scheme.
with pytest.raises(InvalidSchema):
http('foo+bar-BAZ.123://bah')

View File

@ -1,8 +1,8 @@
import pytest import pytest
from utils import TestEnvironment, http, HTTP_OK, COLOR, CRLF
from httpie import ExitStatus from httpie import ExitStatus
from httpie.output.formatters.colors import get_lexer from httpie.output.formatters.colors import get_lexer
from utils import TestEnvironment, http, HTTP_OK, COLOR, CRLF
class TestVerboseFlag: class TestVerboseFlag: