Yay, tests are passing
This commit is contained in:
2
pytest.ini
Normal file
2
pytest.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
addopts = --tb=short
|
3
requests_unixsocket/__init__.py
Normal file
3
requests_unixsocket/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .adapters import UnixAdapter
|
||||
|
||||
__all__ = ['UnixAdapter']
|
54
requests_unixsocket/adapters.py
Normal file
54
requests_unixsocket/adapters.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import socket
|
||||
|
||||
from requests.adapters import HTTPAdapter
|
||||
from requests.compat import urlparse, unquote
|
||||
from requests.packages.urllib3.connection import HTTPConnection
|
||||
from requests.packages.urllib3.connectionpool import HTTPConnectionPool
|
||||
|
||||
|
||||
# The following was adapted from some code from docker-py
|
||||
# https://github.com/docker/docker-py/blob/master/docker/unixconn/unixconn.py
|
||||
class UnixHTTPConnection(HTTPConnection):
|
||||
def __init__(self, unix_socket_url, timeout=60):
|
||||
"""Create an HTTP connection to a unix domain socket
|
||||
|
||||
:param unix_socket_url: A URL with a scheme of 'http+unix' and the
|
||||
netloc is a percent-encoded path to a unix domain socket. E.g.:
|
||||
'http+unix://%2Ftmp%2Fprofilesvc.sock/status/pid'
|
||||
"""
|
||||
HTTPConnection.__init__(self, 'localhost', timeout=timeout)
|
||||
self.unix_socket_url = unix_socket_url
|
||||
self.timeout = timeout
|
||||
|
||||
def connect(self):
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.settimeout(self.timeout)
|
||||
socket_path = unquote(urlparse(self.unix_socket_url).netloc)
|
||||
sock.connect(socket_path)
|
||||
self.sock = sock
|
||||
|
||||
def request(self, method, url, **kwargs):
|
||||
url = urlparse(url).path
|
||||
HTTPConnection.request(self, method, url, **kwargs)
|
||||
|
||||
|
||||
class UnixHTTPConnectionPool(HTTPConnectionPool):
|
||||
def __init__(self, socket_path, timeout=60):
|
||||
HTTPConnectionPool.__init__(self, 'localhost', timeout=timeout)
|
||||
self.socket_path = socket_path
|
||||
self.timeout = timeout
|
||||
|
||||
def _new_conn(self):
|
||||
return UnixHTTPConnection(self.socket_path, self.timeout)
|
||||
|
||||
|
||||
class UnixAdapter(HTTPAdapter):
|
||||
def __init__(self, timeout=60):
|
||||
super(UnixAdapter, self).__init__()
|
||||
self.timeout = timeout
|
||||
|
||||
def get_connection(self, socket_path, proxies=None):
|
||||
if proxies:
|
||||
raise ValueError('%s does not support specifying proxies'
|
||||
% self.__class__.__name__)
|
||||
return UnixHTTPConnectionPool(socket_path, self.timeout)
|
80
requests_unixsocket/tests/test_requests_unixsocket.py
Executable file
80
requests_unixsocket/tests/test_requests_unixsocket.py
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Tests for requests_unixsocket"""
|
||||
|
||||
import multiprocessing
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
import waitress
|
||||
|
||||
from requests_unixsocket import UnixAdapter
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wsgiapp():
|
||||
def _wsgiapp(environ, start_response):
|
||||
start_response(
|
||||
'200 OK',
|
||||
[('X-Transport', 'unix domain socket'),
|
||||
('X-Socket-Path', environ['SERVER_PORT']),
|
||||
('X-Requested-Path', environ['PATH_INFO'])])
|
||||
return ['Hello world!']
|
||||
|
||||
return _wsgiapp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def usock_process(wsgiapp):
|
||||
class UnixSocketServerProcess(multiprocessing.Process):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UnixSocketServerProcess, self).__init__(*args, **kwargs)
|
||||
self.unix_socket = self.get_tempfile_name()
|
||||
|
||||
def get_tempfile_name(self):
|
||||
# I'd rather use tempfile.NamedTemporaryFile but IDNA limits
|
||||
# the hostname to 63 characters and we'll get a "InvalidURL:
|
||||
# URL has an invalid label" error if we exceed that.
|
||||
args = (os.stat(__file__).st_ino,
|
||||
os.getpid(),
|
||||
uuid.uuid4().hex[-8:])
|
||||
return '/tmp/test_requests.%s_%s_%s' % args
|
||||
|
||||
def run(self):
|
||||
waitress.serve(wsgiapp, unix_socket=self.unix_socket)
|
||||
|
||||
return UnixSocketServerProcess()
|
||||
|
||||
|
||||
def test_unix_domain_adapter_ok(usock_process):
|
||||
from requests.compat import quote_plus
|
||||
|
||||
usock_process.start()
|
||||
|
||||
try:
|
||||
session = requests.Session()
|
||||
session.mount('http+unix://', UnixAdapter())
|
||||
urlencoded_socket_name = quote_plus(usock_process.unix_socket)
|
||||
url = 'http+unix://%s/path/to/page' % urlencoded_socket_name
|
||||
r = session.get(url)
|
||||
assert r.status_code == 200
|
||||
assert r.headers['server'] == 'waitress'
|
||||
assert r.headers['X-Transport'] == 'unix domain socket'
|
||||
assert r.headers['X-Requested-Path'] == '/path/to/page'
|
||||
assert r.headers['X-Socket-Path'] == usock_process.unix_socket
|
||||
assert isinstance(r.connection, UnixAdapter)
|
||||
assert r.url == url
|
||||
assert r.text == 'Hello world!'
|
||||
finally:
|
||||
usock_process.terminate()
|
||||
|
||||
|
||||
def test_unix_domain_adapter_connection_error():
|
||||
session = requests.Session()
|
||||
session.mount('http+unix://', UnixAdapter())
|
||||
|
||||
with pytest.raises(requests.ConnectionError):
|
||||
session.get('http+unix://socket_does_not_exist/path/to/page')
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
requests>=1.1
|
28
setup.cfg
Normal file
28
setup.cfg
Normal file
@@ -0,0 +1,28 @@
|
||||
[metadata]
|
||||
name = requests-unixsocket
|
||||
author = Marc Abramowitz
|
||||
author-email = marc@marc-abramowitz.com
|
||||
summary = Use requests to talk HTTP via a UNIX domain socket
|
||||
description-file = README.rst
|
||||
license = Apache-2
|
||||
home-page = https://github.com/msabramo/requests-unixsocket
|
||||
# home-page = https://requests-unixsocket.readthedocs.org/
|
||||
classifier =
|
||||
Development Status :: 3 - Alpha
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: Information Technology
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 2.6
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.3
|
||||
test_suite = requests_unixsocket.tests
|
||||
|
||||
[files]
|
||||
packages = requests_unixsocket
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
8
setup.py
Executable file
8
setup.py
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True,
|
||||
)
|
2
test-requirements.txt
Normal file
2
test-requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
pytest
|
||||
waitress
|
43
tox.ini
Normal file
43
tox.ini
Normal file
@@ -0,0 +1,43 @@
|
||||
[tox]
|
||||
envlist = py26, py27, py33, py34, pypy, pep8
|
||||
|
||||
[testenv]
|
||||
commands = py.test {posargs:requests_unixsocket/tests}
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8 requests_unixsocket
|
||||
deps =
|
||||
flake8
|
||||
{[testenv]deps}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:coverage]
|
||||
commands =
|
||||
coverage erase
|
||||
coverage run --source requests_unixsocket -m py.test requests_unixsocket/tests
|
||||
coverage html
|
||||
deps =
|
||||
coverage
|
||||
{[testenv]deps}
|
||||
|
||||
[testenv:doctest]
|
||||
# note this only works under python 3 because of unicode literals
|
||||
commands =
|
||||
python -m doctest README.rst
|
||||
|
||||
[testenv:sphinx-doctest]
|
||||
# note this only works under python 3 because of unicode literals
|
||||
commands =
|
||||
mkdir build/sphinx/doctest
|
||||
sphinx-build -b doctest docs build/sphinx/doctest
|
||||
deps =
|
||||
pbr
|
||||
{[testenv]deps}
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
Reference in New Issue
Block a user