forked from s434650/CatOrNot
Add README
This commit is contained in:
parent
b34d2361bc
commit
11b478aa97
2
README.MD
Normal file
2
README.MD
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Cat or Not - first prototype
|
||||||
|
### Monday 15:30 group
|
11
venv/bin/gunicorn
Executable file
11
venv/bin/gunicorn
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/home/c00ler/cat_or_not/venv/bin/python3
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from gunicorn.app.wsgiapp import run
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(run())
|
11
venv/bin/gunicorn_paster
Executable file
11
venv/bin/gunicorn_paster
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/home/c00ler/cat_or_not/venv/bin/python3
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from gunicorn.app.pasterapp import run
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(run())
|
@ -0,0 +1 @@
|
|||||||
|
pip
|
@ -0,0 +1,23 @@
|
|||||||
|
2009-2018 (c) Benoît Chesneau <benoitc@e-engura.org>
|
||||||
|
2009-2015 (c) Paul J. Davis <paul.joseph.davis@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,111 @@
|
|||||||
|
Metadata-Version: 2.1
|
||||||
|
Name: gunicorn
|
||||||
|
Version: 19.9.0
|
||||||
|
Summary: WSGI HTTP Server for UNIX
|
||||||
|
Home-page: http://gunicorn.org
|
||||||
|
Author: Benoit Chesneau
|
||||||
|
Author-email: benoitc@e-engura.com
|
||||||
|
License: MIT
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Development Status :: 4 - Beta
|
||||||
|
Classifier: Environment :: Other Environment
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Operating System :: MacOS :: MacOS X
|
||||||
|
Classifier: Operating System :: POSIX
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 2
|
||||||
|
Classifier: Programming Language :: Python :: 2.6
|
||||||
|
Classifier: Programming Language :: Python :: 2.7
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.2
|
||||||
|
Classifier: Programming Language :: Python :: 3.3
|
||||||
|
Classifier: Programming Language :: Python :: 3.4
|
||||||
|
Classifier: Programming Language :: Python :: 3.5
|
||||||
|
Classifier: Programming Language :: Python :: 3.6
|
||||||
|
Classifier: Topic :: Internet
|
||||||
|
Classifier: Topic :: Utilities
|
||||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Classifier: Topic :: Internet :: WWW/HTTP
|
||||||
|
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
|
||||||
|
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server
|
||||||
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||||
|
Requires-Python: >=2.6, !=3.0.*, !=3.1.*
|
||||||
|
Provides-Extra: tornado
|
||||||
|
Provides-Extra: gthread
|
||||||
|
Provides-Extra: eventlet
|
||||||
|
Provides-Extra: gevent
|
||||||
|
Provides-Extra: eventlet
|
||||||
|
Requires-Dist: eventlet (>=0.9.7); extra == 'eventlet'
|
||||||
|
Provides-Extra: gevent
|
||||||
|
Requires-Dist: gevent (>=0.13); extra == 'gevent'
|
||||||
|
Provides-Extra: gthread
|
||||||
|
Provides-Extra: tornado
|
||||||
|
Requires-Dist: tornado (>=0.2); extra == 'tornado'
|
||||||
|
|
||||||
|
Gunicorn
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/v/gunicorn.svg?style=flat
|
||||||
|
:alt: PyPI version
|
||||||
|
:target: https://pypi.python.org/pypi/gunicorn
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/pyversions/gunicorn.svg
|
||||||
|
:alt: Supported Python versions
|
||||||
|
:target: https://pypi.python.org/pypi/gunicorn
|
||||||
|
|
||||||
|
.. image:: https://travis-ci.org/benoitc/gunicorn.svg?branch=master
|
||||||
|
:alt: Build Status
|
||||||
|
:target: https://travis-ci.org/benoitc/gunicorn
|
||||||
|
|
||||||
|
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork
|
||||||
|
worker model ported from Ruby's Unicorn_ project. The Gunicorn server is broadly
|
||||||
|
compatible with various web frameworks, simply implemented, light on server
|
||||||
|
resource usage, and fairly speedy.
|
||||||
|
|
||||||
|
Feel free to join us in `#gunicorn`_ on Freenode_.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The documentation is hosted at http://docs.gunicorn.org.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Gunicorn requires **Python 2.x >= 2.6** or **Python 3.x >= 3.2**.
|
||||||
|
|
||||||
|
Install from PyPI::
|
||||||
|
|
||||||
|
$ pip install gunicorn
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Basic usage::
|
||||||
|
|
||||||
|
$ gunicorn [OPTIONS] APP_MODULE
|
||||||
|
|
||||||
|
Where ``APP_MODULE`` is of the pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``. The
|
||||||
|
module name can be a full dotted path. The variable name refers to a WSGI
|
||||||
|
callable that should be found in the specified module.
|
||||||
|
|
||||||
|
Example with test app::
|
||||||
|
|
||||||
|
$ cd examples
|
||||||
|
$ gunicorn --workers=2 test:app
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
Gunicorn is released under the MIT License. See the LICENSE_ file for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
.. _Unicorn: https://bogomips.org/unicorn/
|
||||||
|
.. _`#gunicorn`: https://webchat.freenode.net/?channels=gunicorn
|
||||||
|
.. _Freenode: https://freenode.net/
|
||||||
|
.. _LICENSE: https://github.com/benoitc/gunicorn/blob/master/LICENSE
|
||||||
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
|||||||
|
gunicorn/__init__.py,sha256=kRm2HQJytwQi-xmyUfM0cOMyls23DfFDgGFbGI2Gj68,255
|
||||||
|
gunicorn/_compat.py,sha256=5cXb6vMfVzInDq-AHNyZfsK-UG5NetDn62nPfqylHSU,9355
|
||||||
|
gunicorn/arbiter.py,sha256=AbJNSFnTmx9Qd-vZAqEH3y5fz8ydPmyli_BERNIwdyE,20158
|
||||||
|
gunicorn/argparse_compat.py,sha256=gsHDGwo4BSJWHdiaEXy0Emr96NKC0LDYmK5nB7PE8Qc,87791
|
||||||
|
gunicorn/config.py,sha256=wYeAJFMweU3FXNF4BdfgZzPC94vUXUnuYgI6lNk-5_U,53420
|
||||||
|
gunicorn/debug.py,sha256=UUw-eteLEm_OQ98D6K3XtDjx4Dya2H35zdiu8z7F7uc,2289
|
||||||
|
gunicorn/errors.py,sha256=JlDBjag90gMiRwLHG3xzEJzDOntSl1iM32R277-U6j0,919
|
||||||
|
gunicorn/glogging.py,sha256=bvnX-sky6HgqJor2JZ9VKZZzT4uh_yOgknkYegB7D7Y,15581
|
||||||
|
gunicorn/pidfile.py,sha256=_69tsfF1aHklrMrJe2sHERovMduRByVTv99my7yQ874,2357
|
||||||
|
gunicorn/reloader.py,sha256=CPNfYAAvJHazX3NAM7qysSRt0fpiHBGPqBlB0tYKhxs,3839
|
||||||
|
gunicorn/selectors.py,sha256=14_UESrpE3AQKXWKeeAUG9vBTzJ0yTYDGtEo6xOtlDY,18997
|
||||||
|
gunicorn/six.py,sha256=6N-6RCENPfBtMpN5UmgDfDKmJebbbuPu_Dk3Zf8ngww,27344
|
||||||
|
gunicorn/sock.py,sha256=gX2FsdsOGMCtSHbDXn7lsiYYYRc3roQklIJLip1oZQo,6019
|
||||||
|
gunicorn/systemd.py,sha256=ffhv17cdv-hDeFAJi1eAVtJskkVciV6cQU75Q2oplqg,1362
|
||||||
|
gunicorn/util.py,sha256=Ns_a8Pf7MkaEi0KbV3GsP9aVQ2a_S45EjSE6Iyg2tYU,16229
|
||||||
|
gunicorn/app/__init__.py,sha256=GuqstqdkizeV4HRbd8aGMBn0Q8IDOyRU1wMMNqNe5GY,127
|
||||||
|
gunicorn/app/base.py,sha256=LKxyziLMPNlK3qm6dPMieELBqfLfmwBFnn9SB-KBogE,6652
|
||||||
|
gunicorn/app/pasterapp.py,sha256=AGzZnUpcpw8O8KrizxTgdJBZ4lQdrHgsV0gdx7FVTs8,6046
|
||||||
|
gunicorn/app/wsgiapp.py,sha256=ny71qjegQHl_bGMjNfq_aemPrmGEpH2bMRIdph6bj4Q,1870
|
||||||
|
gunicorn/http/__init__.py,sha256=b4TF3x5F0VYOPTOeNYwRGR1EYHBaPMhZRMoNeuD5-n0,277
|
||||||
|
gunicorn/http/_sendfile.py,sha256=Eqd-s3HlvLuyfGjqaH_Jk72cAtEV8hQv5tb1M1AqcBU,2217
|
||||||
|
gunicorn/http/body.py,sha256=MmlZpj_6oRPj3oPVSMQZr0X3KH6ikntxDnVcLgfekZs,7345
|
||||||
|
gunicorn/http/errors.py,sha256=sNjF2lm4m2qyZ9l95_U33FRxPXpxXzjnZyYqWS-hxd4,2850
|
||||||
|
gunicorn/http/message.py,sha256=G5po0upwbrTyIggb_IEAItIjSi_aDoWYLPQ62o8pOI4,12257
|
||||||
|
gunicorn/http/parser.py,sha256=IRMvp0veP4wL8Z4vgNV72CPydCNPdNNIy9u-DlDvvSo,1294
|
||||||
|
gunicorn/http/unreader.py,sha256=s4kDW5euiJPsDuHzCqFXUtHCApqIxpShb9dtAyjJw9Y,2019
|
||||||
|
gunicorn/http/wsgi.py,sha256=SETzcFoLggub2aMuGduTVELBwJGg9YvvDbkiFbugkwU,12856
|
||||||
|
gunicorn/instrument/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
gunicorn/instrument/statsd.py,sha256=5xueDuTZMFtmS8ayGT4sU_OyB9qkEv4Agk-eJwAmhJM,4434
|
||||||
|
gunicorn/workers/__init__.py,sha256=arPaAM8HxcK39L2dmDzmMhpK9bsyLOJymuCcBz_qqw0,774
|
||||||
|
gunicorn/workers/_gaiohttp.py,sha256=llho90CjwpeAB9ehrYeGmD9VZZAPdcNpVwnrBA3GEZA,5079
|
||||||
|
gunicorn/workers/base.py,sha256=nzo4KfCQkO3Y2HKuKVk-xInZUiYay_A5B9e_9NVXU28,9121
|
||||||
|
gunicorn/workers/base_async.py,sha256=54VkS3S_wrFD7v3jInhFfkeBhaPnV5UN-cu-i5MoXkc,5575
|
||||||
|
gunicorn/workers/gaiohttp.py,sha256=3rhXky6APkhI0D9nwXlogLo_Jd9v98CiEuCy9inzCU4,823
|
||||||
|
gunicorn/workers/geventlet.py,sha256=mE-Zw3zh8lOZVaprXcfaoBMmwKeDj6sZzdjmgIsvHXw,4258
|
||||||
|
gunicorn/workers/ggevent.py,sha256=OV5KCJ3qlJP5E46sjyWQKGbQ5xGR2SOrZlEtLhIB89s,7412
|
||||||
|
gunicorn/workers/gthread.py,sha256=HIoWuylHZfH1wlSh4eZ8wxo1kQ5abvdUaFfKfIsgQvI,12009
|
||||||
|
gunicorn/workers/gtornado.py,sha256=LtBWnEX7MNpeGX-YmlBoV1_OOhjkdytFmt1pzOlRPZk,5044
|
||||||
|
gunicorn/workers/sync.py,sha256=_vd1JATNLG4MgJppNJG5KWBIzLGYqRzhEAQVz9H11LI,7153
|
||||||
|
gunicorn/workers/workertmp.py,sha256=6QINPBrriLvezgkC_hclOOeXLi_owMt_SOA5KPEIN-A,1459
|
||||||
|
gunicorn-19.9.0.dist-info/LICENSE.txt,sha256=eJ_hG5Lhyr-890S1_MOSyb1cZ5hgOk6J-SW2M3mE0d8,1136
|
||||||
|
gunicorn-19.9.0.dist-info/METADATA,sha256=SBjzTcJcbKUR9ev_rvypyWJYU0qgHvm8KzgfG6FtniE,3388
|
||||||
|
gunicorn-19.9.0.dist-info/RECORD,,
|
||||||
|
gunicorn-19.9.0.dist-info/WHEEL,sha256=gduuPyBvFJQSQ0zdyxF7k0zynDXbIbvg5ZBHoXum5uk,110
|
||||||
|
gunicorn-19.9.0.dist-info/entry_points.txt,sha256=XeFINKRdSUKwJwaVSolO24PuV_YeO71IMF-rOra5JO8,184
|
||||||
|
gunicorn-19.9.0.dist-info/top_level.txt,sha256=cdMaa2yhxb8do-WioY9qRHUCfwf55YztjwQCncaInoE,9
|
||||||
|
../../../bin/gunicorn,sha256=7yu1I7Rtn3C9Bl_xSluXZLIB0F6-0oADZ_RfNlEwpLg,245
|
||||||
|
../../../bin/gunicorn_paster,sha256=GZ9v1m5WHSh7Yup93ysPfhsFUDkSsv-YN-tXToR1k9k,247
|
||||||
|
gunicorn-19.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
gunicorn/__pycache__/selectors.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/_compat.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/__init__.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/systemd.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/pidfile.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/six.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/util.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/glogging.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/errors.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/debug.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/config.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/sock.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/reloader.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/argparse_compat.cpython-36.pyc,,
|
||||||
|
gunicorn/__pycache__/arbiter.cpython-36.pyc,,
|
||||||
|
gunicorn/app/__pycache__/base.cpython-36.pyc,,
|
||||||
|
gunicorn/app/__pycache__/pasterapp.cpython-36.pyc,,
|
||||||
|
gunicorn/app/__pycache__/__init__.cpython-36.pyc,,
|
||||||
|
gunicorn/app/__pycache__/wsgiapp.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/parser.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/__init__.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/message.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/body.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/_sendfile.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/wsgi.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/errors.cpython-36.pyc,,
|
||||||
|
gunicorn/http/__pycache__/unreader.cpython-36.pyc,,
|
||||||
|
gunicorn/instrument/__pycache__/__init__.cpython-36.pyc,,
|
||||||
|
gunicorn/instrument/__pycache__/statsd.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/base_async.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/base.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/gaiohttp.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/workertmp.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/geventlet.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/gtornado.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/sync.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/__init__.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/_gaiohttp.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/gthread.cpython-36.pyc,,
|
||||||
|
gunicorn/workers/__pycache__/ggevent.cpython-36.pyc,,
|
@ -0,0 +1,6 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: bdist_wheel (0.31.1)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py2-none-any
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
[console_scripts]
|
||||||
|
gunicorn=gunicorn.app.wsgiapp:run
|
||||||
|
gunicorn_paster=gunicorn.app.pasterapp:run
|
||||||
|
|
||||||
|
[paste.server_runner]
|
||||||
|
main=gunicorn.app.pasterapp:paste_server
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
gunicorn
|
8
venv/lib/python3.6/site-packages/gunicorn/__init__.py
Normal file
8
venv/lib/python3.6/site-packages/gunicorn/__init__.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
version_info = (19, 9, 0)
|
||||||
|
__version__ = ".".join([str(v) for v in version_info])
|
||||||
|
SERVER_SOFTWARE = "gunicorn/%s" % __version__
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
298
venv/lib/python3.6/site-packages/gunicorn/_compat.py
Normal file
298
venv/lib/python3.6/site-packages/gunicorn/_compat.py
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from gunicorn import six
|
||||||
|
|
||||||
|
PY26 = (sys.version_info[:2] == (2, 6))
|
||||||
|
PY33 = (sys.version_info >= (3, 3))
|
||||||
|
|
||||||
|
|
||||||
|
def _check_if_pyc(fname):
|
||||||
|
"""Return True if the extension is .pyc, False if .py
|
||||||
|
and None if otherwise"""
|
||||||
|
from imp import find_module
|
||||||
|
from os.path import realpath, dirname, basename, splitext
|
||||||
|
|
||||||
|
# Normalize the file-path for the find_module()
|
||||||
|
filepath = realpath(fname)
|
||||||
|
dirpath = dirname(filepath)
|
||||||
|
module_name = splitext(basename(filepath))[0]
|
||||||
|
|
||||||
|
# Validate and fetch
|
||||||
|
try:
|
||||||
|
fileobj, fullpath, (_, _, pytype) = find_module(module_name, [dirpath])
|
||||||
|
except ImportError:
|
||||||
|
raise IOError("Cannot find config file. "
|
||||||
|
"Path maybe incorrect! : {0}".format(filepath))
|
||||||
|
return pytype, fileobj, fullpath
|
||||||
|
|
||||||
|
|
||||||
|
def _get_codeobj(pyfile):
|
||||||
|
""" Returns the code object, given a python file """
|
||||||
|
from imp import PY_COMPILED, PY_SOURCE
|
||||||
|
|
||||||
|
result, fileobj, fullpath = _check_if_pyc(pyfile)
|
||||||
|
|
||||||
|
# WARNING:
|
||||||
|
# fp.read() can blowup if the module is extremely large file.
|
||||||
|
# Lookout for overflow errors.
|
||||||
|
try:
|
||||||
|
data = fileobj.read()
|
||||||
|
finally:
|
||||||
|
fileobj.close()
|
||||||
|
|
||||||
|
# This is a .pyc file. Treat accordingly.
|
||||||
|
if result is PY_COMPILED:
|
||||||
|
# .pyc format is as follows:
|
||||||
|
# 0 - 4 bytes: Magic number, which changes with each create of .pyc file.
|
||||||
|
# First 2 bytes change with each marshal of .pyc file. Last 2 bytes is "\r\n".
|
||||||
|
# 4 - 8 bytes: Datetime value, when the .py was last changed.
|
||||||
|
# 8 - EOF: Marshalled code object data.
|
||||||
|
# So to get code object, just read the 8th byte onwards till EOF, and
|
||||||
|
# UN-marshal it.
|
||||||
|
import marshal
|
||||||
|
code_obj = marshal.loads(data[8:])
|
||||||
|
|
||||||
|
elif result is PY_SOURCE:
|
||||||
|
# This is a .py file.
|
||||||
|
code_obj = compile(data, fullpath, 'exec')
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Unsupported extension
|
||||||
|
raise Exception("Input file is unknown format: {0}".format(fullpath))
|
||||||
|
|
||||||
|
# Return code object
|
||||||
|
return code_obj
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
def execfile_(fname, *args):
|
||||||
|
if fname.endswith(".pyc"):
|
||||||
|
code = _get_codeobj(fname)
|
||||||
|
else:
|
||||||
|
code = compile(open(fname, 'rb').read(), fname, 'exec')
|
||||||
|
return six.exec_(code, *args)
|
||||||
|
|
||||||
|
def bytes_to_str(b):
|
||||||
|
if isinstance(b, six.text_type):
|
||||||
|
return b
|
||||||
|
return str(b, 'latin1')
|
||||||
|
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
def unquote_to_wsgi_str(string):
|
||||||
|
return _unquote_to_bytes(string).decode('latin-1')
|
||||||
|
|
||||||
|
_unquote_to_bytes = urllib.parse.unquote_to_bytes
|
||||||
|
|
||||||
|
else:
|
||||||
|
def execfile_(fname, *args):
|
||||||
|
""" Overriding PY2 execfile() implementation to support .pyc files """
|
||||||
|
if fname.endswith(".pyc"):
|
||||||
|
return six.exec_(_get_codeobj(fname), *args)
|
||||||
|
return execfile(fname, *args)
|
||||||
|
|
||||||
|
def bytes_to_str(s):
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
return s.encode('utf-8')
|
||||||
|
return s
|
||||||
|
|
||||||
|
import urllib
|
||||||
|
unquote_to_wsgi_str = urllib.unquote
|
||||||
|
|
||||||
|
|
||||||
|
# The following code adapted from trollius.py33_exceptions
|
||||||
|
def _wrap_error(exc, mapping, key):
|
||||||
|
if key not in mapping:
|
||||||
|
return
|
||||||
|
new_err_cls = mapping[key]
|
||||||
|
new_err = new_err_cls(*exc.args)
|
||||||
|
|
||||||
|
# raise a new exception with the original traceback
|
||||||
|
six.reraise(new_err_cls, new_err,
|
||||||
|
exc.__traceback__ if hasattr(exc, '__traceback__') else sys.exc_info()[2])
|
||||||
|
|
||||||
|
if PY33:
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
BlockingIOError = builtins.BlockingIOError
|
||||||
|
BrokenPipeError = builtins.BrokenPipeError
|
||||||
|
ChildProcessError = builtins.ChildProcessError
|
||||||
|
ConnectionRefusedError = builtins.ConnectionRefusedError
|
||||||
|
ConnectionResetError = builtins.ConnectionResetError
|
||||||
|
InterruptedError = builtins.InterruptedError
|
||||||
|
ConnectionAbortedError = builtins.ConnectionAbortedError
|
||||||
|
PermissionError = builtins.PermissionError
|
||||||
|
FileNotFoundError = builtins.FileNotFoundError
|
||||||
|
ProcessLookupError = builtins.ProcessLookupError
|
||||||
|
|
||||||
|
def wrap_error(func, *args, **kw):
|
||||||
|
return func(*args, **kw)
|
||||||
|
else:
|
||||||
|
import errno
|
||||||
|
import select
|
||||||
|
import socket
|
||||||
|
|
||||||
|
class BlockingIOError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BrokenPipeError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ChildProcessError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ConnectionRefusedError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InterruptedError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ConnectionResetError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ConnectionAbortedError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PermissionError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class FileNotFoundError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ProcessLookupError(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
_MAP_ERRNO = {
|
||||||
|
errno.EACCES: PermissionError,
|
||||||
|
errno.EAGAIN: BlockingIOError,
|
||||||
|
errno.EALREADY: BlockingIOError,
|
||||||
|
errno.ECHILD: ChildProcessError,
|
||||||
|
errno.ECONNABORTED: ConnectionAbortedError,
|
||||||
|
errno.ECONNREFUSED: ConnectionRefusedError,
|
||||||
|
errno.ECONNRESET: ConnectionResetError,
|
||||||
|
errno.EINPROGRESS: BlockingIOError,
|
||||||
|
errno.EINTR: InterruptedError,
|
||||||
|
errno.ENOENT: FileNotFoundError,
|
||||||
|
errno.EPERM: PermissionError,
|
||||||
|
errno.EPIPE: BrokenPipeError,
|
||||||
|
errno.ESHUTDOWN: BrokenPipeError,
|
||||||
|
errno.EWOULDBLOCK: BlockingIOError,
|
||||||
|
errno.ESRCH: ProcessLookupError,
|
||||||
|
}
|
||||||
|
|
||||||
|
def wrap_error(func, *args, **kw):
|
||||||
|
"""
|
||||||
|
Wrap socket.error, IOError, OSError, select.error to raise new specialized
|
||||||
|
exceptions of Python 3.3 like InterruptedError (PEP 3151).
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return func(*args, **kw)
|
||||||
|
except (socket.error, IOError, OSError) as exc:
|
||||||
|
if hasattr(exc, 'winerror'):
|
||||||
|
_wrap_error(exc, _MAP_ERRNO, exc.winerror)
|
||||||
|
# _MAP_ERRNO does not contain all Windows errors.
|
||||||
|
# For some errors like "file not found", exc.errno should
|
||||||
|
# be used (ex: ENOENT).
|
||||||
|
_wrap_error(exc, _MAP_ERRNO, exc.errno)
|
||||||
|
raise
|
||||||
|
except select.error as exc:
|
||||||
|
if exc.args:
|
||||||
|
_wrap_error(exc, _MAP_ERRNO, exc.args[0])
|
||||||
|
raise
|
||||||
|
|
||||||
|
if PY26:
|
||||||
|
from urlparse import (
|
||||||
|
_parse_cache, MAX_CACHE_SIZE, clear_cache, _splitnetloc, SplitResult,
|
||||||
|
scheme_chars,
|
||||||
|
)
|
||||||
|
|
||||||
|
def urlsplit(url, scheme='', allow_fragments=True):
|
||||||
|
"""Parse a URL into 5 components:
|
||||||
|
<scheme>://<netloc>/<path>?<query>#<fragment>
|
||||||
|
Return a 5-tuple: (scheme, netloc, path, query, fragment).
|
||||||
|
Note that we don't break the components up in smaller bits
|
||||||
|
(e.g. netloc is a single string) and we don't expand % escapes."""
|
||||||
|
allow_fragments = bool(allow_fragments)
|
||||||
|
key = url, scheme, allow_fragments, type(url), type(scheme)
|
||||||
|
cached = _parse_cache.get(key, None)
|
||||||
|
if cached:
|
||||||
|
return cached
|
||||||
|
if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth
|
||||||
|
clear_cache()
|
||||||
|
netloc = query = fragment = ''
|
||||||
|
i = url.find(':')
|
||||||
|
if i > 0:
|
||||||
|
if url[:i] == 'http': # optimize the common case
|
||||||
|
scheme = url[:i].lower()
|
||||||
|
url = url[i+1:]
|
||||||
|
if url[:2] == '//':
|
||||||
|
netloc, url = _splitnetloc(url, 2)
|
||||||
|
if (('[' in netloc and ']' not in netloc) or
|
||||||
|
(']' in netloc and '[' not in netloc)):
|
||||||
|
raise ValueError("Invalid IPv6 URL")
|
||||||
|
if allow_fragments and '#' in url:
|
||||||
|
url, fragment = url.split('#', 1)
|
||||||
|
if '?' in url:
|
||||||
|
url, query = url.split('?', 1)
|
||||||
|
v = SplitResult(scheme, netloc, url, query, fragment)
|
||||||
|
_parse_cache[key] = v
|
||||||
|
return v
|
||||||
|
for c in url[:i]:
|
||||||
|
if c not in scheme_chars:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# make sure "url" is not actually a port number (in which case
|
||||||
|
# "scheme" is really part of the path)
|
||||||
|
rest = url[i+1:]
|
||||||
|
if not rest or any(c not in '0123456789' for c in rest):
|
||||||
|
# not a port number
|
||||||
|
scheme, url = url[:i].lower(), rest
|
||||||
|
|
||||||
|
if url[:2] == '//':
|
||||||
|
netloc, url = _splitnetloc(url, 2)
|
||||||
|
if (('[' in netloc and ']' not in netloc) or
|
||||||
|
(']' in netloc and '[' not in netloc)):
|
||||||
|
raise ValueError("Invalid IPv6 URL")
|
||||||
|
if allow_fragments and '#' in url:
|
||||||
|
url, fragment = url.split('#', 1)
|
||||||
|
if '?' in url:
|
||||||
|
url, query = url.split('?', 1)
|
||||||
|
v = SplitResult(scheme, netloc, url, query, fragment)
|
||||||
|
_parse_cache[key] = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
else:
|
||||||
|
from gunicorn.six.moves.urllib.parse import urlsplit
|
||||||
|
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
if hasattr(inspect, 'signature'):
|
||||||
|
positionals = (
|
||||||
|
inspect.Parameter.POSITIONAL_ONLY,
|
||||||
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_arity(f):
|
||||||
|
sig = inspect.signature(f)
|
||||||
|
arity = 0
|
||||||
|
|
||||||
|
for param in sig.parameters.values():
|
||||||
|
if param.kind in positionals:
|
||||||
|
arity += 1
|
||||||
|
|
||||||
|
return arity
|
||||||
|
else:
|
||||||
|
def get_arity(f):
|
||||||
|
return len(inspect.getargspec(f)[0])
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import html
|
||||||
|
|
||||||
|
def html_escape(s):
|
||||||
|
return html.escape(s)
|
||||||
|
except ImportError:
|
||||||
|
import cgi
|
||||||
|
|
||||||
|
def html_escape(s):
|
||||||
|
return cgi.escape(s, quote=True)
|
@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
223
venv/lib/python3.6/site-packages/gunicorn/app/base.py
Normal file
223
venv/lib/python3.6/site-packages/gunicorn/app/base.py
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from gunicorn._compat import execfile_
|
||||||
|
from gunicorn import util
|
||||||
|
from gunicorn.arbiter import Arbiter
|
||||||
|
from gunicorn.config import Config, get_default_config_file
|
||||||
|
from gunicorn import debug
|
||||||
|
|
||||||
|
class BaseApplication(object):
|
||||||
|
"""
|
||||||
|
An application interface for configuring and loading
|
||||||
|
the various necessities for any given web framework.
|
||||||
|
"""
|
||||||
|
def __init__(self, usage=None, prog=None):
|
||||||
|
self.usage = usage
|
||||||
|
self.cfg = None
|
||||||
|
self.callable = None
|
||||||
|
self.prog = prog
|
||||||
|
self.logger = None
|
||||||
|
self.do_load_config()
|
||||||
|
|
||||||
|
def do_load_config(self):
|
||||||
|
"""
|
||||||
|
Loads the configuration
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.load_default_config()
|
||||||
|
self.load_config()
|
||||||
|
except Exception as e:
|
||||||
|
print("\nError: %s" % str(e), file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def load_default_config(self):
|
||||||
|
# init configuration
|
||||||
|
self.cfg = Config(self.usage, prog=self.prog)
|
||||||
|
|
||||||
|
def init(self, parser, opts, args):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def load_config(self):
|
||||||
|
"""
|
||||||
|
This method is used to load the configuration from one or several input(s).
|
||||||
|
Custom Command line, configuration file.
|
||||||
|
You have to override this method in your class.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
self.do_load_config()
|
||||||
|
if self.cfg.spew:
|
||||||
|
debug.spew()
|
||||||
|
|
||||||
|
def wsgi(self):
|
||||||
|
if self.callable is None:
|
||||||
|
self.callable = self.load()
|
||||||
|
return self.callable
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
Arbiter(self).run()
|
||||||
|
except RuntimeError as e:
|
||||||
|
print("\nError: %s\n" % e, file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
class Application(BaseApplication):
|
||||||
|
|
||||||
|
# 'init' and 'load' methods are implemented by WSGIApplication.
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
|
||||||
|
def chdir(self):
|
||||||
|
# chdir to the configured path before loading,
|
||||||
|
# default is the current dir
|
||||||
|
os.chdir(self.cfg.chdir)
|
||||||
|
|
||||||
|
# add the path to sys.path
|
||||||
|
if self.cfg.chdir not in sys.path:
|
||||||
|
sys.path.insert(0, self.cfg.chdir)
|
||||||
|
|
||||||
|
def get_config_from_filename(self, filename):
|
||||||
|
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
raise RuntimeError("%r doesn't exist" % filename)
|
||||||
|
|
||||||
|
cfg = {
|
||||||
|
"__builtins__": __builtins__,
|
||||||
|
"__name__": "__config__",
|
||||||
|
"__file__": filename,
|
||||||
|
"__doc__": None,
|
||||||
|
"__package__": None
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
execfile_(filename, cfg, cfg)
|
||||||
|
except Exception:
|
||||||
|
print("Failed to read config file: %s" % filename, file=sys.stderr)
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def get_config_from_module_name(self, module_name):
|
||||||
|
return vars(util.import_module(module_name))
|
||||||
|
|
||||||
|
def load_config_from_module_name_or_filename(self, location):
|
||||||
|
"""
|
||||||
|
Loads the configuration file: the file is a python file, otherwise raise an RuntimeError
|
||||||
|
Exception or stop the process if the configuration file contains a syntax error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if location.startswith("python:"):
|
||||||
|
module_name = location[len("python:"):]
|
||||||
|
cfg = self.get_config_from_module_name(module_name)
|
||||||
|
else:
|
||||||
|
if location.startswith("file:"):
|
||||||
|
filename = location[len("file:"):]
|
||||||
|
else:
|
||||||
|
filename = location
|
||||||
|
cfg = self.get_config_from_filename(filename)
|
||||||
|
|
||||||
|
for k, v in cfg.items():
|
||||||
|
# Ignore unknown names
|
||||||
|
if k not in self.cfg.settings:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
self.cfg.set(k.lower(), v)
|
||||||
|
except:
|
||||||
|
print("Invalid value for %s: %s\n" % (k, v), file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
raise
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def load_config_from_file(self, filename):
|
||||||
|
return self.load_config_from_module_name_or_filename(location=filename)
|
||||||
|
|
||||||
|
def load_config(self):
|
||||||
|
# parse console args
|
||||||
|
parser = self.cfg.parser()
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# optional settings from apps
|
||||||
|
cfg = self.init(parser, args, args.args)
|
||||||
|
|
||||||
|
# set up import paths and follow symlinks
|
||||||
|
self.chdir()
|
||||||
|
|
||||||
|
# Load up the any app specific configuration
|
||||||
|
if cfg:
|
||||||
|
for k, v in cfg.items():
|
||||||
|
self.cfg.set(k.lower(), v)
|
||||||
|
|
||||||
|
env_args = parser.parse_args(self.cfg.get_cmd_args_from_env())
|
||||||
|
|
||||||
|
if args.config:
|
||||||
|
self.load_config_from_file(args.config)
|
||||||
|
elif env_args.config:
|
||||||
|
self.load_config_from_file(env_args.config)
|
||||||
|
else:
|
||||||
|
default_config = get_default_config_file()
|
||||||
|
if default_config is not None:
|
||||||
|
self.load_config_from_file(default_config)
|
||||||
|
|
||||||
|
# Load up environment configuration
|
||||||
|
for k, v in vars(env_args).items():
|
||||||
|
if v is None:
|
||||||
|
continue
|
||||||
|
if k == "args":
|
||||||
|
continue
|
||||||
|
self.cfg.set(k.lower(), v)
|
||||||
|
|
||||||
|
# Lastly, update the configuration with any command line settings.
|
||||||
|
for k, v in vars(args).items():
|
||||||
|
if v is None:
|
||||||
|
continue
|
||||||
|
if k == "args":
|
||||||
|
continue
|
||||||
|
self.cfg.set(k.lower(), v)
|
||||||
|
|
||||||
|
# current directory might be changed by the config now
|
||||||
|
# set up import paths and follow symlinks
|
||||||
|
self.chdir()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.cfg.check_config:
|
||||||
|
try:
|
||||||
|
self.load()
|
||||||
|
except:
|
||||||
|
msg = "\nError while loading the application:\n"
|
||||||
|
print(msg, file=sys.stderr)
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.exit(1)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if self.cfg.spew:
|
||||||
|
debug.spew()
|
||||||
|
|
||||||
|
if self.cfg.daemon:
|
||||||
|
util.daemonize(self.cfg.enable_stdio_inheritance)
|
||||||
|
|
||||||
|
# set python paths
|
||||||
|
if self.cfg.pythonpath:
|
||||||
|
paths = self.cfg.pythonpath.split(",")
|
||||||
|
for path in paths:
|
||||||
|
pythonpath = os.path.abspath(path)
|
||||||
|
if pythonpath not in sys.path:
|
||||||
|
sys.path.insert(0, pythonpath)
|
||||||
|
|
||||||
|
super(Application, self).run()
|
209
venv/lib/python3.6/site-packages/gunicorn/app/pasterapp.py
Normal file
209
venv/lib/python3.6/site-packages/gunicorn/app/pasterapp.py
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
# pylint: skip-file
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pkg_resources
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import configparser as ConfigParser
|
||||||
|
except ImportError:
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
from paste.deploy import loadapp, loadwsgi
|
||||||
|
SERVER = loadwsgi.SERVER
|
||||||
|
|
||||||
|
from gunicorn.app.base import Application
|
||||||
|
from gunicorn.config import Config, get_default_config_file
|
||||||
|
from gunicorn import util
|
||||||
|
|
||||||
|
|
||||||
|
def _has_logging_config(paste_file):
|
||||||
|
cfg_parser = ConfigParser.ConfigParser()
|
||||||
|
cfg_parser.read([paste_file])
|
||||||
|
return cfg_parser.has_section('loggers')
|
||||||
|
|
||||||
|
|
||||||
|
def paste_config(gconfig, config_url, relative_to, global_conf=None):
|
||||||
|
# add entry to pkg_resources
|
||||||
|
sys.path.insert(0, relative_to)
|
||||||
|
pkg_resources.working_set.add_entry(relative_to)
|
||||||
|
|
||||||
|
config_url = config_url.split('#')[0]
|
||||||
|
cx = loadwsgi.loadcontext(SERVER, config_url, relative_to=relative_to,
|
||||||
|
global_conf=global_conf)
|
||||||
|
gc, lc = cx.global_conf.copy(), cx.local_conf.copy()
|
||||||
|
cfg = {}
|
||||||
|
|
||||||
|
host, port = lc.pop('host', ''), lc.pop('port', '')
|
||||||
|
if host and port:
|
||||||
|
cfg['bind'] = '%s:%s' % (host, port)
|
||||||
|
elif host:
|
||||||
|
cfg['bind'] = host.split(',')
|
||||||
|
|
||||||
|
cfg['default_proc_name'] = gc.get('__file__')
|
||||||
|
|
||||||
|
# init logging configuration
|
||||||
|
config_file = config_url.split(':')[1]
|
||||||
|
if _has_logging_config(config_file):
|
||||||
|
cfg.setdefault('logconfig', config_file)
|
||||||
|
|
||||||
|
for k, v in gc.items():
|
||||||
|
if k not in gconfig.settings:
|
||||||
|
continue
|
||||||
|
cfg[k] = v
|
||||||
|
|
||||||
|
for k, v in lc.items():
|
||||||
|
if k not in gconfig.settings:
|
||||||
|
continue
|
||||||
|
cfg[k] = v
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
|
def load_pasteapp(config_url, relative_to, global_conf=None):
|
||||||
|
return loadapp(config_url, relative_to=relative_to,
|
||||||
|
global_conf=global_conf)
|
||||||
|
|
||||||
|
class PasterBaseApplication(Application):
|
||||||
|
gcfg = None
|
||||||
|
|
||||||
|
def app_config(self):
|
||||||
|
return paste_config(self.cfg, self.cfgurl, self.relpath,
|
||||||
|
global_conf=self.gcfg)
|
||||||
|
|
||||||
|
def load_config(self):
|
||||||
|
super(PasterBaseApplication, self).load_config()
|
||||||
|
|
||||||
|
# reload logging conf
|
||||||
|
if hasattr(self, "cfgfname"):
|
||||||
|
parser = ConfigParser.ConfigParser()
|
||||||
|
parser.read([self.cfgfname])
|
||||||
|
if parser.has_section('loggers'):
|
||||||
|
from logging.config import fileConfig
|
||||||
|
config_file = os.path.abspath(self.cfgfname)
|
||||||
|
fileConfig(config_file, dict(__file__=config_file,
|
||||||
|
here=os.path.dirname(config_file)))
|
||||||
|
|
||||||
|
|
||||||
|
class PasterApplication(PasterBaseApplication):
|
||||||
|
|
||||||
|
def init(self, parser, opts, args):
|
||||||
|
if len(args) != 1:
|
||||||
|
parser.error("No application name specified.")
|
||||||
|
|
||||||
|
cwd = util.getcwd()
|
||||||
|
cfgfname = os.path.normpath(os.path.join(cwd, args[0]))
|
||||||
|
cfgfname = os.path.abspath(cfgfname)
|
||||||
|
if not os.path.exists(cfgfname):
|
||||||
|
parser.error("Config file not found: %s" % cfgfname)
|
||||||
|
|
||||||
|
self.cfgurl = 'config:%s' % cfgfname
|
||||||
|
self.relpath = os.path.dirname(cfgfname)
|
||||||
|
self.cfgfname = cfgfname
|
||||||
|
|
||||||
|
sys.path.insert(0, self.relpath)
|
||||||
|
pkg_resources.working_set.add_entry(self.relpath)
|
||||||
|
|
||||||
|
return self.app_config()
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
# chdir to the configured path before loading,
|
||||||
|
# default is the current dir
|
||||||
|
os.chdir(self.cfg.chdir)
|
||||||
|
|
||||||
|
return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.gcfg)
|
||||||
|
|
||||||
|
|
||||||
|
class PasterServerApplication(PasterBaseApplication):
|
||||||
|
|
||||||
|
def __init__(self, app, gcfg=None, host="127.0.0.1", port=None, **kwargs):
|
||||||
|
# pylint: disable=super-init-not-called
|
||||||
|
self.cfg = Config()
|
||||||
|
self.gcfg = gcfg # need to hold this for app_config
|
||||||
|
self.app = app
|
||||||
|
self.callable = None
|
||||||
|
|
||||||
|
gcfg = gcfg or {}
|
||||||
|
cfgfname = gcfg.get("__file__")
|
||||||
|
if cfgfname is not None:
|
||||||
|
self.cfgurl = 'config:%s' % cfgfname
|
||||||
|
self.relpath = os.path.dirname(cfgfname)
|
||||||
|
self.cfgfname = cfgfname
|
||||||
|
|
||||||
|
cfg = kwargs.copy()
|
||||||
|
|
||||||
|
if port and not host.startswith("unix:"):
|
||||||
|
bind = "%s:%s" % (host, port)
|
||||||
|
else:
|
||||||
|
bind = host
|
||||||
|
cfg["bind"] = bind.split(',')
|
||||||
|
|
||||||
|
if gcfg:
|
||||||
|
for k, v in gcfg.items():
|
||||||
|
cfg[k] = v
|
||||||
|
cfg["default_proc_name"] = cfg['__file__']
|
||||||
|
|
||||||
|
try:
|
||||||
|
for k, v in cfg.items():
|
||||||
|
if k.lower() in self.cfg.settings and v is not None:
|
||||||
|
self.cfg.set(k.lower(), v)
|
||||||
|
except Exception as e:
|
||||||
|
print("\nConfig error: %s" % str(e), file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if cfg.get("config"):
|
||||||
|
self.load_config_from_file(cfg["config"])
|
||||||
|
else:
|
||||||
|
default_config = get_default_config_file()
|
||||||
|
if default_config is not None:
|
||||||
|
self.load_config_from_file(default_config)
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
return self.app
|
||||||
|
|
||||||
|
|
||||||
|
def run():
|
||||||
|
"""\
|
||||||
|
The ``gunicorn_paster`` command for launching Paster compatible
|
||||||
|
applications like Pylons or Turbogears2
|
||||||
|
"""
|
||||||
|
util.warn("""This command is deprecated.
|
||||||
|
|
||||||
|
You should now use the `--paste` option. Ex.:
|
||||||
|
|
||||||
|
gunicorn --paste development.ini
|
||||||
|
""")
|
||||||
|
|
||||||
|
from gunicorn.app.pasterapp import PasterApplication
|
||||||
|
PasterApplication("%(prog)s [OPTIONS] pasteconfig.ini").run()
|
||||||
|
|
||||||
|
|
||||||
|
def paste_server(app, gcfg=None, host="127.0.0.1", port=None, **kwargs):
|
||||||
|
"""\
|
||||||
|
A paster server.
|
||||||
|
|
||||||
|
Then entry point in your paster ini file should looks like this:
|
||||||
|
|
||||||
|
[server:main]
|
||||||
|
use = egg:gunicorn#main
|
||||||
|
host = 127.0.0.1
|
||||||
|
port = 5000
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
util.warn("""This command is deprecated.
|
||||||
|
|
||||||
|
You should now use the `--paste` option. Ex.:
|
||||||
|
|
||||||
|
gunicorn --paste development.ini
|
||||||
|
""")
|
||||||
|
|
||||||
|
from gunicorn.app.pasterapp import PasterServerApplication
|
||||||
|
PasterServerApplication(app, gcfg=gcfg, host=host, port=port, **kwargs).run()
|
65
venv/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py
Normal file
65
venv/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from gunicorn.errors import ConfigError
|
||||||
|
from gunicorn.app.base import Application
|
||||||
|
from gunicorn import util
|
||||||
|
|
||||||
|
|
||||||
|
class WSGIApplication(Application):
|
||||||
|
def init(self, parser, opts, args):
|
||||||
|
if opts.paste:
|
||||||
|
app_name = 'main'
|
||||||
|
path = opts.paste
|
||||||
|
if '#' in path:
|
||||||
|
path, app_name = path.split('#')
|
||||||
|
path = os.path.abspath(os.path.normpath(
|
||||||
|
os.path.join(util.getcwd(), path)))
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
raise ConfigError("%r not found" % path)
|
||||||
|
|
||||||
|
# paste application, load the config
|
||||||
|
self.cfgurl = 'config:%s#%s' % (path, app_name)
|
||||||
|
self.relpath = os.path.dirname(path)
|
||||||
|
|
||||||
|
from .pasterapp import paste_config
|
||||||
|
return paste_config(self.cfg, self.cfgurl, self.relpath)
|
||||||
|
|
||||||
|
if len(args) < 1:
|
||||||
|
parser.error("No application module specified.")
|
||||||
|
|
||||||
|
self.cfg.set("default_proc_name", args[0])
|
||||||
|
self.app_uri = args[0]
|
||||||
|
|
||||||
|
def load_wsgiapp(self):
|
||||||
|
# load the app
|
||||||
|
return util.import_app(self.app_uri)
|
||||||
|
|
||||||
|
def load_pasteapp(self):
|
||||||
|
# load the paste app
|
||||||
|
from .pasterapp import load_pasteapp
|
||||||
|
return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.cfg.paste_global_conf)
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
if self.cfg.paste is not None:
|
||||||
|
return self.load_pasteapp()
|
||||||
|
else:
|
||||||
|
return self.load_wsgiapp()
|
||||||
|
|
||||||
|
|
||||||
|
def run():
|
||||||
|
"""\
|
||||||
|
The ``gunicorn`` command line runner for launching Gunicorn with
|
||||||
|
generic WSGI applications.
|
||||||
|
"""
|
||||||
|
from gunicorn.app.wsgiapp import WSGIApplication
|
||||||
|
WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run()
|
646
venv/lib/python3.6/site-packages/gunicorn/arbiter.py
Normal file
646
venv/lib/python3.6/site-packages/gunicorn/arbiter.py
Normal file
@ -0,0 +1,646 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import select
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from gunicorn.errors import HaltServer, AppImportError
|
||||||
|
from gunicorn.pidfile import Pidfile
|
||||||
|
from gunicorn import sock, systemd, util
|
||||||
|
|
||||||
|
from gunicorn import __version__, SERVER_SOFTWARE
|
||||||
|
|
||||||
|
|
||||||
|
class Arbiter(object):
|
||||||
|
"""
|
||||||
|
Arbiter maintain the workers processes alive. It launches or
|
||||||
|
kills them if needed. It also manages application reloading
|
||||||
|
via SIGHUP/USR2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# A flag indicating if a worker failed to
|
||||||
|
# to boot. If a worker process exist with
|
||||||
|
# this error code, the arbiter will terminate.
|
||||||
|
WORKER_BOOT_ERROR = 3
|
||||||
|
|
||||||
|
# A flag indicating if an application failed to be loaded
|
||||||
|
APP_LOAD_ERROR = 4
|
||||||
|
|
||||||
|
START_CTX = {}
|
||||||
|
|
||||||
|
LISTENERS = []
|
||||||
|
WORKERS = {}
|
||||||
|
PIPE = []
|
||||||
|
|
||||||
|
# I love dynamic languages
|
||||||
|
SIG_QUEUE = []
|
||||||
|
SIGNALS = [getattr(signal, "SIG%s" % x)
|
||||||
|
for x in "HUP QUIT INT TERM TTIN TTOU USR1 USR2 WINCH".split()]
|
||||||
|
SIG_NAMES = dict(
|
||||||
|
(getattr(signal, name), name[3:].lower()) for name in dir(signal)
|
||||||
|
if name[:3] == "SIG" and name[3] != "_"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
os.environ["SERVER_SOFTWARE"] = SERVER_SOFTWARE
|
||||||
|
|
||||||
|
self._num_workers = None
|
||||||
|
self._last_logged_active_worker_count = None
|
||||||
|
self.log = None
|
||||||
|
|
||||||
|
self.setup(app)
|
||||||
|
|
||||||
|
self.pidfile = None
|
||||||
|
self.systemd = False
|
||||||
|
self.worker_age = 0
|
||||||
|
self.reexec_pid = 0
|
||||||
|
self.master_pid = 0
|
||||||
|
self.master_name = "Master"
|
||||||
|
|
||||||
|
cwd = util.getcwd()
|
||||||
|
|
||||||
|
args = sys.argv[:]
|
||||||
|
args.insert(0, sys.executable)
|
||||||
|
|
||||||
|
# init start context
|
||||||
|
self.START_CTX = {
|
||||||
|
"args": args,
|
||||||
|
"cwd": cwd,
|
||||||
|
0: sys.executable
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_num_workers(self):
|
||||||
|
return self._num_workers
|
||||||
|
|
||||||
|
def _set_num_workers(self, value):
|
||||||
|
old_value = self._num_workers
|
||||||
|
self._num_workers = value
|
||||||
|
self.cfg.nworkers_changed(self, value, old_value)
|
||||||
|
num_workers = property(_get_num_workers, _set_num_workers)
|
||||||
|
|
||||||
|
def setup(self, app):
|
||||||
|
self.app = app
|
||||||
|
self.cfg = app.cfg
|
||||||
|
|
||||||
|
if self.log is None:
|
||||||
|
self.log = self.cfg.logger_class(app.cfg)
|
||||||
|
|
||||||
|
# reopen files
|
||||||
|
if 'GUNICORN_FD' in os.environ:
|
||||||
|
self.log.reopen_files()
|
||||||
|
|
||||||
|
self.worker_class = self.cfg.worker_class
|
||||||
|
self.address = self.cfg.address
|
||||||
|
self.num_workers = self.cfg.workers
|
||||||
|
self.timeout = self.cfg.timeout
|
||||||
|
self.proc_name = self.cfg.proc_name
|
||||||
|
|
||||||
|
self.log.debug('Current configuration:\n{0}'.format(
|
||||||
|
'\n'.join(
|
||||||
|
' {0}: {1}'.format(config, value.value)
|
||||||
|
for config, value
|
||||||
|
in sorted(self.cfg.settings.items(),
|
||||||
|
key=lambda setting: setting[1]))))
|
||||||
|
|
||||||
|
# set enviroment' variables
|
||||||
|
if self.cfg.env:
|
||||||
|
for k, v in self.cfg.env.items():
|
||||||
|
os.environ[k] = v
|
||||||
|
|
||||||
|
if self.cfg.preload_app:
|
||||||
|
self.app.wsgi()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""\
|
||||||
|
Initialize the arbiter. Start listening and set pidfile if needed.
|
||||||
|
"""
|
||||||
|
self.log.info("Starting gunicorn %s", __version__)
|
||||||
|
|
||||||
|
if 'GUNICORN_PID' in os.environ:
|
||||||
|
self.master_pid = int(os.environ.get('GUNICORN_PID'))
|
||||||
|
self.proc_name = self.proc_name + ".2"
|
||||||
|
self.master_name = "Master.2"
|
||||||
|
|
||||||
|
self.pid = os.getpid()
|
||||||
|
if self.cfg.pidfile is not None:
|
||||||
|
pidname = self.cfg.pidfile
|
||||||
|
if self.master_pid != 0:
|
||||||
|
pidname += ".2"
|
||||||
|
self.pidfile = Pidfile(pidname)
|
||||||
|
self.pidfile.create(self.pid)
|
||||||
|
self.cfg.on_starting(self)
|
||||||
|
|
||||||
|
self.init_signals()
|
||||||
|
|
||||||
|
if not self.LISTENERS:
|
||||||
|
fds = None
|
||||||
|
listen_fds = systemd.listen_fds()
|
||||||
|
if listen_fds:
|
||||||
|
self.systemd = True
|
||||||
|
fds = range(systemd.SD_LISTEN_FDS_START,
|
||||||
|
systemd.SD_LISTEN_FDS_START + listen_fds)
|
||||||
|
|
||||||
|
elif self.master_pid:
|
||||||
|
fds = []
|
||||||
|
for fd in os.environ.pop('GUNICORN_FD').split(','):
|
||||||
|
fds.append(int(fd))
|
||||||
|
|
||||||
|
self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds)
|
||||||
|
|
||||||
|
listeners_str = ",".join([str(l) for l in self.LISTENERS])
|
||||||
|
self.log.debug("Arbiter booted")
|
||||||
|
self.log.info("Listening at: %s (%s)", listeners_str, self.pid)
|
||||||
|
self.log.info("Using worker: %s", self.cfg.worker_class_str)
|
||||||
|
|
||||||
|
# check worker class requirements
|
||||||
|
if hasattr(self.worker_class, "check_config"):
|
||||||
|
self.worker_class.check_config(self.cfg, self.log)
|
||||||
|
|
||||||
|
self.cfg.when_ready(self)
|
||||||
|
|
||||||
|
def init_signals(self):
|
||||||
|
"""\
|
||||||
|
Initialize master signal handling. Most of the signals
|
||||||
|
are queued. Child signals only wake up the master.
|
||||||
|
"""
|
||||||
|
# close old PIPE
|
||||||
|
for p in self.PIPE:
|
||||||
|
os.close(p)
|
||||||
|
|
||||||
|
# initialize the pipe
|
||||||
|
self.PIPE = pair = os.pipe()
|
||||||
|
for p in pair:
|
||||||
|
util.set_non_blocking(p)
|
||||||
|
util.close_on_exec(p)
|
||||||
|
|
||||||
|
self.log.close_on_exec()
|
||||||
|
|
||||||
|
# initialize all signals
|
||||||
|
for s in self.SIGNALS:
|
||||||
|
signal.signal(s, self.signal)
|
||||||
|
signal.signal(signal.SIGCHLD, self.handle_chld)
|
||||||
|
|
||||||
|
def signal(self, sig, frame):
|
||||||
|
if len(self.SIG_QUEUE) < 5:
|
||||||
|
self.SIG_QUEUE.append(sig)
|
||||||
|
self.wakeup()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"Main master loop."
|
||||||
|
self.start()
|
||||||
|
util._setproctitle("master [%s]" % self.proc_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.manage_workers()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
self.maybe_promote_master()
|
||||||
|
|
||||||
|
sig = self.SIG_QUEUE.pop(0) if self.SIG_QUEUE else None
|
||||||
|
if sig is None:
|
||||||
|
self.sleep()
|
||||||
|
self.murder_workers()
|
||||||
|
self.manage_workers()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if sig not in self.SIG_NAMES:
|
||||||
|
self.log.info("Ignoring unknown signal: %s", sig)
|
||||||
|
continue
|
||||||
|
|
||||||
|
signame = self.SIG_NAMES.get(sig)
|
||||||
|
handler = getattr(self, "handle_%s" % signame, None)
|
||||||
|
if not handler:
|
||||||
|
self.log.error("Unhandled signal: %s", signame)
|
||||||
|
continue
|
||||||
|
self.log.info("Handling signal: %s", signame)
|
||||||
|
handler()
|
||||||
|
self.wakeup()
|
||||||
|
except StopIteration:
|
||||||
|
self.halt()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
self.halt()
|
||||||
|
except HaltServer as inst:
|
||||||
|
self.halt(reason=inst.reason, exit_status=inst.exit_status)
|
||||||
|
except SystemExit:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
self.log.info("Unhandled exception in main loop",
|
||||||
|
exc_info=True)
|
||||||
|
self.stop(False)
|
||||||
|
if self.pidfile is not None:
|
||||||
|
self.pidfile.unlink()
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
def handle_chld(self, sig, frame):
|
||||||
|
"SIGCHLD handling"
|
||||||
|
self.reap_workers()
|
||||||
|
self.wakeup()
|
||||||
|
|
||||||
|
def handle_hup(self):
|
||||||
|
"""\
|
||||||
|
HUP handling.
|
||||||
|
- Reload configuration
|
||||||
|
- Start the new worker processes with a new configuration
|
||||||
|
- Gracefully shutdown the old worker processes
|
||||||
|
"""
|
||||||
|
self.log.info("Hang up: %s", self.master_name)
|
||||||
|
self.reload()
|
||||||
|
|
||||||
|
def handle_term(self):
|
||||||
|
"SIGTERM handling"
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def handle_int(self):
|
||||||
|
"SIGINT handling"
|
||||||
|
self.stop(False)
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def handle_quit(self):
|
||||||
|
"SIGQUIT handling"
|
||||||
|
self.stop(False)
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def handle_ttin(self):
|
||||||
|
"""\
|
||||||
|
SIGTTIN handling.
|
||||||
|
Increases the number of workers by one.
|
||||||
|
"""
|
||||||
|
self.num_workers += 1
|
||||||
|
self.manage_workers()
|
||||||
|
|
||||||
|
def handle_ttou(self):
|
||||||
|
"""\
|
||||||
|
SIGTTOU handling.
|
||||||
|
Decreases the number of workers by one.
|
||||||
|
"""
|
||||||
|
if self.num_workers <= 1:
|
||||||
|
return
|
||||||
|
self.num_workers -= 1
|
||||||
|
self.manage_workers()
|
||||||
|
|
||||||
|
def handle_usr1(self):
|
||||||
|
"""\
|
||||||
|
SIGUSR1 handling.
|
||||||
|
Kill all workers by sending them a SIGUSR1
|
||||||
|
"""
|
||||||
|
self.log.reopen_files()
|
||||||
|
self.kill_workers(signal.SIGUSR1)
|
||||||
|
|
||||||
|
def handle_usr2(self):
|
||||||
|
"""\
|
||||||
|
SIGUSR2 handling.
|
||||||
|
Creates a new master/worker set as a slave of the current
|
||||||
|
master without affecting old workers. Use this to do live
|
||||||
|
deployment with the ability to backout a change.
|
||||||
|
"""
|
||||||
|
self.reexec()
|
||||||
|
|
||||||
|
def handle_winch(self):
|
||||||
|
"""SIGWINCH handling"""
|
||||||
|
if self.cfg.daemon:
|
||||||
|
self.log.info("graceful stop of workers")
|
||||||
|
self.num_workers = 0
|
||||||
|
self.kill_workers(signal.SIGTERM)
|
||||||
|
else:
|
||||||
|
self.log.debug("SIGWINCH ignored. Not daemonized")
|
||||||
|
|
||||||
|
def maybe_promote_master(self):
|
||||||
|
if self.master_pid == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.master_pid != os.getppid():
|
||||||
|
self.log.info("Master has been promoted.")
|
||||||
|
# reset master infos
|
||||||
|
self.master_name = "Master"
|
||||||
|
self.master_pid = 0
|
||||||
|
self.proc_name = self.cfg.proc_name
|
||||||
|
del os.environ['GUNICORN_PID']
|
||||||
|
# rename the pidfile
|
||||||
|
if self.pidfile is not None:
|
||||||
|
self.pidfile.rename(self.cfg.pidfile)
|
||||||
|
# reset proctitle
|
||||||
|
util._setproctitle("master [%s]" % self.proc_name)
|
||||||
|
|
||||||
|
def wakeup(self):
|
||||||
|
"""\
|
||||||
|
Wake up the arbiter by writing to the PIPE
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
os.write(self.PIPE[1], b'.')
|
||||||
|
except IOError as e:
|
||||||
|
if e.errno not in [errno.EAGAIN, errno.EINTR]:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def halt(self, reason=None, exit_status=0):
|
||||||
|
""" halt arbiter """
|
||||||
|
self.stop()
|
||||||
|
self.log.info("Shutting down: %s", self.master_name)
|
||||||
|
if reason is not None:
|
||||||
|
self.log.info("Reason: %s", reason)
|
||||||
|
if self.pidfile is not None:
|
||||||
|
self.pidfile.unlink()
|
||||||
|
self.cfg.on_exit(self)
|
||||||
|
sys.exit(exit_status)
|
||||||
|
|
||||||
|
def sleep(self):
|
||||||
|
"""\
|
||||||
|
Sleep until PIPE is readable or we timeout.
|
||||||
|
A readable PIPE means a signal occurred.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
ready = select.select([self.PIPE[0]], [], [], 1.0)
|
||||||
|
if not ready[0]:
|
||||||
|
return
|
||||||
|
while os.read(self.PIPE[0], 1):
|
||||||
|
pass
|
||||||
|
except (select.error, OSError) as e:
|
||||||
|
# TODO: select.error is a subclass of OSError since Python 3.3.
|
||||||
|
error_number = getattr(e, 'errno', e.args[0])
|
||||||
|
if error_number not in [errno.EAGAIN, errno.EINTR]:
|
||||||
|
raise
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def stop(self, graceful=True):
|
||||||
|
"""\
|
||||||
|
Stop workers
|
||||||
|
|
||||||
|
:attr graceful: boolean, If True (the default) workers will be
|
||||||
|
killed gracefully (ie. trying to wait for the current connection)
|
||||||
|
"""
|
||||||
|
|
||||||
|
unlink = self.reexec_pid == self.master_pid == 0 and not self.systemd
|
||||||
|
sock.close_sockets(self.LISTENERS, unlink)
|
||||||
|
|
||||||
|
self.LISTENERS = []
|
||||||
|
sig = signal.SIGTERM
|
||||||
|
if not graceful:
|
||||||
|
sig = signal.SIGQUIT
|
||||||
|
limit = time.time() + self.cfg.graceful_timeout
|
||||||
|
# instruct the workers to exit
|
||||||
|
self.kill_workers(sig)
|
||||||
|
# wait until the graceful timeout
|
||||||
|
while self.WORKERS and time.time() < limit:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
self.kill_workers(signal.SIGKILL)
|
||||||
|
|
||||||
|
def reexec(self):
|
||||||
|
"""\
|
||||||
|
Relaunch the master and workers.
|
||||||
|
"""
|
||||||
|
if self.reexec_pid != 0:
|
||||||
|
self.log.warning("USR2 signal ignored. Child exists.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.master_pid != 0:
|
||||||
|
self.log.warning("USR2 signal ignored. Parent exists.")
|
||||||
|
return
|
||||||
|
|
||||||
|
master_pid = os.getpid()
|
||||||
|
self.reexec_pid = os.fork()
|
||||||
|
if self.reexec_pid != 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.cfg.pre_exec(self)
|
||||||
|
|
||||||
|
environ = self.cfg.env_orig.copy()
|
||||||
|
environ['GUNICORN_PID'] = str(master_pid)
|
||||||
|
|
||||||
|
if self.systemd:
|
||||||
|
environ['LISTEN_PID'] = str(os.getpid())
|
||||||
|
environ['LISTEN_FDS'] = str(len(self.LISTENERS))
|
||||||
|
else:
|
||||||
|
environ['GUNICORN_FD'] = ','.join(
|
||||||
|
str(l.fileno()) for l in self.LISTENERS)
|
||||||
|
|
||||||
|
os.chdir(self.START_CTX['cwd'])
|
||||||
|
|
||||||
|
# exec the process using the original environment
|
||||||
|
os.execvpe(self.START_CTX[0], self.START_CTX['args'], environ)
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
old_address = self.cfg.address
|
||||||
|
|
||||||
|
# reset old environment
|
||||||
|
for k in self.cfg.env:
|
||||||
|
if k in self.cfg.env_orig:
|
||||||
|
# reset the key to the value it had before
|
||||||
|
# we launched gunicorn
|
||||||
|
os.environ[k] = self.cfg.env_orig[k]
|
||||||
|
else:
|
||||||
|
# delete the value set by gunicorn
|
||||||
|
try:
|
||||||
|
del os.environ[k]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# reload conf
|
||||||
|
self.app.reload()
|
||||||
|
self.setup(self.app)
|
||||||
|
|
||||||
|
# reopen log files
|
||||||
|
self.log.reopen_files()
|
||||||
|
|
||||||
|
# do we need to change listener ?
|
||||||
|
if old_address != self.cfg.address:
|
||||||
|
# close all listeners
|
||||||
|
for l in self.LISTENERS:
|
||||||
|
l.close()
|
||||||
|
# init new listeners
|
||||||
|
self.LISTENERS = sock.create_sockets(self.cfg, self.log)
|
||||||
|
listeners_str = ",".join([str(l) for l in self.LISTENERS])
|
||||||
|
self.log.info("Listening at: %s", listeners_str)
|
||||||
|
|
||||||
|
# do some actions on reload
|
||||||
|
self.cfg.on_reload(self)
|
||||||
|
|
||||||
|
# unlink pidfile
|
||||||
|
if self.pidfile is not None:
|
||||||
|
self.pidfile.unlink()
|
||||||
|
|
||||||
|
# create new pidfile
|
||||||
|
if self.cfg.pidfile is not None:
|
||||||
|
self.pidfile = Pidfile(self.cfg.pidfile)
|
||||||
|
self.pidfile.create(self.pid)
|
||||||
|
|
||||||
|
# set new proc_name
|
||||||
|
util._setproctitle("master [%s]" % self.proc_name)
|
||||||
|
|
||||||
|
# spawn new workers
|
||||||
|
for _ in range(self.cfg.workers):
|
||||||
|
self.spawn_worker()
|
||||||
|
|
||||||
|
# manage workers
|
||||||
|
self.manage_workers()
|
||||||
|
|
||||||
|
def murder_workers(self):
|
||||||
|
"""\
|
||||||
|
Kill unused/idle workers
|
||||||
|
"""
|
||||||
|
if not self.timeout:
|
||||||
|
return
|
||||||
|
workers = list(self.WORKERS.items())
|
||||||
|
for (pid, worker) in workers:
|
||||||
|
try:
|
||||||
|
if time.time() - worker.tmp.last_update() <= self.timeout:
|
||||||
|
continue
|
||||||
|
except (OSError, ValueError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not worker.aborted:
|
||||||
|
self.log.critical("WORKER TIMEOUT (pid:%s)", pid)
|
||||||
|
worker.aborted = True
|
||||||
|
self.kill_worker(pid, signal.SIGABRT)
|
||||||
|
else:
|
||||||
|
self.kill_worker(pid, signal.SIGKILL)
|
||||||
|
|
||||||
|
def reap_workers(self):
|
||||||
|
"""\
|
||||||
|
Reap workers to avoid zombie processes
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
wpid, status = os.waitpid(-1, os.WNOHANG)
|
||||||
|
if not wpid:
|
||||||
|
break
|
||||||
|
if self.reexec_pid == wpid:
|
||||||
|
self.reexec_pid = 0
|
||||||
|
else:
|
||||||
|
# A worker was terminated. If the termination reason was
|
||||||
|
# that it could not boot, we'll shut it down to avoid
|
||||||
|
# infinite start/stop cycles.
|
||||||
|
exitcode = status >> 8
|
||||||
|
if exitcode == self.WORKER_BOOT_ERROR:
|
||||||
|
reason = "Worker failed to boot."
|
||||||
|
raise HaltServer(reason, self.WORKER_BOOT_ERROR)
|
||||||
|
if exitcode == self.APP_LOAD_ERROR:
|
||||||
|
reason = "App failed to load."
|
||||||
|
raise HaltServer(reason, self.APP_LOAD_ERROR)
|
||||||
|
|
||||||
|
worker = self.WORKERS.pop(wpid, None)
|
||||||
|
if not worker:
|
||||||
|
continue
|
||||||
|
worker.tmp.close()
|
||||||
|
self.cfg.child_exit(self, worker)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ECHILD:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def manage_workers(self):
|
||||||
|
"""\
|
||||||
|
Maintain the number of workers by spawning or killing
|
||||||
|
as required.
|
||||||
|
"""
|
||||||
|
if len(self.WORKERS.keys()) < self.num_workers:
|
||||||
|
self.spawn_workers()
|
||||||
|
|
||||||
|
workers = self.WORKERS.items()
|
||||||
|
workers = sorted(workers, key=lambda w: w[1].age)
|
||||||
|
while len(workers) > self.num_workers:
|
||||||
|
(pid, _) = workers.pop(0)
|
||||||
|
self.kill_worker(pid, signal.SIGTERM)
|
||||||
|
|
||||||
|
active_worker_count = len(workers)
|
||||||
|
if self._last_logged_active_worker_count != active_worker_count:
|
||||||
|
self._last_logged_active_worker_count = active_worker_count
|
||||||
|
self.log.debug("{0} workers".format(active_worker_count),
|
||||||
|
extra={"metric": "gunicorn.workers",
|
||||||
|
"value": active_worker_count,
|
||||||
|
"mtype": "gauge"})
|
||||||
|
|
||||||
|
def spawn_worker(self):
|
||||||
|
self.worker_age += 1
|
||||||
|
worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS,
|
||||||
|
self.app, self.timeout / 2.0,
|
||||||
|
self.cfg, self.log)
|
||||||
|
self.cfg.pre_fork(self, worker)
|
||||||
|
pid = os.fork()
|
||||||
|
if pid != 0:
|
||||||
|
worker.pid = pid
|
||||||
|
self.WORKERS[pid] = worker
|
||||||
|
return pid
|
||||||
|
|
||||||
|
# Do not inherit the temporary files of other workers
|
||||||
|
for sibling in self.WORKERS.values():
|
||||||
|
sibling.tmp.close()
|
||||||
|
|
||||||
|
# Process Child
|
||||||
|
worker.pid = os.getpid()
|
||||||
|
try:
|
||||||
|
util._setproctitle("worker [%s]" % self.proc_name)
|
||||||
|
self.log.info("Booting worker with pid: %s", worker.pid)
|
||||||
|
self.cfg.post_fork(self, worker)
|
||||||
|
worker.init_process()
|
||||||
|
sys.exit(0)
|
||||||
|
except SystemExit:
|
||||||
|
raise
|
||||||
|
except AppImportError as e:
|
||||||
|
self.log.debug("Exception while loading the application",
|
||||||
|
exc_info=True)
|
||||||
|
print("%s" % e, file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.exit(self.APP_LOAD_ERROR)
|
||||||
|
except:
|
||||||
|
self.log.exception("Exception in worker process")
|
||||||
|
if not worker.booted:
|
||||||
|
sys.exit(self.WORKER_BOOT_ERROR)
|
||||||
|
sys.exit(-1)
|
||||||
|
finally:
|
||||||
|
self.log.info("Worker exiting (pid: %s)", worker.pid)
|
||||||
|
try:
|
||||||
|
worker.tmp.close()
|
||||||
|
self.cfg.worker_exit(self, worker)
|
||||||
|
except:
|
||||||
|
self.log.warning("Exception during worker exit:\n%s",
|
||||||
|
traceback.format_exc())
|
||||||
|
|
||||||
|
def spawn_workers(self):
|
||||||
|
"""\
|
||||||
|
Spawn new workers as needed.
|
||||||
|
|
||||||
|
This is where a worker process leaves the main loop
|
||||||
|
of the master process.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for _ in range(self.num_workers - len(self.WORKERS.keys())):
|
||||||
|
self.spawn_worker()
|
||||||
|
time.sleep(0.1 * random.random())
|
||||||
|
|
||||||
|
def kill_workers(self, sig):
|
||||||
|
"""\
|
||||||
|
Kill all workers with the signal `sig`
|
||||||
|
:attr sig: `signal.SIG*` value
|
||||||
|
"""
|
||||||
|
worker_pids = list(self.WORKERS.keys())
|
||||||
|
for pid in worker_pids:
|
||||||
|
self.kill_worker(pid, sig)
|
||||||
|
|
||||||
|
def kill_worker(self, pid, sig):
|
||||||
|
"""\
|
||||||
|
Kill a worker
|
||||||
|
|
||||||
|
:attr pid: int, worker pid
|
||||||
|
:attr sig: `signal.SIG*` value
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
os.kill(pid, sig)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ESRCH:
|
||||||
|
try:
|
||||||
|
worker = self.WORKERS.pop(pid)
|
||||||
|
worker.tmp.close()
|
||||||
|
self.cfg.worker_exit(self, worker)
|
||||||
|
return
|
||||||
|
except (KeyError, OSError):
|
||||||
|
return
|
||||||
|
raise
|
2362
venv/lib/python3.6/site-packages/gunicorn/argparse_compat.py
Normal file
2362
venv/lib/python3.6/site-packages/gunicorn/argparse_compat.py
Normal file
File diff suppressed because it is too large
Load Diff
1950
venv/lib/python3.6/site-packages/gunicorn/config.py
Normal file
1950
venv/lib/python3.6/site-packages/gunicorn/config.py
Normal file
File diff suppressed because it is too large
Load Diff
69
venv/lib/python3.6/site-packages/gunicorn/debug.py
Normal file
69
venv/lib/python3.6/site-packages/gunicorn/debug.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
"""The debug module contains utilities and functions for better
|
||||||
|
debugging Gunicorn."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import linecache
|
||||||
|
import re
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
__all__ = ['spew', 'unspew']
|
||||||
|
|
||||||
|
_token_spliter = re.compile(r'\W+')
|
||||||
|
|
||||||
|
|
||||||
|
class Spew(object):
|
||||||
|
|
||||||
|
def __init__(self, trace_names=None, show_values=True):
|
||||||
|
self.trace_names = trace_names
|
||||||
|
self.show_values = show_values
|
||||||
|
|
||||||
|
def __call__(self, frame, event, arg):
|
||||||
|
if event == 'line':
|
||||||
|
lineno = frame.f_lineno
|
||||||
|
if '__file__' in frame.f_globals:
|
||||||
|
filename = frame.f_globals['__file__']
|
||||||
|
if (filename.endswith('.pyc') or
|
||||||
|
filename.endswith('.pyo')):
|
||||||
|
filename = filename[:-1]
|
||||||
|
name = frame.f_globals['__name__']
|
||||||
|
line = linecache.getline(filename, lineno)
|
||||||
|
else:
|
||||||
|
name = '[unknown]'
|
||||||
|
try:
|
||||||
|
src = inspect.getsourcelines(frame)
|
||||||
|
line = src[lineno]
|
||||||
|
except IOError:
|
||||||
|
line = 'Unknown code named [%s]. VM instruction #%d' % (
|
||||||
|
frame.f_code.co_name, frame.f_lasti)
|
||||||
|
if self.trace_names is None or name in self.trace_names:
|
||||||
|
print('%s:%s: %s' % (name, lineno, line.rstrip()))
|
||||||
|
if not self.show_values:
|
||||||
|
return self
|
||||||
|
details = []
|
||||||
|
tokens = _token_spliter.split(line)
|
||||||
|
for tok in tokens:
|
||||||
|
if tok in frame.f_globals:
|
||||||
|
details.append('%s=%r' % (tok, frame.f_globals[tok]))
|
||||||
|
if tok in frame.f_locals:
|
||||||
|
details.append('%s=%r' % (tok, frame.f_locals[tok]))
|
||||||
|
if details:
|
||||||
|
print("\t%s" % ' '.join(details))
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
def spew(trace_names=None, show_values=False):
|
||||||
|
"""Install a trace hook which writes incredibly detailed logs
|
||||||
|
about what code is being executed to stdout.
|
||||||
|
"""
|
||||||
|
sys.settrace(Spew(trace_names, show_values))
|
||||||
|
|
||||||
|
|
||||||
|
def unspew():
|
||||||
|
"""Remove the trace hook installed by spew.
|
||||||
|
"""
|
||||||
|
sys.settrace(None)
|
29
venv/lib/python3.6/site-packages/gunicorn/errors.py
Normal file
29
venv/lib/python3.6/site-packages/gunicorn/errors.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
# We don't need to call super() in __init__ methods of our
|
||||||
|
# BaseException and Exception classes because we also define
|
||||||
|
# our own __str__ methods so there is no need to pass 'message'
|
||||||
|
# to the base class to get a meaningful output from 'str(exc)'.
|
||||||
|
# pylint: disable=super-init-not-called
|
||||||
|
|
||||||
|
|
||||||
|
# we inherit from BaseException here to make sure to not be caught
|
||||||
|
# at application level
|
||||||
|
class HaltServer(BaseException):
|
||||||
|
def __init__(self, reason, exit_status=1):
|
||||||
|
self.reason = reason
|
||||||
|
self.exit_status = exit_status
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<HaltServer %r %d>" % (self.reason, self.exit_status)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigError(Exception):
|
||||||
|
""" Exception raised on config error """
|
||||||
|
|
||||||
|
|
||||||
|
class AppImportError(Exception):
|
||||||
|
""" Exception raised when loading an application """
|
478
venv/lib/python3.6/site-packages/gunicorn/glogging.py
Normal file
478
venv/lib/python3.6/site-packages/gunicorn/glogging.py
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import binascii
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
logging.Logger.manager.emittedNoHandlerWarning = 1
|
||||||
|
from logging.config import fileConfig
|
||||||
|
try:
|
||||||
|
from logging.config import dictConfig
|
||||||
|
except ImportError:
|
||||||
|
# python 2.6
|
||||||
|
dictConfig = None
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from gunicorn import util
|
||||||
|
from gunicorn.six import PY3, string_types
|
||||||
|
|
||||||
|
|
||||||
|
# syslog facility codes
|
||||||
|
SYSLOG_FACILITIES = {
|
||||||
|
"auth": 4,
|
||||||
|
"authpriv": 10,
|
||||||
|
"cron": 9,
|
||||||
|
"daemon": 3,
|
||||||
|
"ftp": 11,
|
||||||
|
"kern": 0,
|
||||||
|
"lpr": 6,
|
||||||
|
"mail": 2,
|
||||||
|
"news": 7,
|
||||||
|
"security": 4, # DEPRECATED
|
||||||
|
"syslog": 5,
|
||||||
|
"user": 1,
|
||||||
|
"uucp": 8,
|
||||||
|
"local0": 16,
|
||||||
|
"local1": 17,
|
||||||
|
"local2": 18,
|
||||||
|
"local3": 19,
|
||||||
|
"local4": 20,
|
||||||
|
"local5": 21,
|
||||||
|
"local6": 22,
|
||||||
|
"local7": 23
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_DEFAULTS = dict(
|
||||||
|
version=1,
|
||||||
|
disable_existing_loggers=False,
|
||||||
|
|
||||||
|
loggers={
|
||||||
|
"root": {"level": "INFO", "handlers": ["console"]},
|
||||||
|
"gunicorn.error": {
|
||||||
|
"level": "INFO",
|
||||||
|
"handlers": ["error_console"],
|
||||||
|
"propagate": True,
|
||||||
|
"qualname": "gunicorn.error"
|
||||||
|
},
|
||||||
|
|
||||||
|
"gunicorn.access": {
|
||||||
|
"level": "INFO",
|
||||||
|
"handlers": ["console"],
|
||||||
|
"propagate": True,
|
||||||
|
"qualname": "gunicorn.access"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlers={
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "generic",
|
||||||
|
"stream": "ext://sys.stdout"
|
||||||
|
},
|
||||||
|
"error_console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "generic",
|
||||||
|
"stream": "ext://sys.stderr"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formatters={
|
||||||
|
"generic": {
|
||||||
|
"format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
|
||||||
|
"datefmt": "[%Y-%m-%d %H:%M:%S %z]",
|
||||||
|
"class": "logging.Formatter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def loggers():
|
||||||
|
""" get list of all loggers """
|
||||||
|
root = logging.root
|
||||||
|
existing = root.manager.loggerDict.keys()
|
||||||
|
return [logging.getLogger(name) for name in existing]
|
||||||
|
|
||||||
|
|
||||||
|
class SafeAtoms(dict):
|
||||||
|
|
||||||
|
def __init__(self, atoms):
|
||||||
|
dict.__init__(self)
|
||||||
|
for key, value in atoms.items():
|
||||||
|
if isinstance(value, string_types):
|
||||||
|
self[key] = value.replace('"', '\\"')
|
||||||
|
else:
|
||||||
|
self[key] = value
|
||||||
|
|
||||||
|
def __getitem__(self, k):
|
||||||
|
if k.startswith("{"):
|
||||||
|
kl = k.lower()
|
||||||
|
if kl in self:
|
||||||
|
return super(SafeAtoms, self).__getitem__(kl)
|
||||||
|
else:
|
||||||
|
return "-"
|
||||||
|
if k in self:
|
||||||
|
return super(SafeAtoms, self).__getitem__(k)
|
||||||
|
else:
|
||||||
|
return '-'
|
||||||
|
|
||||||
|
|
||||||
|
def parse_syslog_address(addr):
|
||||||
|
|
||||||
|
# unix domain socket type depends on backend
|
||||||
|
# SysLogHandler will try both when given None
|
||||||
|
if addr.startswith("unix://"):
|
||||||
|
sock_type = None
|
||||||
|
|
||||||
|
# set socket type only if explicitly requested
|
||||||
|
parts = addr.split("#", 1)
|
||||||
|
if len(parts) == 2:
|
||||||
|
addr = parts[0]
|
||||||
|
if parts[1] == "dgram":
|
||||||
|
sock_type = socket.SOCK_DGRAM
|
||||||
|
|
||||||
|
return (sock_type, addr.split("unix://")[1])
|
||||||
|
|
||||||
|
if addr.startswith("udp://"):
|
||||||
|
addr = addr.split("udp://")[1]
|
||||||
|
socktype = socket.SOCK_DGRAM
|
||||||
|
elif addr.startswith("tcp://"):
|
||||||
|
addr = addr.split("tcp://")[1]
|
||||||
|
socktype = socket.SOCK_STREAM
|
||||||
|
else:
|
||||||
|
raise RuntimeError("invalid syslog address")
|
||||||
|
|
||||||
|
if '[' in addr and ']' in addr:
|
||||||
|
host = addr.split(']')[0][1:].lower()
|
||||||
|
elif ':' in addr:
|
||||||
|
host = addr.split(':')[0].lower()
|
||||||
|
elif addr == "":
|
||||||
|
host = "localhost"
|
||||||
|
else:
|
||||||
|
host = addr.lower()
|
||||||
|
|
||||||
|
addr = addr.split(']')[-1]
|
||||||
|
if ":" in addr:
|
||||||
|
port = addr.split(':', 1)[1]
|
||||||
|
if not port.isdigit():
|
||||||
|
raise RuntimeError("%r is not a valid port number." % port)
|
||||||
|
port = int(port)
|
||||||
|
else:
|
||||||
|
port = 514
|
||||||
|
|
||||||
|
return (socktype, (host, port))
|
||||||
|
|
||||||
|
|
||||||
|
class Logger(object):
|
||||||
|
|
||||||
|
LOG_LEVELS = {
|
||||||
|
"critical": logging.CRITICAL,
|
||||||
|
"error": logging.ERROR,
|
||||||
|
"warning": logging.WARNING,
|
||||||
|
"info": logging.INFO,
|
||||||
|
"debug": logging.DEBUG
|
||||||
|
}
|
||||||
|
loglevel = logging.INFO
|
||||||
|
|
||||||
|
error_fmt = r"%(asctime)s [%(process)d] [%(levelname)s] %(message)s"
|
||||||
|
datefmt = r"[%Y-%m-%d %H:%M:%S %z]"
|
||||||
|
|
||||||
|
access_fmt = "%(message)s"
|
||||||
|
syslog_fmt = "[%(process)d] %(message)s"
|
||||||
|
|
||||||
|
atoms_wrapper_class = SafeAtoms
|
||||||
|
|
||||||
|
def __init__(self, cfg):
|
||||||
|
self.error_log = logging.getLogger("gunicorn.error")
|
||||||
|
self.error_log.propagate = False
|
||||||
|
self.access_log = logging.getLogger("gunicorn.access")
|
||||||
|
self.access_log.propagate = False
|
||||||
|
self.error_handlers = []
|
||||||
|
self.access_handlers = []
|
||||||
|
self.logfile = None
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.cfg = cfg
|
||||||
|
self.setup(cfg)
|
||||||
|
|
||||||
|
def setup(self, cfg):
|
||||||
|
self.loglevel = self.LOG_LEVELS.get(cfg.loglevel.lower(), logging.INFO)
|
||||||
|
self.error_log.setLevel(self.loglevel)
|
||||||
|
self.access_log.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# set gunicorn.error handler
|
||||||
|
if self.cfg.capture_output and cfg.errorlog != "-":
|
||||||
|
for stream in sys.stdout, sys.stderr:
|
||||||
|
stream.flush()
|
||||||
|
|
||||||
|
self.logfile = open(cfg.errorlog, 'a+')
|
||||||
|
os.dup2(self.logfile.fileno(), sys.stdout.fileno())
|
||||||
|
os.dup2(self.logfile.fileno(), sys.stderr.fileno())
|
||||||
|
|
||||||
|
self._set_handler(self.error_log, cfg.errorlog,
|
||||||
|
logging.Formatter(self.error_fmt, self.datefmt))
|
||||||
|
|
||||||
|
# set gunicorn.access handler
|
||||||
|
if cfg.accesslog is not None:
|
||||||
|
self._set_handler(self.access_log, cfg.accesslog,
|
||||||
|
fmt=logging.Formatter(self.access_fmt), stream=sys.stdout)
|
||||||
|
|
||||||
|
# set syslog handler
|
||||||
|
if cfg.syslog:
|
||||||
|
self._set_syslog_handler(
|
||||||
|
self.error_log, cfg, self.syslog_fmt, "error"
|
||||||
|
)
|
||||||
|
if not cfg.disable_redirect_access_to_syslog:
|
||||||
|
self._set_syslog_handler(
|
||||||
|
self.access_log, cfg, self.syslog_fmt, "access"
|
||||||
|
)
|
||||||
|
|
||||||
|
if dictConfig is None and cfg.logconfig_dict:
|
||||||
|
util.warn("Dictionary-based log configuration requires "
|
||||||
|
"Python 2.7 or above.")
|
||||||
|
|
||||||
|
if dictConfig and cfg.logconfig_dict:
|
||||||
|
config = CONFIG_DEFAULTS.copy()
|
||||||
|
config.update(cfg.logconfig_dict)
|
||||||
|
try:
|
||||||
|
dictConfig(config)
|
||||||
|
except (
|
||||||
|
AttributeError,
|
||||||
|
ImportError,
|
||||||
|
ValueError,
|
||||||
|
TypeError
|
||||||
|
) as exc:
|
||||||
|
raise RuntimeError(str(exc))
|
||||||
|
elif cfg.logconfig:
|
||||||
|
if os.path.exists(cfg.logconfig):
|
||||||
|
defaults = CONFIG_DEFAULTS.copy()
|
||||||
|
defaults['__file__'] = cfg.logconfig
|
||||||
|
defaults['here'] = os.path.dirname(cfg.logconfig)
|
||||||
|
fileConfig(cfg.logconfig, defaults=defaults,
|
||||||
|
disable_existing_loggers=False)
|
||||||
|
else:
|
||||||
|
msg = "Error: log config '%s' not found"
|
||||||
|
raise RuntimeError(msg % cfg.logconfig)
|
||||||
|
|
||||||
|
def critical(self, msg, *args, **kwargs):
|
||||||
|
self.error_log.critical(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def error(self, msg, *args, **kwargs):
|
||||||
|
self.error_log.error(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def warning(self, msg, *args, **kwargs):
|
||||||
|
self.error_log.warning(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def info(self, msg, *args, **kwargs):
|
||||||
|
self.error_log.info(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def debug(self, msg, *args, **kwargs):
|
||||||
|
self.error_log.debug(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def exception(self, msg, *args, **kwargs):
|
||||||
|
self.error_log.exception(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def log(self, lvl, msg, *args, **kwargs):
|
||||||
|
if isinstance(lvl, string_types):
|
||||||
|
lvl = self.LOG_LEVELS.get(lvl.lower(), logging.INFO)
|
||||||
|
self.error_log.log(lvl, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def atoms(self, resp, req, environ, request_time):
|
||||||
|
""" Gets atoms for log formating.
|
||||||
|
"""
|
||||||
|
status = resp.status
|
||||||
|
if isinstance(status, str):
|
||||||
|
status = status.split(None, 1)[0]
|
||||||
|
atoms = {
|
||||||
|
'h': environ.get('REMOTE_ADDR', '-'),
|
||||||
|
'l': '-',
|
||||||
|
'u': self._get_user(environ) or '-',
|
||||||
|
't': self.now(),
|
||||||
|
'r': "%s %s %s" % (environ['REQUEST_METHOD'],
|
||||||
|
environ['RAW_URI'], environ["SERVER_PROTOCOL"]),
|
||||||
|
's': status,
|
||||||
|
'm': environ.get('REQUEST_METHOD'),
|
||||||
|
'U': environ.get('PATH_INFO'),
|
||||||
|
'q': environ.get('QUERY_STRING'),
|
||||||
|
'H': environ.get('SERVER_PROTOCOL'),
|
||||||
|
'b': getattr(resp, 'sent', None) is not None and str(resp.sent) or '-',
|
||||||
|
'B': getattr(resp, 'sent', None),
|
||||||
|
'f': environ.get('HTTP_REFERER', '-'),
|
||||||
|
'a': environ.get('HTTP_USER_AGENT', '-'),
|
||||||
|
'T': request_time.seconds,
|
||||||
|
'D': (request_time.seconds*1000000) + request_time.microseconds,
|
||||||
|
'L': "%d.%06d" % (request_time.seconds, request_time.microseconds),
|
||||||
|
'p': "<%s>" % os.getpid()
|
||||||
|
}
|
||||||
|
|
||||||
|
# add request headers
|
||||||
|
if hasattr(req, 'headers'):
|
||||||
|
req_headers = req.headers
|
||||||
|
else:
|
||||||
|
req_headers = req
|
||||||
|
|
||||||
|
if hasattr(req_headers, "items"):
|
||||||
|
req_headers = req_headers.items()
|
||||||
|
|
||||||
|
atoms.update(dict([("{%s}i" % k.lower(), v) for k, v in req_headers]))
|
||||||
|
|
||||||
|
resp_headers = resp.headers
|
||||||
|
if hasattr(resp_headers, "items"):
|
||||||
|
resp_headers = resp_headers.items()
|
||||||
|
|
||||||
|
# add response headers
|
||||||
|
atoms.update(dict([("{%s}o" % k.lower(), v) for k, v in resp_headers]))
|
||||||
|
|
||||||
|
# add environ variables
|
||||||
|
environ_variables = environ.items()
|
||||||
|
atoms.update(dict([("{%s}e" % k.lower(), v) for k, v in environ_variables]))
|
||||||
|
|
||||||
|
return atoms
|
||||||
|
|
||||||
|
def access(self, resp, req, environ, request_time):
|
||||||
|
""" See http://httpd.apache.org/docs/2.0/logs.html#combined
|
||||||
|
for format details
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not (self.cfg.accesslog or self.cfg.logconfig or
|
||||||
|
self.cfg.logconfig_dict or
|
||||||
|
(self.cfg.syslog and not self.cfg.disable_redirect_access_to_syslog)):
|
||||||
|
return
|
||||||
|
|
||||||
|
# wrap atoms:
|
||||||
|
# - make sure atoms will be test case insensitively
|
||||||
|
# - if atom doesn't exist replace it by '-'
|
||||||
|
safe_atoms = self.atoms_wrapper_class(self.atoms(resp, req, environ,
|
||||||
|
request_time))
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.access_log.info(self.cfg.access_log_format, safe_atoms)
|
||||||
|
except:
|
||||||
|
self.error(traceback.format_exc())
|
||||||
|
|
||||||
|
def now(self):
|
||||||
|
""" return date in Apache Common Log Format """
|
||||||
|
return time.strftime('[%d/%b/%Y:%H:%M:%S %z]')
|
||||||
|
|
||||||
|
def reopen_files(self):
|
||||||
|
if self.cfg.capture_output and self.cfg.errorlog != "-":
|
||||||
|
for stream in sys.stdout, sys.stderr:
|
||||||
|
stream.flush()
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
if self.logfile is not None:
|
||||||
|
self.logfile.close()
|
||||||
|
self.logfile = open(self.cfg.errorlog, 'a+')
|
||||||
|
os.dup2(self.logfile.fileno(), sys.stdout.fileno())
|
||||||
|
os.dup2(self.logfile.fileno(), sys.stderr.fileno())
|
||||||
|
|
||||||
|
|
||||||
|
for log in loggers():
|
||||||
|
for handler in log.handlers:
|
||||||
|
if isinstance(handler, logging.FileHandler):
|
||||||
|
handler.acquire()
|
||||||
|
try:
|
||||||
|
if handler.stream:
|
||||||
|
handler.close()
|
||||||
|
handler.stream = handler._open()
|
||||||
|
finally:
|
||||||
|
handler.release()
|
||||||
|
|
||||||
|
def close_on_exec(self):
|
||||||
|
for log in loggers():
|
||||||
|
for handler in log.handlers:
|
||||||
|
if isinstance(handler, logging.FileHandler):
|
||||||
|
handler.acquire()
|
||||||
|
try:
|
||||||
|
if handler.stream:
|
||||||
|
util.close_on_exec(handler.stream.fileno())
|
||||||
|
finally:
|
||||||
|
handler.release()
|
||||||
|
|
||||||
|
def _get_gunicorn_handler(self, log):
|
||||||
|
for h in log.handlers:
|
||||||
|
if getattr(h, "_gunicorn", False):
|
||||||
|
return h
|
||||||
|
|
||||||
|
def _set_handler(self, log, output, fmt, stream=None):
|
||||||
|
# remove previous gunicorn log handler
|
||||||
|
h = self._get_gunicorn_handler(log)
|
||||||
|
if h:
|
||||||
|
log.handlers.remove(h)
|
||||||
|
|
||||||
|
if output is not None:
|
||||||
|
if output == "-":
|
||||||
|
h = logging.StreamHandler(stream)
|
||||||
|
else:
|
||||||
|
util.check_is_writeable(output)
|
||||||
|
h = logging.FileHandler(output)
|
||||||
|
# make sure the user can reopen the file
|
||||||
|
try:
|
||||||
|
os.chown(h.baseFilename, self.cfg.user, self.cfg.group)
|
||||||
|
except OSError:
|
||||||
|
# it's probably OK there, we assume the user has given
|
||||||
|
# /dev/null as a parameter.
|
||||||
|
pass
|
||||||
|
|
||||||
|
h.setFormatter(fmt)
|
||||||
|
h._gunicorn = True
|
||||||
|
log.addHandler(h)
|
||||||
|
|
||||||
|
def _set_syslog_handler(self, log, cfg, fmt, name):
|
||||||
|
# setup format
|
||||||
|
if not cfg.syslog_prefix:
|
||||||
|
prefix = cfg.proc_name.replace(":", ".")
|
||||||
|
else:
|
||||||
|
prefix = cfg.syslog_prefix
|
||||||
|
|
||||||
|
prefix = "gunicorn.%s.%s" % (prefix, name)
|
||||||
|
|
||||||
|
# set format
|
||||||
|
fmt = logging.Formatter(r"%s: %s" % (prefix, fmt))
|
||||||
|
|
||||||
|
# syslog facility
|
||||||
|
try:
|
||||||
|
facility = SYSLOG_FACILITIES[cfg.syslog_facility.lower()]
|
||||||
|
except KeyError:
|
||||||
|
raise RuntimeError("unknown facility name")
|
||||||
|
|
||||||
|
# parse syslog address
|
||||||
|
socktype, addr = parse_syslog_address(cfg.syslog_addr)
|
||||||
|
|
||||||
|
# finally setup the syslog handler
|
||||||
|
if sys.version_info >= (2, 7):
|
||||||
|
h = logging.handlers.SysLogHandler(address=addr,
|
||||||
|
facility=facility, socktype=socktype)
|
||||||
|
else:
|
||||||
|
# socktype is only supported in 2.7 and sup
|
||||||
|
# fix issue #541
|
||||||
|
h = logging.handlers.SysLogHandler(address=addr,
|
||||||
|
facility=facility)
|
||||||
|
|
||||||
|
h.setFormatter(fmt)
|
||||||
|
h._gunicorn = True
|
||||||
|
log.addHandler(h)
|
||||||
|
|
||||||
|
def _get_user(self, environ):
|
||||||
|
user = None
|
||||||
|
http_auth = environ.get("HTTP_AUTHORIZATION")
|
||||||
|
if http_auth and http_auth.startswith('Basic'):
|
||||||
|
auth = http_auth.split(" ", 1)
|
||||||
|
if len(auth) == 2:
|
||||||
|
try:
|
||||||
|
# b64decode doesn't accept unicode in Python < 3.3
|
||||||
|
# so we need to convert it to a byte string
|
||||||
|
auth = base64.b64decode(auth[1].strip().encode('utf-8'))
|
||||||
|
if PY3: # b64decode returns a byte string in Python 3
|
||||||
|
auth = auth.decode('utf-8')
|
||||||
|
auth = auth.split(":", 1)
|
||||||
|
except (TypeError, binascii.Error, UnicodeDecodeError) as exc:
|
||||||
|
self.debug("Couldn't get username: %s", exc)
|
||||||
|
return user
|
||||||
|
if len(auth) == 2:
|
||||||
|
user = auth[0]
|
||||||
|
return user
|
@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
from gunicorn.http.message import Message, Request
|
||||||
|
from gunicorn.http.parser import RequestParser
|
||||||
|
|
||||||
|
__all__ = ['Message', 'Request', 'RequestParser']
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
67
venv/lib/python3.6/site-packages/gunicorn/http/_sendfile.py
Normal file
67
venv/lib/python3.6/site-packages/gunicorn/http/_sendfile.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
import ctypes.util
|
||||||
|
except MemoryError:
|
||||||
|
# selinux execmem denial
|
||||||
|
# https://bugzilla.redhat.com/show_bug.cgi?id=488396
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
SUPPORTED_PLATFORMS = (
|
||||||
|
'darwin',
|
||||||
|
'freebsd',
|
||||||
|
'dragonfly',
|
||||||
|
'linux2')
|
||||||
|
|
||||||
|
if sys.platform not in SUPPORTED_PLATFORMS:
|
||||||
|
raise ImportError("sendfile isn't supported on this platform")
|
||||||
|
|
||||||
|
_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
|
||||||
|
_sendfile = _libc.sendfile
|
||||||
|
|
||||||
|
|
||||||
|
def sendfile(fdout, fdin, offset, nbytes):
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
_sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64,
|
||||||
|
ctypes.POINTER(ctypes.c_uint64), ctypes.c_voidp,
|
||||||
|
ctypes.c_int]
|
||||||
|
_nbytes = ctypes.c_uint64(nbytes)
|
||||||
|
result = _sendfile(fdin, fdout, offset, _nbytes, None, 0)
|
||||||
|
|
||||||
|
if result == -1:
|
||||||
|
e = ctypes.get_errno()
|
||||||
|
if e == errno.EAGAIN and _nbytes.value is not None:
|
||||||
|
return _nbytes.value
|
||||||
|
raise OSError(e, os.strerror(e))
|
||||||
|
return _nbytes.value
|
||||||
|
elif sys.platform in ('freebsd', 'dragonfly',):
|
||||||
|
_sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64,
|
||||||
|
ctypes.c_uint64, ctypes.c_voidp,
|
||||||
|
ctypes.POINTER(ctypes.c_uint64), ctypes.c_int]
|
||||||
|
_sbytes = ctypes.c_uint64()
|
||||||
|
result = _sendfile(fdin, fdout, offset, nbytes, None, _sbytes, 0)
|
||||||
|
if result == -1:
|
||||||
|
e = ctypes.get_errno()
|
||||||
|
if e == errno.EAGAIN and _sbytes.value is not None:
|
||||||
|
return _sbytes.value
|
||||||
|
raise OSError(e, os.strerror(e))
|
||||||
|
return _sbytes.value
|
||||||
|
|
||||||
|
else:
|
||||||
|
_sendfile.argtypes = [ctypes.c_int, ctypes.c_int,
|
||||||
|
ctypes.POINTER(ctypes.c_uint64), ctypes.c_size_t]
|
||||||
|
|
||||||
|
_offset = ctypes.c_uint64(offset)
|
||||||
|
sent = _sendfile(fdout, fdin, _offset, nbytes)
|
||||||
|
if sent == -1:
|
||||||
|
e = ctypes.get_errno()
|
||||||
|
raise OSError(e, os.strerror(e))
|
||||||
|
return sent
|
259
venv/lib/python3.6/site-packages/gunicorn/http/body.py
Normal file
259
venv/lib/python3.6/site-packages/gunicorn/http/body.py
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
from gunicorn.http.errors import (NoMoreData, ChunkMissingTerminator,
|
||||||
|
InvalidChunkSize)
|
||||||
|
from gunicorn import six
|
||||||
|
|
||||||
|
|
||||||
|
class ChunkedReader(object):
|
||||||
|
def __init__(self, req, unreader):
|
||||||
|
self.req = req
|
||||||
|
self.parser = self.parse_chunked(unreader)
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
|
||||||
|
def read(self, size):
|
||||||
|
if not isinstance(size, six.integer_types):
|
||||||
|
raise TypeError("size must be an integral type")
|
||||||
|
if size < 0:
|
||||||
|
raise ValueError("Size must be positive.")
|
||||||
|
if size == 0:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
if self.parser:
|
||||||
|
while self.buf.tell() < size:
|
||||||
|
try:
|
||||||
|
self.buf.write(six.next(self.parser))
|
||||||
|
except StopIteration:
|
||||||
|
self.parser = None
|
||||||
|
break
|
||||||
|
|
||||||
|
data = self.buf.getvalue()
|
||||||
|
ret, rest = data[:size], data[size:]
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
self.buf.write(rest)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def parse_trailers(self, unreader, data):
|
||||||
|
buf = six.BytesIO()
|
||||||
|
buf.write(data)
|
||||||
|
|
||||||
|
idx = buf.getvalue().find(b"\r\n\r\n")
|
||||||
|
done = buf.getvalue()[:2] == b"\r\n"
|
||||||
|
while idx < 0 and not done:
|
||||||
|
self.get_data(unreader, buf)
|
||||||
|
idx = buf.getvalue().find(b"\r\n\r\n")
|
||||||
|
done = buf.getvalue()[:2] == b"\r\n"
|
||||||
|
if done:
|
||||||
|
unreader.unread(buf.getvalue()[2:])
|
||||||
|
return b""
|
||||||
|
self.req.trailers = self.req.parse_headers(buf.getvalue()[:idx])
|
||||||
|
unreader.unread(buf.getvalue()[idx + 4:])
|
||||||
|
|
||||||
|
def parse_chunked(self, unreader):
|
||||||
|
(size, rest) = self.parse_chunk_size(unreader)
|
||||||
|
while size > 0:
|
||||||
|
while size > len(rest):
|
||||||
|
size -= len(rest)
|
||||||
|
yield rest
|
||||||
|
rest = unreader.read()
|
||||||
|
if not rest:
|
||||||
|
raise NoMoreData()
|
||||||
|
yield rest[:size]
|
||||||
|
# Remove \r\n after chunk
|
||||||
|
rest = rest[size:]
|
||||||
|
while len(rest) < 2:
|
||||||
|
rest += unreader.read()
|
||||||
|
if rest[:2] != b'\r\n':
|
||||||
|
raise ChunkMissingTerminator(rest[:2])
|
||||||
|
(size, rest) = self.parse_chunk_size(unreader, data=rest[2:])
|
||||||
|
|
||||||
|
def parse_chunk_size(self, unreader, data=None):
|
||||||
|
buf = six.BytesIO()
|
||||||
|
if data is not None:
|
||||||
|
buf.write(data)
|
||||||
|
|
||||||
|
idx = buf.getvalue().find(b"\r\n")
|
||||||
|
while idx < 0:
|
||||||
|
self.get_data(unreader, buf)
|
||||||
|
idx = buf.getvalue().find(b"\r\n")
|
||||||
|
|
||||||
|
data = buf.getvalue()
|
||||||
|
line, rest_chunk = data[:idx], data[idx + 2:]
|
||||||
|
|
||||||
|
chunk_size = line.split(b";", 1)[0].strip()
|
||||||
|
try:
|
||||||
|
chunk_size = int(chunk_size, 16)
|
||||||
|
except ValueError:
|
||||||
|
raise InvalidChunkSize(chunk_size)
|
||||||
|
|
||||||
|
if chunk_size == 0:
|
||||||
|
try:
|
||||||
|
self.parse_trailers(unreader, rest_chunk)
|
||||||
|
except NoMoreData:
|
||||||
|
pass
|
||||||
|
return (0, None)
|
||||||
|
return (chunk_size, rest_chunk)
|
||||||
|
|
||||||
|
def get_data(self, unreader, buf):
|
||||||
|
data = unreader.read()
|
||||||
|
if not data:
|
||||||
|
raise NoMoreData()
|
||||||
|
buf.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
class LengthReader(object):
|
||||||
|
def __init__(self, unreader, length):
|
||||||
|
self.unreader = unreader
|
||||||
|
self.length = length
|
||||||
|
|
||||||
|
def read(self, size):
|
||||||
|
if not isinstance(size, six.integer_types):
|
||||||
|
raise TypeError("size must be an integral type")
|
||||||
|
|
||||||
|
size = min(self.length, size)
|
||||||
|
if size < 0:
|
||||||
|
raise ValueError("Size must be positive.")
|
||||||
|
if size == 0:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
buf = six.BytesIO()
|
||||||
|
data = self.unreader.read()
|
||||||
|
while data:
|
||||||
|
buf.write(data)
|
||||||
|
if buf.tell() >= size:
|
||||||
|
break
|
||||||
|
data = self.unreader.read()
|
||||||
|
|
||||||
|
buf = buf.getvalue()
|
||||||
|
ret, rest = buf[:size], buf[size:]
|
||||||
|
self.unreader.unread(rest)
|
||||||
|
self.length -= size
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class EOFReader(object):
|
||||||
|
def __init__(self, unreader):
|
||||||
|
self.unreader = unreader
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
self.finished = False
|
||||||
|
|
||||||
|
def read(self, size):
|
||||||
|
if not isinstance(size, six.integer_types):
|
||||||
|
raise TypeError("size must be an integral type")
|
||||||
|
if size < 0:
|
||||||
|
raise ValueError("Size must be positive.")
|
||||||
|
if size == 0:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
if self.finished:
|
||||||
|
data = self.buf.getvalue()
|
||||||
|
ret, rest = data[:size], data[size:]
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
self.buf.write(rest)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
data = self.unreader.read()
|
||||||
|
while data:
|
||||||
|
self.buf.write(data)
|
||||||
|
if self.buf.tell() > size:
|
||||||
|
break
|
||||||
|
data = self.unreader.read()
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
self.finished = True
|
||||||
|
|
||||||
|
data = self.buf.getvalue()
|
||||||
|
ret, rest = data[:size], data[size:]
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
self.buf.write(rest)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class Body(object):
|
||||||
|
def __init__(self, reader):
|
||||||
|
self.reader = reader
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
ret = self.readline()
|
||||||
|
if not ret:
|
||||||
|
raise StopIteration()
|
||||||
|
return ret
|
||||||
|
next = __next__
|
||||||
|
|
||||||
|
def getsize(self, size):
|
||||||
|
if size is None:
|
||||||
|
return six.MAXSIZE
|
||||||
|
elif not isinstance(size, six.integer_types):
|
||||||
|
raise TypeError("size must be an integral type")
|
||||||
|
elif size < 0:
|
||||||
|
return six.MAXSIZE
|
||||||
|
return size
|
||||||
|
|
||||||
|
def read(self, size=None):
|
||||||
|
size = self.getsize(size)
|
||||||
|
if size == 0:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
if size < self.buf.tell():
|
||||||
|
data = self.buf.getvalue()
|
||||||
|
ret, rest = data[:size], data[size:]
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
self.buf.write(rest)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
while size > self.buf.tell():
|
||||||
|
data = self.reader.read(1024)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
self.buf.write(data)
|
||||||
|
|
||||||
|
data = self.buf.getvalue()
|
||||||
|
ret, rest = data[:size], data[size:]
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
self.buf.write(rest)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def readline(self, size=None):
|
||||||
|
size = self.getsize(size)
|
||||||
|
if size == 0:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
data = self.buf.getvalue()
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
while 1:
|
||||||
|
idx = data.find(b"\n", 0, size)
|
||||||
|
idx = idx + 1 if idx >= 0 else size if len(data) >= size else 0
|
||||||
|
if idx:
|
||||||
|
ret.append(data[:idx])
|
||||||
|
self.buf.write(data[idx:])
|
||||||
|
break
|
||||||
|
|
||||||
|
ret.append(data)
|
||||||
|
size -= len(data)
|
||||||
|
data = self.reader.read(min(1024, size))
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
|
||||||
|
return b"".join(ret)
|
||||||
|
|
||||||
|
def readlines(self, size=None):
|
||||||
|
ret = []
|
||||||
|
data = self.read()
|
||||||
|
while data:
|
||||||
|
pos = data.find(b"\n")
|
||||||
|
if pos < 0:
|
||||||
|
ret.append(data)
|
||||||
|
data = b""
|
||||||
|
else:
|
||||||
|
line, data = data[:pos + 1], data[pos + 1:]
|
||||||
|
ret.append(line)
|
||||||
|
return ret
|
120
venv/lib/python3.6/site-packages/gunicorn/http/errors.py
Normal file
120
venv/lib/python3.6/site-packages/gunicorn/http/errors.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
# We don't need to call super() in __init__ methods of our
|
||||||
|
# BaseException and Exception classes because we also define
|
||||||
|
# our own __str__ methods so there is no need to pass 'message'
|
||||||
|
# to the base class to get a meaningful output from 'str(exc)'.
|
||||||
|
# pylint: disable=super-init-not-called
|
||||||
|
|
||||||
|
|
||||||
|
class ParseException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoMoreData(IOError):
|
||||||
|
def __init__(self, buf=None):
|
||||||
|
self.buf = buf
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "No more data after: %r" % self.buf
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidRequestLine(ParseException):
|
||||||
|
def __init__(self, req):
|
||||||
|
self.req = req
|
||||||
|
self.code = 400
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid HTTP request line: %r" % self.req
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidRequestMethod(ParseException):
|
||||||
|
def __init__(self, method):
|
||||||
|
self.method = method
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid HTTP method: %r" % self.method
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidHTTPVersion(ParseException):
|
||||||
|
def __init__(self, version):
|
||||||
|
self.version = version
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid HTTP Version: %r" % self.version
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidHeader(ParseException):
|
||||||
|
def __init__(self, hdr, req=None):
|
||||||
|
self.hdr = hdr
|
||||||
|
self.req = req
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid HTTP Header: %r" % self.hdr
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidHeaderName(ParseException):
|
||||||
|
def __init__(self, hdr):
|
||||||
|
self.hdr = hdr
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid HTTP header name: %r" % self.hdr
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidChunkSize(IOError):
|
||||||
|
def __init__(self, data):
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid chunk size: %r" % self.data
|
||||||
|
|
||||||
|
|
||||||
|
class ChunkMissingTerminator(IOError):
|
||||||
|
def __init__(self, term):
|
||||||
|
self.term = term
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid chunk terminator is not '\\r\\n': %r" % self.term
|
||||||
|
|
||||||
|
|
||||||
|
class LimitRequestLine(ParseException):
|
||||||
|
def __init__(self, size, max_size):
|
||||||
|
self.size = size
|
||||||
|
self.max_size = max_size
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Request Line is too large (%s > %s)" % (self.size, self.max_size)
|
||||||
|
|
||||||
|
|
||||||
|
class LimitRequestHeaders(ParseException):
|
||||||
|
def __init__(self, msg):
|
||||||
|
self.msg = msg
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.msg
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidProxyLine(ParseException):
|
||||||
|
def __init__(self, line):
|
||||||
|
self.line = line
|
||||||
|
self.code = 400
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Invalid PROXY line: %r" % self.line
|
||||||
|
|
||||||
|
|
||||||
|
class ForbiddenProxyRequest(ParseException):
|
||||||
|
def __init__(self, host):
|
||||||
|
self.host = host
|
||||||
|
self.code = 403
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Proxy request from %r not allowed" % self.host
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidSchemeHeaders(ParseException):
|
||||||
|
def __str__(self):
|
||||||
|
return "Contradictory scheme headers"
|
363
venv/lib/python3.6/site-packages/gunicorn/http/message.py
Normal file
363
venv/lib/python3.6/site-packages/gunicorn/http/message.py
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
from errno import ENOTCONN
|
||||||
|
|
||||||
|
from gunicorn._compat import bytes_to_str
|
||||||
|
from gunicorn.http.unreader import SocketUnreader
|
||||||
|
from gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body
|
||||||
|
from gunicorn.http.errors import (InvalidHeader, InvalidHeaderName, NoMoreData,
|
||||||
|
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion,
|
||||||
|
LimitRequestLine, LimitRequestHeaders)
|
||||||
|
from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
|
||||||
|
from gunicorn.http.errors import InvalidSchemeHeaders
|
||||||
|
from gunicorn.six import BytesIO, string_types
|
||||||
|
from gunicorn.util import split_request_uri
|
||||||
|
|
||||||
|
MAX_REQUEST_LINE = 8190
|
||||||
|
MAX_HEADERS = 32768
|
||||||
|
DEFAULT_MAX_HEADERFIELD_SIZE = 8190
|
||||||
|
|
||||||
|
HEADER_RE = re.compile(r"[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\"]")
|
||||||
|
METH_RE = re.compile(r"[A-Z0-9$-_.]{3,20}")
|
||||||
|
VERSION_RE = re.compile(r"HTTP/(\d+)\.(\d+)")
|
||||||
|
|
||||||
|
|
||||||
|
class Message(object):
|
||||||
|
def __init__(self, cfg, unreader):
|
||||||
|
self.cfg = cfg
|
||||||
|
self.unreader = unreader
|
||||||
|
self.version = None
|
||||||
|
self.headers = []
|
||||||
|
self.trailers = []
|
||||||
|
self.body = None
|
||||||
|
self.scheme = "https" if cfg.is_ssl else "http"
|
||||||
|
|
||||||
|
# set headers limits
|
||||||
|
self.limit_request_fields = cfg.limit_request_fields
|
||||||
|
if (self.limit_request_fields <= 0
|
||||||
|
or self.limit_request_fields > MAX_HEADERS):
|
||||||
|
self.limit_request_fields = MAX_HEADERS
|
||||||
|
self.limit_request_field_size = cfg.limit_request_field_size
|
||||||
|
if self.limit_request_field_size < 0:
|
||||||
|
self.limit_request_field_size = DEFAULT_MAX_HEADERFIELD_SIZE
|
||||||
|
|
||||||
|
# set max header buffer size
|
||||||
|
max_header_field_size = self.limit_request_field_size or DEFAULT_MAX_HEADERFIELD_SIZE
|
||||||
|
self.max_buffer_headers = self.limit_request_fields * \
|
||||||
|
(max_header_field_size + 2) + 4
|
||||||
|
|
||||||
|
unused = self.parse(self.unreader)
|
||||||
|
self.unreader.unread(unused)
|
||||||
|
self.set_body_reader()
|
||||||
|
|
||||||
|
def parse(self, unreader):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def parse_headers(self, data):
|
||||||
|
cfg = self.cfg
|
||||||
|
headers = []
|
||||||
|
|
||||||
|
# Split lines on \r\n keeping the \r\n on each line
|
||||||
|
lines = [bytes_to_str(line) + "\r\n" for line in data.split(b"\r\n")]
|
||||||
|
|
||||||
|
# handle scheme headers
|
||||||
|
scheme_header = False
|
||||||
|
secure_scheme_headers = {}
|
||||||
|
if '*' in cfg.forwarded_allow_ips:
|
||||||
|
secure_scheme_headers = cfg.secure_scheme_headers
|
||||||
|
elif isinstance(self.unreader, SocketUnreader):
|
||||||
|
remote_addr = self.unreader.sock.getpeername()
|
||||||
|
if isinstance(remote_addr, tuple):
|
||||||
|
remote_host = remote_addr[0]
|
||||||
|
if remote_host in cfg.forwarded_allow_ips:
|
||||||
|
secure_scheme_headers = cfg.secure_scheme_headers
|
||||||
|
elif isinstance(remote_addr, string_types):
|
||||||
|
secure_scheme_headers = cfg.secure_scheme_headers
|
||||||
|
|
||||||
|
# Parse headers into key/value pairs paying attention
|
||||||
|
# to continuation lines.
|
||||||
|
while lines:
|
||||||
|
if len(headers) >= self.limit_request_fields:
|
||||||
|
raise LimitRequestHeaders("limit request headers fields")
|
||||||
|
|
||||||
|
# Parse initial header name : value pair.
|
||||||
|
curr = lines.pop(0)
|
||||||
|
header_length = len(curr)
|
||||||
|
if curr.find(":") < 0:
|
||||||
|
raise InvalidHeader(curr.strip())
|
||||||
|
name, value = curr.split(":", 1)
|
||||||
|
name = name.rstrip(" \t").upper()
|
||||||
|
if HEADER_RE.search(name):
|
||||||
|
raise InvalidHeaderName(name)
|
||||||
|
|
||||||
|
name, value = name.strip(), [value.lstrip()]
|
||||||
|
|
||||||
|
# Consume value continuation lines
|
||||||
|
while lines and lines[0].startswith((" ", "\t")):
|
||||||
|
curr = lines.pop(0)
|
||||||
|
header_length += len(curr)
|
||||||
|
if header_length > self.limit_request_field_size > 0:
|
||||||
|
raise LimitRequestHeaders("limit request headers "
|
||||||
|
+ "fields size")
|
||||||
|
value.append(curr)
|
||||||
|
value = ''.join(value).rstrip()
|
||||||
|
|
||||||
|
if header_length > self.limit_request_field_size > 0:
|
||||||
|
raise LimitRequestHeaders("limit request headers fields size")
|
||||||
|
|
||||||
|
if name in secure_scheme_headers:
|
||||||
|
secure = value == secure_scheme_headers[name]
|
||||||
|
scheme = "https" if secure else "http"
|
||||||
|
if scheme_header:
|
||||||
|
if scheme != self.scheme:
|
||||||
|
raise InvalidSchemeHeaders()
|
||||||
|
else:
|
||||||
|
scheme_header = True
|
||||||
|
self.scheme = scheme
|
||||||
|
|
||||||
|
headers.append((name, value))
|
||||||
|
|
||||||
|
return headers
|
||||||
|
|
||||||
|
def set_body_reader(self):
|
||||||
|
chunked = False
|
||||||
|
content_length = None
|
||||||
|
for (name, value) in self.headers:
|
||||||
|
if name == "CONTENT-LENGTH":
|
||||||
|
content_length = value
|
||||||
|
elif name == "TRANSFER-ENCODING":
|
||||||
|
chunked = value.lower() == "chunked"
|
||||||
|
elif name == "SEC-WEBSOCKET-KEY1":
|
||||||
|
content_length = 8
|
||||||
|
|
||||||
|
if chunked:
|
||||||
|
self.body = Body(ChunkedReader(self, self.unreader))
|
||||||
|
elif content_length is not None:
|
||||||
|
try:
|
||||||
|
content_length = int(content_length)
|
||||||
|
except ValueError:
|
||||||
|
raise InvalidHeader("CONTENT-LENGTH", req=self)
|
||||||
|
|
||||||
|
if content_length < 0:
|
||||||
|
raise InvalidHeader("CONTENT-LENGTH", req=self)
|
||||||
|
|
||||||
|
self.body = Body(LengthReader(self.unreader, content_length))
|
||||||
|
else:
|
||||||
|
self.body = Body(EOFReader(self.unreader))
|
||||||
|
|
||||||
|
def should_close(self):
|
||||||
|
for (h, v) in self.headers:
|
||||||
|
if h == "CONNECTION":
|
||||||
|
v = v.lower().strip()
|
||||||
|
if v == "close":
|
||||||
|
return True
|
||||||
|
elif v == "keep-alive":
|
||||||
|
return False
|
||||||
|
break
|
||||||
|
return self.version <= (1, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class Request(Message):
|
||||||
|
def __init__(self, cfg, unreader, req_number=1):
|
||||||
|
self.method = None
|
||||||
|
self.uri = None
|
||||||
|
self.path = None
|
||||||
|
self.query = None
|
||||||
|
self.fragment = None
|
||||||
|
|
||||||
|
# get max request line size
|
||||||
|
self.limit_request_line = cfg.limit_request_line
|
||||||
|
if (self.limit_request_line < 0
|
||||||
|
or self.limit_request_line >= MAX_REQUEST_LINE):
|
||||||
|
self.limit_request_line = MAX_REQUEST_LINE
|
||||||
|
|
||||||
|
self.req_number = req_number
|
||||||
|
self.proxy_protocol_info = None
|
||||||
|
super(Request, self).__init__(cfg, unreader)
|
||||||
|
|
||||||
|
def get_data(self, unreader, buf, stop=False):
|
||||||
|
data = unreader.read()
|
||||||
|
if not data:
|
||||||
|
if stop:
|
||||||
|
raise StopIteration()
|
||||||
|
raise NoMoreData(buf.getvalue())
|
||||||
|
buf.write(data)
|
||||||
|
|
||||||
|
def parse(self, unreader):
|
||||||
|
buf = BytesIO()
|
||||||
|
self.get_data(unreader, buf, stop=True)
|
||||||
|
|
||||||
|
# get request line
|
||||||
|
line, rbuf = self.read_line(unreader, buf, self.limit_request_line)
|
||||||
|
|
||||||
|
# proxy protocol
|
||||||
|
if self.proxy_protocol(bytes_to_str(line)):
|
||||||
|
# get next request line
|
||||||
|
buf = BytesIO()
|
||||||
|
buf.write(rbuf)
|
||||||
|
line, rbuf = self.read_line(unreader, buf, self.limit_request_line)
|
||||||
|
|
||||||
|
self.parse_request_line(line)
|
||||||
|
buf = BytesIO()
|
||||||
|
buf.write(rbuf)
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
data = buf.getvalue()
|
||||||
|
idx = data.find(b"\r\n\r\n")
|
||||||
|
|
||||||
|
done = data[:2] == b"\r\n"
|
||||||
|
while True:
|
||||||
|
idx = data.find(b"\r\n\r\n")
|
||||||
|
done = data[:2] == b"\r\n"
|
||||||
|
|
||||||
|
if idx < 0 and not done:
|
||||||
|
self.get_data(unreader, buf)
|
||||||
|
data = buf.getvalue()
|
||||||
|
if len(data) > self.max_buffer_headers:
|
||||||
|
raise LimitRequestHeaders("max buffer headers")
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if done:
|
||||||
|
self.unreader.unread(data[2:])
|
||||||
|
return b""
|
||||||
|
|
||||||
|
self.headers = self.parse_headers(data[:idx])
|
||||||
|
|
||||||
|
ret = data[idx + 4:]
|
||||||
|
buf = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def read_line(self, unreader, buf, limit=0):
|
||||||
|
data = buf.getvalue()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
idx = data.find(b"\r\n")
|
||||||
|
if idx >= 0:
|
||||||
|
# check if the request line is too large
|
||||||
|
if idx > limit > 0:
|
||||||
|
raise LimitRequestLine(idx, limit)
|
||||||
|
break
|
||||||
|
elif len(data) - 2 > limit > 0:
|
||||||
|
raise LimitRequestLine(len(data), limit)
|
||||||
|
self.get_data(unreader, buf)
|
||||||
|
data = buf.getvalue()
|
||||||
|
|
||||||
|
return (data[:idx], # request line,
|
||||||
|
data[idx + 2:]) # residue in the buffer, skip \r\n
|
||||||
|
|
||||||
|
def proxy_protocol(self, line):
|
||||||
|
"""\
|
||||||
|
Detect, check and parse proxy protocol.
|
||||||
|
|
||||||
|
:raises: ForbiddenProxyRequest, InvalidProxyLine.
|
||||||
|
:return: True for proxy protocol line else False
|
||||||
|
"""
|
||||||
|
if not self.cfg.proxy_protocol:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.req_number != 1:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not line.startswith("PROXY"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.proxy_protocol_access_check()
|
||||||
|
self.parse_proxy_protocol(line)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def proxy_protocol_access_check(self):
|
||||||
|
# check in allow list
|
||||||
|
if isinstance(self.unreader, SocketUnreader):
|
||||||
|
try:
|
||||||
|
remote_host = self.unreader.sock.getpeername()[0]
|
||||||
|
except socket.error as e:
|
||||||
|
if e.args[0] == ENOTCONN:
|
||||||
|
raise ForbiddenProxyRequest("UNKNOW")
|
||||||
|
raise
|
||||||
|
if ("*" not in self.cfg.proxy_allow_ips and
|
||||||
|
remote_host not in self.cfg.proxy_allow_ips):
|
||||||
|
raise ForbiddenProxyRequest(remote_host)
|
||||||
|
|
||||||
|
def parse_proxy_protocol(self, line):
|
||||||
|
bits = line.split()
|
||||||
|
|
||||||
|
if len(bits) != 6:
|
||||||
|
raise InvalidProxyLine(line)
|
||||||
|
|
||||||
|
# Extract data
|
||||||
|
proto = bits[1]
|
||||||
|
s_addr = bits[2]
|
||||||
|
d_addr = bits[3]
|
||||||
|
|
||||||
|
# Validation
|
||||||
|
if proto not in ["TCP4", "TCP6"]:
|
||||||
|
raise InvalidProxyLine("protocol '%s' not supported" % proto)
|
||||||
|
if proto == "TCP4":
|
||||||
|
try:
|
||||||
|
socket.inet_pton(socket.AF_INET, s_addr)
|
||||||
|
socket.inet_pton(socket.AF_INET, d_addr)
|
||||||
|
except socket.error:
|
||||||
|
raise InvalidProxyLine(line)
|
||||||
|
elif proto == "TCP6":
|
||||||
|
try:
|
||||||
|
socket.inet_pton(socket.AF_INET6, s_addr)
|
||||||
|
socket.inet_pton(socket.AF_INET6, d_addr)
|
||||||
|
except socket.error:
|
||||||
|
raise InvalidProxyLine(line)
|
||||||
|
|
||||||
|
try:
|
||||||
|
s_port = int(bits[4])
|
||||||
|
d_port = int(bits[5])
|
||||||
|
except ValueError:
|
||||||
|
raise InvalidProxyLine("invalid port %s" % line)
|
||||||
|
|
||||||
|
if not ((0 <= s_port <= 65535) and (0 <= d_port <= 65535)):
|
||||||
|
raise InvalidProxyLine("invalid port %s" % line)
|
||||||
|
|
||||||
|
# Set data
|
||||||
|
self.proxy_protocol_info = {
|
||||||
|
"proxy_protocol": proto,
|
||||||
|
"client_addr": s_addr,
|
||||||
|
"client_port": s_port,
|
||||||
|
"proxy_addr": d_addr,
|
||||||
|
"proxy_port": d_port
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_request_line(self, line_bytes):
|
||||||
|
bits = [bytes_to_str(bit) for bit in line_bytes.split(None, 2)]
|
||||||
|
if len(bits) != 3:
|
||||||
|
raise InvalidRequestLine(bytes_to_str(line_bytes))
|
||||||
|
|
||||||
|
# Method
|
||||||
|
if not METH_RE.match(bits[0]):
|
||||||
|
raise InvalidRequestMethod(bits[0])
|
||||||
|
self.method = bits[0].upper()
|
||||||
|
|
||||||
|
# URI
|
||||||
|
self.uri = bits[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
parts = split_request_uri(self.uri)
|
||||||
|
except ValueError:
|
||||||
|
raise InvalidRequestLine(bytes_to_str(line_bytes))
|
||||||
|
self.path = parts.path or ""
|
||||||
|
self.query = parts.query or ""
|
||||||
|
self.fragment = parts.fragment or ""
|
||||||
|
|
||||||
|
# Version
|
||||||
|
match = VERSION_RE.match(bits[2])
|
||||||
|
if match is None:
|
||||||
|
raise InvalidHTTPVersion(bits[2])
|
||||||
|
self.version = (int(match.group(1)), int(match.group(2)))
|
||||||
|
|
||||||
|
def set_body_reader(self):
|
||||||
|
super(Request, self).set_body_reader()
|
||||||
|
if isinstance(self.body.reader, EOFReader):
|
||||||
|
self.body = Body(LengthReader(self.unreader, 0))
|
51
venv/lib/python3.6/site-packages/gunicorn/http/parser.py
Normal file
51
venv/lib/python3.6/site-packages/gunicorn/http/parser.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
from gunicorn.http.message import Request
|
||||||
|
from gunicorn.http.unreader import SocketUnreader, IterUnreader
|
||||||
|
|
||||||
|
|
||||||
|
class Parser(object):
|
||||||
|
|
||||||
|
mesg_class = None
|
||||||
|
|
||||||
|
def __init__(self, cfg, source):
|
||||||
|
self.cfg = cfg
|
||||||
|
if hasattr(source, "recv"):
|
||||||
|
self.unreader = SocketUnreader(source)
|
||||||
|
else:
|
||||||
|
self.unreader = IterUnreader(source)
|
||||||
|
self.mesg = None
|
||||||
|
|
||||||
|
# request counter (for keepalive connetions)
|
||||||
|
self.req_count = 0
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
# Stop if HTTP dictates a stop.
|
||||||
|
if self.mesg and self.mesg.should_close():
|
||||||
|
raise StopIteration()
|
||||||
|
|
||||||
|
# Discard any unread body of the previous message
|
||||||
|
if self.mesg:
|
||||||
|
data = self.mesg.body.read(8192)
|
||||||
|
while data:
|
||||||
|
data = self.mesg.body.read(8192)
|
||||||
|
|
||||||
|
# Parse the next request
|
||||||
|
self.req_count += 1
|
||||||
|
self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
|
||||||
|
if not self.mesg:
|
||||||
|
raise StopIteration()
|
||||||
|
return self.mesg
|
||||||
|
|
||||||
|
next = __next__
|
||||||
|
|
||||||
|
|
||||||
|
class RequestParser(Parser):
|
||||||
|
|
||||||
|
mesg_class = Request
|
80
venv/lib/python3.6/site-packages/gunicorn/http/unreader.py
Normal file
80
venv/lib/python3.6/site-packages/gunicorn/http/unreader.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from gunicorn import six
|
||||||
|
|
||||||
|
# Classes that can undo reading data from
|
||||||
|
# a given type of data source.
|
||||||
|
|
||||||
|
|
||||||
|
class Unreader(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
|
||||||
|
def chunk(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def read(self, size=None):
|
||||||
|
if size is not None and not isinstance(size, six.integer_types):
|
||||||
|
raise TypeError("size parameter must be an int or long.")
|
||||||
|
|
||||||
|
if size is not None:
|
||||||
|
if size == 0:
|
||||||
|
return b""
|
||||||
|
if size < 0:
|
||||||
|
size = None
|
||||||
|
|
||||||
|
self.buf.seek(0, os.SEEK_END)
|
||||||
|
|
||||||
|
if size is None and self.buf.tell():
|
||||||
|
ret = self.buf.getvalue()
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
return ret
|
||||||
|
if size is None:
|
||||||
|
d = self.chunk()
|
||||||
|
return d
|
||||||
|
|
||||||
|
while self.buf.tell() < size:
|
||||||
|
chunk = self.chunk()
|
||||||
|
if not chunk:
|
||||||
|
ret = self.buf.getvalue()
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
return ret
|
||||||
|
self.buf.write(chunk)
|
||||||
|
data = self.buf.getvalue()
|
||||||
|
self.buf = six.BytesIO()
|
||||||
|
self.buf.write(data[size:])
|
||||||
|
return data[:size]
|
||||||
|
|
||||||
|
def unread(self, data):
|
||||||
|
self.buf.seek(0, os.SEEK_END)
|
||||||
|
self.buf.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
class SocketUnreader(Unreader):
|
||||||
|
def __init__(self, sock, max_chunk=8192):
|
||||||
|
super(SocketUnreader, self).__init__()
|
||||||
|
self.sock = sock
|
||||||
|
self.mxchunk = max_chunk
|
||||||
|
|
||||||
|
def chunk(self):
|
||||||
|
return self.sock.recv(self.mxchunk)
|
||||||
|
|
||||||
|
|
||||||
|
class IterUnreader(Unreader):
|
||||||
|
def __init__(self, iterable):
|
||||||
|
super(IterUnreader, self).__init__()
|
||||||
|
self.iter = iter(iterable)
|
||||||
|
|
||||||
|
def chunk(self):
|
||||||
|
if not self.iter:
|
||||||
|
return b""
|
||||||
|
try:
|
||||||
|
return six.next(self.iter)
|
||||||
|
except StopIteration:
|
||||||
|
self.iter = None
|
||||||
|
return b""
|
411
venv/lib/python3.6/site-packages/gunicorn/http/wsgi.py
Normal file
411
venv/lib/python3.6/site-packages/gunicorn/http/wsgi.py
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from gunicorn._compat import unquote_to_wsgi_str
|
||||||
|
from gunicorn.http.message import HEADER_RE
|
||||||
|
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName
|
||||||
|
from gunicorn.six import string_types, binary_type, reraise
|
||||||
|
from gunicorn import SERVER_SOFTWARE
|
||||||
|
import gunicorn.util as util
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Python 3.3 has os.sendfile().
|
||||||
|
from os import sendfile
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from ._sendfile import sendfile
|
||||||
|
except ImportError:
|
||||||
|
sendfile = None
|
||||||
|
|
||||||
|
# Send files in at most 1GB blocks as some operating systems can have problems
|
||||||
|
# with sending files in blocks over 2GB.
|
||||||
|
BLKSIZE = 0x3FFFFFFF
|
||||||
|
|
||||||
|
HEADER_VALUE_RE = re.compile(r'[\x00-\x1F\x7F]')
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FileWrapper(object):
|
||||||
|
|
||||||
|
def __init__(self, filelike, blksize=8192):
|
||||||
|
self.filelike = filelike
|
||||||
|
self.blksize = blksize
|
||||||
|
if hasattr(filelike, 'close'):
|
||||||
|
self.close = filelike.close
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
data = self.filelike.read(self.blksize)
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
raise IndexError
|
||||||
|
|
||||||
|
|
||||||
|
class WSGIErrorsWrapper(io.RawIOBase):
|
||||||
|
|
||||||
|
def __init__(self, cfg):
|
||||||
|
# There is no public __init__ method for RawIOBase so
|
||||||
|
# we don't need to call super() in the __init__ method.
|
||||||
|
# pylint: disable=super-init-not-called
|
||||||
|
errorlog = logging.getLogger("gunicorn.error")
|
||||||
|
handlers = errorlog.handlers
|
||||||
|
self.streams = []
|
||||||
|
|
||||||
|
if cfg.errorlog == "-":
|
||||||
|
self.streams.append(sys.stderr)
|
||||||
|
handlers = handlers[1:]
|
||||||
|
|
||||||
|
for h in handlers:
|
||||||
|
if hasattr(h, "stream"):
|
||||||
|
self.streams.append(h.stream)
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
for stream in self.streams:
|
||||||
|
try:
|
||||||
|
stream.write(data)
|
||||||
|
except UnicodeError:
|
||||||
|
stream.write(data.encode("UTF-8"))
|
||||||
|
stream.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def base_environ(cfg):
|
||||||
|
return {
|
||||||
|
"wsgi.errors": WSGIErrorsWrapper(cfg),
|
||||||
|
"wsgi.version": (1, 0),
|
||||||
|
"wsgi.multithread": False,
|
||||||
|
"wsgi.multiprocess": (cfg.workers > 1),
|
||||||
|
"wsgi.run_once": False,
|
||||||
|
"wsgi.file_wrapper": FileWrapper,
|
||||||
|
"SERVER_SOFTWARE": SERVER_SOFTWARE,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def default_environ(req, sock, cfg):
|
||||||
|
env = base_environ(cfg)
|
||||||
|
env.update({
|
||||||
|
"wsgi.input": req.body,
|
||||||
|
"gunicorn.socket": sock,
|
||||||
|
"REQUEST_METHOD": req.method,
|
||||||
|
"QUERY_STRING": req.query,
|
||||||
|
"RAW_URI": req.uri,
|
||||||
|
"SERVER_PROTOCOL": "HTTP/%s" % ".".join([str(v) for v in req.version])
|
||||||
|
})
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def proxy_environ(req):
|
||||||
|
info = req.proxy_protocol_info
|
||||||
|
|
||||||
|
if not info:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"PROXY_PROTOCOL": info["proxy_protocol"],
|
||||||
|
"REMOTE_ADDR": info["client_addr"],
|
||||||
|
"REMOTE_PORT": str(info["client_port"]),
|
||||||
|
"PROXY_ADDR": info["proxy_addr"],
|
||||||
|
"PROXY_PORT": str(info["proxy_port"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create(req, sock, client, server, cfg):
|
||||||
|
resp = Response(req, sock, cfg)
|
||||||
|
|
||||||
|
# set initial environ
|
||||||
|
environ = default_environ(req, sock, cfg)
|
||||||
|
|
||||||
|
# default variables
|
||||||
|
host = None
|
||||||
|
script_name = os.environ.get("SCRIPT_NAME", "")
|
||||||
|
|
||||||
|
# add the headers to the environ
|
||||||
|
for hdr_name, hdr_value in req.headers:
|
||||||
|
if hdr_name == "EXPECT":
|
||||||
|
# handle expect
|
||||||
|
if hdr_value.lower() == "100-continue":
|
||||||
|
sock.send(b"HTTP/1.1 100 Continue\r\n\r\n")
|
||||||
|
elif hdr_name == 'HOST':
|
||||||
|
host = hdr_value
|
||||||
|
elif hdr_name == "SCRIPT_NAME":
|
||||||
|
script_name = hdr_value
|
||||||
|
elif hdr_name == "CONTENT-TYPE":
|
||||||
|
environ['CONTENT_TYPE'] = hdr_value
|
||||||
|
continue
|
||||||
|
elif hdr_name == "CONTENT-LENGTH":
|
||||||
|
environ['CONTENT_LENGTH'] = hdr_value
|
||||||
|
continue
|
||||||
|
|
||||||
|
key = 'HTTP_' + hdr_name.replace('-', '_')
|
||||||
|
if key in environ:
|
||||||
|
hdr_value = "%s,%s" % (environ[key], hdr_value)
|
||||||
|
environ[key] = hdr_value
|
||||||
|
|
||||||
|
# set the url scheme
|
||||||
|
environ['wsgi.url_scheme'] = req.scheme
|
||||||
|
|
||||||
|
# set the REMOTE_* keys in environ
|
||||||
|
# authors should be aware that REMOTE_HOST and REMOTE_ADDR
|
||||||
|
# may not qualify the remote addr:
|
||||||
|
# http://www.ietf.org/rfc/rfc3875
|
||||||
|
if isinstance(client, string_types):
|
||||||
|
environ['REMOTE_ADDR'] = client
|
||||||
|
elif isinstance(client, binary_type):
|
||||||
|
environ['REMOTE_ADDR'] = client.decode()
|
||||||
|
else:
|
||||||
|
environ['REMOTE_ADDR'] = client[0]
|
||||||
|
environ['REMOTE_PORT'] = str(client[1])
|
||||||
|
|
||||||
|
# handle the SERVER_*
|
||||||
|
# Normally only the application should use the Host header but since the
|
||||||
|
# WSGI spec doesn't support unix sockets, we are using it to create
|
||||||
|
# viable SERVER_* if possible.
|
||||||
|
if isinstance(server, string_types):
|
||||||
|
server = server.split(":")
|
||||||
|
if len(server) == 1:
|
||||||
|
# unix socket
|
||||||
|
if host:
|
||||||
|
server = host.split(':')
|
||||||
|
if len(server) == 1:
|
||||||
|
if req.scheme == "http":
|
||||||
|
server.append(80)
|
||||||
|
elif req.scheme == "https":
|
||||||
|
server.append(443)
|
||||||
|
else:
|
||||||
|
server.append('')
|
||||||
|
else:
|
||||||
|
# no host header given which means that we are not behind a
|
||||||
|
# proxy, so append an empty port.
|
||||||
|
server.append('')
|
||||||
|
environ['SERVER_NAME'] = server[0]
|
||||||
|
environ['SERVER_PORT'] = str(server[1])
|
||||||
|
|
||||||
|
# set the path and script name
|
||||||
|
path_info = req.path
|
||||||
|
if script_name:
|
||||||
|
path_info = path_info.split(script_name, 1)[1]
|
||||||
|
environ['PATH_INFO'] = unquote_to_wsgi_str(path_info)
|
||||||
|
environ['SCRIPT_NAME'] = script_name
|
||||||
|
|
||||||
|
# override the environ with the correct remote and server address if
|
||||||
|
# we are behind a proxy using the proxy protocol.
|
||||||
|
environ.update(proxy_environ(req))
|
||||||
|
return resp, environ
|
||||||
|
|
||||||
|
|
||||||
|
class Response(object):
|
||||||
|
|
||||||
|
def __init__(self, req, sock, cfg):
|
||||||
|
self.req = req
|
||||||
|
self.sock = sock
|
||||||
|
self.version = SERVER_SOFTWARE
|
||||||
|
self.status = None
|
||||||
|
self.chunked = False
|
||||||
|
self.must_close = False
|
||||||
|
self.headers = []
|
||||||
|
self.headers_sent = False
|
||||||
|
self.response_length = None
|
||||||
|
self.sent = 0
|
||||||
|
self.upgrade = False
|
||||||
|
self.cfg = cfg
|
||||||
|
|
||||||
|
def force_close(self):
|
||||||
|
self.must_close = True
|
||||||
|
|
||||||
|
def should_close(self):
|
||||||
|
if self.must_close or self.req.should_close():
|
||||||
|
return True
|
||||||
|
if self.response_length is not None or self.chunked:
|
||||||
|
return False
|
||||||
|
if self.req.method == 'HEAD':
|
||||||
|
return False
|
||||||
|
if self.status_code < 200 or self.status_code in (204, 304):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def start_response(self, status, headers, exc_info=None):
|
||||||
|
if exc_info:
|
||||||
|
try:
|
||||||
|
if self.status and self.headers_sent:
|
||||||
|
reraise(exc_info[0], exc_info[1], exc_info[2])
|
||||||
|
finally:
|
||||||
|
exc_info = None
|
||||||
|
elif self.status is not None:
|
||||||
|
raise AssertionError("Response headers already set!")
|
||||||
|
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
# get the status code from the response here so we can use it to check
|
||||||
|
# the need for the connection header later without parsing the string
|
||||||
|
# each time.
|
||||||
|
try:
|
||||||
|
self.status_code = int(self.status.split()[0])
|
||||||
|
except ValueError:
|
||||||
|
self.status_code = None
|
||||||
|
|
||||||
|
self.process_headers(headers)
|
||||||
|
self.chunked = self.is_chunked()
|
||||||
|
return self.write
|
||||||
|
|
||||||
|
def process_headers(self, headers):
|
||||||
|
for name, value in headers:
|
||||||
|
if not isinstance(name, string_types):
|
||||||
|
raise TypeError('%r is not a string' % name)
|
||||||
|
|
||||||
|
if HEADER_RE.search(name):
|
||||||
|
raise InvalidHeaderName('%r' % name)
|
||||||
|
|
||||||
|
if HEADER_VALUE_RE.search(value):
|
||||||
|
raise InvalidHeader('%r' % value)
|
||||||
|
|
||||||
|
value = str(value).strip()
|
||||||
|
lname = name.lower().strip()
|
||||||
|
if lname == "content-length":
|
||||||
|
self.response_length = int(value)
|
||||||
|
elif util.is_hoppish(name):
|
||||||
|
if lname == "connection":
|
||||||
|
# handle websocket
|
||||||
|
if value.lower().strip() == "upgrade":
|
||||||
|
self.upgrade = True
|
||||||
|
elif lname == "upgrade":
|
||||||
|
if value.lower().strip() == "websocket":
|
||||||
|
self.headers.append((name.strip(), value))
|
||||||
|
|
||||||
|
# ignore hopbyhop headers
|
||||||
|
continue
|
||||||
|
self.headers.append((name.strip(), value))
|
||||||
|
|
||||||
|
def is_chunked(self):
|
||||||
|
# Only use chunked responses when the client is
|
||||||
|
# speaking HTTP/1.1 or newer and there was
|
||||||
|
# no Content-Length header set.
|
||||||
|
if self.response_length is not None:
|
||||||
|
return False
|
||||||
|
elif self.req.version <= (1, 0):
|
||||||
|
return False
|
||||||
|
elif self.req.method == 'HEAD':
|
||||||
|
# Responses to a HEAD request MUST NOT contain a response body.
|
||||||
|
return False
|
||||||
|
elif self.status_code in (204, 304):
|
||||||
|
# Do not use chunked responses when the response is guaranteed to
|
||||||
|
# not have a response body.
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def default_headers(self):
|
||||||
|
# set the connection header
|
||||||
|
if self.upgrade:
|
||||||
|
connection = "upgrade"
|
||||||
|
elif self.should_close():
|
||||||
|
connection = "close"
|
||||||
|
else:
|
||||||
|
connection = "keep-alive"
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
"HTTP/%s.%s %s\r\n" % (self.req.version[0],
|
||||||
|
self.req.version[1], self.status),
|
||||||
|
"Server: %s\r\n" % self.version,
|
||||||
|
"Date: %s\r\n" % util.http_date(),
|
||||||
|
"Connection: %s\r\n" % connection
|
||||||
|
]
|
||||||
|
if self.chunked:
|
||||||
|
headers.append("Transfer-Encoding: chunked\r\n")
|
||||||
|
return headers
|
||||||
|
|
||||||
|
def send_headers(self):
|
||||||
|
if self.headers_sent:
|
||||||
|
return
|
||||||
|
tosend = self.default_headers()
|
||||||
|
tosend.extend(["%s: %s\r\n" % (k, v) for k, v in self.headers])
|
||||||
|
|
||||||
|
header_str = "%s\r\n" % "".join(tosend)
|
||||||
|
util.write(self.sock, util.to_bytestring(header_str, "ascii"))
|
||||||
|
self.headers_sent = True
|
||||||
|
|
||||||
|
def write(self, arg):
|
||||||
|
self.send_headers()
|
||||||
|
if not isinstance(arg, binary_type):
|
||||||
|
raise TypeError('%r is not a byte' % arg)
|
||||||
|
arglen = len(arg)
|
||||||
|
tosend = arglen
|
||||||
|
if self.response_length is not None:
|
||||||
|
if self.sent >= self.response_length:
|
||||||
|
# Never write more than self.response_length bytes
|
||||||
|
return
|
||||||
|
|
||||||
|
tosend = min(self.response_length - self.sent, tosend)
|
||||||
|
if tosend < arglen:
|
||||||
|
arg = arg[:tosend]
|
||||||
|
|
||||||
|
# Sending an empty chunk signals the end of the
|
||||||
|
# response and prematurely closes the response
|
||||||
|
if self.chunked and tosend == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.sent += tosend
|
||||||
|
util.write(self.sock, arg, self.chunked)
|
||||||
|
|
||||||
|
def can_sendfile(self):
|
||||||
|
return self.cfg.sendfile is not False and sendfile is not None
|
||||||
|
|
||||||
|
def sendfile(self, respiter):
|
||||||
|
if self.cfg.is_ssl or not self.can_sendfile():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not util.has_fileno(respiter.filelike):
|
||||||
|
return False
|
||||||
|
|
||||||
|
fileno = respiter.filelike.fileno()
|
||||||
|
try:
|
||||||
|
offset = os.lseek(fileno, 0, os.SEEK_CUR)
|
||||||
|
if self.response_length is None:
|
||||||
|
filesize = os.fstat(fileno).st_size
|
||||||
|
|
||||||
|
# The file may be special and sendfile will fail.
|
||||||
|
# It may also be zero-length, but that is okay.
|
||||||
|
if filesize == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
nbytes = filesize - offset
|
||||||
|
else:
|
||||||
|
nbytes = self.response_length
|
||||||
|
except (OSError, io.UnsupportedOperation):
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.send_headers()
|
||||||
|
|
||||||
|
if self.is_chunked():
|
||||||
|
chunk_size = "%X\r\n" % nbytes
|
||||||
|
self.sock.sendall(chunk_size.encode('utf-8'))
|
||||||
|
|
||||||
|
sockno = self.sock.fileno()
|
||||||
|
sent = 0
|
||||||
|
|
||||||
|
while sent != nbytes:
|
||||||
|
count = min(nbytes - sent, BLKSIZE)
|
||||||
|
sent += sendfile(sockno, fileno, offset + sent, count)
|
||||||
|
|
||||||
|
if self.is_chunked():
|
||||||
|
self.sock.sendall(b"\r\n")
|
||||||
|
|
||||||
|
os.lseek(fileno, offset, os.SEEK_SET)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def write_file(self, respiter):
|
||||||
|
if not self.sendfile(respiter):
|
||||||
|
for item in respiter:
|
||||||
|
self.write(item)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if not self.headers_sent:
|
||||||
|
self.send_headers()
|
||||||
|
if self.chunked:
|
||||||
|
util.write_chunk(self.sock, b"")
|
Binary file not shown.
Binary file not shown.
123
venv/lib/python3.6/site-packages/gunicorn/instrument/statsd.py
Normal file
123
venv/lib/python3.6/site-packages/gunicorn/instrument/statsd.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
"Bare-bones implementation of statsD's protocol, client-side"
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import logging
|
||||||
|
from re import sub
|
||||||
|
|
||||||
|
from gunicorn.glogging import Logger
|
||||||
|
from gunicorn import six
|
||||||
|
|
||||||
|
# Instrumentation constants
|
||||||
|
METRIC_VAR = "metric"
|
||||||
|
VALUE_VAR = "value"
|
||||||
|
MTYPE_VAR = "mtype"
|
||||||
|
GAUGE_TYPE = "gauge"
|
||||||
|
COUNTER_TYPE = "counter"
|
||||||
|
HISTOGRAM_TYPE = "histogram"
|
||||||
|
|
||||||
|
class Statsd(Logger):
|
||||||
|
"""statsD-based instrumentation, that passes as a logger
|
||||||
|
"""
|
||||||
|
def __init__(self, cfg):
|
||||||
|
"""host, port: statsD server
|
||||||
|
"""
|
||||||
|
Logger.__init__(self, cfg)
|
||||||
|
self.prefix = sub(r"^(.+[^.]+)\.*$", "\\g<1>.", cfg.statsd_prefix)
|
||||||
|
try:
|
||||||
|
host, port = cfg.statsd_host
|
||||||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
self.sock.connect((host, int(port)))
|
||||||
|
except Exception:
|
||||||
|
self.sock = None
|
||||||
|
|
||||||
|
# Log errors and warnings
|
||||||
|
def critical(self, msg, *args, **kwargs):
|
||||||
|
Logger.critical(self, msg, *args, **kwargs)
|
||||||
|
self.increment("gunicorn.log.critical", 1)
|
||||||
|
|
||||||
|
def error(self, msg, *args, **kwargs):
|
||||||
|
Logger.error(self, msg, *args, **kwargs)
|
||||||
|
self.increment("gunicorn.log.error", 1)
|
||||||
|
|
||||||
|
def warning(self, msg, *args, **kwargs):
|
||||||
|
Logger.warning(self, msg, *args, **kwargs)
|
||||||
|
self.increment("gunicorn.log.warning", 1)
|
||||||
|
|
||||||
|
def exception(self, msg, *args, **kwargs):
|
||||||
|
Logger.exception(self, msg, *args, **kwargs)
|
||||||
|
self.increment("gunicorn.log.exception", 1)
|
||||||
|
|
||||||
|
# Special treatement for info, the most common log level
|
||||||
|
def info(self, msg, *args, **kwargs):
|
||||||
|
self.log(logging.INFO, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
# skip the run-of-the-mill logs
|
||||||
|
def debug(self, msg, *args, **kwargs):
|
||||||
|
self.log(logging.DEBUG, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def log(self, lvl, msg, *args, **kwargs):
|
||||||
|
"""Log a given statistic if metric, value and type are present
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
extra = kwargs.get("extra", None)
|
||||||
|
if extra is not None:
|
||||||
|
metric = extra.get(METRIC_VAR, None)
|
||||||
|
value = extra.get(VALUE_VAR, None)
|
||||||
|
typ = extra.get(MTYPE_VAR, None)
|
||||||
|
if metric and value and typ:
|
||||||
|
if typ == GAUGE_TYPE:
|
||||||
|
self.gauge(metric, value)
|
||||||
|
elif typ == COUNTER_TYPE:
|
||||||
|
self.increment(metric, value)
|
||||||
|
elif typ == HISTOGRAM_TYPE:
|
||||||
|
self.histogram(metric, value)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Log to parent logger only if there is something to say
|
||||||
|
if msg:
|
||||||
|
Logger.log(self, lvl, msg, *args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
Logger.warning(self, "Failed to log to statsd", exc_info=True)
|
||||||
|
|
||||||
|
# access logging
|
||||||
|
def access(self, resp, req, environ, request_time):
|
||||||
|
"""Measure request duration
|
||||||
|
request_time is a datetime.timedelta
|
||||||
|
"""
|
||||||
|
Logger.access(self, resp, req, environ, request_time)
|
||||||
|
duration_in_ms = request_time.seconds * 1000 + float(request_time.microseconds) / 10 ** 3
|
||||||
|
status = resp.status
|
||||||
|
if isinstance(status, str):
|
||||||
|
status = int(status.split(None, 1)[0])
|
||||||
|
self.histogram("gunicorn.request.duration", duration_in_ms)
|
||||||
|
self.increment("gunicorn.requests", 1)
|
||||||
|
self.increment("gunicorn.request.status.%d" % status, 1)
|
||||||
|
|
||||||
|
# statsD methods
|
||||||
|
# you can use those directly if you want
|
||||||
|
def gauge(self, name, value):
|
||||||
|
self._sock_send("{0}{1}:{2}|g".format(self.prefix, name, value))
|
||||||
|
|
||||||
|
def increment(self, name, value, sampling_rate=1.0):
|
||||||
|
self._sock_send("{0}{1}:{2}|c|@{3}".format(self.prefix, name, value, sampling_rate))
|
||||||
|
|
||||||
|
def decrement(self, name, value, sampling_rate=1.0):
|
||||||
|
self._sock_send("{0}{1}:-{2}|c|@{3}".format(self.prefix, name, value, sampling_rate))
|
||||||
|
|
||||||
|
def histogram(self, name, value):
|
||||||
|
self._sock_send("{0}{1}:{2}|ms".format(self.prefix, name, value))
|
||||||
|
|
||||||
|
def _sock_send(self, msg):
|
||||||
|
try:
|
||||||
|
if isinstance(msg, six.text_type):
|
||||||
|
msg = msg.encode("ascii")
|
||||||
|
if self.sock:
|
||||||
|
self.sock.send(msg)
|
||||||
|
except Exception:
|
||||||
|
Logger.warning(self, "Error sending message to statsd", exc_info=True)
|
86
venv/lib/python3.6/site-packages/gunicorn/pidfile.py
Normal file
86
venv/lib/python3.6/site-packages/gunicorn/pidfile.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
class Pidfile(object):
|
||||||
|
"""\
|
||||||
|
Manage a PID file. If a specific name is provided
|
||||||
|
it and '"%s.oldpid" % name' will be used. Otherwise
|
||||||
|
we create a temp file using os.mkstemp.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, fname):
|
||||||
|
self.fname = fname
|
||||||
|
self.pid = None
|
||||||
|
|
||||||
|
def create(self, pid):
|
||||||
|
oldpid = self.validate()
|
||||||
|
if oldpid:
|
||||||
|
if oldpid == os.getpid():
|
||||||
|
return
|
||||||
|
msg = "Already running on PID %s (or pid file '%s' is stale)"
|
||||||
|
raise RuntimeError(msg % (oldpid, self.fname))
|
||||||
|
|
||||||
|
self.pid = pid
|
||||||
|
|
||||||
|
# Write pidfile
|
||||||
|
fdir = os.path.dirname(self.fname)
|
||||||
|
if fdir and not os.path.isdir(fdir):
|
||||||
|
raise RuntimeError("%s doesn't exist. Can't create pidfile." % fdir)
|
||||||
|
fd, fname = tempfile.mkstemp(dir=fdir)
|
||||||
|
os.write(fd, ("%s\n" % self.pid).encode('utf-8'))
|
||||||
|
if self.fname:
|
||||||
|
os.rename(fname, self.fname)
|
||||||
|
else:
|
||||||
|
self.fname = fname
|
||||||
|
os.close(fd)
|
||||||
|
|
||||||
|
# set permissions to -rw-r--r--
|
||||||
|
os.chmod(self.fname, 420)
|
||||||
|
|
||||||
|
def rename(self, path):
|
||||||
|
self.unlink()
|
||||||
|
self.fname = path
|
||||||
|
self.create(self.pid)
|
||||||
|
|
||||||
|
def unlink(self):
|
||||||
|
""" delete pidfile"""
|
||||||
|
try:
|
||||||
|
with open(self.fname, "r") as f:
|
||||||
|
pid1 = int(f.read() or 0)
|
||||||
|
|
||||||
|
if pid1 == self.pid:
|
||||||
|
os.unlink(self.fname)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
""" Validate pidfile and make it stale if needed"""
|
||||||
|
if not self.fname:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(self.fname, "r") as f:
|
||||||
|
try:
|
||||||
|
wpid = int(f.read())
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.kill(wpid, 0)
|
||||||
|
return wpid
|
||||||
|
except OSError as e:
|
||||||
|
if e.args[0] == errno.EPERM:
|
||||||
|
return wpid
|
||||||
|
if e.args[0] == errno.ESRCH:
|
||||||
|
return
|
||||||
|
raise
|
||||||
|
except IOError as e:
|
||||||
|
if e.args[0] == errno.ENOENT:
|
||||||
|
return
|
||||||
|
raise
|
132
venv/lib/python3.6/site-packages/gunicorn/reloader.py
Normal file
132
venv/lib/python3.6/site-packages/gunicorn/reloader.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
COMPILED_EXT_RE = re.compile(r'py[co]$')
|
||||||
|
|
||||||
|
|
||||||
|
class Reloader(threading.Thread):
|
||||||
|
def __init__(self, extra_files=None, interval=1, callback=None):
|
||||||
|
super(Reloader, self).__init__()
|
||||||
|
self.setDaemon(True)
|
||||||
|
self._extra_files = set(extra_files or ())
|
||||||
|
self._extra_files_lock = threading.RLock()
|
||||||
|
self._interval = interval
|
||||||
|
self._callback = callback
|
||||||
|
|
||||||
|
def add_extra_file(self, filename):
|
||||||
|
with self._extra_files_lock:
|
||||||
|
self._extra_files.add(filename)
|
||||||
|
|
||||||
|
def get_files(self):
|
||||||
|
fnames = [
|
||||||
|
COMPILED_EXT_RE.sub('py', module.__file__)
|
||||||
|
for module in tuple(sys.modules.values())
|
||||||
|
if getattr(module, '__file__', None)
|
||||||
|
]
|
||||||
|
|
||||||
|
with self._extra_files_lock:
|
||||||
|
fnames.extend(self._extra_files)
|
||||||
|
|
||||||
|
return fnames
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
mtimes = {}
|
||||||
|
while True:
|
||||||
|
for filename in self.get_files():
|
||||||
|
try:
|
||||||
|
mtime = os.stat(filename).st_mtime
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
old_time = mtimes.get(filename)
|
||||||
|
if old_time is None:
|
||||||
|
mtimes[filename] = mtime
|
||||||
|
continue
|
||||||
|
elif mtime > old_time:
|
||||||
|
if self._callback:
|
||||||
|
self._callback(filename)
|
||||||
|
time.sleep(self._interval)
|
||||||
|
|
||||||
|
has_inotify = False
|
||||||
|
if sys.platform.startswith('linux'):
|
||||||
|
try:
|
||||||
|
from inotify.adapters import Inotify
|
||||||
|
import inotify.constants
|
||||||
|
has_inotify = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if has_inotify:
|
||||||
|
|
||||||
|
class InotifyReloader(threading.Thread):
|
||||||
|
event_mask = (inotify.constants.IN_CREATE | inotify.constants.IN_DELETE
|
||||||
|
| inotify.constants.IN_DELETE_SELF | inotify.constants.IN_MODIFY
|
||||||
|
| inotify.constants.IN_MOVE_SELF | inotify.constants.IN_MOVED_FROM
|
||||||
|
| inotify.constants.IN_MOVED_TO)
|
||||||
|
|
||||||
|
def __init__(self, extra_files=None, callback=None):
|
||||||
|
super(InotifyReloader, self).__init__()
|
||||||
|
self.setDaemon(True)
|
||||||
|
self._callback = callback
|
||||||
|
self._dirs = set()
|
||||||
|
self._watcher = Inotify()
|
||||||
|
|
||||||
|
for extra_file in extra_files:
|
||||||
|
self.add_extra_file(extra_file)
|
||||||
|
|
||||||
|
def add_extra_file(self, filename):
|
||||||
|
dirname = os.path.dirname(filename)
|
||||||
|
|
||||||
|
if dirname in self._dirs:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._watcher.add_watch(dirname, mask=self.event_mask)
|
||||||
|
self._dirs.add(dirname)
|
||||||
|
|
||||||
|
def get_dirs(self):
|
||||||
|
fnames = [
|
||||||
|
os.path.dirname(COMPILED_EXT_RE.sub('py', module.__file__))
|
||||||
|
for module in tuple(sys.modules.values())
|
||||||
|
if hasattr(module, '__file__')
|
||||||
|
]
|
||||||
|
|
||||||
|
return set(fnames)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._dirs = self.get_dirs()
|
||||||
|
|
||||||
|
for dirname in self._dirs:
|
||||||
|
self._watcher.add_watch(dirname, mask=self.event_mask)
|
||||||
|
|
||||||
|
for event in self._watcher.event_gen():
|
||||||
|
if event is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
filename = event[3]
|
||||||
|
|
||||||
|
self._callback(filename)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class InotifyReloader(object):
|
||||||
|
def __init__(self, callback=None):
|
||||||
|
raise ImportError('You must have the inotify module installed to '
|
||||||
|
'use the inotify reloader')
|
||||||
|
|
||||||
|
|
||||||
|
preferred_reloader = InotifyReloader if has_inotify else Reloader
|
||||||
|
|
||||||
|
reloader_engines = {
|
||||||
|
'auto': preferred_reloader,
|
||||||
|
'poll': Reloader,
|
||||||
|
'inotify': InotifyReloader,
|
||||||
|
}
|
592
venv/lib/python3.6/site-packages/gunicorn/selectors.py
Normal file
592
venv/lib/python3.6/site-packages/gunicorn/selectors.py
Normal file
@ -0,0 +1,592 @@
|
|||||||
|
"""Selectors module.
|
||||||
|
|
||||||
|
This module allows high-level and efficient I/O multiplexing, built upon the
|
||||||
|
`select` module primitives.
|
||||||
|
|
||||||
|
The following code adapted from trollius.selectors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
from collections import namedtuple, Mapping
|
||||||
|
import math
|
||||||
|
import select
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from gunicorn._compat import wrap_error, InterruptedError
|
||||||
|
from gunicorn import six
|
||||||
|
|
||||||
|
|
||||||
|
# generic events, that must be mapped to implementation-specific ones
|
||||||
|
EVENT_READ = (1 << 0)
|
||||||
|
EVENT_WRITE = (1 << 1)
|
||||||
|
|
||||||
|
|
||||||
|
def _fileobj_to_fd(fileobj):
|
||||||
|
"""Return a file descriptor from a file object.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
fileobj -- file object or file descriptor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
corresponding file descriptor
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError if the object is invalid
|
||||||
|
"""
|
||||||
|
if isinstance(fileobj, six.integer_types):
|
||||||
|
fd = fileobj
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
fd = int(fileobj.fileno())
|
||||||
|
except (AttributeError, TypeError, ValueError):
|
||||||
|
raise ValueError("Invalid file object: "
|
||||||
|
"{0!r}".format(fileobj))
|
||||||
|
if fd < 0:
|
||||||
|
raise ValueError("Invalid file descriptor: {0}".format(fd))
|
||||||
|
return fd
|
||||||
|
|
||||||
|
|
||||||
|
SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
|
||||||
|
"""Object used to associate a file object to its backing file descriptor,
|
||||||
|
selected event mask and attached data."""
|
||||||
|
|
||||||
|
|
||||||
|
class _SelectorMapping(Mapping):
|
||||||
|
"""Mapping of file objects to selector keys."""
|
||||||
|
|
||||||
|
def __init__(self, selector):
|
||||||
|
self._selector = selector
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._selector._fd_to_key)
|
||||||
|
|
||||||
|
def __getitem__(self, fileobj):
|
||||||
|
try:
|
||||||
|
fd = self._selector._fileobj_lookup(fileobj)
|
||||||
|
return self._selector._fd_to_key[fd]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError("{0!r} is not registered".format(fileobj))
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._selector._fd_to_key)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSelector(six.with_metaclass(ABCMeta)):
|
||||||
|
"""Selector abstract base class.
|
||||||
|
|
||||||
|
A selector supports registering file objects to be monitored for specific
|
||||||
|
I/O events.
|
||||||
|
|
||||||
|
A file object is a file descriptor or any object with a `fileno()` method.
|
||||||
|
An arbitrary object can be attached to the file object, which can be used
|
||||||
|
for example to store context information, a callback, etc.
|
||||||
|
|
||||||
|
A selector can use various implementations (select(), poll(), epoll()...)
|
||||||
|
depending on the platform. The default `Selector` class uses the most
|
||||||
|
efficient implementation on the current platform.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
"""Register a file object.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
fileobj -- file object or file descriptor
|
||||||
|
events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
|
||||||
|
data -- attached data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SelectorKey instance
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError if events is invalid
|
||||||
|
KeyError if fileobj is already registered
|
||||||
|
OSError if fileobj is closed or otherwise is unacceptable to
|
||||||
|
the underlying system call (if a system call is made)
|
||||||
|
|
||||||
|
Note:
|
||||||
|
OSError may or may not be raised
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
"""Unregister a file object.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
fileobj -- file object or file descriptor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SelectorKey instance
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
KeyError if fileobj is not registered
|
||||||
|
|
||||||
|
Note:
|
||||||
|
If fileobj is registered but has since been closed this does
|
||||||
|
*not* raise OSError (even if the wrapped syscall does)
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def modify(self, fileobj, events, data=None):
|
||||||
|
"""Change a registered file object monitored events or attached data.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
fileobj -- file object or file descriptor
|
||||||
|
events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
|
||||||
|
data -- attached data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SelectorKey instance
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Anything that unregister() or register() raises
|
||||||
|
"""
|
||||||
|
self.unregister(fileobj)
|
||||||
|
return self.register(fileobj, events, data)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def select(self, timeout=None):
|
||||||
|
"""Perform the actual selection, until some monitored file objects are
|
||||||
|
ready or a timeout expires.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
timeout -- if timeout > 0, this specifies the maximum wait time, in
|
||||||
|
seconds
|
||||||
|
if timeout <= 0, the select() call won't block, and will
|
||||||
|
report the currently ready file objects
|
||||||
|
if timeout is None, select() will block until a monitored
|
||||||
|
file object becomes ready
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list of (key, events) for ready file objects
|
||||||
|
`events` is a bitwise mask of EVENT_READ|EVENT_WRITE
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Close the selector.
|
||||||
|
|
||||||
|
This must be called to make sure that any underlying resource is freed.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_key(self, fileobj):
|
||||||
|
"""Return the key associated to a registered file object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SelectorKey for this file object
|
||||||
|
"""
|
||||||
|
mapping = self.get_map()
|
||||||
|
try:
|
||||||
|
return mapping[fileobj]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError("{0!r} is not registered".format(fileobj))
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_map(self):
|
||||||
|
"""Return a mapping of file objects to selector keys."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class _BaseSelectorImpl(BaseSelector):
|
||||||
|
"""Base selector implementation."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# this maps file descriptors to keys
|
||||||
|
self._fd_to_key = {}
|
||||||
|
# read-only mapping returned by get_map()
|
||||||
|
self._map = _SelectorMapping(self)
|
||||||
|
|
||||||
|
def _fileobj_lookup(self, fileobj):
|
||||||
|
"""Return a file descriptor from a file object.
|
||||||
|
|
||||||
|
This wraps _fileobj_to_fd() to do an exhaustive search in case
|
||||||
|
the object is invalid but we still have it in our map. This
|
||||||
|
is used by unregister() so we can unregister an object that
|
||||||
|
was previously registered even if it is closed. It is also
|
||||||
|
used by _SelectorMapping.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return _fileobj_to_fd(fileobj)
|
||||||
|
except ValueError:
|
||||||
|
# Do an exhaustive search.
|
||||||
|
for key in self._fd_to_key.values():
|
||||||
|
if key.fileobj is fileobj:
|
||||||
|
return key.fd
|
||||||
|
# Raise ValueError after all.
|
||||||
|
raise
|
||||||
|
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
|
||||||
|
raise ValueError("Invalid events: {0!r}".format(events))
|
||||||
|
|
||||||
|
key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
|
||||||
|
|
||||||
|
if key.fd in self._fd_to_key:
|
||||||
|
raise KeyError("{0!r} (FD {1}) is already registered"
|
||||||
|
.format(fileobj, key.fd))
|
||||||
|
|
||||||
|
self._fd_to_key[key.fd] = key
|
||||||
|
return key
|
||||||
|
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
try:
|
||||||
|
key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError("{0!r} is not registered".format(fileobj))
|
||||||
|
return key
|
||||||
|
|
||||||
|
def modify(self, fileobj, events, data=None):
|
||||||
|
# TODO: Subclasses can probably optimize this even further.
|
||||||
|
try:
|
||||||
|
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError("{0!r} is not registered".format(fileobj))
|
||||||
|
if events != key.events:
|
||||||
|
self.unregister(fileobj)
|
||||||
|
key = self.register(fileobj, events, data)
|
||||||
|
elif data != key.data:
|
||||||
|
# Use a shortcut to update the data.
|
||||||
|
key = key._replace(data=data)
|
||||||
|
self._fd_to_key[key.fd] = key
|
||||||
|
return key
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._fd_to_key.clear()
|
||||||
|
|
||||||
|
def get_map(self):
|
||||||
|
return self._map
|
||||||
|
|
||||||
|
def _key_from_fd(self, fd):
|
||||||
|
"""Return the key associated to a given file descriptor.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
fd -- file descriptor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
corresponding key, or None if not found
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._fd_to_key[fd]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class SelectSelector(_BaseSelectorImpl):
|
||||||
|
"""Select-based selector."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(SelectSelector, self).__init__()
|
||||||
|
self._readers = set()
|
||||||
|
self._writers = set()
|
||||||
|
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
key = super(SelectSelector, self).register(fileobj, events, data)
|
||||||
|
if events & EVENT_READ:
|
||||||
|
self._readers.add(key.fd)
|
||||||
|
if events & EVENT_WRITE:
|
||||||
|
self._writers.add(key.fd)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
key = super(SelectSelector, self).unregister(fileobj)
|
||||||
|
self._readers.discard(key.fd)
|
||||||
|
self._writers.discard(key.fd)
|
||||||
|
return key
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
def _select(self, r, w, _, timeout=None):
|
||||||
|
r, w, x = select.select(r, w, w, timeout)
|
||||||
|
return r, w + x, []
|
||||||
|
else:
|
||||||
|
_select = select.select
|
||||||
|
|
||||||
|
def select(self, timeout=None):
|
||||||
|
timeout = None if timeout is None else max(timeout, 0)
|
||||||
|
ready = []
|
||||||
|
try:
|
||||||
|
r, w, _ = wrap_error(self._select,
|
||||||
|
self._readers, self._writers, [], timeout)
|
||||||
|
except InterruptedError:
|
||||||
|
return ready
|
||||||
|
r = set(r)
|
||||||
|
w = set(w)
|
||||||
|
for fd in r | w:
|
||||||
|
events = 0
|
||||||
|
if fd in r:
|
||||||
|
events |= EVENT_READ
|
||||||
|
if fd in w:
|
||||||
|
events |= EVENT_WRITE
|
||||||
|
|
||||||
|
key = self._key_from_fd(fd)
|
||||||
|
if key:
|
||||||
|
ready.append((key, events & key.events))
|
||||||
|
return ready
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(select, 'poll'):
|
||||||
|
|
||||||
|
class PollSelector(_BaseSelectorImpl):
|
||||||
|
"""Poll-based selector."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(PollSelector, self).__init__()
|
||||||
|
self._poll = select.poll()
|
||||||
|
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
key = super(PollSelector, self).register(fileobj, events, data)
|
||||||
|
poll_events = 0
|
||||||
|
if events & EVENT_READ:
|
||||||
|
poll_events |= select.POLLIN
|
||||||
|
if events & EVENT_WRITE:
|
||||||
|
poll_events |= select.POLLOUT
|
||||||
|
self._poll.register(key.fd, poll_events)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
key = super(PollSelector, self).unregister(fileobj)
|
||||||
|
self._poll.unregister(key.fd)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def select(self, timeout=None):
|
||||||
|
if timeout is None:
|
||||||
|
timeout = None
|
||||||
|
elif timeout <= 0:
|
||||||
|
timeout = 0
|
||||||
|
else:
|
||||||
|
# poll() has a resolution of 1 millisecond, round away from
|
||||||
|
# zero to wait *at least* timeout seconds.
|
||||||
|
timeout = int(math.ceil(timeout * 1e3))
|
||||||
|
ready = []
|
||||||
|
try:
|
||||||
|
fd_event_list = wrap_error(self._poll.poll, timeout)
|
||||||
|
except InterruptedError:
|
||||||
|
return ready
|
||||||
|
for fd, event in fd_event_list:
|
||||||
|
events = 0
|
||||||
|
if event & ~select.POLLIN:
|
||||||
|
events |= EVENT_WRITE
|
||||||
|
if event & ~select.POLLOUT:
|
||||||
|
events |= EVENT_READ
|
||||||
|
|
||||||
|
key = self._key_from_fd(fd)
|
||||||
|
if key:
|
||||||
|
ready.append((key, events & key.events))
|
||||||
|
return ready
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(select, 'epoll'):
|
||||||
|
|
||||||
|
class EpollSelector(_BaseSelectorImpl):
|
||||||
|
"""Epoll-based selector."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(EpollSelector, self).__init__()
|
||||||
|
self._epoll = select.epoll()
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
return self._epoll.fileno()
|
||||||
|
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
key = super(EpollSelector, self).register(fileobj, events, data)
|
||||||
|
epoll_events = 0
|
||||||
|
if events & EVENT_READ:
|
||||||
|
epoll_events |= select.EPOLLIN
|
||||||
|
if events & EVENT_WRITE:
|
||||||
|
epoll_events |= select.EPOLLOUT
|
||||||
|
self._epoll.register(key.fd, epoll_events)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
key = super(EpollSelector, self).unregister(fileobj)
|
||||||
|
try:
|
||||||
|
self._epoll.unregister(key.fd)
|
||||||
|
except OSError:
|
||||||
|
# This can happen if the FD was closed since it
|
||||||
|
# was registered.
|
||||||
|
pass
|
||||||
|
return key
|
||||||
|
|
||||||
|
def select(self, timeout=None):
|
||||||
|
if timeout is None:
|
||||||
|
timeout = -1
|
||||||
|
elif timeout <= 0:
|
||||||
|
timeout = 0
|
||||||
|
else:
|
||||||
|
# epoll_wait() has a resolution of 1 millisecond, round away
|
||||||
|
# from zero to wait *at least* timeout seconds.
|
||||||
|
timeout = math.ceil(timeout * 1e3) * 1e-3
|
||||||
|
max_ev = len(self._fd_to_key)
|
||||||
|
ready = []
|
||||||
|
try:
|
||||||
|
fd_event_list = wrap_error(self._epoll.poll, timeout, max_ev)
|
||||||
|
except InterruptedError:
|
||||||
|
return ready
|
||||||
|
for fd, event in fd_event_list:
|
||||||
|
events = 0
|
||||||
|
if event & ~select.EPOLLIN:
|
||||||
|
events |= EVENT_WRITE
|
||||||
|
if event & ~select.EPOLLOUT:
|
||||||
|
events |= EVENT_READ
|
||||||
|
|
||||||
|
key = self._key_from_fd(fd)
|
||||||
|
if key:
|
||||||
|
ready.append((key, events & key.events))
|
||||||
|
return ready
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._epoll.close()
|
||||||
|
super(EpollSelector, self).close()
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(select, 'devpoll'):
|
||||||
|
|
||||||
|
class DevpollSelector(_BaseSelectorImpl):
|
||||||
|
"""Solaris /dev/poll selector."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(DevpollSelector, self).__init__()
|
||||||
|
self._devpoll = select.devpoll()
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
return self._devpoll.fileno()
|
||||||
|
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
key = super(DevpollSelector, self).register(fileobj, events, data)
|
||||||
|
poll_events = 0
|
||||||
|
if events & EVENT_READ:
|
||||||
|
poll_events |= select.POLLIN
|
||||||
|
if events & EVENT_WRITE:
|
||||||
|
poll_events |= select.POLLOUT
|
||||||
|
self._devpoll.register(key.fd, poll_events)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
key = super(DevpollSelector, self).unregister(fileobj)
|
||||||
|
self._devpoll.unregister(key.fd)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def select(self, timeout=None):
|
||||||
|
if timeout is None:
|
||||||
|
timeout = None
|
||||||
|
elif timeout <= 0:
|
||||||
|
timeout = 0
|
||||||
|
else:
|
||||||
|
# devpoll() has a resolution of 1 millisecond, round away from
|
||||||
|
# zero to wait *at least* timeout seconds.
|
||||||
|
timeout = math.ceil(timeout * 1e3)
|
||||||
|
ready = []
|
||||||
|
try:
|
||||||
|
fd_event_list = self._devpoll.poll(timeout)
|
||||||
|
except InterruptedError:
|
||||||
|
return ready
|
||||||
|
for fd, event in fd_event_list:
|
||||||
|
events = 0
|
||||||
|
if event & ~select.POLLIN:
|
||||||
|
events |= EVENT_WRITE
|
||||||
|
if event & ~select.POLLOUT:
|
||||||
|
events |= EVENT_READ
|
||||||
|
|
||||||
|
key = self._key_from_fd(fd)
|
||||||
|
if key:
|
||||||
|
ready.append((key, events & key.events))
|
||||||
|
return ready
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._devpoll.close()
|
||||||
|
super(DevpollSelector, self).close()
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(select, 'kqueue'):
|
||||||
|
|
||||||
|
class KqueueSelector(_BaseSelectorImpl):
|
||||||
|
"""Kqueue-based selector."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(KqueueSelector, self).__init__()
|
||||||
|
self._kqueue = select.kqueue()
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
return self._kqueue.fileno()
|
||||||
|
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
key = super(KqueueSelector, self).register(fileobj, events, data)
|
||||||
|
if events & EVENT_READ:
|
||||||
|
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
||||||
|
select.KQ_EV_ADD)
|
||||||
|
self._kqueue.control([kev], 0, 0)
|
||||||
|
if events & EVENT_WRITE:
|
||||||
|
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
||||||
|
select.KQ_EV_ADD)
|
||||||
|
self._kqueue.control([kev], 0, 0)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
key = super(KqueueSelector, self).unregister(fileobj)
|
||||||
|
if key.events & EVENT_READ:
|
||||||
|
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
||||||
|
select.KQ_EV_DELETE)
|
||||||
|
try:
|
||||||
|
self._kqueue.control([kev], 0, 0)
|
||||||
|
except OSError:
|
||||||
|
# This can happen if the FD was closed since it
|
||||||
|
# was registered.
|
||||||
|
pass
|
||||||
|
if key.events & EVENT_WRITE:
|
||||||
|
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
||||||
|
select.KQ_EV_DELETE)
|
||||||
|
try:
|
||||||
|
self._kqueue.control([kev], 0, 0)
|
||||||
|
except OSError:
|
||||||
|
# See comment above.
|
||||||
|
pass
|
||||||
|
return key
|
||||||
|
|
||||||
|
def select(self, timeout=None):
|
||||||
|
timeout = None if timeout is None else max(timeout, 0)
|
||||||
|
max_ev = len(self._fd_to_key)
|
||||||
|
ready = []
|
||||||
|
try:
|
||||||
|
kev_list = wrap_error(self._kqueue.control,
|
||||||
|
None, max_ev, timeout)
|
||||||
|
except InterruptedError:
|
||||||
|
return ready
|
||||||
|
for kev in kev_list:
|
||||||
|
fd = kev.ident
|
||||||
|
flag = kev.filter
|
||||||
|
events = 0
|
||||||
|
if flag == select.KQ_FILTER_READ:
|
||||||
|
events |= EVENT_READ
|
||||||
|
if flag == select.KQ_FILTER_WRITE:
|
||||||
|
events |= EVENT_WRITE
|
||||||
|
|
||||||
|
key = self._key_from_fd(fd)
|
||||||
|
if key:
|
||||||
|
ready.append((key, events & key.events))
|
||||||
|
return ready
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._kqueue.close()
|
||||||
|
super(KqueueSelector, self).close()
|
||||||
|
|
||||||
|
|
||||||
|
# Choose the best implementation: roughly, epoll|kqueue|devpoll > poll > select.
|
||||||
|
# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
|
||||||
|
if 'KqueueSelector' in globals():
|
||||||
|
DefaultSelector = KqueueSelector
|
||||||
|
elif 'EpollSelector' in globals():
|
||||||
|
DefaultSelector = EpollSelector
|
||||||
|
elif 'DevpollSelector' in globals():
|
||||||
|
DefaultSelector = DevpollSelector
|
||||||
|
elif 'PollSelector' in globals():
|
||||||
|
DefaultSelector = PollSelector
|
||||||
|
else:
|
||||||
|
DefaultSelector = SelectSelector
|
762
venv/lib/python3.6/site-packages/gunicorn/six.py
Normal file
762
venv/lib/python3.6/site-packages/gunicorn/six.py
Normal file
@ -0,0 +1,762 @@
|
|||||||
|
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||||
|
|
||||||
|
# Copyright (c) 2010-2014 Benjamin Peterson
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import operator
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||||
|
__version__ = "1.8.0"
|
||||||
|
|
||||||
|
|
||||||
|
# Useful for very coarse version differentiation.
|
||||||
|
PY2 = sys.version_info[0] == 2
|
||||||
|
PY3 = sys.version_info[0] == 3
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
string_types = str,
|
||||||
|
integer_types = int,
|
||||||
|
class_types = type,
|
||||||
|
text_type = str
|
||||||
|
binary_type = bytes
|
||||||
|
|
||||||
|
MAXSIZE = sys.maxsize
|
||||||
|
else:
|
||||||
|
string_types = basestring,
|
||||||
|
integer_types = (int, long)
|
||||||
|
class_types = (type, types.ClassType)
|
||||||
|
text_type = unicode
|
||||||
|
binary_type = str
|
||||||
|
|
||||||
|
if sys.platform.startswith("java"):
|
||||||
|
# Jython always uses 32 bits.
|
||||||
|
MAXSIZE = int((1 << 31) - 1)
|
||||||
|
else:
|
||||||
|
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||||
|
class X(object):
|
||||||
|
def __len__(self):
|
||||||
|
return 1 << 31
|
||||||
|
try:
|
||||||
|
len(X())
|
||||||
|
except OverflowError:
|
||||||
|
# 32-bit
|
||||||
|
MAXSIZE = int((1 << 31) - 1)
|
||||||
|
else:
|
||||||
|
# 64-bit
|
||||||
|
MAXSIZE = int((1 << 63) - 1)
|
||||||
|
del X
|
||||||
|
|
||||||
|
|
||||||
|
def _add_doc(func, doc):
|
||||||
|
"""Add documentation to a function."""
|
||||||
|
func.__doc__ = doc
|
||||||
|
|
||||||
|
|
||||||
|
def _import_module(name):
|
||||||
|
"""Import module, returning the module after the last dot."""
|
||||||
|
__import__(name)
|
||||||
|
return sys.modules[name]
|
||||||
|
|
||||||
|
|
||||||
|
class _LazyDescr(object):
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __get__(self, obj, tp):
|
||||||
|
result = self._resolve()
|
||||||
|
setattr(obj, self.name, result) # Invokes __set__.
|
||||||
|
# This is a bit ugly, but it avoids running this again.
|
||||||
|
delattr(obj.__class__, self.name)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class MovedModule(_LazyDescr):
|
||||||
|
|
||||||
|
def __init__(self, name, old, new=None):
|
||||||
|
super(MovedModule, self).__init__(name)
|
||||||
|
if PY3:
|
||||||
|
if new is None:
|
||||||
|
new = name
|
||||||
|
self.mod = new
|
||||||
|
else:
|
||||||
|
self.mod = old
|
||||||
|
|
||||||
|
def _resolve(self):
|
||||||
|
return _import_module(self.mod)
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
_module = self._resolve()
|
||||||
|
value = getattr(_module, attr)
|
||||||
|
setattr(self, attr, value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class _LazyModule(types.ModuleType):
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
super(_LazyModule, self).__init__(name)
|
||||||
|
self.__doc__ = self.__class__.__doc__
|
||||||
|
|
||||||
|
def __dir__(self):
|
||||||
|
attrs = ["__doc__", "__name__"]
|
||||||
|
attrs += [attr.name for attr in self._moved_attributes]
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
# Subclasses should override this
|
||||||
|
_moved_attributes = []
|
||||||
|
|
||||||
|
|
||||||
|
class MovedAttribute(_LazyDescr):
|
||||||
|
|
||||||
|
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||||
|
super(MovedAttribute, self).__init__(name)
|
||||||
|
if PY3:
|
||||||
|
if new_mod is None:
|
||||||
|
new_mod = name
|
||||||
|
self.mod = new_mod
|
||||||
|
if new_attr is None:
|
||||||
|
if old_attr is None:
|
||||||
|
new_attr = name
|
||||||
|
else:
|
||||||
|
new_attr = old_attr
|
||||||
|
self.attr = new_attr
|
||||||
|
else:
|
||||||
|
self.mod = old_mod
|
||||||
|
if old_attr is None:
|
||||||
|
old_attr = name
|
||||||
|
self.attr = old_attr
|
||||||
|
|
||||||
|
def _resolve(self):
|
||||||
|
module = _import_module(self.mod)
|
||||||
|
return getattr(module, self.attr)
|
||||||
|
|
||||||
|
|
||||||
|
class _SixMetaPathImporter(object):
|
||||||
|
"""
|
||||||
|
A meta path importer to import six.moves and its submodules.
|
||||||
|
|
||||||
|
This class implements a PEP302 finder and loader. It should be compatible
|
||||||
|
with Python 2.5 and all existing versions of Python3
|
||||||
|
"""
|
||||||
|
def __init__(self, six_module_name):
|
||||||
|
self.name = six_module_name
|
||||||
|
self.known_modules = {}
|
||||||
|
|
||||||
|
def _add_module(self, mod, *fullnames):
|
||||||
|
for fullname in fullnames:
|
||||||
|
self.known_modules[self.name + "." + fullname] = mod
|
||||||
|
|
||||||
|
def _get_module(self, fullname):
|
||||||
|
return self.known_modules[self.name + "." + fullname]
|
||||||
|
|
||||||
|
def find_module(self, fullname, path=None):
|
||||||
|
if fullname in self.known_modules:
|
||||||
|
return self
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __get_module(self, fullname):
|
||||||
|
try:
|
||||||
|
return self.known_modules[fullname]
|
||||||
|
except KeyError:
|
||||||
|
raise ImportError("This loader does not know module " + fullname)
|
||||||
|
|
||||||
|
def load_module(self, fullname):
|
||||||
|
try:
|
||||||
|
# in case of a reload
|
||||||
|
return sys.modules[fullname]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
mod = self.__get_module(fullname)
|
||||||
|
if isinstance(mod, MovedModule):
|
||||||
|
mod = mod._resolve()
|
||||||
|
else:
|
||||||
|
mod.__loader__ = self
|
||||||
|
sys.modules[fullname] = mod
|
||||||
|
return mod
|
||||||
|
|
||||||
|
def is_package(self, fullname):
|
||||||
|
"""
|
||||||
|
Return true, if the named module is a package.
|
||||||
|
|
||||||
|
We need this method to get correct spec objects with
|
||||||
|
Python 3.4 (see PEP451)
|
||||||
|
"""
|
||||||
|
return hasattr(self.__get_module(fullname), "__path__")
|
||||||
|
|
||||||
|
def get_code(self, fullname):
|
||||||
|
"""Return None
|
||||||
|
|
||||||
|
Required, if is_package is implemented"""
|
||||||
|
self.__get_module(fullname) # eventually raises ImportError
|
||||||
|
return None
|
||||||
|
get_source = get_code # same as get_code
|
||||||
|
|
||||||
|
_importer = _SixMetaPathImporter(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class _MovedItems(_LazyModule):
|
||||||
|
"""Lazy loading of moved objects"""
|
||||||
|
__path__ = [] # mark as package
|
||||||
|
|
||||||
|
|
||||||
|
_moved_attributes = [
|
||||||
|
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||||
|
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||||
|
MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
|
||||||
|
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
||||||
|
MovedAttribute("intern", "__builtin__", "sys"),
|
||||||
|
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||||
|
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
||||||
|
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
|
||||||
|
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||||
|
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
|
||||||
|
MovedAttribute("StringIO", "StringIO", "io"),
|
||||||
|
MovedAttribute("UserDict", "UserDict", "collections"),
|
||||||
|
MovedAttribute("UserList", "UserList", "collections"),
|
||||||
|
MovedAttribute("UserString", "UserString", "collections"),
|
||||||
|
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||||
|
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||||
|
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
|
||||||
|
|
||||||
|
MovedModule("builtins", "__builtin__"),
|
||||||
|
MovedModule("configparser", "ConfigParser"),
|
||||||
|
MovedModule("copyreg", "copy_reg"),
|
||||||
|
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
||||||
|
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
|
||||||
|
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||||
|
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||||
|
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||||
|
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||||
|
MovedModule("http_client", "httplib", "http.client"),
|
||||||
|
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
||||||
|
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
|
||||||
|
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
||||||
|
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
||||||
|
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||||
|
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||||
|
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||||
|
MovedModule("cPickle", "cPickle", "pickle"),
|
||||||
|
MovedModule("queue", "Queue"),
|
||||||
|
MovedModule("reprlib", "repr"),
|
||||||
|
MovedModule("socketserver", "SocketServer"),
|
||||||
|
MovedModule("_thread", "thread", "_thread"),
|
||||||
|
MovedModule("tkinter", "Tkinter"),
|
||||||
|
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
||||||
|
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
||||||
|
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
|
||||||
|
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
|
||||||
|
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
||||||
|
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
|
||||||
|
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
||||||
|
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
||||||
|
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
||||||
|
"tkinter.colorchooser"),
|
||||||
|
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
||||||
|
"tkinter.commondialog"),
|
||||||
|
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
||||||
|
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
||||||
|
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
||||||
|
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
||||||
|
"tkinter.simpledialog"),
|
||||||
|
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
|
||||||
|
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
|
||||||
|
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
|
||||||
|
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
||||||
|
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
|
||||||
|
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
|
||||||
|
MovedModule("winreg", "_winreg"),
|
||||||
|
]
|
||||||
|
for attr in _moved_attributes:
|
||||||
|
setattr(_MovedItems, attr.name, attr)
|
||||||
|
if isinstance(attr, MovedModule):
|
||||||
|
_importer._add_module(attr, "moves." + attr.name)
|
||||||
|
del attr
|
||||||
|
|
||||||
|
_MovedItems._moved_attributes = _moved_attributes
|
||||||
|
|
||||||
|
moves = _MovedItems(__name__ + ".moves")
|
||||||
|
_importer._add_module(moves, "moves")
|
||||||
|
|
||||||
|
|
||||||
|
class Module_six_moves_urllib_parse(_LazyModule):
|
||||||
|
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
||||||
|
|
||||||
|
|
||||||
|
_urllib_parse_moved_attributes = [
|
||||||
|
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("quote", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
|
||||||
|
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
|
||||||
|
]
|
||||||
|
for attr in _urllib_parse_moved_attributes:
|
||||||
|
setattr(Module_six_moves_urllib_parse, attr.name, attr)
|
||||||
|
del attr
|
||||||
|
|
||||||
|
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
|
||||||
|
|
||||||
|
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
|
||||||
|
"moves.urllib_parse", "moves.urllib.parse")
|
||||||
|
|
||||||
|
|
||||||
|
class Module_six_moves_urllib_error(_LazyModule):
|
||||||
|
"""Lazy loading of moved objects in six.moves.urllib_error"""
|
||||||
|
|
||||||
|
|
||||||
|
_urllib_error_moved_attributes = [
|
||||||
|
MovedAttribute("URLError", "urllib2", "urllib.error"),
|
||||||
|
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
|
||||||
|
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
|
||||||
|
]
|
||||||
|
for attr in _urllib_error_moved_attributes:
|
||||||
|
setattr(Module_six_moves_urllib_error, attr.name, attr)
|
||||||
|
del attr
|
||||||
|
|
||||||
|
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
|
||||||
|
|
||||||
|
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
|
||||||
|
"moves.urllib_error", "moves.urllib.error")
|
||||||
|
|
||||||
|
|
||||||
|
class Module_six_moves_urllib_request(_LazyModule):
|
||||||
|
"""Lazy loading of moved objects in six.moves.urllib_request"""
|
||||||
|
|
||||||
|
|
||||||
|
_urllib_request_moved_attributes = [
|
||||||
|
MovedAttribute("urlopen", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("install_opener", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("build_opener", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("pathname2url", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("url2pathname", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("getproxies", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("Request", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
||||||
|
]
|
||||||
|
for attr in _urllib_request_moved_attributes:
|
||||||
|
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
||||||
|
del attr
|
||||||
|
|
||||||
|
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
|
||||||
|
|
||||||
|
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
|
||||||
|
"moves.urllib_request", "moves.urllib.request")
|
||||||
|
|
||||||
|
|
||||||
|
class Module_six_moves_urllib_response(_LazyModule):
|
||||||
|
"""Lazy loading of moved objects in six.moves.urllib_response"""
|
||||||
|
|
||||||
|
|
||||||
|
_urllib_response_moved_attributes = [
|
||||||
|
MovedAttribute("addbase", "urllib", "urllib.response"),
|
||||||
|
MovedAttribute("addclosehook", "urllib", "urllib.response"),
|
||||||
|
MovedAttribute("addinfo", "urllib", "urllib.response"),
|
||||||
|
MovedAttribute("addinfourl", "urllib", "urllib.response"),
|
||||||
|
]
|
||||||
|
for attr in _urllib_response_moved_attributes:
|
||||||
|
setattr(Module_six_moves_urllib_response, attr.name, attr)
|
||||||
|
del attr
|
||||||
|
|
||||||
|
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
|
||||||
|
|
||||||
|
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
|
||||||
|
"moves.urllib_response", "moves.urllib.response")
|
||||||
|
|
||||||
|
|
||||||
|
class Module_six_moves_urllib_robotparser(_LazyModule):
|
||||||
|
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
|
||||||
|
|
||||||
|
|
||||||
|
_urllib_robotparser_moved_attributes = [
|
||||||
|
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
|
||||||
|
]
|
||||||
|
for attr in _urllib_robotparser_moved_attributes:
|
||||||
|
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
||||||
|
del attr
|
||||||
|
|
||||||
|
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
|
||||||
|
|
||||||
|
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
|
||||||
|
"moves.urllib_robotparser", "moves.urllib.robotparser")
|
||||||
|
|
||||||
|
|
||||||
|
class Module_six_moves_urllib(types.ModuleType):
|
||||||
|
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
|
||||||
|
__path__ = [] # mark as package
|
||||||
|
parse = _importer._get_module("moves.urllib_parse")
|
||||||
|
error = _importer._get_module("moves.urllib_error")
|
||||||
|
request = _importer._get_module("moves.urllib_request")
|
||||||
|
response = _importer._get_module("moves.urllib_response")
|
||||||
|
robotparser = _importer._get_module("moves.urllib_robotparser")
|
||||||
|
|
||||||
|
def __dir__(self):
|
||||||
|
return ['parse', 'error', 'request', 'response', 'robotparser']
|
||||||
|
|
||||||
|
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
|
||||||
|
"moves.urllib")
|
||||||
|
|
||||||
|
|
||||||
|
def add_move(move):
|
||||||
|
"""Add an item to six.moves."""
|
||||||
|
setattr(_MovedItems, move.name, move)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_move(name):
|
||||||
|
"""Remove item from six.moves."""
|
||||||
|
try:
|
||||||
|
delattr(_MovedItems, name)
|
||||||
|
except AttributeError:
|
||||||
|
try:
|
||||||
|
del moves.__dict__[name]
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError("no such move, %r" % (name,))
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
_meth_func = "__func__"
|
||||||
|
_meth_self = "__self__"
|
||||||
|
|
||||||
|
_func_closure = "__closure__"
|
||||||
|
_func_code = "__code__"
|
||||||
|
_func_defaults = "__defaults__"
|
||||||
|
_func_globals = "__globals__"
|
||||||
|
else:
|
||||||
|
_meth_func = "im_func"
|
||||||
|
_meth_self = "im_self"
|
||||||
|
|
||||||
|
_func_closure = "func_closure"
|
||||||
|
_func_code = "func_code"
|
||||||
|
_func_defaults = "func_defaults"
|
||||||
|
_func_globals = "func_globals"
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
advance_iterator = next
|
||||||
|
except NameError:
|
||||||
|
def advance_iterator(it):
|
||||||
|
return it.next()
|
||||||
|
next = advance_iterator
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
callable = callable
|
||||||
|
except NameError:
|
||||||
|
def callable(obj):
|
||||||
|
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
def get_unbound_function(unbound):
|
||||||
|
return unbound
|
||||||
|
|
||||||
|
create_bound_method = types.MethodType
|
||||||
|
|
||||||
|
Iterator = object
|
||||||
|
else:
|
||||||
|
def get_unbound_function(unbound):
|
||||||
|
return unbound.im_func
|
||||||
|
|
||||||
|
def create_bound_method(func, obj):
|
||||||
|
return types.MethodType(func, obj, obj.__class__)
|
||||||
|
|
||||||
|
class Iterator(object):
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
return type(self).__next__(self)
|
||||||
|
|
||||||
|
callable = callable
|
||||||
|
_add_doc(get_unbound_function,
|
||||||
|
"""Get the function out of a possibly unbound function""")
|
||||||
|
|
||||||
|
|
||||||
|
get_method_function = operator.attrgetter(_meth_func)
|
||||||
|
get_method_self = operator.attrgetter(_meth_self)
|
||||||
|
get_function_closure = operator.attrgetter(_func_closure)
|
||||||
|
get_function_code = operator.attrgetter(_func_code)
|
||||||
|
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||||
|
get_function_globals = operator.attrgetter(_func_globals)
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
def iterkeys(d, **kw):
|
||||||
|
return iter(d.keys(**kw))
|
||||||
|
|
||||||
|
def itervalues(d, **kw):
|
||||||
|
return iter(d.values(**kw))
|
||||||
|
|
||||||
|
def iteritems(d, **kw):
|
||||||
|
return iter(d.items(**kw))
|
||||||
|
|
||||||
|
def iterlists(d, **kw):
|
||||||
|
return iter(d.lists(**kw))
|
||||||
|
else:
|
||||||
|
def iterkeys(d, **kw):
|
||||||
|
return iter(d.iterkeys(**kw))
|
||||||
|
|
||||||
|
def itervalues(d, **kw):
|
||||||
|
return iter(d.itervalues(**kw))
|
||||||
|
|
||||||
|
def iteritems(d, **kw):
|
||||||
|
return iter(d.iteritems(**kw))
|
||||||
|
|
||||||
|
def iterlists(d, **kw):
|
||||||
|
return iter(d.iterlists(**kw))
|
||||||
|
|
||||||
|
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
|
||||||
|
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
|
||||||
|
_add_doc(iteritems,
|
||||||
|
"Return an iterator over the (key, value) pairs of a dictionary.")
|
||||||
|
_add_doc(iterlists,
|
||||||
|
"Return an iterator over the (key, [values]) pairs of a dictionary.")
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
def b(s):
|
||||||
|
return s.encode("latin-1")
|
||||||
|
def u(s):
|
||||||
|
return s
|
||||||
|
unichr = chr
|
||||||
|
if sys.version_info[1] <= 1:
|
||||||
|
def int2byte(i):
|
||||||
|
return bytes((i,))
|
||||||
|
else:
|
||||||
|
# This is about 2x faster than the implementation above on 3.2+
|
||||||
|
int2byte = operator.methodcaller("to_bytes", 1, "big")
|
||||||
|
byte2int = operator.itemgetter(0)
|
||||||
|
indexbytes = operator.getitem
|
||||||
|
iterbytes = iter
|
||||||
|
import io
|
||||||
|
StringIO = io.StringIO
|
||||||
|
BytesIO = io.BytesIO
|
||||||
|
else:
|
||||||
|
def b(s):
|
||||||
|
return s
|
||||||
|
# Workaround for standalone backslash
|
||||||
|
def u(s):
|
||||||
|
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
|
||||||
|
unichr = unichr
|
||||||
|
int2byte = chr
|
||||||
|
def byte2int(bs):
|
||||||
|
return ord(bs[0])
|
||||||
|
def indexbytes(buf, i):
|
||||||
|
return ord(buf[i])
|
||||||
|
def iterbytes(buf):
|
||||||
|
return (ord(byte) for byte in buf)
|
||||||
|
import StringIO
|
||||||
|
StringIO = BytesIO = StringIO.StringIO
|
||||||
|
_add_doc(b, """Byte literal""")
|
||||||
|
_add_doc(u, """Text literal""")
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
exec_ = getattr(moves.builtins, "exec")
|
||||||
|
|
||||||
|
|
||||||
|
def reraise(tp, value, tb=None):
|
||||||
|
if value is None:
|
||||||
|
value = tp()
|
||||||
|
if value.__traceback__ is not tb:
|
||||||
|
raise value.with_traceback(tb)
|
||||||
|
raise value
|
||||||
|
|
||||||
|
else:
|
||||||
|
def exec_(_code_, _globs_=None, _locs_=None):
|
||||||
|
"""Execute code in a namespace."""
|
||||||
|
if _globs_ is None:
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
_globs_ = frame.f_globals
|
||||||
|
if _locs_ is None:
|
||||||
|
_locs_ = frame.f_locals
|
||||||
|
del frame
|
||||||
|
elif _locs_ is None:
|
||||||
|
_locs_ = _globs_
|
||||||
|
exec("""exec _code_ in _globs_, _locs_""")
|
||||||
|
|
||||||
|
|
||||||
|
exec_("""def reraise(tp, value, tb=None):
|
||||||
|
raise tp, value, tb
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
print_ = getattr(moves.builtins, "print", None)
|
||||||
|
if print_ is None:
|
||||||
|
def print_(*args, **kwargs):
|
||||||
|
"""The new-style print function for Python 2.4 and 2.5."""
|
||||||
|
fp = kwargs.pop("file", sys.stdout)
|
||||||
|
if fp is None:
|
||||||
|
return
|
||||||
|
def write(data):
|
||||||
|
if not isinstance(data, basestring):
|
||||||
|
data = str(data)
|
||||||
|
# If the file has an encoding, encode unicode with it.
|
||||||
|
if (isinstance(fp, file) and
|
||||||
|
isinstance(data, unicode) and
|
||||||
|
fp.encoding is not None):
|
||||||
|
errors = getattr(fp, "errors", None)
|
||||||
|
if errors is None:
|
||||||
|
errors = "strict"
|
||||||
|
data = data.encode(fp.encoding, errors)
|
||||||
|
fp.write(data)
|
||||||
|
want_unicode = False
|
||||||
|
sep = kwargs.pop("sep", None)
|
||||||
|
if sep is not None:
|
||||||
|
if isinstance(sep, unicode):
|
||||||
|
want_unicode = True
|
||||||
|
elif not isinstance(sep, str):
|
||||||
|
raise TypeError("sep must be None or a string")
|
||||||
|
end = kwargs.pop("end", None)
|
||||||
|
if end is not None:
|
||||||
|
if isinstance(end, unicode):
|
||||||
|
want_unicode = True
|
||||||
|
elif not isinstance(end, str):
|
||||||
|
raise TypeError("end must be None or a string")
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError("invalid keyword arguments to print()")
|
||||||
|
if not want_unicode:
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, unicode):
|
||||||
|
want_unicode = True
|
||||||
|
break
|
||||||
|
if want_unicode:
|
||||||
|
newline = unicode("\n")
|
||||||
|
space = unicode(" ")
|
||||||
|
else:
|
||||||
|
newline = "\n"
|
||||||
|
space = " "
|
||||||
|
if sep is None:
|
||||||
|
sep = space
|
||||||
|
if end is None:
|
||||||
|
end = newline
|
||||||
|
for i, arg in enumerate(args):
|
||||||
|
if i:
|
||||||
|
write(sep)
|
||||||
|
write(arg)
|
||||||
|
write(end)
|
||||||
|
|
||||||
|
_add_doc(reraise, """Reraise an exception.""")
|
||||||
|
|
||||||
|
if sys.version_info[0:2] < (3, 4):
|
||||||
|
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||||
|
updated=functools.WRAPPER_UPDATES):
|
||||||
|
def wrapper(f):
|
||||||
|
f = functools.wraps(wrapped)(f)
|
||||||
|
f.__wrapped__ = wrapped
|
||||||
|
return f
|
||||||
|
return wrapper
|
||||||
|
else:
|
||||||
|
wraps = functools.wraps
|
||||||
|
|
||||||
|
def with_metaclass(meta, *bases):
|
||||||
|
"""Create a base class with a metaclass."""
|
||||||
|
# This requires a bit of explanation: the basic idea is to make a dummy
|
||||||
|
# metaclass for one level of class instantiation that replaces itself with
|
||||||
|
# the actual metaclass.
|
||||||
|
class metaclass(meta):
|
||||||
|
def __new__(cls, name, this_bases, d):
|
||||||
|
return meta(name, bases, d)
|
||||||
|
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||||
|
|
||||||
|
|
||||||
|
def add_metaclass(metaclass):
|
||||||
|
"""Class decorator for creating a class with a metaclass."""
|
||||||
|
def wrapper(cls):
|
||||||
|
orig_vars = cls.__dict__.copy()
|
||||||
|
slots = orig_vars.get('__slots__')
|
||||||
|
if slots is not None:
|
||||||
|
if isinstance(slots, str):
|
||||||
|
slots = [slots]
|
||||||
|
for slots_var in slots:
|
||||||
|
orig_vars.pop(slots_var)
|
||||||
|
orig_vars.pop('__dict__', None)
|
||||||
|
orig_vars.pop('__weakref__', None)
|
||||||
|
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
# Complete the moves implementation.
|
||||||
|
# This code is at the end of this module to speed up module loading.
|
||||||
|
# Turn this module into a package.
|
||||||
|
__path__ = [] # required for PEP 302 and PEP 451
|
||||||
|
__package__ = __name__ # see PEP 366 @ReservedAssignment
|
||||||
|
if globals().get("__spec__") is not None:
|
||||||
|
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
|
||||||
|
# Remove other six meta path importers, since they cause problems. This can
|
||||||
|
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
|
||||||
|
# this for some reason.)
|
||||||
|
if sys.meta_path:
|
||||||
|
for i, importer in enumerate(sys.meta_path):
|
||||||
|
# Here's some real nastiness: Another "instance" of the six module might
|
||||||
|
# be floating around. Therefore, we can't use isinstance() to check for
|
||||||
|
# the six meta path importer, since the other six instance will have
|
||||||
|
# inserted an importer with different class.
|
||||||
|
if (type(importer).__name__ == "_SixMetaPathImporter" and
|
||||||
|
importer.name == __name__):
|
||||||
|
del sys.meta_path[i]
|
||||||
|
break
|
||||||
|
del i, importer
|
||||||
|
# Finally, add the importer to the meta path import hook.
|
||||||
|
sys.meta_path.append(_importer)
|
209
venv/lib/python3.6/site-packages/gunicorn/sock.py
Normal file
209
venv/lib/python3.6/site-packages/gunicorn/sock.py
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import stat
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from gunicorn import util
|
||||||
|
from gunicorn.six import string_types
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSocket(object):
|
||||||
|
|
||||||
|
def __init__(self, address, conf, log, fd=None):
|
||||||
|
self.log = log
|
||||||
|
self.conf = conf
|
||||||
|
|
||||||
|
self.cfg_addr = address
|
||||||
|
if fd is None:
|
||||||
|
sock = socket.socket(self.FAMILY, socket.SOCK_STREAM)
|
||||||
|
bound = False
|
||||||
|
else:
|
||||||
|
sock = socket.fromfd(fd, self.FAMILY, socket.SOCK_STREAM)
|
||||||
|
os.close(fd)
|
||||||
|
bound = True
|
||||||
|
|
||||||
|
self.sock = self.set_options(sock, bound=bound)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<socket %d>" % self.sock.fileno()
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self.sock, name)
|
||||||
|
|
||||||
|
def set_options(self, sock, bound=False):
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
if (self.conf.reuse_port
|
||||||
|
and hasattr(socket, 'SO_REUSEPORT')): # pragma: no cover
|
||||||
|
try:
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||||
|
except socket.error as err:
|
||||||
|
if err.errno not in (errno.ENOPROTOOPT, errno.EINVAL):
|
||||||
|
raise
|
||||||
|
if not bound:
|
||||||
|
self.bind(sock)
|
||||||
|
sock.setblocking(0)
|
||||||
|
|
||||||
|
# make sure that the socket can be inherited
|
||||||
|
if hasattr(sock, "set_inheritable"):
|
||||||
|
sock.set_inheritable(True)
|
||||||
|
|
||||||
|
sock.listen(self.conf.backlog)
|
||||||
|
return sock
|
||||||
|
|
||||||
|
def bind(self, sock):
|
||||||
|
sock.bind(self.cfg_addr)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.sock is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.sock.close()
|
||||||
|
except socket.error as e:
|
||||||
|
self.log.info("Error while closing socket %s", str(e))
|
||||||
|
|
||||||
|
self.sock = None
|
||||||
|
|
||||||
|
|
||||||
|
class TCPSocket(BaseSocket):
|
||||||
|
|
||||||
|
FAMILY = socket.AF_INET
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.conf.is_ssl:
|
||||||
|
scheme = "https"
|
||||||
|
else:
|
||||||
|
scheme = "http"
|
||||||
|
|
||||||
|
addr = self.sock.getsockname()
|
||||||
|
return "%s://%s:%d" % (scheme, addr[0], addr[1])
|
||||||
|
|
||||||
|
def set_options(self, sock, bound=False):
|
||||||
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
return super(TCPSocket, self).set_options(sock, bound=bound)
|
||||||
|
|
||||||
|
|
||||||
|
class TCP6Socket(TCPSocket):
|
||||||
|
|
||||||
|
FAMILY = socket.AF_INET6
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
(host, port, _, _) = self.sock.getsockname()
|
||||||
|
return "http://[%s]:%d" % (host, port)
|
||||||
|
|
||||||
|
|
||||||
|
class UnixSocket(BaseSocket):
|
||||||
|
|
||||||
|
FAMILY = socket.AF_UNIX
|
||||||
|
|
||||||
|
def __init__(self, addr, conf, log, fd=None):
|
||||||
|
if fd is None:
|
||||||
|
try:
|
||||||
|
st = os.stat(addr)
|
||||||
|
except OSError as e:
|
||||||
|
if e.args[0] != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
if stat.S_ISSOCK(st.st_mode):
|
||||||
|
os.remove(addr)
|
||||||
|
else:
|
||||||
|
raise ValueError("%r is not a socket" % addr)
|
||||||
|
super(UnixSocket, self).__init__(addr, conf, log, fd=fd)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "unix:%s" % self.cfg_addr
|
||||||
|
|
||||||
|
def bind(self, sock):
|
||||||
|
old_umask = os.umask(self.conf.umask)
|
||||||
|
sock.bind(self.cfg_addr)
|
||||||
|
util.chown(self.cfg_addr, self.conf.uid, self.conf.gid)
|
||||||
|
os.umask(old_umask)
|
||||||
|
|
||||||
|
|
||||||
|
def _sock_type(addr):
|
||||||
|
if isinstance(addr, tuple):
|
||||||
|
if util.is_ipv6(addr[0]):
|
||||||
|
sock_type = TCP6Socket
|
||||||
|
else:
|
||||||
|
sock_type = TCPSocket
|
||||||
|
elif isinstance(addr, string_types):
|
||||||
|
sock_type = UnixSocket
|
||||||
|
else:
|
||||||
|
raise TypeError("Unable to create socket from: %r" % addr)
|
||||||
|
return sock_type
|
||||||
|
|
||||||
|
|
||||||
|
def create_sockets(conf, log, fds=None):
|
||||||
|
"""
|
||||||
|
Create a new socket for the configured addresses or file descriptors.
|
||||||
|
|
||||||
|
If a configured address is a tuple then a TCP socket is created.
|
||||||
|
If it is a string, a Unix socket is created. Otherwise, a TypeError is
|
||||||
|
raised.
|
||||||
|
"""
|
||||||
|
listeners = []
|
||||||
|
|
||||||
|
# get it only once
|
||||||
|
laddr = conf.address
|
||||||
|
|
||||||
|
# check ssl config early to raise the error on startup
|
||||||
|
# only the certfile is needed since it can contains the keyfile
|
||||||
|
if conf.certfile and not os.path.exists(conf.certfile):
|
||||||
|
raise ValueError('certfile "%s" does not exist' % conf.certfile)
|
||||||
|
|
||||||
|
if conf.keyfile and not os.path.exists(conf.keyfile):
|
||||||
|
raise ValueError('keyfile "%s" does not exist' % conf.keyfile)
|
||||||
|
|
||||||
|
# sockets are already bound
|
||||||
|
if fds is not None:
|
||||||
|
for fd in fds:
|
||||||
|
sock = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
sock_name = sock.getsockname()
|
||||||
|
sock_type = _sock_type(sock_name)
|
||||||
|
listener = sock_type(sock_name, conf, log, fd=fd)
|
||||||
|
listeners.append(listener)
|
||||||
|
|
||||||
|
return listeners
|
||||||
|
|
||||||
|
# no sockets is bound, first initialization of gunicorn in this env.
|
||||||
|
for addr in laddr:
|
||||||
|
sock_type = _sock_type(addr)
|
||||||
|
sock = None
|
||||||
|
for i in range(5):
|
||||||
|
try:
|
||||||
|
sock = sock_type(addr, conf, log)
|
||||||
|
except socket.error as e:
|
||||||
|
if e.args[0] == errno.EADDRINUSE:
|
||||||
|
log.error("Connection in use: %s", str(addr))
|
||||||
|
if e.args[0] == errno.EADDRNOTAVAIL:
|
||||||
|
log.error("Invalid address: %s", str(addr))
|
||||||
|
if i < 5:
|
||||||
|
msg = "connection to {addr} failed: {error}"
|
||||||
|
log.debug(msg.format(addr=str(addr), error=str(e)))
|
||||||
|
log.error("Retrying in 1 second.")
|
||||||
|
time.sleep(1)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if sock is None:
|
||||||
|
log.error("Can't connect to %s", str(addr))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
listeners.append(sock)
|
||||||
|
|
||||||
|
return listeners
|
||||||
|
|
||||||
|
|
||||||
|
def close_sockets(listeners, unlink=True):
|
||||||
|
for sock in listeners:
|
||||||
|
sock_name = sock.getsockname()
|
||||||
|
sock.close()
|
||||||
|
if unlink and _sock_type(sock_name) is UnixSocket:
|
||||||
|
os.unlink(sock_name)
|
45
venv/lib/python3.6/site-packages/gunicorn/systemd.py
Normal file
45
venv/lib/python3.6/site-packages/gunicorn/systemd.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
SD_LISTEN_FDS_START = 3
|
||||||
|
|
||||||
|
|
||||||
|
def listen_fds(unset_environment=True):
|
||||||
|
"""
|
||||||
|
Get the number of sockets inherited from systemd socket activation.
|
||||||
|
|
||||||
|
:param unset_environment: clear systemd environment variables unless False
|
||||||
|
:type unset_environment: bool
|
||||||
|
:return: the number of sockets to inherit from systemd socket activation
|
||||||
|
:rtype: int
|
||||||
|
|
||||||
|
Returns zero immediately if $LISTEN_PID is not set to the current pid.
|
||||||
|
Otherwise, returns the number of systemd activation sockets specified by
|
||||||
|
$LISTEN_FDS.
|
||||||
|
|
||||||
|
When $LISTEN_PID matches the current pid, unsets the environment variables
|
||||||
|
unless the ``unset_environment`` flag is ``False``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Unlike the sd_listen_fds C function, this implementation does not set
|
||||||
|
the FD_CLOEXEC flag because the gunicorn arbiter never needs to do this.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
`<https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html>`_
|
||||||
|
|
||||||
|
"""
|
||||||
|
fds = int(os.environ.get('LISTEN_FDS', 0))
|
||||||
|
listen_pid = int(os.environ.get('LISTEN_PID', 0))
|
||||||
|
|
||||||
|
if listen_pid != os.getpid():
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if unset_environment:
|
||||||
|
os.environ.pop('LISTEN_PID', None)
|
||||||
|
os.environ.pop('LISTEN_FDS', None)
|
||||||
|
|
||||||
|
return fds
|
557
venv/lib/python3.6/site-packages/gunicorn/util.py
Normal file
557
venv/lib/python3.6/site-packages/gunicorn/util.py
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import email.utils
|
||||||
|
import fcntl
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import pkg_resources
|
||||||
|
import pwd
|
||||||
|
import random
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
import inspect
|
||||||
|
import errno
|
||||||
|
import warnings
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from gunicorn import _compat
|
||||||
|
from gunicorn.errors import AppImportError
|
||||||
|
from gunicorn.six import text_type
|
||||||
|
from gunicorn.workers import SUPPORTED_WORKERS
|
||||||
|
|
||||||
|
REDIRECT_TO = getattr(os, 'devnull', '/dev/null')
|
||||||
|
|
||||||
|
# Server and Date aren't technically hop-by-hop
|
||||||
|
# headers, but they are in the purview of the
|
||||||
|
# origin server which the WSGI spec says we should
|
||||||
|
# act like. So we drop them and add our own.
|
||||||
|
#
|
||||||
|
# In the future, concatenation server header values
|
||||||
|
# might be better, but nothing else does it and
|
||||||
|
# dropping them is easier.
|
||||||
|
hop_headers = set("""
|
||||||
|
connection keep-alive proxy-authenticate proxy-authorization
|
||||||
|
te trailers transfer-encoding upgrade
|
||||||
|
server date
|
||||||
|
""".split())
|
||||||
|
|
||||||
|
try:
|
||||||
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
|
def _setproctitle(title):
|
||||||
|
setproctitle("gunicorn: %s" % title)
|
||||||
|
except ImportError:
|
||||||
|
def _setproctitle(title):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from importlib import import_module
|
||||||
|
except ImportError:
|
||||||
|
def _resolve_name(name, package, level):
|
||||||
|
"""Return the absolute name of the module to be imported."""
|
||||||
|
if not hasattr(package, 'rindex'):
|
||||||
|
raise ValueError("'package' not set to a string")
|
||||||
|
dot = len(package)
|
||||||
|
for _ in range(level, 1, -1):
|
||||||
|
try:
|
||||||
|
dot = package.rindex('.', 0, dot)
|
||||||
|
except ValueError:
|
||||||
|
msg = "attempted relative import beyond top-level package"
|
||||||
|
raise ValueError(msg)
|
||||||
|
return "%s.%s" % (package[:dot], name)
|
||||||
|
|
||||||
|
def import_module(name, package=None):
|
||||||
|
"""Import a module.
|
||||||
|
|
||||||
|
The 'package' argument is required when performing a relative import. It
|
||||||
|
specifies the package to use as the anchor point from which to resolve the
|
||||||
|
relative import to an absolute import.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if name.startswith('.'):
|
||||||
|
if not package:
|
||||||
|
raise TypeError("relative imports require the 'package' argument")
|
||||||
|
level = 0
|
||||||
|
for character in name:
|
||||||
|
if character != '.':
|
||||||
|
break
|
||||||
|
level += 1
|
||||||
|
name = _resolve_name(name[level:], package, level)
|
||||||
|
__import__(name)
|
||||||
|
return sys.modules[name]
|
||||||
|
|
||||||
|
|
||||||
|
def load_class(uri, default="gunicorn.workers.sync.SyncWorker",
|
||||||
|
section="gunicorn.workers"):
|
||||||
|
if inspect.isclass(uri):
|
||||||
|
return uri
|
||||||
|
if uri.startswith("egg:"):
|
||||||
|
# uses entry points
|
||||||
|
entry_str = uri.split("egg:")[1]
|
||||||
|
try:
|
||||||
|
dist, name = entry_str.rsplit("#", 1)
|
||||||
|
except ValueError:
|
||||||
|
dist = entry_str
|
||||||
|
name = default
|
||||||
|
|
||||||
|
try:
|
||||||
|
return pkg_resources.load_entry_point(dist, section, name)
|
||||||
|
except:
|
||||||
|
exc = traceback.format_exc()
|
||||||
|
msg = "class uri %r invalid or not found: \n\n[%s]"
|
||||||
|
raise RuntimeError(msg % (uri, exc))
|
||||||
|
else:
|
||||||
|
components = uri.split('.')
|
||||||
|
if len(components) == 1:
|
||||||
|
while True:
|
||||||
|
if uri.startswith("#"):
|
||||||
|
uri = uri[1:]
|
||||||
|
|
||||||
|
if uri in SUPPORTED_WORKERS:
|
||||||
|
components = SUPPORTED_WORKERS[uri].split(".")
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
return pkg_resources.load_entry_point("gunicorn",
|
||||||
|
section, uri)
|
||||||
|
except:
|
||||||
|
exc = traceback.format_exc()
|
||||||
|
msg = "class uri %r invalid or not found: \n\n[%s]"
|
||||||
|
raise RuntimeError(msg % (uri, exc))
|
||||||
|
|
||||||
|
klass = components.pop(-1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
mod = import_module('.'.join(components))
|
||||||
|
except:
|
||||||
|
exc = traceback.format_exc()
|
||||||
|
msg = "class uri %r invalid or not found: \n\n[%s]"
|
||||||
|
raise RuntimeError(msg % (uri, exc))
|
||||||
|
return getattr(mod, klass)
|
||||||
|
|
||||||
|
|
||||||
|
def get_username(uid):
|
||||||
|
""" get the username for a user id"""
|
||||||
|
return pwd.getpwuid(uid).pw_name
|
||||||
|
|
||||||
|
|
||||||
|
def set_owner_process(uid, gid, initgroups=False):
|
||||||
|
""" set user and group of workers processes """
|
||||||
|
|
||||||
|
if gid:
|
||||||
|
if uid:
|
||||||
|
try:
|
||||||
|
username = get_username(uid)
|
||||||
|
except KeyError:
|
||||||
|
initgroups = False
|
||||||
|
|
||||||
|
# versions of python < 2.6.2 don't manage unsigned int for
|
||||||
|
# groups like on osx or fedora
|
||||||
|
gid = abs(gid) & 0x7FFFFFFF
|
||||||
|
|
||||||
|
if initgroups:
|
||||||
|
os.initgroups(username, gid)
|
||||||
|
elif gid != os.getgid():
|
||||||
|
os.setgid(gid)
|
||||||
|
|
||||||
|
if uid:
|
||||||
|
os.setuid(uid)
|
||||||
|
|
||||||
|
|
||||||
|
def chown(path, uid, gid):
|
||||||
|
gid = abs(gid) & 0x7FFFFFFF # see note above.
|
||||||
|
os.chown(path, uid, gid)
|
||||||
|
|
||||||
|
|
||||||
|
if sys.platform.startswith("win"):
|
||||||
|
def _waitfor(func, pathname, waitall=False):
|
||||||
|
# Peform the operation
|
||||||
|
func(pathname)
|
||||||
|
# Now setup the wait loop
|
||||||
|
if waitall:
|
||||||
|
dirname = pathname
|
||||||
|
else:
|
||||||
|
dirname, name = os.path.split(pathname)
|
||||||
|
dirname = dirname or '.'
|
||||||
|
# Check for `pathname` to be removed from the filesystem.
|
||||||
|
# The exponential backoff of the timeout amounts to a total
|
||||||
|
# of ~1 second after which the deletion is probably an error
|
||||||
|
# anyway.
|
||||||
|
# Testing on a i7@4.3GHz shows that usually only 1 iteration is
|
||||||
|
# required when contention occurs.
|
||||||
|
timeout = 0.001
|
||||||
|
while timeout < 1.0:
|
||||||
|
# Note we are only testing for the existence of the file(s) in
|
||||||
|
# the contents of the directory regardless of any security or
|
||||||
|
# access rights. If we have made it this far, we have sufficient
|
||||||
|
# permissions to do that much using Python's equivalent of the
|
||||||
|
# Windows API FindFirstFile.
|
||||||
|
# Other Windows APIs can fail or give incorrect results when
|
||||||
|
# dealing with files that are pending deletion.
|
||||||
|
L = os.listdir(dirname)
|
||||||
|
if not L if waitall else name in L:
|
||||||
|
return
|
||||||
|
# Increase the timeout and try again
|
||||||
|
time.sleep(timeout)
|
||||||
|
timeout *= 2
|
||||||
|
warnings.warn('tests may fail, delete still pending for ' + pathname,
|
||||||
|
RuntimeWarning, stacklevel=4)
|
||||||
|
|
||||||
|
def _unlink(filename):
|
||||||
|
_waitfor(os.unlink, filename)
|
||||||
|
else:
|
||||||
|
_unlink = os.unlink
|
||||||
|
|
||||||
|
|
||||||
|
def unlink(filename):
|
||||||
|
try:
|
||||||
|
_unlink(filename)
|
||||||
|
except OSError as error:
|
||||||
|
# The filename need not exist.
|
||||||
|
if error.errno not in (errno.ENOENT, errno.ENOTDIR):
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def is_ipv6(addr):
|
||||||
|
try:
|
||||||
|
socket.inet_pton(socket.AF_INET6, addr)
|
||||||
|
except socket.error: # not a valid address
|
||||||
|
return False
|
||||||
|
except ValueError: # ipv6 not supported on this platform
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def parse_address(netloc, default_port=8000):
|
||||||
|
if re.match(r'unix:(//)?', netloc):
|
||||||
|
return re.split(r'unix:(//)?', netloc)[-1]
|
||||||
|
|
||||||
|
if netloc.startswith("tcp://"):
|
||||||
|
netloc = netloc.split("tcp://")[1]
|
||||||
|
|
||||||
|
# get host
|
||||||
|
if '[' in netloc and ']' in netloc:
|
||||||
|
host = netloc.split(']')[0][1:].lower()
|
||||||
|
elif ':' in netloc:
|
||||||
|
host = netloc.split(':')[0].lower()
|
||||||
|
elif netloc == "":
|
||||||
|
host = "0.0.0.0"
|
||||||
|
else:
|
||||||
|
host = netloc.lower()
|
||||||
|
|
||||||
|
#get port
|
||||||
|
netloc = netloc.split(']')[-1]
|
||||||
|
if ":" in netloc:
|
||||||
|
port = netloc.split(':', 1)[1]
|
||||||
|
if not port.isdigit():
|
||||||
|
raise RuntimeError("%r is not a valid port number." % port)
|
||||||
|
port = int(port)
|
||||||
|
else:
|
||||||
|
port = default_port
|
||||||
|
return (host, port)
|
||||||
|
|
||||||
|
|
||||||
|
def close_on_exec(fd):
|
||||||
|
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||||
|
flags |= fcntl.FD_CLOEXEC
|
||||||
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags)
|
||||||
|
|
||||||
|
|
||||||
|
def set_non_blocking(fd):
|
||||||
|
flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK
|
||||||
|
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
|
||||||
|
|
||||||
|
|
||||||
|
def close(sock):
|
||||||
|
try:
|
||||||
|
sock.close()
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
from os import closerange
|
||||||
|
except ImportError:
|
||||||
|
def closerange(fd_low, fd_high):
|
||||||
|
# Iterate through and close all file descriptors.
|
||||||
|
for fd in range(fd_low, fd_high):
|
||||||
|
try:
|
||||||
|
os.close(fd)
|
||||||
|
except OSError: # ERROR, fd wasn't open to begin with (ignored)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def write_chunk(sock, data):
|
||||||
|
if isinstance(data, text_type):
|
||||||
|
data = data.encode('utf-8')
|
||||||
|
chunk_size = "%X\r\n" % len(data)
|
||||||
|
chunk = b"".join([chunk_size.encode('utf-8'), data, b"\r\n"])
|
||||||
|
sock.sendall(chunk)
|
||||||
|
|
||||||
|
|
||||||
|
def write(sock, data, chunked=False):
|
||||||
|
if chunked:
|
||||||
|
return write_chunk(sock, data)
|
||||||
|
sock.sendall(data)
|
||||||
|
|
||||||
|
|
||||||
|
def write_nonblock(sock, data, chunked=False):
|
||||||
|
timeout = sock.gettimeout()
|
||||||
|
if timeout != 0.0:
|
||||||
|
try:
|
||||||
|
sock.setblocking(0)
|
||||||
|
return write(sock, data, chunked)
|
||||||
|
finally:
|
||||||
|
sock.setblocking(1)
|
||||||
|
else:
|
||||||
|
return write(sock, data, chunked)
|
||||||
|
|
||||||
|
|
||||||
|
def write_error(sock, status_int, reason, mesg):
|
||||||
|
html = textwrap.dedent("""\
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>%(reason)s</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><p>%(reason)s</p></h1>
|
||||||
|
%(mesg)s
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""") % {"reason": reason, "mesg": _compat.html_escape(mesg)}
|
||||||
|
|
||||||
|
http = textwrap.dedent("""\
|
||||||
|
HTTP/1.1 %s %s\r
|
||||||
|
Connection: close\r
|
||||||
|
Content-Type: text/html\r
|
||||||
|
Content-Length: %d\r
|
||||||
|
\r
|
||||||
|
%s""") % (str(status_int), reason, len(html), html)
|
||||||
|
write_nonblock(sock, http.encode('latin1'))
|
||||||
|
|
||||||
|
|
||||||
|
def import_app(module):
|
||||||
|
parts = module.split(":", 1)
|
||||||
|
if len(parts) == 1:
|
||||||
|
module, obj = module, "application"
|
||||||
|
else:
|
||||||
|
module, obj = parts[0], parts[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
__import__(module)
|
||||||
|
except ImportError:
|
||||||
|
if module.endswith(".py") and os.path.exists(module):
|
||||||
|
msg = "Failed to find application, did you mean '%s:%s'?"
|
||||||
|
raise ImportError(msg % (module.rsplit(".", 1)[0], obj))
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
mod = sys.modules[module]
|
||||||
|
|
||||||
|
is_debug = logging.root.level == logging.DEBUG
|
||||||
|
try:
|
||||||
|
app = eval(obj, vars(mod))
|
||||||
|
except NameError:
|
||||||
|
if is_debug:
|
||||||
|
traceback.print_exception(*sys.exc_info())
|
||||||
|
raise AppImportError("Failed to find application object %r in %r" % (obj, module))
|
||||||
|
|
||||||
|
if app is None:
|
||||||
|
raise AppImportError("Failed to find application object: %r" % obj)
|
||||||
|
|
||||||
|
if not callable(app):
|
||||||
|
raise AppImportError("Application object must be callable.")
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
def getcwd():
|
||||||
|
# get current path, try to use PWD env first
|
||||||
|
try:
|
||||||
|
a = os.stat(os.environ['PWD'])
|
||||||
|
b = os.stat(os.getcwd())
|
||||||
|
if a.st_ino == b.st_ino and a.st_dev == b.st_dev:
|
||||||
|
cwd = os.environ['PWD']
|
||||||
|
else:
|
||||||
|
cwd = os.getcwd()
|
||||||
|
except:
|
||||||
|
cwd = os.getcwd()
|
||||||
|
return cwd
|
||||||
|
|
||||||
|
|
||||||
|
def http_date(timestamp=None):
|
||||||
|
"""Return the current date and time formatted for a message header."""
|
||||||
|
if timestamp is None:
|
||||||
|
timestamp = time.time()
|
||||||
|
s = email.utils.formatdate(timestamp, localtime=False, usegmt=True)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def is_hoppish(header):
|
||||||
|
return header.lower().strip() in hop_headers
|
||||||
|
|
||||||
|
|
||||||
|
def daemonize(enable_stdio_inheritance=False):
|
||||||
|
"""\
|
||||||
|
Standard daemonization of a process.
|
||||||
|
http://www.svbug.com/documentation/comp.unix.programmer-FAQ/faq_2.html#SEC16
|
||||||
|
"""
|
||||||
|
if 'GUNICORN_FD' not in os.environ:
|
||||||
|
if os.fork():
|
||||||
|
os._exit(0)
|
||||||
|
os.setsid()
|
||||||
|
|
||||||
|
if os.fork():
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
|
os.umask(0o22)
|
||||||
|
|
||||||
|
# In both the following any file descriptors above stdin
|
||||||
|
# stdout and stderr are left untouched. The inheritance
|
||||||
|
# option simply allows one to have output go to a file
|
||||||
|
# specified by way of shell redirection when not wanting
|
||||||
|
# to use --error-log option.
|
||||||
|
|
||||||
|
if not enable_stdio_inheritance:
|
||||||
|
# Remap all of stdin, stdout and stderr on to
|
||||||
|
# /dev/null. The expectation is that users have
|
||||||
|
# specified the --error-log option.
|
||||||
|
|
||||||
|
closerange(0, 3)
|
||||||
|
|
||||||
|
fd_null = os.open(REDIRECT_TO, os.O_RDWR)
|
||||||
|
|
||||||
|
if fd_null != 0:
|
||||||
|
os.dup2(fd_null, 0)
|
||||||
|
|
||||||
|
os.dup2(fd_null, 1)
|
||||||
|
os.dup2(fd_null, 2)
|
||||||
|
|
||||||
|
else:
|
||||||
|
fd_null = os.open(REDIRECT_TO, os.O_RDWR)
|
||||||
|
|
||||||
|
# Always redirect stdin to /dev/null as we would
|
||||||
|
# never expect to need to read interactive input.
|
||||||
|
|
||||||
|
if fd_null != 0:
|
||||||
|
os.close(0)
|
||||||
|
os.dup2(fd_null, 0)
|
||||||
|
|
||||||
|
# If stdout and stderr are still connected to
|
||||||
|
# their original file descriptors we check to see
|
||||||
|
# if they are associated with terminal devices.
|
||||||
|
# When they are we map them to /dev/null so that
|
||||||
|
# are still detached from any controlling terminal
|
||||||
|
# properly. If not we preserve them as they are.
|
||||||
|
#
|
||||||
|
# If stdin and stdout were not hooked up to the
|
||||||
|
# original file descriptors, then all bets are
|
||||||
|
# off and all we can really do is leave them as
|
||||||
|
# they were.
|
||||||
|
#
|
||||||
|
# This will allow 'gunicorn ... > output.log 2>&1'
|
||||||
|
# to work with stdout/stderr going to the file
|
||||||
|
# as expected.
|
||||||
|
#
|
||||||
|
# Note that if using --error-log option, the log
|
||||||
|
# file specified through shell redirection will
|
||||||
|
# only be used up until the log file specified
|
||||||
|
# by the option takes over. As it replaces stdout
|
||||||
|
# and stderr at the file descriptor level, then
|
||||||
|
# anything using stdout or stderr, including having
|
||||||
|
# cached a reference to them, will still work.
|
||||||
|
|
||||||
|
def redirect(stream, fd_expect):
|
||||||
|
try:
|
||||||
|
fd = stream.fileno()
|
||||||
|
if fd == fd_expect and stream.isatty():
|
||||||
|
os.close(fd)
|
||||||
|
os.dup2(fd_null, fd)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
redirect(sys.stdout, 1)
|
||||||
|
redirect(sys.stderr, 2)
|
||||||
|
|
||||||
|
|
||||||
|
def seed():
|
||||||
|
try:
|
||||||
|
random.seed(os.urandom(64))
|
||||||
|
except NotImplementedError:
|
||||||
|
random.seed('%s.%s' % (time.time(), os.getpid()))
|
||||||
|
|
||||||
|
|
||||||
|
def check_is_writeable(path):
|
||||||
|
try:
|
||||||
|
f = open(path, 'a')
|
||||||
|
except IOError as e:
|
||||||
|
raise RuntimeError("Error: '%s' isn't writable [%r]" % (path, e))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
def to_bytestring(value, encoding="utf8"):
|
||||||
|
"""Converts a string argument to a byte string"""
|
||||||
|
if isinstance(value, bytes):
|
||||||
|
return value
|
||||||
|
if not isinstance(value, text_type):
|
||||||
|
raise TypeError('%r is not a string' % value)
|
||||||
|
|
||||||
|
return value.encode(encoding)
|
||||||
|
|
||||||
|
def has_fileno(obj):
|
||||||
|
if not hasattr(obj, "fileno"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# check BytesIO case and maybe others
|
||||||
|
try:
|
||||||
|
obj.fileno()
|
||||||
|
except (AttributeError, IOError, io.UnsupportedOperation):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def warn(msg):
|
||||||
|
print("!!!", file=sys.stderr)
|
||||||
|
|
||||||
|
lines = msg.splitlines()
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if i == 0:
|
||||||
|
line = "WARNING: %s" % line
|
||||||
|
print("!!! %s" % line, file=sys.stderr)
|
||||||
|
|
||||||
|
print("!!!\n", file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def make_fail_app(msg):
|
||||||
|
msg = to_bytestring(msg)
|
||||||
|
|
||||||
|
def app(environ, start_response):
|
||||||
|
start_response("500 Internal Server Error", [
|
||||||
|
("Content-Type", "text/plain"),
|
||||||
|
("Content-Length", str(len(msg)))
|
||||||
|
])
|
||||||
|
return [msg]
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
def split_request_uri(uri):
|
||||||
|
if uri.startswith("//"):
|
||||||
|
# When the path starts with //, urlsplit considers it as a
|
||||||
|
# relative uri while the RFC says we should consider it as abs_path
|
||||||
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
|
||||||
|
# We use temporary dot prefix to workaround this behaviour
|
||||||
|
parts = _compat.urlsplit("." + uri)
|
||||||
|
return parts._replace(path=parts.path[1:])
|
||||||
|
|
||||||
|
return _compat.urlsplit(uri)
|
@ -0,0 +1,22 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# supported gunicorn workers.
|
||||||
|
SUPPORTED_WORKERS = {
|
||||||
|
"sync": "gunicorn.workers.sync.SyncWorker",
|
||||||
|
"eventlet": "gunicorn.workers.geventlet.EventletWorker",
|
||||||
|
"gevent": "gunicorn.workers.ggevent.GeventWorker",
|
||||||
|
"gevent_wsgi": "gunicorn.workers.ggevent.GeventPyWSGIWorker",
|
||||||
|
"gevent_pywsgi": "gunicorn.workers.ggevent.GeventPyWSGIWorker",
|
||||||
|
"tornado": "gunicorn.workers.gtornado.TornadoWorker",
|
||||||
|
"gthread": "gunicorn.workers.gthread.ThreadWorker",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 4):
|
||||||
|
# gaiohttp worker can be used with Python 3.4+ only.
|
||||||
|
SUPPORTED_WORKERS["gaiohttp"] = "gunicorn.workers.gaiohttp.AiohttpWorker"
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
168
venv/lib/python3.6/site-packages/gunicorn/workers/_gaiohttp.py
Normal file
168
venv/lib/python3.6/site-packages/gunicorn/workers/_gaiohttp.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import datetime
|
||||||
|
import functools
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ssl
|
||||||
|
except ImportError:
|
||||||
|
ssl = None
|
||||||
|
|
||||||
|
import gunicorn.workers.base as base
|
||||||
|
|
||||||
|
from aiohttp.wsgi import WSGIServerHttpProtocol as OldWSGIServerHttpProtocol
|
||||||
|
|
||||||
|
|
||||||
|
class WSGIServerHttpProtocol(OldWSGIServerHttpProtocol):
|
||||||
|
def log_access(self, request, environ, response, time):
|
||||||
|
self.logger.access(response, request, environ, datetime.timedelta(0, 0, time))
|
||||||
|
|
||||||
|
|
||||||
|
class AiohttpWorker(base.Worker):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kw): # pragma: no cover
|
||||||
|
super().__init__(*args, **kw)
|
||||||
|
cfg = self.cfg
|
||||||
|
if cfg.is_ssl:
|
||||||
|
self.ssl_context = self._create_ssl_context(cfg)
|
||||||
|
else:
|
||||||
|
self.ssl_context = None
|
||||||
|
self.servers = []
|
||||||
|
self.connections = {}
|
||||||
|
|
||||||
|
def init_process(self):
|
||||||
|
# create new event_loop after fork
|
||||||
|
asyncio.get_event_loop().close()
|
||||||
|
|
||||||
|
self.loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(self.loop)
|
||||||
|
|
||||||
|
super().init_process()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._runner = asyncio.ensure_future(self._run(), loop=self.loop)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.loop.run_until_complete(self._runner)
|
||||||
|
finally:
|
||||||
|
self.loop.close()
|
||||||
|
|
||||||
|
def wrap_protocol(self, proto):
|
||||||
|
proto.connection_made = _wrp(
|
||||||
|
proto, proto.connection_made, self.connections)
|
||||||
|
proto.connection_lost = _wrp(
|
||||||
|
proto, proto.connection_lost, self.connections, False)
|
||||||
|
return proto
|
||||||
|
|
||||||
|
def factory(self, wsgi, addr):
|
||||||
|
# are we in debug level
|
||||||
|
is_debug = self.log.loglevel == logging.DEBUG
|
||||||
|
|
||||||
|
proto = WSGIServerHttpProtocol(
|
||||||
|
wsgi, readpayload=True,
|
||||||
|
loop=self.loop,
|
||||||
|
log=self.log,
|
||||||
|
debug=is_debug,
|
||||||
|
keep_alive=self.cfg.keepalive,
|
||||||
|
access_log=self.log.access_log,
|
||||||
|
access_log_format=self.cfg.access_log_format)
|
||||||
|
return self.wrap_protocol(proto)
|
||||||
|
|
||||||
|
def get_factory(self, sock, addr):
|
||||||
|
return functools.partial(self.factory, self.wsgi, addr)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def close(self):
|
||||||
|
try:
|
||||||
|
if hasattr(self.wsgi, 'close'):
|
||||||
|
yield from self.wsgi.close()
|
||||||
|
except:
|
||||||
|
self.log.exception('Process shutdown exception')
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _run(self):
|
||||||
|
for sock in self.sockets:
|
||||||
|
factory = self.get_factory(sock.sock, sock.cfg_addr)
|
||||||
|
self.servers.append(
|
||||||
|
(yield from self._create_server(factory, sock)))
|
||||||
|
|
||||||
|
# If our parent changed then we shut down.
|
||||||
|
pid = os.getpid()
|
||||||
|
try:
|
||||||
|
while self.alive or self.connections:
|
||||||
|
self.notify()
|
||||||
|
|
||||||
|
if (self.alive and
|
||||||
|
pid == os.getpid() and self.ppid != os.getppid()):
|
||||||
|
self.log.info("Parent changed, shutting down: %s", self)
|
||||||
|
self.alive = False
|
||||||
|
|
||||||
|
# stop accepting requests
|
||||||
|
if not self.alive:
|
||||||
|
if self.servers:
|
||||||
|
self.log.info(
|
||||||
|
"Stopping server: %s, connections: %s",
|
||||||
|
pid, len(self.connections))
|
||||||
|
for server in self.servers:
|
||||||
|
server.close()
|
||||||
|
self.servers.clear()
|
||||||
|
|
||||||
|
# prepare connections for closing
|
||||||
|
for conn in self.connections.values():
|
||||||
|
if hasattr(conn, 'closing'):
|
||||||
|
conn.closing()
|
||||||
|
|
||||||
|
yield from asyncio.sleep(1.0, loop=self.loop)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.servers:
|
||||||
|
for server in self.servers:
|
||||||
|
server.close()
|
||||||
|
|
||||||
|
yield from self.close()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _create_server(self, factory, sock):
|
||||||
|
return self.loop.create_server(factory, sock=sock.sock,
|
||||||
|
ssl=self.ssl_context)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_ssl_context(cfg):
|
||||||
|
""" Creates SSLContext instance for usage in asyncio.create_server.
|
||||||
|
|
||||||
|
See ssl.SSLSocket.__init__ for more details.
|
||||||
|
"""
|
||||||
|
ctx = ssl.SSLContext(cfg.ssl_version)
|
||||||
|
ctx.load_cert_chain(cfg.certfile, cfg.keyfile)
|
||||||
|
ctx.verify_mode = cfg.cert_reqs
|
||||||
|
if cfg.ca_certs:
|
||||||
|
ctx.load_verify_locations(cfg.ca_certs)
|
||||||
|
if cfg.ciphers:
|
||||||
|
ctx.set_ciphers(cfg.ciphers)
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
class _wrp:
|
||||||
|
|
||||||
|
def __init__(self, proto, meth, tracking, add=True):
|
||||||
|
self._proto = proto
|
||||||
|
self._id = id(proto)
|
||||||
|
self._meth = meth
|
||||||
|
self._tracking = tracking
|
||||||
|
self._add = add
|
||||||
|
|
||||||
|
def __call__(self, *args):
|
||||||
|
if self._add:
|
||||||
|
self._tracking[self._id] = self._proto
|
||||||
|
elif self._id in self._tracking:
|
||||||
|
del self._tracking[self._id]
|
||||||
|
|
||||||
|
conn = self._meth(*args)
|
||||||
|
return conn
|
264
venv/lib/python3.6/site-packages/gunicorn/workers/base.py
Normal file
264
venv/lib/python3.6/site-packages/gunicorn/workers/base.py
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
from random import randint
|
||||||
|
import signal
|
||||||
|
from ssl import SSLError
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from gunicorn import six
|
||||||
|
from gunicorn import util
|
||||||
|
from gunicorn.workers.workertmp import WorkerTmp
|
||||||
|
from gunicorn.reloader import reloader_engines
|
||||||
|
from gunicorn.http.errors import (
|
||||||
|
InvalidHeader, InvalidHeaderName, InvalidRequestLine, InvalidRequestMethod,
|
||||||
|
InvalidHTTPVersion, LimitRequestLine, LimitRequestHeaders,
|
||||||
|
)
|
||||||
|
from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
|
||||||
|
from gunicorn.http.errors import InvalidSchemeHeaders
|
||||||
|
from gunicorn.http.wsgi import default_environ, Response
|
||||||
|
from gunicorn.six import MAXSIZE
|
||||||
|
|
||||||
|
|
||||||
|
class Worker(object):
|
||||||
|
|
||||||
|
SIGNALS = [getattr(signal, "SIG%s" % x)
|
||||||
|
for x in "ABRT HUP QUIT INT TERM USR1 USR2 WINCH CHLD".split()]
|
||||||
|
|
||||||
|
PIPE = []
|
||||||
|
|
||||||
|
def __init__(self, age, ppid, sockets, app, timeout, cfg, log):
|
||||||
|
"""\
|
||||||
|
This is called pre-fork so it shouldn't do anything to the
|
||||||
|
current process. If there's a need to make process wide
|
||||||
|
changes you'll want to do that in ``self.init_process()``.
|
||||||
|
"""
|
||||||
|
self.age = age
|
||||||
|
self.pid = "[booting]"
|
||||||
|
self.ppid = ppid
|
||||||
|
self.sockets = sockets
|
||||||
|
self.app = app
|
||||||
|
self.timeout = timeout
|
||||||
|
self.cfg = cfg
|
||||||
|
self.booted = False
|
||||||
|
self.aborted = False
|
||||||
|
self.reloader = None
|
||||||
|
|
||||||
|
self.nr = 0
|
||||||
|
jitter = randint(0, cfg.max_requests_jitter)
|
||||||
|
self.max_requests = cfg.max_requests + jitter or MAXSIZE
|
||||||
|
self.alive = True
|
||||||
|
self.log = log
|
||||||
|
self.tmp = WorkerTmp(cfg)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<Worker %s>" % self.pid
|
||||||
|
|
||||||
|
def notify(self):
|
||||||
|
"""\
|
||||||
|
Your worker subclass must arrange to have this method called
|
||||||
|
once every ``self.timeout`` seconds. If you fail in accomplishing
|
||||||
|
this task, the master process will murder your workers.
|
||||||
|
"""
|
||||||
|
self.tmp.notify()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""\
|
||||||
|
This is the mainloop of a worker process. You should override
|
||||||
|
this method in a subclass to provide the intended behaviour
|
||||||
|
for your particular evil schemes.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def init_process(self):
|
||||||
|
"""\
|
||||||
|
If you override this method in a subclass, the last statement
|
||||||
|
in the function should be to call this method with
|
||||||
|
super(MyWorkerClass, self).init_process() so that the ``run()``
|
||||||
|
loop is initiated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# set environment' variables
|
||||||
|
if self.cfg.env:
|
||||||
|
for k, v in self.cfg.env.items():
|
||||||
|
os.environ[k] = v
|
||||||
|
|
||||||
|
util.set_owner_process(self.cfg.uid, self.cfg.gid,
|
||||||
|
initgroups=self.cfg.initgroups)
|
||||||
|
|
||||||
|
# Reseed the random number generator
|
||||||
|
util.seed()
|
||||||
|
|
||||||
|
# For waking ourselves up
|
||||||
|
self.PIPE = os.pipe()
|
||||||
|
for p in self.PIPE:
|
||||||
|
util.set_non_blocking(p)
|
||||||
|
util.close_on_exec(p)
|
||||||
|
|
||||||
|
# Prevent fd inheritance
|
||||||
|
for s in self.sockets:
|
||||||
|
util.close_on_exec(s)
|
||||||
|
util.close_on_exec(self.tmp.fileno())
|
||||||
|
|
||||||
|
self.wait_fds = self.sockets + [self.PIPE[0]]
|
||||||
|
|
||||||
|
self.log.close_on_exec()
|
||||||
|
|
||||||
|
self.init_signals()
|
||||||
|
|
||||||
|
# start the reloader
|
||||||
|
if self.cfg.reload:
|
||||||
|
def changed(fname):
|
||||||
|
self.log.info("Worker reloading: %s modified", fname)
|
||||||
|
self.alive = False
|
||||||
|
self.cfg.worker_int(self)
|
||||||
|
time.sleep(0.1)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
reloader_cls = reloader_engines[self.cfg.reload_engine]
|
||||||
|
self.reloader = reloader_cls(extra_files=self.cfg.reload_extra_files,
|
||||||
|
callback=changed)
|
||||||
|
self.reloader.start()
|
||||||
|
|
||||||
|
self.load_wsgi()
|
||||||
|
self.cfg.post_worker_init(self)
|
||||||
|
|
||||||
|
# Enter main run loop
|
||||||
|
self.booted = True
|
||||||
|
self.run()
|
||||||
|
|
||||||
|
def load_wsgi(self):
|
||||||
|
try:
|
||||||
|
self.wsgi = self.app.wsgi()
|
||||||
|
except SyntaxError as e:
|
||||||
|
if not self.cfg.reload:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.log.exception(e)
|
||||||
|
|
||||||
|
# fix from PR #1228
|
||||||
|
# storing the traceback into exc_tb will create a circular reference.
|
||||||
|
# per https://docs.python.org/2/library/sys.html#sys.exc_info warning,
|
||||||
|
# delete the traceback after use.
|
||||||
|
try:
|
||||||
|
_, exc_val, exc_tb = sys.exc_info()
|
||||||
|
self.reloader.add_extra_file(exc_val.filename)
|
||||||
|
|
||||||
|
tb_string = six.StringIO()
|
||||||
|
traceback.print_tb(exc_tb, file=tb_string)
|
||||||
|
self.wsgi = util.make_fail_app(tb_string.getvalue())
|
||||||
|
finally:
|
||||||
|
del exc_tb
|
||||||
|
|
||||||
|
def init_signals(self):
|
||||||
|
# reset signaling
|
||||||
|
for s in self.SIGNALS:
|
||||||
|
signal.signal(s, signal.SIG_DFL)
|
||||||
|
# init new signaling
|
||||||
|
signal.signal(signal.SIGQUIT, self.handle_quit)
|
||||||
|
signal.signal(signal.SIGTERM, self.handle_exit)
|
||||||
|
signal.signal(signal.SIGINT, self.handle_quit)
|
||||||
|
signal.signal(signal.SIGWINCH, self.handle_winch)
|
||||||
|
signal.signal(signal.SIGUSR1, self.handle_usr1)
|
||||||
|
signal.signal(signal.SIGABRT, self.handle_abort)
|
||||||
|
|
||||||
|
# Don't let SIGTERM and SIGUSR1 disturb active requests
|
||||||
|
# by interrupting system calls
|
||||||
|
if hasattr(signal, 'siginterrupt'): # python >= 2.6
|
||||||
|
signal.siginterrupt(signal.SIGTERM, False)
|
||||||
|
signal.siginterrupt(signal.SIGUSR1, False)
|
||||||
|
|
||||||
|
if hasattr(signal, 'set_wakeup_fd'):
|
||||||
|
signal.set_wakeup_fd(self.PIPE[1])
|
||||||
|
|
||||||
|
def handle_usr1(self, sig, frame):
|
||||||
|
self.log.reopen_files()
|
||||||
|
|
||||||
|
def handle_exit(self, sig, frame):
|
||||||
|
self.alive = False
|
||||||
|
|
||||||
|
def handle_quit(self, sig, frame):
|
||||||
|
self.alive = False
|
||||||
|
# worker_int callback
|
||||||
|
self.cfg.worker_int(self)
|
||||||
|
time.sleep(0.1)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def handle_abort(self, sig, frame):
|
||||||
|
self.alive = False
|
||||||
|
self.cfg.worker_abort(self)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def handle_error(self, req, client, addr, exc):
|
||||||
|
request_start = datetime.now()
|
||||||
|
addr = addr or ('', -1) # unix socket case
|
||||||
|
if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod,
|
||||||
|
InvalidHTTPVersion, InvalidHeader, InvalidHeaderName,
|
||||||
|
LimitRequestLine, LimitRequestHeaders,
|
||||||
|
InvalidProxyLine, ForbiddenProxyRequest,
|
||||||
|
InvalidSchemeHeaders,
|
||||||
|
SSLError)):
|
||||||
|
|
||||||
|
status_int = 400
|
||||||
|
reason = "Bad Request"
|
||||||
|
|
||||||
|
if isinstance(exc, InvalidRequestLine):
|
||||||
|
mesg = "Invalid Request Line '%s'" % str(exc)
|
||||||
|
elif isinstance(exc, InvalidRequestMethod):
|
||||||
|
mesg = "Invalid Method '%s'" % str(exc)
|
||||||
|
elif isinstance(exc, InvalidHTTPVersion):
|
||||||
|
mesg = "Invalid HTTP Version '%s'" % str(exc)
|
||||||
|
elif isinstance(exc, (InvalidHeaderName, InvalidHeader,)):
|
||||||
|
mesg = "%s" % str(exc)
|
||||||
|
if not req and hasattr(exc, "req"):
|
||||||
|
req = exc.req # for access log
|
||||||
|
elif isinstance(exc, LimitRequestLine):
|
||||||
|
mesg = "%s" % str(exc)
|
||||||
|
elif isinstance(exc, LimitRequestHeaders):
|
||||||
|
mesg = "Error parsing headers: '%s'" % str(exc)
|
||||||
|
elif isinstance(exc, InvalidProxyLine):
|
||||||
|
mesg = "'%s'" % str(exc)
|
||||||
|
elif isinstance(exc, ForbiddenProxyRequest):
|
||||||
|
reason = "Forbidden"
|
||||||
|
mesg = "Request forbidden"
|
||||||
|
status_int = 403
|
||||||
|
elif isinstance(exc, InvalidSchemeHeaders):
|
||||||
|
mesg = "%s" % str(exc)
|
||||||
|
elif isinstance(exc, SSLError):
|
||||||
|
reason = "Forbidden"
|
||||||
|
mesg = "'%s'" % str(exc)
|
||||||
|
status_int = 403
|
||||||
|
|
||||||
|
msg = "Invalid request from ip={ip}: {error}"
|
||||||
|
self.log.debug(msg.format(ip=addr[0], error=str(exc)))
|
||||||
|
else:
|
||||||
|
if hasattr(req, "uri"):
|
||||||
|
self.log.exception("Error handling request %s", req.uri)
|
||||||
|
status_int = 500
|
||||||
|
reason = "Internal Server Error"
|
||||||
|
mesg = ""
|
||||||
|
|
||||||
|
if req is not None:
|
||||||
|
request_time = datetime.now() - request_start
|
||||||
|
environ = default_environ(req, client, self.cfg)
|
||||||
|
environ['REMOTE_ADDR'] = addr[0]
|
||||||
|
environ['REMOTE_PORT'] = str(addr[1])
|
||||||
|
resp = Response(req, client, self.cfg)
|
||||||
|
resp.status = "%s %s" % (status_int, reason)
|
||||||
|
resp.response_length = len(mesg)
|
||||||
|
self.log.access(resp, req, environ, request_time)
|
||||||
|
|
||||||
|
try:
|
||||||
|
util.write_error(client, status_int, reason, mesg)
|
||||||
|
except:
|
||||||
|
self.log.debug("Failed to send error message.")
|
||||||
|
|
||||||
|
def handle_winch(self, sig, fname):
|
||||||
|
# Ignore SIGWINCH in worker. Fixes a crash on OpenBSD.
|
||||||
|
self.log.debug("worker: SIGWINCH ignored.")
|
147
venv/lib/python3.6/site-packages/gunicorn/workers/base_async.py
Normal file
147
venv/lib/python3.6/site-packages/gunicorn/workers/base_async.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import errno
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import gunicorn.http as http
|
||||||
|
import gunicorn.http.wsgi as wsgi
|
||||||
|
import gunicorn.util as util
|
||||||
|
import gunicorn.workers.base as base
|
||||||
|
from gunicorn import six
|
||||||
|
|
||||||
|
ALREADY_HANDLED = object()
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncWorker(base.Worker):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AsyncWorker, self).__init__(*args, **kwargs)
|
||||||
|
self.worker_connections = self.cfg.worker_connections
|
||||||
|
|
||||||
|
def timeout_ctx(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def is_already_handled(self, respiter):
|
||||||
|
# some workers will need to overload this function to raise a StopIteration
|
||||||
|
return respiter == ALREADY_HANDLED
|
||||||
|
|
||||||
|
def handle(self, listener, client, addr):
|
||||||
|
req = None
|
||||||
|
try:
|
||||||
|
parser = http.RequestParser(self.cfg, client)
|
||||||
|
try:
|
||||||
|
listener_name = listener.getsockname()
|
||||||
|
if not self.cfg.keepalive:
|
||||||
|
req = six.next(parser)
|
||||||
|
self.handle_request(listener_name, req, client, addr)
|
||||||
|
else:
|
||||||
|
# keepalive loop
|
||||||
|
proxy_protocol_info = {}
|
||||||
|
while True:
|
||||||
|
req = None
|
||||||
|
with self.timeout_ctx():
|
||||||
|
req = six.next(parser)
|
||||||
|
if not req:
|
||||||
|
break
|
||||||
|
if req.proxy_protocol_info:
|
||||||
|
proxy_protocol_info = req.proxy_protocol_info
|
||||||
|
else:
|
||||||
|
req.proxy_protocol_info = proxy_protocol_info
|
||||||
|
self.handle_request(listener_name, req, client, addr)
|
||||||
|
except http.errors.NoMoreData as e:
|
||||||
|
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||||
|
except StopIteration as e:
|
||||||
|
self.log.debug("Closing connection. %s", e)
|
||||||
|
except ssl.SSLError:
|
||||||
|
# pass to next try-except level
|
||||||
|
six.reraise(*sys.exc_info())
|
||||||
|
except EnvironmentError:
|
||||||
|
# pass to next try-except level
|
||||||
|
six.reraise(*sys.exc_info())
|
||||||
|
except Exception as e:
|
||||||
|
self.handle_error(req, client, addr, e)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
if e.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
self.log.debug("ssl connection closed")
|
||||||
|
client.close()
|
||||||
|
else:
|
||||||
|
self.log.debug("Error processing SSL request.")
|
||||||
|
self.handle_error(req, client, addr, e)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno not in (errno.EPIPE, errno.ECONNRESET):
|
||||||
|
self.log.exception("Socket error processing request.")
|
||||||
|
else:
|
||||||
|
if e.errno == errno.ECONNRESET:
|
||||||
|
self.log.debug("Ignoring connection reset")
|
||||||
|
else:
|
||||||
|
self.log.debug("Ignoring EPIPE")
|
||||||
|
except Exception as e:
|
||||||
|
self.handle_error(req, client, addr, e)
|
||||||
|
finally:
|
||||||
|
util.close(client)
|
||||||
|
|
||||||
|
def handle_request(self, listener_name, req, sock, addr):
|
||||||
|
request_start = datetime.now()
|
||||||
|
environ = {}
|
||||||
|
resp = None
|
||||||
|
try:
|
||||||
|
self.cfg.pre_request(self, req)
|
||||||
|
resp, environ = wsgi.create(req, sock, addr,
|
||||||
|
listener_name, self.cfg)
|
||||||
|
environ["wsgi.multithread"] = True
|
||||||
|
self.nr += 1
|
||||||
|
if self.alive and self.nr >= self.max_requests:
|
||||||
|
self.log.info("Autorestarting worker after current request.")
|
||||||
|
resp.force_close()
|
||||||
|
self.alive = False
|
||||||
|
|
||||||
|
if not self.cfg.keepalive:
|
||||||
|
resp.force_close()
|
||||||
|
|
||||||
|
respiter = self.wsgi(environ, resp.start_response)
|
||||||
|
if self.is_already_handled(respiter):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
if isinstance(respiter, environ['wsgi.file_wrapper']):
|
||||||
|
resp.write_file(respiter)
|
||||||
|
else:
|
||||||
|
for item in respiter:
|
||||||
|
resp.write(item)
|
||||||
|
resp.close()
|
||||||
|
request_time = datetime.now() - request_start
|
||||||
|
self.log.access(resp, req, environ, request_time)
|
||||||
|
finally:
|
||||||
|
if hasattr(respiter, "close"):
|
||||||
|
respiter.close()
|
||||||
|
if resp.should_close():
|
||||||
|
raise StopIteration()
|
||||||
|
except StopIteration:
|
||||||
|
raise
|
||||||
|
except EnvironmentError:
|
||||||
|
# If the original exception was a socket.error we delegate
|
||||||
|
# handling it to the caller (where handle() might ignore it)
|
||||||
|
six.reraise(*sys.exc_info())
|
||||||
|
except Exception:
|
||||||
|
if resp and resp.headers_sent:
|
||||||
|
# If the requests have already been sent, we should close the
|
||||||
|
# connection to indicate the error.
|
||||||
|
self.log.exception("Error handling request")
|
||||||
|
try:
|
||||||
|
sock.shutdown(socket.SHUT_RDWR)
|
||||||
|
sock.close()
|
||||||
|
except EnvironmentError:
|
||||||
|
pass
|
||||||
|
raise StopIteration()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
self.cfg.post_request(self, req, environ, resp)
|
||||||
|
except Exception:
|
||||||
|
self.log.exception("Exception in post_request hook")
|
||||||
|
return True
|
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from gunicorn import util
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 4):
|
||||||
|
try:
|
||||||
|
import aiohttp # pylint: disable=unused-import
|
||||||
|
except ImportError:
|
||||||
|
raise RuntimeError("You need aiohttp installed to use this worker.")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
from aiohttp.worker import GunicornWebWorker as AiohttpWorker
|
||||||
|
except ImportError:
|
||||||
|
from gunicorn.workers._gaiohttp import AiohttpWorker
|
||||||
|
|
||||||
|
util.warn(
|
||||||
|
"The 'gaiohttp' worker is deprecated. See --worker-class "
|
||||||
|
"documentation for more information."
|
||||||
|
)
|
||||||
|
__all__ = ['AiohttpWorker']
|
||||||
|
else:
|
||||||
|
raise RuntimeError("You need Python >= 3.4 to use the gaiohttp worker")
|
148
venv/lib/python3.6/site-packages/gunicorn/workers/geventlet.py
Normal file
148
venv/lib/python3.6/site-packages/gunicorn/workers/geventlet.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
import errno
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import eventlet
|
||||||
|
except ImportError:
|
||||||
|
raise RuntimeError("You need eventlet installed to use this worker.")
|
||||||
|
|
||||||
|
# validate the eventlet version
|
||||||
|
if eventlet.version_info < (0, 9, 7):
|
||||||
|
raise RuntimeError("You need eventlet >= 0.9.7")
|
||||||
|
|
||||||
|
|
||||||
|
from eventlet import hubs, greenthread
|
||||||
|
from eventlet.greenio import GreenSocket
|
||||||
|
from eventlet.hubs import trampoline
|
||||||
|
from eventlet.wsgi import ALREADY_HANDLED as EVENTLET_ALREADY_HANDLED
|
||||||
|
import greenlet
|
||||||
|
|
||||||
|
from gunicorn.http.wsgi import sendfile as o_sendfile
|
||||||
|
from gunicorn.workers.base_async import AsyncWorker
|
||||||
|
|
||||||
|
def _eventlet_sendfile(fdout, fdin, offset, nbytes):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
return o_sendfile(fdout, fdin, offset, nbytes)
|
||||||
|
except OSError as e:
|
||||||
|
if e.args[0] == errno.EAGAIN:
|
||||||
|
trampoline(fdout, write=True)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _eventlet_serve(sock, handle, concurrency):
|
||||||
|
"""
|
||||||
|
Serve requests forever.
|
||||||
|
|
||||||
|
This code is nearly identical to ``eventlet.convenience.serve`` except
|
||||||
|
that it attempts to join the pool at the end, which allows for gunicorn
|
||||||
|
graceful shutdowns.
|
||||||
|
"""
|
||||||
|
pool = eventlet.greenpool.GreenPool(concurrency)
|
||||||
|
server_gt = eventlet.greenthread.getcurrent()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
conn, addr = sock.accept()
|
||||||
|
gt = pool.spawn(handle, conn, addr)
|
||||||
|
gt.link(_eventlet_stop, server_gt, conn)
|
||||||
|
conn, addr, gt = None, None, None
|
||||||
|
except eventlet.StopServe:
|
||||||
|
sock.close()
|
||||||
|
pool.waitall()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def _eventlet_stop(client, server, conn):
|
||||||
|
"""
|
||||||
|
Stop a greenlet handling a request and close its connection.
|
||||||
|
|
||||||
|
This code is lifted from eventlet so as not to depend on undocumented
|
||||||
|
functions in the library.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
client.wait()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
except greenlet.GreenletExit:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
greenthread.kill(server, *sys.exc_info())
|
||||||
|
|
||||||
|
|
||||||
|
def patch_sendfile():
|
||||||
|
from gunicorn.http import wsgi
|
||||||
|
|
||||||
|
if o_sendfile is not None:
|
||||||
|
setattr(wsgi, "sendfile", _eventlet_sendfile)
|
||||||
|
|
||||||
|
|
||||||
|
class EventletWorker(AsyncWorker):
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
hubs.use_hub()
|
||||||
|
eventlet.monkey_patch(os=False)
|
||||||
|
patch_sendfile()
|
||||||
|
|
||||||
|
def is_already_handled(self, respiter):
|
||||||
|
if respiter == EVENTLET_ALREADY_HANDLED:
|
||||||
|
raise StopIteration()
|
||||||
|
else:
|
||||||
|
return super(EventletWorker, self).is_already_handled(respiter)
|
||||||
|
|
||||||
|
def init_process(self):
|
||||||
|
super(EventletWorker, self).init_process()
|
||||||
|
self.patch()
|
||||||
|
|
||||||
|
def handle_quit(self, sig, frame):
|
||||||
|
eventlet.spawn(super(EventletWorker, self).handle_quit, sig, frame)
|
||||||
|
|
||||||
|
def handle_usr1(self, sig, frame):
|
||||||
|
eventlet.spawn(super(EventletWorker, self).handle_usr1, sig, frame)
|
||||||
|
|
||||||
|
def timeout_ctx(self):
|
||||||
|
return eventlet.Timeout(self.cfg.keepalive or None, False)
|
||||||
|
|
||||||
|
def handle(self, listener, client, addr):
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
client = eventlet.wrap_ssl(client, server_side=True,
|
||||||
|
**self.cfg.ssl_options)
|
||||||
|
|
||||||
|
super(EventletWorker, self).handle(listener, client, addr)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
acceptors = []
|
||||||
|
for sock in self.sockets:
|
||||||
|
gsock = GreenSocket(sock)
|
||||||
|
gsock.setblocking(1)
|
||||||
|
hfun = partial(self.handle, gsock)
|
||||||
|
acceptor = eventlet.spawn(_eventlet_serve, gsock, hfun,
|
||||||
|
self.worker_connections)
|
||||||
|
|
||||||
|
acceptors.append(acceptor)
|
||||||
|
eventlet.sleep(0.0)
|
||||||
|
|
||||||
|
while self.alive:
|
||||||
|
self.notify()
|
||||||
|
eventlet.sleep(1.0)
|
||||||
|
|
||||||
|
self.notify()
|
||||||
|
try:
|
||||||
|
with eventlet.Timeout(self.cfg.graceful_timeout) as t:
|
||||||
|
for a in acceptors:
|
||||||
|
a.kill(eventlet.StopServe())
|
||||||
|
for a in acceptors:
|
||||||
|
a.wait()
|
||||||
|
except eventlet.Timeout as te:
|
||||||
|
if te != t:
|
||||||
|
raise
|
||||||
|
for a in acceptors:
|
||||||
|
a.kill()
|
246
venv/lib/python3.6/site-packages/gunicorn/workers/ggevent.py
Normal file
246
venv/lib/python3.6/site-packages/gunicorn/workers/ggevent.py
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from functools import partial
|
||||||
|
import time
|
||||||
|
|
||||||
|
_socket = __import__("socket")
|
||||||
|
|
||||||
|
# workaround on osx, disable kqueue
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
os.environ['EVENT_NOKQUEUE'] = "1"
|
||||||
|
|
||||||
|
try:
|
||||||
|
import gevent
|
||||||
|
except ImportError:
|
||||||
|
raise RuntimeError("You need gevent installed to use this worker.")
|
||||||
|
from gevent.pool import Pool
|
||||||
|
from gevent.server import StreamServer
|
||||||
|
from gevent.socket import wait_write, socket
|
||||||
|
from gevent import pywsgi
|
||||||
|
|
||||||
|
import gunicorn
|
||||||
|
from gunicorn.http.wsgi import base_environ
|
||||||
|
from gunicorn.workers.base_async import AsyncWorker
|
||||||
|
from gunicorn.http.wsgi import sendfile as o_sendfile
|
||||||
|
|
||||||
|
VERSION = "gevent/%s gunicorn/%s" % (gevent.__version__, gunicorn.__version__)
|
||||||
|
|
||||||
|
def _gevent_sendfile(fdout, fdin, offset, nbytes):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
return o_sendfile(fdout, fdin, offset, nbytes)
|
||||||
|
except OSError as e:
|
||||||
|
if e.args[0] == errno.EAGAIN:
|
||||||
|
wait_write(fdout)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def patch_sendfile():
|
||||||
|
from gunicorn.http import wsgi
|
||||||
|
|
||||||
|
if o_sendfile is not None:
|
||||||
|
setattr(wsgi, "sendfile", _gevent_sendfile)
|
||||||
|
|
||||||
|
|
||||||
|
class GeventWorker(AsyncWorker):
|
||||||
|
|
||||||
|
server_class = None
|
||||||
|
wsgi_handler = None
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
from gevent import monkey
|
||||||
|
monkey.noisy = False
|
||||||
|
|
||||||
|
# if the new version is used make sure to patch subprocess
|
||||||
|
if gevent.version_info[0] == 0:
|
||||||
|
monkey.patch_all()
|
||||||
|
else:
|
||||||
|
monkey.patch_all(subprocess=True)
|
||||||
|
|
||||||
|
# monkey patch sendfile to make it none blocking
|
||||||
|
patch_sendfile()
|
||||||
|
|
||||||
|
# patch sockets
|
||||||
|
sockets = []
|
||||||
|
for s in self.sockets:
|
||||||
|
if sys.version_info[0] == 3:
|
||||||
|
sockets.append(socket(s.FAMILY, _socket.SOCK_STREAM,
|
||||||
|
fileno=s.sock.fileno()))
|
||||||
|
else:
|
||||||
|
sockets.append(socket(s.FAMILY, _socket.SOCK_STREAM,
|
||||||
|
_sock=s))
|
||||||
|
self.sockets = sockets
|
||||||
|
|
||||||
|
def notify(self):
|
||||||
|
super(GeventWorker, self).notify()
|
||||||
|
if self.ppid != os.getppid():
|
||||||
|
self.log.info("Parent changed, shutting down: %s", self)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def timeout_ctx(self):
|
||||||
|
return gevent.Timeout(self.cfg.keepalive, False)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
servers = []
|
||||||
|
ssl_args = {}
|
||||||
|
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
ssl_args = dict(server_side=True, **self.cfg.ssl_options)
|
||||||
|
|
||||||
|
for s in self.sockets:
|
||||||
|
s.setblocking(1)
|
||||||
|
pool = Pool(self.worker_connections)
|
||||||
|
if self.server_class is not None:
|
||||||
|
environ = base_environ(self.cfg)
|
||||||
|
environ.update({
|
||||||
|
"wsgi.multithread": True,
|
||||||
|
"SERVER_SOFTWARE": VERSION,
|
||||||
|
})
|
||||||
|
server = self.server_class(
|
||||||
|
s, application=self.wsgi, spawn=pool, log=self.log,
|
||||||
|
handler_class=self.wsgi_handler, environ=environ,
|
||||||
|
**ssl_args)
|
||||||
|
else:
|
||||||
|
hfun = partial(self.handle, s)
|
||||||
|
server = StreamServer(s, handle=hfun, spawn=pool, **ssl_args)
|
||||||
|
|
||||||
|
server.start()
|
||||||
|
servers.append(server)
|
||||||
|
|
||||||
|
while self.alive:
|
||||||
|
self.notify()
|
||||||
|
gevent.sleep(1.0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Stop accepting requests
|
||||||
|
for server in servers:
|
||||||
|
if hasattr(server, 'close'): # gevent 1.0
|
||||||
|
server.close()
|
||||||
|
if hasattr(server, 'kill'): # gevent < 1.0
|
||||||
|
server.kill()
|
||||||
|
|
||||||
|
# Handle current requests until graceful_timeout
|
||||||
|
ts = time.time()
|
||||||
|
while time.time() - ts <= self.cfg.graceful_timeout:
|
||||||
|
accepting = 0
|
||||||
|
for server in servers:
|
||||||
|
if server.pool.free_count() != server.pool.size:
|
||||||
|
accepting += 1
|
||||||
|
|
||||||
|
# if no server is accepting a connection, we can exit
|
||||||
|
if not accepting:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.notify()
|
||||||
|
gevent.sleep(1.0)
|
||||||
|
|
||||||
|
# Force kill all active the handlers
|
||||||
|
self.log.warning("Worker graceful timeout (pid:%s)" % self.pid)
|
||||||
|
for server in servers:
|
||||||
|
server.stop(timeout=1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle(self, listener, client, addr):
|
||||||
|
# Connected socket timeout defaults to socket.getdefaulttimeout().
|
||||||
|
# This forces to blocking mode.
|
||||||
|
client.setblocking(1)
|
||||||
|
super(GeventWorker, self).handle(listener, client, addr)
|
||||||
|
|
||||||
|
def handle_request(self, listener_name, req, sock, addr):
|
||||||
|
try:
|
||||||
|
super(GeventWorker, self).handle_request(listener_name, req, sock,
|
||||||
|
addr)
|
||||||
|
except gevent.GreenletExit:
|
||||||
|
pass
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_quit(self, sig, frame):
|
||||||
|
# Move this out of the signal handler so we can use
|
||||||
|
# blocking calls. See #1126
|
||||||
|
gevent.spawn(super(GeventWorker, self).handle_quit, sig, frame)
|
||||||
|
|
||||||
|
def handle_usr1(self, sig, frame):
|
||||||
|
# Make the gevent workers handle the usr1 signal
|
||||||
|
# by deferring to a new greenlet. See #1645
|
||||||
|
gevent.spawn(super(GeventWorker, self).handle_usr1, sig, frame)
|
||||||
|
|
||||||
|
if gevent.version_info[0] == 0:
|
||||||
|
|
||||||
|
def init_process(self):
|
||||||
|
# monkey patch here
|
||||||
|
self.patch()
|
||||||
|
|
||||||
|
# reinit the hub
|
||||||
|
import gevent.core
|
||||||
|
gevent.core.reinit()
|
||||||
|
|
||||||
|
#gevent 0.13 and older doesn't reinitialize dns for us after forking
|
||||||
|
#here's the workaround
|
||||||
|
gevent.core.dns_shutdown(fail_requests=1)
|
||||||
|
gevent.core.dns_init()
|
||||||
|
super(GeventWorker, self).init_process()
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
def init_process(self):
|
||||||
|
# monkey patch here
|
||||||
|
self.patch()
|
||||||
|
|
||||||
|
# reinit the hub
|
||||||
|
from gevent import hub
|
||||||
|
hub.reinit()
|
||||||
|
|
||||||
|
# then initialize the process
|
||||||
|
super(GeventWorker, self).init_process()
|
||||||
|
|
||||||
|
|
||||||
|
class GeventResponse(object):
|
||||||
|
|
||||||
|
status = None
|
||||||
|
headers = None
|
||||||
|
sent = None
|
||||||
|
|
||||||
|
def __init__(self, status, headers, clength):
|
||||||
|
self.status = status
|
||||||
|
self.headers = headers
|
||||||
|
self.sent = clength
|
||||||
|
|
||||||
|
|
||||||
|
class PyWSGIHandler(pywsgi.WSGIHandler):
|
||||||
|
|
||||||
|
def log_request(self):
|
||||||
|
start = datetime.fromtimestamp(self.time_start)
|
||||||
|
finish = datetime.fromtimestamp(self.time_finish)
|
||||||
|
response_time = finish - start
|
||||||
|
resp_headers = getattr(self, 'response_headers', {})
|
||||||
|
resp = GeventResponse(self.status, resp_headers, self.response_length)
|
||||||
|
if hasattr(self, 'headers'):
|
||||||
|
req_headers = self.headers.items()
|
||||||
|
else:
|
||||||
|
req_headers = []
|
||||||
|
self.server.log.access(resp, req_headers, self.environ, response_time)
|
||||||
|
|
||||||
|
def get_environ(self):
|
||||||
|
env = super(PyWSGIHandler, self).get_environ()
|
||||||
|
env['gunicorn.sock'] = self.socket
|
||||||
|
env['RAW_URI'] = self.path
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
class PyWSGIServer(pywsgi.WSGIServer):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GeventPyWSGIWorker(GeventWorker):
|
||||||
|
"The Gevent StreamServer based workers."
|
||||||
|
server_class = PyWSGIServer
|
||||||
|
wsgi_handler = PyWSGIHandler
|
367
venv/lib/python3.6/site-packages/gunicorn/workers/gthread.py
Normal file
367
venv/lib/python3.6/site-packages/gunicorn/workers/gthread.py
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
# design:
|
||||||
|
# a threaded worker accepts connections in the main loop, accepted
|
||||||
|
# connections are are added to the thread pool as a connection job. On
|
||||||
|
# keepalive connections are put back in the loop waiting for an event.
|
||||||
|
# If no event happen after the keep alive timeout, the connectoin is
|
||||||
|
# closed.
|
||||||
|
|
||||||
|
from collections import deque
|
||||||
|
from datetime import datetime
|
||||||
|
import errno
|
||||||
|
from functools import partial
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
from threading import RLock
|
||||||
|
import time
|
||||||
|
|
||||||
|
from .. import http
|
||||||
|
from ..http import wsgi
|
||||||
|
from .. import util
|
||||||
|
from . import base
|
||||||
|
from .. import six
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import concurrent.futures as futures
|
||||||
|
except ImportError:
|
||||||
|
raise RuntimeError("""
|
||||||
|
You need to install the 'futures' package to use this worker with this
|
||||||
|
Python version.
|
||||||
|
""")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from asyncio import selectors
|
||||||
|
except ImportError:
|
||||||
|
from gunicorn import selectors
|
||||||
|
|
||||||
|
|
||||||
|
class TConn(object):
|
||||||
|
|
||||||
|
def __init__(self, cfg, sock, client, server):
|
||||||
|
self.cfg = cfg
|
||||||
|
self.sock = sock
|
||||||
|
self.client = client
|
||||||
|
self.server = server
|
||||||
|
|
||||||
|
self.timeout = None
|
||||||
|
self.parser = None
|
||||||
|
|
||||||
|
# set the socket to non blocking
|
||||||
|
self.sock.setblocking(False)
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.sock.setblocking(True)
|
||||||
|
if self.parser is None:
|
||||||
|
# wrap the socket if needed
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
self.sock = ssl.wrap_socket(self.sock, server_side=True,
|
||||||
|
**self.cfg.ssl_options)
|
||||||
|
|
||||||
|
# initialize the parser
|
||||||
|
self.parser = http.RequestParser(self.cfg, self.sock)
|
||||||
|
|
||||||
|
def set_timeout(self):
|
||||||
|
# set the timeout
|
||||||
|
self.timeout = time.time() + self.cfg.keepalive
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
util.close(self.sock)
|
||||||
|
|
||||||
|
|
||||||
|
class ThreadWorker(base.Worker):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ThreadWorker, self).__init__(*args, **kwargs)
|
||||||
|
self.worker_connections = self.cfg.worker_connections
|
||||||
|
self.max_keepalived = self.cfg.worker_connections - self.cfg.threads
|
||||||
|
# initialise the pool
|
||||||
|
self.tpool = None
|
||||||
|
self.poller = None
|
||||||
|
self._lock = None
|
||||||
|
self.futures = deque()
|
||||||
|
self._keep = deque()
|
||||||
|
self.nr_conns = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_config(cls, cfg, log):
|
||||||
|
max_keepalived = cfg.worker_connections - cfg.threads
|
||||||
|
|
||||||
|
if max_keepalived <= 0 and cfg.keepalive:
|
||||||
|
log.warning("No keepalived connections can be handled. " +
|
||||||
|
"Check the number of worker connections and threads.")
|
||||||
|
|
||||||
|
def init_process(self):
|
||||||
|
self.tpool = futures.ThreadPoolExecutor(max_workers=self.cfg.threads)
|
||||||
|
self.poller = selectors.DefaultSelector()
|
||||||
|
self._lock = RLock()
|
||||||
|
super(ThreadWorker, self).init_process()
|
||||||
|
|
||||||
|
def handle_quit(self, sig, frame):
|
||||||
|
self.alive = False
|
||||||
|
# worker_int callback
|
||||||
|
self.cfg.worker_int(self)
|
||||||
|
self.tpool.shutdown(False)
|
||||||
|
time.sleep(0.1)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def _wrap_future(self, fs, conn):
|
||||||
|
fs.conn = conn
|
||||||
|
self.futures.append(fs)
|
||||||
|
fs.add_done_callback(self.finish_request)
|
||||||
|
|
||||||
|
def enqueue_req(self, conn):
|
||||||
|
conn.init()
|
||||||
|
# submit the connection to a worker
|
||||||
|
fs = self.tpool.submit(self.handle, conn)
|
||||||
|
self._wrap_future(fs, conn)
|
||||||
|
|
||||||
|
def accept(self, server, listener):
|
||||||
|
try:
|
||||||
|
sock, client = listener.accept()
|
||||||
|
# initialize the connection object
|
||||||
|
conn = TConn(self.cfg, sock, client, server)
|
||||||
|
self.nr_conns += 1
|
||||||
|
# enqueue the job
|
||||||
|
self.enqueue_req(conn)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno not in (errno.EAGAIN,
|
||||||
|
errno.ECONNABORTED, errno.EWOULDBLOCK):
|
||||||
|
raise
|
||||||
|
|
||||||
|
def reuse_connection(self, conn, client):
|
||||||
|
with self._lock:
|
||||||
|
# unregister the client from the poller
|
||||||
|
self.poller.unregister(client)
|
||||||
|
# remove the connection from keepalive
|
||||||
|
try:
|
||||||
|
self._keep.remove(conn)
|
||||||
|
except ValueError:
|
||||||
|
# race condition
|
||||||
|
return
|
||||||
|
|
||||||
|
# submit the connection to a worker
|
||||||
|
self.enqueue_req(conn)
|
||||||
|
|
||||||
|
def murder_keepalived(self):
|
||||||
|
now = time.time()
|
||||||
|
while True:
|
||||||
|
with self._lock:
|
||||||
|
try:
|
||||||
|
# remove the connection from the queue
|
||||||
|
conn = self._keep.popleft()
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
|
||||||
|
delta = conn.timeout - now
|
||||||
|
if delta > 0:
|
||||||
|
# add the connection back to the queue
|
||||||
|
with self._lock:
|
||||||
|
self._keep.appendleft(conn)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.nr_conns -= 1
|
||||||
|
# remove the socket from the poller
|
||||||
|
with self._lock:
|
||||||
|
try:
|
||||||
|
self.poller.unregister(conn.sock)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno != errno.EBADF:
|
||||||
|
raise
|
||||||
|
except KeyError:
|
||||||
|
# already removed by the system, continue
|
||||||
|
pass
|
||||||
|
|
||||||
|
# close the socket
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def is_parent_alive(self):
|
||||||
|
# If our parent changed then we shut down.
|
||||||
|
if self.ppid != os.getppid():
|
||||||
|
self.log.info("Parent changed, shutting down: %s", self)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# init listeners, add them to the event loop
|
||||||
|
for sock in self.sockets:
|
||||||
|
sock.setblocking(False)
|
||||||
|
# a race condition during graceful shutdown may make the listener
|
||||||
|
# name unavailable in the request handler so capture it once here
|
||||||
|
server = sock.getsockname()
|
||||||
|
acceptor = partial(self.accept, server)
|
||||||
|
self.poller.register(sock, selectors.EVENT_READ, acceptor)
|
||||||
|
|
||||||
|
while self.alive:
|
||||||
|
# notify the arbiter we are alive
|
||||||
|
self.notify()
|
||||||
|
|
||||||
|
# can we accept more connections?
|
||||||
|
if self.nr_conns < self.worker_connections:
|
||||||
|
# wait for an event
|
||||||
|
events = self.poller.select(1.0)
|
||||||
|
for key, _ in events:
|
||||||
|
callback = key.data
|
||||||
|
callback(key.fileobj)
|
||||||
|
|
||||||
|
# check (but do not wait) for finished requests
|
||||||
|
result = futures.wait(self.futures, timeout=0,
|
||||||
|
return_when=futures.FIRST_COMPLETED)
|
||||||
|
else:
|
||||||
|
# wait for a request to finish
|
||||||
|
result = futures.wait(self.futures, timeout=1.0,
|
||||||
|
return_when=futures.FIRST_COMPLETED)
|
||||||
|
|
||||||
|
# clean up finished requests
|
||||||
|
for fut in result.done:
|
||||||
|
self.futures.remove(fut)
|
||||||
|
|
||||||
|
if not self.is_parent_alive():
|
||||||
|
break
|
||||||
|
|
||||||
|
# hanle keepalive timeouts
|
||||||
|
self.murder_keepalived()
|
||||||
|
|
||||||
|
self.tpool.shutdown(False)
|
||||||
|
self.poller.close()
|
||||||
|
|
||||||
|
for s in self.sockets:
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
futures.wait(self.futures, timeout=self.cfg.graceful_timeout)
|
||||||
|
|
||||||
|
def finish_request(self, fs):
|
||||||
|
if fs.cancelled():
|
||||||
|
self.nr_conns -= 1
|
||||||
|
fs.conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
(keepalive, conn) = fs.result()
|
||||||
|
# if the connection should be kept alived add it
|
||||||
|
# to the eventloop and record it
|
||||||
|
if keepalive:
|
||||||
|
# flag the socket as non blocked
|
||||||
|
conn.sock.setblocking(False)
|
||||||
|
|
||||||
|
# register the connection
|
||||||
|
conn.set_timeout()
|
||||||
|
with self._lock:
|
||||||
|
self._keep.append(conn)
|
||||||
|
|
||||||
|
# add the socket to the event loop
|
||||||
|
self.poller.register(conn.sock, selectors.EVENT_READ,
|
||||||
|
partial(self.reuse_connection, conn))
|
||||||
|
else:
|
||||||
|
self.nr_conns -= 1
|
||||||
|
conn.close()
|
||||||
|
except:
|
||||||
|
# an exception happened, make sure to close the
|
||||||
|
# socket.
|
||||||
|
self.nr_conns -= 1
|
||||||
|
fs.conn.close()
|
||||||
|
|
||||||
|
def handle(self, conn):
|
||||||
|
keepalive = False
|
||||||
|
req = None
|
||||||
|
try:
|
||||||
|
req = six.next(conn.parser)
|
||||||
|
if not req:
|
||||||
|
return (False, conn)
|
||||||
|
|
||||||
|
# handle the request
|
||||||
|
keepalive = self.handle_request(req, conn)
|
||||||
|
if keepalive:
|
||||||
|
return (keepalive, conn)
|
||||||
|
except http.errors.NoMoreData as e:
|
||||||
|
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||||
|
|
||||||
|
except StopIteration as e:
|
||||||
|
self.log.debug("Closing connection. %s", e)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
if e.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
self.log.debug("ssl connection closed")
|
||||||
|
conn.sock.close()
|
||||||
|
else:
|
||||||
|
self.log.debug("Error processing SSL request.")
|
||||||
|
self.handle_error(req, conn.sock, conn.client, e)
|
||||||
|
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno not in (errno.EPIPE, errno.ECONNRESET):
|
||||||
|
self.log.exception("Socket error processing request.")
|
||||||
|
else:
|
||||||
|
if e.errno == errno.ECONNRESET:
|
||||||
|
self.log.debug("Ignoring connection reset")
|
||||||
|
else:
|
||||||
|
self.log.debug("Ignoring connection epipe")
|
||||||
|
except Exception as e:
|
||||||
|
self.handle_error(req, conn.sock, conn.client, e)
|
||||||
|
|
||||||
|
return (False, conn)
|
||||||
|
|
||||||
|
def handle_request(self, req, conn):
|
||||||
|
environ = {}
|
||||||
|
resp = None
|
||||||
|
try:
|
||||||
|
self.cfg.pre_request(self, req)
|
||||||
|
request_start = datetime.now()
|
||||||
|
resp, environ = wsgi.create(req, conn.sock, conn.client,
|
||||||
|
conn.server, self.cfg)
|
||||||
|
environ["wsgi.multithread"] = True
|
||||||
|
self.nr += 1
|
||||||
|
if self.alive and self.nr >= self.max_requests:
|
||||||
|
self.log.info("Autorestarting worker after current request.")
|
||||||
|
resp.force_close()
|
||||||
|
self.alive = False
|
||||||
|
|
||||||
|
if not self.cfg.keepalive:
|
||||||
|
resp.force_close()
|
||||||
|
elif len(self._keep) >= self.max_keepalived:
|
||||||
|
resp.force_close()
|
||||||
|
|
||||||
|
respiter = self.wsgi(environ, resp.start_response)
|
||||||
|
try:
|
||||||
|
if isinstance(respiter, environ['wsgi.file_wrapper']):
|
||||||
|
resp.write_file(respiter)
|
||||||
|
else:
|
||||||
|
for item in respiter:
|
||||||
|
resp.write(item)
|
||||||
|
|
||||||
|
resp.close()
|
||||||
|
request_time = datetime.now() - request_start
|
||||||
|
self.log.access(resp, req, environ, request_time)
|
||||||
|
finally:
|
||||||
|
if hasattr(respiter, "close"):
|
||||||
|
respiter.close()
|
||||||
|
|
||||||
|
if resp.should_close():
|
||||||
|
self.log.debug("Closing connection.")
|
||||||
|
return False
|
||||||
|
except EnvironmentError:
|
||||||
|
# pass to next try-except level
|
||||||
|
six.reraise(*sys.exc_info())
|
||||||
|
except Exception:
|
||||||
|
if resp and resp.headers_sent:
|
||||||
|
# If the requests have already been sent, we should close the
|
||||||
|
# connection to indicate the error.
|
||||||
|
self.log.exception("Error handling request")
|
||||||
|
try:
|
||||||
|
conn.sock.shutdown(socket.SHUT_RDWR)
|
||||||
|
conn.sock.close()
|
||||||
|
except EnvironmentError:
|
||||||
|
pass
|
||||||
|
raise StopIteration()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
self.cfg.post_request(self, req, environ, resp)
|
||||||
|
except Exception:
|
||||||
|
self.log.exception("Exception in post_request hook")
|
||||||
|
|
||||||
|
return True
|
146
venv/lib/python3.6/site-packages/gunicorn/workers/gtornado.py
Normal file
146
venv/lib/python3.6/site-packages/gunicorn/workers/gtornado.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import tornado
|
||||||
|
except ImportError:
|
||||||
|
raise RuntimeError("You need tornado installed to use this worker.")
|
||||||
|
import tornado.web
|
||||||
|
import tornado.httpserver
|
||||||
|
from tornado.ioloop import IOLoop, PeriodicCallback
|
||||||
|
from tornado.wsgi import WSGIContainer
|
||||||
|
from gunicorn.workers.base import Worker
|
||||||
|
from gunicorn import __version__ as gversion
|
||||||
|
|
||||||
|
|
||||||
|
# `io_loop` arguments to many Tornado functions have been removed in Tornado 5.0
|
||||||
|
# <http://www.tornadoweb.org/en/stable/releases/v5.0.0.html#backwards-compatibility-notes>
|
||||||
|
IOLOOP_PARAMETER_REMOVED = tornado.version_info >= (5, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class TornadoWorker(Worker):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup(cls):
|
||||||
|
web = sys.modules.pop("tornado.web")
|
||||||
|
old_clear = web.RequestHandler.clear
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
old_clear(self)
|
||||||
|
if "Gunicorn" not in self._headers["Server"]:
|
||||||
|
self._headers["Server"] += " (Gunicorn/%s)" % gversion
|
||||||
|
web.RequestHandler.clear = clear
|
||||||
|
sys.modules["tornado.web"] = web
|
||||||
|
|
||||||
|
def handle_exit(self, sig, frame):
|
||||||
|
if self.alive:
|
||||||
|
super(TornadoWorker, self).handle_exit(sig, frame)
|
||||||
|
|
||||||
|
def handle_request(self):
|
||||||
|
self.nr += 1
|
||||||
|
if self.alive and self.nr >= self.max_requests:
|
||||||
|
self.log.info("Autorestarting worker after current request.")
|
||||||
|
self.alive = False
|
||||||
|
|
||||||
|
def watchdog(self):
|
||||||
|
if self.alive:
|
||||||
|
self.notify()
|
||||||
|
|
||||||
|
if self.ppid != os.getppid():
|
||||||
|
self.log.info("Parent changed, shutting down: %s", self)
|
||||||
|
self.alive = False
|
||||||
|
|
||||||
|
def heartbeat(self):
|
||||||
|
if not self.alive:
|
||||||
|
if self.server_alive:
|
||||||
|
if hasattr(self, 'server'):
|
||||||
|
try:
|
||||||
|
self.server.stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self.server_alive = False
|
||||||
|
else:
|
||||||
|
if not self.ioloop._callbacks:
|
||||||
|
self.ioloop.stop()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.ioloop = IOLoop.instance()
|
||||||
|
self.alive = True
|
||||||
|
self.server_alive = False
|
||||||
|
if IOLOOP_PARAMETER_REMOVED:
|
||||||
|
PeriodicCallback(self.watchdog, 1000).start()
|
||||||
|
PeriodicCallback(self.heartbeat, 1000).start()
|
||||||
|
else:
|
||||||
|
PeriodicCallback(self.watchdog, 1000, io_loop=self.ioloop).start()
|
||||||
|
PeriodicCallback(self.heartbeat, 1000, io_loop=self.ioloop).start()
|
||||||
|
|
||||||
|
# Assume the app is a WSGI callable if its not an
|
||||||
|
# instance of tornado.web.Application or is an
|
||||||
|
# instance of tornado.wsgi.WSGIApplication
|
||||||
|
app = self.wsgi
|
||||||
|
if not isinstance(app, tornado.web.Application) or \
|
||||||
|
isinstance(app, tornado.wsgi.WSGIApplication):
|
||||||
|
app = WSGIContainer(app)
|
||||||
|
|
||||||
|
# Monkey-patching HTTPConnection.finish to count the
|
||||||
|
# number of requests being handled by Tornado. This
|
||||||
|
# will help gunicorn shutdown the worker if max_requests
|
||||||
|
# is exceeded.
|
||||||
|
httpserver = sys.modules["tornado.httpserver"]
|
||||||
|
if hasattr(httpserver, 'HTTPConnection'):
|
||||||
|
old_connection_finish = httpserver.HTTPConnection.finish
|
||||||
|
|
||||||
|
def finish(other):
|
||||||
|
self.handle_request()
|
||||||
|
old_connection_finish(other)
|
||||||
|
httpserver.HTTPConnection.finish = finish
|
||||||
|
sys.modules["tornado.httpserver"] = httpserver
|
||||||
|
|
||||||
|
server_class = tornado.httpserver.HTTPServer
|
||||||
|
else:
|
||||||
|
|
||||||
|
class _HTTPServer(tornado.httpserver.HTTPServer):
|
||||||
|
|
||||||
|
def on_close(instance, server_conn):
|
||||||
|
self.handle_request()
|
||||||
|
super(_HTTPServer, instance).on_close(server_conn)
|
||||||
|
|
||||||
|
server_class = _HTTPServer
|
||||||
|
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
_ssl_opt = copy.deepcopy(self.cfg.ssl_options)
|
||||||
|
# tornado refuses initialization if ssl_options contains following
|
||||||
|
# options
|
||||||
|
del _ssl_opt["do_handshake_on_connect"]
|
||||||
|
del _ssl_opt["suppress_ragged_eofs"]
|
||||||
|
if IOLOOP_PARAMETER_REMOVED:
|
||||||
|
server = server_class(app, ssl_options=_ssl_opt)
|
||||||
|
else:
|
||||||
|
server = server_class(app, io_loop=self.ioloop,
|
||||||
|
ssl_options=_ssl_opt)
|
||||||
|
else:
|
||||||
|
if IOLOOP_PARAMETER_REMOVED:
|
||||||
|
server = server_class(app)
|
||||||
|
else:
|
||||||
|
server = server_class(app, io_loop=self.ioloop)
|
||||||
|
|
||||||
|
self.server = server
|
||||||
|
self.server_alive = True
|
||||||
|
|
||||||
|
for s in self.sockets:
|
||||||
|
s.setblocking(0)
|
||||||
|
if hasattr(server, "add_socket"): # tornado > 2.0
|
||||||
|
server.add_socket(s)
|
||||||
|
elif hasattr(server, "_sockets"): # tornado 2.0
|
||||||
|
server._sockets[s.fileno()] = s
|
||||||
|
|
||||||
|
server.no_keep_alive = self.cfg.keepalive <= 0
|
||||||
|
server.start(num_processes=1)
|
||||||
|
|
||||||
|
self.ioloop.start()
|
208
venv/lib/python3.6/site-packages/gunicorn/workers/sync.py
Normal file
208
venv/lib/python3.6/site-packages/gunicorn/workers/sync.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import select
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import gunicorn.http as http
|
||||||
|
import gunicorn.http.wsgi as wsgi
|
||||||
|
import gunicorn.util as util
|
||||||
|
import gunicorn.workers.base as base
|
||||||
|
from gunicorn import six
|
||||||
|
|
||||||
|
class StopWaiting(Exception):
|
||||||
|
""" exception raised to stop waiting for a connnection """
|
||||||
|
|
||||||
|
class SyncWorker(base.Worker):
|
||||||
|
|
||||||
|
def accept(self, listener):
|
||||||
|
client, addr = listener.accept()
|
||||||
|
client.setblocking(1)
|
||||||
|
util.close_on_exec(client)
|
||||||
|
self.handle(listener, client, addr)
|
||||||
|
|
||||||
|
def wait(self, timeout):
|
||||||
|
try:
|
||||||
|
self.notify()
|
||||||
|
ret = select.select(self.wait_fds, [], [], timeout)
|
||||||
|
if ret[0]:
|
||||||
|
if self.PIPE[0] in ret[0]:
|
||||||
|
os.read(self.PIPE[0], 1)
|
||||||
|
return ret[0]
|
||||||
|
|
||||||
|
except select.error as e:
|
||||||
|
if e.args[0] == errno.EINTR:
|
||||||
|
return self.sockets
|
||||||
|
if e.args[0] == errno.EBADF:
|
||||||
|
if self.nr < 0:
|
||||||
|
return self.sockets
|
||||||
|
else:
|
||||||
|
raise StopWaiting
|
||||||
|
raise
|
||||||
|
|
||||||
|
def is_parent_alive(self):
|
||||||
|
# If our parent changed then we shut down.
|
||||||
|
if self.ppid != os.getppid():
|
||||||
|
self.log.info("Parent changed, shutting down: %s", self)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run_for_one(self, timeout):
|
||||||
|
listener = self.sockets[0]
|
||||||
|
while self.alive:
|
||||||
|
self.notify()
|
||||||
|
|
||||||
|
# Accept a connection. If we get an error telling us
|
||||||
|
# that no connection is waiting we fall down to the
|
||||||
|
# select which is where we'll wait for a bit for new
|
||||||
|
# workers to come give us some love.
|
||||||
|
try:
|
||||||
|
self.accept(listener)
|
||||||
|
# Keep processing clients until no one is waiting. This
|
||||||
|
# prevents the need to select() for every client that we
|
||||||
|
# process.
|
||||||
|
continue
|
||||||
|
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno not in (errno.EAGAIN, errno.ECONNABORTED,
|
||||||
|
errno.EWOULDBLOCK):
|
||||||
|
raise
|
||||||
|
|
||||||
|
if not self.is_parent_alive():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.wait(timeout)
|
||||||
|
except StopWaiting:
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_for_multiple(self, timeout):
|
||||||
|
while self.alive:
|
||||||
|
self.notify()
|
||||||
|
|
||||||
|
try:
|
||||||
|
ready = self.wait(timeout)
|
||||||
|
except StopWaiting:
|
||||||
|
return
|
||||||
|
|
||||||
|
if ready is not None:
|
||||||
|
for listener in ready:
|
||||||
|
if listener == self.PIPE[0]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.accept(listener)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno not in (errno.EAGAIN, errno.ECONNABORTED,
|
||||||
|
errno.EWOULDBLOCK):
|
||||||
|
raise
|
||||||
|
|
||||||
|
if not self.is_parent_alive():
|
||||||
|
return
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# if no timeout is given the worker will never wait and will
|
||||||
|
# use the CPU for nothing. This minimal timeout prevent it.
|
||||||
|
timeout = self.timeout or 0.5
|
||||||
|
|
||||||
|
# self.socket appears to lose its blocking status after
|
||||||
|
# we fork in the arbiter. Reset it here.
|
||||||
|
for s in self.sockets:
|
||||||
|
s.setblocking(0)
|
||||||
|
|
||||||
|
if len(self.sockets) > 1:
|
||||||
|
self.run_for_multiple(timeout)
|
||||||
|
else:
|
||||||
|
self.run_for_one(timeout)
|
||||||
|
|
||||||
|
def handle(self, listener, client, addr):
|
||||||
|
req = None
|
||||||
|
try:
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
client = ssl.wrap_socket(client, server_side=True,
|
||||||
|
**self.cfg.ssl_options)
|
||||||
|
|
||||||
|
parser = http.RequestParser(self.cfg, client)
|
||||||
|
req = six.next(parser)
|
||||||
|
self.handle_request(listener, req, client, addr)
|
||||||
|
except http.errors.NoMoreData as e:
|
||||||
|
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||||
|
except StopIteration as e:
|
||||||
|
self.log.debug("Closing connection. %s", e)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
if e.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
self.log.debug("ssl connection closed")
|
||||||
|
client.close()
|
||||||
|
else:
|
||||||
|
self.log.debug("Error processing SSL request.")
|
||||||
|
self.handle_error(req, client, addr, e)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno not in (errno.EPIPE, errno.ECONNRESET):
|
||||||
|
self.log.exception("Socket error processing request.")
|
||||||
|
else:
|
||||||
|
if e.errno == errno.ECONNRESET:
|
||||||
|
self.log.debug("Ignoring connection reset")
|
||||||
|
else:
|
||||||
|
self.log.debug("Ignoring EPIPE")
|
||||||
|
except Exception as e:
|
||||||
|
self.handle_error(req, client, addr, e)
|
||||||
|
finally:
|
||||||
|
util.close(client)
|
||||||
|
|
||||||
|
def handle_request(self, listener, req, client, addr):
|
||||||
|
environ = {}
|
||||||
|
resp = None
|
||||||
|
try:
|
||||||
|
self.cfg.pre_request(self, req)
|
||||||
|
request_start = datetime.now()
|
||||||
|
resp, environ = wsgi.create(req, client, addr,
|
||||||
|
listener.getsockname(), self.cfg)
|
||||||
|
# Force the connection closed until someone shows
|
||||||
|
# a buffering proxy that supports Keep-Alive to
|
||||||
|
# the backend.
|
||||||
|
resp.force_close()
|
||||||
|
self.nr += 1
|
||||||
|
if self.nr >= self.max_requests:
|
||||||
|
self.log.info("Autorestarting worker after current request.")
|
||||||
|
self.alive = False
|
||||||
|
respiter = self.wsgi(environ, resp.start_response)
|
||||||
|
try:
|
||||||
|
if isinstance(respiter, environ['wsgi.file_wrapper']):
|
||||||
|
resp.write_file(respiter)
|
||||||
|
else:
|
||||||
|
for item in respiter:
|
||||||
|
resp.write(item)
|
||||||
|
resp.close()
|
||||||
|
request_time = datetime.now() - request_start
|
||||||
|
self.log.access(resp, req, environ, request_time)
|
||||||
|
finally:
|
||||||
|
if hasattr(respiter, "close"):
|
||||||
|
respiter.close()
|
||||||
|
except EnvironmentError:
|
||||||
|
# pass to next try-except level
|
||||||
|
six.reraise(*sys.exc_info())
|
||||||
|
except Exception:
|
||||||
|
if resp and resp.headers_sent:
|
||||||
|
# If the requests have already been sent, we should close the
|
||||||
|
# connection to indicate the error.
|
||||||
|
self.log.exception("Error handling request")
|
||||||
|
try:
|
||||||
|
client.shutdown(socket.SHUT_RDWR)
|
||||||
|
client.close()
|
||||||
|
except EnvironmentError:
|
||||||
|
pass
|
||||||
|
raise StopIteration()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
self.cfg.post_request(self, req, environ, resp)
|
||||||
|
except Exception:
|
||||||
|
self.log.exception("Exception in post_request hook")
|
@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from gunicorn import util
|
||||||
|
|
||||||
|
PLATFORM = platform.system()
|
||||||
|
IS_CYGWIN = PLATFORM.startswith('CYGWIN')
|
||||||
|
|
||||||
|
|
||||||
|
class WorkerTmp(object):
|
||||||
|
|
||||||
|
def __init__(self, cfg):
|
||||||
|
old_umask = os.umask(cfg.umask)
|
||||||
|
fdir = cfg.worker_tmp_dir
|
||||||
|
if fdir and not os.path.isdir(fdir):
|
||||||
|
raise RuntimeError("%s doesn't exist. Can't create workertmp." % fdir)
|
||||||
|
fd, name = tempfile.mkstemp(prefix="wgunicorn-", dir=fdir)
|
||||||
|
|
||||||
|
# allows the process to write to the file
|
||||||
|
util.chown(name, cfg.uid, cfg.gid)
|
||||||
|
os.umask(old_umask)
|
||||||
|
|
||||||
|
# unlink the file so we don't leak tempory files
|
||||||
|
try:
|
||||||
|
if not IS_CYGWIN:
|
||||||
|
util.unlink(name)
|
||||||
|
self._tmp = os.fdopen(fd, 'w+b', 1)
|
||||||
|
except:
|
||||||
|
os.close(fd)
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.spinner = 0
|
||||||
|
|
||||||
|
def notify(self):
|
||||||
|
try:
|
||||||
|
self.spinner = (self.spinner + 1) % 2
|
||||||
|
os.fchmod(self._tmp.fileno(), self.spinner)
|
||||||
|
except AttributeError:
|
||||||
|
# python < 2.6
|
||||||
|
self._tmp.truncate(0)
|
||||||
|
os.write(self._tmp.fileno(), b"X")
|
||||||
|
|
||||||
|
def last_update(self):
|
||||||
|
return os.fstat(self._tmp.fileno()).st_ctime
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
return self._tmp.fileno()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
return self._tmp.close()
|
Loading…
Reference in New Issue
Block a user