# amphp/socket AMPHP is a collection of event-driven libraries for PHP designed with fibers and concurrency in mind. `amphp/socket` is a library for establishing and encrypting non-blocking sockets. It provides a socket abstraction for clients and servers. It abstracts the really low levels of non-blocking streams in PHP. [![Latest Release](https://img.shields.io/github/release/amphp/socket.svg?style=flat-square)](https://github.com/amphp/socket/releases) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/amphp/socket/blob/master/LICENSE) ## Installation This package can be installed as a [Composer](https://getcomposer.org/) dependency. ```bash composer require amphp/socket ``` ## Requirements `amphp/socket` heavily relies on `amphp/byte-stream`, specifically its `ReadableStream` and `WritableStream` interfaces. ## Connecting to a Server `amphp/socket` allows clients to connect to servers via TCP, UDP, or Unix domain sockets. You can establish a socket connection using `Amp\Socket\connect()`. It will automatically resolve DNS names and retries other IPs if a connection fails and multiple IPs are available. ```php // You can customize connect() options using ConnectContext $connectContext = (new Amp\Socket\ConnectContext) ->withConnectTimeout(5); // You can optionally pass a Cancellation object to cancel a pending connect() operation $deferredCancellation = new Amp\DeferredCancellation(); $socket = connect('amphp.org:80', $connectContext, $deferredCancellation->getCancellation()); ``` ### Encrypted Connections / TLS If you want to connect via TLS, use `Amp\Socket\connectTls()` instead or call `$socket->setupTls()` on the returned socket. ### Handling Connections `Socket` implements `ReadableStream` and `WritableStream`, so everything from [`amphp/byte-stream`](https://v3.amphp.org/byte-stream) applies for receiving and sending data. ```php #!/usr/bin/env php write('Usage: examples/simple-http-client.php ' . PHP_EOL); exit(1); } $uri = Http::createFromString($argv[1]); $host = $uri->getHost(); $port = $uri->getPort() ?? ($uri->getScheme() === 'https' ? 443 : 80); $path = $uri->getPath() ?: '/'; $connectContext = (new ConnectContext) ->withTlsContext(new ClientTlsContext($host)); $socket = $uri->getScheme() === 'http' ? connect($host . ':' . $port, $connectContext) : connectTls($host . ':' . $port, $connectContext); $socket->write("GET {$path} HTTP/1.1\r\nHost: $host\r\nConnection: close\r\n\r\n"); ByteStream\pipe($socket, $stdout); ``` ## Server `amphp/socket` allows listening for incoming TCP connections as well as connections via Unix domain sockets. It defaults to secure TLS settings if you decide to enable TLS. ### Listening and Accepting Connections Use `Amp\Socket\Socket\listen()` to listen on a port or unix domain socket. It's a wrapper around `stream_socket_server` that gives useful error message on failures via exceptions. Once you're listening, accept clients using `Server::accept()`. It returns a `Socket` that returns once a new client has been accepted. It's usually called within a `while` loop: ```php $server = Socket\listen("tcp://127.0.0.1:1337"); while ($client = $server->accept()) { // You shouldn't spend too much time here, because that blocks accepting another client, so we use async(): async(function () use ($client) { // Handle client connection here }); } ``` ### Handling Connections `Socket` implements `ReadableStream` and `WritableStream`, so everything from [`amphp/byte-stream`](https://v3.amphp.org/byte-stream) applies for receiving and sending data. It's best to handle clients in their own coroutine, while letting the server accept all clients as soon as there are new clients. ```php #!/usr/bin/env php getAddress() . ' ...' . PHP_EOL; echo 'Open your browser and visit http://' . $server->getAddress() . '/' . PHP_EOL; while ($socket = $server->accept()) { async(function () use ($socket) { $address = $socket->getRemoteAddress(); $ip = $address->getHost(); $port = $address->getPort(); echo "Accepted connection from {$address}." . PHP_EOL; $body = "Hey, your IP is {$ip} and your local port used is {$port}."; $bodyLength = \strlen($body); $socket->write("HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: {$bodyLength}\r\n\r\n{$body}"); $socket->end(); }); } ``` ### Closing Connections Once you're done with a client, close the connection using `Socket::close()`. If you want to wait for all data to be successfully written before closing the connection, use `Socket::end()`. See above for an example. ### Server Address Sometimes you don't know the address the server is listening on, e.g. because you listed to `tcp://127.0.0.1:0`, which assigns a random free port. You can use `Server::getAddress()` to get the address the server is bound to. ### Server Shutdown Once you're done with the server socket, close the socket. That means, the server won't listen on the specified location anymore. Use `Server::close()` to close the server socket. ### Encrypted Connections / TLS As already mentioned in the documentation for `Amp\Socket\Socket\listen()`, you need to enable TLS manually after accepting connections. For a TLS server socket, you listen on the `tcp://` protocol on a specified address. After accepting clients, call `$socket->setupTls()` where `$socket` is the socket returned from `SocketServer::accept()`. > **Warning** > Any data transmitted before `Socket::setupTls()` completes will be transmitted in clear text. > Don't attempt to read from the socket or write to it manually. > Doing so will read the raw TLS handshake data that's supposed to be read by OpenSSL. #### Self-Signed Certificates There's no option to allow self-signed certificates in `ClientTlsContext` since it is no more secure than disabling peer verification. To safely use a self-signed certificate, disable peer verification and require fingerprint verification of the certificate using `ClientTlsContext::withPeerFingerprint()`. ## Security If you discover any security related issues, please email [`me@kelunik.com`](mailto:me@kelunik.com) instead of using the issue tracker. ## License The MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information.