Skip to content

Commit

Permalink
Makes loop-detection threshold configurable.
Browse files Browse the repository at this point in the history
  • Loading branch information
jhedstrom committed May 1, 2015
1 parent 79c0141 commit 60e2af5
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 29 deletions.
1 change: 1 addition & 0 deletions config/install/redirect.settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ global_clean: true
global_admin_paths: false
global_home: true
global_deslash: false
loop_threshold: 5
3 changes: 3 additions & 0 deletions config/schema/redirect.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ redirect.settings:
global_deslash:
type: boolean
label: 'Remove trailing slashes from paths.'
loop_threshold:
type: integer
label: 'Number of redirects in a 15-second period that will determine a redirect loop.'
9 changes: 8 additions & 1 deletion src/Form/RedirectSettingsForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ public function buildForm(array $form, FormStateInterface $form_state) {
'#options' => redirect_status_code_options(),
'#default_value' => $config->get('default_status_code'),
);
$form['redirect_loop_threshold'] = [
'#type' => 'select',
'#title' => $this->t('Loop detection threshold'),
'#description' => $this->t('Number of repeated redirects from a single user in a 15-second time period that will cause a redirect loop to be detected.'),
'#options' => [0 => $this->t('Disable loop detection'), 5 => 5, 10 => 10, 15 => 15],
'#default_value' => $config->get('loop_threshold'),
];

$form['globals'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Global redirects'),
Expand Down Expand Up @@ -102,7 +110,6 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
}
}
$config->save();
redirect_page_cache_clear();
drupal_set_message(t('Configuration was saved.'));
}

Expand Down
10 changes: 7 additions & 3 deletions src/RedirectChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,17 @@ public function canRedirect(Request $request) {
* TRUE if a redirect loop has been identified.
*/
public function isLoop(Request $request) {
$hash = Crypt::hashBase64('redirection' . $request->getRequestUri());
if ($this->flood->isAllowed($hash, 5, 15)) {
$this->flood->register($hash, 60);
if (!$this->config->get('loop_threshold')) {
// Loop detection is disabled.
return FALSE;
}
if ($this->flood->isAllowed('redirection', $this->config->get('loop_threshold'), 15)) {
$this->flood->register('redirection', 60);
return FALSE;
}
else {
return TRUE;
}
}

}
45 changes: 20 additions & 25 deletions src/Tests/RedirectUITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,39 +306,34 @@ function testRedirectLoop() {
$log = reset($log);
$this->assertEqual($log->severity, RfcLogLevel::WARNING);
$this->assertEqual(SafeMarkup::format($log->message, unserialize($log->variables)),
SafeMarkup::format('Redirect loop identified at %path for redirect %id', array('%path' => '/node', '%id' => $redirect1->id())));
}

// Test flood protection.
// Create 20 redirects.
$redirects = [];
for ($i = 0; $i < 20; $i++) {
$redirect = $this->storage->create();
$redirect->setSource('test-redirect' . $i);
$redirect->setRedirect('node');
$redirect->setStatusCode(301);
$redirect->save();
$redirects[$i] = $redirect;
SafeMarkup::format('Redirect loop identified at %path for redirect %id', array('%path' => '/admin', '%id' => $redirect2->id())));
}
}

// Hit the first redirect 10 times, should result in a loop.
for ($i = 0; $i < 10; $i++) {
// Should detect a redirect after 4.
if ($i > 4) {
$this->drupalGet('test-redirect0');
/**
* Test loop detection threshold.
*/
public function testLoopThreshold() {
$redirect = $this->storage->create();
$redirect->setSource('test-redirect');
$redirect->setRedirect('node');
$redirect->setStatusCode(301);
$redirect->save();

// Set loop threshold to 3.
\Drupal::configFactory()->getEditable('redirect.settings')->set('loop_threshold', 3)->save();
// Hit the first redirect 5 times, should result in a loop.
for ($i = 0; $i < 5; $i++) {
// Should detect a redirect loop after 3.
if ($i > 2) {
$this->drupalGet('test-redirect');
$this->assertText('Service unavailable');
$this->assertResponse(503);
}
else {
$this->assertRedirect('test-redirect0', 'node');
$this->assertRedirect('test-redirect', 'node');
}
}
unset($redirects[0]);

// Hitting each different redirect should work.
foreach ($redirects as $i => $redirect) {
$this->assertRedirect('test-redirect' . $i, 'node');
}
}

/**
Expand Down

0 comments on commit 60e2af5

Please sign in to comment.