Skip to content

Commit

Permalink
#feature: Add proxy support to client
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurkushman committed Dec 6, 2019
1 parent c322974 commit c98ffcc
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 42 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Server:
Client:
- You have the ability to handshake (which is performed automatically) and send messages to server
- Receive a response from the server
- Initiate connection via proxy

## How do I get set up?

Expand Down Expand Up @@ -175,7 +176,10 @@ $config->setTimeout(15);
$config->setHeaders([
'X-Custom-Header' => 'Foo Bar Baz',
]);
$config->setProxy('127.0.0.1', '80', 'proxy_user', 'proxy_pass');

// if proxy settings is of need
$config->setProxy('127.0.0.1', '80');
$config->setProxyAuth('proxyUser', 'proxyPass');

$client = new WebSocketClient('ws:https://localhost:8000/notifications/messanger/yourtoken123', $config);
```
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "arthurkushman/php-wss",
"description": "Web-socket server with URI parse and multi-process and PROXY support",
"description": "Web-socket server with URI parse and multi-process support",
"keywords": [
"web-sockets",
"web socket server",
Expand Down
43 changes: 34 additions & 9 deletions src/Components/ClientConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ClientConfig
private $fragmentSize = WscCommonsContract::DEFAULT_FRAGMENT_SIZE;
private $context;


// proxy settings
private $hasProxy = false;
private $proxyIp;
private $proxyPort;
Expand Down Expand Up @@ -166,7 +166,7 @@ public function getPort(): string
*/
public function setPort(array $urlParts): void
{
$this->port = isset($urlParts['port']) ? $urlParts['port'] : ($this->scheme === 'wss' ? 443 : 80);
$this->port = isset($urlParts['port']) ? $urlParts['port'] : ($this->scheme === 'wss' ? '443' : '80');
}

/**
Expand All @@ -180,37 +180,62 @@ public function getContextOptions(): array
/**
* @param array $contextOptions
*/
public function setContextOptions($contextOptions)
public function setContextOptions($contextOptions): void
{
$this->contextOptions = $contextOptions;
}

public function setProxy($ip, $port, $username = '', $password = '')
/**
* @param string $ip
* @param string $port
*/
public function setProxy(string $ip, string $port): void
{
$this->hasProxy = true;
$this->proxyIp = $ip;
$this->proxyPort = $port;
$this->proxyAuth = ($username && $password) ? base64_encode($username.':'.$password) : null;
}

/**
* Sets auth for proxy
*
* @param string $userName
* @param string $password
*/
public function setProxyAuth(string $userName, string $password): void
{
$this->proxyAuth = (empty($userName) === false && empty($password) === false) ? base64_encode($userName.':'.$password) : null;
}

/**
* @return bool
*/
public function hasProxy() : bool
{
return $this->hasProxy;
}

/**
* @return string|null
*/
public function getProxyIp() : ?string
{
return ($this->hasProxy) ? $this->proxyIp : null;
return $this->proxyIp;
}

/**
* @return string|null
*/
public function getProxyPort() : ?string
{
return ($this->hasProxy) ? $this->proxyPort : null;
return $this->proxyPort;
}

/**
* @return string|null
*/
public function getProxyAuth() : ?string
{
return ($this->hasProxy) ? $this->proxyAuth : null;
return $this->proxyAuth;
}

}
8 changes: 8 additions & 0 deletions src/Components/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ private function getOpType(string $type): array
return $frameHead;
}

/**
* @param array $frameHead
* @param string $payload
* @param int $payloadLength
* @param bool $masked
* @return string
* @throws \Exception
*/
private function getComposedFrame(array $frameHead, string $payload, int $payloadLength, bool $masked)
{
// convert frame-head to string:
Expand Down
10 changes: 5 additions & 5 deletions src/Components/ServerConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function getClientsPerFork(): int
/**
* @param mixed $clientsPerFork
*/
public function setClientsPerFork(int $clientsPerFork)
public function setClientsPerFork(int $clientsPerFork): void
{
$this->clientsPerFork = $clientsPerFork;
}
Expand All @@ -44,7 +44,7 @@ public function getStreamSelectTimeout(): int
/**
* @param mixed $streamSelectTimeout
*/
public function setStreamSelectTimeout(int $streamSelectTimeout)
public function setStreamSelectTimeout(int $streamSelectTimeout): void
{
$this->streamSelectTimeout = $streamSelectTimeout;
}
Expand All @@ -60,7 +60,7 @@ public function getHost(): string
/**
* @param string $host
*/
public function setHost(string $host)
public function setHost(string $host): void
{
$this->host = $host;
}
Expand All @@ -76,7 +76,7 @@ public function getPort(): int
/**
* @param int $port
*/
public function setPort(int $port)
public function setPort(int $port): void
{
$this->port = $port;
}
Expand All @@ -92,7 +92,7 @@ public function isForking(): bool
/**
* @param bool $isForking
*/
public function setForking(bool $isForking)
public function setForking(bool $isForking): void
{
$this->isForking = $isForking;
}
Expand Down
1 change: 1 addition & 0 deletions src/Components/WSClientTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ private function validateResponse(ClientConfig $config, string $pathWithQuery, s
{
$response = stream_get_line($this->socket, self::DEFAULT_RESPONSE_HEADER, "\r\n\r\n");
if (!preg_match(self::SEC_WEBSOCKET_ACCEPT_PTTRN, $response, $matches)) {
print_r($response);
$address = $config->getScheme() . ':https://' . $config->getHost() . ':' . $config->getPort() . $pathWithQuery;
throw new ConnectionException(
"Connection to '{$address}' failed: Server sent invalid upgrade response:\n"
Expand Down
48 changes: 22 additions & 26 deletions src/Components/WscMain.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,8 @@ protected function connect()
$context = $this->getStreamContext();


if ($this->config->hasProxy()){
$this->socket = $this->proxy(
$this->config->getHost(),
$this->config->getProxyIp(),
$this->config->getProxyPort(),
$this->config->getProxyAuth()
);
if ($this->config->hasProxy()) {
$this->socket = $this->proxy($this->config);
} else {
$this->socket = @stream_socket_client(
$hostUri . ':' . $this->config->getPort(),
Expand Down Expand Up @@ -122,32 +117,39 @@ protected function connect()
}


private function proxy($host, $ip, $port, $auth)
/**
* Init a proxy connection
*
* @param ClientConfig $config
* @return bool|resource
* @throws \InvalidArgumentException
* @throws \WSSC\Exceptions\ConnectionException
*/
private function proxy(ClientConfig $config)
{
// Set the stream context options if they're already set in the config
$context = $this->getStreamContext();
$sock = @stream_socket_client(
"tcp:https://$ip:$port",
WscCommonsContract::TCP_SCHEME . $config->getProxyIp() . ':' . $config->getProxyPort(),
$errno,
$errstr,
$this->config->getTimeout(),
STREAM_CLIENT_CONNECT,
$context
$this->getStreamContext()
);

$write = "CONNECT $host HTTP/1.1\r\n";
if ($auth) {
$write .= "Proxy-Authorization: Basic $auth\r\n";
$write = "CONNECT {$config->getHost()} HTTP/1.1\r\n";
$auth = $config->getProxyAuth();
if ($auth !== NULL) {
$write .= "Proxy-Authorization: Basic {$auth}\r\n";
}
$write .= "\r\n";
fwrite($sock, $write);
$rsp = fread($sock, 1024);
$resp = fread($sock, 1024);

if (preg_match('/^HTTP\/\d\.\d 200/', $rsp) == 1) {
if (preg_match('/^HTTP\/\d\.\d 200/', $resp) === 1) {
return $sock;
} else {
throw new \Exception("Failed to connect to the host via proxy");
}

throw new ConnectionException('Failed to connect to the host via proxy');
}


Expand Down Expand Up @@ -315,7 +317,7 @@ public function receive()
$this->hugePayload = '';

$response = null;
while (null === $response) {
while ($response === null) {
$response = $this->receiveFragment();
}

Expand Down Expand Up @@ -433,10 +435,4 @@ private function generateKey(): string

return base64_encode($key);
}


public function __destruct()
{
fclose($this->socket);
}
}
2 changes: 2 additions & 0 deletions src/Contracts/WscCommonsContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
interface WscCommonsContract
{

public const TCP_SCHEME = 'tcp:https://';

public const MAX_BYTES_READ = 65535;
public const DEFAULT_TIMEOUT = 5;
public const DEFAULT_FRAGMENT_SIZE = 4096;
Expand Down

0 comments on commit c98ffcc

Please sign in to comment.