Skip to content

Request Line is too large (400) sometimes #347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 24, 2013
92 changes: 92 additions & 0 deletions debug_toolbar/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.functional import cached_property

try:
import json
except ImportError:
from django.utils import simplejson as json

try:
from hashlib import sha1
except ImportError:
from django.utils.hashcompat import sha_constructor as sha1

from debug_toolbar.utils.compat.db import connections


class SQLSelectForm(forms.Form):
"""
Validate params

sql: urlencoded sql with positional arguments
params: JSON encoded parameter values
duration: time for SQL to execute passed in from toolbar just for redisplay
hash: the hash of (secret + sql + params) for tamper checking
"""
sql = forms.CharField()
params = forms.CharField()
alias = forms.CharField(required=False, initial='default')
duration = forms.FloatField()
hash = forms.CharField()

def __init__(self, *args, **kwargs):
initial = kwargs.get('initial', None)

if initial is not None:
initial['hash'] = self.make_hash(initial)

super(SQLSelectForm, self).__init__(*args, **kwargs)

for name in self.fields:
self.fields[name].widget = forms.HiddenInput()

def clean_sql(self):
value = self.cleaned_data['sql']

if not value.lower().strip().startswith('select'):
raise ValidationError("Only 'select' queries are allowed.")

return value

def clean_params(self):
value = self.cleaned_data['params']

try:
return json.loads(value)
except ValueError:
raise ValidationError('Is not valid JSON')

def clean_alias(self):
value = self.cleaned_data['alias']

if value not in connections:
raise ValidationError("Database alias '%s' not found" % value)

return value

def clean_hash(self):
hash = self.cleaned_data['hash']

if hash != self.make_hash(self.data):
raise ValidationError('Tamper alert')

return hash

def reformat_sql(self):
from debug_toolbar.panels.sql import reformat_sql
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this to a third module, e.g. debug_toolbar.utils or something match.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move what? reformat_sql function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jezdez please, be exactly :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the reformat_sql function so that the import can be moved at the top of the file. I assume you put the import here to prevent a circular import.

sql, params = self.cleaned_data['sql'], self.cleaned_data['params']
return reformat_sql(self.cursor.db.ops.last_executed_query(self.cursor, sql, params))

def make_hash(self, data):
params = settings.SECRET_KEY + data['sql'] + data['params']
return sha1(params).hexdigest()

@property
def connection(self):
return connections[self.cleaned_data['alias']]

@cached_property
def cursor(self):
return self.connection.cursor()
5 changes: 5 additions & 0 deletions debug_toolbar/panels/sql.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import re
import uuid
from copy import copy

from django.db.backends import BaseDatabaseWrapper
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __

from debug_toolbar.forms import SQLSelectForm
from debug_toolbar.utils.compat.db import connections
from debug_toolbar.middleware import DebugToolbarMiddleware
from debug_toolbar.panels import DebugPanel
Expand Down Expand Up @@ -170,6 +172,9 @@ def process_response(self, request, response):
query['iso_level'] = get_isolation_level_display(query['engine'], query['iso_level'])
if 'trans_status' in query:
query['trans_status'] = get_transaction_status_display(query['engine'], query['trans_status'])

query['form'] = SQLSelectForm(auto_id=None, initial=copy(query))

if query['sql']:
query['sql'] = reformat_sql(query['sql'])
query['rgb_color'] = self._databases[alias]['rgb_color']
Expand Down
8 changes: 7 additions & 1 deletion debug_toolbar/static/debug_toolbar/css/toolbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,16 @@
vertical-align:top;
padding:2px 3px;
}
#djDebug .panelContent tbody td.time {
text-align: center;
}

#djDebug .panelContent thead th {
padding:1px 6px 1px 3px;
text-align:left;
font-weight:bold;
font-size:14px;
white-space: nowrap;
}
#djDebug .panelContent tbody th {
width:12em;
Expand Down Expand Up @@ -502,8 +507,9 @@
width: 14px;
padding-top: 3px;
}
#djdebug .panelcontent table .actions {
#djDebug .panelContent table .actions {
min-width: 70px;
white-space: nowrap;
}
#djdebug .panelcontent table .color {
width: 3px;
Expand Down
2 changes: 1 addition & 1 deletion debug_toolbar/static/debug_toolbar/css/toolbar.min.css

Large diffs are not rendered by default.

