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]
|
||||
paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory
|
||||
|
||||
[filter:legacy_v2_compatible]
|
||||
paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory
|
||||
|
||||
[app:osapi_compute_app_v2]
|
||||
paste.app_factory = nova.api.openstack.compute:APIRouter.factory
|
||||
|
||||
|
@ -128,6 +128,46 @@ class FaultWrapper(base_wsgi.Middleware):
|
||||
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):
|
||||
def routematch(self, url=None, environ=None):
|
||||
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):
|
||||
|
||||
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):
|
||||
super(ApiPasteFixture, self).setUp()
|
||||
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(tmp_api_paste_file_name, 'w') as tmp_file:
|
||||
for line in orig_api_paste:
|
||||
tmp_file.write(line.replace(
|
||||
"/v2: openstack_compute_api_v2",
|
||||
"/v2: openstack_compute_api_v21"))
|
||||
self._replace_line(tmp_file, line)
|
||||
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