Compare commits
4 Commits
nilmdb-1.2
...
nilmdb-1.2
Author | SHA1 | Date | |
---|---|---|---|
2d45466f66 | |||
c6a0e6e96f | |||
79755dc624 | |||
c512631184 |
@@ -53,6 +53,7 @@ def main():
|
||||
|
||||
# Print info
|
||||
if not args.quiet:
|
||||
print "Version: %s" % nilmdb.__version__
|
||||
print "Database: %s" % (os.path.realpath(args.database))
|
||||
if args.address == '0.0.0.0' or args.address == '::':
|
||||
host = socket.getfqdn()
|
||||
|
@@ -221,9 +221,11 @@ class File(object):
|
||||
# An optimized verison of append, to avoid flushing the file
|
||||
# and resizing the mmap after each data point.
|
||||
try:
|
||||
rows = []
|
||||
for i in xrange(count):
|
||||
row = dataiter.next()
|
||||
self._f.write(packer(*row))
|
||||
rows.append(packer(*row))
|
||||
self._f.write("".join(rows))
|
||||
finally:
|
||||
self._f.flush()
|
||||
self.size = self._f.tell()
|
||||
|
@@ -71,16 +71,51 @@ def exception_to_httperror(*expected):
|
||||
# care of that.
|
||||
return decorator.decorator(wrapper)
|
||||
|
||||
# Custom Cherrypy tools
|
||||
def allow_methods(methods):
|
||||
method = cherrypy.request.method.upper()
|
||||
if method not in methods:
|
||||
if method in cherrypy.request.methods_with_bodies:
|
||||
cherrypy.request.body.read()
|
||||
allowed = ', '.join(methods)
|
||||
cherrypy.response.headers['Allow'] = allowed
|
||||
raise cherrypy.HTTPError(405, method + " not allowed; use " + allowed)
|
||||
cherrypy.tools.allow_methods = cherrypy.Tool('before_handler', allow_methods)
|
||||
# Custom CherryPy tools
|
||||
|
||||
def CORS_allow(methods):
|
||||
"""This does several things:
|
||||
|
||||
Handles CORS preflight requests.
|
||||
Adds Allow: header to all requests.
|
||||
Raise 405 if request.method not in method.
|
||||
|
||||
It is similar to cherrypy.tools.allow, with the CORS stuff added.
|
||||
"""
|
||||
request = cherrypy.request.headers
|
||||
response = cherrypy.response.headers
|
||||
|
||||
if not isinstance(methods, (tuple, list)): # pragma: no cover
|
||||
methods = [ methods ]
|
||||
methods = [ m.upper() for m in methods if m ]
|
||||
if not methods: # pragma: no cover
|
||||
methods = [ 'GET', 'HEAD' ]
|
||||
elif 'GET' in methods and 'HEAD' not in methods: # pragma: no cover
|
||||
methods.append('HEAD')
|
||||
response['Allow'] = ', '.join(methods)
|
||||
|
||||
# Allow all origins
|
||||
if 'Origin' in request:
|
||||
response['Access-Control-Allow-Origin'] = request['Origin']
|
||||
|
||||
# If it's a CORS request, send response.
|
||||
request_method = request.get("Access-Control-Request-Method", None)
|
||||
request_headers = request.get("Access-Control-Request-Headers", None)
|
||||
if (cherrypy.request.method == "OPTIONS" and
|
||||
request_method and request_headers):
|
||||
response['Access-Control-Allow-Headers'] = request_headers
|
||||
response['Access-Control-Allow-Methods'] = ', '.join(methods)
|
||||
# Try to stop further processing and return a 200 OK
|
||||
cherrypy.response.status = "200 OK"
|
||||
cherrypy.response.body = ""
|
||||
cherrypy.request.handler = lambda: ""
|
||||
return
|
||||
|
||||
# Reject methods that were not explicitly allowed
|
||||
if cherrypy.request.method not in methods:
|
||||
raise cherrypy.HTTPError(405)
|
||||
|
||||
cherrypy.tools.CORS_allow = cherrypy.Tool('on_start_resource', CORS_allow)
|
||||
|
||||
# CherryPy apps
|
||||
class Root(NilmApp):
|
||||
@@ -142,7 +177,7 @@ class Stream(NilmApp):
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@exception_to_httperror(NilmDBError, ValueError)
|
||||
@cherrypy.tools.allow_methods(methods = ["POST"])
|
||||
@cherrypy.tools.CORS_allow(methods = ["POST"])
|
||||
def create(self, path, layout):
|
||||
"""Create a new stream in the database. Provide path
|
||||
and one of the nilmdb.layout.layouts keys.
|
||||
@@ -153,7 +188,7 @@ class Stream(NilmApp):
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@exception_to_httperror(NilmDBError)
|
||||
@cherrypy.tools.allow_methods(methods = ["POST"])
|
||||
@cherrypy.tools.CORS_allow(methods = ["POST"])
|
||||
def destroy(self, path):
|
||||
"""Delete a stream and its associated data."""
|
||||
return self.db.stream_destroy(path)
|
||||
@@ -186,7 +221,7 @@ class Stream(NilmApp):
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@exception_to_httperror(NilmDBError, LookupError, TypeError)
|
||||
@cherrypy.tools.allow_methods(methods = ["POST"])
|
||||
@cherrypy.tools.CORS_allow(methods = ["POST"])
|
||||
def set_metadata(self, path, data):
|
||||
"""Set metadata for the named stream, replacing any
|
||||
existing metadata. Data should be a json-encoded
|
||||
@@ -198,7 +233,7 @@ class Stream(NilmApp):
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@exception_to_httperror(NilmDBError, LookupError, TypeError)
|
||||
@cherrypy.tools.allow_methods(methods = ["POST"])
|
||||
@cherrypy.tools.CORS_allow(methods = ["POST"])
|
||||
def update_metadata(self, path, data):
|
||||
"""Update metadata for the named stream. Data
|
||||
should be a json-encoded dictionary"""
|
||||
@@ -208,7 +243,7 @@ class Stream(NilmApp):
|
||||
# /stream/insert?path=/newton/prep
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@cherrypy.tools.allow_methods(methods = ["PUT"])
|
||||
@cherrypy.tools.CORS_allow(methods = ["PUT"])
|
||||
def insert(self, path, start, end):
|
||||
"""
|
||||
Insert new data into the database. Provide textual data
|
||||
@@ -264,7 +299,7 @@ class Stream(NilmApp):
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@exception_to_httperror(NilmDBError)
|
||||
@cherrypy.tools.allow_methods(methods = ["POST"])
|
||||
@cherrypy.tools.CORS_allow(methods = ["POST"])
|
||||
def remove(self, path, start = None, end = None):
|
||||
"""
|
||||
Remove data from the backend database. Removes all data in
|
||||
@@ -419,16 +454,14 @@ class Server(object):
|
||||
'error_page.default': self.json_error_page,
|
||||
})
|
||||
|
||||
# Send a permissive Access-Control-Allow-Origin (CORS) header
|
||||
# with all responses so that browsers can send cross-domain
|
||||
# requests to this server.
|
||||
app_config.update({ 'response.headers.Access-Control-Allow-Origin':
|
||||
'*' })
|
||||
# Some default headers to just help identify that things are working
|
||||
app_config.update({ 'response.headers.X-Jim-Is-Awesome': 'yeah' })
|
||||
|
||||
# Only allow GET and HEAD by default. Individual handlers
|
||||
# can override.
|
||||
app_config.update({ 'tools.allow_methods.on': True,
|
||||
'tools.allow_methods.methods': ['GET', 'HEAD'] })
|
||||
# Set up Cross-Origin Resource Sharing (CORS) handler so we
|
||||
# can correctly respond to browsers' CORS preflight requests.
|
||||
# This also limits verbs to GET and HEAD by default.
|
||||
app_config.update({ 'tools.CORS_allow.on': True,
|
||||
'tools.CORS_allow.methods': ['GET', 'HEAD'] })
|
||||
|
||||
# Send tracebacks in error responses. They're hidden by the
|
||||
# error_page function for client errors (code 400-499).
|
||||
|
@@ -55,12 +55,6 @@ class TestClient(object):
|
||||
client.version()
|
||||
client.close()
|
||||
|
||||
# Trigger same error with a PUT request
|
||||
client = nilmdb.Client(url = "http://localhost:1/")
|
||||
with assert_raises(nilmdb.client.ServerError):
|
||||
client.version()
|
||||
client.close()
|
||||
|
||||
# Then a fake URL on a real host
|
||||
client = nilmdb.Client(url = "http://localhost:32180/fake/")
|
||||
with assert_raises(nilmdb.client.ClientError):
|
||||
@@ -360,12 +354,6 @@ class TestClient(object):
|
||||
raise AssertionError("/stream/extract is not text/plain:\n" +
|
||||
headers())
|
||||
|
||||
# Make sure Access-Control-Allow-Origin gets set
|
||||
if "access-control-allow-origin: " not in headers():
|
||||
raise AssertionError("No Access-Control-Allow-Origin (CORS) "
|
||||
"header in /stream/extract response:\n" +
|
||||
headers())
|
||||
|
||||
client.close()
|
||||
|
||||
def test_client_08_unicode(self):
|
||||
|
@@ -11,12 +11,7 @@ from nose.tools import assert_raises
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import threading
|
||||
import urllib2
|
||||
from urllib2 import urlopen, HTTPError
|
||||
import Queue
|
||||
import StringIO
|
||||
import shlex
|
||||
|
||||
|
@@ -9,14 +9,7 @@ from nose.tools import assert_raises
|
||||
import distutils.version
|
||||
import itertools
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import cherrypy
|
||||
import threading
|
||||
import urllib2
|
||||
from urllib2 import urlopen, HTTPError
|
||||
import Queue
|
||||
import cStringIO
|
||||
import random
|
||||
import unittest
|
||||
|
||||
|
@@ -6,15 +6,13 @@ import distutils.version
|
||||
import simplejson as json
|
||||
import itertools
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import cherrypy
|
||||
import threading
|
||||
import urllib2
|
||||
from urllib2 import urlopen, HTTPError
|
||||
import Queue
|
||||
import cStringIO
|
||||
import time
|
||||
import requests
|
||||
|
||||
from nilmdb.utils import serializer_proxy
|
||||
|
||||
@@ -208,3 +206,34 @@ class TestServer(object):
|
||||
data = getjson("/stream/get_metadata?path=/newton/prep"
|
||||
"&key=foo")
|
||||
eq_(data, {'foo': None})
|
||||
|
||||
def test_cors_headers(self):
|
||||
# Test that CORS headers are being set correctly
|
||||
|
||||
# Normal GET should send simple response
|
||||
url = "http://127.0.0.1:32180/stream/list"
|
||||
r = requests.get(url, headers = { "Origin": "http://google.com/" })
|
||||
eq_(r.status_code, 200)
|
||||
if "access-control-allow-origin" not in r.headers:
|
||||
raise AssertionError("No Access-Control-Allow-Origin (CORS) "
|
||||
"header in response:\n", r.headers)
|
||||
eq_(r.headers["access-control-allow-origin"], "http://google.com/")
|
||||
|
||||
# OPTIONS without CORS preflight headers should result in 405
|
||||
r = requests.options(url, headers = {
|
||||
"Origin": "http://google.com/",
|
||||
})
|
||||
eq_(r.status_code, 405)
|
||||
|
||||
# OPTIONS with preflight headers should give preflight response
|
||||
r = requests.options(url, headers = {
|
||||
"Origin": "http://google.com/",
|
||||
"Access-Control-Request-Method": "POST",
|
||||
"Access-Control-Request-Headers": "X-Custom",
|
||||
})
|
||||
eq_(r.status_code, 200)
|
||||
if "access-control-allow-origin" not in r.headers:
|
||||
raise AssertionError("No Access-Control-Allow-Origin (CORS) "
|
||||
"header in response:\n", r.headers)
|
||||
eq_(r.headers["access-control-allow-methods"], "GET, HEAD")
|
||||
eq_(r.headers["access-control-allow-headers"], "X-Custom")
|
||||
|
Reference in New Issue
Block a user