Hello community, here is the log from the commit of package python-Werkzeug for openSUSE:Factory checked in at 2016-09-27 13:44:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Werkzeug (Old) and /work/SRC/openSUSE:Factory/.python-Werkzeug.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Werkzeug" Changes: -------- New Changes file: --- /dev/null 2016-09-15 12:42:18.240042505 +0200 +++ /work/SRC/openSUSE:Factory/.python-Werkzeug.new/python-Werkzeug-doc.changes 2016-09-27 13:44:41.000000000 +0200 @@ -0,0 +1,53 @@ +------------------------------------------------------------------- +Thu Sep 15 23:08:05 UTC 2016 - [email protected] + +- update to version 0.11.11: + * Fix JSONRequestMixin for Python3. See #731 + * Fix broken string handling in test client when passing + integers. See #852 + * Fix a bug in "parse_options_header" where an invalid content type + starting with comma or semi-colon would result in an invalid + return value, see issue "#995". + * Fix a bug in multidicts when passing empty lists as values, see + issue "#979". + * Fix a security issue that allows XSS on the Werkzeug debugger. See + "#1001". +- update to version 0.11.10: + * Fixed a bug that occurs when running on Python 2.6 and using a + broken locale. See pull request #912. + * Fixed a crash when running the debugger on Google App Engine. See + issue #925. + * Fixed an issue with multipart parsing that could cause memory + exhaustion. +- Update to 0.11.9 + - Corrected an issue that caused the debugger not to use the + machine GUID on POSIX systems. + - Corrected an Unicode error on Python 3 for the debugger's + PIN usage. + - Corrected the timestamp verification in the pin debug code. + Without this fix the pin was remebered until too long. +- update to version 0.11.8: + * fixed a problem with the machine GUID detection code on OS X on + Python 3. +- changes from version 0.11.7: + * fixed a regression on Python 3 for the debugger. +- changes from version 0.11.6: + * werkzeug.serving: Still show the client address on bad requests. + * improved the PIN based protection for the debugger to make it + harder to brute force via trying cookies. Please keep in mind + that the debugger *is not intended for running on production + environments* + * increased the pin timeout to a week to make it less annoying for + people which should decrease the change that users disable the pin + check entirely. + * werkzeug.serving: Fix broken HTTP_HOST when path starts with + double slash. +- update to version 0.11.5: + * werkzeug.serving: Fix crash when attempting SSL connection to HTTP + server. +- update to version 0.11.4: + * Fixed werkzeug.serving not working from -m flag. + * Fixed incorrect weak etag handling. +- Rebase 0001_create_a_thread_to_reap_death_process.patch +- Split documentation into own subpackage to speed up build. + --- /work/SRC/openSUSE:Factory/python-Werkzeug/python-Werkzeug.changes 2016-02-11 12:37:49.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python-Werkzeug.new/python-Werkzeug.changes 2016-09-27 13:44:42.000000000 +0200 @@ -1,0 +2,58 @@ +Fri Sep 16 14:25:04 UTC 2016 - [email protected] + +- Fix download url. + +------------------------------------------------------------------- +Thu Sep 15 23:08:05 UTC 2016 - [email protected] + +- update to version 0.11.11: + * Fix JSONRequestMixin for Python3. See #731 + * Fix broken string handling in test client when passing + integers. See #852 + * Fix a bug in "parse_options_header" where an invalid content type + starting with comma or semi-colon would result in an invalid + return value, see issue "#995". + * Fix a bug in multidicts when passing empty lists as values, see + issue "#979". + * Fix a security issue that allows XSS on the Werkzeug debugger. See + "#1001". +- update to version 0.11.10: + * Fixed a bug that occurs when running on Python 2.6 and using a + broken locale. See pull request #912. + * Fixed a crash when running the debugger on Google App Engine. See + issue #925. + * Fixed an issue with multipart parsing that could cause memory + exhaustion. +- Update to 0.11.9 + - Corrected an issue that caused the debugger not to use the + machine GUID on POSIX systems. + - Corrected an Unicode error on Python 3 for the debugger's + PIN usage. + - Corrected the timestamp verification in the pin debug code. + Without this fix the pin was remebered until too long. +- update to version 0.11.8: + * fixed a problem with the machine GUID detection code on OS X on + Python 3. +- changes from version 0.11.7: + * fixed a regression on Python 3 for the debugger. +- changes from version 0.11.6: + * werkzeug.serving: Still show the client address on bad requests. + * improved the PIN based protection for the debugger to make it + harder to brute force via trying cookies. Please keep in mind + that the debugger *is not intended for running on production + environments* + * increased the pin timeout to a week to make it less annoying for + people which should decrease the change that users disable the pin + check entirely. + * werkzeug.serving: Fix broken HTTP_HOST when path starts with + double slash. +- update to version 0.11.5: + * werkzeug.serving: Fix crash when attempting SSL connection to HTTP + server. +- update to version 0.11.4: + * Fixed werkzeug.serving not working from -m flag. + * Fixed incorrect weak etag handling. +- Rebase 0001_create_a_thread_to_reap_death_process.patch +- Split documentation into own subpackage to speed up build. + +------------------------------------------------------------------- Old: ---- Werkzeug-0.11.3.tar.gz New: ---- Werkzeug-0.11.11.tar.gz python-Werkzeug-doc.changes python-Werkzeug-doc.spec ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Werkzeug-doc.spec ++++++ # # spec file for package python3-Werkzeug-doc # # Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ # Name: python-Werkzeug-doc Version: 0.11.11 Release: 0 Url: http://werkzeug.pocoo.org/ Summary: Documentation for python3-Werkzeug License: BSD-3-Clause Group: Documentation/Other Source: https://files.pythonhosted.org/packages/source/W/Werkzeug/Werkzeug-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: python-Sphinx BuildRequires: python-Werkzeug = %{version} BuildRequires: python-setuptools BuildArch: noarch Requires: python-Werkzeug = %{version} %description Documentation and examples for python3-Werkzeug %prep %setup -q -n Werkzeug-%{version} sed -i "s/\r//" LICENSE # Fix wrong EOL-encoding sed -i "1d" examples/manage-{i18nurls,simplewiki,shorty,couchy,cupoftee,webpylike,plnt,coolmagic}.py # Fix non-executable scripts %build # Not needed %install cd docs && make html && rm -rf _build/html/.buildinfo # Build HTML Documentation %files %defattr(-,root,root,-) %doc AUTHORS LICENSE %doc docs/_build/html %doc examples %changelog ++++++ python-Werkzeug.spec ++++++ --- /var/tmp/diff_new_pack.aB0Gzq/_old 2016-09-27 13:44:43.000000000 +0200 +++ /var/tmp/diff_new_pack.aB0Gzq/_new 2016-09-27 13:44:43.000000000 +0200 @@ -17,19 +17,18 @@ Name: python-Werkzeug -Version: 0.11.3 +Version: 0.11.11 Release: 0 Url: http://werkzeug.pocoo.org/ Summary: The Swiss Army knife of Python web development License: BSD-3-Clause Group: Development/Languages/Python -Source: http://pypi.python.org/packages/source/W/Werkzeug/Werkzeug-%{version}.tar.gz +Source: https://files.pythonhosted.org/packages/source/W/Werkzeug/Werkzeug-%{version}.tar.gz # PATCH-FIX-UPSTREAM 0001_create_a_thread_to_reap_death_process.patch bsc#954591 Patch0: 0001_create_a_thread_to_reap_death_process.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildRequires: python-Sphinx BuildRequires: python-devel -BuildRequires: python-nose +BuildRequires: python-pytest BuildRequires: python-setuptools Provides: python-werkzeug = %{version} Obsoletes: python-werkzeug < %{version} @@ -54,14 +53,6 @@ on as many server environments as possible (such as blogs, wikis, bulletin boards, etc.). -%package doc -Summary: Documentation for %{name} -Group: Documentation/Other -Requires: %{name} = %{version} - -%description doc -Documentation and examples for %{name}. - %prep %setup -q -n Werkzeug-%{version} sed -i "s/\r//" LICENSE # Fix wrong EOL-encoding @@ -70,18 +61,16 @@ %build python setup.py build -cd docs && make html && rm -rf _build/html/.buildinfo # Build HTML Documentation %install python setup.py install --prefix=%{_prefix} --root=%{buildroot} +%check +python setup.py test + %files %defattr(-,root,root,-) %doc AUTHORS LICENSE CHANGES %{python_sitelib}/* -%files doc -%defattr(-,root,root,-) -%doc docs/_build/html examples - %changelog ++++++ 0001_create_a_thread_to_reap_death_process.patch ++++++ --- /var/tmp/diff_new_pack.aB0Gzq/_old 2016-09-27 13:44:43.000000000 +0200 +++ /var/tmp/diff_new_pack.aB0Gzq/_new 2016-09-27 13:44:43.000000000 +0200 @@ -29,9 +29,10 @@ import signal +import threading - from ._compat import PY2 + try: + import ssl -@@ -522,11 +523,29 @@ class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer): +@@ -524,11 +525,29 @@ multiprocess = True def __init__(self, host, port, app, processes=40, handler=None, @@ -39,7 +40,7 @@ + passthrough_errors=False, ssl_context=None, fd=None, + frequency=5): BaseWSGIServer.__init__(self, host, port, app, handler, - passthrough_errors, ssl_context, fd) + passthrough_errors, ssl_context, fd) self.max_children = processes + if frequency: ++++++ Werkzeug-0.11.3.tar.gz -> Werkzeug-0.11.11.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/AUTHORS new/Werkzeug-0.11.11/AUTHORS --- old/Werkzeug-0.11.3/AUTHORS 2015-01-03 16:03:47.000000000 +0100 +++ new/Werkzeug-0.11.11/AUTHORS 2016-08-31 15:12:07.000000000 +0200 @@ -28,6 +28,8 @@ - Daniel Neuhäuser - Markus Unterwaditzer - Joe Esposito <[email protected]> +- Cédric Krier +- Lars Holm Nielsen Contributors of code for werkzeug/examples are: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/CHANGES new/Werkzeug-0.11.11/CHANGES --- old/Werkzeug-0.11.3/CHANGES 2015-12-20 00:13:11.000000000 +0100 +++ new/Werkzeug-0.11.11/CHANGES 2016-08-31 15:12:53.000000000 +0200 @@ -1,6 +1,86 @@ Werkzeug Changelog ================== +Version 0.11.11 +--------------- + +Released on August 31st 2016. + +- Fix JSONRequestMixin for Python3. See #731 +- Fix broken string handling in test client when passing integers. See #852 +- Fix a bug in ``parse_options_header`` where an invalid content type + starting with comma or semi-colon would result in an invalid return value, + see issue ``#995``. +- Fix a bug in multidicts when passing empty lists as values, see issue + ``#979``. +- Fix a security issue that allows XSS on the Werkzeug debugger. See ``#1001``. + +Version 0.11.10 +--------------- + +Released on May 24th 2016. + +- Fixed a bug that occurs when running on Python 2.6 and using a broken locale. + See pull request #912. +- Fixed a crash when running the debugger on Google App Engine. See issue #925. +- Fixed an issue with multipart parsing that could cause memory exhaustion. + +Version 0.11.9 +-------------- + +Released on April 24th 2016. + +- Corrected an issue that caused the debugger not to use the + machine GUID on POSIX systems. +- Corrected an Unicode error on Python 3 for the debugger's + PIN usage. +- Corrected the timestamp verification in the pin debug code. + Without this fix the pin was remebered until too long. + +Version 0.11.8 +-------------- + +Released on April 15th 2016. + +- fixed a problem with the machine GUID detection code on OS X + on Python 3. + +Version 0.11.7 +-------------- + +Released on April 14th 2016. + +- fixed a regression on Python 3 for the debugger. + +Version 0.11.6 +-------------- + +Released on April 14th 2016. + +- werkzeug.serving: Still show the client address on bad requests. +- improved the PIN based protection for the debugger to make it harder to + brute force via trying cookies. Please keep in mind that the debugger + *is not intended for running on production environments* +- increased the pin timeout to a week to make it less annoying for people + which should decrease the change that users disable the pin check + entirely. +- werkzeug.serving: Fix broken HTTP_HOST when path starts with double slash. + +Version 0.11.5 +-------------- + +Released on March 22nd 2016. + +- werkzeug.serving: Fix crash when attempting SSL connection to HTTP server. + +Version 0.11.4 +-------------- + +Released on February 14th 2016. + +- Fixed werkzeug.serving not working from -m flag. +- Fixed incorrect weak etag handling. + Version 0.11.3 -------------- @@ -211,7 +291,7 @@ object (pull request ``#583``). - The ``qop`` parameter for ``WWW-Authenticate`` headers is now always quoted, as required by RFC 2617 (issue ``#633``). -- Fix bug in ``werkzeug.contrib.cache.SimpleCache`` with Python 3 where add/set +- Fix bug in ``werkzeug.contrib.cache.SimpleCache`` with Python 3 where add/set may throw an exception when pruning old entries from the cache (pull request ``#651``). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/PKG-INFO new/Werkzeug-0.11.11/PKG-INFO --- old/Werkzeug-0.11.3/PKG-INFO 2015-12-20 00:13:39.000000000 +0100 +++ new/Werkzeug-0.11.11/PKG-INFO 2016-08-31 15:13:05.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: Werkzeug -Version: 0.11.3 +Version: 0.11.11 Summary: The Swiss Army knife of Python web development Home-page: http://werkzeug.pocoo.org/ Author: Armin Ronacher diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/Werkzeug.egg-info/PKG-INFO new/Werkzeug-0.11.11/Werkzeug.egg-info/PKG-INFO --- old/Werkzeug-0.11.3/Werkzeug.egg-info/PKG-INFO 2015-12-20 00:13:38.000000000 +0100 +++ new/Werkzeug-0.11.11/Werkzeug.egg-info/PKG-INFO 2016-08-31 15:13:04.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: Werkzeug -Version: 0.11.3 +Version: 0.11.11 Summary: The Swiss Army knife of Python web development Home-page: http://werkzeug.pocoo.org/ Author: Armin Ronacher diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/Werkzeug.egg-info/SOURCES.txt new/Werkzeug-0.11.11/Werkzeug.egg-info/SOURCES.txt --- old/Werkzeug-0.11.3/Werkzeug.egg-info/SOURCES.txt 2015-12-20 00:13:39.000000000 +0100 +++ new/Werkzeug-0.11.11/Werkzeug.egg-info/SOURCES.txt 2016-08-31 15:13:04.000000000 +0200 @@ -11,10 +11,8 @@ Werkzeug.egg-info/dependency_links.txt Werkzeug.egg-info/not-zip-safe Werkzeug.egg-info/top_level.txt -artwork/.DS_Store artwork/logo.png artwork/logo.svg -docs/.DS_Store docs/Makefile docs/changes.rst docs/conf.py Files old/Werkzeug-0.11.3/artwork/.DS_Store and new/Werkzeug-0.11.11/artwork/.DS_Store differ Files old/Werkzeug-0.11.3/docs/.DS_Store and new/Werkzeug-0.11.11/docs/.DS_Store differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/setup.cfg new/Werkzeug-0.11.11/setup.cfg --- old/Werkzeug-0.11.3/setup.cfg 2015-12-20 00:13:39.000000000 +0100 +++ new/Werkzeug-0.11.11/setup.cfg 2016-08-31 15:13:05.000000000 +0200 @@ -8,7 +8,7 @@ universal = 1 [flake8] -ignore = E126,E241,E272 +ignore = E126,E241,E272,E402,E731,W503 exclude = .tox,examples,docs max-line-length = 100 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/contrib/test_wrappers.py new/Werkzeug-0.11.11/tests/contrib/test_wrappers.py --- old/Werkzeug-0.11.3/tests/contrib/test_wrappers.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/contrib/test_wrappers.py 2016-08-28 23:58:14.000000000 +0200 @@ -16,6 +16,16 @@ from werkzeug.wrappers import Request, Response +def test_json_request_mixin(): + class MyRequest(wrappers.JSONRequestMixin, Request): + pass + req = MyRequest.from_values( + data=u'{"foä": "bar"}'.encode('utf-8'), + content_type='text/json' + ) + assert req.json == {u'foä': 'bar'} + + def test_reverse_slash_behavior(): class MyRequest(wrappers.ReverseSlashBehaviorRequestMixin, Request): pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_datastructures.py new/Werkzeug-0.11.11/tests/test_datastructures.py --- old/Werkzeug-0.11.3/tests/test_datastructures.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/test_datastructures.py 2016-08-31 15:12:07.000000000 +0200 @@ -377,6 +377,15 @@ assert list(zip(iterkeys(md), iterlistvalues(md))) == \ list(iterlists(md)) + def test_getitem_raise_badrequestkeyerror_for_empty_list_value(self): + mapping = [('a', 'b'), ('a', 'c')] + md = self.storage_class(mapping) + + md.setlistdefault('empty', []) + + with pytest.raises(KeyError): + md['empty'] + class TestOrderedMultiDict(_MutableMultiDictTests): storage_class = datastructures.OrderedMultiDict diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_formparser.py new/Werkzeug-0.11.11/tests/test_formparser.py --- old/Werkzeug-0.11.3/tests/test_formparser.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/test_formparser.py 2016-08-31 15:12:07.000000000 +0200 @@ -154,7 +154,8 @@ class StreamMPP(formparser.MultiPartParser): def parse(self, file, boundary, content_length): - i = iter(self.parse_lines(file, boundary, content_length)) + i = iter(self.parse_lines(file, boundary, content_length, + cap_at_buffer=False)) one = next(i) two = next(i) return self.cls(()), {'one': one, 'two': two} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_http.py new/Werkzeug-0.11.11/tests/test_http.py --- old/Werkzeug-0.11.3/tests/test_http.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/test_http.py 2016-08-31 15:12:07.000000000 +0200 @@ -179,23 +179,23 @@ def test_etags(self): assert http.quote_etag('foo') == '"foo"' - assert http.quote_etag('foo', True) == 'w/"foo"' + assert http.quote_etag('foo', True) == 'W/"foo"' assert http.unquote_etag('"foo"') == ('foo', False) - assert http.unquote_etag('w/"foo"') == ('foo', True) - es = http.parse_etags('"foo", "bar", w/"baz", blar') + assert http.unquote_etag('W/"foo"') == ('foo', True) + es = http.parse_etags('"foo", "bar", W/"baz", blar') assert sorted(es) == ['bar', 'blar', 'foo'] assert 'foo' in es assert 'baz' not in es assert es.contains_weak('baz') assert 'blar' in es - assert es.contains_raw('w/"baz"') + assert es.contains_raw('W/"baz"') assert es.contains_raw('"foo"') - assert sorted(es.to_header().split(', ')) == ['"bar"', '"blar"', '"foo"', 'w/"baz"'] + assert sorted(es.to_header().split(', ')) == ['"bar"', '"blar"', '"foo"', 'W/"baz"'] def test_etags_nonzero(self): - etags = http.parse_etags('w/"foo"') + etags = http.parse_etags('W/"foo"') assert bool(etags) - assert etags.contains_raw('w/"foo"') + assert etags.contains_raw('W/"foo"') def test_parse_date(self): assert http.parse_date('Sun, 06 Nov 1994 08:49:37 GMT ') == datetime( @@ -247,7 +247,8 @@ assert http.parse_options_header('something; foo="otherthing"; meh=; bleh') == \ ('something', {'foo': 'otherthing', 'meh': None, 'bleh': None}) # Issue #404 - assert http.parse_options_header('multipart/form-data; name="foo bar"; filename="bar foo"') == \ + assert http.parse_options_header('multipart/form-data; name="foo bar"; ' + 'filename="bar foo"') == \ ('multipart/form-data', {'name': 'foo bar', 'filename': 'bar foo'}) # Examples from RFC assert http.parse_options_header('audio/*; q=0.2, audio/basic') == \ @@ -260,9 +261,20 @@ multiple=True) == \ ('text/plain', {'q': '0.5'}, "text/html", {}, "text/x-dvi", {'q': '0.8'}, "text/x-c", {}) - assert http.parse_options_header('text/plain; q=0.5, text/html\n text/x-dvi; q=0.8, text/x-c') == \ + assert http.parse_options_header('text/plain; q=0.5, text/html\n' + ' ' + 'text/x-dvi; q=0.8, text/x-c') == \ ('text/plain', {'q': '0.5'}) + def test_parse_options_header_broken_values(self): + # Issue #995 + assert http.parse_options_header(' ') == ('', {}) + assert http.parse_options_header(' , ') == ('', {}) + assert http.parse_options_header(' ; ') == ('', {}) + assert http.parse_options_header(' ,; ') == ('', {}) + assert http.parse_options_header(' , a ') == ('', {}) + assert http.parse_options_header(' ; a ') == ('', {}) + def test_dump_options_header(self): assert http.dump_options_header('foo', {'bar': 42}) == \ 'foo; bar=42' @@ -377,7 +389,7 @@ assert rv.to_header() == '"Test"' # weak information is dropped - rv = http.parse_if_range_header('w/"Test"') + rv = http.parse_if_range_header('W/"Test"') assert rv.etag == 'Test' assert rv.date is None assert rv.to_header() == '"Test"' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_local.py new/Werkzeug-0.11.11/tests/test_local.py --- old/Werkzeug-0.11.3/tests/test_local.py 2015-11-25 09:46:21.000000000 +0100 +++ new/Werkzeug-0.11.11/tests/test_local.py 2016-08-31 15:12:07.000000000 +0200 @@ -162,8 +162,10 @@ def test_deepcopy_on_proxy(): class Foo(object): attr = 42 + def __copy__(self): return self + def __deepcopy__(self, memo): return self f = Foo() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_serving.py new/Werkzeug-0.11.11/tests/test_serving.py --- old/Werkzeug-0.11.3/tests/test_serving.py 2015-10-02 16:54:47.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/test_serving.py 2016-08-31 15:12:07.000000000 +0200 @@ -24,6 +24,11 @@ except ImportError: watchdog = None +try: + import httplib +except ImportError: + from http import client as httplib + import requests import requests.exceptions import pytest @@ -39,6 +44,36 @@ assert b'Werkzeug/' + version.encode('ascii') in rv +def test_absolute_requests(dev_server): + server = dev_server(''' + def app(environ, start_response): + assert environ['HTTP_HOST'] == 'surelynotexisting.example.com:1337' + assert environ['PATH_INFO'] == '/index.htm' + addr = environ['HTTP_X_WERKZEUG_ADDR'] + assert environ['SERVER_PORT'] == addr.split(':')[1] + start_response('200 OK', [('Content-Type', 'text/html')]) + return [b'YES'] + ''') + + conn = httplib.HTTPConnection(server.addr) + conn.request('GET', 'http://surelynotexisting.example.com:1337/index.htm#ignorethis', + headers={'X-Werkzeug-Addr': server.addr}) + res = conn.getresponse() + assert res.read() == b'YES' + + +def test_double_slash_path(dev_server): + server = dev_server(''' + def app(environ, start_response): + assert 'fail' not in environ['HTTP_HOST'] + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [b'YES'] + ''') + + r = requests.get(server.url + '//fail') + assert r.content == b'YES' + + def test_broken_app(dev_server): server = dev_server(''' def app(environ, start_response): @@ -196,3 +231,21 @@ ReloaderLoop()._sleep(0) ''')) subprocess.check_call(['python', str(script)]) + + +def test_wrong_protocol(dev_server): + # Assert that sending HTTPS requests to a HTTP server doesn't show a + # traceback + # See https://github.com/mitsuhiko/werkzeug/pull/838 + + server = dev_server(''' + def app(environ, start_response): + start_response('200 OK', [('Content-Type', 'text/html')]) + return [b'hello'] + ''') + with pytest.raises(requests.exceptions.ConnectionError): + requests.get('https://%s/' % server.addr) + + log = server.logfile.read() + assert 'Traceback' not in log + assert '\n127.0.0.1' in log diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_test.py new/Werkzeug-0.11.11/tests/test_test.py --- old/Werkzeug-0.11.3/tests/test_test.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/test_test.py 2016-08-31 15:12:07.000000000 +0200 @@ -143,6 +143,7 @@ assert b.content_type == 'application/x-www-form-urlencoded' b.files.add_file('test', BytesIO(b'test contents'), 'test.txt') assert b.files['test'].content_type == 'text/plain' + b.form['test_int'] = 1 assert b.content_type == 'multipart/form-data' req = b.get_request() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_wrappers.py new/Werkzeug-0.11.11/tests/test_wrappers.py --- old/Werkzeug-0.11.3/tests/test_wrappers.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/test_wrappers.py 2016-08-31 15:12:07.000000000 +0200 @@ -345,8 +345,8 @@ def test_etag_request_mixin(): request = wrappers.Request({ 'HTTP_CACHE_CONTROL': 'no-store, no-cache', - 'HTTP_IF_MATCH': 'w/"foo", bar, "baz"', - 'HTTP_IF_NONE_MATCH': 'w/"foo", bar, "baz"', + 'HTTP_IF_MATCH': 'W/"foo", bar, "baz"', + 'HTTP_IF_NONE_MATCH': 'W/"foo", bar, "baz"', 'HTTP_IF_MODIFIED_SINCE': 'Tue, 22 Jan 2008 11:18:44 GMT', 'HTTP_IF_UNMODIFIED_SINCE': 'Tue, 22 Jan 2008 11:18:44 GMT' }) @@ -355,7 +355,7 @@ for etags in request.if_match, request.if_none_match: assert etags('bar') - assert etags.contains_raw('w/"foo"') + assert etags.contains_raw('W/"foo"') assert etags.contains_weak('foo') assert not etags.contains('foo') @@ -639,17 +639,25 @@ def test_form_parsing_failed(): - data = ( - b'--blah\r\n' - ) - data = wrappers.Request.from_values( + data = b'--blah\r\n' + request = wrappers.Request.from_values( input_stream=BytesIO(data), content_length=len(data), content_type='multipart/form-data; boundary=foo', method='POST' ) - assert not data.files - assert not data.form + assert not request.files + assert not request.form + + # Bad Content-Type + data = b'test' + request = wrappers.Request.from_values( + input_stream=BytesIO(data), + content_length=len(data), + content_type=', ', + method='POST' + ) + assert not request.form def test_file_closing(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/tests/test_wsgi.py new/Werkzeug-0.11.11/tests/test_wsgi.py --- old/Werkzeug-0.11.3/tests/test_wsgi.py 2015-10-24 22:22:59.000000000 +0200 +++ new/Werkzeug-0.11.11/tests/test_wsgi.py 2016-08-28 23:58:14.000000000 +0200 @@ -381,6 +381,13 @@ buffer_size=4)) assert rv == [b'abcdef', b'ghijkl', b'mnopqrstuvwxyz', b'ABCDEFGHIJK'] + data = b'abcdefXghijklXmnopqrstuvwxyzXABCDEFGHIJK' + test_stream = BytesIO(data) + rv = list(wsgi.make_chunk_iter(test_stream, 'X', limit=len(data), + buffer_size=4, cap_at_buffer=True)) + assert rv == [b'abcd', b'ef', b'ghij', b'kl', b'mnop', b'qrst', b'uvwx', + b'yz', b'ABCD', b'EFGH', b'IJK'] + def test_lines_longer_buffer_size(): data = '1234567890\n1234567890\n' @@ -388,3 +395,11 @@ lines = list(wsgi.make_line_iter(NativeStringIO(data), limit=len(data), buffer_size=4)) assert lines == ['1234567890\n', '1234567890\n'] + + +def test_lines_longer_buffer_size_cap(): + data = '1234567890\n1234567890\n' + for bufsize in range(1, 15): + lines = list(wsgi.make_line_iter(NativeStringIO(data), limit=len(data), + buffer_size=4, cap_at_buffer=True)) + assert lines == ['1234', '5678', '90\n', '1234', '5678', '90\n'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/__init__.py new/Werkzeug-0.11.11/werkzeug/__init__.py --- old/Werkzeug-0.11.3/werkzeug/__init__.py 2015-12-20 00:13:27.000000000 +0100 +++ new/Werkzeug-0.11.11/werkzeug/__init__.py 2016-08-31 15:13:02.000000000 +0200 @@ -20,7 +20,7 @@ from werkzeug._compat import iteritems # the version. Usually set automatically by a script. -__version__ = '0.11.3' +__version__ = '0.11.11' # This import magic raises concerns quite often which is why the implementation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/contrib/lint.py new/Werkzeug-0.11.11/werkzeug/contrib/lint.py --- old/Werkzeug-0.11.3/werkzeug/contrib/lint.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/contrib/lint.py 2016-08-19 21:37:38.000000000 +0200 @@ -276,7 +276,10 @@ def check_headers(self, headers): etag = headers.get('etag') if etag is not None: - if etag.startswith('w/'): + if etag.startswith(('W/', 'w/')): + if etag.startswith('w/'): + warn(HTTPWarning('weak etag indicator should be upcase.'), + stacklevel=4) etag = etag[2:] if not (etag[:1] == etag[-1:] == '"'): warn(HTTPWarning('unquoted etag emitted.'), stacklevel=4) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/contrib/wrappers.py new/Werkzeug-0.11.11/werkzeug/contrib/wrappers.py --- old/Werkzeug-0.11.3/werkzeug/contrib/wrappers.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/contrib/wrappers.py 2016-08-31 15:12:07.000000000 +0200 @@ -56,7 +56,7 @@ if 'json' not in self.environ.get('CONTENT_TYPE', ''): raise BadRequest('Not a JSON request') try: - return loads(self.data) + return loads(self.data.decode(self.charset, self.encoding_errors)) except Exception: raise BadRequest('Unable to read JSON request') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/datastructures.py new/Werkzeug-0.11.11/werkzeug/datastructures.py --- old/Werkzeug-0.11.3/werkzeug/datastructures.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/datastructures.py 2016-08-31 15:12:07.000000000 +0200 @@ -372,6 +372,8 @@ tmp = {} for key, value in iteritems(mapping): if isinstance(value, (tuple, list)): + if len(value) == 0: + continue value = list(value) else: value = [value] @@ -398,7 +400,9 @@ :raise KeyError: if the key does not exist. """ if key in self: - return dict.__getitem__(self, key)[0] + lst = dict.__getitem__(self, key) + if len(lst) > 0: + return lst[0] raise exceptions.BadRequestKeyError(key) def __setitem__(self, key, value): @@ -2160,7 +2164,7 @@ return '*' return ', '.join( ['"%s"' % x for x in self._strong] + - ['w/"%s"' % x for x in self._weak] + ['W/"%s"' % x for x in self._weak] ) def __call__(self, etag=None, data=None, include_weak=False): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/debug/__init__.py new/Werkzeug-0.11.11/werkzeug/debug/__init__.py --- old/Werkzeug-0.11.3/werkzeug/debug/__init__.py 2015-11-10 12:49:52.000000000 +0100 +++ new/Werkzeug-0.11.11/werkzeug/debug/__init__.py 2016-08-31 15:12:07.000000000 +0200 @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for more details. """ import os +import re import sys import uuid import json @@ -16,6 +17,7 @@ import getpass import hashlib import mimetypes +from itertools import chain from os.path import join, dirname, basename, isfile from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response from werkzeug.http import parse_cookie @@ -32,7 +34,70 @@ from werkzeug.debug.repr import debug_repr # noqa -PIN_TIME = 60 * 60 * 8 +# A week +PIN_TIME = 60 * 60 * 24 * 7 + + +def hash_pin(pin): + if isinstance(pin, text_type): + pin = pin.encode('utf-8', 'replace') + return hashlib.md5(pin + b'shittysalt').hexdigest()[:12] + + +_machine_id = None + + +def get_machine_id(): + global _machine_id + rv = _machine_id + if rv is not None: + return rv + + def _generate(): + # Potential sources of secret information on linux. The machine-id + # is stable across boots, the boot id is not + for filename in '/etc/machine-id', '/proc/sys/kernel/random/boot_id': + try: + with open(filename, 'rb') as f: + return f.readline().strip() + except IOError: + continue + + # On OS X we can use the computer's serial number assuming that + # ioreg exists and can spit out that information. + try: + # Also catch import errors: subprocess may not be available, e.g. + # Google App Engine + # See https://github.com/pallets/werkzeug/issues/925 + from subprocess import Popen, PIPE + dump = Popen(['ioreg', '-c', 'IOPlatformExpertDevice', '-d', '2'], + stdout=PIPE).communicate()[0] + match = re.search(b'"serial-number" = <([^>]+)', dump) + if match is not None: + return match.group(1) + except (OSError, ImportError): + pass + + # On Windows we can use winreg to get the machine guid + wr = None + try: + import winreg as wr + except ImportError: + try: + import _winreg as wr + except ImportError: + pass + if wr is not None: + try: + with wr.OpenKey(wr.HKEY_LOCAL_MACHINE, + 'SOFTWARE\\Microsoft\\Cryptography', 0, + wr.KEY_READ | wr.KEY_WOW64_64KEY) as rk: + return wr.QueryValueEx(rk, 'MachineGuid')[0] + except WindowsError: + pass + + _machine_id = rv = _generate() + return rv class _ConsoleFrame(object): @@ -72,30 +137,50 @@ modname = getattr(app, '__module__', getattr(app.__class__, '__module__')) - bits = [ - getpass.getuser(), - str(uuid.getnode()), + + try: + # `getpass.getuser()` imports the `pwd` module, + # which does not exist in the Google App Engine sandbox. + username = getpass.getuser() + except ImportError: + username = None + + mod = sys.modules.get(modname) + + # This information only exists to make the cookie unique on the + # computer, not as a security feature. + probably_public_bits = [ + username, modname, getattr(app, '__name__', getattr(app.__class__, '__name__')), + getattr(mod, '__file__', None), ] - mod = sys.modules.get(modname) - bits.append(getattr(mod, '__file__', None)) - bits.append('cookiesalt') + # This information is here to make it harder for an attacker to + # guess the cookie name. They are unlikely to be contained anywhere + # within the unauthenticated debug page. + private_bits = [ + str(uuid.getnode()), + get_machine_id(), + ] h = hashlib.md5() - for bit in bits: + for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, text_type): bit = bit.encode('utf-8') h.update(bit) + h.update(b'cookiesalt') + cookie_name = '__wzd' + h.hexdigest()[:20] + + # If we need to generate a pin we salt it a bit more so that we don't + # end up with the same value and generate out 9 digits if num is None: + h.update(b'pinsalt') num = ('%09d' % int(h.hexdigest(), 16))[:9] - cookie_name = '__wzd' + h.hexdigest()[:12] - # Format the pincode in groups of digits for easier remembering if # we don't have a result yet. if rv is None: @@ -228,7 +313,7 @@ 'response at a point where response headers were already ' 'sent.\n') else: - is_trusted = self.is_trusted(environ) + is_trusted = bool(self.check_pin_trust(environ)) yield traceback.render_full(evalex=self.evalex, evalex_trusted=is_trusted, secret=self.secret) \ @@ -249,7 +334,7 @@ ns = dict(self.console_init_func()) ns.setdefault('app', self.app) self.frames[0] = _ConsoleFrame(ns) - is_trusted = self.is_trusted(request.environ) + is_trusted = bool(self.check_pin_trust(request.environ)) return Response(render_console_html(secret=self.secret, evalex_trusted=is_trusted), mimetype='text/html') @@ -272,23 +357,53 @@ f.close() return Response('Not Found', status=404) - def is_trusted(self, environ): - """Checks if the request passed the pin test.""" + def check_pin_trust(self, environ): + """Checks if the request passed the pin test. This returns `True` if the + request is trusted on a pin/cookie basis and returns `False` if not. + Additionally if the cookie's stored pin hash is wrong it will return + `None` so that appropriate action can be taken. + """ if self.pin is None: return True - ts = parse_cookie(environ).get(self.pin_cookie_name, type=int) - if ts is None: + val = parse_cookie(environ).get(self.pin_cookie_name) + if not val or '|' not in val: return False - return (time.time() - PIN_TIME) < ts + ts, pin_hash = val.split('|', 1) + if not ts.isdigit(): + return False + if pin_hash != hash_pin(self.pin): + return None + return (time.time() - PIN_TIME) < int(ts) + + def _fail_pin_auth(self): + time.sleep(self._failed_pin_auth > 5 and 5.0 or 0.5) + self._failed_pin_auth += 1 def pin_auth(self, request): """Authenticates with the pin.""" exhausted = False auth = False - if self.is_trusted(request.environ): + trust = self.check_pin_trust(request.environ) + + # If the trust return value is `None` it means that the cookie is + # set but the stored pin hash value is bad. This means that the + # pin was changed. In this case we count a bad auth and unset the + # cookie. This way it becomes harder to guess the cookie name + # instead of the pin as we still count up failures. + bad_cookie = False + if trust is None: + self._fail_pin_auth() + bad_cookie = True + + # If we're trusted, we're authenticated. + elif trust: auth = True + + # If we failed too many times, then we're locked out. elif self._failed_pin_auth > 10: exhausted = True + + # Otherwise go through pin based authentication else: entered_pin = request.args.get('pin') if entered_pin.strip().replace('-', '') == \ @@ -296,17 +411,19 @@ self._failed_pin_auth = 0 auth = True else: - time.sleep(self._failed_pin_auth > 5 and 5.0 or 0.5) - self._failed_pin_auth += 1 - auth = False + self._fail_pin_auth() rv = Response(json.dumps({ 'auth': auth, 'exhausted': exhausted, }), mimetype='application/json') if auth: - rv.set_cookie(self.pin_cookie_name, str(int(time.time())), - httponly=True) + rv.set_cookie(self.pin_cookie_name, '%s|%s' % ( + int(time.time()), + hash_pin(self.pin) + ), httponly=True) + elif bad_cookie: + rv.delete_cookie(self.pin_cookie_name) return rv def log_pin_request(self): @@ -341,7 +458,7 @@ response = self.log_pin_request() elif self.evalex and cmd is not None and frame is not None \ and self.secret == secret and \ - self.is_trusted(environ): + self.check_pin_trust(environ): response = self.execute_command(request, cmd, frame) elif self.evalex and self.console_path is not None and \ request.path == self.console_path: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/debug/tbtools.py new/Werkzeug-0.11.11/werkzeug/debug/tbtools.py --- old/Werkzeug-0.11.3/werkzeug/debug/tbtools.py 2015-10-24 22:22:59.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/debug/tbtools.py 2016-08-31 15:12:01.000000000 +0200 @@ -358,7 +358,7 @@ 'exception': exc, 'exception_type': escape(self.exception_type), 'summary': self.render_summary(include_title=False), - 'plaintext': self.plaintext, + 'plaintext': escape(self.plaintext), 'plaintext_cs': re.sub('-{2,}', '-', self.plaintext), 'traceback_id': self.id, 'secret': secret diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/filesystem.py new/Werkzeug-0.11.11/werkzeug/filesystem.py --- old/Werkzeug-0.11.3/werkzeug/filesystem.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/filesystem.py 2016-08-28 23:58:14.000000000 +0200 @@ -59,7 +59,7 @@ if not _warned_about_filesystem_encoding: warnings.warn( 'Detected a misconfigured UNIX filesystem: Will use UTF-8 as ' - 'filesystem encoding instead of {!r}'.format(rv), + 'filesystem encoding instead of {0!r}'.format(rv), BrokenFilesystemWarning) _warned_about_filesystem_encoding = True return 'utf-8' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/formparser.py new/Werkzeug-0.11.11/werkzeug/formparser.py --- old/Werkzeug-0.11.3/werkzeug/formparser.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/formparser.py 2016-08-31 15:12:07.000000000 +0200 @@ -372,7 +372,7 @@ # the assert is skipped. self.fail('Boundary longer than buffer size') - def parse_lines(self, file, boundary, content_length): + def parse_lines(self, file, boundary, content_length, cap_at_buffer=True): """Generate parts of ``('begin_form', (headers, name))`` ``('begin_file', (headers, name, filename))`` @@ -387,7 +387,8 @@ last_part = next_part + b'--' iterator = chain(make_line_iter(file, limit=content_length, - buffer_size=self.buffer_size), + buffer_size=self.buffer_size, + cap_at_buffer=cap_at_buffer), _empty_string_iter) terminator = self._find_terminator(iterator) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/http.py new/Werkzeug-0.11.11/werkzeug/http.py --- old/Werkzeug-0.11.3/werkzeug/http.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/http.py 2016-08-31 15:12:07.000000000 +0200 @@ -336,7 +336,6 @@ :return: (mimetype, options) or (mimetype, options, mimetype, options, …) if multiple=True """ - if not value: return '', {} @@ -368,7 +367,7 @@ return tuple(result) value = rest - return tuple(result) + return tuple(result) if result else ('', {}) def parse_accept_header(value, cls=None): @@ -627,14 +626,14 @@ raise ValueError('invalid etag') etag = '"%s"' % etag if weak: - etag = 'w/' + etag + etag = 'W/' + etag return etag def unquote_etag(etag): """Unquote a single etag: - >>> unquote_etag('w/"bar"') + >>> unquote_etag('W/"bar"') ('bar', True) >>> unquote_etag('"bar"') ('bar', False) @@ -646,7 +645,7 @@ return None, None etag = etag.strip() weak = False - if etag[:2] in ('w/', 'W/'): + if etag.startswith(('W/', 'w/')): weak = True etag = etag[2:] if etag[:1] == etag[-1:] == '"': @@ -800,7 +799,11 @@ if etag: if_none_match = parse_etags(environ.get('HTTP_IF_NONE_MATCH')) if if_none_match: - unmodified = if_none_match.contains_raw(etag) + # http://tools.ietf.org/html/rfc7232#section-3.2 + # "A recipient MUST use the weak comparison function when comparing + # entity-tags for If-None-Match" + etag, _ = unquote_etag(etag) + unmodified = if_none_match.contains_weak(etag) return not unmodified diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/serving.py new/Werkzeug-0.11.11/werkzeug/serving.py --- old/Werkzeug-0.11.3/werkzeug/serving.py 2015-12-20 00:12:55.000000000 +0100 +++ new/Werkzeug-0.11.11/werkzeug/serving.py 2016-08-31 15:12:07.000000000 +0200 @@ -42,8 +42,6 @@ import sys import signal -from ._compat import PY2 - try: import ssl except ImportError: @@ -70,9 +68,10 @@ from socketserver import ThreadingMixIn, ForkingMixIn from http.server import HTTPServer, BaseHTTPRequestHandler +# important: do not use relative imports here or python -m will break import werkzeug from werkzeug._internal import _log -from werkzeug._compat import reraise, wsgi_encoding_dance +from werkzeug._compat import PY2, reraise, wsgi_encoding_dance from werkzeug.urls import url_parse, url_unquote from werkzeug.exceptions import InternalServerError @@ -114,8 +113,8 @@ 'QUERY_STRING': wsgi_encoding_dance(request_url.query), 'CONTENT_TYPE': self.headers.get('Content-Type', ''), 'CONTENT_LENGTH': self.headers.get('Content-Length', ''), - 'REMOTE_ADDR': self.client_address[0], - 'REMOTE_PORT': self.client_address[1], + 'REMOTE_ADDR': self.address_string(), + 'REMOTE_PORT': self.port_integer(), 'SERVER_NAME': self.server.server_address[0], 'SERVER_PORT': str(self.server.server_address[1]), 'SERVER_PROTOCOL': self.request_version @@ -126,7 +125,7 @@ if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): environ[key] = value - if request_url.netloc: + if request_url.scheme and request_url.netloc: environ['HTTP_HOST'] = request_url.netloc return environ @@ -264,7 +263,10 @@ return BaseHTTPRequestHandler.version_string(self).strip() def address_string(self): - return self.environ['REMOTE_ADDR'] + return self.client_address[0] + + def port_integer(self): + return self.client_address[1] def log_request(self, code='-', size='-'): self.log('info', '"%s" %s %s', self.requestline, code, size) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/test.py new/Werkzeug-0.11.11/werkzeug/test.py --- old/Werkzeug-0.11.3/werkzeug/test.py 2015-09-20 20:59:05.000000000 +0200 +++ new/Werkzeug-0.11.11/werkzeug/test.py 2016-08-31 15:12:07.000000000 +0200 @@ -99,8 +99,8 @@ else: if not isinstance(value, string_types): value = str(value) - else: - value = to_bytes(value, charset) + + value = to_bytes(value, charset) write('\r\n\r\n') write_binary(value) write('\r\n') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/urls.py new/Werkzeug-0.11.11/werkzeug/urls.py --- old/Werkzeug-0.11.3/werkzeug/urls.py 2015-11-12 12:21:06.000000000 +0100 +++ new/Werkzeug-0.11.11/werkzeug/urls.py 2016-08-19 21:37:38.000000000 +0200 @@ -40,8 +40,10 @@ ) -_URLTuple = fix_tuple_repr(namedtuple('_URLTuple', - ['scheme', 'netloc', 'path', 'query', 'fragment'])) +_URLTuple = fix_tuple_repr(namedtuple( + '_URLTuple', + ['scheme', 'netloc', 'path', 'query', 'fragment'] +)) class BaseURL(_URLTuple): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Werkzeug-0.11.3/werkzeug/wsgi.py new/Werkzeug-0.11.11/werkzeug/wsgi.py --- old/Werkzeug-0.11.3/werkzeug/wsgi.py 2015-11-12 12:21:06.000000000 +0100 +++ new/Werkzeug-0.11.11/werkzeug/wsgi.py 2016-08-31 15:12:07.000000000 +0200 @@ -784,7 +784,8 @@ yield item -def make_line_iter(stream, limit=None, buffer_size=10 * 1024): +def make_line_iter(stream, limit=None, buffer_size=10 * 1024, + cap_at_buffer=False): """Safely iterates line-based over an input stream. If the input stream is not a :class:`LimitedStream` the `limit` parameter is mandatory. @@ -808,6 +809,12 @@ content length. Not necessary if the `stream` is a :class:`LimitedStream`. :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. """ _iter = _make_chunk_iter(stream, limit, buffer_size) @@ -831,11 +838,19 @@ if not new_data: break new_buf = [] + buf_size = 0 for item in chain(buffer, new_data.splitlines(True)): new_buf.append(item) + buf_size += len(item) if item and item[-1:] in crlf: yield _join(new_buf) new_buf = [] + elif cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] buffer = new_buf if buffer: yield _join(buffer) @@ -854,7 +869,8 @@ yield previous -def make_chunk_iter(stream, separator, limit=None, buffer_size=10 * 1024): +def make_chunk_iter(stream, separator, limit=None, buffer_size=10 * 1024, + cap_at_buffer=False): """Works like :func:`make_line_iter` but accepts a separator which divides chunks. If you want newline based processing you should use :func:`make_line_iter` instead as it @@ -865,12 +881,19 @@ .. versionadded:: 0.9 added support for iterators as input stream. + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. + :param stream: the stream or iterate to iterate over. :param separator: the separator that divides chunks. :param limit: the limit in bytes for the stream. (Usually content length. Not necessary if the `stream` is otherwise already limited). :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. """ _iter = _make_chunk_iter(stream, limit, buffer_size) @@ -895,12 +918,24 @@ break chunks = _split(new_data) new_buf = [] + buf_size = 0 for item in chain(buffer, chunks): if item == separator: yield _join(new_buf) new_buf = [] + buf_size = 0 else: + buf_size += len(item) new_buf.append(item) + + if cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] + buf_size = len(rv) + buffer = new_buf if buffer: yield _join(buffer)
