Retire repo
This repo was created by accident, use deb-python-oslo.middleware instead. Needed-By: I1ac1a06931c8b6dd7c2e73620a0302c29e605f03 Change-Id: I81894aea69b9d09b0977039623c26781093a397a
This commit is contained in:
parent
37d1a0f6c3
commit
c42fcbead3
@ -1,8 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = oslo_middleware
|
||||
omit = oslo_middleware/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
precision = 2
|
52
.gitignore
vendored
52
.gitignore
vendored
@ -1,52 +0,0 @@
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
cover
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Complexity
|
||||
output/*.html
|
||||
output/*/index.html
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/oslo.middleware.git
|
3
.mailmap
3
.mailmap
@ -1,3 +0,0 @@
|
||||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
@ -1,7 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
@ -1,16 +0,0 @@
|
||||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps in this page:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/oslo.middleware
|
@ -1,4 +0,0 @@
|
||||
oslo.middleware Style Commandments
|
||||
==================================
|
||||
|
||||
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
175
LICENSE
175
LICENSE
@ -1,175 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
21
README.rst
21
README.rst
@ -1,21 +0,0 @@
|
||||
===================================
|
||||
oslo.middleware
|
||||
===================================
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/oslo.middleware.svg
|
||||
:target: https://pypi.python.org/pypi/oslo.middleware/
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/oslo.middleware.svg
|
||||
:target: https://pypi.python.org/pypi/oslo.middleware/
|
||||
:alt: Downloads
|
||||
|
||||
Oslo middleware library includes components that can be injected into
|
||||
wsgi pipelines to intercept request/response flows. The base class can be
|
||||
enhanced with functionality like add/delete/modification of http headers
|
||||
and support for limiting size/connection etc.
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: http://docs.openstack.org/developer/oslo.middleware
|
||||
* Source: http://git.openstack.org/cgit/openstack/oslo.middleware
|
||||
* Bugs: http://bugs.launchpad.net/oslo.middleware
|
13
README.txt
Normal file
13
README.txt
Normal file
@ -0,0 +1,13 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
Use instead the project deb-python-oslo.middleware at
|
||||
http://git.openstack.org/cgit/openstack/deb-python-oslo.middleware .
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
@ -1,19 +0,0 @@
|
||||
=====
|
||||
API
|
||||
=====
|
||||
|
||||
.. automodule:: oslo_middleware
|
||||
:members:
|
||||
|
||||
Configuration Options
|
||||
=====================
|
||||
|
||||
RequestBodySizeLimiter
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. show-options:: oslo.middleware.sizelimit
|
||||
|
||||
SSLMiddleware
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. show-options:: oslo.middleware.ssl
|
@ -1,77 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
#'sphinx.ext.intersphinx',
|
||||
'oslosphinx',
|
||||
'oslo_config.sphinxext',
|
||||
'stevedore.sphinxext',
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'oslo.middleware'
|
||||
copyright = u'2014, OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = ['static']
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
@ -1,5 +0,0 @@
|
||||
==============
|
||||
Contributing
|
||||
==============
|
||||
|
||||
.. include:: ../../CONTRIBUTING.rst
|
@ -1,112 +0,0 @@
|
||||
===============
|
||||
CORS Middleware
|
||||
===============
|
||||
|
||||
This middleware provides a comprehensive, configurable implementation of the
|
||||
CORS_ (Cross Origin Resource Sharing) specification as oslo-supported python
|
||||
wsgi middleware.
|
||||
|
||||
.. note::
|
||||
|
||||
While this middleware supports the use of the `*` wildcard origin in the
|
||||
specification, this feature is not recommended for security reasons. It
|
||||
is provided to simplify basic use of CORS, practically meaning "I don't
|
||||
care how this is used." In an intranet setting, this could lead to leakage
|
||||
of data beyond the intranet and therefore should be avoided.
|
||||
|
||||
Quickstart
|
||||
----------
|
||||
First, include the middleware in your application::
|
||||
|
||||
from oslo_middleware import cors
|
||||
|
||||
app = cors.CORS(your_wsgi_application)
|
||||
|
||||
Secondly, add as many allowed origins as you would like::
|
||||
|
||||
app.add_origin(allowed_origin='https://website.example.com:443',
|
||||
allow_credentials=True,
|
||||
max_age=3600,
|
||||
allow_methods=['GET','PUT','POST','DELETE'],
|
||||
allow_headers=['X-Custom-Header'],
|
||||
expose_headers=['X-Custom-Header'])
|
||||
|
||||
# ... add more origins here.
|
||||
|
||||
|
||||
Configuration for oslo_config
|
||||
-----------------------------
|
||||
|
||||
A factory method has been provided to simplify configuration of your CORS
|
||||
domain, using oslo_config::
|
||||
|
||||
from oslo_middleware import cors
|
||||
from oslo_config import cfg
|
||||
|
||||
app = cors.CORS(your_wsgi_application, cfg.CONF)
|
||||
|
||||
In your application's config file, then include a configuration block
|
||||
something like this::
|
||||
|
||||
[cors]
|
||||
allowed_origin=https://website.example.com:443,https://website2.example.com:443
|
||||
max_age=3600
|
||||
allow_methods=GET,POST,PUT,DELETE
|
||||
allow_headers=X-Custom-Header
|
||||
expose_headers=X-Custom-Header
|
||||
|
||||
If your software requires specific headers or methods for proper operation, you
|
||||
may include these as latent properties. These will be evaluated in addition
|
||||
to any found in configuration::
|
||||
|
||||
from oslo_middleware import cors
|
||||
|
||||
app = cors.CORS(your_wsgi_application)
|
||||
app.set_latent(allow_headers=['X-System-Header'],
|
||||
expose_headers=['X-System-Header'],
|
||||
allow_methods=['GET','PATCH'])
|
||||
|
||||
|
||||
Configuration for pastedeploy
|
||||
-----------------------------
|
||||
|
||||
If your application is using pastedeploy, the following configuration block
|
||||
will add CORS support.::
|
||||
|
||||
[filter:cors]
|
||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||
allowed_origin=https://website.example.com:443,https://website2.example.com:443
|
||||
max_age=3600
|
||||
allow_methods=GET,POST,PUT,DELETE
|
||||
allow_headers=X-Custom-Header
|
||||
expose_headers=X-Custom-Header
|
||||
|
||||
If your application is using pastedeploy, but would also like to use the
|
||||
existing configuration from oslo_config in order to simplify the points of
|
||||
configuration, this may be done as follows.::
|
||||
|
||||
[filter:cors]
|
||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||
oslo_config_project = oslo_project_name
|
||||
|
||||
# Optional field, in case the program name is different from the project:
|
||||
oslo_config_program = oslo_project_name-api
|
||||
|
||||
# This method also permits setting latent properties, for any origins set
|
||||
# in oslo config.
|
||||
latent_allow_headers=X-Auth-Token
|
||||
latent_expose_headers=X-Auth-Token
|
||||
latent_methods=GET,PUT,POST
|
||||
|
||||
Configuration Options
|
||||
---------------------
|
||||
|
||||
.. show-options:: oslo.middleware.cors
|
||||
|
||||
Module Documentation
|
||||
--------------------
|
||||
|
||||
.. automodule:: oslo_middleware.cors
|
||||
:members:
|
||||
|
||||
.. _CORS: http://www.w3.org/TR/cors/
|
@ -1,16 +0,0 @@
|
||||
================================
|
||||
Healthcheck middleware plugins
|
||||
================================
|
||||
|
||||
.. automodule:: oslo_middleware.healthcheck
|
||||
:members:
|
||||
|
||||
.. automodule:: oslo_middleware.healthcheck.disable_by_file
|
||||
:members:
|
||||
|
||||
|
||||
Available Plugins
|
||||
------------------
|
||||
|
||||
.. list-plugins:: oslo.middleware.healthcheck
|
||||
:detailed:
|
@ -1 +0,0 @@
|
||||
.. include:: ../../ChangeLog
|
@ -1,22 +0,0 @@
|
||||
.. include:: ../../README.rst
|
||||
|
||||
Contents
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation
|
||||
api
|
||||
healthcheck_plugins
|
||||
cors
|
||||
oslo_config
|
||||
contributing
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
history
|
@ -1,12 +0,0 @@
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
At the command line::
|
||||
|
||||
$ pip install oslo.middleware
|
||||
|
||||
Or, if you have virtualenvwrapper installed::
|
||||
|
||||
$ mkvirtualenv oslo.middleware
|
||||
$ pip install oslo.middleware
|
@ -1,57 +0,0 @@
|
||||
=============================
|
||||
Middlewares and configuration
|
||||
=============================
|
||||
|
||||
Middlewares can be configured in multiple fashion depending of the
|
||||
application needs. Here is some use-cases:
|
||||
|
||||
Configuration from the application
|
||||
----------------------------------
|
||||
|
||||
The application code will looks like::
|
||||
|
||||
from oslo_middleware import sizelimit
|
||||
from oslo_config import cfg
|
||||
|
||||
conf = cfg.ConfigOpts()
|
||||
app = sizelimit.RequestBodySizeLimiter(your_wsgi_application, conf)
|
||||
|
||||
|
||||
Configuration with paste-deploy and the oslo.config
|
||||
---------------------------------------------------
|
||||
|
||||
The paste filter (in /etc/my_app/api-paste.ini) will looks like::
|
||||
|
||||
[filter:sizelimit]
|
||||
paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
|
||||
# In case of the application doesn't use the global oslo.config
|
||||
# object. The middleware must known the app name to load
|
||||
# the application configuration, by setting this:
|
||||
# oslo_config_project = my_app
|
||||
|
||||
# In some cases, you may need to specify the program name for the project
|
||||
# as well.
|
||||
# oslo_config_program = my_app-api
|
||||
|
||||
The oslo.config file of the application (eg: /etc/my_app/my_app.conf) will looks like::
|
||||
|
||||
[oslo_middleware]
|
||||
max_request_body_size=1000
|
||||
|
||||
|
||||
Configuration with pastedeploy only
|
||||
-----------------------------------
|
||||
|
||||
The paste filter (in /etc/my_app/api-paste.ini) will looks like::
|
||||
|
||||
[filter:sizelimit]
|
||||
paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
|
||||
max_request_body_size=1000
|
||||
|
||||
This will override any configuration done via oslo.config
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
healtcheck middleware does not yet use oslo.config, see :doc:`healthcheck_plugins`
|
||||
|
@ -1,13 +0,0 @@
|
||||
# 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__('pkg_resources').declare_namespace(__name__)
|
@ -1,52 +0,0 @@
|
||||
# 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 sys
|
||||
import warnings
|
||||
|
||||
def deprecated():
|
||||
new_name = __name__.replace('.', '_')
|
||||
warnings.warn(
|
||||
('The oslo namespace package is deprecated. Please use %s instead.' %
|
||||
new_name),
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
|
||||
# NOTE(dims): We cannot remove the deprecation or redirects below
|
||||
# until Liberty-EOL
|
||||
deprecated()
|
||||
|
||||
from oslo_middleware import base
|
||||
from oslo_middleware import catch_errors
|
||||
from oslo_middleware import correlation_id
|
||||
from oslo_middleware import debug
|
||||
from oslo_middleware import request_id
|
||||
from oslo_middleware import sizelimit
|
||||
|
||||
sys.modules['oslo.middleware.base'] = base
|
||||
sys.modules['oslo.middleware.catch_errors'] = catch_errors
|
||||
sys.modules['oslo.middleware.correlation_id'] = correlation_id
|
||||
sys.modules['oslo.middleware.debug'] = debug
|
||||
sys.modules['oslo.middleware.request_id'] = request_id
|
||||
sys.modules['oslo.middleware.sizelimit'] = sizelimit
|
||||
|
||||
from oslo_middleware.catch_errors import CatchErrors
|
||||
from oslo_middleware.correlation_id import CorrelationId
|
||||
from oslo_middleware.cors import CORS
|
||||
from oslo_middleware.debug import Debug
|
||||
from oslo_middleware.healthcheck import Healthcheck
|
||||
from oslo_middleware.http_proxy_to_wsgi import HTTPProxyToWSGI
|
||||
from oslo_middleware.request_id import RequestId
|
||||
from oslo_middleware.sizelimit import RequestBodySizeLimiter
|
||||
from oslo_middleware.ssl import SSLMiddleware
|
@ -1,31 +0,0 @@
|
||||
# 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.
|
||||
|
||||
__all__ = ['CatchErrors',
|
||||
'CorrelationId',
|
||||
'CORS',
|
||||
'Debug',
|
||||
'Healthcheck',
|
||||
'HTTPProxyToWSGI',
|
||||
'RequestId',
|
||||
'RequestBodySizeLimiter',
|
||||
'SSLMiddleware']
|
||||
|
||||
from oslo_middleware.catch_errors import CatchErrors
|
||||
from oslo_middleware.correlation_id import CorrelationId
|
||||
from oslo_middleware.cors import CORS
|
||||
from oslo_middleware.debug import Debug
|
||||
from oslo_middleware.healthcheck import Healthcheck
|
||||
from oslo_middleware.http_proxy_to_wsgi import HTTPProxyToWSGI
|
||||
from oslo_middleware.request_id import RequestId
|
||||
from oslo_middleware.sizelimit import RequestBodySizeLimiter
|
||||
from oslo_middleware.ssl import SSLMiddleware
|
@ -1,35 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""oslo.i18n integration module.
|
||||
|
||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html
|
||||
|
||||
"""
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain='oslo_middleware')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# Translators for log levels.
|
||||
#
|
||||
# The abbreviated names are meant to reflect the usual use of a short
|
||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||
# the level.
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
@ -1,144 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# 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.
|
||||
|
||||
"""Base class(es) for WSGI Middleware."""
|
||||
|
||||
from inspect import getargspec
|
||||
import webob.dec
|
||||
import webob.request
|
||||
import webob.response
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
class NoContentTypeResponse(webob.response.Response):
|
||||
|
||||
default_content_type = None # prevents webob assigning content type
|
||||
|
||||
|
||||
class NoContentTypeRequest(webob.request.Request):
|
||||
|
||||
ResponseClass = NoContentTypeResponse
|
||||
|
||||
|
||||
class ConfigurableMiddleware(object):
|
||||
"""Base WSGI middleware wrapper.
|
||||
|
||||
These classes require an application to be initialized that will be called
|
||||
next. By default the middleware will simply call its wrapped app, or you
|
||||
can override __call__ to customize its behavior.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def factory(cls, global_conf, **local_conf):
|
||||
"""Factory method for paste.deploy.
|
||||
|
||||
:param global_conf: dict of options for all middlewares
|
||||
(usually the [DEFAULT] section of the paste deploy
|
||||
configuration file)
|
||||
:param local_conf: options dedicated to this middleware
|
||||
(usually the option defined in the middleware
|
||||
section of the paste deploy configuration file)
|
||||
"""
|
||||
conf = global_conf.copy() if global_conf else {}
|
||||
conf.update(local_conf)
|
||||
|
||||
def middleware_filter(app):
|
||||
return cls(app, conf)
|
||||
|
||||
return middleware_filter
|
||||
|
||||
def __init__(self, application, conf=None):
|
||||
"""Base middleware constructor
|
||||
|
||||
:param conf: a dict of options or a cfg.ConfigOpts object
|
||||
"""
|
||||
self.application = application
|
||||
|
||||
# NOTE(sileht): If the configuration come from oslo.config
|
||||
# just use it.
|
||||
if isinstance(conf, cfg.ConfigOpts):
|
||||
self.conf = {}
|
||||
self.oslo_conf = conf
|
||||
else:
|
||||
self.conf = conf or {}
|
||||
if "oslo_config_project" in self.conf:
|
||||
if 'oslo_config_file' in self.conf:
|
||||
default_config_files = [self.conf['oslo_config_file']]
|
||||
else:
|
||||
default_config_files = None
|
||||
|
||||
if 'oslo_config_program' in self.conf:
|
||||
program = self.conf['oslo_config_program']
|
||||
else:
|
||||
program = None
|
||||
|
||||
self.oslo_conf = cfg.ConfigOpts()
|
||||
self.oslo_conf([],
|
||||
project=self.conf['oslo_config_project'],
|
||||
prog=program,
|
||||
default_config_files=default_config_files,
|
||||
validate_default_values=True)
|
||||
|
||||
else:
|
||||
# Fallback to global object
|
||||
self.oslo_conf = cfg.CONF
|
||||
|
||||
def _conf_get(self, key, group="oslo_middleware"):
|
||||
if key in self.conf:
|
||||
# Validate value type
|
||||
self.oslo_conf.set_override(key, self.conf[key], group=group,
|
||||
enforce_type=True)
|
||||
return getattr(getattr(self.oslo_conf, group), key)
|
||||
|
||||
@staticmethod
|
||||
def process_request(req):
|
||||
"""Called on each request.
|
||||
|
||||
If this returns None, the next application down the stack will be
|
||||
executed. If it returns a response then that response will be returned
|
||||
and execution will stop here.
|
||||
"""
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def process_response(response, request=None):
|
||||
"""Do whatever you'd like to the response."""
|
||||
return response
|
||||
|
||||
@webob.dec.wsgify(RequestClass=NoContentTypeRequest)
|
||||
def __call__(self, req):
|
||||
response = self.process_request(req)
|
||||
if response:
|
||||
return response
|
||||
response = req.get_response(self.application)
|
||||
|
||||
(args, varargs, varkw, defaults) = getargspec(self.process_response)
|
||||
if 'request' in args:
|
||||
return self.process_response(response, request=req)
|
||||
return self.process_response(response)
|
||||
|
||||
|
||||
class Middleware(ConfigurableMiddleware):
|
||||
"""Legacy base WSGI middleware wrapper.
|
||||
|
||||
Legacy interface that doesn't pass configuration options
|
||||
to the middleware when it's loaded via paste.deploy.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def factory(cls, global_conf, **local_conf):
|
||||
"""Factory method for paste.deploy."""
|
||||
return cls
|
@ -1,43 +0,0 @@
|
||||
# Copyright (c) 2013 NEC 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 logging
|
||||
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from oslo_middleware._i18n import _LE
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CatchErrors(base.ConfigurableMiddleware):
|
||||
"""Middleware that provides high-level error handling.
|
||||
|
||||
It catches all exceptions from subsequent applications in WSGI pipeline
|
||||
to hide internal errors from API response.
|
||||
"""
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
try:
|
||||
response = req.get_response(self.application)
|
||||
except Exception:
|
||||
LOG.exception(_LE('An error occurred during '
|
||||
'processing the request: %s'), req)
|
||||
response = webob.exc.HTTPInternalServerError()
|
||||
return response
|
@ -1,27 +0,0 @@
|
||||
# Copyright (c) 2013 Rackspace Hosting
|
||||
# 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 uuid
|
||||
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
class CorrelationId(base.ConfigurableMiddleware):
|
||||
"Middleware that attaches a correlation id to WSGI request"
|
||||
|
||||
def process_request(self, req):
|
||||
correlation_id = (req.headers.get("X_CORRELATION_ID") or
|
||||
str(uuid.uuid4()))
|
||||
req.headers['X_CORRELATION_ID'] = correlation_id
|
@ -1,452 +0,0 @@
|
||||
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 copy
|
||||
import logging
|
||||
|
||||
import debtcollector
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import base
|
||||
import six
|
||||
import webob.exc
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CORS_OPTS = [
|
||||
cfg.ListOpt('allowed_origin',
|
||||
default=None,
|
||||
help='Indicate whether this resource may be shared with the '
|
||||
'domain received in the requests "origin" header. '
|
||||
'Format: "<protocol>://<host>[:<port>]", no trailing '
|
||||
'slash. Example: https://horizon.example.com'),
|
||||
cfg.BoolOpt('allow_credentials',
|
||||
default=True,
|
||||
help='Indicate that the actual request can include user '
|
||||
'credentials'),
|
||||
cfg.ListOpt('expose_headers',
|
||||
default=[],
|
||||
help='Indicate which headers are safe to expose to the API. '
|
||||
'Defaults to HTTP Simple Headers.'),
|
||||
cfg.IntOpt('max_age',
|
||||
default=3600,
|
||||
help='Maximum cache age of CORS preflight requests.'),
|
||||
cfg.ListOpt('allow_methods',
|
||||
default=['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE',
|
||||
'TRACE', 'PATCH'], # RFC 2616, RFC 5789
|
||||
help='Indicate which methods can be used during the actual '
|
||||
'request.'),
|
||||
cfg.ListOpt('allow_headers',
|
||||
default=[],
|
||||
help='Indicate which header field names may be used during '
|
||||
'the actual request.')
|
||||
]
|
||||
|
||||
|
||||
def set_defaults(**kwargs):
|
||||
"""Override the default values for configuration options.
|
||||
|
||||
This method permits a project to override the default CORS option values.
|
||||
For example, it may wish to offer a set of sane default headers which
|
||||
allow it to function with only minimal additional configuration.
|
||||
|
||||
:param allow_credentials: Whether to permit credentials.
|
||||
:type allow_credentials: bool
|
||||
:param expose_headers: A list of headers to expose.
|
||||
:type expose_headers: List of Strings
|
||||
:param max_age: Maximum cache duration in seconds.
|
||||
:type max_age: Int
|
||||
:param allow_methods: List of HTTP methods to permit.
|
||||
:type allow_methods: List of Strings
|
||||
:param allow_headers: List of HTTP headers to permit from the client.
|
||||
:type allow_headers: List of Strings
|
||||
"""
|
||||
# Since 'None' is a valid config override, we have to use kwargs. Else
|
||||
# there's no good way for a user to override only one option, because all
|
||||
# the others would be overridden to 'None'.
|
||||
|
||||
valid_params = set(k.name for k in CORS_OPTS
|
||||
if k.name != 'allowed_origin')
|
||||
passed_params = set(k for k in kwargs)
|
||||
|
||||
wrong_params = passed_params - valid_params
|
||||
if wrong_params:
|
||||
raise AttributeError('Parameter(s) [%s] invalid, please only use [%s]'
|
||||
% (wrong_params, valid_params))
|
||||
|
||||
# Set global defaults.
|
||||
cfg.set_defaults(CORS_OPTS, **kwargs)
|
||||
|
||||
|
||||
class InvalidOriginError(Exception):
|
||||
"""Exception raised when Origin is invalid."""
|
||||
|
||||
def __init__(self, origin):
|
||||
self.origin = origin
|
||||
super(InvalidOriginError, self).__init__(
|
||||
'CORS request from origin \'%s\' not permitted.' % origin)
|
||||
|
||||
|
||||
class CORS(base.ConfigurableMiddleware):
|
||||
"""CORS Middleware.
|
||||
|
||||
This middleware allows a WSGI app to serve CORS headers for multiple
|
||||
configured domains.
|
||||
|
||||
For more information, see http://www.w3.org/TR/cors/
|
||||
"""
|
||||
|
||||
simple_headers = [
|
||||
'Accept',
|
||||
'Accept-Language',
|
||||
'Content-Type',
|
||||
'Cache-Control',
|
||||
'Content-Language',
|
||||
'Expires',
|
||||
'Last-Modified',
|
||||
'Pragma'
|
||||
]
|
||||
|
||||
def __init__(self, application, *args, **kwargs):
|
||||
super(CORS, self).__init__(application, *args, **kwargs)
|
||||
# Begin constructing our configuration hash.
|
||||
self.allowed_origins = {}
|
||||
self._init_conf()
|
||||
|
||||
def sanitize(csv_list):
|
||||
try:
|
||||
return [str.strip(x) for x in csv_list.split(',')]
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
self.set_latent(
|
||||
allow_headers=sanitize(self.conf.get('latent_allow_headers')),
|
||||
expose_headers=sanitize(self.conf.get('latent_expose_headers')),
|
||||
allow_methods=sanitize(self.conf.get('latent_allow_methods'))
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def factory(cls, global_conf, **local_conf):
|
||||
"""factory method for paste.deploy
|
||||
|
||||
allowed_origin: Protocol, host, and port for the allowed origin.
|
||||
allow_credentials: Whether to permit credentials.
|
||||
expose_headers: A list of headers to expose.
|
||||
max_age: Maximum cache duration.
|
||||
allow_methods: List of HTTP methods to permit.
|
||||
allow_headers: List of HTTP headers to permit from the client.
|
||||
"""
|
||||
if ('allowed_origin' not in local_conf
|
||||
and 'oslo_config_project' not in local_conf):
|
||||
raise TypeError("allowed_origin or oslo_config_project "
|
||||
"is required")
|
||||
return super(CORS, cls).factory(global_conf, **local_conf)
|
||||
|
||||
def _init_conf(self):
|
||||
'''Initialize this middleware from an oslo.config instance.'''
|
||||
|
||||
# Set up a location for our latent configuration options
|
||||
self._latent_configuration = {
|
||||
'allow_headers': [],
|
||||
'expose_headers': [],
|
||||
'methods': []
|
||||
}
|
||||
|
||||
# First, check the configuration and register global options.
|
||||
self.oslo_conf.register_opts(CORS_OPTS, 'cors')
|
||||
|
||||
allowed_origin = self._conf_get('allowed_origin', 'cors')
|
||||
allow_credentials = self._conf_get('allow_credentials', 'cors')
|
||||
expose_headers = self._conf_get('expose_headers', 'cors')
|
||||
max_age = self._conf_get('max_age', 'cors')
|
||||
allow_methods = self._conf_get('allow_methods', 'cors')
|
||||
allow_headers = self._conf_get('allow_headers', 'cors')
|
||||
|
||||
# Clone our original CORS_OPTS, and set the defaults to whatever is
|
||||
# set in the global conf instance. This is done explicitly (instead
|
||||
# of **kwargs), since we don't accidentally want to catch
|
||||
# allowed_origin.
|
||||
subgroup_opts = copy.deepcopy(CORS_OPTS)
|
||||
cfg.set_defaults(subgroup_opts,
|
||||
allow_credentials=allow_credentials,
|
||||
expose_headers=expose_headers,
|
||||
max_age=max_age,
|
||||
allow_methods=allow_methods,
|
||||
allow_headers=allow_headers)
|
||||
|
||||
# If the default configuration contains an allowed_origin, don't
|
||||
# forget to register that.
|
||||
self.add_origin(allowed_origin=allowed_origin,
|
||||
allow_credentials=allow_credentials,
|
||||
expose_headers=expose_headers,
|
||||
max_age=max_age,
|
||||
allow_methods=allow_methods,
|
||||
allow_headers=allow_headers)
|
||||
|
||||
# Iterate through all the loaded config sections, looking for ones
|
||||
# prefixed with 'cors.'
|
||||
for section in self.oslo_conf.list_all_sections():
|
||||
if section.startswith('cors.'):
|
||||
debtcollector.deprecate('Multiple configuration blocks are '
|
||||
'deprecated and will be removed in '
|
||||
'future versions. Please consolidate '
|
||||
'your configuration in the [cors] '
|
||||
'configuration block.')
|
||||
# Register with the preconstructed defaults
|
||||
self.oslo_conf.register_opts(subgroup_opts, section)
|
||||
self.add_origin(**self.oslo_conf[section])
|
||||
|
||||
def add_origin(self, allowed_origin, allow_credentials=True,
|
||||
expose_headers=None, max_age=None, allow_methods=None,
|
||||
allow_headers=None):
|
||||
'''Add another origin to this filter.
|
||||
|
||||
:param allowed_origin: Protocol, host, and port for the allowed origin.
|
||||
:param allow_credentials: Whether to permit credentials.
|
||||
:param expose_headers: A list of headers to expose.
|
||||
:param max_age: Maximum cache duration.
|
||||
:param allow_methods: List of HTTP methods to permit.
|
||||
:param allow_headers: List of HTTP headers to permit from the client.
|
||||
:return:
|
||||
'''
|
||||
|
||||
# NOTE(dims): Support older code that still passes in
|
||||
# a string for allowed_origin instead of a list
|
||||
if isinstance(allowed_origin, six.string_types):
|
||||
# TODO(krotscheck): https://review.openstack.org/#/c/312687/
|
||||
LOG.warning('DEPRECATED: The `allowed_origin` keyword argument in '
|
||||
'`add_origin()` should be a list, found String.')
|
||||
allowed_origin = [allowed_origin]
|
||||
|
||||
if allowed_origin:
|
||||
for origin in allowed_origin:
|
||||
|
||||
if origin in self.allowed_origins:
|
||||
LOG.warning('Allowed origin [%s] already exists, skipping'
|
||||
% (allowed_origin,))
|
||||
continue
|
||||
|
||||
self.allowed_origins[origin] = {
|
||||
'allow_credentials': allow_credentials,
|
||||
'expose_headers': expose_headers,
|
||||
'max_age': max_age,
|
||||
'allow_methods': allow_methods,
|
||||
'allow_headers': allow_headers
|
||||
}
|
||||
|
||||
def set_latent(self, allow_headers=None, allow_methods=None,
|
||||
expose_headers=None):
|
||||
'''Add a new latent property for this middleware.
|
||||
|
||||
Latent properties are those values which a system requires for
|
||||
operation. API-specific headers, for example, may be added by an
|
||||
engineer so that they ship with the codebase, and thus do not require
|
||||
extra documentation or passing of institutional knowledge.
|
||||
|
||||
:param allow_headers: HTTP headers permitted in client requests.
|
||||
:param allow_methods: HTTP methods permitted in client requests.
|
||||
:param expose_headers: HTTP Headers exposed to clients.
|
||||
'''
|
||||
|
||||
if allow_headers:
|
||||
if isinstance(allow_headers, list):
|
||||
self._latent_configuration['allow_headers'] = allow_headers
|
||||
else:
|
||||
raise TypeError("allow_headers must be a list or None.")
|
||||
|
||||
if expose_headers:
|
||||
if isinstance(expose_headers, list):
|
||||
self._latent_configuration['expose_headers'] = expose_headers
|
||||
else:
|
||||
raise TypeError("expose_headers must be a list or None.")
|
||||
|
||||
if allow_methods:
|
||||
if isinstance(allow_methods, list):
|
||||
self._latent_configuration['methods'] = allow_methods
|
||||
else:
|
||||
raise TypeError("allow_methods parameter must be a list or"
|
||||
" None.")
|
||||
|
||||
def process_response(self, response, request=None):
|
||||
'''Check for CORS headers, and decorate if necessary.
|
||||
|
||||
Perform two checks. First, if an OPTIONS request was issued, let the
|
||||
application handle it, and (if necessary) decorate the response with
|
||||
preflight headers. In this case, if a 404 is thrown by the underlying
|
||||
application (i.e. if the underlying application does not handle
|
||||
OPTIONS requests, the response code is overridden.
|
||||
|
||||
In the case of all other requests, regular request headers are applied.
|
||||
'''
|
||||
|
||||
# Sanity precheck: If we detect CORS headers provided by something in
|
||||
# in the middleware chain, assume that it knows better.
|
||||
if 'Access-Control-Allow-Origin' in response.headers:
|
||||
return response
|
||||
|
||||
# Doublecheck for an OPTIONS request.
|
||||
if request.method == 'OPTIONS':
|
||||
return self._apply_cors_preflight_headers(request=request,
|
||||
response=response)
|
||||
|
||||
# Apply regular CORS headers.
|
||||
self._apply_cors_request_headers(request=request, response=response)
|
||||
|
||||
# Finally, return the response.
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def _split_header_values(request, header_name):
|
||||
"""Convert a comma-separated header value into a list of values."""
|
||||
values = []
|
||||
if header_name in request.headers:
|
||||
for value in request.headers[header_name].rsplit(','):
|
||||
value = value.strip()
|
||||
if value:
|
||||
values.append(value)
|
||||
return values
|
||||
|
||||
def _apply_cors_preflight_headers(self, request, response):
|
||||
"""Handle CORS Preflight (Section 6.2)
|
||||
|
||||
Given a request and a response, apply the CORS preflight headers
|
||||
appropriate for the request.
|
||||
"""
|
||||
|
||||
# If the response contains a 2XX code, we have to assume that the
|
||||
# underlying middleware's response content needs to be persisted.
|
||||
# Otherwise, create a new response.
|
||||
if 200 > response.status_code or response.status_code >= 300:
|
||||
response = base.NoContentTypeResponse(status=webob.exc.HTTPOk.code)
|
||||
|
||||
# Does the request have an origin header? (Section 6.2.1)
|
||||
if 'Origin' not in request.headers:
|
||||
return response
|
||||
|
||||
# Is this origin registered? (Section 6.2.2)
|
||||
try:
|
||||
origin, cors_config = self._get_cors_config_by_origin(
|
||||
request.headers['Origin'])
|
||||
except InvalidOriginError:
|
||||
return response
|
||||
|
||||
# If there's no request method, exit. (Section 6.2.3)
|
||||
if 'Access-Control-Request-Method' not in request.headers:
|
||||
LOG.debug('CORS request does not contain '
|
||||
'Access-Control-Request-Method header.')
|
||||
return response
|
||||
request_method = request.headers['Access-Control-Request-Method']
|
||||
|
||||
# Extract Request headers. If parsing fails, exit. (Section 6.2.4)
|
||||
try:
|
||||
request_headers = \
|
||||
self._split_header_values(request,
|
||||
'Access-Control-Request-Headers')
|
||||
except Exception:
|
||||
LOG.debug('Cannot parse request headers.')
|
||||
return response
|
||||
|
||||
# Compare request method to permitted methods (Section 6.2.5)
|
||||
permitted_methods = (
|
||||
cors_config['allow_methods'] +
|
||||
self._latent_configuration['methods']
|
||||
)
|
||||
if request_method not in permitted_methods:
|
||||
LOG.debug('Request method \'%s\' not in permitted list: %s'
|
||||
% (request_method, permitted_methods))
|
||||
return response
|
||||
|
||||
# Compare request headers to permitted headers, case-insensitively.
|
||||
# (Section 6.2.6)
|
||||
permitted_headers = [header.upper() for header in
|
||||
(cors_config['allow_headers'] +
|
||||
self.simple_headers +
|
||||
self._latent_configuration['allow_headers'])]
|
||||
for requested_header in request_headers:
|
||||
upper_header = requested_header.upper()
|
||||
if upper_header not in permitted_headers:
|
||||
LOG.debug('Request header \'%s\' not in permitted list: %s'
|
||||
% (requested_header, permitted_headers))
|
||||
return response
|
||||
|
||||
# Set the default origin permission headers. (Sections 6.2.7, 6.4)
|
||||
response.headers['Vary'] = 'Origin'
|
||||
response.headers['Access-Control-Allow-Origin'] = origin
|
||||
|
||||
# Does this CORS configuration permit credentials? (Section 6.2.7)
|
||||
if cors_config['allow_credentials']:
|
||||
response.headers['Access-Control-Allow-Credentials'] = 'true'
|
||||
|
||||
# Attach Access-Control-Max-Age if appropriate. (Section 6.2.8)
|
||||
if 'max_age' in cors_config and cors_config['max_age']:
|
||||
response.headers['Access-Control-Max-Age'] = \
|
||||
str(cors_config['max_age'])
|
||||
|
||||
# Attach Access-Control-Allow-Methods. (Section 6.2.9)
|
||||
response.headers['Access-Control-Allow-Methods'] = request_method
|
||||
|
||||
# Attach Access-Control-Allow-Headers. (Section 6.2.10)
|
||||
if request_headers:
|
||||
response.headers['Access-Control-Allow-Headers'] = \
|
||||
','.join(request_headers)
|
||||
|
||||
return response
|
||||
|
||||
def _get_cors_config_by_origin(self, origin):
|
||||
if origin not in self.allowed_origins:
|
||||
if '*' in self.allowed_origins:
|
||||
origin = '*'
|
||||
else:
|
||||
LOG.debug('CORS request from origin \'%s\' not permitted.'
|
||||
% origin)
|
||||
raise InvalidOriginError(origin)
|
||||
return origin, self.allowed_origins[origin]
|
||||
|
||||
def _apply_cors_request_headers(self, request, response):
|
||||
"""Handle Basic CORS Request (Section 6.1)
|
||||
|
||||
Given a request and a response, apply the CORS headers appropriate
|
||||
for the request to the response.
|
||||
"""
|
||||
|
||||
# Does the request have an origin header? (Section 6.1.1)
|
||||
if 'Origin' not in request.headers:
|
||||
return
|
||||
|
||||
# Is this origin registered? (Section 6.1.2)
|
||||
try:
|
||||
origin, cors_config = self._get_cors_config_by_origin(
|
||||
request.headers['Origin'])
|
||||
except InvalidOriginError:
|
||||
return
|
||||
|
||||
# Set the default origin permission headers. (Sections 6.1.3 & 6.4)
|
||||
if 'Vary' in response.headers:
|
||||
response.headers['Vary'] += ',Origin'
|
||||
else:
|
||||
response.headers['Vary'] = 'Origin'
|
||||
response.headers['Access-Control-Allow-Origin'] = origin
|
||||
|
||||
# Does this CORS configuration permit credentials? (Section 6.1.3)
|
||||
if cors_config['allow_credentials']:
|
||||
response.headers['Access-Control-Allow-Credentials'] = 'true'
|
||||
|
||||
# Attach the exposed headers and exit. (Section 6.1.4)
|
||||
if cors_config['expose_headers']:
|
||||
response.headers['Access-Control-Expose-Headers'] = \
|
||||
','.join(cors_config['expose_headers'] +
|
||||
self._latent_configuration['expose_headers'])
|
||||
|
||||
# NOTE(sileht): Shortcut for backwards compatibility
|
||||
filter_factory = CORS.factory
|
@ -1,60 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# 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.
|
||||
|
||||
"""Debug middleware"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
import six
|
||||
import webob.dec
|
||||
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
class Debug(base.ConfigurableMiddleware):
|
||||
"""Helper class that returns debug information.
|
||||
|
||||
Can be inserted into any WSGI application chain to get information about
|
||||
the request and response.
|
||||
"""
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
print(("*" * 40) + " REQUEST ENVIRON")
|
||||
for key, value in req.environ.items():
|
||||
print(key, "=", value)
|
||||
print()
|
||||
resp = req.get_response(self.application)
|
||||
|
||||
print(("*" * 40) + " RESPONSE HEADERS")
|
||||
for (key, value) in six.iteritems(resp.headers):
|
||||
print(key, "=", value)
|
||||
print()
|
||||
|
||||
resp.app_iter = self.print_generator(resp.app_iter)
|
||||
|
||||
return resp
|
||||
|
||||
@staticmethod
|
||||
def print_generator(app_iter):
|
||||
"""Prints the contents of a wrapper string iterator when iterated."""
|
||||
print(("*" * 40) + " BODY")
|
||||
for part in app_iter:
|
||||
sys.stdout.write(part)
|
||||
sys.stdout.flush()
|
||||
yield part
|
||||
print()
|
@ -1,377 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# 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 collections
|
||||
import gc
|
||||
import json
|
||||
import platform
|
||||
import socket
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import jinja2
|
||||
from oslo_utils import reflection
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
import stevedore
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
import webob.response
|
||||
|
||||
try:
|
||||
import greenlet
|
||||
except ImportError:
|
||||
greenlet = None
|
||||
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
def _find_objects(t):
|
||||
return [o for o in gc.get_objects() if isinstance(o, t)]
|
||||
|
||||
|
||||
def _expand_template(contents, params):
|
||||
tpl = jinja2.Template(source=contents,
|
||||
undefined=jinja2.StrictUndefined)
|
||||
return tpl.render(**params)
|
||||
|
||||
|
||||
class Healthcheck(base.ConfigurableMiddleware):
|
||||
"""Healthcheck middleware used for monitoring.
|
||||
|
||||
If the path is /healthcheck, it will respond 200 with "OK" as the body.
|
||||
Or 503 with the reason as the body if one of the backend report
|
||||
an application issue.
|
||||
|
||||
This is useful for the following reasons:
|
||||
|
||||
1. Load balancers can 'ping' this url to determine service availability.
|
||||
2. Provides an endpoint that is similar to 'mod_status' in apache which
|
||||
can provide details (or no details, depending on if configured) about
|
||||
the activity of the server.
|
||||
|
||||
Example requests/responses:
|
||||
|
||||
$ curl -i -X HEAD "http://0.0.0.0:8775/status"
|
||||
HTTP/1.1 204 No Content
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Length: 0
|
||||
Date: Fri, 11 Sep 2015 18:55:08 GMT
|
||||
|
||||
$ curl -i "http://0.0.0.0:8775/status"
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Length: 2
|
||||
Date: Fri, 11 Sep 2015 18:55:43 GMT
|
||||
|
||||
OK
|
||||
|
||||
Example of paste configuration:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[filter:healthcheck]
|
||||
paste.filter_factory = oslo_middleware:Healthcheck.factory
|
||||
path = /healthcheck
|
||||
backends = disable_by_file
|
||||
disable_by_file_path = /var/run/nova/healthcheck_disable
|
||||
|
||||
[pipeline:public_api]
|
||||
pipeline = healthcheck sizelimit [...] public_service
|
||||
|
||||
|
||||
Multiple filter sections can be defined if it desired to have
|
||||
pipelines with different healthcheck configuration, example:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pipeline:public_api]
|
||||
pipeline = healthcheck_public sizelimit [...] public_service
|
||||
|
||||
[pipeline:admin_api]
|
||||
pipeline = healthcheck_admin sizelimit [...] admin_service
|
||||
|
||||
[filter:healthcheck_public]
|
||||
paste.filter_factory = oslo_middleware:Healthcheck.factory
|
||||
path = /healthcheck_public
|
||||
backends = disable_by_file
|
||||
disable_by_file_path = /var/run/nova/healthcheck_public_disable
|
||||
|
||||
[filter:healthcheck_admin]
|
||||
paste.filter_factory = oslo_middleware:Healthcheck.factory
|
||||
path = /healthcheck_admin
|
||||
backends = disable_by_file
|
||||
disable_by_file_path = /var/run/nova/healthcheck_admin_disable
|
||||
|
||||
More details on available backends and their configuration can be found
|
||||
on this page: :doc:`healthcheck_plugins`.
|
||||
|
||||
"""
|
||||
|
||||
NAMESPACE = "oslo.middleware.healthcheck"
|
||||
HEALTHY_TO_STATUS_CODES = {
|
||||
True: webob.exc.HTTPOk.code,
|
||||
False: webob.exc.HTTPServiceUnavailable.code,
|
||||
}
|
||||
HEAD_HEALTHY_TO_STATUS_CODES = {
|
||||
True: webob.exc.HTTPNoContent.code,
|
||||
False: webob.exc.HTTPServiceUnavailable.code,
|
||||
}
|
||||
PLAIN_RESPONSE_TEMPLATE = """
|
||||
{% for reason in reasons %}
|
||||
{% if reason %}{{reason}}{% endif -%}
|
||||
{% endfor %}
|
||||
"""
|
||||
|
||||
HTML_RESPONSE_TEMPLATE = """
|
||||
<HTML>
|
||||
<HEAD><TITLE>Healthcheck Status</TITLE></HEAD>
|
||||
<BODY>
|
||||
{% if detailed -%}
|
||||
<H1>Server status</H1>
|
||||
{% if hostname -%}
|
||||
<B>Server hostname:</B><PRE>{{hostname|e}}</PRE>
|
||||
{%- endif %}
|
||||
<B>Current time:</B><PRE>{{now|e}}</PRE>
|
||||
<B>Python version:</B><PRE>{{python_version|e}}</PRE>
|
||||
<B>Platform:</B><PRE>{{platform|e}}</PRE>
|
||||
<HR></HR>
|
||||
<H2>Garbage collector:</H2>
|
||||
<B>Counts:</B><PRE>{{gc.counts|e}}</PRE>
|
||||
<B>Thresholds:</B><PRE>{{gc.threshold|e}}</PRE>
|
||||
<HR></HR>
|
||||
{%- endif %}
|
||||
<H2>Result of {{results|length}} checks:</H2>
|
||||
<TABLE bgcolor="#ffffff" border="1">
|
||||
<TBODY>
|
||||
<TR>
|
||||
{% if detailed -%}
|
||||
<TH>
|
||||
Kind
|
||||
</TH>
|
||||
<TH>
|
||||
Reason
|
||||
</TH>
|
||||
<TH>
|
||||
Details
|
||||
</TH>
|
||||
{% else %}
|
||||
<TH>
|
||||
Reason
|
||||
</TH>
|
||||
{%- endif %}
|
||||
</TR>
|
||||
{% for result in results -%}
|
||||
{% if result.reason -%}
|
||||
<TR>
|
||||
{% if detailed -%}
|
||||
<TD>{{result.class|e}}</TD>
|
||||
{%- endif %}
|
||||
<TD>{{result.reason|e}}</TD>
|
||||
{% if detailed -%}
|
||||
<TD>{{result.details|e}}</TD>
|
||||
{%- endif %}
|
||||
</TR>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
<HR></HR>
|
||||
{% if detailed -%}
|
||||
{% if greenthreads -%}
|
||||
<H2>{{greenthreads|length}} greenthread(s) active:</H2>
|
||||
<TABLE bgcolor="#ffffff" border="1">
|
||||
<TBODY>
|
||||
{% for stack in greenthreads -%}
|
||||
<TR>
|
||||
<TD><PRE>{{stack|e}}</PRE></TD>
|
||||
</TR>
|
||||
{%- endfor %}
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
<HR></HR>
|
||||
{%- endif %}
|
||||
{% if threads -%}
|
||||
<H2>{{threads|length}} thread(s) active:</H2>
|
||||
<TABLE bgcolor="#ffffff" border="1">
|
||||
<TBODY>
|
||||
{% for stack in threads -%}
|
||||
<TR>
|
||||
<TD><PRE>{{stack|e}}</PRE></TD>
|
||||
</TR>
|
||||
{%- endfor %}
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
</BODY>
|
||||
</HTML>
|
||||
"""
|
||||
|
||||
def __init__(self, application, conf):
|
||||
super(Healthcheck, self).__init__(application)
|
||||
self._path = conf.get('path', '/healthcheck')
|
||||
self._show_details = strutils.bool_from_string(conf.get('detailed'))
|
||||
self._backend_names = []
|
||||
backends = conf.get('backends')
|
||||
if backends:
|
||||
self._backend_names = backends.split(',')
|
||||
self._backends = stevedore.NamedExtensionManager(
|
||||
self.NAMESPACE, self._backend_names,
|
||||
name_order=True, invoke_on_load=True,
|
||||
invoke_args=(conf,))
|
||||
self._accept_to_functor = collections.OrderedDict([
|
||||
# Order here matters...
|
||||
('text/plain', self._make_text_response),
|
||||
('text/html', self._make_html_response),
|
||||
('application/json', self._make_json_response),
|
||||
])
|
||||
self._accept_order = tuple(six.iterkeys(self._accept_to_functor))
|
||||
# When no accept type matches instead of returning 406 we will
|
||||
# always return text/plain (because sending an error from this
|
||||
# middleware actually can cause issues).
|
||||
self._default_accept = 'text/plain'
|
||||
|
||||
@staticmethod
|
||||
def _get_threadstacks():
|
||||
threadstacks = []
|
||||
try:
|
||||
active_frames = sys._current_frames()
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
buf = six.StringIO()
|
||||
for stack in six.itervalues(active_frames):
|
||||
traceback.print_stack(stack, file=buf)
|
||||
threadstacks.append(buf.getvalue())
|
||||
buf.seek(0)
|
||||
buf.truncate()
|
||||
return threadstacks
|
||||
|
||||
@staticmethod
|
||||
def _get_greenstacks():
|
||||
greenstacks = []
|
||||
if greenlet is not None:
|
||||
buf = six.StringIO()
|
||||
for gt in _find_objects(greenlet.greenlet):
|
||||
traceback.print_stack(gt.gr_frame, file=buf)
|
||||
greenstacks.append(buf.getvalue())
|
||||
buf.seek(0)
|
||||
buf.truncate()
|
||||
return greenstacks
|
||||
|
||||
@staticmethod
|
||||
def _pretty_json_dumps(contents):
|
||||
return json.dumps(contents, indent=4, sort_keys=True)
|
||||
|
||||
@staticmethod
|
||||
def _are_results_healthy(results):
|
||||
for result in results:
|
||||
if not result.available:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _make_text_response(self, results, healthy):
|
||||
params = {
|
||||
'reasons': [result.reason for result in results],
|
||||
'detailed': self._show_details,
|
||||
}
|
||||
body = _expand_template(self.PLAIN_RESPONSE_TEMPLATE, params)
|
||||
return (body.strip(), 'text/plain')
|
||||
|
||||
def _make_json_response(self, results, healthy):
|
||||
if self._show_details:
|
||||
body = {
|
||||
'detailed': True,
|
||||
'python_version': sys.version,
|
||||
'now': str(timeutils.utcnow()),
|
||||
'platform': platform.platform(),
|
||||
'gc': {
|
||||
'counts': gc.get_count(),
|
||||
'threshold': gc.get_threshold(),
|
||||
},
|
||||
}
|
||||
reasons = []
|
||||
for result in results:
|
||||
reasons.append({
|
||||
'reason': result.reason,
|
||||
'details': result.details or '',
|
||||
'class': reflection.get_class_name(result,
|
||||
fully_qualified=False),
|
||||
})
|
||||
body['reasons'] = reasons
|
||||
body['greenthreads'] = self._get_greenstacks()
|
||||
body['threads'] = self._get_threadstacks()
|
||||
else:
|
||||
body = {
|
||||
'reasons': [result.reason for result in results],
|
||||
'detailed': False,
|
||||
}
|
||||
return (self._pretty_json_dumps(body), 'application/json')
|
||||
|
||||
def _make_head_response(self, results, healthy):
|
||||
return ( "", "text/plain")
|
||||
|
||||
def _make_html_response(self, results, healthy):
|
||||
try:
|
||||
hostname = socket.gethostname()
|
||||
except socket.error:
|
||||
hostname = None
|
||||
translated_results = []
|
||||
for result in results:
|
||||
translated_results.append({
|
||||
'details': result.details or '',
|
||||
'reason': result.reason,
|
||||
'class': reflection.get_class_name(result,
|
||||
fully_qualified=False),
|
||||
})
|
||||
params = {
|
||||
'healthy': healthy,
|
||||
'hostname': hostname,
|
||||
'results': translated_results,
|
||||
'detailed': self._show_details,
|
||||
'now': str(timeutils.utcnow()),
|
||||
'python_version': sys.version,
|
||||
'platform': platform.platform(),
|
||||
'gc': {
|
||||
'counts': gc.get_count(),
|
||||
'threshold': gc.get_threshold(),
|
||||
},
|
||||
'threads': self._get_threadstacks(),
|
||||
'greenthreads': self._get_threadstacks(),
|
||||
}
|
||||
body = _expand_template(self.HTML_RESPONSE_TEMPLATE, params)
|
||||
return (body.strip(), 'text/html')
|
||||
|
||||
@webob.dec.wsgify
|
||||
def process_request(self, req):
|
||||
if req.path != self._path:
|
||||
return None
|
||||
results = [ext.obj.healthcheck(req.server_port)
|
||||
for ext in self._backends]
|
||||
healthy = self._are_results_healthy(results)
|
||||
if req.method == "HEAD":
|
||||
functor = self._make_head_response
|
||||
status = self.HEAD_HEALTHY_TO_STATUS_CODES[healthy]
|
||||
else:
|
||||
status = self.HEALTHY_TO_STATUS_CODES[healthy]
|
||||
accept_type = req.accept.best_match(self._accept_order)
|
||||
if not accept_type:
|
||||
accept_type = self._default_accept
|
||||
functor = self._accept_to_functor[accept_type]
|
||||
body, content_type = functor(results, healthy)
|
||||
return webob.response.Response(status=status, body=body,
|
||||
content_type=content_type)
|
@ -1,69 +0,0 @@
|
||||
#
|
||||
# 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 argparse
|
||||
|
||||
from six.moves import SimpleHTTPServer # noqa
|
||||
from six.moves import socketserver
|
||||
import webob
|
||||
|
||||
from oslo_middleware import healthcheck
|
||||
|
||||
|
||||
class HttpHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
@webob.dec.wsgify
|
||||
def dummy_application(req):
|
||||
return 'test'
|
||||
app = healthcheck.Healthcheck(dummy_application, {'detailed': True})
|
||||
req = webob.Request.blank("/healthcheck", accept='text/html',
|
||||
method='GET')
|
||||
res = req.get_response(app)
|
||||
self.send_response(res.status_code)
|
||||
for header_name, header_value in res.headerlist:
|
||||
self.send_header(header_name, header_value)
|
||||
self.end_headers()
|
||||
self.wfile.write(res.body)
|
||||
self.wfile.close()
|
||||
|
||||
|
||||
def positive_int(blob):
|
||||
value = int(blob)
|
||||
if value < 0:
|
||||
msg = "%r is not a positive integer" % blob
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
return value
|
||||
|
||||
|
||||
def create_server(port=0):
|
||||
handler = HttpHandler
|
||||
server = socketserver.TCPServer(("", port), handler)
|
||||
return server
|
||||
|
||||
|
||||
def main(args=None):
|
||||
"""Runs a basic http server to show healthcheck functionality."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-p", "--port",
|
||||
help="Unused port to run the tiny"
|
||||
" http server on (or zero to select a"
|
||||
" random unused port)",
|
||||
type=positive_int, required=True)
|
||||
args = parser.parse_args(args=args)
|
||||
server = create_server(args.port)
|
||||
print("Serving at port: %s" % server.server_address[1])
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,111 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# 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 logging
|
||||
import os
|
||||
|
||||
from oslo_middleware._i18n import _LW
|
||||
from oslo_middleware.healthcheck import pluginbase
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DisableByFilesPortsHealthcheck(pluginbase.HealthcheckBaseExtension):
|
||||
"""DisableByFilesPorts healthcheck middleware plugin
|
||||
|
||||
This plugin checks presence of a file that is provided for a application
|
||||
running on a certain port to report if the service is unavailable
|
||||
or not.
|
||||
|
||||
Example of middleware configuration:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[filter:healthcheck]
|
||||
paste.filter_factory = oslo_middleware:Healthcheck.factory
|
||||
path = /healthcheck
|
||||
backends = disable_by_files_ports
|
||||
disable_by_file_paths = 5000:/var/run/keystone/healthcheck_disable, \
|
||||
35357:/var/run/keystone/admin_healthcheck_disable
|
||||
"""
|
||||
def __init__(self, conf):
|
||||
super(DisableByFilesPortsHealthcheck, self).__init__(conf)
|
||||
self.status_files = {}
|
||||
self.status_files.update(
|
||||
self._iter_paths_ports(self.conf.get('disable_by_file_paths')))
|
||||
|
||||
@staticmethod
|
||||
def _iter_paths_ports(paths):
|
||||
if paths:
|
||||
for port_path in paths.split(","):
|
||||
port_path = port_path.strip()
|
||||
if port_path:
|
||||
# On windows, drive letters are followed by colons,
|
||||
# which makes split() return 3 elements in this case
|
||||
port, path = port_path.split(":", 1)
|
||||
port = int(port)
|
||||
yield (port, path)
|
||||
|
||||
def healthcheck(self, server_port):
|
||||
path = self.status_files.get(server_port)
|
||||
if not path:
|
||||
LOG.warning(_LW('DisableByFilesPorts healthcheck middleware'
|
||||
' enabled without disable_by_file_paths set'
|
||||
' for port %s') % server_port)
|
||||
return pluginbase.HealthcheckResult(available=True,
|
||||
reason="OK")
|
||||
else:
|
||||
if not os.path.exists(path):
|
||||
return pluginbase.HealthcheckResult(available=True,
|
||||
reason="OK")
|
||||
else:
|
||||
return pluginbase.HealthcheckResult(available=False,
|
||||
reason="DISABLED BY FILE")
|
||||
|
||||
|
||||
class DisableByFileHealthcheck(pluginbase.HealthcheckBaseExtension):
|
||||
"""DisableByFile healthcheck middleware plugin
|
||||
|
||||
This plugin checks presence of a file to report if the service
|
||||
is unavailable or not.
|
||||
|
||||
Example of middleware configuration:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[filter:healthcheck]
|
||||
paste.filter_factory = oslo_middleware:Healthcheck.factory
|
||||
path = /healthcheck
|
||||
backends = disable_by_file
|
||||
disable_by_file_path = /var/run/nova/healthcheck_disable
|
||||
"""
|
||||
|
||||
def healthcheck(self, server_port):
|
||||
path = self.conf.get('disable_by_file_path')
|
||||
if path is None:
|
||||
LOG.warning(_LW('DisableByFile healthcheck middleware enabled '
|
||||
'without disable_by_file_path set'))
|
||||
return pluginbase.HealthcheckResult(
|
||||
available=True, reason="OK",
|
||||
details="No 'disable_by_file_path' configuration value"
|
||||
" specified")
|
||||
elif not os.path.exists(path):
|
||||
return pluginbase.HealthcheckResult(
|
||||
available=True, reason="OK",
|
||||
details="Path '%s' was not found" % path)
|
||||
else:
|
||||
return pluginbase.HealthcheckResult(
|
||||
available=False, reason="DISABLED BY FILE",
|
||||
details="Path '%s' was found" % path)
|
@ -1,40 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# 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 abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class HealthcheckResult(object):
|
||||
"""Result of a ``healthcheck`` method call should be this object."""
|
||||
|
||||
def __init__(self, available, reason, details=None):
|
||||
self.available = available
|
||||
self.reason = reason
|
||||
self.details = details
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class HealthcheckBaseExtension(object):
|
||||
def __init__(self, conf):
|
||||
self.conf = conf
|
||||
|
||||
@abc.abstractmethod
|
||||
def healthcheck(self, server_port):
|
||||
"""method called by the healthcheck middleware
|
||||
|
||||
return: HealthcheckResult object
|
||||
"""
|
@ -1,91 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# 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 debtcollector import removals
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
OPTS = [
|
||||
cfg.BoolOpt('enable_proxy_headers_parsing',
|
||||
default=False,
|
||||
help="Whether the application is behind a proxy or not. "
|
||||
"This determines if the middleware should parse the "
|
||||
"headers or not.")
|
||||
]
|
||||
|
||||
|
||||
class HTTPProxyToWSGI(base.ConfigurableMiddleware):
|
||||
"""HTTP proxy to WSGI termination middleware.
|
||||
|
||||
This middleware overloads WSGI environment variables with the one provided
|
||||
by the remote HTTP reverse proxy.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, application, *args, **kwargs):
|
||||
super(HTTPProxyToWSGI, self).__init__(application, *args, **kwargs)
|
||||
self.oslo_conf.register_opts(OPTS, group='oslo_middleware')
|
||||
|
||||
@staticmethod
|
||||
def _parse_rfc7239_header(header):
|
||||
"""Parses RFC7239 Forward headers.
|
||||
|
||||
e.g. for=192.0.2.60;proto=http, for=192.0.2.60;by=203.0.113.43
|
||||
|
||||
"""
|
||||
result = []
|
||||
for proxy in header.split(","):
|
||||
entry = {}
|
||||
for d in proxy.split(";"):
|
||||
key, _, value = d.partition("=")
|
||||
entry[key.lower()] = value
|
||||
result.append(entry)
|
||||
return result
|
||||
|
||||
def process_request(self, req):
|
||||
if not self._conf_get('enable_proxy_headers_parsing'):
|
||||
return
|
||||
fwd_hdr = req.environ.get("HTTP_FORWARDED")
|
||||
if fwd_hdr:
|
||||
proxies = self._parse_rfc7239_header(fwd_hdr)
|
||||
# Let's use the value from the first proxy
|
||||
if proxies:
|
||||
proxy = proxies[0]
|
||||
|
||||
forwarded_proto = proxy.get("proto")
|
||||
if forwarded_proto:
|
||||
req.environ['wsgi.url_scheme'] = forwarded_proto
|
||||
|
||||
forwarded_host = proxy.get("host")
|
||||
if forwarded_host:
|
||||
req.environ['HTTP_HOST'] = forwarded_host
|
||||
|
||||
else:
|
||||
# World before RFC7239
|
||||
forwarded_proto = req.environ.get("HTTP_X_FORWARDED_PROTO")
|
||||
if forwarded_proto:
|
||||
req.environ['wsgi.url_scheme'] = forwarded_proto
|
||||
|
||||
forwarded_host = req.environ.get("HTTP_X_FORWARDED_HOST")
|
||||
if forwarded_host:
|
||||
req.environ['HTTP_HOST'] = forwarded_host
|
||||
|
||||
v = req.environ.get("HTTP_X_FORWARDED_PREFIX")
|
||||
if v:
|
||||
req.environ['SCRIPT_NAME'] = v + req.environ['SCRIPT_NAME']
|
||||
|
||||
|
||||
@removals.remove
|
||||
class HTTPProxyToWSGIMiddleware(HTTPProxyToWSGI):
|
||||
"""Placeholder for backward compatibility"""
|
@ -1,27 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
#
|
||||
# Translators:
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-19 23:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2014-09-07 08:10+0000\n"
|
||||
"Last-Translator: Andreas Jaeger <jaegerandi@gmail.com>\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: German\n"
|
||||
|
||||
#, python-format
|
||||
msgid "An error occurred during processing the request: %s"
|
||||
msgstr "Ein Fehler trat auf während die Anfrage behandelt wurde: %s"
|
@ -1,26 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
#
|
||||
# Translators:
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-19 23:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2014-09-07 08:09+0000\n"
|
||||
"Last-Translator: Andreas Jaeger <jaegerandi@gmail.com>\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: German\n"
|
||||
|
||||
msgid "Request is too large."
|
||||
msgstr "Die Anfrage ist zu groß."
|
@ -1,27 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
#
|
||||
# Translators:
|
||||
# Andi Chandler <andi@gowling.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-19 23:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2014-11-03 11:03+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language: en-GB\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
|
||||
#, python-format
|
||||
msgid "An error occurred during processing the request: %s"
|
||||
msgstr "An error occurred during processing the request: %s"
|
@ -1,26 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
#
|
||||
# Translators:
|
||||
# Andi Chandler <andi@gowling.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-19 23:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2014-11-03 11:03+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language: en-GB\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
|
||||
msgid "Request is too large."
|
||||
msgstr "Request is too large."
|
@ -1,27 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
#
|
||||
# Translators:
|
||||
# Maxime COQUEREL <max.coquerel@gmail.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-19 23:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2014-09-25 09:01+0000\n"
|
||||
"Last-Translator: Maxime COQUEREL <max.coquerel@gmail.com>\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: French\n"
|
||||
|
||||
#, python-format
|
||||
msgid "An error occurred during processing the request: %s"
|
||||
msgstr "Une erreur s'est produite lors du traitement de la demande: %s"
|
@ -1,26 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2015 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
#
|
||||
# Translators:
|
||||
# Maxime COQUEREL <max.coquerel@gmail.com>, 2014
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-19 23:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2014-09-17 09:06+0000\n"
|
||||
"Last-Translator: Maxime COQUEREL <max.coquerel@gmail.com>\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Generated-By: Babel 2.0\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Language-Team: French\n"
|
||||
|
||||
msgid "Request is too large."
|
||||
msgstr "Demande trop importante."
|
@ -1,25 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2016 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-21 06:02+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.2.0\n"
|
||||
|
||||
#: oslo_middleware/catch_errors.py:40
|
||||
#, python-format
|
||||
msgid "An error occurred during processing the request: %s"
|
||||
msgstr ""
|
||||
|
@ -1,25 +0,0 @@
|
||||
# Translations template for oslo.middleware.
|
||||
# Copyright (C) 2016 ORGANIZATION
|
||||
# This file is distributed under the same license as the oslo.middleware
|
||||
# project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.middleware 3.7.1.dev18\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-04-21 06:02+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.2.0\n"
|
||||
|
||||
#: oslo_middleware/sizelimit.py:59 oslo_middleware/sizelimit.py:73
|
||||
#: oslo_middleware/sizelimit.py:90
|
||||
msgid "Request is too large."
|
||||
msgstr ""
|
||||
|
@ -1,157 +0,0 @@
|
||||
# Copyright 2014 IBM Corp.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
__all__ = [
|
||||
'list_opts',
|
||||
'list_opts_sizelimit',
|
||||
'list_opts_ssl',
|
||||
'list_opts_cors',
|
||||
'list_opts_http_proxy_to_wsgi',
|
||||
]
|
||||
|
||||
|
||||
import copy
|
||||
import itertools
|
||||
|
||||
from oslo_middleware import cors
|
||||
from oslo_middleware import http_proxy_to_wsgi
|
||||
from oslo_middleware import sizelimit
|
||||
from oslo_middleware import ssl
|
||||
|
||||
|
||||
def list_opts():
|
||||
"""Return a list of oslo.config options for ALL of the middleware classes.
|
||||
|
||||
The returned list includes all oslo.config options which may be registered
|
||||
at runtime by the library.
|
||||
|
||||
Each element of the list is a tuple. The first element is the name of the
|
||||
group under which the list of elements in the second element will be
|
||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
||||
config files.
|
||||
|
||||
This function is also discoverable via the 'oslo.middleware' entry point
|
||||
under the 'oslo.config.opts' namespace.
|
||||
|
||||
The purpose of this is to allow tools like the Oslo sample config file
|
||||
generator to discover the options exposed to users by this library.
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
return list(
|
||||
itertools.chain(
|
||||
list_opts_sizelimit(),
|
||||
list_opts_ssl(),
|
||||
list_opts_cors(),
|
||||
list_opts_http_proxy_to_wsgi(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def list_opts_sizelimit():
|
||||
"""Return a list of oslo.config options for the sizelimit middleware.
|
||||
|
||||
The returned list includes all oslo.config options which may be registered
|
||||
at runtime by the library.
|
||||
|
||||
Each element of the list is a tuple. The first element is the name of the
|
||||
group under which the list of elements in the second element will be
|
||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
||||
config files.
|
||||
|
||||
This function is also discoverable via the 'oslo.middleware' entry point
|
||||
under the 'oslo.config.opts' namespace.
|
||||
|
||||
The purpose of this is to allow tools like the Oslo sample config file
|
||||
generator to discover the options exposed to users by this library.
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
return [
|
||||
('oslo_middleware', copy.deepcopy(sizelimit._opts)),
|
||||
]
|
||||
|
||||
|
||||
def list_opts_ssl():
|
||||
"""Return a list of oslo.config options for the SSL middleware.
|
||||
|
||||
The returned list includes all oslo.config options which may be registered
|
||||
at runtime by the library.
|
||||
|
||||
Each element of the list is a tuple. The first element is the name of the
|
||||
group under which the list of elements in the second element will be
|
||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
||||
config files.
|
||||
|
||||
This function is also discoverable via the 'oslo.middleware' entry point
|
||||
under the 'oslo.config.opts' namespace.
|
||||
|
||||
The purpose of this is to allow tools like the Oslo sample config file
|
||||
generator to discover the options exposed to users by this library.
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
return [
|
||||
('oslo_middleware', copy.deepcopy(ssl.OPTS)),
|
||||
]
|
||||
|
||||
|
||||
def list_opts_cors():
|
||||
"""Return a list of oslo.config options for the cors middleware.
|
||||
|
||||
The returned list includes all oslo.config options which may be registered
|
||||
at runtime by the library.
|
||||
|
||||
Each element of the list is a tuple. The first element is the name of the
|
||||
group under which the list of elements in the second element will be
|
||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
||||
config files.
|
||||
|
||||
This function is also discoverable via the 'oslo.middleware' entry point
|
||||
under the 'oslo.config.opts' namespace.
|
||||
|
||||
The purpose of this is to allow tools like the Oslo sample config file
|
||||
generator to discover the options exposed to users by this library.
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
return [
|
||||
('cors', copy.deepcopy(cors.CORS_OPTS)),
|
||||
('cors.subdomain', copy.deepcopy(cors.CORS_OPTS))
|
||||
]
|
||||
|
||||
|
||||
def list_opts_http_proxy_to_wsgi():
|
||||
"""Return a list of oslo.config options for http_proxy_to_wsgi.
|
||||
|
||||
The returned list includes all oslo.config options which may be registered
|
||||
at runtime by the library.
|
||||
|
||||
Each element of the list is a tuple. The first element is the name of the
|
||||
group under which the list of elements in the second element will be
|
||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
||||
config files.
|
||||
|
||||
This function is also discoverable via the 'oslo.middleware' entry point
|
||||
under the 'oslo.config.opts' namespace.
|
||||
|
||||
The purpose of this is to allow tools like the Oslo sample config file
|
||||
generator to discover the options exposed to users by this library.
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
return [
|
||||
('oslo_middleware', copy.deepcopy(http_proxy_to_wsgi.OPTS)),
|
||||
]
|
@ -1,40 +0,0 @@
|
||||
# Copyright (c) 2013 NEC 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 oslo_context import context
|
||||
import webob.dec
|
||||
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
ENV_REQUEST_ID = 'openstack.request_id'
|
||||
HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
|
||||
|
||||
|
||||
class RequestId(base.ConfigurableMiddleware):
|
||||
"""Middleware that ensures request ID.
|
||||
|
||||
It ensures to assign request ID for each API request and set it to
|
||||
request environment. The request ID is also added to API response.
|
||||
"""
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
req_id = context.generate_request_id()
|
||||
req.environ[ENV_REQUEST_ID] = req_id
|
||||
response = req.get_response(self.application)
|
||||
if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
|
||||
response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
|
||||
return response
|
@ -1,95 +0,0 @@
|
||||
# Copyright (c) 2012 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Request Body limiting middleware.
|
||||
|
||||
"""
|
||||
|
||||
from oslo_config import cfg
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from oslo_middleware._i18n import _
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
_oldopts = [cfg.DeprecatedOpt('osapi_max_request_body_size',
|
||||
group='DEFAULT'),
|
||||
cfg.DeprecatedOpt('max_request_body_size',
|
||||
group='DEFAULT')]
|
||||
|
||||
_opts = [
|
||||
# default request size is 112k
|
||||
cfg.IntOpt('max_request_body_size',
|
||||
default=114688,
|
||||
help='The maximum body size for each '
|
||||
' request, in bytes.',
|
||||
deprecated_opts=_oldopts)
|
||||
]
|
||||
|
||||
|
||||
class LimitingReader(object):
|
||||
"""Reader to limit the size of an incoming request."""
|
||||
def __init__(self, data, limit):
|
||||
"""Initiates LimitingReader object.
|
||||
|
||||
:param data: Underlying data object
|
||||
:param limit: maximum number of bytes the reader should allow
|
||||
"""
|
||||
self.data = data
|
||||
self.limit = limit
|
||||
self.bytes_read = 0
|
||||
|
||||
def __iter__(self):
|
||||
for chunk in self.data:
|
||||
self.bytes_read += len(chunk)
|
||||
if self.bytes_read > self.limit:
|
||||
msg = _("Request is too large.")
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||
else:
|
||||
yield chunk
|
||||
|
||||
def read(self, i=None):
|
||||
# NOTE(jamielennox): We can't simply provide the default to the read()
|
||||
# call as the expected default differs between mod_wsgi and eventlet
|
||||
if i is None:
|
||||
result = self.data.read()
|
||||
else:
|
||||
result = self.data.read(i)
|
||||
self.bytes_read += len(result)
|
||||
if self.bytes_read > self.limit:
|
||||
msg = _("Request is too large.")
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||
return result
|
||||
|
||||
|
||||
class RequestBodySizeLimiter(base.ConfigurableMiddleware):
|
||||
"""Limit the size of incoming requests."""
|
||||
|
||||
def __init__(self, application, conf=None):
|
||||
super(RequestBodySizeLimiter, self).__init__(application, conf)
|
||||
self.oslo_conf.register_opts(_opts, group='oslo_middleware')
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
max_size = self._conf_get('max_request_body_size')
|
||||
if (req.content_length is not None and
|
||||
req.content_length > max_size):
|
||||
msg = _("Request is too large.")
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||
if req.content_length is None and req.is_body_readable:
|
||||
limiter = LimitingReader(req.body_file, max_size)
|
||||
req.body_file = limiter
|
||||
return self.application
|
@ -1,48 +0,0 @@
|
||||
# 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 debtcollector import removals
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import base
|
||||
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt('secure_proxy_ssl_header',
|
||||
default='X-Forwarded-Proto',
|
||||
deprecated_for_removal=True,
|
||||
help="The HTTP Header that will be used to determine what "
|
||||
"the original request protocol scheme was, even if it was "
|
||||
"hidden by an SSL termination proxy.")
|
||||
]
|
||||
|
||||
|
||||
removals.removed_module(__name__,
|
||||
"oslo_middleware.http_proxy_to_wsgi")
|
||||
|
||||
|
||||
class SSLMiddleware(base.ConfigurableMiddleware):
|
||||
"""SSL termination proxies middleware.
|
||||
|
||||
This middleware overloads wsgi.url_scheme with the one provided in
|
||||
secure_proxy_ssl_header header. This is useful when behind a SSL
|
||||
termination proxy.
|
||||
"""
|
||||
|
||||
def __init__(self, application, *args, **kwargs):
|
||||
super(SSLMiddleware, self).__init__(application, *args, **kwargs)
|
||||
self.oslo_conf.register_opts(OPTS, group='oslo_middleware')
|
||||
|
||||
def process_request(self, req):
|
||||
self.header_name = 'HTTP_{0}'.format(
|
||||
self._conf_get('secure_proxy_ssl_header').upper()
|
||||
.replace('-', '_'))
|
||||
req.environ['wsgi.url_scheme'] = req.environ.get(
|
||||
self.header_name, req.environ['wsgi.url_scheme'])
|
@ -1,102 +0,0 @@
|
||||
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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
|
||||
|
||||
from oslo_middleware.base import ConfigurableMiddleware
|
||||
from oslo_middleware.base import Middleware
|
||||
from oslotest.base import BaseTestCase
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
def application(req):
|
||||
return 'Hello, World!!!'
|
||||
|
||||
|
||||
class TestBase(BaseTestCase):
|
||||
"""Test the base middleware class."""
|
||||
|
||||
def test_extend_with_request(self):
|
||||
"""Assert that a newer middleware behaves as appropriate.
|
||||
|
||||
This tests makes sure that the request is passed to the
|
||||
middleware's implementation.
|
||||
"""
|
||||
# Bootstrap the application
|
||||
self.application = RequestBase(application)
|
||||
|
||||
# Send a request through.
|
||||
request = webob.Request({}, method='GET')
|
||||
request.get_response(self.application)
|
||||
|
||||
self.assertTrue(self.application.called_with_request)
|
||||
|
||||
def test_extend_without_request(self):
|
||||
"""Assert that an older middleware behaves as appropriate.
|
||||
|
||||
This tests makes sure that the request method is NOT passed to the
|
||||
middleware's implementation, and that there are no other expected
|
||||
errors.
|
||||
"""
|
||||
# Bootstrap the application
|
||||
self.application = NoRequestBase(application)
|
||||
|
||||
# Send a request through.
|
||||
request = webob.Request({}, method='GET')
|
||||
request.get_response(self.application)
|
||||
|
||||
self.assertTrue(self.application.called_without_request)
|
||||
|
||||
def test_no_content_type_added(self):
|
||||
class TestMiddleware(Middleware):
|
||||
@staticmethod
|
||||
def process_request(req):
|
||||
return "foobar"
|
||||
|
||||
m = TestMiddleware(None)
|
||||
request = webob.Request({}, method='GET')
|
||||
response = request.get_response(m)
|
||||
self.assertNotIn('Content-Type', response.headers)
|
||||
|
||||
def test_paste_deploy_legacy(self):
|
||||
app = LegacyMiddlewareTest.factory(
|
||||
{'global': True}, local=True)(application)
|
||||
self.assertEqual(app.conf, {})
|
||||
|
||||
def test_paste_deploy_configurable(self):
|
||||
app = ConfigurableMiddlewareTest.factory(
|
||||
{'global': True}, local=True)(application)
|
||||
self.assertEqual(app.conf, {'global': True, 'local': True})
|
||||
|
||||
|
||||
class NoRequestBase(Middleware):
|
||||
"""Test middleware, implements old model."""
|
||||
def process_response(self, response):
|
||||
self.called_without_request = True
|
||||
return response
|
||||
|
||||
|
||||
class RequestBase(Middleware):
|
||||
"""Test middleware, implements new model."""
|
||||
def process_response(self, response, request):
|
||||
self.called_with_request = True
|
||||
return response
|
||||
|
||||
|
||||
class ConfigurableMiddlewareTest(ConfigurableMiddleware):
|
||||
pass
|
||||
|
||||
|
||||
class LegacyMiddlewareTest(Middleware):
|
||||
pass
|
@ -1,47 +0,0 @@
|
||||
# Copyright (c) 2013 NEC 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 mock
|
||||
from oslotest import base as test_base
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from oslo_middleware import catch_errors
|
||||
|
||||
|
||||
class CatchErrorsTest(test_base.BaseTestCase):
|
||||
|
||||
def _test_has_request_id(self, application, expected_code=None):
|
||||
app = catch_errors.CatchErrors(application)
|
||||
req = webob.Request.blank('/test')
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(expected_code, res.status_int)
|
||||
|
||||
def test_success_response(self):
|
||||
@webob.dec.wsgify
|
||||
def application(req):
|
||||
return 'Hello, World!!!'
|
||||
|
||||
self._test_has_request_id(application, webob.exc.HTTPOk.code)
|
||||
|
||||
def test_internal_server_error(self):
|
||||
@webob.dec.wsgify
|
||||
def application(req):
|
||||
raise Exception()
|
||||
|
||||
with mock.patch.object(catch_errors.LOG, 'exception') as log_exc:
|
||||
self._test_has_request_id(application,
|
||||
webob.exc.HTTPInternalServerError.code)
|
||||
self.assertEqual(1, log_exc.call_count)
|
@ -1,53 +0,0 @@
|
||||
# Copyright (c) 2013 Rackspace Hosting
|
||||
# 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 uuid
|
||||
|
||||
import mock
|
||||
from oslotest import base as test_base
|
||||
from oslotest import moxstubout
|
||||
|
||||
from oslo_middleware import correlation_id
|
||||
|
||||
|
||||
class CorrelationIdTest(test_base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(CorrelationIdTest, self).setUp()
|
||||
self.stubs = self.useFixture(moxstubout.MoxStubout()).stubs
|
||||
|
||||
def test_process_request(self):
|
||||
app = mock.Mock()
|
||||
req = mock.Mock()
|
||||
req.headers = {}
|
||||
|
||||
mock_uuid4 = mock.Mock()
|
||||
mock_uuid4.return_value = "fake_uuid"
|
||||
self.stubs.Set(uuid, 'uuid4', mock_uuid4)
|
||||
|
||||
middleware = correlation_id.CorrelationId(app)
|
||||
middleware(req)
|
||||
|
||||
self.assertEqual(req.headers.get("X_CORRELATION_ID"), "fake_uuid")
|
||||
|
||||
def test_process_request_should_not_regenerate_correlation_id(self):
|
||||
app = mock.Mock()
|
||||
req = mock.Mock()
|
||||
req.headers = {"X_CORRELATION_ID": "correlation_id"}
|
||||
|
||||
middleware = correlation_id.CorrelationId(app)
|
||||
middleware(req)
|
||||
|
||||
self.assertEqual(req.headers.get("X_CORRELATION_ID"), "correlation_id")
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
# 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 oslotest import base
|
||||
import stevedore
|
||||
from testtools import matchers
|
||||
|
||||
|
||||
class TestPasteDeploymentEntryPoints(base.BaseTestCase):
|
||||
|
||||
def test_entry_points(self):
|
||||
factory_classes = {
|
||||
'catch_errors': 'CatchErrors',
|
||||
'correlation_id': 'CorrelationId',
|
||||
'cors': 'CORS',
|
||||
'debug': 'Debug',
|
||||
'healthcheck': 'Healthcheck',
|
||||
'http_proxy_to_wsgi': 'HTTPProxyToWSGI',
|
||||
'request_id': 'RequestId',
|
||||
'sizelimit': 'RequestBodySizeLimiter',
|
||||
'ssl': 'SSLMiddleware',
|
||||
}
|
||||
|
||||
em = stevedore.ExtensionManager('paste.filter_factory')
|
||||
|
||||
# Ensure all the factories are defined by their names
|
||||
factory_names = [extension.name for extension in em]
|
||||
self.assertThat(factory_names,
|
||||
matchers.ContainsAll(factory_classes))
|
@ -1,189 +0,0 @@
|
||||
# Copyright (c) 2013 NEC 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 threading
|
||||
import time
|
||||
|
||||
import mock
|
||||
from oslotest import base as test_base
|
||||
import requests
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from oslo_middleware import healthcheck
|
||||
from oslo_middleware.healthcheck import __main__
|
||||
|
||||
|
||||
class HealthcheckMainTests(test_base.BaseTestCase):
|
||||
|
||||
def test_startup_response(self):
|
||||
server = __main__.create_server(0)
|
||||
th = threading.Thread(target=server.serve_forever)
|
||||
th.start()
|
||||
self.addCleanup(server.shutdown)
|
||||
while True:
|
||||
try:
|
||||
# Connecting on 0.0.0.0 is not allowed on windows
|
||||
# The operating system will return WSAEADDRNOTAVAIL which
|
||||
# in turn will throw a requests.ConnectionError
|
||||
r = requests.get("http://127.0.0.1:%s" % (
|
||||
server.server_address[1]))
|
||||
except requests.ConnectionError:
|
||||
# Server hasn't started up yet, try again in a few.
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.assertEqual(200, r.status_code)
|
||||
break
|
||||
|
||||
|
||||
class HealthcheckTests(test_base.BaseTestCase):
|
||||
|
||||
@staticmethod
|
||||
@webob.dec.wsgify
|
||||
def application(req):
|
||||
return 'Hello, World!!!'
|
||||
|
||||
def _do_test_request(self, conf={}, path='/healthcheck',
|
||||
accept='text/plain', method='GET',
|
||||
server_port=80):
|
||||
self.app = healthcheck.Healthcheck(self.application, conf)
|
||||
req = webob.Request.blank(path, accept=accept, method=method)
|
||||
req.server_port = server_port
|
||||
res = req.get_response(self.app)
|
||||
return res
|
||||
|
||||
def _do_test(self, conf={}, path='/healthcheck',
|
||||
expected_code=webob.exc.HTTPOk.code,
|
||||
expected_body=b'', accept='text/plain',
|
||||
method='GET', server_port=80):
|
||||
res = self._do_test_request(conf=conf, path=path,
|
||||
accept=accept, method=method,
|
||||
server_port=server_port)
|
||||
self.assertEqual(expected_code, res.status_int)
|
||||
self.assertEqual(expected_body, res.body)
|
||||
|
||||
def test_default_path_match(self):
|
||||
self._do_test()
|
||||
|
||||
def test_default_path_not_match(self):
|
||||
self._do_test(path='/toto', expected_body=b'Hello, World!!!')
|
||||
|
||||
def test_configured_path_match(self):
|
||||
conf = {'path': '/hidden_healthcheck'}
|
||||
self._do_test(conf, path='/hidden_healthcheck')
|
||||
|
||||
def test_configured_path_not_match(self):
|
||||
conf = {'path': '/hidden_healthcheck'}
|
||||
self._do_test(conf, path='/toto', expected_body=b'Hello, World!!!')
|
||||
|
||||
@mock.patch('oslo_middleware.healthcheck.disable_by_file.LOG')
|
||||
def test_disablefile_unconfigured(self, fake_log):
|
||||
fake_warn = fake_log.warning
|
||||
conf = {'backends': 'disable_by_file'}
|
||||
self._do_test(conf, expected_body=b'OK')
|
||||
self.assertIn('disable_by_file', self.app._backends.names())
|
||||
fake_warn.assert_called_once_with(
|
||||
'DisableByFile healthcheck middleware '
|
||||
'enabled without disable_by_file_path '
|
||||
'set'
|
||||
)
|
||||
|
||||
def test_disablefile_enabled(self):
|
||||
conf = {'backends': 'disable_by_file',
|
||||
'disable_by_file_path': '/foobar'}
|
||||
self._do_test(conf, expected_body=b'OK')
|
||||
self.assertIn('disable_by_file', self.app._backends.names())
|
||||
|
||||
def test_disablefile_enabled_head(self):
|
||||
conf = {'backends': 'disable_by_file',
|
||||
'disable_by_file_path': '/foobar'}
|
||||
self._do_test(conf, expected_body=b'', method='HEAD',
|
||||
expected_code=webob.exc.HTTPNoContent.code)
|
||||
|
||||
def test_disablefile_enabled_html_detailed(self):
|
||||
conf = {'backends': 'disable_by_file',
|
||||
'disable_by_file_path': '/foobar', 'detailed': True}
|
||||
res = self._do_test_request(conf, accept="text/html")
|
||||
self.assertIn(b'Result of 1 checks:', res.body)
|
||||
self.assertIn(b'<TD>OK</TD>', res.body)
|
||||
self.assertEqual(webob.exc.HTTPOk.code, res.status_int)
|
||||
|
||||
def test_disablefile_disabled(self):
|
||||
filename = self.create_tempfiles([('test', 'foobar')])[0]
|
||||
conf = {'backends': 'disable_by_file',
|
||||
'disable_by_file_path': filename}
|
||||
self._do_test(conf,
|
||||
expected_code=webob.exc.HTTPServiceUnavailable.code,
|
||||
expected_body=b'DISABLED BY FILE')
|
||||
self.assertIn('disable_by_file', self.app._backends.names())
|
||||
|
||||
def test_disablefile_disabled_head(self):
|
||||
filename = self.create_tempfiles([('test', 'foobar')])[0]
|
||||
conf = {'backends': 'disable_by_file',
|
||||
'disable_by_file_path': filename}
|
||||
self._do_test(conf,
|
||||
expected_code=webob.exc.HTTPServiceUnavailable.code,
|
||||
expected_body=b'', method='HEAD')
|
||||
self.assertIn('disable_by_file', self.app._backends.names())
|
||||
|
||||
def test_disablefile_disabled_html_detailed(self):
|
||||
filename = self.create_tempfiles([('test', 'foobar')])[0]
|
||||
conf = {'backends': 'disable_by_file',
|
||||
'disable_by_file_path': filename, 'detailed': True}
|
||||
res = self._do_test_request(conf, accept="text/html")
|
||||
self.assertIn(b'<TD>DISABLED BY FILE</TD>', res.body)
|
||||
self.assertEqual(webob.exc.HTTPServiceUnavailable.code,
|
||||
res.status_int)
|
||||
|
||||
def test_two_backends(self):
|
||||
filename = self.create_tempfiles([('test', 'foobar')])[0]
|
||||
conf = {'backends': 'disable_by_file,disable_by_file',
|
||||
'disable_by_file_path': filename}
|
||||
self._do_test(conf,
|
||||
expected_code=webob.exc.HTTPServiceUnavailable.code,
|
||||
expected_body=b'DISABLED BY FILE\nDISABLED BY FILE')
|
||||
self.assertIn('disable_by_file', self.app._backends.names())
|
||||
|
||||
def test_disable_by_port_file(self):
|
||||
filename = self.create_tempfiles([('test', 'foobar')])[0]
|
||||
conf = {'backends': 'disable_by_files_ports',
|
||||
'disable_by_file_paths': "80:%s" % filename}
|
||||
self._do_test(conf,
|
||||
expected_code=webob.exc.HTTPServiceUnavailable.code,
|
||||
expected_body=b'DISABLED BY FILE')
|
||||
self.assertIn('disable_by_files_ports', self.app._backends.names())
|
||||
|
||||
def test_no_disable_by_port_file(self):
|
||||
filename = self.create_tempfiles([('test', 'foobar')])[0]
|
||||
conf = {'backends': 'disable_by_files_ports',
|
||||
'disable_by_file_paths': "8000:%s" % filename}
|
||||
self._do_test(conf,
|
||||
expected_code=webob.exc.HTTPOk.code,
|
||||
expected_body=b'OK')
|
||||
self.assertIn('disable_by_files_ports', self.app._backends.names())
|
||||
|
||||
def test_disable_by_port_many_files(self):
|
||||
filename = self.create_tempfiles([('test', 'foobar')])[0]
|
||||
filename2 = self.create_tempfiles([('test2', 'foobar2')])[0]
|
||||
conf = {'backends': 'disable_by_files_ports',
|
||||
'disable_by_file_paths': "80:%s,81:%s" % (filename, filename2)}
|
||||
self._do_test(conf,
|
||||
expected_code=webob.exc.HTTPServiceUnavailable.code,
|
||||
expected_body=b'DISABLED BY FILE')
|
||||
self._do_test(conf,
|
||||
expected_code=webob.exc.HTTPServiceUnavailable.code,
|
||||
expected_body=b'DISABLED BY FILE',
|
||||
server_port=81)
|
||||
self.assertIn('disable_by_files_ports', self.app._backends.names())
|
@ -1,131 +0,0 @@
|
||||
# Copyright (c) 2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 wsgiref import util
|
||||
|
||||
from oslotest import base as test_base
|
||||
import webob
|
||||
|
||||
from oslo_middleware import http_proxy_to_wsgi
|
||||
|
||||
|
||||
class TestHTTPProxyToWSGI(test_base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHTTPProxyToWSGI, self).setUp()
|
||||
|
||||
@webob.dec.wsgify()
|
||||
def fake_app(req):
|
||||
return util.application_uri(req.environ)
|
||||
|
||||
self.middleware = http_proxy_to_wsgi.HTTPProxyToWSGI(fake_app)
|
||||
self.middleware.oslo_conf.set_override('enable_proxy_headers_parsing',
|
||||
True,
|
||||
group='oslo_middleware',
|
||||
enforce_type=True)
|
||||
self.request = webob.Request.blank('/foo/bar', method='POST')
|
||||
|
||||
def test_backward_compat(self):
|
||||
@webob.dec.wsgify()
|
||||
def fake_app(req):
|
||||
return util.application_uri(req.environ)
|
||||
|
||||
self.middleware = http_proxy_to_wsgi.HTTPProxyToWSGIMiddleware(
|
||||
fake_app)
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"http://localhost:80/", response.body)
|
||||
|
||||
def test_no_headers(self):
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"http://localhost:80/", response.body)
|
||||
|
||||
def test_url_translate_ssl(self):
|
||||
self.request.headers['X-Forwarded-Proto'] = "https"
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"https://localhost:80/", response.body)
|
||||
|
||||
def test_url_translate_ssl_port(self):
|
||||
self.request.headers['X-Forwarded-Proto'] = "https"
|
||||
self.request.headers['X-Forwarded-Host'] = "example.com:123"
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"https://example.com:123/", response.body)
|
||||
|
||||
def test_url_translate_host_ipv6(self):
|
||||
self.request.headers['X-Forwarded-Proto'] = "https"
|
||||
self.request.headers['X-Forwarded-Host'] = "[f00:b4d::1]:123"
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"https://[f00:b4d::1]:123/", response.body)
|
||||
|
||||
def test_url_translate_base(self):
|
||||
self.request.headers['X-Forwarded-Prefix'] = "/bla"
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"http://localhost:80/bla", response.body)
|
||||
|
||||
def test_url_translate_port_and_base_and_proto_and_host(self):
|
||||
self.request.headers['X-Forwarded-Proto'] = "https"
|
||||
self.request.headers['X-Forwarded-Prefix'] = "/bla"
|
||||
self.request.headers['X-Forwarded-Host'] = "example.com:8043"
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"https://example.com:8043/bla", response.body)
|
||||
|
||||
def test_rfc7239_invalid(self):
|
||||
self.request.headers['Forwarded'] = (
|
||||
"iam=anattacker;metoo, I will crash you!!P;m,xx")
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"http://localhost:80/", response.body)
|
||||
|
||||
def test_rfc7239_proto(self):
|
||||
self.request.headers['Forwarded'] = (
|
||||
"for=foobar;proto=https, for=foobaz;proto=http")
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"https://localhost:80/", response.body)
|
||||
|
||||
def test_rfc7239_proto_host(self):
|
||||
self.request.headers['Forwarded'] = (
|
||||
"for=foobar;proto=https;host=example.com, for=foobaz;proto=http")
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"https://example.com/", response.body)
|
||||
|
||||
def test_rfc7239_proto_host_base(self):
|
||||
self.request.headers['Forwarded'] = (
|
||||
"for=foobar;proto=https;host=example.com:8043, for=foobaz")
|
||||
self.request.headers['X-Forwarded-Prefix'] = "/bla"
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"https://example.com:8043/bla", response.body)
|
||||
|
||||
|
||||
class TestHTTPProxyToWSGIDisabled(test_base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHTTPProxyToWSGIDisabled, self).setUp()
|
||||
|
||||
@webob.dec.wsgify()
|
||||
def fake_app(req):
|
||||
return util.application_uri(req.environ)
|
||||
|
||||
self.middleware = http_proxy_to_wsgi.HTTPProxyToWSGI(fake_app)
|
||||
self.middleware.oslo_conf.set_override('enable_proxy_headers_parsing',
|
||||
False,
|
||||
group='oslo_middleware',
|
||||
enforce_type=True)
|
||||
self.request = webob.Request.blank('/foo/bar', method='POST')
|
||||
|
||||
def test_no_headers(self):
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"http://localhost:80/", response.body)
|
||||
|
||||
def test_url_translate_ssl_has_no_effect(self):
|
||||
self.request.headers['X-Forwarded-Proto'] = "https"
|
||||
self.request.headers['X-Forwarded-Host'] = "example.com:123"
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(b"http://localhost:80/", response.body)
|
@ -1,31 +0,0 @@
|
||||
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 oslo_middleware import opts
|
||||
from oslotest.base import BaseTestCase
|
||||
|
||||
|
||||
class TestOptionDiscovery(BaseTestCase):
|
||||
|
||||
def test_all(self):
|
||||
opts.list_opts()
|
||||
|
||||
def test_sizelimit(self):
|
||||
opts.list_opts_sizelimit()
|
||||
|
||||
def test_cors(self):
|
||||
opts.list_opts_cors()
|
||||
|
||||
def test_ssl(self):
|
||||
opts.list_opts_ssl()
|
@ -1,39 +0,0 @@
|
||||
# Copyright (c) 2013 NEC 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 oslotest import base as test_base
|
||||
from testtools import matchers
|
||||
import webob
|
||||
import webob.dec
|
||||
|
||||
from oslo_middleware import request_id
|
||||
|
||||
|
||||
class RequestIdTest(test_base.BaseTestCase):
|
||||
def test_generate_request_id(self):
|
||||
@webob.dec.wsgify
|
||||
def application(req):
|
||||
return req.environ[request_id.ENV_REQUEST_ID]
|
||||
|
||||
app = request_id.RequestId(application)
|
||||
req = webob.Request.blank('/test')
|
||||
res = req.get_response(app)
|
||||
res_req_id = res.headers.get(request_id.HTTP_RESP_HEADER_REQUEST_ID)
|
||||
if isinstance(res_req_id, bytes):
|
||||
res_req_id = res_req_id.decode('utf-8')
|
||||
self.assertThat(res_req_id, matchers.StartsWith('req-'))
|
||||
# request-id in request environ is returned as response body
|
||||
self.assertEqual(res_req_id, res.body.decode('utf-8'))
|
@ -1,108 +0,0 @@
|
||||
# Copyright (c) 2012 Red Hat, Inc.
|
||||
#
|
||||
# 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 oslo_config import fixture as config
|
||||
from oslotest import base as test_base
|
||||
import six
|
||||
import webob
|
||||
|
||||
from oslo_middleware import sizelimit
|
||||
|
||||
|
||||
class TestLimitingReader(test_base.BaseTestCase):
|
||||
|
||||
def test_limiting_reader(self):
|
||||
BYTES = 1024
|
||||
bytes_read = 0
|
||||
data = six.StringIO("*" * BYTES)
|
||||
for chunk in sizelimit.LimitingReader(data, BYTES):
|
||||
bytes_read += len(chunk)
|
||||
|
||||
self.assertEqual(bytes_read, BYTES)
|
||||
|
||||
bytes_read = 0
|
||||
data = six.StringIO("*" * BYTES)
|
||||
reader = sizelimit.LimitingReader(data, BYTES)
|
||||
byte = reader.read(1)
|
||||
while len(byte) != 0:
|
||||
bytes_read += 1
|
||||
byte = reader.read(1)
|
||||
|
||||
self.assertEqual(bytes_read, BYTES)
|
||||
|
||||
def test_read_default_value(self):
|
||||
BYTES = 1024
|
||||
data_str = "*" * BYTES
|
||||
data = six.StringIO(data_str)
|
||||
reader = sizelimit.LimitingReader(data, BYTES)
|
||||
res = reader.read()
|
||||
self.assertEqual(data_str, res)
|
||||
|
||||
def test_limiting_reader_fails(self):
|
||||
BYTES = 1024
|
||||
|
||||
def _consume_all_iter():
|
||||
bytes_read = 0
|
||||
data = six.StringIO("*" * BYTES)
|
||||
for chunk in sizelimit.LimitingReader(data, BYTES - 1):
|
||||
bytes_read += len(chunk)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
_consume_all_iter)
|
||||
|
||||
def _consume_all_read():
|
||||
bytes_read = 0
|
||||
data = six.StringIO("*" * BYTES)
|
||||
reader = sizelimit.LimitingReader(data, BYTES - 1)
|
||||
byte = reader.read(1)
|
||||
while len(byte) != 0:
|
||||
bytes_read += 1
|
||||
byte = reader.read(1)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
_consume_all_read)
|
||||
|
||||
|
||||
class TestRequestBodySizeLimiter(test_base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestRequestBodySizeLimiter, self).setUp()
|
||||
self.useFixture(config.Config())
|
||||
|
||||
@webob.dec.wsgify()
|
||||
def fake_app(req):
|
||||
return webob.Response(req.body)
|
||||
|
||||
self.middleware = sizelimit.RequestBodySizeLimiter(fake_app)
|
||||
self.MAX_REQUEST_BODY_SIZE = (
|
||||
self.middleware.oslo_conf.oslo_middleware.max_request_body_size)
|
||||
self.request = webob.Request.blank('/', method='POST')
|
||||
|
||||
def test_content_length_acceptable(self):
|
||||
self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE
|
||||
self.request.body = b"0" * self.MAX_REQUEST_BODY_SIZE
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(response.status_int, 200)
|
||||
|
||||
def test_content_length_too_large(self):
|
||||
self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE + 1
|
||||
self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(response.status_int, 413)
|
||||
|
||||
def test_request_too_large_no_content_length(self):
|
||||
self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
|
||||
self.request.headers['Content-Length'] = None
|
||||
response = self.request.get_response(self.middleware)
|
||||
self.assertEqual(response.status_int, 413)
|
@ -1,57 +0,0 @@
|
||||
# Copyright (c) 2015 Thales Services SAS
|
||||
# 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 oslo_config import fixture as config
|
||||
from oslotest import base
|
||||
import webob
|
||||
|
||||
from oslo_middleware import ssl
|
||||
|
||||
|
||||
class SSLMiddlewareTest(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SSLMiddlewareTest, self).setUp()
|
||||
self.useFixture(config.Config())
|
||||
|
||||
def _test_scheme(self, expected, headers, secure_proxy_ssl_header=None):
|
||||
middleware = ssl.SSLMiddleware(None)
|
||||
if secure_proxy_ssl_header:
|
||||
middleware.oslo_conf.set_override(
|
||||
'secure_proxy_ssl_header', secure_proxy_ssl_header,
|
||||
group='oslo_middleware', enforce_type=True)
|
||||
request = webob.Request.blank('http://example.com/', headers=headers)
|
||||
|
||||
# Ensure ssl middleware does not stop pipeline execution
|
||||
self.assertIsNone(middleware.process_request(request))
|
||||
|
||||
self.assertEqual(expected, request.scheme)
|
||||
|
||||
def test_without_forwarded_protocol(self):
|
||||
self._test_scheme('http', {})
|
||||
|
||||
def test_with_forwarded_protocol(self):
|
||||
headers = {'X-Forwarded-Proto': 'https'}
|
||||
self._test_scheme('https', headers)
|
||||
|
||||
def test_with_custom_header(self):
|
||||
headers = {'X-Forwarded-Proto': 'https'}
|
||||
self._test_scheme('http', headers,
|
||||
secure_proxy_ssl_header='X-My-Header')
|
||||
|
||||
def test_with_custom_header_and_forwarded_protocol(self):
|
||||
headers = {'X-My-Header': 'https'}
|
||||
self._test_scheme('https', headers,
|
||||
secure_proxy_ssl_header='X-My-Header')
|
@ -1,14 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=1.6 # Apache-2.0
|
||||
Jinja2>=2.8 # BSD License (3 clause)
|
||||
oslo.config>=3.14.0 # Apache-2.0
|
||||
oslo.context>=2.4.0 # Apache-2.0
|
||||
oslo.i18n>=2.1.0 # Apache-2.0
|
||||
oslo.utils>=3.16.0 # Apache-2.0
|
||||
six>=1.9.0 # MIT
|
||||
stevedore>=1.16.0 # Apache-2.0
|
||||
WebOb>=1.2.3 # MIT
|
||||
debtcollector>=1.2.0 # Apache-2.0
|
74
setup.cfg
74
setup.cfg
@ -1,74 +0,0 @@
|
||||
[metadata]
|
||||
name = oslo.middleware
|
||||
summary = Oslo Middleware library
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://wiki.openstack.org/wiki/Oslo#oslo.middleware
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.4
|
||||
|
||||
[files]
|
||||
packages =
|
||||
oslo_middleware
|
||||
|
||||
[entry_points]
|
||||
oslo.config.opts =
|
||||
oslo.middleware = oslo_middleware.opts:list_opts
|
||||
oslo.middleware.cors = oslo_middleware.opts:list_opts_cors
|
||||
oslo.middleware.sizelimit = oslo_middleware.opts:list_opts_sizelimit
|
||||
oslo.middleware.ssl = oslo_middleware.opts:list_opts_ssl
|
||||
oslo.middleware.http_proxy_to_wsgi = oslo_middleware.opts:list_opts_http_proxy_to_wsgi
|
||||
|
||||
oslo.middleware.healthcheck =
|
||||
disable_by_file = oslo_middleware.healthcheck.disable_by_file:DisableByFileHealthcheck
|
||||
disable_by_files_ports = oslo_middleware.healthcheck.disable_by_file:DisableByFilesPortsHealthcheck
|
||||
|
||||
paste.filter_factory =
|
||||
catch_errors = oslo_middleware:CatchErrors.factory
|
||||
correlation_id = oslo_middleware:CorrelationId.factory
|
||||
cors = oslo_middleware:CORS.factory
|
||||
debug = oslo_middleware:Debug.factory
|
||||
healthcheck = oslo_middleware:Healthcheck.factory
|
||||
http_proxy_to_wsgi = oslo_middleware:HTTPProxyToWSGI.factory
|
||||
request_id = oslo_middleware:RequestId.factory
|
||||
sizelimit = oslo_middleware:RequestBodySizeLimiter.factory
|
||||
ssl = oslo_middleware:SSLMiddleware.factory
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[compile_catalog]
|
||||
directory = oslo_middleware/locale
|
||||
domain = oslo_middleware
|
||||
|
||||
[update_catalog]
|
||||
domain = oslo_middleware
|
||||
output_dir = oslo_middleware/locale
|
||||
input_file = oslo_middleware/locale/oslo_middleware.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = oslo_middleware/locale/oslo_middleware.pot
|
||||
|
||||
[pbr]
|
||||
warnerrors = True
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
29
setup.py
29
setup.py
@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=1.8'],
|
||||
pbr=True)
|
@ -1,12 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
hacking<0.11,>=0.10.0
|
||||
mock>=2.0 # BSD
|
||||
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
coverage>=3.6 # Apache-2.0
|
37
tox.ini
37
tox.ini
@ -1,37 +0,0 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py34,py27,pypy,pep8
|
||||
|
||||
[testenv]
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py test --coverage --coverage-package-name=oslo_middleware --testr-args='{posargs}'
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,__init__.py
|
||||
|
||||
[hacking]
|
||||
import_exceptions = oslo_middleware._i18n
|
||||
|
||||
[testenv:pip-missing-reqs]
|
||||
# do not install test-requirements as that will pollute the virtualenv for
|
||||
# determining missing packages
|
||||
# this also means that pip-missing-reqs must be installed separately, outside
|
||||
# of the requirements.txt files
|
||||
deps = pip_missing_reqs
|
||||
commands = pip-missing-reqs -d --ignore-module=oslo_middleware* --ignore-module=pkg_resources --ignore-file=oslo_middleware/tests/* oslo_middleware
|
Loading…
x
Reference in New Issue
Block a user