mirror of
https://github.com/shlinkio/shlink.git
synced 2024-12-22 23:23:42 -06:00
Ensure query parameters are preserved verbatim when forwarded to long URL
This commit is contained in:
parent
a8e4b2fceb
commit
1773e6ecae
@ -18,7 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
|||||||
* *Nothing*
|
* *Nothing*
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* *Nothing*
|
* [#2213](https://github.com/shlinkio/shlink/issues/2213) Fix spaces being replaced with underscores in query parameter names, when forwarded from short URL to long URL.
|
||||||
|
|
||||||
|
|
||||||
## [4.2.1] - 2024-10-04
|
## [4.2.1] - 2024-10-04
|
||||||
|
@ -7,7 +7,7 @@ return [
|
|||||||
'rabbitmq' => [
|
'rabbitmq' => [
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'host' => 'shlink_rabbitmq',
|
'host' => 'shlink_rabbitmq',
|
||||||
'port' => '5672',
|
'port' => 5672,
|
||||||
'user' => 'rabbit',
|
'user' => 'rabbit',
|
||||||
'password' => 'rabbit',
|
'password' => 'rabbit',
|
||||||
],
|
],
|
||||||
|
@ -28,11 +28,15 @@ readonly class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderI
|
|||||||
?string $extraPath = null,
|
?string $extraPath = null,
|
||||||
): string {
|
): string {
|
||||||
$uri = new Uri($this->redirectionResolver->resolveLongUrl($shortUrl, $request));
|
$uri = new Uri($this->redirectionResolver->resolveLongUrl($shortUrl, $request));
|
||||||
$currentQuery = $request->getQueryParams();
|
|
||||||
$shouldForwardQuery = $shortUrl->forwardQuery();
|
$shouldForwardQuery = $shortUrl->forwardQuery();
|
||||||
$baseQueryString = $uri->getQuery();
|
$baseQueryString = $uri->getQuery();
|
||||||
$basePath = $uri->getPath();
|
$basePath = $uri->getPath();
|
||||||
|
|
||||||
|
// Get current query by manually parsing query string, instead of using $request->getQueryParams().
|
||||||
|
// That prevents some weird PHP logic in which some characters in param names are converted to ensure resulting
|
||||||
|
// names are valid variable names.
|
||||||
|
$currentQuery = Query::parse($request->getUri()->getQuery());
|
||||||
|
|
||||||
return $uri
|
return $uri
|
||||||
->withQuery($shouldForwardQuery ? $this->resolveQuery($baseQueryString, $currentQuery) : $baseQueryString)
|
->withQuery($shouldForwardQuery ? $this->resolveQuery($baseQueryString, $currentQuery) : $baseQueryString)
|
||||||
->withPath($this->resolvePath($basePath, $extraPath))
|
->withPath($this->resolvePath($basePath, $extraPath))
|
||||||
|
@ -122,4 +122,22 @@ class RedirectTest extends ApiTestCase
|
|||||||
self::assertEquals(302, $response->getStatusCode());
|
self::assertEquals(302, $response->getStatusCode());
|
||||||
self::assertEquals($longUrl, $response->getHeaderLine('Location'));
|
self::assertEquals($longUrl, $response->getHeaderLine('Location'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function queryParametersAreProperlyForwarded(): void
|
||||||
|
{
|
||||||
|
$slug = 'forward-query-params';
|
||||||
|
$this->callApiWithKey('POST', '/short-urls', [
|
||||||
|
RequestOptions::JSON => [
|
||||||
|
'longUrl' => 'https://example.com',
|
||||||
|
'customSlug' => $slug,
|
||||||
|
'forwardQuery' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->callShortUrl($slug, [RequestOptions::QUERY => ['foo bar' => '123']]);
|
||||||
|
|
||||||
|
self::assertEquals(302, $response->getStatusCode());
|
||||||
|
self::assertEquals('https://example.com?foo%20bar=123', $response->getHeaderLine('Location'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace ShlinkioTest\Shlink\Core\ShortUrl\Helper;
|
namespace ShlinkioTest\Shlink\Core\ShortUrl\Helper;
|
||||||
|
|
||||||
use Laminas\Diactoros\ServerRequestFactory;
|
use Laminas\Diactoros\ServerRequestFactory;
|
||||||
|
use Laminas\Diactoros\Uri;
|
||||||
use PHPUnit\Framework\Attributes\DataProvider;
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use PHPUnit\Framework\Attributes\TestWith;
|
use PHPUnit\Framework\Attributes\TestWith;
|
||||||
@ -53,62 +54,70 @@ class ShortUrlRedirectionBuilderTest extends TestCase
|
|||||||
|
|
||||||
public static function provideData(): iterable
|
public static function provideData(): iterable
|
||||||
{
|
{
|
||||||
$request = static fn (array $query = []) => ServerRequestFactory::fromGlobals()->withQueryParams($query);
|
$request = static fn (string $query = '') => ServerRequestFactory::fromGlobals()->withUri(
|
||||||
|
(new Uri())->withQuery($query),
|
||||||
|
);
|
||||||
|
|
||||||
yield ['https://example.com/foo/bar?some=thing', $request(), null, true];
|
yield ['https://example.com/foo/bar?some=thing', $request(), null, true];
|
||||||
yield ['https://example.com/foo/bar?some=thing', $request(), null, null];
|
yield ['https://example.com/foo/bar?some=thing', $request(), null, null];
|
||||||
yield ['https://example.com/foo/bar?some=thing', $request(), null, false];
|
yield ['https://example.com/foo/bar?some=thing', $request(), null, false];
|
||||||
yield ['https://example.com/foo/bar?some=thing&else', $request(['else' => null]), null, true];
|
yield ['https://example.com/foo/bar?some=thing&else', $request('else'), null, true];
|
||||||
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request(['foo' => 'bar']), null, true];
|
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request('foo=bar'), null, true];
|
||||||
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request(['foo' => 'bar']), null, null];
|
yield ['https://example.com/foo/bar?some=thing&foo=bar', $request('foo=bar'), null, null];
|
||||||
yield ['https://example.com/foo/bar?some=thing', $request(['foo' => 'bar']), null, false];
|
yield ['https://example.com/foo/bar?some=thing', $request('foo=bar'), null, false];
|
||||||
yield ['https://example.com/foo/bar?some=thing&123=foo', $request(['123' => 'foo']), null, true];
|
yield ['https://example.com/foo/bar?some=thing&123=foo', $request('123=foo'), null, true];
|
||||||
yield ['https://example.com/foo/bar?some=thing&456=foo', $request([456 => 'foo']), null, true];
|
yield ['https://example.com/foo/bar?some=thing&456=foo', $request('456=foo'), null, true];
|
||||||
yield ['https://example.com/foo/bar?some=thing&456=foo', $request([456 => 'foo']), null, null];
|
yield ['https://example.com/foo/bar?some=thing&456=foo', $request('456=foo'), null, null];
|
||||||
yield ['https://example.com/foo/bar?some=thing', $request([456 => 'foo']), null, false];
|
yield ['https://example.com/foo/bar?some=thing', $request('456=foo'), null, false];
|
||||||
yield [
|
yield [
|
||||||
'https://example.com/foo/bar?some=overwritten&foo=bar',
|
'https://example.com/foo/bar?some=overwritten&foo=bar',
|
||||||
$request(['foo' => 'bar', 'some' => 'overwritten']),
|
$request('foo=bar&some=overwritten'),
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
];
|
];
|
||||||
yield [
|
yield [
|
||||||
'https://example.com/foo/bar?some=overwritten',
|
'https://example.com/foo/bar?some=overwritten',
|
||||||
$request(['foobar' => 'notrack', 'some' => 'overwritten']),
|
$request('foobar=notrack&some=overwritten'),
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
];
|
];
|
||||||
yield [
|
yield [
|
||||||
'https://example.com/foo/bar?some=overwritten',
|
'https://example.com/foo/bar?some=overwritten',
|
||||||
$request(['foobar' => 'notrack', 'some' => 'overwritten']),
|
$request('foobar=notrack&some=overwritten'),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
];
|
];
|
||||||
yield [
|
yield [
|
||||||
'https://example.com/foo/bar?some=thing',
|
'https://example.com/foo/bar?some=thing',
|
||||||
$request(['foobar' => 'notrack', 'some' => 'overwritten']),
|
$request('foobar=notrack&some=overwritten'),
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
];
|
];
|
||||||
yield ['https://example.com/foo/bar/something/else-baz?some=thing', $request(), '/something/else-baz', true];
|
yield ['https://example.com/foo/bar/something/else-baz?some=thing', $request(), '/something/else-baz', true];
|
||||||
yield [
|
yield [
|
||||||
'https://example.com/foo/bar/something/else-baz?some=thing&hello=world',
|
'https://example.com/foo/bar/something/else-baz?some=thing&hello=world',
|
||||||
$request(['hello' => 'world']),
|
$request('hello=world',),
|
||||||
'/something/else-baz',
|
'/something/else-baz',
|
||||||
true,
|
true,
|
||||||
];
|
];
|
||||||
yield [
|
yield [
|
||||||
'https://example.com/foo/bar/something/else-baz?some=thing&hello=world',
|
'https://example.com/foo/bar/something/else-baz?some=thing&hello=world',
|
||||||
$request(['hello' => 'world']),
|
$request('hello=world',),
|
||||||
'/something/else-baz',
|
'/something/else-baz',
|
||||||
null,
|
null,
|
||||||
];
|
];
|
||||||
yield [
|
yield [
|
||||||
'https://example.com/foo/bar/something/else-baz?some=thing',
|
'https://example.com/foo/bar/something/else-baz?some=thing',
|
||||||
$request(['hello' => 'world']),
|
$request('hello=world',),
|
||||||
'/something/else-baz',
|
'/something/else-baz',
|
||||||
false,
|
false,
|
||||||
];
|
];
|
||||||
|
yield [
|
||||||
|
'https://example.com/foo/bar/something/else-baz?some=thing¶meter%20with%20spaces=world',
|
||||||
|
$request('parameter with spaces=world',),
|
||||||
|
'/something/else-baz',
|
||||||
|
true,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user