41 changes: 31 additions & 10 deletions debug_toolbar/static/debug_toolbar/js/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,41 @@ window.djdt = (function(window, document, jQuery) {
$('#djDebugToolbar li').removeClass('active');
return false;
});
$('#djDebug a.remoteCall').live('click', function() {
$('#djDebugWindow').load(this.href, function(response, status, xhr) {
if (status == "error") {
var message = '<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href="">Back</a><h3>'+xhr.status+': '+xhr.statusText+'</h3></div>';
$('#djDebugWindow').html(message);

$('#djDebug .remoteCall').live('click', function() {
var self = $(this);
var name = self[0].tagName.toLowerCase();
var ajax_data = {};

if (name == 'button') {
var form = self.parents('form:eq(0)');
ajax_data['url'] = self.attr('formaction');

if (form.length) {
ajax_data['data'] = form.serialize();
ajax_data['type'] = form.attr('method') || 'POST';
}
$('#djDebugWindow a.djDebugBack').live('click', function() {
$(this).parent().parent().hide();
return false;
});
}

if (name == 'a') {
ajax_data['url'] = self.attr('href');
}

$.ajax(ajax_data).done(function(data){
$('#djDebugWindow').html(data).show();
}).fail(function(xhr){
var message = '<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href="">Back</a><h3>'+xhr.status+': '+xhr.statusText+'</h3></div>';
$('#djDebugWindow').html(message).show();
});

$('#djDebugWindow a.djDebugBack').live('click', function() {
$(this).parent().parent().hide();
return false;
});
$('#djDebugWindow').show();

return false;
});

$('#djDebugTemplatePanel a.djTemplateShowContext').live('click', function() {
djdt.toggle_arrow($(this).children('.toggleArrow'));
djdt.toggle_content($(this).parent().next());
Expand Down
2 changes: 1 addition & 1 deletion debug_toolbar/static/debug_toolbar/js/toolbar.min.js

Large diffs are not rendered by default.

18 changes: 13 additions & 5 deletions debug_toolbar/templates/debug_toolbar/panels/sql.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,21 @@
{{ query.duration|floatformat:"2" }}
</td>
<td class="actions">

{% if query.params %}
{% if query.is_select %}
<a class="remoteCall" href="/__debug__/sql_select/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}&amp;alias={{ query.alias|urlencode }}">Sel</a>
<a class="remoteCall" href="/__debug__/sql_explain/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}&amp;alias={{ query.alias|urlencode }}">Expl</a>
{% ifequal query.engine 'mysql' %}
<a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}&amp;alias={{ query.alias|urlencode }}">Prof</a>
{% endifequal %}
<form method="post">
{% for field in query.form.hidden_fields %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only the hidden fields? I think {{ query.form }} should suffice.

{{ field }}
{% endfor %}

<button formaction="/__debug__/sql_select/" class="remoteCall">Sel</button>
<button formaction="/__debug__/sql_explain/" class="remoteCall">Expl</button>

{% ifequal query.engine 'mysql' %}
<button formaction="/__debug__/sql_profile/" class="remoteCall">Prof</button>
{% endifequal %}
</form>
{% endif %}
{% endif %}
</td>
Expand Down
10 changes: 5 additions & 5 deletions debug_toolbar/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

_PREFIX = '__debug__'

urlpatterns = patterns('',
url(r'^%s/sql_select/$' % _PREFIX, 'debug_toolbar.views.sql_select', name='sql_select'),
url(r'^%s/sql_explain/$' % _PREFIX, 'debug_toolbar.views.sql_explain', name='sql_explain'),
url(r'^%s/sql_profile/$' % _PREFIX, 'debug_toolbar.views.sql_profile', name='sql_profile'),
url(r'^%s/template_source/$' % _PREFIX, 'debug_toolbar.views.template_source', name='template_source'),
urlpatterns = patterns('debug_toolbar.views',
url(r'^%s/sql_select/$' % _PREFIX, 'sql_select', name='sql_select'),
url(r'^%s/sql_explain/$' % _PREFIX, 'sql_explain', name='sql_explain'),
url(r'^%s/sql_profile/$' % _PREFIX, 'sql_profile', name='sql_profile'),
url(r'^%s/template_source/$' % _PREFIX, 'template_source', name='template_source'),
)
3 changes: 0 additions & 3 deletions debug_toolbar/utils/tracking/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,6 @@ def execute(self, sql, params=()):
'duration': duration,
'raw_sql': sql,
'params': _params,
'hash': sha1(settings.SECRET_KEY \
+ smart_str(sql) \
+ _params).hexdigest(),
'stacktrace': stacktrace,
'start_time': start,
'stop_time': stop,
Expand Down
Loading