Add CORS (Cross Origin Resource Sharing) support to HTTP
parent
16adc30210
commit
ab294d12f7
|
@ -16,18 +16,23 @@ from http import cookies
|
|||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
from socketserver import ThreadingMixIn
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.bootstrap as bootstrap
|
||||
import homeassistant.core as ha
|
||||
import homeassistant.remote as rem
|
||||
import homeassistant.util as util
|
||||
import homeassistant.util.dt as date_util
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import (
|
||||
CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT_PLAIN, HTTP_HEADER_ACCEPT_ENCODING,
|
||||
HTTP_HEADER_CACHE_CONTROL, HTTP_HEADER_CONTENT_ENCODING,
|
||||
HTTP_HEADER_CONTENT_LENGTH, HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_EXPIRES,
|
||||
HTTP_HEADER_HA_AUTH, HTTP_HEADER_VARY, HTTP_METHOD_NOT_ALLOWED,
|
||||
HTTP_HEADER_HA_AUTH, HTTP_HEADER_VARY,
|
||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, HTTP_METHOD_NOT_ALLOWED,
|
||||
HTTP_NOT_FOUND, HTTP_OK, HTTP_UNAUTHORIZED, HTTP_UNPROCESSABLE_ENTITY,
|
||||
ALLOWED_CORS_HEADERS,
|
||||
SERVER_PORT, URL_ROOT, URL_API_EVENT_FORWARD)
|
||||
|
||||
DOMAIN = "http"
|
||||
|
@ -38,6 +43,7 @@ CONF_SERVER_PORT = "server_port"
|
|||
CONF_DEVELOPMENT = "development"
|
||||
CONF_SSL_CERTIFICATE = 'ssl_certificate'
|
||||
CONF_SSL_KEY = 'ssl_key'
|
||||
CONF_CORS_ORIGINS = 'cors_allowed_origins'
|
||||
|
||||
DATA_API_PASSWORD = 'api_password'
|
||||
|
||||
|
@ -48,6 +54,19 @@ SESSION_KEY = 'sessionId'
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Optional(CONF_API_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_SERVER_HOST): cv.string,
|
||||
vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT):
|
||||
vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)),
|
||||
vol.Optional(CONF_DEVELOPMENT): cv.string,
|
||||
vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
|
||||
vol.Optional(CONF_SSL_KEY): cv.isfile,
|
||||
vol.Optional(CONF_CORS_ORIGINS): cv.ensure_list
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the HTTP API and debug interface."""
|
||||
|
@ -61,11 +80,12 @@ def setup(hass, config):
|
|||
development = str(conf.get(CONF_DEVELOPMENT, "")) == "1"
|
||||
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
|
||||
ssl_key = conf.get(CONF_SSL_KEY)
|
||||
cors_origins = conf.get(CONF_CORS_ORIGINS, [])
|
||||
|
||||
try:
|
||||
server = HomeAssistantHTTPServer(
|
||||
(server_host, server_port), RequestHandler, hass, api_password,
|
||||
development, ssl_certificate, ssl_key)
|
||||
development, ssl_certificate, ssl_key, cors_origins)
|
||||
except OSError:
|
||||
# If address already in use
|
||||
_LOGGER.exception("Error setting up HTTP server")
|
||||
|
@ -96,7 +116,8 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, server_address, request_handler_class,
|
||||
hass, api_password, development, ssl_certificate, ssl_key):
|
||||
hass, api_password, development, ssl_certificate, ssl_key,
|
||||
cors_origins):
|
||||
"""Initialize the server."""
|
||||
super().__init__(server_address, request_handler_class)
|
||||
|
||||
|
@ -107,6 +128,7 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||
self.paths = []
|
||||
self.sessions = SessionStore()
|
||||
self.use_ssl = ssl_certificate is not None
|
||||
self.cors_origins = cors_origins
|
||||
|
||||
# We will lazy init this one if needed
|
||||
self.event_forwarder = None
|
||||
|
@ -351,6 +373,16 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||
self.send_header(HTTP_HEADER_VARY, HTTP_HEADER_ACCEPT_ENCODING)
|
||||
|
||||
self.send_header(HTTP_HEADER_CONTENT_LENGTH, str(len(content)))
|
||||
|
||||
cors_check = (self.headers.get("Origin") in self.server.cors_origins)
|
||||
|
||||
cors_headers = ", ".join(ALLOWED_CORS_HEADERS)
|
||||
|
||||
if self.server.cors_origins and cors_check:
|
||||
self.send_header(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
self.headers.get("Origin"))
|
||||
self.send_header(HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
cors_headers)
|
||||
self.end_headers()
|
||||
|
||||
if self.command == 'HEAD':
|
||||
|
|
|
@ -229,6 +229,15 @@ HTTP_HEADER_VARY = "Vary"
|
|||
HTTP_HEADER_CONTENT_LENGTH = "Content-Length"
|
||||
HTTP_HEADER_CACHE_CONTROL = "Cache-Control"
|
||||
HTTP_HEADER_EXPIRES = "Expires"
|
||||
HTTP_HEADER_ORIGIN = "Origin"
|
||||
HTTP_HEADER_X_REQUESTED_WITH = "X-Requested-With"
|
||||
HTTP_HEADER_ACCEPT = "Accept"
|
||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"
|
||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"
|
||||
|
||||
ALLOWED_CORS_HEADERS = [HTTP_HEADER_ORIGIN, HTTP_HEADER_ACCEPT,
|
||||
HTTP_HEADER_X_REQUESTED_WITH, HTTP_HEADER_CONTENT_TYPE,
|
||||
HTTP_HEADER_HA_AUTH]
|
||||
|
||||
CONTENT_TYPE_JSON = "application/json"
|
||||
CONTENT_TYPE_MULTIPART = 'multipart/x-mixed-replace; boundary={}'
|
||||
|
|
Loading…
Reference in New Issue