diff --git a/docs/api/httpclient.rst b/docs/api/httpclient.rst new file mode 100644 index 00000000..85e258c3 --- /dev/null +++ b/docs/api/httpclient.rst @@ -0,0 +1,9 @@ +.. _httpclient-helper: + +************************************************ +:mod:`mopidy.httpclient` --- HTTP Client helpers +************************************************ + +.. automodule:: mopidy.httpclient + :synopsis: HTTP Client helpers for Mopidy its Extensions. + :members: diff --git a/docs/api/index.rst b/docs/api/index.rst index 3a79af5d..d4bd2f61 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -54,6 +54,7 @@ Utilities .. toctree:: - config commands + config + httpclient zeroconf diff --git a/docs/changelog.rst b/docs/changelog.rst index d010ba5f..bb009eb2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -44,6 +44,12 @@ Models reuse instances. For the test data set this was developed against, a library of ~14000 tracks, went from needing ~75MB to ~17MB. (Fixes: :issue:`348`) +Utils +----- + +- Add :func:`mopidy.httpclient.format_proxy` and + :func:`mopidy.httpclient.format_user_agent`. (Part of: :issue:`1156`) + Internal changes ---------------- diff --git a/mopidy/audio/utils.py b/mopidy/audio/utils.py index 1a8bf6a7..3b9ea30f 100644 --- a/mopidy/audio/utils.py +++ b/mopidy/audio/utils.py @@ -8,7 +8,7 @@ import pygst pygst.require('0.10') import gst # noqa -from mopidy import compat +from mopidy import compat, httpclient from mopidy.models import Album, Artist, Track logger = logging.getLogger(__name__) @@ -142,11 +142,7 @@ def setup_proxy(element, config): if not hasattr(element.props, 'proxy') or not config.get('hostname'): return - proxy = "%s://%s:%d" % (config.get('scheme', 'http'), - config.get('hostname'), - config.get('port', 80)) - - element.set_property('proxy', proxy) + element.set_property('proxy', httpclient.format_proxy(config, auth=False)) element.set_property('proxy-id', config.get('username')) element.set_property('proxy-pw', config.get('password')) diff --git a/mopidy/httpclient.py b/mopidy/httpclient.py new file mode 100644 index 00000000..d0b3cb4c --- /dev/null +++ b/mopidy/httpclient.py @@ -0,0 +1,48 @@ +from __future__ import unicode_literals + +import platform + +import mopidy + +"Helpers for configuring HTTP clients used in Mopidy extensions." + + +def format_proxy(proxy_config, auth=True): + """Convert a Mopidy proxy config to the commonly used proxy string format. + + Outputs ``scheme://host:port``, ``scheme://user:pass@host:port`` or + :class:`None` depending on the proxy config provided. + + You can also opt out of getting the basic auth by setting ``auth`` to + :type:`False`. + """ + if not proxy_config.get('hostname'): + return None + + port = proxy_config.get('port', 80) + if port < 0: + port = 80 + + if proxy_config.get('username') and proxy_config.get('password') and auth: + template = '{scheme}://{username}:{password}@{hostname}:{port}' + else: + template = '{scheme}://{hostname}:{port}' + + return template.format(scheme=proxy_config.get('scheme') or 'http', + username=proxy_config.get('username'), + password=proxy_config.get('password'), + hostname=proxy_config['hostname'], port=port) + + +def format_user_agent(name=None): + """Construct a User-Agent suitable for use in client code. + + This will identify use by the provided name (which should be + ``dist_name/version``), Mopidy version and Python version. + """ + parts = ['Mopidy/%s' % (mopidy.__version__), + '%s/%s' % (platform.python_implementation(), + platform.python_version())] + if name: + parts.insert(0, name) + return ' '.join(parts) diff --git a/tests/test_httpclient.py b/tests/test_httpclient.py new file mode 100644 index 00000000..63591f80 --- /dev/null +++ b/tests/test_httpclient.py @@ -0,0 +1,38 @@ +from __future__ import unicode_literals + +import re + +import pytest + +from mopidy import httpclient + + +@pytest.mark.parametrize("config,expected", [ + ({}, None), + ({'hostname': 'proxy.lan'}, 'http://proxy.lan:80'), + ({'scheme': None, 'hostname': 'proxy.lan'}, 'http://proxy.lan:80'), + ({'scheme': 'https', 'hostname': 'proxy.lan'}, 'https://proxy.lan:80'), + ({'username': 'user', 'hostname': 'proxy.lan'}, 'http://proxy.lan:80'), + ({'password': 'pass', 'hostname': 'proxy.lan'}, 'http://proxy.lan:80'), + ({'hostname': 'proxy.lan', 'port': 8080}, 'http://proxy.lan:8080'), + ({'hostname': 'proxy.lan', 'port': -1}, 'http://proxy.lan:80'), + ({'username': 'user', 'password': 'pass', 'hostname': 'proxy.lan'}, + 'http://user:pass@proxy.lan:80'), +]) +def test_format_proxy(config, expected): + assert httpclient.format_proxy(config) == expected + + +def test_format_proxy_without_auth(): + config = {'username': 'user', 'password': 'pass', 'hostname': 'proxy.lan'} + formated_proxy = httpclient.format_proxy(config, auth=False) + assert formated_proxy == 'http://proxy.lan:80' + + +@pytest.mark.parametrize("name,expected", [ + (None, r'^Mopidy/[^ ]+ CPython|/[^ ]+$'), + ('Foo', r'^Foo Mopidy/[^ ]+ CPython|/[^ ]+$'), + ('Foo/1.2.3', r'^Foo/1.2.3 Mopidy/[^ ]+ CPython|/[^ ]+$'), +]) +def test_format_user_agent(name, expected): + assert re.match(expected, httpclient.format_user_agent(name))