diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index a6b9588fce3..d43ba989f28 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -99,6 +99,7 @@ class ApiConfig: self.port = port self.api_password = api_password + host = host.rstrip('/') if host.startswith(("http://", "https://")): self.base_url = host elif use_ssl: diff --git a/homeassistant/components/webhook.py b/homeassistant/components/webhook.py index 6742f33c72d..9ec6d0298ea 100644 --- a/homeassistant/components/webhook.py +++ b/homeassistant/components/webhook.py @@ -19,6 +19,7 @@ DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) +URL_WEBHOOK_PATH = "/api/webhook/{webhook_id}" WS_TYPE_LIST = 'webhook/list' SCHEMA_WS_LIST = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ vol.Required('type'): WS_TYPE_LIST, @@ -58,8 +59,15 @@ def async_generate_id(): @callback @bind_hass def async_generate_url(hass, webhook_id): - """Generate a webhook_id.""" - return "{}/api/webhook/{}".format(hass.config.api.base_url, webhook_id) + """Generate the full URL for a webhook_id.""" + return "{}{}".format(hass.config.api.base_url, + async_generate_path(webhook_id)) + + +@callback +def async_generate_path(webhook_id): + """Generate the path component for a webhook_id.""" + return URL_WEBHOOK_PATH.format(webhook_id=webhook_id) @bind_hass @@ -97,7 +105,7 @@ async def async_setup(hass, config): class WebhookView(HomeAssistantView): """Handle incoming webhook requests.""" - url = "/api/webhook/{webhook_id}" + url = URL_WEBHOOK_PATH name = "api:webhook" requires_auth = False diff --git a/tests/components/http/test_init.py b/tests/components/http/test_init.py index 1c1afe711c6..fadb91a3e03 100644 --- a/tests/components/http/test_init.py +++ b/tests/components/http/test_init.py @@ -80,6 +80,11 @@ class TestApiConfig(unittest.TestCase): api_config = http.ApiConfig('http://example.com', use_ssl=True) assert api_config.base_url == 'http://example.com:8123' + def test_api_base_url_removes_trailing_slash(hass): + """Test a trialing slash is removed when setting the API URL.""" + api_config = http.ApiConfig('http://example.com/') + assert api_config.base_url == 'http://example.com:8123' + async def test_api_base_url_with_domain(hass): """Test setting API URL.""" @@ -124,6 +129,17 @@ async def test_api_no_base_url(hass): assert hass.config.api.base_url == 'http://127.0.0.1:8123' +async def test_api_base_url_removes_trailing_slash(hass): + """Test setting api url.""" + result = await async_setup_component(hass, 'http', { + 'http': { + 'base_url': 'https://example.com/' + } + }) + assert result + assert hass.config.api.base_url == 'https://example.com' + + async def test_not_log_password(hass, aiohttp_client, caplog, legacy_auth): """Test access with password doesn't get logged.""" assert await async_setup_component(hass, 'api', { diff --git a/tests/components/test_webhook.py b/tests/components/test_webhook.py index e67cf7481cc..9c6c9e6a799 100644 --- a/tests/components/test_webhook.py +++ b/tests/components/test_webhook.py @@ -44,6 +44,12 @@ async def test_generate_webhook_url(hass): assert url == 'https://example.com/api/webhook/some_id' +async def test_async_generate_path(hass): + """Test generating just the path component of the url correctly.""" + path = hass.components.webhook.async_generate_path('some_id') + assert path == '/api/webhook/some_id' + + async def test_posting_webhook_nonexisting(hass, mock_client): """Test posting to a nonexisting webhook.""" resp = await mock_client.post('/api/webhook/non-existing')