Add middleware filterout Microversions http headers
This patch adds new WSGI middleware which filter out Microversions http header X-OpenStack-Nova-API-Version in the request and response and Mircoversion field in http header 'Vary'. Partial implement blueprint api-relax-validation Change-Id: I153ba2e71cd2fa4e2d4e5edf9993d076f7d0adb3
This commit is contained in:
parent
7555df8acf
commit
d709d6c339
@ -108,6 +108,9 @@ paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.
|
|||||||
[filter:sizelimit]
|
[filter:sizelimit]
|
||||||
paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory
|
paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory
|
||||||
|
|
||||||
|
[filter:legacy_v2_compatible]
|
||||||
|
paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory
|
||||||
|
|
||||||
[app:osapi_compute_app_v2]
|
[app:osapi_compute_app_v2]
|
||||||
paste.app_factory = nova.api.openstack.compute:APIRouter.factory
|
paste.app_factory = nova.api.openstack.compute:APIRouter.factory
|
||||||
|
|
||||||
|
@ -128,6 +128,46 @@ class FaultWrapper(base_wsgi.Middleware):
|
|||||||
return self._error(ex, req)
|
return self._error(ex, req)
|
||||||
|
|
||||||
|
|
||||||
|
class LegacyV2CompatibleWrapper(base_wsgi.Middleware):
|
||||||
|
|
||||||
|
def _filter_request_headers(self, req):
|
||||||
|
"""For keeping same behavior with v2 API, ignores microversions
|
||||||
|
HTTP header X-OpenStack-Nova-API-Version in the request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if wsgi.API_VERSION_REQUEST_HEADER in req.headers:
|
||||||
|
del req.headers[wsgi.API_VERSION_REQUEST_HEADER]
|
||||||
|
return req
|
||||||
|
|
||||||
|
def _filter_response_headers(self, response):
|
||||||
|
"""For keeping same behavior with v2 API, filter out microversions
|
||||||
|
HTTP header and microversions field in header 'Vary'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if wsgi.API_VERSION_REQUEST_HEADER in response.headers:
|
||||||
|
del response.headers[wsgi.API_VERSION_REQUEST_HEADER]
|
||||||
|
|
||||||
|
if 'Vary' in response.headers:
|
||||||
|
vary_headers = response.headers['Vary'].split(',')
|
||||||
|
filtered_vary = []
|
||||||
|
for vary in vary_headers:
|
||||||
|
vary = vary.strip()
|
||||||
|
if vary == wsgi.API_VERSION_REQUEST_HEADER:
|
||||||
|
continue
|
||||||
|
filtered_vary.append(vary)
|
||||||
|
if filtered_vary:
|
||||||
|
response.headers['Vary'] = ','.join(filtered_vary)
|
||||||
|
else:
|
||||||
|
del response.headers['Vary']
|
||||||
|
return response
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
req = self._filter_request_headers(req)
|
||||||
|
response = req.get_response(self.application)
|
||||||
|
return self._filter_response_headers(response)
|
||||||
|
|
||||||
|
|
||||||
class APIMapper(routes.Mapper):
|
class APIMapper(routes.Mapper):
|
||||||
def routematch(self, url=None, environ=None):
|
def routematch(self, url=None, environ=None):
|
||||||
if url == "":
|
if url == "":
|
||||||
|
34
nova/tests/functional/test_legacy_v3_compatible_wrapper.py
Normal file
34
nova/tests/functional/test_legacy_v3_compatible_wrapper.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Copyright 2015 Intel Corporation.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from nova.api.openstack import wsgi
|
||||||
|
from nova.tests.functional import integrated_helpers
|
||||||
|
from nova.tests.functional.v3 import api_paste_fixture
|
||||||
|
|
||||||
|
|
||||||
|
class LegacyV2CompatibleTestBase(integrated_helpers._IntegratedTestBase):
|
||||||
|
_api_version = 'v2'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.useFixture(api_paste_fixture.ApiPasteV2CompatibleFixture())
|
||||||
|
super(LegacyV2CompatibleTestBase, self).setUp()
|
||||||
|
|
||||||
|
def test_request_with_microversion_headers(self):
|
||||||
|
response = self.api.api_post('os-keypairs',
|
||||||
|
{"keypair": {"name": "test"}},
|
||||||
|
headers={wsgi.API_VERSION_REQUEST_HEADER: '2.100'})
|
||||||
|
self.assertNotIn(wsgi.API_VERSION_REQUEST_HEADER, response.headers)
|
||||||
|
self.assertNotIn('Vary', response.headers)
|
||||||
|
self.assertNotIn('type', response.body["keypair"])
|
@ -25,6 +25,11 @@ CONF = cfg.CONF
|
|||||||
|
|
||||||
class ApiPasteFixture(fixtures.Fixture):
|
class ApiPasteFixture(fixtures.Fixture):
|
||||||
|
|
||||||
|
def _replace_line(self, target_file, line):
|
||||||
|
target_file.write(line.replace(
|
||||||
|
"/v2: openstack_compute_api_v2",
|
||||||
|
"/v2: openstack_compute_api_v21"))
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ApiPasteFixture, self).setUp()
|
super(ApiPasteFixture, self).setUp()
|
||||||
CONF.set_default('api_paste_config',
|
CONF.set_default('api_paste_config',
|
||||||
@ -35,7 +40,16 @@ class ApiPasteFixture(fixtures.Fixture):
|
|||||||
with open(CONF.api_paste_config, 'r') as orig_api_paste:
|
with open(CONF.api_paste_config, 'r') as orig_api_paste:
|
||||||
with open(tmp_api_paste_file_name, 'w') as tmp_file:
|
with open(tmp_api_paste_file_name, 'w') as tmp_file:
|
||||||
for line in orig_api_paste:
|
for line in orig_api_paste:
|
||||||
tmp_file.write(line.replace(
|
self._replace_line(tmp_file, line)
|
||||||
"/v2: openstack_compute_api_v2",
|
|
||||||
"/v2: openstack_compute_api_v21"))
|
|
||||||
CONF.set_override('api_paste_config', tmp_api_paste_file_name)
|
CONF.set_override('api_paste_config', tmp_api_paste_file_name)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiPasteV2CompatibleFixture(ApiPasteFixture):
|
||||||
|
|
||||||
|
def _replace_line(self, target_file, line):
|
||||||
|
line = line.replace("noauth2 osapi_compute_app_v21",
|
||||||
|
"noauth2 legacy_v2_compatible osapi_compute_app_v21")
|
||||||
|
line = line.replace(
|
||||||
|
"/v2: openstack_compute_api_v2",
|
||||||
|
"/v2: openstack_compute_api_v21")
|
||||||
|
target_file.write(line)
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
# Copyright 2015 Intel Corporation
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import webob
|
||||||
|
import webob.dec
|
||||||
|
|
||||||
|
import nova.api.openstack
|
||||||
|
from nova.api.openstack import wsgi
|
||||||
|
from nova import test
|
||||||
|
|
||||||
|
|
||||||
|
class TestLegacyV2CompatibleWrapper(test.NoDBTestCase):
|
||||||
|
|
||||||
|
def test_filter_out_microverions_request_header(self):
|
||||||
|
req = webob.Request.blank('/')
|
||||||
|
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.2'
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def fake_app(req, *args, **kwargs):
|
||||||
|
self.assertNotIn(wsgi.API_VERSION_REQUEST_HEADER, req)
|
||||||
|
resp = webob.Response()
|
||||||
|
return resp
|
||||||
|
|
||||||
|
wrapper = nova.api.openstack.LegacyV2CompatibleWrapper(fake_app)
|
||||||
|
req.get_response(wrapper)
|
||||||
|
|
||||||
|
def test_filter_out_microverions_response_header(self):
|
||||||
|
req = webob.Request.blank('/')
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def fake_app(req, *args, **kwargs):
|
||||||
|
resp = webob.Response()
|
||||||
|
resp.status_int = 204
|
||||||
|
resp.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.3'
|
||||||
|
return resp
|
||||||
|
|
||||||
|
wrapper = nova.api.openstack.LegacyV2CompatibleWrapper(fake_app)
|
||||||
|
response = req.get_response(wrapper)
|
||||||
|
self.assertNotIn(wsgi.API_VERSION_REQUEST_HEADER, response.headers)
|
||||||
|
|
||||||
|
def test_filter_out_microverions_vary_header(self):
|
||||||
|
req = webob.Request.blank('/')
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def fake_app(req, *args, **kwargs):
|
||||||
|
resp = webob.Response()
|
||||||
|
resp.status_int = 204
|
||||||
|
resp.headers['Vary'] = wsgi.API_VERSION_REQUEST_HEADER
|
||||||
|
return resp
|
||||||
|
|
||||||
|
wrapper = nova.api.openstack.LegacyV2CompatibleWrapper(fake_app)
|
||||||
|
response = req.get_response(wrapper)
|
||||||
|
self.assertNotIn('Vary', response.headers)
|
||||||
|
|
||||||
|
def test_filter_out_microverions_vary_header_with_multi_fields(self):
|
||||||
|
req = webob.Request.blank('/')
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def fake_app(req, *args, **kwargs):
|
||||||
|
resp = webob.Response()
|
||||||
|
resp.status_int = 204
|
||||||
|
resp.headers['Vary'] = '%s, %s, %s' % (
|
||||||
|
wsgi.API_VERSION_REQUEST_HEADER, 'FAKE_HEADER1',
|
||||||
|
'FAKE_HEADER2')
|
||||||
|
return resp
|
||||||
|
|
||||||
|
wrapper = nova.api.openstack.LegacyV2CompatibleWrapper(fake_app)
|
||||||
|
response = req.get_response(wrapper)
|
||||||
|
self.assertEqual('FAKE_HEADER1,FAKE_HEADER2',
|
||||||
|
response.headers['Vary'])
|
||||||
|
|
||||||
|
def test_filter_out_microverions_no_vary_header(self):
|
||||||
|
req = webob.Request.blank('/')
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def fake_app(req, *args, **kwargs):
|
||||||
|
resp = webob.Response()
|
||||||
|
resp.status_int = 204
|
||||||
|
return resp
|
||||||
|
|
||||||
|
wrapper = nova.api.openstack.LegacyV2CompatibleWrapper(fake_app)
|
||||||
|
response = req.get_response(wrapper)
|
||||||
|
self.assertNotIn('Vary', response.headers)
|
Loading…
Reference in New Issue
Block a user