Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

User and password for basic authentication now Latin1. Also FieldStorage

is all str now. Added a couple of Unicode notes to the docs.
  • Loading branch information...
commit 1d8a8fd657c49cfd2144818360c8b6ce213637de 1 parent 88e43f0
@grisha authored
View
36 Doc/installation.rst
@@ -9,7 +9,7 @@ Installation
By far the best place to get help with installation and other issues
is the mod_python mailing list. Please take a moment to join the
mod_python mailing list by sending an e-mail with the word
- "subscribe" in the subject to mod_python-request@modpython.org or visit the
+ "subscribe" in the subject to mod_python-request@modpython.org or visit the
`mod_python mailing list page <http://mailman.modpython.org/mailman/listinfo/mod_python>`_
@@ -22,7 +22,7 @@ In the ideal case your Operating System provides a pre-packaged
version of mod_python. If not, you will need to compile it
yourself. This version of mod_python requires:
-* Python 2.6 or 2.7. (earlier versions might work too).
+* Python 2 (2.6 and up) or Python 3 (3.3 and up).
* Apache 2.2 or later. Apache 2.4 is highly recommended over 2.2.
In order to compile mod_python you will need to have the include files
@@ -60,7 +60,7 @@ standard autoconf stuff, :file:`./configure` does the following:
You can manually specify the location of apxs by using the
:option:`with-apxs` option, e.g.::
- $ ./configure --with-apxs=/usr/local/apache/bin/apxs
+ $ ./configure --with-apxs=/usr/local/apache/bin/apxs
It is recommended that you specify this option.
@@ -97,7 +97,7 @@ standard autoconf stuff, :file:`./configure` does the following:
$ ./configure --with-mutex-dir=/var/run/mod_python
- The mutex directory can also be specified at run time using
+ The mutex directory can also be specified at run time using
:ref:`dir-other-po` ``mod_python.mutex_directory``.
See :ref:`inst-apacheconfig`.
@@ -115,14 +115,14 @@ standard autoconf stuff, :file:`./configure` does the following:
The mutexes used for locking are a limited resource on some
systems. Increasing the maximum number of locks may increase performance
- when using session locking. The default is 8. A reasonable number for
+ when using session locking. The default is 8. A reasonable number for
higher performance would be 32.
Use :option:`with-max-locks` option, e.g::
$ ./configure --with-max-locks=32
The number of locks can also be specified at run time using
- :ref:`dir-other-po` ``mod_python.mutex_locks``.
+ :ref:`dir-other-po` ``mod_python.mutex_locks``.
See :ref:`inst-apacheconfig`.
*New in version 3.2.0*
@@ -131,20 +131,20 @@ standard autoconf stuff, :file:`./configure` does the following:
single: flex
pair: ./configure; --with-flex
-* Attempts to locate :program:`flex` and determine its version.
+* Attempts to locate :program:`flex` and determine its version.
If :program:`flex` cannot be found in your :envvar:`PATH` :program:`configure`
will fail. If the wrong version is found :program:`configure` will generate a warning.
You can generally ignore this warning unless you need to re-create
:file:`src/psp_parser.c`.
-
+
The parser used by psp (See :ref:`pyapi-psp`) is written in C
generated using :program:`flex`. (This requires a reentrant version
of :program:`flex`, 2.5.31 or later).
-
+
If the first flex binary in the path is not suitable or not the one desired
you can specify an alternative location with the option:with-flex:
option, e.g::
-
+
$ ./configure --with-flex=/usr/local/bin/flex
*New in version 3.2.0*
@@ -227,10 +227,10 @@ Testing
#. Add the following configuration directives to the main server config file::
- <Directory /some/directory/htdocs/test>
+ <Directory /some/directory/htdocs/test>
AddHandler mod_python .py
- PythonHandler mptest
- PythonDebug On
+ PythonHandler mptest
+ PythonDebug On
</Directory>
(Substitute ``/some/directory`` above for something applicable to
@@ -262,7 +262,7 @@ Testing
def handler(req):
req.content_type = 'text/plain'
req.write("Hello World!")
- return apache.OK
+ return apache.OK
#. Point your browser to the URL referring to the :file:`mptest.py`;
you should see ``'Hello World!'``. If you didn't - refer to the
@@ -284,11 +284,11 @@ Testing
Troubleshooting
===============
-There are a few things you can try to identify the problem:
+There are a few things you can try to identify the problem:
-* Carefully study the error output, if any.
+* Carefully study the error output, if any.
-* Check the server error log file, it may contain useful clues.
+* Check the server error log file, it may contain useful clues.
* Try running Apache from the command line in single process mode::
@@ -309,7 +309,7 @@ There are a few things you can try to identify the problem:
(e.g. :file:`http://localhost/mpinfo`) and note down the information given.
This will help you reporting your problem to the mod_python list.
-* Ask on the `mod_python list <http://mailman.modpython.org/mailman/listinfo/mod_python>`_.
+* Ask on the `mod_python list <http://mailman.modpython.org/mailman/listinfo/mod_python>`_.
Please make sure to provide specifics such as:
* mod_python version.
View
10 Doc/pythonapi.rst
@@ -706,6 +706,7 @@ Request Methods
Returns a string containing the password when Basic authentication is
used.
+ On Python 3 the string will be decoded to Unicode using Latin1.
.. method:: request.get_config()
@@ -861,6 +862,7 @@ Request Methods
more data than available, which will make the function block until
a ``Timeout`` is reached.
+ On Python 3 the output is always bytes.
.. method:: request.readline([len])
@@ -1004,7 +1006,8 @@ Request Methods
.. method:: request.write(string[, flush=1])
Writes *string* directly to the client, then flushes the buffer,
- unless flush is 0.
+ unless flush is 0. Unicode strings are encoded using ``utf-8``
+ encoding.
.. method:: request.flush()
@@ -1304,6 +1307,11 @@ Request Members
If an authentication check is made, this will hold the user
name. Same as CGI :envvar:`REMOTE_USER`.
+ On Python 3 the string is decoded using Latin1. (Different browsers
+ use different encodings for non-Latin1 characters for the basic
+ authentication string making a solution that fits all impossible,
+ you can always decode the header manually.)
+
.. note::
:meth:`request.get_basic_auth_pw` must be called prior to using this value.
View
14 lib/python/mod_python/util.py
@@ -361,9 +361,11 @@ def __init__(self, req, keep_blank_values=0, strict_parsing=0, file_callback=Non
# create a file object
# is this a file?
+ filename = None
if b"filename" in disp_options:
+ filename = disp_options[b"filename"]
if file_callback and isinstance(file_callback, collections.Callable):
- file = file_callback(disp_options[b"filename"])
+ file = file_callback(filename)
else:
file = tempfile.TemporaryFile("w+b")
else:
@@ -377,16 +379,16 @@ def __init__(self, req, keep_blank_values=0, strict_parsing=0, file_callback=Non
file.seek(0)
# make a Field
- if b"filename" in disp_options:
+ if filename:
field = Field(name)
- field.filename = disp_options[b"filename"]
+ field.filename = filename
else:
field = StringField(file.read())
field.name = name
field.file = file
- field.type = ctype
+ field.type = PY2 and ctype or ctype.decode('latin1')
field.type_options = type_options
- field.disposition = disp
+ field.disposition = PY2 and disp or disp.decode('latin1')
field.disposition_options = disp_options
field.headers = headers
self.list.append(field)
@@ -546,7 +548,7 @@ def parse_header(line):
value = p[i+1:].strip()
if len(value) >= 2 and value[:1] == value[-1:] == b'"':
value = value[1:-1]
- pdict[name] = value
+ pdict[name] = PY2 and value or value.decode('latin1')
return key, pdict
def apply_fs_data(object, fs, **args):
View
19 src/requestobject.c
@@ -815,12 +815,17 @@ static PyObject * req_get_basic_auth_pw(requestobject *self, PyObject *args)
request_rec *req;
/* http://stackoverflow.com/questions/702629/utf-8-characters-mangled-in-http-basic-auth-username/703341#703341 */
+ /* Latin1 is Safari, Chrome and Mozilla - otherwise it can be decoded manually */
req = self->request_rec;
- if (! ap_get_basic_auth_pw(req, &pw))
+ if (! ap_get_basic_auth_pw(req, &pw)) {
+#if PY_MAJOR_VERSION < 3
return PyBytes_FromString(pw);
- else {
+#else
+ return PyUnicode_DecodeLatin1(pw, strlen(pw), NULL);
+#endif
+ } else {
Py_INCREF(Py_None);
return Py_None;
}
@@ -1856,10 +1861,14 @@ static PyObject *getreq_recmbr(requestobject *self, void *name)
}
}
else if (strcmp(name, "user") == 0) {
- /* http://stackoverflow.com/questions/702629/utf-8-characters-mangled-in-http-basic-auth-username/703341#703341 */
- if (self->request_rec->user)
+ /* Use Latin1, see req_get_basic_auth_pw() comment */
+ if (self->request_rec->user) {
+#if PY_MAJOR_VERSION < 3
return PyBytes_FromString(self->request_rec->user);
- else {
+#else
+ return PyUnicode_DecodeLatin1(self->request_rec->user, strlen(self->request_rec->user), NULL);
+#endif
+ } else {
Py_INCREF(Py_None);
return Py_None;
}
View
18 test/htdocs/tests.py
@@ -708,18 +708,22 @@ def req_allow_methods(req):
def req_get_basic_auth_pw(req):
+ LATIN1_SPAM = 'sp\xe1m'
+ LATIN1_EGGS = '\xe9ggs'
+
pw = req.get_basic_auth_pw()
- if req.user != b"spam" or pw != b"eggs":
- req.write("test failed, user %s, pw %s" % (repr(req.user), repr(pw)))
- else:
+ if (req.user == "spam" and pw == "eggs" or
+ req.user == LATIN1_SPAM and pw == LATIN1_EGGS):
req.write("test ok")
+ else:
+ req.write("test failed, user %s, pw %s" % (repr(req.user), repr(pw)))
return apache.OK
def req_unauthorized(req):
pw = req.get_basic_auth_pw()
- if req.user == b"spam" and pw == b"eggs":
+ if req.user == "spam" and pw == "eggs":
req.write("test ok")
return apache.OK
@@ -955,8 +959,8 @@ def util_redirect(req):
def req_server_get_config(req):
- if req.server.get_config().get(b"PythonDebug", "0") != "1" or \
- req.get_config().get(b"PythonDebug", "0") != "0":
+ if req.server.get_config().get("PythonDebug", "0") != "1" or \
+ req.get_config().get("PythonDebug", "0") != "0":
req.write('test failed')
else:
req.write('test ok')
@@ -993,7 +997,7 @@ def fileupload(req):
from mod_python import util
fields = util.FieldStorage(req)
- f = fields.getfirst(b'testfile') # ZZZ these shouldn't be bytes?
+ f = fields.getfirst('testfile')
if PY2:
import md5
View
24 test/test.py
@@ -835,6 +835,29 @@ def test_req_get_basic_auth_pw(self):
if (rsp != b"test ok"):
self.fail(repr(rsp))
+ def test_req_get_basic_auth_pw_latin1_conf(self):
+ return self.test_req_get_basic_auth_pw_conf()
+
+ def test_req_get_basic_auth_pw_latin1(self):
+
+ print("\n * Testing req.get_basic_auth_pw_latin1()")
+
+ conn = http_connection("127.0.0.1:%s" % PORT)
+ conn.putrequest("GET", "/tests.py", skip_host=1)
+ conn.putheader("Host", "%s:%s" % ("test_req_get_basic_auth_pw", PORT))
+ auth = base64.encodestring(b'sp\xe1m:\xe9ggs').strip()
+ if PY2:
+ conn.putheader("Authorization", "Basic %s" % auth)
+ else:
+ conn.putheader("Authorization", "Basic %s" % auth.decode("latin1"))
+ conn.endheaders()
+ response = conn.getresponse()
+ rsp = response.read()
+ conn.close()
+
+ if (rsp != b"test ok"):
+ self.fail(repr(rsp))
+
def test_req_auth_type_conf(self):
c = VirtualHost("*",
@@ -2929,6 +2952,7 @@ def testPerRequestTests(self):
perRequestSuite.addTest(PerRequestTestCase("test_req_allow_methods"))
perRequestSuite.addTest(PerRequestTestCase("test_req_unauthorized"))
perRequestSuite.addTest(PerRequestTestCase("test_req_get_basic_auth_pw"))
+ perRequestSuite.addTest(PerRequestTestCase("test_req_get_basic_auth_pw_latin1"))
perRequestSuite.addTest(PerRequestTestCase("test_req_auth_type"))
if APACHE_VERSION != '2.4':
perRequestSuite.addTest(PerRequestTestCase("test_req_requires"))
Please sign in to comment.
Something went wrong with that request. Please try again.