Download the PHP package amphp/http-server without Composer
On this page you can find all versions of the php package amphp/http-server. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download amphp/http-server
More information about amphp/http-server
Files in amphp/http-server
Package http-server
Short Description A non-blocking HTTP application server for PHP based on Amp.
License MIT
Homepage https://github.com/amphp/http-server
Informations about the package http-server
amphp/http-server
AMPHP is a collection of event-driven libraries for PHP designed with fibers and concurrency in mind. This package provides a non-blocking, concurrent HTTP/1.1 and HTTP/2 application server for PHP based on Revolt. Several features are provided in separate packages, such as the WebSocket component.
Features
- Static file serving
- WebSockets
- Dynamic app endpoint routing
- Request body parser
- Sessions
- Full TLS support
- Customizable GZIP compression
- Supports HTTP/1.1 and HTTP/2
- Middleware hooks
- CORS (3rd party)
Requirements
- PHP 8.1+
Installation
This package can be installed as a Composer dependency.
Additionally, you might want to install the nghttp2
library to take advantage of FFI to speed up and reduce the memory usage.
Usage
This library provides access to your application through the HTTP protocol, accepting client requests and forwarding those requests to handlers defined by your application which will return a response.
Incoming requests are represented by Request
objects. A request is provided to an implementor of RequestHandler
, which defines a handleRequest()
method returning an instance of Response
.
Request handlers are covered in greater detail in the RequestHandler
section.
This HTTP server is built on top of the Revolt event-loop and the non-blocking concurrency framework Amp. Thus it inherits full support of all their primitives and it is possible to use all the non-blocking libraries built on top of Revolt.
Note In general, you should make yourself familiar with the
Future
concept, with coroutines, and be aware of the several combinator functions to really succeed at using the HTTP server.
Blocking I/O
Nearly every built-in function of PHP is doing blocking I/O, that means, the executing thread (mostly equivalent to the process in the case of PHP) will effectively be halted until the response is received.
A few examples of such functions: mysqli_query
, file_get_contents
, usleep
and many more.
A good rule of thumb is: Every built-in PHP function doing I/O is doing it in a blocking way, unless you know for sure it doesn't.
There are libraries providing implementations that use non-blocking I/O. You should use these instead of the built-in functions.
We cover the most common I/O needs, such as network sockets, file access, HTTP requests and websockets, MySQL and Postgres database clients, and Redis. If using blocking I/O or long computations are necessary to fulfill a request, consider using the Parallel library to run that code in a separate process or thread.
Warning Do not use any blocking I/O functions in the HTTP server.
Creating an HTTP Server
Your application will be served by an instance of HttpServer
. This library provides SocketHttpServer
, which will be suitable for most applications, built on components found in this library and in amphp/socket
.
To create an instance of SocketHttpServer
and listen for requests, minimally four things are required:
- an instance of
RequestHandler
to respond to incoming requests, - an instance of
ErrorHander
to provide responses to invalid requests, - an instance of
Psr\Log\LoggerInterface
, and - at least one host+port on which to listen for connections.
The above example creates a simple server which sends a plain-text response to every request received.
SocketHttpServer
provides two static constructors for common use-cases in addition to the normal constructor for more advanced and custom uses.
SocketHttpServer::createForDirectAccess()
: Used in the example above, this creates an HTTP application server suitable for direct network access. Adjustable limits are imposed on connections per IP, total connections, and concurrent requests (10, 1000, and 1000 by default, respectively). Response compression may be toggled on or off (on by default) and request methods are limited to a known set of HTTP verbs by default.SocketHttpServer::createForBehindProxy()
: Creates a server appropriate for use when behind a proxy service such as nginx. This static constructor requires a list of trusted proxy IPs (with optional subnet masks) and an enum case ofForwardedHeaderType
(corresponding to eitherForwarded
orX-Forwarded-For
) to parse the original client IP from request headers. No limits are imposed on the number of connections to the server, however the number of concurrent requests are limited (1000 by default, adjustable or can be removed). Response compression may be toggled on or off (on by default). Request methods are limited to a known set of HTTP verbs by default.
If neither of these methods serve your application needs, the SocketHttpServer
constructor may be used directly. This provides an enormous amount of flexibility in how incoming connections client connections are created and handled, but will require more code to create. The constructor requires the user to pass an instance of SocketServerFactory
, used to create client Socket
instances (both components of the amphp/socket
library), and an instance of ClientFactory
, which appropriately creates Client
instances which are attached to each Request
made by the client.
RequestHandler
Incoming requests are represented by Request
objects. A request is provided to an implementor of RequestHandler
, which defines a handleRequest()
method returning an instance of Response
.
Each client request (i.e., call to RequestHandler::handleRequest()
) is executed within a separate coroutine so requests are automatically handled cooperatively within the server process. When a request handler waits on Amp\async()
to execute multiple tasks for a single request.
Usually a RequestHandler
directly generates a response, but it might also delegate to another RequestHandler
.
An example for such a delegating RequestHandler
is the Router
.
The RequestHandler
interface is meant to be implemented by custom classes.
For very simple use cases or quick mocking, you can use CallableRequestHandler
, which can wrap any callable
and accepting a Request
and returning a Response
.
Middleware
Middleware allows pre-processing of requests and post-processing of responses.
Apart from that, a middleware can also intercept the request processing and return a response without delegating to the passed request handler.
Classes have to implement the Middleware
interface for that.
Note Middleware generally follows other words like soft- and hardware with its plural. However, we use the term middlewares to refer to multiple objects implementing the
Middleware
interface.
handleRequest
is the only method of the Middleware
interface. If the Middleware
doesn't handle the request itself, it should delegate the response creation to the received RequestHandler
.
Multiple middlewares can be stacked by using Amp\Http\Server\Middleware\stackMiddleware()
, which accepts a RequestHandler
as first argument and a variable number of Middleware
instances. The returned RequestHandler
will invoke each middleware in the provided order.
ErrorHandler
An ErrorHander
is used by the HTTP server when a malformed or otherwise invalid request is received. The Request
object is provided if one constructed from the incoming data, but may not always be set.
This library provides DefaultErrorHandler
which returns a stylized HTML page as the response body. You may wish to provide a different implementation for your application, potentially using multiple in conjunction with a router.
Request
Constructor
It is rare you will need to construct a Request
object yourself, as they will typically be provided to RequestHandler::handleRequest()
by the server.
Methods
Returns the Сlient
sending the request
Returns the HTTP method used to make this request, e.g. "GET"
.
Sets the request HTTP method.
Returns the request URI
.
Sets a new URI
for the request.
Returns the HTTP protocol version as a string (e.g. "1.0", "1.1", "2").
Sets a new protocol version number for the request.
Returns the headers as a string-indexed array of arrays of strings or an empty array if no headers have been set.
Checks if given header exists.
Returns the array of values for the given header or an empty array if the header does not exist.
Returns the value of the given header.
If multiple headers are present for the named header, only the first header value will be returned.
Use getHeaderArray()
to return an array of all values for the particular header.
Returns null
if the header does not exist.
Sets the headers from the given array.
Sets the header to the given value(s). All previous header lines with the given name will be replaced.
Adds an additional header line with the given name.
Removes the given header if it exists. If multiple header lines with the same name exist, all of them are removed.
Returns the request body. The InputStream
.
Sets the stream for the message body
Note Using a string will automatically set the
Content-Length
header to the length of the given string. Setting anReadableStream
will remove theContent-Length
header. If you know the exact content length of your stream, you can add acontent-length
header after callingsetBody()
.
Returns all cookies in associative map of cookie name to RequestCookie
.
Gets a cookie value by name or null
.
Adds a Cookie
to the request.
Removes a cookie from the request.
Returns an array of all the attributes stored in the request's mutable local storage.
Removes all request attributes from the request's mutable local storage.
Check whether an attribute with the given name exists in the request's mutable local storage.
Retrieve a variable from the request's mutable local storage.
Note Name of the attribute should be namespaced with a vendor and package namespace, like classes.
Assign a variable to the request's mutable local storage.
Note Name of the attribute should be namespaced with a vendor and package namespace, like classes.
Removes a variable from the request's mutable local storage.
Allows access to the Trailers
of a request.
Assigns the Trailers
object to be used in the request.
Request Clients
Client-related details are bundled into Amp\Http\Server\Driver\Client
objects returned from Request::getClient()
. The Client
interface provides methods to retrieve the remote and local socket addresses and TLS info (if applicable).
Response
The Response
class represents an HTTP response. A Response
is returned by middleware.
Constructor
Destructor
Invokes dispose handlers (i.e. functions that registered via onDispose()
method).
Note Uncaught exceptions from the dispose handlers will be forwarded to the event loop error handler.
Methods
Returns the stream for the message body.
Sets the stream for the message body.
Note Using a string will automatically set the
Content-Length
header to the length of the given string. Setting anReadableStream
will remove theContent-Length
header. If you know the exact content length of your stream, you can add acontent-length
header after callingsetBody()
.
Returns the headers as a string-indexed array of arrays of strings or an empty array if no headers have been set.
Checks if given header exists.
Returns the array of values for the given header or an empty array if the header does not exist.
Returns the value of the given header.
If multiple headers are present for the named header, only the first header value will be returned.
Use getHeaderArray()
to return an array of all values for the particular header.
Returns null
if the header does not exist.
Sets the headers from the given array.
Sets the header to the given value(s). All previous header lines with the given name will be replaced.
Adds an additional header line with the given name.
Removes the given header if it exists. If multiple header lines with the same name exist, all of them are removed.
Returns the response status code.
Returns the reason phrase describing the status code.
Sets the numeric HTTP status code (between 100 and 599) and reason phrase. Use null for the reason phrase to use the default phrase associated with the status code.
Returns all cookies in an associative map of cookie name to ResponseCookie
.
Gets a cookie value by name or null
if no cookie with that name is present.
Adds a cookie to the response.
Removes a cookie from the response.
Returns list of push resources in an associative map of URL strings to Push
objects.
Indicate resources which a client likely needs to fetch. (e.g. Link: preload
or HTTP/2 Server Push).
Returns true
if a detach callback has been set, false
if none.
Sets a callback to be invoked once the response has been written to the client and changes the status of the response to 101 Switching Protocols
. The callback receives an instance of Driver\UpgradedSocket
, the Request
which initiated the upgrade, and this Response
.
The callback may be removed by changing the status to something other than 101.
Returns the upgrade function if present.
Registers a function that is invoked when the Response is discarded. A response is discarded either once it has been written to the client or if it gets replaced in a middleware chain.
Allows access to the Trailers
of a response.
Assigns the Trailers
object to be used in the response. Trailers are sent once the entire response body has been set to the client.
Body
RequestBody
, returned from Request::getBody()
, provides buffered and streamed access to the request body.
Use the streamed access to handle large messages, which is particularly important if you have larger message limits (like tens of megabytes) and don't want to buffer it all in memory.
If multiple people are uploading large bodies concurrently, the memory might quickly get exhausted.
Hence, incremental handling is important, accessible via the read()
API of Amp\ByteStream\ReadableStream
.
In case a client disconnects, the read()
fails with an Amp\Http\Server\ClientException
.
This exception is thrown for both the read()
and buffer()
API.
Note
ClientException
s do not need to be caught. You may catch them if you want to continue, but don't have to. The Server will silently end the request cycle and discard that exception then.
Instead of setting the generic body limit high, you should consider increasing the body limit only where needed, which is dynamically possible with the increaseSizeLimit()
method on RequestBody
.
Note
RequestBody
itself doesn't provide parsing of form data. You can useamphp/http-server-form-parser
if you need it.
Constructor
Like Request
, it is rare to need to construct a RequestBody
instance as one will be provided as part of the Request
.
Methods
Increases the body size limit dynamically to allow individual request handlers to handle larger request bodies than the default set for the HTTP server.
Trailers
The Trailers
class allows access to the trailers of an HTTP request, accessible via Request::getTrailers()
. null
is returned if trailers are not expected on the request.
Trailers::await()
returns a Future
which is resolved with an HttpMessage
object providing methods to access the trailer headers.
Bottlenecks
The HTTP server won't be the bottleneck. Misconfiguration, use of blocking I/O, or inefficient applications are.
The server is well-optimized and can handle tens of thousands of requests per second on typical hardware while maintaining a high level of concurrency of thousands of clients.
But that performance will decrease drastically with inefficient applications. The server has the nice advantage of classes and handlers being always loaded, so there's no time lost with compilation and initialization.
A common trap is to begin operating on big data with simple string operations, requiring many inefficient big copies. Instead, streaming should be used where possible for larger request and response bodies.
The problem really is CPU cost. Inefficient I/O management (as long as it is non-blocking!) is just delaying individual requests. It is recommended to dispatch simultaneously and eventually bundle multiple independent I/O requests via Amp's combinators, but a slow handler will slow down every other request too. While one handler is computing, all the other handlers can't continue. Thus it is imperative to reduce computation times of the handlers to a minimum.
Examples
Several examples can be found in the ./examples
directory of the repository.
These can be executed as normal PHP scripts on the command line.
You can then access the example server at http://localhost:1337/
in your browser.
Security
If you discover any security related issues, please use the private security issue reporter instead of using the public issue tracker.
License
The MIT License (MIT). Please see LICENSE for more information.
All versions of http-server with dependencies
amphp/amp Version ^3
amphp/byte-stream Version ^2
amphp/cache Version ^2
amphp/hpack Version ^3
amphp/http Version ^2
amphp/pipeline Version ^1
amphp/socket Version ^2.1
amphp/sync Version ^2.2
league/uri Version ^7.1
league/uri-interfaces Version ^7.1
psr/http-message Version ^1 | ^2
psr/log Version ^1 | ^2 | ^3
revolt/event-loop Version ^1