mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Merge branch 'develop'
This commit is contained in:
commit
0b40e69e67
@ -3,10 +3,15 @@ language: python
|
||||
python:
|
||||
- 2.6
|
||||
- 2.7
|
||||
- 3.2
|
||||
- 3.3
|
||||
- pypy
|
||||
|
||||
install: python setup.py install
|
||||
script: nosetests
|
||||
install:
|
||||
- pip install -r requirements_dev.txt --use-mirrors
|
||||
- python setup.py install
|
||||
|
||||
script: py.test tests/
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
8
Makefile
8
Makefile
@ -1,15 +1,13 @@
|
||||
build:
|
||||
virtualenv --no-site-packages .
|
||||
bin/python setup.py develop
|
||||
bin/pip install coverage
|
||||
bin/pip install nose
|
||||
bin/pip install -r requirements_dev.txt
|
||||
|
||||
test:
|
||||
bin/nosetests
|
||||
bin/python setup.py test
|
||||
|
||||
coverage:
|
||||
bin/nosetests --with-coverage --cover-html --cover-html-dir=html \
|
||||
--cover-package=gunicorn
|
||||
bin/python setup.py test --cov
|
||||
|
||||
clean:
|
||||
@rm -rf .Python bin lib include man build html
|
||||
|
||||
22
README.rst
22
README.rst
@ -153,6 +153,28 @@ And then as per usual::
|
||||
$ cd yourpasteproject
|
||||
$ paster serve development.ini workers=2
|
||||
|
||||
**Gunicorn paster from script**
|
||||
|
||||
If you'd like to run Gunicorn paster from a script instead of the command line (for example: a runapp.py to start a Pyramid app),
|
||||
you can use this example to help get you started::
|
||||
|
||||
import os
|
||||
import multiprocessing
|
||||
|
||||
from paste.deploy import appconfig, loadapp
|
||||
from gunicorn.app.pasterapp import paste_server
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
iniFile = 'config:development.ini'
|
||||
port = int(os.environ.get("PORT", 5000))
|
||||
workers = multiprocessing.cpu_count() * 2 + 1
|
||||
worker_class = 'gevent'
|
||||
|
||||
app = loadapp(iniFile, relative_to='.')
|
||||
paste_server(app, host='0.0.0.0', port=port, workers=workers, worker_class=worker_class)
|
||||
|
||||
|
||||
LICENSE
|
||||
-------
|
||||
|
||||
|
||||
4
THANKS
4
THANKS
@ -45,3 +45,7 @@ Caleb Brown <git@calebbrown.id.au>
|
||||
Marc Abramowitz <marc@marc-abramowitz.com>
|
||||
Vangelis Koukis <vkoukis@grnet.gr>
|
||||
Prateek Singh Paudel <pratykschingh@gmail.com>
|
||||
Andrew Gorcester <andrewsg@terra.(none)>
|
||||
Kenneth Reitz <me@kennethreitz.com>
|
||||
Eric Shull <eric@elevenbasetwo.com>
|
||||
Christos Stavrakakis <cstavr@grnet.gr>
|
||||
|
||||
@ -60,6 +60,15 @@ a:hover {
|
||||
border-bottom: 1px solid #2A8729;
|
||||
}
|
||||
|
||||
.latest {
|
||||
width: 150px;
|
||||
top: 0;
|
||||
display: block;
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.logo-div {
|
||||
width: 1000px;
|
||||
margin: 0 auto;
|
||||
@ -283,12 +292,14 @@ a:hover {
|
||||
margin: 0 0 9px;
|
||||
}
|
||||
|
||||
.tab-box a {
|
||||
.tab-box a,
|
||||
.latest a {
|
||||
color: #3F3F27;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.tab-box a:hover {
|
||||
.tab-box a:hover,
|
||||
.latest a:hover {
|
||||
color: #1D692D;
|
||||
}
|
||||
|
||||
@ -389,4 +400,4 @@ pre {
|
||||
|
||||
.footer-wp a:hover {
|
||||
color: #1D692D;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,10 +6,19 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<link rel="stylesheet" type="text/css" href="css/style.css" />
|
||||
<link rel="shortcut icon" href="images/favicon.png" type="image/x-icon">
|
||||
<link rel="alternate"
|
||||
href="https://github.com/benoitc/gunicorn/commits/master.atom"
|
||||
type="application/atom+xml" title="Gunicorn commits">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="logo-wrapper">
|
||||
<div class="logo-div">
|
||||
<div class="latest">
|
||||
Latest version: <strong><a
|
||||
href="http://docs.gunicorn.org/en/latest/news.html#id1">0.15.0</a></strong>
|
||||
</div>
|
||||
|
||||
<div class="logo"><img src="images/logo.jpg" ></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -20,6 +20,17 @@ Once again, in order of least to most authoritative:
|
||||
2. Configuration File
|
||||
3. Command Line
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
To check your configuration when using the command line or the
|
||||
configuration file you can run the following command::
|
||||
|
||||
$ gunicorn --check-config
|
||||
|
||||
It will also allows you to know if your applican can be launched.
|
||||
|
||||
|
||||
Framework Settings
|
||||
==================
|
||||
|
||||
|
||||
@ -1,6 +1,18 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
0.16.0 / develop
|
||||
----------------
|
||||
|
||||
- **Added support for Python 3.2 & 3.3**
|
||||
- Expose --pythonpath command to all gunicorn commands
|
||||
- Honor $PORT environment variable, useful for deployement on heroku
|
||||
- Removed support for Python 2.5
|
||||
- Make sure we reopen teh logs on the console
|
||||
- Fix django settings module detection from path
|
||||
- Reverted timeout for client socket. Fix issue on blocking issues.
|
||||
- Fixed gevent worker
|
||||
|
||||
0.15.0 / 2012-10-18
|
||||
-------------------
|
||||
|
||||
|
||||
@ -482,6 +482,9 @@ The Python path to a Django settings module.
|
||||
e.g. 'myproject.settings.main'. If this isn't provided, the
|
||||
DJANGO_SETTINGS_MODULE environment variable will be used.
|
||||
|
||||
Server Mechanics
|
||||
----------------
|
||||
|
||||
pythonpath
|
||||
~~~~~~~~~~
|
||||
|
||||
|
||||
@ -12,18 +12,18 @@ class MsgForm(forms.Form):
|
||||
subject = forms.CharField(max_length=100)
|
||||
message = forms.CharField()
|
||||
f = forms.FileField()
|
||||
|
||||
|
||||
|
||||
def home(request):
|
||||
from django.conf import settings
|
||||
print settings.SOME_VALUE
|
||||
print(settings.SOME_VALUE)
|
||||
subject = None
|
||||
message = None
|
||||
size = 0
|
||||
print request.META
|
||||
print(request.META)
|
||||
if request.POST:
|
||||
form = MsgForm(request.POST, request.FILES)
|
||||
print request.FILES
|
||||
print(request.FILES)
|
||||
if form.is_valid():
|
||||
subject = form.cleaned_data['subject']
|
||||
message = form.cleaned_data['message']
|
||||
@ -31,29 +31,29 @@ def home(request):
|
||||
size = int(os.fstat(f.fileno())[6])
|
||||
else:
|
||||
form = MsgForm()
|
||||
|
||||
|
||||
|
||||
|
||||
return render_to_response('home.html', {
|
||||
'form': form,
|
||||
'subject': subject,
|
||||
'message': message,
|
||||
'size': size
|
||||
}, RequestContext(request))
|
||||
|
||||
|
||||
|
||||
|
||||
def acsv(request):
|
||||
rows = [
|
||||
{'a': 1, 'b': 2},
|
||||
{'a': 3, 'b': 3}
|
||||
]
|
||||
|
||||
|
||||
response = HttpResponse(mimetype='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename=report.csv'
|
||||
|
||||
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(['a', 'b'])
|
||||
|
||||
|
||||
for r in rows:
|
||||
writer.writerow([r['a'], r['b']])
|
||||
|
||||
|
||||
return response
|
||||
|
||||
@ -4,7 +4,7 @@ import gevent
|
||||
|
||||
def child_process(queue):
|
||||
while True:
|
||||
print queue.get()
|
||||
print(queue.get())
|
||||
requests.get('http://requestb.in/15s95oz1')
|
||||
|
||||
class GunicornSubProcessTestMiddleware(object):
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
# Create your views here.
|
||||
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
from django import forms
|
||||
from django.http import HttpResponse
|
||||
@ -16,22 +17,27 @@ class MsgForm(forms.Form):
|
||||
|
||||
def home(request):
|
||||
from django.conf import settings
|
||||
print settings.SOME_VALUE
|
||||
print(settings.SOME_VALUE)
|
||||
subject = None
|
||||
message = None
|
||||
size = 0
|
||||
print request.META
|
||||
print(request.META)
|
||||
if request.POST:
|
||||
form = MsgForm(request.POST, request.FILES)
|
||||
print request.FILES
|
||||
print(request.FILES)
|
||||
if form.is_valid():
|
||||
subject = form.cleaned_data['subject']
|
||||
message = form.cleaned_data['message']
|
||||
f = request.FILES['f']
|
||||
|
||||
|
||||
if not hasattr(f, "fileno"):
|
||||
size = len(f.read())
|
||||
else:
|
||||
size = int(os.fstat(f.fileno())[6])
|
||||
try:
|
||||
size = int(os.fstat(f.fileno())[6])
|
||||
except io.UnsupportedOperation:
|
||||
size = len(f.read())
|
||||
else:
|
||||
form = MsgForm()
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
class TestIter(object):
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
lines = ['line 1\n', 'line 2\n']
|
||||
for line in lines:
|
||||
@ -16,12 +17,13 @@ class TestIter(object):
|
||||
|
||||
def app(environ, start_response):
|
||||
"""Application which cooperatively pauses 20 seconds (needed to surpass normal timeouts) before responding"""
|
||||
data = 'Hello, World!\n'
|
||||
data = b'Hello, World!\n'
|
||||
status = '200 OK'
|
||||
response_headers = [
|
||||
('Content-type','text/plain'),
|
||||
('Transfer-Encoding', "chunked"),
|
||||
]
|
||||
print 'request received'
|
||||
sys.stdout.write('request received')
|
||||
sys.stdout.flush()
|
||||
start_response(status, response_headers)
|
||||
return TestIter()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
#
|
||||
# Run this application with:
|
||||
@ -17,7 +17,7 @@
|
||||
try:
|
||||
from routes import Mapper
|
||||
except:
|
||||
print "This example requires Routes to be installed"
|
||||
print("This example requires Routes to be installed")
|
||||
|
||||
# Obviously you'd import your app callables
|
||||
# from different places...
|
||||
@ -38,7 +38,7 @@ class Application(object):
|
||||
return match[0]['app'](environ, start_response)
|
||||
|
||||
def error404(self, environ, start_response):
|
||||
html = """\
|
||||
html = b"""\
|
||||
<html>
|
||||
<head>
|
||||
<title>404 - Not Found</title>
|
||||
@ -55,4 +55,4 @@ class Application(object):
|
||||
start_response('404 Not Found', headers)
|
||||
return [html]
|
||||
|
||||
app = Application()
|
||||
app = Application()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import re
|
||||
@ -20,15 +20,15 @@ class SubDomainApp:
|
||||
return app(environ, start_response)
|
||||
else:
|
||||
start_response("404 Not Found", [])
|
||||
return [""]
|
||||
return [b""]
|
||||
|
||||
def hello(environ, start_response):
|
||||
start_response("200 OK", [("Content-Type", "text/plain")])
|
||||
return ["Hello, world\n"]
|
||||
return [b"Hello, world\n"]
|
||||
|
||||
def bye(environ, start_response):
|
||||
start_response("200 OK", [("Content-Type", "text/plain")])
|
||||
return ["Goodbye!\n"]
|
||||
return [b"Goodbye!\n"]
|
||||
|
||||
app = SubDomainApp([
|
||||
("localhost", hello),
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def app(environ, start_response):
|
||||
"""Application which cooperatively pauses 10 seconds before responding"""
|
||||
data = 'Hello, World!\n'
|
||||
data = b'Hello, World!\n'
|
||||
status = '200 OK'
|
||||
response_headers = [
|
||||
('Content-type','text/plain'),
|
||||
('Content-Length', str(len(data))) ]
|
||||
print 'request received, pausing 10 seconds'
|
||||
sys.stdout.write('request received, pausing 10 seconds')
|
||||
sys.stdout.flush()
|
||||
time.sleep(10)
|
||||
start_response(status, response_headers)
|
||||
return iter([data])
|
||||
return iter([data])
|
||||
|
||||
@ -12,14 +12,14 @@ from gunicorn import __version__
|
||||
#@validator
|
||||
def app(environ, start_response):
|
||||
"""Simplest possible application object"""
|
||||
data = 'Hello, World!\n'
|
||||
data = b'Hello, World!\n'
|
||||
status = '200 OK'
|
||||
# print("print to stdout in test app")
|
||||
# sys.stderr.write("stderr, print to stderr in test app\n")
|
||||
|
||||
response_headers = [
|
||||
('Content-type','text/plain'),
|
||||
('Content-Length', str(len(data))),
|
||||
('X-Gunicorn-Version', __version__)
|
||||
('X-Gunicorn-Version', __version__),
|
||||
("Test", "test тест"),
|
||||
]
|
||||
start_response(status, response_headers)
|
||||
return iter([data])
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
version_info = (0, 15, 0)
|
||||
__version__ = ".".join(map(str, version_info))
|
||||
version_info = (0, 16, 0)
|
||||
__version__ = ".".join([str(v) for v in version_info])
|
||||
SERVER_SOFTWARE = "gunicorn/%s" % __version__
|
||||
|
||||
@ -3,17 +3,15 @@
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
from gunicorn.glogging import Logger
|
||||
from gunicorn import util
|
||||
from gunicorn.arbiter import Arbiter
|
||||
from gunicorn.config import Config
|
||||
from gunicorn import debug
|
||||
from gunicorn.six import execfile_
|
||||
|
||||
class Application(object):
|
||||
"""\
|
||||
@ -31,7 +29,7 @@ class Application(object):
|
||||
def do_load_config(self):
|
||||
try:
|
||||
self.load_config()
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
sys.stderr.write("\nError: %s\n" % str(e))
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
@ -62,9 +60,9 @@ class Application(object):
|
||||
"__package__": None
|
||||
}
|
||||
try:
|
||||
execfile(opts.config, cfg, cfg)
|
||||
execfile_(opts.config, cfg, cfg)
|
||||
except Exception:
|
||||
print "Failed to read config file: %s" % opts.config
|
||||
print("Failed to read config file: %s" % opts.config)
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
@ -104,15 +102,12 @@ class Application(object):
|
||||
def run(self):
|
||||
if self.cfg.check_config:
|
||||
try:
|
||||
|
||||
self.load()
|
||||
except:
|
||||
sys.stderr.write("\nError while loading the application:\n\n")
|
||||
traceback.print_exc()
|
||||
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
if self.cfg.spew:
|
||||
@ -120,9 +115,17 @@ class Application(object):
|
||||
if self.cfg.daemon:
|
||||
util.daemonize()
|
||||
|
||||
# set python paths
|
||||
if self.cfg.pythonpath and self.cfg.pythonpath is not None:
|
||||
paths = self.cfg.pythonpath.split(",")
|
||||
for path in paths:
|
||||
pythonpath = os.path.abspath(self.cfg.pythonpath)
|
||||
if pythonpath not in sys.path:
|
||||
sys.path.insert(0, pythonpath)
|
||||
|
||||
try:
|
||||
Arbiter(self).run()
|
||||
except RuntimeError, e:
|
||||
except RuntimeError as e:
|
||||
sys.stderr.write("\nError: %s\n\n" % e)
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
|
||||
@ -10,10 +10,12 @@ import re
|
||||
import sys
|
||||
import time
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
from io import StringIO
|
||||
from imp import reload
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.validation import get_validation_errors
|
||||
from django.utils import translation
|
||||
@ -72,10 +74,10 @@ def reload_django_settings():
|
||||
app_mod = util.import_module(app[:-2])
|
||||
appdir = os.path.dirname(app_mod.__file__)
|
||||
app_subdirs = os.listdir(appdir)
|
||||
app_subdirs.sort()
|
||||
name_pattern = re.compile(r'[a-zA-Z]\w*')
|
||||
for d in app_subdirs:
|
||||
if name_pattern.match(d) and os.path.isdir(os.path.join(appdir, d)):
|
||||
for d in sorted(app_subdirs):
|
||||
if (name_pattern.match(d) and
|
||||
os.path.isdir(os.path.join(appdir, d))):
|
||||
new_installed_apps.append('%s.%s' % (app[:-2], d))
|
||||
else:
|
||||
new_installed_apps.append(app)
|
||||
|
||||
@ -44,12 +44,14 @@ def make_default_env(cfg):
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = cfg.django_settings
|
||||
|
||||
if cfg.pythonpath and cfg.pythonpath is not None:
|
||||
pythonpath = os.path.abspath(cfg.pythonpath)
|
||||
if pythonpath not in sys.path:
|
||||
sys.path.insert(0, pythonpath)
|
||||
paths = cfg.pythonpath.split(",")
|
||||
for path in paths:
|
||||
pythonpath = os.path.abspath(cfg.pythonpath)
|
||||
if pythonpath not in sys.path:
|
||||
sys.path.insert(0, pythonpath)
|
||||
|
||||
try:
|
||||
_ = os.environ['DJANGO_SETTINGS_MODULE']
|
||||
os.environ['DJANGO_SETTINGS_MODULE']
|
||||
except KeyError:
|
||||
# not settings env set, try to build one.
|
||||
project_path, settings_name = find_settings_module(os.getcwd())
|
||||
@ -71,12 +73,15 @@ class DjangoApplication(Application):
|
||||
|
||||
def init(self, parser, opts, args):
|
||||
if args:
|
||||
if "." in args[0]:
|
||||
if ("." in args[0] and not (os.path.isfile(args[0])
|
||||
or os.path.isdir(args[0]))):
|
||||
self.cfg.set("django_settings", args[0])
|
||||
else:
|
||||
# not settings env set, try to build one.
|
||||
project_path, settings_name = find_settings_module(
|
||||
os.path.abspath(args[0]))
|
||||
if project_path not in sys.path:
|
||||
sys.path.insert(0, project_path)
|
||||
|
||||
if not project_path:
|
||||
raise RuntimeError("django project not found")
|
||||
|
||||
@ -6,7 +6,11 @@
|
||||
import os
|
||||
import pkg_resources
|
||||
import sys
|
||||
import ConfigParser
|
||||
|
||||
try:
|
||||
import configparser as ConfigParser
|
||||
except ImportError:
|
||||
import ConfigParser
|
||||
|
||||
from paste.deploy import loadapp, loadwsgi
|
||||
SERVER = loadwsgi.SERVER
|
||||
@ -118,7 +122,7 @@ class PasterServerApplication(PasterBaseApplication):
|
||||
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, e:
|
||||
except Exception as e:
|
||||
sys.stderr.write("\nConfig error: %s\n" % str(e))
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
|
||||
@ -41,10 +41,8 @@ class Arbiter(object):
|
||||
|
||||
# I love dynamic languages
|
||||
SIG_QUEUE = []
|
||||
SIGNALS = map(
|
||||
lambda x: getattr(signal, "SIG%s" % x),
|
||||
"HUP QUIT INT TERM TTIN TTOU USR1 USR2 WINCH".split()
|
||||
)
|
||||
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] != "_"
|
||||
@ -99,7 +97,10 @@ class Arbiter(object):
|
||||
|
||||
if self.cfg.debug:
|
||||
self.log.debug("Current configuration:")
|
||||
for config, value in sorted(self.cfg.settings.iteritems()):
|
||||
|
||||
|
||||
for config, value in sorted(self.cfg.settings.items(),
|
||||
key=lambda setting: setting[1]):
|
||||
self.log.debug(" %s: %s", config, value.value)
|
||||
|
||||
if self.cfg.preload_app:
|
||||
@ -135,13 +136,20 @@ class Arbiter(object):
|
||||
Initialize master signal handling. Most of the signals
|
||||
are queued. Child signals only wake up the master.
|
||||
"""
|
||||
# close old PIPE
|
||||
if self.PIPE:
|
||||
map(os.close, self.PIPE)
|
||||
[os.close(p) for p in self.PIPE]
|
||||
|
||||
# initialize the pipe
|
||||
self.PIPE = pair = os.pipe()
|
||||
map(util.set_non_blocking, pair)
|
||||
map(util.close_on_exec, pair)
|
||||
for p in pair:
|
||||
util.set_non_blocking(p)
|
||||
util.close_on_exec(p)
|
||||
|
||||
self.log.close_on_exec()
|
||||
map(lambda s: signal.signal(s, self.signal), self.SIGNALS)
|
||||
|
||||
# intialiatze all signals
|
||||
[signal.signal(s, self.signal) for s in self.SIGNALS]
|
||||
signal.signal(signal.SIGCHLD, self.handle_chld)
|
||||
|
||||
def signal(self, sig, frame):
|
||||
@ -181,7 +189,7 @@ class Arbiter(object):
|
||||
self.halt()
|
||||
except KeyboardInterrupt:
|
||||
self.halt()
|
||||
except HaltServer, inst:
|
||||
except HaltServer as inst:
|
||||
self.halt(reason=inst.reason, exit_status=inst.exit_status)
|
||||
except SystemExit:
|
||||
raise
|
||||
@ -270,8 +278,8 @@ class Arbiter(object):
|
||||
Wake up the arbiter by writing to the PIPE
|
||||
"""
|
||||
try:
|
||||
os.write(self.PIPE[1], '.')
|
||||
except IOError, e:
|
||||
os.write(self.PIPE[1], b'.')
|
||||
except IOError as e:
|
||||
if e.errno not in [errno.EAGAIN, errno.EINTR]:
|
||||
raise
|
||||
|
||||
@ -296,10 +304,10 @@ class Arbiter(object):
|
||||
return
|
||||
while os.read(self.PIPE[0], 1):
|
||||
pass
|
||||
except select.error, e:
|
||||
if e[0] not in [errno.EAGAIN, errno.EINTR]:
|
||||
except select.error as e:
|
||||
if e.args[0] not in [errno.EAGAIN, errno.EINTR]:
|
||||
raise
|
||||
except OSError, e:
|
||||
except OSError as e:
|
||||
if e.errno not in [errno.EAGAIN, errno.EINTR]:
|
||||
raise
|
||||
except KeyboardInterrupt:
|
||||
@ -423,7 +431,7 @@ class Arbiter(object):
|
||||
if not worker:
|
||||
continue
|
||||
worker.tmp.close()
|
||||
except OSError, e:
|
||||
except OSError as e:
|
||||
if e.errno == errno.ECHILD:
|
||||
pass
|
||||
|
||||
@ -436,7 +444,7 @@ class Arbiter(object):
|
||||
self.spawn_workers()
|
||||
|
||||
workers = self.WORKERS.items()
|
||||
workers.sort(key=lambda w: w[1].age)
|
||||
workers = sorted(workers, key=lambda w: w[1].age)
|
||||
while len(workers) > self.num_workers:
|
||||
(pid, _) = workers.pop(0)
|
||||
self.kill_worker(pid, signal.SIGQUIT)
|
||||
@ -504,7 +512,7 @@ class Arbiter(object):
|
||||
"""
|
||||
try:
|
||||
os.kill(pid, sig)
|
||||
except OSError, e:
|
||||
except OSError as e:
|
||||
if e.errno == errno.ESRCH:
|
||||
try:
|
||||
worker = self.WORKERS.pop(pid)
|
||||
|
||||
@ -15,6 +15,7 @@ import types
|
||||
from gunicorn import __version__
|
||||
from gunicorn.errors import ConfigError
|
||||
from gunicorn import util
|
||||
from gunicorn.six import string_types, integer_types, bytes_to_str
|
||||
|
||||
KNOWN_SETTINGS = []
|
||||
|
||||
@ -61,10 +62,12 @@ class Config(object):
|
||||
}
|
||||
parser = optparse.OptionParser(**kwargs)
|
||||
|
||||
keys = self.settings.keys()
|
||||
keys = list(self.settings)
|
||||
def sorter(k):
|
||||
return (self.settings[k].section, self.settings[k].order)
|
||||
keys.sort(key=sorter)
|
||||
|
||||
|
||||
keys = sorted(self.settings, key=self.settings.__getitem__)
|
||||
for k in keys:
|
||||
self.settings[k].add_option(parser)
|
||||
return parser
|
||||
@ -84,7 +87,7 @@ class Config(object):
|
||||
@property
|
||||
def address(self):
|
||||
bind = self.settings['bind'].get()
|
||||
return util.parse_address(util.to_bytestring(bind))
|
||||
return util.parse_address(bytes_to_str(bind))
|
||||
|
||||
@property
|
||||
def uid(self):
|
||||
@ -134,8 +137,6 @@ class SettingMeta(type):
|
||||
setattr(cls, "short", desc.splitlines()[0])
|
||||
|
||||
class Setting(object):
|
||||
__metaclass__ = SettingMeta
|
||||
|
||||
name = None
|
||||
value = None
|
||||
section = None
|
||||
@ -178,10 +179,17 @@ class Setting(object):
|
||||
assert callable(self.validator), "Invalid validator: %s" % self.name
|
||||
self.value = self.validator(val)
|
||||
|
||||
def __lt__(self, other):
|
||||
return (self.section == other.section and
|
||||
self.order < other.order)
|
||||
__cmp__ = __lt__
|
||||
|
||||
Setting = SettingMeta('Setting', (Setting,), {})
|
||||
|
||||
def validate_bool(val):
|
||||
if isinstance(val, types.BooleanType):
|
||||
if isinstance(val, bool):
|
||||
return val
|
||||
if not isinstance(val, basestring):
|
||||
if not isinstance(val, string_types):
|
||||
raise TypeError("Invalid type for casting: %s" % val)
|
||||
if val.lower().strip() == "true":
|
||||
return True
|
||||
@ -196,7 +204,7 @@ def validate_dict(val):
|
||||
return val
|
||||
|
||||
def validate_pos_int(val):
|
||||
if not isinstance(val, (types.IntType, types.LongType)):
|
||||
if not isinstance(val, integer_types):
|
||||
val = int(val, 0)
|
||||
else:
|
||||
# Booleans are ints!
|
||||
@ -208,7 +216,7 @@ def validate_pos_int(val):
|
||||
def validate_string(val):
|
||||
if val is None:
|
||||
return None
|
||||
if not isinstance(val, basestring):
|
||||
if not isinstance(val, string_types):
|
||||
raise TypeError("Not a string: %s" % val)
|
||||
return val.strip()
|
||||
|
||||
@ -229,7 +237,7 @@ def validate_class(val):
|
||||
|
||||
def validate_callable(arity):
|
||||
def _validate_callable(val):
|
||||
if isinstance(val, basestring):
|
||||
if isinstance(val, string_types):
|
||||
try:
|
||||
mod_name, obj_name = val.rsplit(".", 1)
|
||||
except ValueError:
|
||||
@ -311,7 +319,12 @@ class Bind(Setting):
|
||||
cli = ["-b", "--bind"]
|
||||
meta = "ADDRESS"
|
||||
validator = validate_string
|
||||
default = "127.0.0.1:8000"
|
||||
|
||||
if 'PORT' in os.environ:
|
||||
default = '0.0.0.0:{0}'.format(os.environ.get('PORT'))
|
||||
else:
|
||||
default = '127.0.0.1:8000'
|
||||
|
||||
desc = """\
|
||||
The socket to bind.
|
||||
|
||||
@ -863,9 +876,9 @@ class DjangoSettings(Setting):
|
||||
DJANGO_SETTINGS_MODULE environment variable will be used.
|
||||
"""
|
||||
|
||||
class DjangoPythonPath(Setting):
|
||||
class PythonPath(Setting):
|
||||
name = "pythonpath"
|
||||
section = "Django"
|
||||
section = "Server Mechanics"
|
||||
cli = ["--pythonpath"]
|
||||
meta = "STRING"
|
||||
validator = validate_string
|
||||
|
||||
@ -43,7 +43,7 @@ class Spew(object):
|
||||
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())
|
||||
print('%s:%s: %s' % (name, lineno, line.rstrip()))
|
||||
if not self.show_values:
|
||||
return self
|
||||
details = []
|
||||
@ -54,7 +54,7 @@ class Spew(object):
|
||||
if tok in frame.f_locals:
|
||||
details.append('%s=%r' % (tok, frame.f_locals[tok]))
|
||||
if details:
|
||||
print "\t%s" % ' '.join(details)
|
||||
print("\t%s" % ' '.join(details))
|
||||
return self
|
||||
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
# See the NOTICE for more information.
|
||||
|
||||
|
||||
class HaltServer(Exception):
|
||||
class HaltServer(BaseException):
|
||||
def __init__(self, reason, exit_status=1):
|
||||
self.reason = reason
|
||||
self.exit_status = exit_status
|
||||
@ -12,5 +12,5 @@ class HaltServer(Exception):
|
||||
def __str__(self):
|
||||
return "<HaltServer %r %d>" % (self.reason, self.exit_status)
|
||||
|
||||
class ConfigError(Exception):
|
||||
class ConfigError(BaseException):
|
||||
""" Exception raised on config error """
|
||||
|
||||
@ -6,17 +6,15 @@
|
||||
import datetime
|
||||
import logging
|
||||
logging.Logger.manager.emittedNoHandlerWarning = 1
|
||||
from logging.config import fileConfig
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
try:
|
||||
from logging.config import fileConfig
|
||||
except ImportError:
|
||||
from gunicorn.logging_config import fileConfig
|
||||
|
||||
from gunicorn import util
|
||||
from gunicorn.six import string_types
|
||||
|
||||
CONFIG_DEFAULTS = dict(
|
||||
version = 1,
|
||||
@ -76,6 +74,16 @@ class LazyWriter(object):
|
||||
self.lock.release()
|
||||
return self.fileobj
|
||||
|
||||
def close(self):
|
||||
if self.fileobj:
|
||||
self.lock.acquire()
|
||||
try:
|
||||
if self.fileobj:
|
||||
self.fileobj.close()
|
||||
self.fileobj = None
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def write(self, text):
|
||||
fileobj = self.open()
|
||||
fileobj.write(text)
|
||||
@ -89,6 +97,9 @@ class LazyWriter(object):
|
||||
def flush(self):
|
||||
self.open().flush()
|
||||
|
||||
def isatty(self):
|
||||
return bool(self.fileobj and self.fileobj.isatty())
|
||||
|
||||
class SafeAtoms(dict):
|
||||
|
||||
def __init__(self, atoms):
|
||||
@ -179,7 +190,7 @@ class Logger(object):
|
||||
self.error_log.exception(msg, *args)
|
||||
|
||||
def log(self, lvl, msg, *args, **kwargs):
|
||||
if isinstance(lvl, basestring):
|
||||
if isinstance(lvl, string_types):
|
||||
lvl = self.LOG_LEVELS.get(lvl.lower(), logging.INFO)
|
||||
self.error_log.log(lvl, msg, *args, **kwargs)
|
||||
|
||||
@ -238,6 +249,10 @@ class Logger(object):
|
||||
|
||||
|
||||
def reopen_files(self):
|
||||
if self.cfg.errorlog != "-":
|
||||
# Close stderr & stdout if they are redirected to error log file
|
||||
sys.stderr.close()
|
||||
sys.stdout.close()
|
||||
for log in loggers():
|
||||
for handler in log.handlers:
|
||||
if isinstance(handler, logging.FileHandler):
|
||||
|
||||
@ -5,55 +5,51 @@
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
from gunicorn.http.errors import NoMoreData, ChunkMissingTerminator, \
|
||||
InvalidChunkSize
|
||||
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 = StringIO()
|
||||
self.buf = six.BytesIO()
|
||||
|
||||
def read(self, size):
|
||||
if not isinstance(size, (int, long)):
|
||||
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 ""
|
||||
return b""
|
||||
|
||||
if self.parser:
|
||||
while self.buf.tell() < size:
|
||||
try:
|
||||
self.buf.write(self.parser.next())
|
||||
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.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
def parse_trailers(self, unreader, data):
|
||||
buf = StringIO()
|
||||
buf = six.BytesIO()
|
||||
buf.write(data)
|
||||
|
||||
idx = buf.getvalue().find("\r\n\r\n")
|
||||
done = buf.getvalue()[:2] == "\r\n"
|
||||
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("\r\n\r\n")
|
||||
done = buf.getvalue()[:2] == "\r\n"
|
||||
idx = buf.getvalue().find(b"\r\n\r\n")
|
||||
done = buf.getvalue()[:2] == b"\r\n"
|
||||
if done:
|
||||
unreader.unread(buf.getvalue()[2:])
|
||||
return ""
|
||||
return b""
|
||||
self.req.trailers = self.req.parse_headers(buf.getvalue()[:idx])
|
||||
unreader.unread(buf.getvalue()[idx+4:])
|
||||
|
||||
@ -71,24 +67,24 @@ class ChunkedReader(object):
|
||||
rest = rest[size:]
|
||||
while len(rest) < 2:
|
||||
rest += unreader.read()
|
||||
if rest[:2] != '\r\n':
|
||||
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 = StringIO()
|
||||
buf = six.BytesIO()
|
||||
if data is not None:
|
||||
buf.write(data)
|
||||
|
||||
idx = buf.getvalue().find("\r\n")
|
||||
idx = buf.getvalue().find(b"\r\n")
|
||||
while idx < 0:
|
||||
self.get_data(unreader, buf)
|
||||
idx = buf.getvalue().find("\r\n")
|
||||
idx = buf.getvalue().find(b"\r\n")
|
||||
|
||||
data = buf.getvalue()
|
||||
line, rest_chunk = data[:idx], data[idx+2:]
|
||||
|
||||
chunk_size = line.split(";", 1)[0].strip()
|
||||
chunk_size = line.split(b";", 1)[0].strip()
|
||||
try:
|
||||
chunk_size = int(chunk_size, 16)
|
||||
except ValueError:
|
||||
@ -114,17 +110,17 @@ class LengthReader(object):
|
||||
self.length = length
|
||||
|
||||
def read(self, size):
|
||||
if not isinstance(size, (int, long)):
|
||||
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 ""
|
||||
return b""
|
||||
|
||||
|
||||
buf = StringIO()
|
||||
buf = six.BytesIO()
|
||||
data = self.unreader.read()
|
||||
while data:
|
||||
buf.write(data)
|
||||
@ -141,21 +137,21 @@ class LengthReader(object):
|
||||
class EOFReader(object):
|
||||
def __init__(self, unreader):
|
||||
self.unreader = unreader
|
||||
self.buf = StringIO()
|
||||
self.buf = six.BytesIO()
|
||||
self.finished = False
|
||||
|
||||
def read(self, size):
|
||||
if not isinstance(size, (int, long)):
|
||||
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 ""
|
||||
return b""
|
||||
|
||||
if self.finished:
|
||||
data = self.buf.getvalue()
|
||||
ret, rest = data[:size], data[size:]
|
||||
self.buf.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
@ -171,42 +167,43 @@ class EOFReader(object):
|
||||
|
||||
data = self.buf.getvalue()
|
||||
ret, rest = data[:size], data[size:]
|
||||
self.buf.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
class Body(object):
|
||||
def __init__(self, reader):
|
||||
self.reader = reader
|
||||
self.buf = StringIO()
|
||||
self.buf = six.BytesIO()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(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 sys.maxint
|
||||
elif not isinstance(size, (int, long)):
|
||||
return six.MAXSIZE
|
||||
elif not isinstance(size, six.integer_types):
|
||||
raise TypeError("size must be an integral type")
|
||||
elif size < 0:
|
||||
return sys.maxint
|
||||
return six.MAXSIZE
|
||||
return size
|
||||
|
||||
def read(self, size=None):
|
||||
size = self.getsize(size)
|
||||
if size == 0:
|
||||
return ""
|
||||
return b""
|
||||
|
||||
if size < self.buf.tell():
|
||||
data = self.buf.getvalue()
|
||||
ret, rest = data[:size], data[size:]
|
||||
self.buf.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
@ -218,23 +215,23 @@ class Body(object):
|
||||
|
||||
data = self.buf.getvalue()
|
||||
ret, rest = data[:size], data[size:]
|
||||
self.buf.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
self.buf.write(rest)
|
||||
return ret
|
||||
|
||||
def readline(self, size=None):
|
||||
size = self.getsize(size)
|
||||
if size == 0:
|
||||
return ""
|
||||
return b""
|
||||
|
||||
line = self.buf.getvalue()
|
||||
self.buf.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
if len(line) < size:
|
||||
line += self.reader.read(size - len(line))
|
||||
extra_buf_data = line[size:]
|
||||
line = line[:size]
|
||||
|
||||
idx = line.find("\n")
|
||||
idx = line.find(b"\n")
|
||||
if idx >= 0:
|
||||
ret = line[:idx+1]
|
||||
self.buf.write(line[idx+1:])
|
||||
@ -247,12 +244,11 @@ class Body(object):
|
||||
ret = []
|
||||
data = self.read()
|
||||
while len(data):
|
||||
pos = data.find("\n")
|
||||
pos = data.find(b"\n")
|
||||
if pos < 0:
|
||||
ret.append(data)
|
||||
data = ""
|
||||
data = b""
|
||||
else:
|
||||
line, data = data[:pos+1], data[pos+1:]
|
||||
ret.append(line)
|
||||
return ret
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ class ParseException(Exception):
|
||||
class NoMoreData(IOError):
|
||||
def __init__(self, buf=None):
|
||||
self.buf = buf
|
||||
|
||||
def __str__(self):
|
||||
return "No more data after: %r" % self.buf
|
||||
|
||||
|
||||
@ -4,21 +4,16 @@
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import re
|
||||
import urlparse
|
||||
import socket
|
||||
from errno import ENOTCONN
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
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.six import BytesIO, urlsplit, bytes_to_str
|
||||
|
||||
MAX_REQUEST_LINE = 8190
|
||||
MAX_HEADERS = 32768
|
||||
@ -61,7 +56,7 @@ class Message(object):
|
||||
headers = []
|
||||
|
||||
# Split lines on \r\n keeping the \r\n on each line
|
||||
lines = [line + "\r\n" for line in data.split("\r\n")]
|
||||
lines = [bytes_to_str(line) + "\r\n" for line in data.split(b"\r\n")]
|
||||
|
||||
# Parse headers into key/value pairs paying attention
|
||||
# to continuation lines.
|
||||
@ -153,7 +148,6 @@ class Request(Message):
|
||||
|
||||
self.req_number = req_number
|
||||
self.proxy_protocol_info = None
|
||||
|
||||
super(Request, self).__init__(cfg, unreader)
|
||||
|
||||
|
||||
@ -166,31 +160,31 @@ class Request(Message):
|
||||
buf.write(data)
|
||||
|
||||
def parse(self, unreader):
|
||||
buf = StringIO()
|
||||
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(line):
|
||||
if self.proxy_protocol(bytes_to_str(line)):
|
||||
# get next request line
|
||||
buf = StringIO()
|
||||
buf = BytesIO()
|
||||
buf.write(rbuf)
|
||||
line, rbuf = self.read_line(unreader, buf, self.limit_request_line)
|
||||
|
||||
self.parse_request_line(line)
|
||||
buf = StringIO()
|
||||
self.parse_request_line(bytes_to_str(line))
|
||||
buf = BytesIO()
|
||||
buf.write(rbuf)
|
||||
|
||||
# Headers
|
||||
data = buf.getvalue()
|
||||
idx = data.find("\r\n\r\n")
|
||||
idx = data.find(b"\r\n\r\n")
|
||||
|
||||
done = data[:2] == "\r\n"
|
||||
done = data[:2] == b"\r\n"
|
||||
while True:
|
||||
idx = data.find("\r\n\r\n")
|
||||
done = data[:2] == "\r\n"
|
||||
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)
|
||||
@ -202,19 +196,19 @@ class Request(Message):
|
||||
|
||||
if done:
|
||||
self.unreader.unread(data[2:])
|
||||
return ""
|
||||
return b""
|
||||
|
||||
self.headers = self.parse_headers(data[:idx])
|
||||
|
||||
ret = data[idx+4:]
|
||||
buf = StringIO()
|
||||
buf = BytesIO()
|
||||
return ret
|
||||
|
||||
def read_line(self, unreader, buf, limit=0):
|
||||
data = buf.getvalue()
|
||||
|
||||
while True:
|
||||
idx = data.find("\r\n")
|
||||
idx = data.find(b"\r\n")
|
||||
if idx >= 0:
|
||||
# check if the request line is too large
|
||||
if idx > limit > 0:
|
||||
@ -256,7 +250,7 @@ class Request(Message):
|
||||
try:
|
||||
remote_host = self.unreader.sock.getpeername()[0]
|
||||
except socket.error as e:
|
||||
if e[0] == ENOTCONN:
|
||||
if e.args[0] == ENOTCONN:
|
||||
raise ForbiddenProxyRequest("UNKNOW")
|
||||
raise
|
||||
if remote_host not in self.cfg.proxy_allow_ips:
|
||||
@ -328,7 +322,7 @@ class Request(Message):
|
||||
else:
|
||||
self.uri = bits[1]
|
||||
|
||||
parts = urlparse.urlsplit(self.uri)
|
||||
parts = urlsplit(self.uri)
|
||||
self.path = parts.path or ""
|
||||
self.query = parts.query or ""
|
||||
self.fragment = parts.fragment or ""
|
||||
|
||||
@ -22,7 +22,7 @@ class Parser(object):
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
def __next__(self):
|
||||
# Stop if HTTP dictates a stop.
|
||||
if self.mesg and self.mesg.should_close():
|
||||
raise StopIteration()
|
||||
@ -33,6 +33,7 @@ class Parser(object):
|
||||
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)
|
||||
@ -40,6 +41,8 @@ class Parser(object):
|
||||
raise StopIteration()
|
||||
return self.mesg
|
||||
|
||||
next = __next__
|
||||
|
||||
class RequestParser(Parser):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RequestParser, self).__init__(Request, *args, **kwargs)
|
||||
|
||||
@ -5,47 +5,47 @@
|
||||
|
||||
import os
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
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 = StringIO()
|
||||
self.buf = six.BytesIO()
|
||||
|
||||
def chunk(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def read(self, size=None):
|
||||
if size is not None and not isinstance(size, (int, long)):
|
||||
if size is not None and not isinstance(size, six.integer_types):
|
||||
raise TypeError("size parameter must be an int or long.")
|
||||
if size == 0:
|
||||
return ""
|
||||
if size < 0:
|
||||
size = None
|
||||
|
||||
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.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
return ret
|
||||
if size is None:
|
||||
return self.chunk()
|
||||
d = self.chunk()
|
||||
return d
|
||||
|
||||
while self.buf.tell() < size:
|
||||
chunk = self.chunk()
|
||||
if not len(chunk):
|
||||
ret = self.buf.getvalue()
|
||||
self.buf.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
return ret
|
||||
self.buf.write(chunk)
|
||||
data = self.buf.getvalue()
|
||||
self.buf.truncate(0)
|
||||
self.buf = six.BytesIO()
|
||||
self.buf.write(data[size:])
|
||||
return data[:size]
|
||||
|
||||
@ -69,9 +69,9 @@ class IterUnreader(Unreader):
|
||||
|
||||
def chunk(self):
|
||||
if not self.iter:
|
||||
return ""
|
||||
return b""
|
||||
try:
|
||||
return self.iter.next()
|
||||
return six.next(self.iter)
|
||||
except StopIteration:
|
||||
self.iter = None
|
||||
return ""
|
||||
return b""
|
||||
|
||||
@ -7,8 +7,9 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from urllib import unquote
|
||||
|
||||
from gunicorn.six import (unquote, string_types, binary_type, reraise,
|
||||
text_type)
|
||||
from gunicorn import SERVER_SOFTWARE
|
||||
import gunicorn.util as util
|
||||
|
||||
@ -54,7 +55,7 @@ def default_environ(req, sock, cfg):
|
||||
"REQUEST_METHOD": req.method,
|
||||
"QUERY_STRING": req.query,
|
||||
"RAW_URI": req.uri,
|
||||
"SERVER_PROTOCOL": "HTTP/%s" % ".".join(map(str, req.version))
|
||||
"SERVER_PROTOCOL": "HTTP/%s" % ".".join([str(v) for v in req.version])
|
||||
}
|
||||
|
||||
def proxy_environ(req):
|
||||
@ -118,7 +119,7 @@ def create(req, sock, client, server, cfg):
|
||||
|
||||
environ['wsgi.url_scheme'] = url_scheme
|
||||
|
||||
if isinstance(forward, basestring):
|
||||
if isinstance(forward, string_types):
|
||||
# we only took the last one
|
||||
# http://en.wikipedia.org/wiki/X-Forwarded-For
|
||||
if forward.find(",") >= 0:
|
||||
@ -145,7 +146,7 @@ def create(req, sock, client, server, cfg):
|
||||
environ['REMOTE_ADDR'] = remote[0]
|
||||
environ['REMOTE_PORT'] = str(remote[1])
|
||||
|
||||
if isinstance(server, basestring):
|
||||
if isinstance(server, string_types):
|
||||
server = server.split(":")
|
||||
if len(server) == 1:
|
||||
if url_scheme == "http":
|
||||
@ -196,7 +197,7 @@ class Response(object):
|
||||
if exc_info:
|
||||
try:
|
||||
if self.status and self.headers_sent:
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
reraise(exc_info[0], exc_info[1], exc_info[2])
|
||||
finally:
|
||||
exc_info = None
|
||||
elif self.status is not None:
|
||||
@ -209,7 +210,9 @@ class Response(object):
|
||||
|
||||
def process_headers(self, headers):
|
||||
for name, value in headers:
|
||||
assert isinstance(name, basestring), "%r is not a string" % name
|
||||
assert isinstance(name, string_types), "%r is not a string" % name
|
||||
|
||||
value = str(value).strip()
|
||||
lname = name.lower().strip()
|
||||
if lname == "content-length":
|
||||
self.response_length = int(value)
|
||||
@ -220,11 +223,11 @@ class Response(object):
|
||||
self.upgrade = True
|
||||
elif lname == "upgrade":
|
||||
if value.lower().strip() == "websocket":
|
||||
self.headers.append((name.strip(), str(value).strip()))
|
||||
self.headers.append((name.strip(), value))
|
||||
|
||||
# ignore hopbyhop headers
|
||||
continue
|
||||
self.headers.append((name.strip(), str(value).strip()))
|
||||
self.headers.append((name.strip(), value))
|
||||
|
||||
|
||||
def is_chunked(self):
|
||||
@ -265,13 +268,19 @@ class Response(object):
|
||||
if self.headers_sent:
|
||||
return
|
||||
tosend = self.default_headers()
|
||||
tosend.extend(["%s: %s\r\n" % (n, v) for n, v in self.headers])
|
||||
util.write(self.sock, "%s\r\n" % "".join(tosend))
|
||||
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))
|
||||
self.headers_sent = True
|
||||
|
||||
def write(self, arg):
|
||||
self.send_headers()
|
||||
assert isinstance(arg, basestring), "%r is not a string." % arg
|
||||
|
||||
if isinstance(arg, text_type):
|
||||
arg = arg.encode('utf-8')
|
||||
|
||||
assert isinstance(arg, binary_type), "%r is not a byte." % arg
|
||||
|
||||
arglen = len(arg)
|
||||
tosend = arglen
|
||||
@ -329,12 +338,13 @@ class Response(object):
|
||||
self.send_headers()
|
||||
|
||||
if self.is_chunked():
|
||||
self.sock.sendall("%X\r\n" % nbytes)
|
||||
chunk_size = "%X\r\n" % nbytes
|
||||
self.sock.sendall(chunk_size.encode('utf-8'))
|
||||
|
||||
self.sendfile_all(fileno, self.sock.fileno(), fo_offset, nbytes)
|
||||
|
||||
if self.is_chunked():
|
||||
self.sock.sendall("\r\n")
|
||||
self.sock.sendall(b"\r\n")
|
||||
|
||||
os.lseek(fileno, fd_offset, os.SEEK_SET)
|
||||
else:
|
||||
@ -345,4 +355,4 @@ class Response(object):
|
||||
if not self.headers_sent:
|
||||
self.send_headers()
|
||||
if self.chunked:
|
||||
util.write_chunk(self.sock, "")
|
||||
util.write_chunk(self.sock, b"")
|
||||
|
||||
@ -1,346 +0,0 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
#
|
||||
# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
|
||||
#
|
||||
|
||||
"""
|
||||
Configuration functions for the logging package for Python. The core package
|
||||
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
|
||||
by Apache's log4j system.
|
||||
|
||||
Should work under Python versions >= 1.5.2, except that source line
|
||||
information is not available unless 'sys._getframe()' is.
|
||||
|
||||
Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
|
||||
|
||||
To use, simply 'import logging' and log away!
|
||||
"""
|
||||
|
||||
import sys, logging, logging.handlers, string, socket, struct, os, traceback, types
|
||||
|
||||
try:
|
||||
import thread
|
||||
import threading
|
||||
except ImportError:
|
||||
thread = None
|
||||
|
||||
from SocketServer import ThreadingTCPServer, StreamRequestHandler
|
||||
|
||||
|
||||
DEFAULT_LOGGING_CONFIG_PORT = 9030
|
||||
|
||||
if sys.platform == "win32":
|
||||
RESET_ERROR = 10054 #WSAECONNRESET
|
||||
else:
|
||||
RESET_ERROR = 104 #ECONNRESET
|
||||
|
||||
#
|
||||
# The following code implements a socket listener for on-the-fly
|
||||
# reconfiguration of logging.
|
||||
#
|
||||
# _listener holds the server object doing the listening
|
||||
_listener = None
|
||||
|
||||
def fileConfig(fname, defaults=None):
|
||||
"""
|
||||
Read the logging configuration from a ConfigParser-format file.
|
||||
|
||||
This can be called several times from an application, allowing an end user
|
||||
the ability to select from various pre-canned configurations (if the
|
||||
developer provides a mechanism to present the choices and load the chosen
|
||||
configuration).
|
||||
In versions of ConfigParser which have the readfp method [typically
|
||||
shipped in 2.x versions of Python], you can pass in a file-like object
|
||||
rather than a filename, in which case the file-like object will be read
|
||||
using readfp.
|
||||
"""
|
||||
import ConfigParser
|
||||
|
||||
cp = ConfigParser.ConfigParser(defaults)
|
||||
if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
|
||||
cp.readfp(fname)
|
||||
else:
|
||||
cp.read(fname)
|
||||
|
||||
formatters = _create_formatters(cp)
|
||||
|
||||
# critical section
|
||||
logging._acquireLock()
|
||||
try:
|
||||
logging._handlers.clear()
|
||||
if hasattr(logging, '_handlerList'):
|
||||
del logging._handlerList[:]
|
||||
# Handlers add themselves to logging._handlers
|
||||
handlers = _install_handlers(cp, formatters)
|
||||
_install_loggers(cp, handlers)
|
||||
finally:
|
||||
logging._releaseLock()
|
||||
|
||||
|
||||
def _resolve(name):
|
||||
"""Resolve a dotted name to a global object."""
|
||||
name = string.split(name, '.')
|
||||
used = name.pop(0)
|
||||
found = __import__(used)
|
||||
for n in name:
|
||||
used = used + '.' + n
|
||||
try:
|
||||
found = getattr(found, n)
|
||||
except AttributeError:
|
||||
__import__(used)
|
||||
found = getattr(found, n)
|
||||
return found
|
||||
|
||||
|
||||
def _create_formatters(cp):
|
||||
"""Create and return formatters"""
|
||||
flist = cp.get("formatters", "keys")
|
||||
if not len(flist):
|
||||
return {}
|
||||
flist = string.split(flist, ",")
|
||||
formatters = {}
|
||||
for form in flist:
|
||||
form = string.strip(form)
|
||||
sectname = "formatter_%s" % form
|
||||
opts = cp.options(sectname)
|
||||
if "format" in opts:
|
||||
fs = cp.get(sectname, "format", 1)
|
||||
else:
|
||||
fs = None
|
||||
if "datefmt" in opts:
|
||||
dfs = cp.get(sectname, "datefmt", 1)
|
||||
else:
|
||||
dfs = None
|
||||
c = logging.Formatter
|
||||
if "class" in opts:
|
||||
class_name = cp.get(sectname, "class")
|
||||
if class_name:
|
||||
c = _resolve(class_name)
|
||||
f = c(fs, dfs)
|
||||
formatters[form] = f
|
||||
return formatters
|
||||
|
||||
|
||||
def _install_handlers(cp, formatters):
|
||||
"""Install and return handlers"""
|
||||
hlist = cp.get("handlers", "keys")
|
||||
if not len(hlist):
|
||||
return {}
|
||||
hlist = string.split(hlist, ",")
|
||||
handlers = {}
|
||||
fixups = [] #for inter-handler references
|
||||
for hand in hlist:
|
||||
hand = string.strip(hand)
|
||||
sectname = "handler_%s" % hand
|
||||
klass = cp.get(sectname, "class")
|
||||
opts = cp.options(sectname)
|
||||
if "formatter" in opts:
|
||||
fmt = cp.get(sectname, "formatter")
|
||||
else:
|
||||
fmt = ""
|
||||
try:
|
||||
klass = eval(klass, vars(logging))
|
||||
except (AttributeError, NameError):
|
||||
klass = _resolve(klass)
|
||||
args = cp.get(sectname, "args")
|
||||
args = eval(args, vars(logging))
|
||||
h = apply(klass, args)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
h.setLevel(logging._levelNames[level])
|
||||
if len(fmt):
|
||||
h.setFormatter(formatters[fmt])
|
||||
#temporary hack for FileHandler and MemoryHandler.
|
||||
if klass == logging.handlers.MemoryHandler:
|
||||
if "target" in opts:
|
||||
target = cp.get(sectname,"target")
|
||||
else:
|
||||
target = ""
|
||||
if len(target): #the target handler may not be loaded yet, so keep for later...
|
||||
fixups.append((h, target))
|
||||
handlers[hand] = h
|
||||
#now all handlers are loaded, fixup inter-handler references...
|
||||
for h, t in fixups:
|
||||
h.setTarget(handlers[t])
|
||||
return handlers
|
||||
|
||||
|
||||
def _install_loggers(cp, handlers):
|
||||
"""Create and install loggers"""
|
||||
|
||||
# configure the root first
|
||||
llist = cp.get("loggers", "keys")
|
||||
llist = string.split(llist, ",")
|
||||
llist = map(lambda x: string.strip(x), llist)
|
||||
llist.remove("root")
|
||||
sectname = "logger_root"
|
||||
root = logging.root
|
||||
log = root
|
||||
opts = cp.options(sectname)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
log.setLevel(logging._levelNames[level])
|
||||
for h in root.handlers[:]:
|
||||
root.removeHandler(h)
|
||||
hlist = cp.get(sectname, "handlers")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
for hand in hlist:
|
||||
log.addHandler(handlers[string.strip(hand)])
|
||||
|
||||
#and now the others...
|
||||
#we don't want to lose the existing loggers,
|
||||
#since other threads may have pointers to them.
|
||||
#existing is set to contain all existing loggers,
|
||||
#and as we go through the new configuration we
|
||||
#remove any which are configured. At the end,
|
||||
#what's left in existing is the set of loggers
|
||||
#which were in the previous configuration but
|
||||
#which are not in the new configuration.
|
||||
existing = root.manager.loggerDict.keys()
|
||||
#now set up the new ones...
|
||||
for log in llist:
|
||||
sectname = "logger_%s" % log
|
||||
qn = cp.get(sectname, "qualname")
|
||||
opts = cp.options(sectname)
|
||||
if "propagate" in opts:
|
||||
propagate = cp.getint(sectname, "propagate")
|
||||
else:
|
||||
propagate = 1
|
||||
logger = logging.getLogger(qn)
|
||||
if qn in existing:
|
||||
existing.remove(qn)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
logger.setLevel(logging._levelNames[level])
|
||||
for h in logger.handlers[:]:
|
||||
logger.removeHandler(h)
|
||||
logger.propagate = propagate
|
||||
logger.disabled = 0
|
||||
hlist = cp.get(sectname, "handlers")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
for hand in hlist:
|
||||
logger.addHandler(handlers[string.strip(hand)])
|
||||
|
||||
#Disable any old loggers. There's no point deleting
|
||||
#them as other threads may continue to hold references
|
||||
#and by disabling them, you stop them doing any logging.
|
||||
for log in existing:
|
||||
root.manager.loggerDict[log].disabled = 1
|
||||
|
||||
|
||||
def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
|
||||
"""
|
||||
Start up a socket server on the specified port, and listen for new
|
||||
configurations.
|
||||
|
||||
These will be sent as a file suitable for processing by fileConfig().
|
||||
Returns a Thread object on which you can call start() to start the server,
|
||||
and which you can join() when appropriate. To stop the server, call
|
||||
stopListening().
|
||||
"""
|
||||
if not thread:
|
||||
raise NotImplementedError, "listen() needs threading to work"
|
||||
|
||||
class ConfigStreamHandler(StreamRequestHandler):
|
||||
"""
|
||||
Handler for a logging configuration request.
|
||||
|
||||
It expects a completely new logging configuration and uses fileConfig
|
||||
to install it.
|
||||
"""
|
||||
def handle(self):
|
||||
"""
|
||||
Handle a request.
|
||||
|
||||
Each request is expected to be a 4-byte length, packed using
|
||||
struct.pack(">L", n), followed by the config file.
|
||||
Uses fileConfig() to do the grunt work.
|
||||
"""
|
||||
import tempfile
|
||||
try:
|
||||
conn = self.connection
|
||||
chunk = conn.recv(4)
|
||||
if len(chunk) == 4:
|
||||
slen = struct.unpack(">L", chunk)[0]
|
||||
chunk = self.connection.recv(slen)
|
||||
while len(chunk) < slen:
|
||||
chunk = chunk + conn.recv(slen - len(chunk))
|
||||
#Apply new configuration. We'd like to be able to
|
||||
#create a StringIO and pass that in, but unfortunately
|
||||
#1.5.2 ConfigParser does not support reading file
|
||||
#objects, only actual files. So we create a temporary
|
||||
#file and remove it later.
|
||||
file = tempfile.mktemp(".ini")
|
||||
f = open(file, "w")
|
||||
f.write(chunk)
|
||||
f.close()
|
||||
try:
|
||||
fileConfig(file)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
traceback.print_exc()
|
||||
os.remove(file)
|
||||
except socket.error, e:
|
||||
if type(e.args) != types.TupleType:
|
||||
raise
|
||||
else:
|
||||
errcode = e.args[0]
|
||||
if errcode != RESET_ERROR:
|
||||
raise
|
||||
|
||||
class ConfigSocketReceiver(ThreadingTCPServer):
|
||||
"""
|
||||
A simple TCP socket-based logging config receiver.
|
||||
"""
|
||||
|
||||
allow_reuse_address = 1
|
||||
|
||||
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
|
||||
handler=None):
|
||||
ThreadingTCPServer.__init__(self, (host, port), handler)
|
||||
logging._acquireLock()
|
||||
self.abort = 0
|
||||
logging._releaseLock()
|
||||
self.timeout = 1
|
||||
|
||||
def serve_until_stopped(self):
|
||||
import select
|
||||
abort = 0
|
||||
while not abort:
|
||||
rd, wr, ex = select.select([self.socket.fileno()],
|
||||
[], [],
|
||||
self.timeout)
|
||||
if rd:
|
||||
self.handle_request()
|
||||
logging._acquireLock()
|
||||
abort = self.abort
|
||||
logging._releaseLock()
|
||||
|
||||
def serve(rcvr, hdlr, port):
|
||||
server = rcvr(port=port, handler=hdlr)
|
||||
global _listener
|
||||
logging._acquireLock()
|
||||
_listener = server
|
||||
logging._releaseLock()
|
||||
server.serve_until_stopped()
|
||||
|
||||
return threading.Thread(target=serve,
|
||||
args=(ConfigSocketReceiver,
|
||||
ConfigStreamHandler, port))
|
||||
|
||||
def stopListening():
|
||||
"""
|
||||
Stop the listening server which was created with a call to listen().
|
||||
"""
|
||||
global _listener
|
||||
if _listener:
|
||||
logging._acquireLock()
|
||||
_listener.abort = 1
|
||||
_listener = None
|
||||
logging._releaseLock()
|
||||
@ -76,11 +76,11 @@ class Pidfile(object):
|
||||
try:
|
||||
os.kill(wpid, 0)
|
||||
return wpid
|
||||
except OSError, e:
|
||||
if e[0] == errno.ESRCH:
|
||||
except OSError as e:
|
||||
if e.args[0] == errno.ESRCH:
|
||||
return
|
||||
raise
|
||||
except IOError, e:
|
||||
if e[0] == errno.ENOENT:
|
||||
except IOError as e:
|
||||
if e.args[0] == errno.ENOENT:
|
||||
return
|
||||
raise
|
||||
|
||||
399
gunicorn/six.py
Normal file
399
gunicorn/six.py
Normal file
@ -0,0 +1,399 @@
|
||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||
|
||||
import operator
|
||||
import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.2.0"
|
||||
|
||||
|
||||
# True if we are running on Python 3.
|
||||
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 == "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)
|
||||
# This is a bit ugly, but it avoids running this again.
|
||||
delattr(tp, 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)
|
||||
|
||||
|
||||
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 _MovedItems(types.ModuleType):
|
||||
"""Lazy loading of moved objects"""
|
||||
|
||||
|
||||
_moved_attributes = [
|
||||
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
||||
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
|
||||
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||
MovedAttribute("StringIO", "StringIO", "io"),
|
||||
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||
|
||||
MovedModule("builtins", "__builtin__"),
|
||||
MovedModule("configparser", "ConfigParser"),
|
||||
MovedModule("copyreg", "copy_reg"),
|
||||
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("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("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_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_robotparser", "robotparser", "urllib.robotparser"),
|
||||
MovedModule("winreg", "_winreg"),
|
||||
]
|
||||
for attr in _moved_attributes:
|
||||
setattr(_MovedItems, attr.name, attr)
|
||||
del attr
|
||||
|
||||
moves = sys.modules["gunicorn.six.moves"] = _MovedItems("moves")
|
||||
|
||||
|
||||
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_code = "__code__"
|
||||
_func_defaults = "__defaults__"
|
||||
|
||||
_iterkeys = "keys"
|
||||
_itervalues = "values"
|
||||
_iteritems = "items"
|
||||
else:
|
||||
_meth_func = "im_func"
|
||||
_meth_self = "im_self"
|
||||
|
||||
_func_code = "func_code"
|
||||
_func_defaults = "func_defaults"
|
||||
|
||||
_iterkeys = "iterkeys"
|
||||
_itervalues = "itervalues"
|
||||
_iteritems = "iteritems"
|
||||
|
||||
|
||||
try:
|
||||
advance_iterator = next
|
||||
except NameError:
|
||||
def advance_iterator(it):
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
|
||||
if PY3:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound
|
||||
|
||||
Iterator = object
|
||||
|
||||
def callable(obj):
|
||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||
else:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound.im_func
|
||||
|
||||
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_code = operator.attrgetter(_func_code)
|
||||
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||
|
||||
|
||||
def iterkeys(d):
|
||||
"""Return an iterator over the keys of a dictionary."""
|
||||
return iter(getattr(d, _iterkeys)())
|
||||
|
||||
def itervalues(d):
|
||||
"""Return an iterator over the values of a dictionary."""
|
||||
return iter(getattr(d, _itervalues)())
|
||||
|
||||
def iteritems(d):
|
||||
"""Return an iterator over the (key, value) pairs of a dictionary."""
|
||||
return iter(getattr(d, _iteritems)())
|
||||
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
def u(s):
|
||||
return s
|
||||
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")
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
BytesIO = io.BytesIO
|
||||
else:
|
||||
def b(s):
|
||||
return s
|
||||
def u(s):
|
||||
return unicode(s, "unicode_escape")
|
||||
int2byte = chr
|
||||
import StringIO
|
||||
StringIO = BytesIO = StringIO.StringIO
|
||||
_add_doc(b, """Byte literal""")
|
||||
_add_doc(u, """Text literal""")
|
||||
|
||||
|
||||
if PY3:
|
||||
|
||||
import builtins
|
||||
exec_ = getattr(builtins, "exec")
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
|
||||
print_ = getattr(builtins, "print")
|
||||
|
||||
def execfile_(fname, *args):
|
||||
return exec_(compile(open(fname, 'rb').read(), fname, 'exec'), *args)
|
||||
|
||||
|
||||
del builtins
|
||||
|
||||
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
|
||||
""")
|
||||
|
||||
execfile_ = execfile
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
"""The new-style print function."""
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
def write(data):
|
||||
if not isinstance(data, basestring):
|
||||
data = str(data)
|
||||
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.""")
|
||||
|
||||
|
||||
def with_metaclass(meta, base=object):
|
||||
"""Create a base class with a metaclass."""
|
||||
return meta("NewBase", (base,), {})
|
||||
|
||||
|
||||
# specific to gunicorn
|
||||
if PY3:
|
||||
def bytes_to_str(b):
|
||||
if isinstance(b, text_type):
|
||||
return b
|
||||
return str(b, 'latin1')
|
||||
|
||||
import urllib.parse
|
||||
|
||||
unquote = urllib.parse.unquote
|
||||
urlsplit = urllib.parse.urlsplit
|
||||
urlparse = urllib.parse.urlparse
|
||||
|
||||
else:
|
||||
def bytes_to_str(s):
|
||||
if isinstance(s, unicode):
|
||||
return s.encode('utf-8')
|
||||
return s
|
||||
|
||||
import urlparse as orig_urlparse
|
||||
urlsplit = orig_urlparse.urlsplit
|
||||
urlparse = orig_urlparse.urlparse
|
||||
|
||||
import urllib
|
||||
unquote = urllib.unquote
|
||||
@ -10,7 +10,7 @@ import sys
|
||||
import time
|
||||
|
||||
from gunicorn import util
|
||||
|
||||
from gunicorn.six import string_types
|
||||
|
||||
class BaseSocket(object):
|
||||
|
||||
@ -44,7 +44,7 @@ class BaseSocket(object):
|
||||
def close(self):
|
||||
try:
|
||||
self.sock.close()
|
||||
except socket.error, e:
|
||||
except socket.error as e:
|
||||
self.log.info("Error while closing socket %s", str(e))
|
||||
time.sleep(0.3)
|
||||
del self.sock
|
||||
@ -108,7 +108,7 @@ def create_socket(conf, log):
|
||||
sock_type = TCP6Socket
|
||||
else:
|
||||
sock_type = TCPSocket
|
||||
elif isinstance(addr, basestring):
|
||||
elif isinstance(addr, string_types):
|
||||
sock_type = UnixSocket
|
||||
else:
|
||||
raise TypeError("Unable to create socket from: %r" % addr)
|
||||
@ -117,8 +117,8 @@ def create_socket(conf, log):
|
||||
fd = int(os.environ.pop('GUNICORN_FD'))
|
||||
try:
|
||||
return sock_type(conf, log, fd=fd)
|
||||
except socket.error, e:
|
||||
if e[0] == errno.ENOTCONN:
|
||||
except socket.error as e:
|
||||
if e.args[0] == errno.ENOTCONN:
|
||||
log.error("GUNICORN_FD should refer to an open socket.")
|
||||
else:
|
||||
raise
|
||||
@ -130,10 +130,10 @@ def create_socket(conf, log):
|
||||
for i in range(5):
|
||||
try:
|
||||
return sock_type(conf, log)
|
||||
except socket.error, e:
|
||||
if e[0] == errno.EADDRINUSE:
|
||||
except socket.error as e:
|
||||
if e.args[0] == errno.EADDRINUSE:
|
||||
log.error("Connection in use: %s", str(addr))
|
||||
if e[0] == errno.EADDRNOTAVAIL:
|
||||
if e.args[0] == errno.EADDRNOTAVAIL:
|
||||
log.error("Invalid address: %s", str(addr))
|
||||
sys.exit(1)
|
||||
if i < 5:
|
||||
|
||||
@ -25,6 +25,7 @@ import textwrap
|
||||
import time
|
||||
import inspect
|
||||
|
||||
from gunicorn.six import text_type, string_types
|
||||
|
||||
MAXFD = 1024
|
||||
if (hasattr(os, "devnull")):
|
||||
@ -74,7 +75,7 @@ except ImportError:
|
||||
if not hasattr(package, 'rindex'):
|
||||
raise ValueError("'package' not set to a string")
|
||||
dot = len(package)
|
||||
for x in xrange(level, 1, -1):
|
||||
for x in range(level, 1, -1):
|
||||
try:
|
||||
dot = package.rindex('.', 0, dot)
|
||||
except ValueError:
|
||||
@ -125,7 +126,7 @@ def load_class(uri, default="sync", section="gunicorn.workers"):
|
||||
|
||||
return pkg_resources.load_entry_point("gunicorn",
|
||||
section, uri)
|
||||
except ImportError, e:
|
||||
except ImportError as e:
|
||||
raise RuntimeError("class uri invalid or not found: " +
|
||||
"[%s]" % str(e))
|
||||
klass = components.pop(-1)
|
||||
@ -216,14 +217,17 @@ try:
|
||||
except ImportError:
|
||||
def closerange(fd_low, fd_high):
|
||||
# Iterate through and close all file descriptors.
|
||||
for fd in xrange(fd_low, fd_high):
|
||||
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):
|
||||
chunk = "".join(("%X\r\n" % len(data), data, "\r\n"))
|
||||
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):
|
||||
@ -267,7 +271,7 @@ def write_error(sock, status_int, reason, mesg):
|
||||
\r
|
||||
%s
|
||||
""") % (str(status_int), reason, len(html), html)
|
||||
write_nonblock(sock, http)
|
||||
write_nonblock(sock, http.encode('latin1'))
|
||||
|
||||
def normalize_name(name):
|
||||
return "-".join([w.lower().capitalize() for w in name.split("-")])
|
||||
@ -307,15 +311,6 @@ def http_date(timestamp=None):
|
||||
hh, mm, ss)
|
||||
return s
|
||||
|
||||
def to_bytestring(s):
|
||||
""" convert to bytestring an unicode """
|
||||
if not isinstance(s, basestring):
|
||||
return s
|
||||
if isinstance(s, unicode):
|
||||
return s.encode('utf-8')
|
||||
else:
|
||||
return s
|
||||
|
||||
def is_hoppish(header):
|
||||
return header.lower().strip() in hop_headers
|
||||
|
||||
@ -350,6 +345,13 @@ def seed():
|
||||
def check_is_writeable(path):
|
||||
try:
|
||||
f = open(path, 'a')
|
||||
except IOError, e:
|
||||
except IOError as e:
|
||||
raise RuntimeError("Error: '%s' isn't writable [%r]" % (path, e))
|
||||
f.close()
|
||||
|
||||
def to_bytestring(value):
|
||||
"""Converts a string argument to a byte string"""
|
||||
if isinstance(value, bytes):
|
||||
return value
|
||||
assert isinstance(value, text_type)
|
||||
return value.encode("utf-8")
|
||||
|
||||
@ -13,6 +13,7 @@ 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()
|
||||
|
||||
@ -28,40 +29,37 @@ class AsyncWorker(base.Worker):
|
||||
def handle(self, client, addr):
|
||||
req = None
|
||||
try:
|
||||
client.settimeout(self.cfg.timeout)
|
||||
parser = http.RequestParser(self.cfg, client)
|
||||
try:
|
||||
if not self.cfg.keepalive:
|
||||
req = parser.next()
|
||||
req = six.next(parser)
|
||||
self.handle_request(req, client, addr)
|
||||
else:
|
||||
# keepalive loop
|
||||
while True:
|
||||
req = None
|
||||
with self.timeout_ctx():
|
||||
req = parser.next()
|
||||
req = six.next(parser)
|
||||
if not req:
|
||||
break
|
||||
self.handle_request(req, client, addr)
|
||||
except http.errors.NoMoreData, e:
|
||||
except http.errors.NoMoreData as e:
|
||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||
except StopIteration, e:
|
||||
except StopIteration as e:
|
||||
self.log.debug("Closing connection. %s", e)
|
||||
except socket.error:
|
||||
raise # pass to next try-except level
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.handle_error(req, client, addr, e)
|
||||
except socket.timeout as e:
|
||||
self.handle_error(req, client, addr, e)
|
||||
except socket.error, e:
|
||||
if e[0] not in (errno.EPIPE, errno.ECONNRESET):
|
||||
except socket.error as e:
|
||||
if e.args[0] not in (errno.EPIPE, errno.ECONNRESET):
|
||||
self.log.exception("Socket error processing request.")
|
||||
else:
|
||||
if e[0] == errno.ECONNRESET:
|
||||
if e.args[0] == errno.ECONNRESET:
|
||||
self.log.debug("Ignoring connection reset")
|
||||
else:
|
||||
self.log.debug("Ignoring EPIPE")
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.handle_error(req, client, addr, e)
|
||||
finally:
|
||||
util.close(client)
|
||||
|
||||
@ -8,7 +8,6 @@ import os
|
||||
import signal
|
||||
import sys
|
||||
import traceback
|
||||
import socket
|
||||
|
||||
|
||||
from gunicorn import util
|
||||
@ -18,13 +17,12 @@ InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \
|
||||
LimitRequestLine, LimitRequestHeaders
|
||||
from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
|
||||
from gunicorn.http.wsgi import default_environ, Response
|
||||
from gunicorn.six import MAXSIZE
|
||||
|
||||
class Worker(object):
|
||||
|
||||
SIGNALS = map(
|
||||
lambda x: getattr(signal, "SIG%s" % x),
|
||||
"HUP QUIT INT TERM USR1 USR2 WINCH CHLD".split()
|
||||
)
|
||||
SIGNALS = [getattr(signal, "SIG%s" % x) \
|
||||
for x in "HUP QUIT INT TERM USR1 USR2 WINCH CHLD".split()]
|
||||
|
||||
PIPE = []
|
||||
|
||||
@ -43,7 +41,7 @@ class Worker(object):
|
||||
self.booted = False
|
||||
|
||||
self.nr = 0
|
||||
self.max_requests = cfg.max_requests or sys.maxint
|
||||
self.max_requests = cfg.max_requests or MAXSIZE
|
||||
self.alive = True
|
||||
self.log = log
|
||||
self.debug = cfg.debug
|
||||
@ -87,8 +85,9 @@ class Worker(object):
|
||||
|
||||
# For waking ourselves up
|
||||
self.PIPE = os.pipe()
|
||||
map(util.set_non_blocking, self.PIPE)
|
||||
map(util.close_on_exec, self.PIPE)
|
||||
for p in self.PIPE:
|
||||
util.set_non_blocking(p)
|
||||
util.close_on_exec(p)
|
||||
|
||||
# Prevent fd inherientence
|
||||
util.close_on_exec(self.socket)
|
||||
@ -105,7 +104,9 @@ class Worker(object):
|
||||
self.run()
|
||||
|
||||
def init_signals(self):
|
||||
map(lambda s: signal.signal(s, signal.SIG_DFL), self.SIGNALS)
|
||||
# reset signaling
|
||||
[signal.signal(s, signal.SIG_DFL) for s in self.SIGNALS]
|
||||
# init new signaling
|
||||
signal.signal(signal.SIGQUIT, self.handle_quit)
|
||||
signal.signal(signal.SIGTERM, self.handle_exit)
|
||||
signal.signal(signal.SIGINT, self.handle_exit)
|
||||
@ -164,10 +165,6 @@ class Worker(object):
|
||||
error=str(exc),
|
||||
)
|
||||
)
|
||||
elif isinstance(exc, socket.timeout):
|
||||
status_int = 408
|
||||
reason = "Request Timeout"
|
||||
mesg = "<p>The server timed out handling for the request</p>"
|
||||
else:
|
||||
self.log.exception("Error handling request")
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ 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 SyncWorker(base.Worker):
|
||||
|
||||
@ -40,8 +41,8 @@ class SyncWorker(base.Worker):
|
||||
# process.
|
||||
continue
|
||||
|
||||
except socket.error, e:
|
||||
if e[0] not in (errno.EAGAIN, errno.ECONNABORTED):
|
||||
except socket.error as e:
|
||||
if e.args[0] not in (errno.EAGAIN, errno.ECONNABORTED):
|
||||
raise
|
||||
|
||||
# If our parent changed then we shut down.
|
||||
@ -54,10 +55,10 @@ class SyncWorker(base.Worker):
|
||||
ret = select.select([self.socket], [], self.PIPE, self.timeout)
|
||||
if ret[0]:
|
||||
continue
|
||||
except select.error, e:
|
||||
if e[0] == errno.EINTR:
|
||||
except select.error as e:
|
||||
if e.args[0] == errno.EINTR:
|
||||
continue
|
||||
if e[0] == errno.EBADF:
|
||||
if e.args[0] == errno.EBADF:
|
||||
if self.nr < 0:
|
||||
continue
|
||||
else:
|
||||
@ -67,22 +68,19 @@ class SyncWorker(base.Worker):
|
||||
def handle(self, client, addr):
|
||||
req = None
|
||||
try:
|
||||
client.settimeout(self.cfg.timeout)
|
||||
parser = http.RequestParser(self.cfg, client)
|
||||
req = parser.next()
|
||||
req = six.next(parser)
|
||||
self.handle_request(req, client, addr)
|
||||
except http.errors.NoMoreData, e:
|
||||
except http.errors.NoMoreData as e:
|
||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||
except StopIteration, e:
|
||||
except StopIteration as e:
|
||||
self.log.debug("Closing connection. %s", e)
|
||||
except socket.timeout as e:
|
||||
self.handle_error(req, client, addr, e)
|
||||
except socket.error, e:
|
||||
if e[0] != errno.EPIPE:
|
||||
except socket.error as e:
|
||||
if e.args[0] != errno.EPIPE:
|
||||
self.log.exception("Error processing request.")
|
||||
else:
|
||||
self.log.debug("Ignoring EPIPE")
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.handle_error(req, client, addr, e)
|
||||
finally:
|
||||
util.close(client)
|
||||
@ -117,7 +115,7 @@ class SyncWorker(base.Worker):
|
||||
respiter.close()
|
||||
except socket.error:
|
||||
raise
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
# Only send back traceback in HTTP in debug mode.
|
||||
self.handle_error(req, client, addr, e)
|
||||
return
|
||||
|
||||
2
requirements_dev.txt
Normal file
2
requirements_dev.txt
Normal file
@ -0,0 +1,2 @@
|
||||
pytest
|
||||
pytest-cov
|
||||
@ -2,3 +2,6 @@
|
||||
build-requires = python2-devel python-setuptools
|
||||
requires = python-setuptools >= 0.6c6 python-ctypes
|
||||
install_script = rpm/install
|
||||
|
||||
[pytest]
|
||||
norecursedirs = examples lib local src
|
||||
|
||||
85
setup.py
85
setup.py
@ -5,47 +5,83 @@
|
||||
|
||||
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools import setup, find_packages, Command
|
||||
import sys
|
||||
|
||||
from gunicorn import __version__
|
||||
|
||||
CLASSIFIERS = [
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Other Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: MacOS :: MacOS X',
|
||||
'Operating System :: POSIX',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Topic :: Internet',
|
||||
'Topic :: Utilities',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Internet :: WWW/HTTP :: WSGI',
|
||||
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
|
||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content']
|
||||
|
||||
# read long description
|
||||
with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as f:
|
||||
long_description = f.read()
|
||||
|
||||
# read dev requirements
|
||||
fname = os.path.join(os.path.dirname(__file__), 'requirements_dev.txt')
|
||||
with open(fname) as f:
|
||||
tests_require = list(map(lambda l: l.strip(), f.readlines()))
|
||||
|
||||
class PyTest(Command):
|
||||
user_options = [
|
||||
("cov", None, "measure coverage")
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
self.cov = None
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
import sys,subprocess
|
||||
basecmd = [sys.executable, '-m', 'py.test']
|
||||
if self.cov:
|
||||
basecmd += ['--cov', 'gunicorn']
|
||||
errno = subprocess.call(basecmd + ['tests'])
|
||||
raise SystemExit(errno)
|
||||
|
||||
|
||||
setup(
|
||||
name = 'gunicorn',
|
||||
version = __version__,
|
||||
|
||||
description = 'WSGI HTTP Server for UNIX',
|
||||
long_description = file(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'README.rst'
|
||||
)
|
||||
).read(),
|
||||
long_description = long_description,
|
||||
author = 'Benoit Chesneau',
|
||||
author_email = 'benoitc@e-engura.com',
|
||||
license = 'MIT',
|
||||
url = 'http://gunicorn.org',
|
||||
|
||||
classifiers = [
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Other Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: MacOS :: MacOS X',
|
||||
'Operating System :: POSIX',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Internet',
|
||||
'Topic :: Utilities',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Internet :: WWW/HTTP :: WSGI',
|
||||
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
|
||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||
],
|
||||
classifiers = CLASSIFIERS,
|
||||
zip_safe = False,
|
||||
packages = find_packages(exclude=['examples', 'tests']),
|
||||
include_package_data = True,
|
||||
|
||||
tests_require = tests_require,
|
||||
cmdclass = {'test': PyTest},
|
||||
|
||||
entry_points="""
|
||||
|
||||
[console_scripts]
|
||||
@ -66,6 +102,5 @@ setup(
|
||||
|
||||
[paste.server_runner]
|
||||
main=gunicorn.app.pasterapp:paste_server
|
||||
""",
|
||||
test_suite = 'nose.collector',
|
||||
"""
|
||||
)
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
from StringIO import StringIO
|
||||
|
||||
import t
|
||||
from gunicorn.http.body import Body
|
||||
|
||||
|
||||
def assert_readline(payload, size, expected):
|
||||
body = Body(StringIO(payload))
|
||||
t.eq(body.readline(size), expected)
|
||||
|
||||
|
||||
def test_readline_empty_body():
|
||||
assert_readline("", None, "")
|
||||
assert_readline("", 1, "")
|
||||
|
||||
|
||||
def test_readline_zero_size():
|
||||
assert_readline("abc", 0, "")
|
||||
assert_readline("\n", 0, "")
|
||||
|
||||
|
||||
def test_readline_new_line_before_size():
|
||||
body = Body(StringIO("abc\ndef"))
|
||||
t.eq(body.readline(4), "abc\n")
|
||||
t.eq(body.readline(), "def")
|
||||
|
||||
|
||||
def test_readline_new_line_after_size():
|
||||
body = Body(StringIO("abc\ndef"))
|
||||
t.eq(body.readline(2), "ab")
|
||||
t.eq(body.readline(), "c\n")
|
||||
|
||||
|
||||
def test_readline_no_new_line():
|
||||
body = Body(StringIO("abcdef"))
|
||||
t.eq(body.readline(), "abcdef")
|
||||
body = Body(StringIO("abcdef"))
|
||||
t.eq(body.readline(2), "ab")
|
||||
t.eq(body.readline(2), "cd")
|
||||
t.eq(body.readline(2), "ef")
|
||||
|
||||
|
||||
def test_readline_buffer_loaded():
|
||||
reader = StringIO("abc\ndef")
|
||||
body = Body(reader)
|
||||
body.read(1) # load internal buffer
|
||||
reader.write("g\nhi")
|
||||
reader.seek(7)
|
||||
t.eq(body.readline(), "bc\n")
|
||||
t.eq(body.readline(), "defg\n")
|
||||
t.eq(body.readline(), "hi")
|
||||
|
||||
|
||||
def test_readline_buffer_loaded_with_size():
|
||||
body = Body(StringIO("abc\ndef"))
|
||||
body.read(1) # load internal buffer
|
||||
t.eq(body.readline(2), "bc")
|
||||
t.eq(body.readline(2), "\n")
|
||||
t.eq(body.readline(2), "de")
|
||||
t.eq(body.readline(2), "f")
|
||||
|
||||
@ -7,5 +7,5 @@ request = {
|
||||
("CONTENT-TYPE", "application/json"),
|
||||
("CONTENT-LENGTH", "14")
|
||||
],
|
||||
"body": '{"nom": "nom"}'
|
||||
}
|
||||
"body": b'{"nom": "nom"}'
|
||||
}
|
||||
|
||||
@ -7,5 +7,5 @@ request = {
|
||||
("HOST", "0.0.0.0=5000"),
|
||||
("ACCEPT", "*/*")
|
||||
],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -12,5 +12,5 @@ request = {
|
||||
("KEEP-ALIVE", "300"),
|
||||
("CONNECTION", "keep-alive")
|
||||
],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -5,5 +5,5 @@ request = {
|
||||
"headers": [
|
||||
("AAAAAAAAAAAAA", "++++++++++")
|
||||
],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -3,5 +3,5 @@ request = {
|
||||
"uri": uri("/forums/1/topics/2375?page=1#posts-17408"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -3,5 +3,5 @@ request = {
|
||||
"uri": uri("/get_no_headers_no_body/world"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -5,5 +5,5 @@ request = {
|
||||
"headers": [
|
||||
("ACCEPT", "*/*")
|
||||
],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -5,5 +5,5 @@ request = {
|
||||
"headers": [
|
||||
("CONTENT-LENGTH", "5")
|
||||
],
|
||||
"body": "HELLO"
|
||||
}
|
||||
"body": b"HELLO"
|
||||
}
|
||||
|
||||
@ -7,5 +7,5 @@ request = {
|
||||
("TRANSFER-ENCODING", "identity"),
|
||||
("CONTENT-LENGTH", "5")
|
||||
],
|
||||
"body": "World"
|
||||
}
|
||||
"body": b"World"
|
||||
}
|
||||
|
||||
@ -5,5 +5,5 @@ request = {
|
||||
"headers": [
|
||||
("TRANSFER-ENCODING", "chunked"),
|
||||
],
|
||||
"body": "all your base are belong to us"
|
||||
}
|
||||
"body": b"all your base are belong to us"
|
||||
}
|
||||
|
||||
@ -5,5 +5,5 @@ request = {
|
||||
"headers": [
|
||||
("TRANSFER-ENCODING", "chunked")
|
||||
],
|
||||
"body": "hello world"
|
||||
}
|
||||
"body": b"hello world"
|
||||
}
|
||||
|
||||
@ -5,9 +5,9 @@ request = {
|
||||
"headers": [
|
||||
("TRANSFER-ENCODING", "chunked")
|
||||
],
|
||||
"body": "hello world",
|
||||
"body": b"hello world",
|
||||
"trailers": [
|
||||
("VARY", "*"),
|
||||
("CONTENT-TYPE", "text/plain")
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,5 +5,5 @@ request = {
|
||||
"headers": [
|
||||
("TRANSFER-ENCODING", "chunked")
|
||||
],
|
||||
"body": "hello world"
|
||||
}
|
||||
"body": b"hello world"
|
||||
}
|
||||
|
||||
@ -3,5 +3,5 @@ request = {
|
||||
"uri": uri('/with_"quotes"?foo="bar"'),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -7,5 +7,5 @@ request = {
|
||||
("USER-AGENT", "ApacheBench/2.3"),
|
||||
("ACCEPT", "*/*")
|
||||
],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -36,5 +36,5 @@ request = {
|
||||
"uri": uri("/"),
|
||||
"version": (1, 1),
|
||||
"headers": [("X-SSL-CERT", certificate)],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -6,5 +6,5 @@ request = {
|
||||
("IF-MATCH", "bazinga!"),
|
||||
("IF-MATCH", "large-sound")
|
||||
],
|
||||
"body": ""
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ req1 = {
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
"body": b""
|
||||
}
|
||||
|
||||
req2 = {
|
||||
@ -11,7 +11,7 @@ req2 = {
|
||||
"uri": uri("/second"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
"body": b""
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
|
||||
@ -3,5 +3,5 @@ request = {
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 0),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -3,5 +3,5 @@ request = {
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 0),
|
||||
"headers": [('CONTENT-LENGTH', '24')],
|
||||
"body": "GET /second HTTP/1.1\r\n\r\n"
|
||||
}
|
||||
"body": b"GET /second HTTP/1.1\r\n\r\n"
|
||||
}
|
||||
|
||||
@ -3,5 +3,5 @@ request = {
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 1),
|
||||
"headers": [("CONNECTION", "Close")],
|
||||
"body": ""
|
||||
}
|
||||
"body": b""
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ req1 = {
|
||||
"uri": uri("/first"),
|
||||
"version": (1, 0),
|
||||
"headers": [("CONNECTION", "Keep-Alive")],
|
||||
"body": ""
|
||||
"body": b""
|
||||
}
|
||||
|
||||
req2 = {
|
||||
@ -11,7 +11,7 @@ req2 = {
|
||||
"uri": uri("/second"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
"body": b""
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
request = [req1, req2]
|
||||
|
||||
@ -5,7 +5,7 @@ req1 = {
|
||||
"headers": [
|
||||
("TRANSFER-ENCODING", "chunked")
|
||||
],
|
||||
"body": "hello world"
|
||||
"body": b"hello world"
|
||||
}
|
||||
|
||||
req2 = {
|
||||
@ -13,7 +13,7 @@ req2 = {
|
||||
"uri": uri("/second"),
|
||||
"version": (1, 1),
|
||||
"headers": [],
|
||||
"body": ""
|
||||
"body": b""
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
request = [req1, req2]
|
||||
|
||||
@ -6,7 +6,7 @@ req1 = {
|
||||
("CONTENT-LENGTH", "-1"),
|
||||
("TRANSFER-ENCODING", "chunked")
|
||||
],
|
||||
"body": "hello world"
|
||||
"body": b"hello world"
|
||||
}
|
||||
|
||||
req2 = {
|
||||
@ -17,7 +17,7 @@ req2 = {
|
||||
("TRANSFER-ENCODING", "chunked"),
|
||||
("CONTENT-LENGTH", "-1"),
|
||||
],
|
||||
"body": "hello world"
|
||||
"body": b"hello world"
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
|
||||
@ -12,5 +12,5 @@ request = {
|
||||
("CONTENT-TYPE", "application/json"),
|
||||
("CONTENT-LENGTH", "14")
|
||||
],
|
||||
"body": '{"nom": "nom"}'
|
||||
"body": b'{"nom": "nom"}'
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ req1 = {
|
||||
("CONTENT-LENGTH", "14"),
|
||||
("CONNECTION", "keep-alive")
|
||||
],
|
||||
"body": '{"nom": "nom"}'
|
||||
"body": b'{"nom": "nom"}'
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ req2 = {
|
||||
"headers": [
|
||||
("TRANSFER-ENCODING", "chunked"),
|
||||
],
|
||||
"body": "all your base are belong to us"
|
||||
"body": b"all your base are belong to us"
|
||||
}
|
||||
|
||||
request = [req1, req2]
|
||||
|
||||
35
tests/t.py
35
tests/t.py
@ -1,43 +1,43 @@
|
||||
# -*- coding: utf-8 -
|
||||
# Copyright 2009 Paul J. Davis <paul.joseph.davis@gmail.com>
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import array
|
||||
import os
|
||||
from StringIO import StringIO
|
||||
import tempfile
|
||||
|
||||
dirname = os.path.dirname(__file__)
|
||||
|
||||
from gunicorn.http.parser import RequestParser
|
||||
from gunicorn.config import Config
|
||||
from gunicorn.six import BytesIO
|
||||
|
||||
def data_source(fname):
|
||||
buf = StringIO()
|
||||
buf = BytesIO()
|
||||
with open(fname) as handle:
|
||||
for line in handle:
|
||||
line = line.rstrip("\n").replace("\\r\\n", "\r\n")
|
||||
buf.write(line)
|
||||
buf.write(line.encode('latin1'))
|
||||
return buf
|
||||
|
||||
class request(object):
|
||||
def __init__(self, name):
|
||||
self.fname = os.path.join(dirname, "requests", name)
|
||||
|
||||
|
||||
def __call__(self, func):
|
||||
def run():
|
||||
src = data_source(self.fname)
|
||||
func(src, RequestParser(src))
|
||||
run.func_name = func.func_name
|
||||
return run
|
||||
|
||||
|
||||
|
||||
|
||||
class FakeSocket(object):
|
||||
|
||||
|
||||
def __init__(self, data):
|
||||
self.tmp = tempfile.TemporaryFile()
|
||||
if data:
|
||||
@ -47,32 +47,32 @@ class FakeSocket(object):
|
||||
|
||||
def fileno(self):
|
||||
return self.tmp.fileno()
|
||||
|
||||
|
||||
def len(self):
|
||||
return self.tmp.len
|
||||
|
||||
|
||||
def recv(self, length=None):
|
||||
return self.tmp.read()
|
||||
|
||||
|
||||
def recv_into(self, buf, length):
|
||||
tmp_buffer = self.tmp.read(length)
|
||||
v = len(tmp_buffer)
|
||||
for i, c in enumerate(tmp_buffer):
|
||||
buf[i] = c
|
||||
return v
|
||||
|
||||
|
||||
def send(self, data):
|
||||
self.tmp.write(data)
|
||||
self.tmp.flush()
|
||||
|
||||
|
||||
def seek(self, offset, whence=0):
|
||||
self.tmp.seek(offset, whence)
|
||||
|
||||
|
||||
|
||||
|
||||
class http_request(object):
|
||||
def __init__(self, name):
|
||||
self.fname = os.path.join(dirname, "requests", name)
|
||||
|
||||
|
||||
def __call__(self, func):
|
||||
def run():
|
||||
fsock = FakeSocket(data_source(self.fname))
|
||||
@ -80,7 +80,7 @@ class http_request(object):
|
||||
func(req)
|
||||
run.func_name = func.func_name
|
||||
return run
|
||||
|
||||
|
||||
def eq(a, b):
|
||||
assert a == b, "%r != %r" % (a, b)
|
||||
|
||||
@ -117,4 +117,3 @@ def raises(exctype, func, *args, **kwargs):
|
||||
func_name = getattr(func, "func_name", "<builtin_function>")
|
||||
raise AssertionError("Function %s did not raise %s" % (
|
||||
func_name, exctype.__name__))
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import t
|
||||
@ -9,6 +9,8 @@ import treq
|
||||
import glob
|
||||
import os
|
||||
dirname = os.path.dirname(__file__)
|
||||
|
||||
from py.test import skip
|
||||
reqdir = os.path.join(dirname, "requests", "valid")
|
||||
|
||||
def a_case(fname):
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import t
|
||||
@ -8,7 +8,8 @@ import treq
|
||||
|
||||
import glob
|
||||
import os
|
||||
from nose.tools import raises
|
||||
|
||||
import pytest
|
||||
|
||||
dirname = os.path.dirname(__file__)
|
||||
reqdir = os.path.join(dirname, "requests", "invalid")
|
||||
@ -17,12 +18,12 @@ reqdir = os.path.join(dirname, "requests", "invalid")
|
||||
def test_http_parser():
|
||||
for fname in glob.glob(os.path.join(reqdir, "*.http")):
|
||||
env = treq.load_py(os.path.splitext(fname)[0] + ".py")
|
||||
|
||||
expect = env['request']
|
||||
cfg = env['cfg']
|
||||
req = treq.badrequest(fname)
|
||||
|
||||
@raises(expect)
|
||||
def check(fname):
|
||||
return req.check(cfg)
|
||||
|
||||
yield check, fname # fname is pass so that we know which test failed
|
||||
with pytest.raises(expect):
|
||||
def f(fname):
|
||||
return req.check(cfg)
|
||||
f(fname)
|
||||
@ -1,12 +1,10 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
import t
|
||||
|
||||
import functools
|
||||
@ -23,14 +21,6 @@ def cfg_file():
|
||||
def paster_ini():
|
||||
return os.path.join(dirname, "..", "examples", "frameworks", "pylonstest", "nose.ini")
|
||||
|
||||
def PasterApp():
|
||||
try:
|
||||
from paste.deploy import loadapp, loadwsgi
|
||||
except ImportError:
|
||||
raise SkipTest()
|
||||
from gunicorn.app.pasterapp import PasterApplication
|
||||
return PasterApplication("no_usage")
|
||||
|
||||
class AltArgs(object):
|
||||
def __init__(self, args=None):
|
||||
self.args = args or []
|
||||
@ -38,17 +28,17 @@ class AltArgs(object):
|
||||
|
||||
def __enter__(self):
|
||||
sys.argv = self.args
|
||||
|
||||
|
||||
def __exit__(self, exc_type, exc_inst, traceback):
|
||||
sys.argv = self.orig
|
||||
|
||||
class NoConfigApp(Application):
|
||||
def __init__(self):
|
||||
super(NoConfigApp, self).__init__("no_usage")
|
||||
|
||||
|
||||
def init(self, parser, opts, args):
|
||||
pass
|
||||
|
||||
|
||||
def load(self):
|
||||
pass
|
||||
|
||||
@ -63,25 +53,25 @@ def test_property_access():
|
||||
c = config.Config()
|
||||
for s in config.KNOWN_SETTINGS:
|
||||
getattr(c, s.name)
|
||||
|
||||
|
||||
# Class was loaded
|
||||
t.eq(c.worker_class, SyncWorker)
|
||||
|
||||
|
||||
# Debug affects workers
|
||||
t.eq(c.workers, 1)
|
||||
c.set("workers", 3)
|
||||
t.eq(c.workers, 3)
|
||||
|
||||
|
||||
# Address is parsed
|
||||
t.eq(c.address, ("127.0.0.1", 8000))
|
||||
|
||||
|
||||
# User and group defaults
|
||||
t.eq(os.geteuid(), c.uid)
|
||||
t.eq(os.getegid(), c.gid)
|
||||
|
||||
|
||||
# Proc name
|
||||
t.eq("gunicorn", c.proc_name)
|
||||
|
||||
|
||||
# Not a config property
|
||||
t.raises(AttributeError, getattr, c, "foo")
|
||||
# Force to be not an error
|
||||
@ -93,10 +83,10 @@ def test_property_access():
|
||||
|
||||
# Attempt to set a cfg not via c.set
|
||||
t.raises(AttributeError, setattr, c, "proc_name", "baz")
|
||||
|
||||
|
||||
# No setting for name
|
||||
t.raises(AttributeError, c.set, "baz", "bar")
|
||||
|
||||
|
||||
def test_bool_validation():
|
||||
c = config.Config()
|
||||
t.eq(c.debug, False)
|
||||
@ -196,30 +186,9 @@ def test_load_config():
|
||||
t.eq(app.cfg.bind, "unix:/tmp/bar/baz")
|
||||
t.eq(app.cfg.workers, 3)
|
||||
t.eq(app.cfg.proc_name, "fooey")
|
||||
|
||||
|
||||
def test_cli_overrides_config():
|
||||
with AltArgs(["prog_name", "-c", cfg_file(), "-b", "blarney"]):
|
||||
app = NoConfigApp()
|
||||
t.eq(app.cfg.bind, "blarney")
|
||||
t.eq(app.cfg.proc_name, "fooey")
|
||||
|
||||
def test_paster_config():
|
||||
with AltArgs(["prog_name", paster_ini()]):
|
||||
app = PasterApp()
|
||||
t.eq(app.cfg.bind, "192.168.0.1:80")
|
||||
t.eq(app.cfg.proc_name, "brim")
|
||||
t.eq("ignore_me" in app.cfg.settings, False)
|
||||
|
||||
def test_cfg_over_paster():
|
||||
with AltArgs(["prog_name", "-c", cfg_file(), paster_ini()]):
|
||||
app = PasterApp()
|
||||
t.eq(app.cfg.bind, "unix:/tmp/bar/baz")
|
||||
t.eq(app.cfg.proc_name, "fooey")
|
||||
t.eq(app.cfg.default_proc_name, "blurgh")
|
||||
|
||||
def test_cli_cfg_paster():
|
||||
with AltArgs(["prog_name", "-c", cfg_file(), "-b", "whee", paster_ini()]):
|
||||
app = PasterApp()
|
||||
t.eq(app.cfg.bind, "whee")
|
||||
t.eq(app.cfg.proc_name, "fooey")
|
||||
t.eq(app.cfg.default_proc_name, "blurgh")
|
||||
61
tests/test_004-http-body.py
Normal file
61
tests/test_004-http-body.py
Normal file
@ -0,0 +1,61 @@
|
||||
import t
|
||||
from gunicorn.http.body import Body
|
||||
from gunicorn.six import BytesIO
|
||||
|
||||
|
||||
def assert_readline(payload, size, expected):
|
||||
body = Body(BytesIO(payload))
|
||||
t.eq(body.readline(size), expected)
|
||||
|
||||
|
||||
def test_readline_empty_body():
|
||||
assert_readline(b"", None, b"")
|
||||
assert_readline(b"", 1, b"")
|
||||
|
||||
|
||||
def test_readline_zero_size():
|
||||
assert_readline(b"abc", 0, b"")
|
||||
assert_readline(b"\n", 0, b"")
|
||||
|
||||
|
||||
def test_readline_new_line_before_size():
|
||||
body = Body(BytesIO(b"abc\ndef"))
|
||||
t.eq(body.readline(4), b"abc\n")
|
||||
t.eq(body.readline(), b"def")
|
||||
|
||||
|
||||
def test_readline_new_line_after_size():
|
||||
body = Body(BytesIO(b"abc\ndef"))
|
||||
t.eq(body.readline(2), b"ab")
|
||||
t.eq(body.readline(), b"c\n")
|
||||
|
||||
|
||||
def test_readline_no_new_line():
|
||||
body = Body(BytesIO(b"abcdef"))
|
||||
t.eq(body.readline(), b"abcdef")
|
||||
body = Body(BytesIO(b"abcdef"))
|
||||
t.eq(body.readline(2), b"ab")
|
||||
t.eq(body.readline(2), b"cd")
|
||||
t.eq(body.readline(2), b"ef")
|
||||
|
||||
|
||||
def test_readline_buffer_loaded():
|
||||
reader = BytesIO(b"abc\ndef")
|
||||
body = Body(reader)
|
||||
body.read(1) # load internal buffer
|
||||
reader.write(b"g\nhi")
|
||||
reader.seek(7)
|
||||
print(reader.getvalue())
|
||||
t.eq(body.readline(), b"bc\n")
|
||||
t.eq(body.readline(), b"defg\n")
|
||||
t.eq(body.readline(), b"hi")
|
||||
|
||||
|
||||
def test_readline_buffer_loaded_with_size():
|
||||
body = Body(BytesIO(b"abc\ndef"))
|
||||
body.read(1) # load internal buffer
|
||||
t.eq(body.readline(2), b"bc")
|
||||
t.eq(body.readline(2), b"\n")
|
||||
t.eq(body.readline(2), b"de")
|
||||
t.eq(body.readline(2), b"f")
|
||||
|
||||
13
tests/test_005-lazywriter-isatty.py
Normal file
13
tests/test_005-lazywriter-isatty.py
Normal file
@ -0,0 +1,13 @@
|
||||
import sys
|
||||
|
||||
from gunicorn.glogging import LazyWriter
|
||||
|
||||
|
||||
def test_lazywriter_isatty():
|
||||
orig = sys.stdout
|
||||
sys.stdout = LazyWriter('test.log')
|
||||
try:
|
||||
sys.stdout.isatty()
|
||||
except AttributeError:
|
||||
raise AssertionError("LazyWriter has no attribute 'isatty'")
|
||||
sys.stdout = orig
|
||||
@ -10,18 +10,19 @@ import t
|
||||
import inspect
|
||||
import os
|
||||
import random
|
||||
import urlparse
|
||||
|
||||
from gunicorn.config import Config
|
||||
from gunicorn.http.errors import ParseException
|
||||
from gunicorn.http.parser import RequestParser
|
||||
from gunicorn.six import urlparse, execfile_
|
||||
from gunicorn import six
|
||||
|
||||
dirname = os.path.dirname(__file__)
|
||||
random.seed()
|
||||
|
||||
def uri(data):
|
||||
ret = {"raw": data}
|
||||
parts = urlparse.urlparse(data)
|
||||
parts = urlparse(data)
|
||||
ret["scheme"] = parts.scheme or ''
|
||||
ret["host"] = parts.netloc.rsplit(":", 1)[0] or None
|
||||
ret["port"] = parts.port or 80
|
||||
@ -42,7 +43,7 @@ def load_py(fname):
|
||||
config = globals().copy()
|
||||
config["uri"] = uri
|
||||
config["cfg"] = Config()
|
||||
execfile(fname, config)
|
||||
execfile_(fname, config)
|
||||
return config
|
||||
|
||||
class request(object):
|
||||
@ -54,10 +55,10 @@ class request(object):
|
||||
if not isinstance(self.expect, list):
|
||||
self.expect = [self.expect]
|
||||
|
||||
with open(self.fname) as handle:
|
||||
with open(self.fname, 'rb') as handle:
|
||||
self.data = handle.read()
|
||||
self.data = self.data.replace("\n", "").replace("\\r\\n", "\r\n")
|
||||
self.data = self.data.replace("\\0", "\000")
|
||||
self.data = self.data.replace(b"\n", b"").replace(b"\\r\\n", b"\r\n")
|
||||
self.data = self.data.replace(b"\\0", b"\000")
|
||||
|
||||
# Functions for sending data to the parser.
|
||||
# These functions mock out reading from a
|
||||
@ -69,20 +70,20 @@ class request(object):
|
||||
|
||||
def send_lines(self):
|
||||
lines = self.data
|
||||
pos = lines.find("\r\n")
|
||||
pos = lines.find(b"\r\n")
|
||||
while pos > 0:
|
||||
yield lines[:pos+2]
|
||||
lines = lines[pos+2:]
|
||||
pos = lines.find("\r\n")
|
||||
pos = lines.find(b"\r\n")
|
||||
if len(lines):
|
||||
yield lines
|
||||
|
||||
def send_bytes(self):
|
||||
for d in self.data:
|
||||
yield d
|
||||
for d in str(self.data.decode("latin1")):
|
||||
yield bytes(d.encode("latin1"))
|
||||
|
||||
def send_random(self):
|
||||
maxs = len(self.data) / 10
|
||||
maxs = round(len(self.data) / 10)
|
||||
read = 0
|
||||
while read < len(self.data):
|
||||
chunk = random.randint(1, maxs)
|
||||
@ -143,7 +144,7 @@ class request(object):
|
||||
while len(body):
|
||||
if body[:len(data)] != data:
|
||||
raise AssertionError("Invalid data read: %r" % data)
|
||||
if '\n' in data[:-1]:
|
||||
if b'\n' in data[:-1]:
|
||||
raise AssertionError("Embedded new line: %r" % data)
|
||||
body = body[len(data):]
|
||||
data = self.szread(req.body.readline, sizes)
|
||||
@ -165,7 +166,7 @@ class request(object):
|
||||
"""
|
||||
data = req.body.readlines()
|
||||
for line in data:
|
||||
if '\n' in line[:-1]:
|
||||
if b'\n' in line[:-1]:
|
||||
raise AssertionError("Embedded new line: %r" % line)
|
||||
if line != body[:len(line)]:
|
||||
raise AssertionError("Invalid body data read: %r != %r" % (
|
||||
@ -182,7 +183,7 @@ class request(object):
|
||||
This skips sizes because there's its not part of the iter api.
|
||||
"""
|
||||
for line in req.body:
|
||||
if '\n' in line[:-1]:
|
||||
if b'\n' in line[:-1]:
|
||||
raise AssertionError("Embedded new line: %r" % line)
|
||||
if line != body[:len(line)]:
|
||||
raise AssertionError("Invalid body data read: %r != %r" % (
|
||||
@ -191,7 +192,7 @@ class request(object):
|
||||
if len(body):
|
||||
raise AssertionError("Failed to read entire body: %r" % body)
|
||||
try:
|
||||
data = iter(req.body).next()
|
||||
data = six.next(iter(req.body))
|
||||
raise AssertionError("Read data after body finished: %r" % data)
|
||||
except StopIteration:
|
||||
pass
|
||||
@ -214,9 +215,15 @@ class request(object):
|
||||
|
||||
ret = []
|
||||
for (mt, sz, sn) in cfgs:
|
||||
mtn = mt.func_name[6:]
|
||||
szn = sz.func_name[5:]
|
||||
snn = sn.func_name[5:]
|
||||
if hasattr(mt, 'funcname'):
|
||||
mtn = mt.func_name[6:]
|
||||
szn = sz.func_name[5:]
|
||||
snn = sn.func_name[5:]
|
||||
else:
|
||||
mtn = mt.__name__[6:]
|
||||
szn = sz.__name__[5:]
|
||||
snn = sn.__name__[5:]
|
||||
|
||||
def test_req(sn, sz, mt):
|
||||
self.check(cfg, sn, sz, mt)
|
||||
desc = "%s: MT: %s SZ: %s SN: %s" % (self.name, mtn, szn, snn)
|
||||
@ -251,9 +258,10 @@ class badrequest(object):
|
||||
self.data = handle.read()
|
||||
self.data = self.data.replace("\n", "").replace("\\r\\n", "\r\n")
|
||||
self.data = self.data.replace("\\0", "\000")
|
||||
self.data = self.data.encode('latin1')
|
||||
|
||||
def send(self):
|
||||
maxs = len(self.data) / 10
|
||||
maxs = round(len(self.data) / 10)
|
||||
read = 0
|
||||
while read < len(self.data):
|
||||
chunk = random.randint(1, maxs)
|
||||
@ -262,5 +270,4 @@ class badrequest(object):
|
||||
|
||||
def check(self, cfg):
|
||||
p = RequestParser(cfg, self.send())
|
||||
[req for req in p]
|
||||
|
||||
six.next(p)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user