Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
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.