summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Rivera <rivera@joel.mx>2015-10-24 17:23:55 -0500
committerJoel Rivera <rivera@joel.mx>2015-10-24 17:23:55 -0500
commitab9752ddad9b21d5d920dee54f307f0cc701400e (patch)
tree1b3d29dd3166d369701e8873ba189972c08e8f87
parentb253f9fbbf0d5827eb59758558b404b788a09155 (diff)
parentca23da570526c2ffb26a457145a30531a4daf2b3 (diff)
downloadcherrypy-ab9752ddad9b21d5d920dee54f307f0cc701400e.tar.gz
Merged in rajaramsrn/cherrypy (pull request #104)
Fix for constant SSL socket connection error (for python 2 only):
-rw-r--r--cherrypy/_cpcompat_subprocess.py4
-rw-r--r--cherrypy/_cpwsgi.py3
-rw-r--r--cherrypy/test/test_logging.py5
-rw-r--r--cherrypy/test/test_wsgi_unix_socket.py104
-rw-r--r--docs/basics.rst168
5 files changed, 195 insertions, 89 deletions
diff --git a/cherrypy/_cpcompat_subprocess.py b/cherrypy/_cpcompat_subprocess.py
index ce363725..517b8d44 100644
--- a/cherrypy/_cpcompat_subprocess.py
+++ b/cherrypy/_cpcompat_subprocess.py
@@ -883,7 +883,7 @@ class Popen(object):
startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = _subprocess.SW_HIDE
comspec = os.environ.get("COMSPEC", "cmd.exe")
- args = '{} /c "{}"'.format(comspec, args)
+ args = '{0} /c "{1}"'.format(comspec, args)
if (_subprocess.GetVersion() >= 0x80000000 or
os.path.basename(comspec).lower() == "command.com"):
# Win9x, or using command.com on NT. We need to
@@ -1029,7 +1029,7 @@ class Popen(object):
elif sig == signal.CTRL_BREAK_EVENT:
os.kill(self.pid, signal.CTRL_BREAK_EVENT)
else:
- raise ValueError("Unsupported signal: {}".format(sig))
+ raise ValueError("Unsupported signal: {0}".format(sig))
def terminate(self):
"""Terminates the process
diff --git a/cherrypy/_cpwsgi.py b/cherrypy/_cpwsgi.py
index f6db68b0..a8068fb0 100644
--- a/cherrypy/_cpwsgi.py
+++ b/cherrypy/_cpwsgi.py
@@ -296,7 +296,8 @@ class AppResponse(object):
"""Create a Request object using environ."""
env = self.environ.get
- local = httputil.Host('', int(env('SERVER_PORT', 80)),
+ local = httputil.Host('',
+ int(env('SERVER_PORT', 80) or -1),
env('SERVER_NAME', ''))
remote = httputil.Host(env('REMOTE_ADDR', ''),
int(env('REMOTE_PORT', -1) or -1),
diff --git a/cherrypy/test/test_logging.py b/cherrypy/test/test_logging.py
index 0c029ad7..429b8f2f 100644
--- a/cherrypy/test/test_logging.py
+++ b/cherrypy/test/test_logging.py
@@ -169,7 +169,8 @@ class ErrorLogTests(helper.CPWebCase, logtest.LogCase):
try:
self.getPage("/error")
self.assertInBody("raise ValueError()")
- self.assertLog(0, 'HTTP Traceback (most recent call last):')
- self.assertLog(-3, 'raise ValueError()')
+ self.assertLog(0, 'HTTP')
+ self.assertLog(1, 'Traceback (most recent call last):')
+ self.assertLog(-2, 'raise ValueError()')
finally:
ignore.pop()
diff --git a/cherrypy/test/test_wsgi_unix_socket.py b/cherrypy/test/test_wsgi_unix_socket.py
new file mode 100644
index 00000000..0e98a4c5
--- /dev/null
+++ b/cherrypy/test/test_wsgi_unix_socket.py
@@ -0,0 +1,104 @@
+import os
+import sys
+import socket
+import atexit
+
+import cherrypy
+from cherrypy.test import helper
+from cherrypy._cpcompat import HTTPConnection
+
+
+USOCKET_PATH = os.path.join(
+ os.path.dirname(os.path.realpath(__file__)),
+ 'cp_test.sock'
+)
+
+
+class USocketHTTPConnection(HTTPConnection):
+ """
+ HTTPConnection over a unix socket.
+ """
+
+ def __init__(self, path):
+ HTTPConnection.__init__(self, 'localhost')
+ self.path = path
+
+ def __call__(self, *args, **kwargs):
+ """
+ Catch-all method just to present itself as a constructor for the
+ HTTPConnection.
+ """
+ return self
+
+ def connect(self):
+ """
+ Override the connect method and assign a unix socket as a transport.
+ """
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(self.path)
+ self.sock = sock
+ atexit.register(lambda: os.remove(self.path))
+
+
+def skip_on_windows(method):
+ """
+ Decorator to skip the method call if the test is executing on Windows.
+ """
+ def wrapper(self):
+ if sys.platform == "win32":
+ return self.skip("No UNIX Socket support in Windows.")
+ else:
+ return method(self)
+ wrapper.__doc__ = method.__doc__
+ wrapper.__name__ = method.__name__
+ return wrapper
+
+
+
+class WSGI_UnixSocket_Test(helper.CPWebCase):
+ """
+ Test basic behavior on a cherrypy wsgi server listening
+ on a unix socket.
+
+ It exercises the config option `server.socket_file`.
+ """
+ HTTP_CONN = USocketHTTPConnection(USOCKET_PATH)
+
+
+ @staticmethod
+ def setup_server():
+ class Root(object):
+
+ @cherrypy.expose
+ def index(self):
+ return "Test OK"
+
+ @cherrypy.expose
+ def error(self):
+ raise Exception("Invalid page")
+
+ config = {
+ 'server.socket_file': USOCKET_PATH
+ }
+ cherrypy.config.update(config)
+ cherrypy.tree.mount(Root())
+
+ def tearDown(self):
+ cherrypy.config.update({'server.socket_file': None})
+
+ @skip_on_windows
+ def test_simple_request(self):
+ self.getPage("/")
+ self.assertStatus("200 OK")
+ self.assertInBody("Test OK")
+
+ @skip_on_windows
+ def test_not_found(self):
+ self.getPage("/invalid_path")
+ self.assertStatus("404 Not Found")
+
+ @skip_on_windows
+ def test_internal_error(self):
+ self.getPage("/error")
+ self.assertStatus("500 Internal Server Error")
+ self.assertInBody("Invalid page")
diff --git a/docs/basics.rst b/docs/basics.rst
index 9e266bae..b1896f22 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -12,14 +12,14 @@ a CherryPy application, introducing some essential concepts.
The one-minute application example
##################################
-The most basic application you can write with CherryPy
+The most basic application you can write with CherryPy
involves almost all its core concepts.
.. code-block:: python
:linenos:
import cherrypy
-
+
class Root(object):
@cherrypy.expose
def index(self):
@@ -34,16 +34,16 @@ a single import statement as demonstrated in line 1.
Before discussing the meat, let's jump to line 9 which shows,
how to host your application with the CherryPy application server
-and serve it with its builtin HTTP server at the `'/'` path.
+and serve it with its builtin HTTP server at the `'/'` path.
All in one single line. Not bad.
Let's now step back to the actual application. Even though CherryPy
-does not mandate it, most of the time your applications
+does not mandate it, most of the time your applications
will be written as Python classes. Methods of those classes will
be called by CherryPy to respond to client requests. However,
CherryPy needs to be aware that a method can be used that way, we
say the method needs to be :term:`exposed`. This is precisely
-what the :func:`cherrypy.expose()` decorator does in line 4.
+what the :func:`cherrypy.expose()` decorator does in line 4.
Save the snippet in a file named `myapp.py` and run your first
CherryPy application:
@@ -57,14 +57,14 @@ Then point your browser at http://127.0.0.1:8080. Tada!
.. note::
- CherryPy is a small framework that focuses on one single task:
+ CherryPy is a small framework that focuses on one single task:
take a HTTP request and locate the most appropriate
- Python function or method that match the request's URL.
- Unlike other well-known frameworks, CherryPy does not
+ Python function or method that match the request's URL.
+ Unlike other well-known frameworks, CherryPy does not
provide a built-in support for database access, HTML
- templating or any other middleware nifty features.
+ templating or any other middleware nifty features.
- In a nutshell, once CherryPy has found and called an
+ In a nutshell, once CherryPy has found and called an
:term:`exposed` method, it is up to you, as a developer, to
provide the tools to implement your application's logic.
@@ -75,7 +75,7 @@ Then point your browser at http://127.0.0.1:8080. Tada!
The previous example demonstrated the simplicty of the
CherryPy interface but, your application will likely
contain a few other bits and pieces: static service,
- more complex structure, database access, etc.
+ more complex structure, database access, etc.
This will be developed in the tutorial section.
@@ -94,7 +94,7 @@ Single application
^^^^^^^^^^^^^^^^^^
The most straightforward way is to use :func:`cherrypy.quickstart`
-function. It takes at least one argument, the instance of the
+function. It takes at least one argument, the instance of the
application to host. Two other settings are optionals. First, the
base path at which the application will be accessible from. Second,
a config dictionary or file to configure your application.
@@ -112,8 +112,8 @@ the last one provides specific settings for the application.
.. note::
- Notice in the third case how the settings are still
- relative to the application, not where it is made available at,
+ Notice in the third case how the settings are still
+ relative to the application, not where it is made available at,
hence the `{'/': ... }` rather than a `{'/blog': ... }`
@@ -122,25 +122,25 @@ Multiple applications
The :func:`cherrypy.quickstart` approach is fine for a single application,
but lacks the capacity to host several applications with the server.
-To achieve this, one must use the :meth:`cherrypy.tree.mount <cherrypy._cptree.Tree.mount>`
+To achieve this, one must use the :meth:`cherrypy.tree.mount <cherrypy._cptree.Tree.mount>`
function as follows:
.. code-block:: python
cherrypy.tree.mount(Blog(), '/blog', blog_conf)
cherrypy.tree.mount(Forum(), '/forum', forum_conf)
-
+
cherrypy.engine.start()
cherrypy.engine.block()
-Essentially, :meth:`cherrypy.tree.mount <cherrypy._cptree.Tree.mount>`
-takes the same parameters as :func:`cherrypy.quickstart`: an :term:`application`,
-a hosting path segment and a configuration. The last two lines
+Essentially, :meth:`cherrypy.tree.mount <cherrypy._cptree.Tree.mount>`
+takes the same parameters as :func:`cherrypy.quickstart`: an :term:`application`,
+a hosting path segment and a configuration. The last two lines
are simply starting application server.
.. important::
- :func:`cherrypy.quickstart` and :meth:`cherrypy.tree.mount <cherrypy._cptree.Tree.mount>`
+ :func:`cherrypy.quickstart` and :meth:`cherrypy.tree.mount <cherrypy._cptree.Tree.mount>`
are not exclusive. For instance, the previous lines can be written as:
.. code-block:: python
@@ -149,7 +149,7 @@ are simply starting application server.
cherrypy.quickstart(Forum(), '/forum', forum_conf)
.. note::
-
+
You can also :ref:`host foreign WSGI application <hostwsgiapp>`.
@@ -161,11 +161,11 @@ log all incoming requests as well as protocol errors.
To do so, CherryPy manages two loggers:
-- an access one that logs every incoming requests
+- an access one that logs every incoming requests
- an application/error log that traces errors or other application-level messages
Your application may leverage that second logger by calling
-:func:`cherrypy.log() <cherrypy._cplogging.LogManager.error>`.
+:func:`cherrypy.log() <cherrypy._cplogging.LogManager.error>`.
.. code-block:: python
@@ -183,7 +183,7 @@ You can also log an exception:
Both logs are writing to files identified by the following keys
in your configuration:
-- ``log.access_file`` for incoming requests using the
+- ``log.access_file`` for incoming requests using the
`common log format <http://en.wikipedia.org/wiki/Common_Log_Format>`_
- ``log.error_file`` for the other log
@@ -197,8 +197,8 @@ Disable logging
You may be interested in disabling either logs.
-To disable file logging, simply set a en empty string to the
-``log.access_file`` or ``log.error_file`` keys in your
+To disable file logging, simply set a en empty string to the
+``log.access_file`` or ``log.error_file`` keys in your
:ref:`global configuration <globalsettings>`.
To disable, console logging, set ``log.screen`` to `False`.
@@ -218,7 +218,7 @@ module to trace application level messages. Below is a simple
example on setting it up.
.. code-block:: python
-
+
import logging
import logging.config
@@ -228,7 +228,7 @@ example on setting it up.
db_logger = logging.getLogger('db')
LOG_CONF = {
- 'version': 1,
+ 'version': 1,
'formatters': {
'void': {
@@ -240,55 +240,55 @@ example on setting it up.
},
'handlers': {
'default': {
- 'level':'INFO',
+ 'level':'INFO',
'class':'logging.StreamHandler',
'formatter': 'standard',
'stream': 'ext://sys.stdout'
- },
+ },
'cherrypy_console': {
- 'level':'INFO',
+ 'level':'INFO',
'class':'logging.StreamHandler',
'formatter': 'void',
'stream': 'ext://sys.stdout'
- },
+ },
'cherrypy_access': {
- 'level':'INFO',
+ 'level':'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'void',
'filename': 'access.log',
'maxBytes': 10485760,
'backupCount': 20,
'encoding': 'utf8'
- },
+ },
'cherrypy_error': {
- 'level':'INFO',
+ 'level':'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'void',
'filename': 'errors.log',
'maxBytes': 10485760,
'backupCount': 20,
'encoding': 'utf8'
- },
+ },
},
'loggers': {
- '': {
- 'handlers': ['default'],
+ '': {
+ 'handlers': ['default'],
'level': 'INFO'
},
- 'db': {
- 'handlers': ['default'],
- 'level': 'INFO' ,
- 'propagate': False
+ 'db': {
+ 'handlers': ['default'],
+ 'level': 'INFO' ,
+ 'propagate': False
},
- 'cherrypy.access': {
- 'handlers': ['cherrypy_access'],
- 'level': 'INFO',
- 'propagate': False
+ 'cherrypy.access': {
+ 'handlers': ['cherrypy_access'],
+ 'level': 'INFO',
+ 'propagate': False
},
- 'cherrypy.error': {
- 'handlers': ['cherrypy_console', 'cherrypy_error'],
- 'level': 'INFO',
- 'propagate': False
+ 'cherrypy.error': {
+ 'handlers': ['cherrypy_console', 'cherrypy_error'],
+ 'level': 'INFO',
+ 'propagate': False
},
}
}
@@ -311,7 +311,7 @@ example on setting it up.
logging.config.dictConfig(LOG_CONF)
cherrypy.quickstart(Root())
-
+
In this snippet, we create a `configuration dictionary <https://docs.python.org/2/library/logging.config.html#logging.config.dictConfig>`_
that we pass on to the ``logging`` module to configure
our loggers:
@@ -329,20 +329,20 @@ We also prevent CherryPy from trying to open its log files when
the autoreloader kicks in. This is not strictly required since we do not
even let CherryPy open them in the first place. But, this avoids
wasting time on something useless.
-
+
.. _config:
Configuring
###########
-CherryPy comes with a fine-grained configuration mechanism and
+CherryPy comes with a fine-grained configuration mechanism and
settings can be set at various levels.
.. seealso::
Once you have the reviewed the basics, please refer
- to the :ref:`in-depth discussion <configindepth>`
+ to the :ref:`in-depth discussion <configindepth>`
around configuration.
.. _globalsettings:
@@ -350,15 +350,15 @@ settings can be set at various levels.
Global server configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^
-To configure the HTTP and application servers,
-use the :meth:`cherrypy.config.update() <cherrypy._cpconfig.Config.update>`
+To configure the HTTP and application servers,
+use the :meth:`cherrypy.config.update() <cherrypy._cpconfig.Config.update>`
method.
.. code-block:: python
cherrypy.config.update({'server.socket_port': 9090})
-The :mod:`cherrypy.config <cherrypy._cpconfig>` object is a dictionary and the
+The :mod:`cherrypy.config <cherrypy._cpconfig>` object is a dictionary and the
update method merges the passed dictionary into it.
You can also pass a file instead (assuming a `server.conf`
@@ -375,8 +375,8 @@ file):
.. warning::
- :meth:`cherrypy.config.update() <cherrypy._cpconfig.Config.update>`
- is not meant to be used to configure the application.
+ :meth:`cherrypy.config.update() <cherrypy._cpconfig.Config.update>`
+ is not meant to be used to configure the application.
It is a common mistake. It is used to configure the server and engine.
.. _perappconf:
@@ -384,7 +384,7 @@ file):
Per-application configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-To configure your application, pass in a dictionary or a file
+To configure your application, pass in a dictionary or a file
when you associate your application to the server.
.. code-block:: python
@@ -401,7 +401,7 @@ or via a file (called `app.conf` for instance):
.. code-block:: python
cherrypy.quickstart(myapp, '/', "app.conf")
-
+
Although, you can define most of your configuration in a global
fashion, it is sometimes convenient to define them
where they are applied in the code.
@@ -434,14 +434,14 @@ You can add settings that are not specific to a request URL
and retrieve them from your page handler as follows:
.. code-block:: ini
-
+
[/]
tools.gzip.on: True
[googleapi]
key = "..."
appid = "..."
-
+
.. code-block:: python
class Root(object):
@@ -512,7 +512,7 @@ Extended example:
Using sessions
##############
-Sessions are one of the most common mechanism used by developers to
+Sessions are one of the most common mechanism used by developers to
identify users and synchronize their activity. By default, CherryPy
does not activate sessions because it is not a mandatory feature
to have, to enable it simply add the following settings in your
@@ -526,7 +526,7 @@ configuration:
.. code-block:: python
cherrypy.quickstart(myapp, '/', "app.conf")
-
+
Sessions are, by default, stored in RAM so, if you restart your server
all of your current sessions will be lost. You can store them in memcached
or on the filesystem instead.
@@ -536,7 +536,7 @@ Using sessions in your applications is done as follows:
.. code-block:: python
import cherrypy
-
+
@cherrypy.expose
def index(self):
if 'count' not in cherrypy.session:
@@ -562,7 +562,7 @@ Filesystem backend
Using a filesystem is a simple to not lose your sessions
between reboots. Each session is saved in its own file within
-the given directory.
+the given directory.
.. code-block:: ini
@@ -574,7 +574,7 @@ the given directory.
Memcached backend
^^^^^^^^^^^^^^^^^
-`Memcached <http://memcached.org/>`_ is a popular key-store on top of your RAM,
+`Memcached <http://memcached.org/>`_ is a popular key-store on top of your RAM,
it is distributed and a good choice if you want to
share sessions outside of the process running CherryPy.
@@ -589,8 +589,8 @@ share sessions outside of the process running CherryPy.
Static content serving
######################
-CherryPy can serve your static content such as images, javascript and
-CSS resources, etc.
+CherryPy can serve your static content such as images, javascript and
+CSS resources, etc.
.. note::
@@ -599,7 +599,7 @@ CSS resources, etc.
is not valid, you can simply set more media-types as follows:
.. code-block:: python
-
+
import mimetypes
mimetypes.types_map['.csv'] = 'text/csv'
@@ -615,7 +615,7 @@ You can serve a single file as follows:
tools.staticfile.on = True
tools.staticfile.filename = "/home/site/style.css"
-CherryPy will automatically respond to URLs such as
+CherryPy will automatically respond to URLs such as
`http://hostname/style.css`.
Serving a whole directory
@@ -629,8 +629,8 @@ Serving a whole directory is similar to a single file:
tools.staticdir.on = True
tools.staticdir.dir = "/home/site/static"
-Assuming you have a file at `static/js/my.js`,
-CherryPy will automatically respond to URLs such as
+Assuming you have a file at `static/js/my.js`,
+CherryPy will automatically respond to URLs such as
`http://hostname/static/js/my.js`.
@@ -638,10 +638,10 @@ CherryPy will automatically respond to URLs such as
CherryPy always requires the absolute path to the files or directories
it will serve. If you have several static sections to configure
- but located in the same root directory, you can use the following
+ but located in the same root directory, you can use the following
shortcut:
-
+
.. code-block:: ini
[/]
@@ -665,8 +665,8 @@ To specify an index file, you can use the following:
tools.staticdir.dir = "/home/site/static"
tools.staticdir.index = "index.html"
-Assuming you have a file at `static/index.html`,
-CherryPy will automatically respond to URLs such as
+Assuming you have a file at `static/index.html`,
+CherryPy will automatically respond to URLs such as
`http://hostname/static/` by returning its contents.
@@ -687,7 +687,7 @@ You could for instance write a page handler as follows:
def download(self, filepath):
return serve_file(filepath, "application/x-download", "attachment")
-Assuming the filepath is a valid path on your machine, the
+Assuming the filepath is a valid path on your machine, the
response would be considered as a downloadable content by
the browser.
@@ -759,7 +759,7 @@ SSL or within a closed network.
USERS = {'jon': 'secret'}
- def validate_password(username, password):
+ def validate_password(realm, username, password):
if username in USERS and USERS[username] == password:
return True
return False
@@ -769,13 +769,13 @@ SSL or within a closed network.
'tools.auth_basic.on': True,
'tools.auth_basic.realm': 'localhost',
'tools.auth_basic.checkpassword': validate_password
- }
+ }
}
cherrypy.quickstart(myapp, '/', conf)
Simply put, you have to provide a function that will
-be called by CherryPy passing the username and password
+be called by CherryPy passing the username and password
decoded from the request.
The function can read its data from any source it has to: a file,
@@ -789,7 +789,7 @@ Digest authentication differs by the fact the credentials
are not carried on by the request so it's a little more secure
than basic.
-CherryPy's digest support has a similar interface to the
+CherryPy's digest support has a similar interface to the
basic one explained above.
.. code-block:: python
@@ -812,7 +812,7 @@ basic one explained above.
Favicon
#######
-CherryPy serves its own sweet red cherrypy as the default
+CherryPy serves its own sweet red cherrypy as the default
`favicon <http://en.wikipedia.org/wiki/Favicon>`_ using the static file
tool. You can serve your own favicon as follows: