Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ dependencies = [
"urllib3>=1.12",
]
optional-dependencies.diazo = [
"diazo>=1.0.5",
"diazo==1.5.0",
"lxml>=3.4",
]
optional-dependencies.tests = [
"diazo",
"diazo==1.5.0",
"lxml>=3.4",
"coverage",
"flake8",
Expand Down
17 changes: 12 additions & 5 deletions revproxy/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

try:
from django.utils.six.moves.urllib.parse import (
urlparse, urlencode, quote_plus, quote
urlparse, quote_plus, quote, parse_qsl
)
except ImportError:
# Django 3 has no six
from urllib.parse import (
urlparse, urlencode, quote_plus, quote
urlparse, quote_plus, quote, parse_qsl
)

from django.conf import settings
Expand All @@ -27,7 +27,7 @@
from .exceptions import InvalidUpstream
from .response import get_django_response
from .transformer import DiazoTransformer
from .utils import normalize_request_headers, encode_items
from .utils import normalize_request_headers

# Chars that don't need to be quoted. We use same than nginx:
# https://github.com/nginx/nginx/blob/nginx-1.9/src/core/ngx_string.c
Expand Down Expand Up @@ -176,8 +176,15 @@ def get_quoted_path(self, path):

def get_encoded_query_params(self):
"""Return encoded query params to be used in proxied request"""
get_data = encode_items(self.request.GET.lists())
return urlencode(get_data)
params = parse_qsl(
self.request.GET.urlencode(),
keep_blank_values=True,
)
custom_query = '&'.join(
k if v == '' else f'{k}={v}'
for k, v in params
)
return custom_query

def _created_proxy_response(self, request, path):
request_payload = request.body
Expand Down
27 changes: 27 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,30 @@ def get_proxy_request_headers(self, request):
preload_content=False,
decode_content=False,
headers=custom_headers)

def test_raw_query_string_is_preserved_for_flag_params(self):
"""
Regression test: preserve "flag" params (no '=') when proxying.

Example: `...?. . .&c` must NOT become `...&c=`.
"""
class CustomProxyView(ProxyView):
upstream = 'http://example.com'

raw_qs = 'q=1&a=2&c'
path = 'some/path'

# Important: set QUERY_STRING explicitly so Django keeps it in request.META
request = self.factory.get(path, QUERY_STRING=raw_qs)
CustomProxyView.as_view()(request, path)

url = 'http://example.com/' + path + '?' + raw_qs
headers = {u'Cookie': u''}
self.urlopen.assert_called_with('GET',
url,
body=b'',
redirect=False,
retries=None,
preload_content=False,
decode_content=False,
headers=headers)