Fix http/s protocol headers

Improve request ids
This commit is contained in:
René Preuß
2021-01-01 16:06:59 +01:00
parent f6d04777e1
commit 6f72d719bf
5 changed files with 35 additions and 26 deletions

View File

@@ -45,7 +45,7 @@ class Client
$sharedUrl = $this->prepareSharedUrl($sharedUrl); $sharedUrl = $this->prepareSharedUrl($sharedUrl);
foreach ($subdomains as $subdomain) { foreach ($subdomains as $subdomain) {
$this->connectToServer($sharedUrl, $subdomain, $this->configuration->auth()); $this->connectToServerAndShareHttp($sharedUrl, $subdomain, $this->configuration->auth());
} }
} }
@@ -61,18 +61,13 @@ class Client
} }
$url = Arr::get($parsedUrl, 'host', Arr::get($parsedUrl, 'path')); $url = Arr::get($parsedUrl, 'host', Arr::get($parsedUrl, 'path'));
$scheme = Arr::get($parsedUrl, 'scheme');
$port = Arr::get($parsedUrl, 'port', $scheme === 'https' ? 443 : 80);
if (Arr::get($parsedUrl, 'scheme') === 'https') { return sprintf('%s:%s:%s', $scheme, $url, $port);
$url .= ':443';
}
if (! is_null($port = Arr::get($parsedUrl, 'port'))) {
$url .= ":{$port}";
}
return $url;
} }
public function connectToServer(string $sharedUrl, $subdomain, $authToken = ''): PromiseInterface public function connectToServerAndShareHttp(string $sharedUrl, $subdomain, $authToken = ''): PromiseInterface
{ {
$deferred = new Deferred(); $deferred = new Deferred();
$promise = $deferred->promise(); $promise = $deferred->promise();
@@ -93,7 +88,7 @@ class Client
$this->logger->error('Connection to server closed.'); $this->logger->error('Connection to server closed.');
$this->retryConnectionOrExit(function () use ($sharedUrl, $subdomain, $authToken) { $this->retryConnectionOrExit(function () use ($sharedUrl, $subdomain, $authToken) {
$this->connectToServer($sharedUrl, $subdomain, $authToken); $this->connectToServerAndShareHttp($sharedUrl, $subdomain, $authToken);
}); });
}); });
@@ -126,7 +121,7 @@ class Client
}, function (\Exception $e) use ($deferred, $sharedUrl, $subdomain, $authToken) { }, function (\Exception $e) use ($deferred, $sharedUrl, $subdomain, $authToken) {
if ($this->connectionRetries > 0) { if ($this->connectionRetries > 0) {
$this->retryConnectionOrExit(function () use ($sharedUrl, $subdomain, $authToken) { $this->retryConnectionOrExit(function () use ($sharedUrl, $subdomain, $authToken) {
$this->connectToServer($sharedUrl, $subdomain, $authToken); $this->connectToServerAndShareHttp($sharedUrl, $subdomain, $authToken);
}); });
return; return;

View File

@@ -6,6 +6,8 @@ use App\Client\Configuration;
use App\Client\Http\Modifiers\CheckBasicAuthentication; use App\Client\Http\Modifiers\CheckBasicAuthentication;
use App\Logger\RequestLogger; use App\Logger\RequestLogger;
use Clue\React\Buzz\Browser; use Clue\React\Buzz\Browser;
use Psr\Http\Message\UriInterface;
use React\Stream\ReadableStreamInterface;
use function GuzzleHttp\Psr7\parse_request; use function GuzzleHttp\Psr7\parse_request;
use function GuzzleHttp\Psr7\str; use function GuzzleHttp\Psr7\str;
use Laminas\Http\Request; use Laminas\Http\Request;
@@ -84,22 +86,19 @@ class HttpClient
protected function sendRequestToApplication(RequestInterface $request, $proxyConnection = null) protected function sendRequestToApplication(RequestInterface $request, $proxyConnection = null)
{ {
(new Browser($this->loop, $this->createConnector())) (new Browser($this->loop, $this->createConnector()))
->withOptions([ ->withFollowRedirects(false)
'followRedirects' => false, ->withRejectErrorResponse(false)
'obeySuccessCode' => false, ->requestStreaming($request->getMethod(), $this->getExposeUri($request), $request->getHeaders(), $request->getBody())
'streaming' => true,
])
->send($request)
->then(function (ResponseInterface $response) use ($proxyConnection) { ->then(function (ResponseInterface $response) use ($proxyConnection) {
if (! isset($response->buffer)) { if (! isset($response->buffer)) {
$response = $this->rewriteResponseHeaders($response); //$response = $this->rewriteResponseHeaders($response);
$response->buffer = str($response); $response->buffer = str($response);
} }
$this->sendChunkToServer($response->buffer, $proxyConnection); $this->sendChunkToServer($response->buffer, $proxyConnection);
/* @var $body \React\Stream\ReadableStreamInterface */ /* @var $body ReadableStreamInterface */
$body = $response->getBody(); $body = $response->getBody();
$this->logResponse(str($response)); $this->logResponse(str($response));
@@ -108,6 +107,7 @@ class HttpClient
$response->buffer .= $chunk; $response->buffer .= $chunk;
$this->sendChunkToServer($chunk, $proxyConnection); $this->sendChunkToServer($chunk, $proxyConnection);
}); });
$body->on('close', function () use ($proxyConnection, $response) { $body->on('close', function () use ($proxyConnection, $response) {
@@ -156,4 +156,15 @@ class HttpClient
return $response->withHeader('Location', $location); return $response->withHeader('Location', $location);
} }
private function getExposeUri(RequestInterface $request): UriInterface
{
$exposeProto = $request->getHeader('x-expose-proto')[0];
$exposeHost = explode(':', $request->getHeader('x-expose-host')[0]);
return $request->getUri()
->withScheme($exposeProto)
->withHost($exposeHost[0])
->withPort($exposeHost[1]);
}
} }

View File

@@ -45,15 +45,13 @@ class ConnectionManager implements ConnectionManagerContract
public function storeConnection(string $host, ?string $subdomain, ConnectionInterface $connection): ControlConnection public function storeConnection(string $host, ?string $subdomain, ConnectionInterface $connection): ControlConnection
{ {
$clientId = (string) uniqid(); $connection->client_id = sha1(uniqid('', true));
$connection->client_id = $clientId;
$storedConnection = new ControlConnection( $storedConnection = new ControlConnection(
$connection, $connection,
$host, $host,
$subdomain ?? $this->subdomainGenerator->generateSubdomain(), $subdomain ?? $this->subdomainGenerator->generateSubdomain(),
$clientId, $connection->client_id,
$this->getAuthTokenFromConnection($connection) $this->getAuthTokenFromConnection($connection)
); );

View File

@@ -3,6 +3,7 @@
namespace App\Server\Connections; namespace App\Server\Connections;
use Evenement\EventEmitterTrait; use Evenement\EventEmitterTrait;
use Illuminate\Support\Str;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
class ControlConnection class ControlConnection

View File

@@ -113,9 +113,13 @@ class TunnelMessageController extends Controller
$host .= ":{$this->configuration->port()}"; $host .= ":{$this->configuration->port()}";
} }
$request->headers->set('Host', $controlConnection->host); $exposeUrl = parse_url($controlConnection->host);
$request->headers->set('Host', "{$controlConnection->subdomain}.{$host}");
$request->headers->set('X-Forwarded-Proto', $request->isSecure() ? 'https' : 'http'); $request->headers->set('X-Forwarded-Proto', $request->isSecure() ? 'https' : 'http');
$request->headers->set('X-Expose-Request-ID', uniqid()); $request->headers->set('X-Expose-Request-ID', sha1(uniqid('', true)));
$request->headers->set('X-Expose-Host', sprintf('%s:%s', $exposeUrl['host'], $exposeUrl['port']));
$request->headers->set('X-Expose-Proto', $exposeUrl['scheme']);
$request->headers->set('Upgrade-Insecure-Requests', 1); $request->headers->set('Upgrade-Insecure-Requests', 1);
$request->headers->set('X-Exposed-By', config('app.name').' '.config('app.version')); $request->headers->set('X-Exposed-By', config('app.name').' '.config('app.version'));
$request->headers->set('X-Original-Host', "{$controlConnection->subdomain}.{$host}"); $request->headers->set('X-Original-Host', "{$controlConnection->subdomain}.{$host}");