Httpsocket improvements - markstory/cakephp GitHub Wiki
Problems
- Code is crazy
- No multipart file uploads
- Handcoded HTTP parser & serializer.
Proposal
- Split HttpSocket into more smaller pieces.
- Share HTTPMessage code with the rest of the framework.
- Use PHP's stream API to remove need to manually parse/serialize HTTP responses/requests.
Class descriptions
The existing HttpSocket class has a number of responsibilities that can be decomposed into smaller objects:
- Cake\Network\Http\Client - End user interface, replaces HttpSocket
- Cake\Network\Http\Adapter\Stream - Implements sending requests using php streams, and creating response objects.
- Cake\Network\Http\Authentication\Basic - Basic authentication support.
- Cake\Network\Http\Authentication\Digest - Digest authentication support.
- Cake\Network\Http\Message - Base class for request/response
- Cake\Network\Http\Request - Used by Client + Adapter\Stream to do requests.
- Cake\Network\Http\Response - Created by Client for handling responses.
- Cake\Network\Http\FormData - Creates multipart form data requests.
- Cake\Network\Http\FormData\Part - A single part in a multipart data request.
Doing requests
Doing requests should be simple and straight forward. Doing a get request should work like:
<?php
use Cake\Network\Http\Client;
$http = new Client();
// Simple get
$response = $http->get('http://example.com/test.html');
// Simple get with querystring
$response = $http->get('http://example.com/search', ['q' => 'widget']);
// Simple get with querystring & additional headers
$response = $http->get('http://example.com/search', ['q' => 'widget'], [
'headers' => ['X-Requested-With' => 'XMLHttpRequest']
]);
Doing post and put requests is equally simple:
<?php
// Send a POST request with application/x-www-form-urlencoded encoded data
$http = new Client();
$response = $http->post('http://example.com/posts/add', [
'title' => 'testing',
'body' => 'content in the post'
]);
// Send a PUT request with application/x-www-form-urlencoded encoded data
$response = $http->put('http://example.com/posts/add', [
'title' => 'testing',
'body' => 'content in the post'
]);
// Other methods as well.
$http->delete(...);
$http->head(...);
$http->patch(...);
Creating multipart requests with files
You can include files in request bodies by including them in the data array:
<?php
$http = new Client();
$response = $http->post('http://example.com/api', [
'image' => '@/path/to/a/file',
'logo' => $fileHandle
]);
By prefixing data values with @
or including a filehandle in the data. If a filehandle is used, the filehandle will be read until its end, it will not be rewound before being read.
Sending request bodies
When dealing with REST API's you often need to send request bodies that are not form encoded. Http\Client exposes this through the type option:
<?php
// Send a JSON request body.
$http = new Client();
$response = $http->post(
'http://example.com/tasks',
json_encode($data),
['type' => 'json']
);
The type
key can either be a mapped mime type in Cake\Network\Response or a full mime type. When using the type
option, you should provide the data as a string. If you're doing a GET request that needs both querystring parameters and a request body you can do the following:
<?php
// Send a JSON body in a GET request with query string parameters.
$http = new Client();
$response = $http->get(
'http://example.com/tasks',
['q' => 'test', '_content' => json_encode($data)],
['type' => 'json']
);
The general signature of each method would be:
$method($uri, $data, $options)
Request options
$options is used to provide addition request information. The following keys can be used in $options:
- headers - Array of additional headers
- cookie - Array of cookies to use.
- proxy - Array of proxy information.
- auth - Array of authentication data, the
type
key is used to delegate to an authentication strategy. By default Basic auth is used. - ssl_verify_peer - defaults to true. Set to false to disable SSL certification verification (not advised)
- ssl_verify_depth - defaults to 5. Depth to traverse in the CA chain.
- ssl_verify_host - defaults to true. Validate the SSL certificate against the host name.
- ssl_cafile - defaults to built in cafile. Overwrite to use custom CA bundles.
- timeout - Duration to wait before timing out.
- type - Send a request body in a custom content type. Requires
$data
to either be a string, or the_content
option to be set.
Authentication
Http\Client will support a few different authentication systems.
Using basic authentication
An example of basic authentication.
<?php
$http = new Client();
$response = $http->get('http://example.com/profile/1', [], [
'auth' => ['username' => 'mark', 'password' => 'secret']
]);
By default Http\Client will use basic authentication is there is no 'type'
key in the auth option.
Using digest authentication
An example of basic authentication.
<?php
$http = new Client();
$response = $http->get('http://example.com/profile/1', [], [
'auth' => [
'type' => 'digest',
'username' => 'mark',
'password' => 'secret',
'realm' => 'myrealm',
'nonce' => 'onetimevalue',
'qop' => 1,
'opaque' => 'someval'
]
]);
By setting the 'type' key to 'digest', you tell the authentication subsystem to use digest authentication. Different authentication strategies can be added by developers. Auth strategies are called before the request is sent, and allow headers to be added to the request context.
Oauth 1 authentication
An example of OAuth1 authentication. Many modern web-services require OAuth authentication to access their API's. The included OAuth authentication assumes that you already have your consumer key and consumer secret:
<?php
$http = new Client();
$response = $http->get('http://example.com/profile/1', [], [
'auth' => [
'type' => 'oauth',
'consumerKey' => 'bigkey',
'consumerSecret' => 'secret',
'token' => '...',
'tokenSecret' => '...',
'realm' => 'tickets',
]
]);
Proxy authentication
Some proxies require authentication to use them. Generally this authentication is Basic, but it can be implemented by any authentication adapter. By default Http\Client will assume Basic authentication, unless the type key is set.
<?php
$http = new Client();
$response = $http->get('http://example.com/test.php', [], [
'proxy' => [
'username' => 'mark',
'password' => 'testing',
'port' => 12345,
]
]);
Creating scoped clients
Having to re-type the domain name, authentication and proxy settings can become tedious & error prone. To reduce the change for mistake and relieve some of the tedium, developers can create scoped clients.
<?php
// Create a scoped client.
$http = new Client([
'host' => 'api.example.com',
'scheme' => 'https',
'auth' => ['username' => 'mark', 'password' => 'testing']
]);
// Do a request to api.example.com
$response = $http->get('/test.php');
The following information can be used when creating a scoped client:
- host
- scheme
- proxy
- auth
- port
- cookies
- timeout
- ssl_verify_peer
- ssl_verify_depth
- ssl_verify_host
Any of these options can be overridden by specifying them when doing requests. host, scheme, proxy, port are overridden in the request URL:
<?php
// Using the scoped client we created earlier.
$response = $http->get('http://foo.com/test.php');
The above will replace the domain, scheme, and port. However, this request will continue using all the other options defined when the scoped client was created.
Response objects
Response objects have a number of methods for inspecting the response data.
- body($parser = null) - Get the response body. Pass in an optional parser, to decode the response body. For example.
json_decode
could be used for decoding response data. - header($name) - Get a header with $name. $name is case-insensitive.
- headers() - Get all the headers.
- isOk() - Check if the response was ok.
- isRedirect() - Check if the response was a redirect.
- cookies() - Get the cookies from the response.
- statusCode() - Get the status code.
- encoding() - Get the encoding of the response.
The __get()
interface will also provide read access to the following methods: cookies, body, status, headers.