Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support force_ip_resolve in StreamHandler #1659

Merged
merged 3 commits into from
May 1, 2017

Conversation

c960657
Copy link

@c960657 c960657 commented Nov 15, 2016

The option force_ip_resolve that was added in #1608 is only supported for CurlHandler.

This patch adds support for force_ip_resolve to StreamHandler.

Copy link
Member

@sagikazarmark sagikazarmark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution. I added a few comments, can you please take a look at them?

@@ -286,7 +288,7 @@ private function createStream(RequestInterface $request, array $options)
foreach ($options as $key => $value) {
$method = "add_{$key}";
if (isset($methods[$method])) {
$this->{$method}($request, $context, $value, $params);
$this->{$method}($request, $context, $value, $params, $uri);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really necessary to add the URI as a parameter? It's accessible from the request, isn't it?

Copy link
Author

@c960657 c960657 Dec 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, but I chose to preserve the original URI in the request in case other callbacks, middleware etc. need it. The modified $url is not the actual URL being requested but a version where the hostname has been replaced with an IP address, i.e. it lacks information about HTTP virtual host. This version is only used to "trick" fopen() into binding to the proper IP. The virtual host information is sent to the server in the Host HTTP header.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay, you are totally right. For some reasons I thought you are trying to set the host in the original request. Even then, adding the URI to all methods bellow seems to be too much. How about a resolveUri or resolveAddress or something similar method? It would make things easier IMO.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -277,6 +278,7 @@ private function createStream(RequestInterface $request, array $options)

$params = [];
$context = $this->getDefaultContext($request, $options);
$uri = $request->getUri()->withFragment('');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this better than the original code?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact it's not a good idea IMO as the reference for this object will not be the same as you expected.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment below on why I created a separate $uri variable outside the callback.

@@ -350,7 +352,7 @@ private function getDefaultContext(RequestInterface $request)
return $context;
}

private function add_proxy(RequestInterface $request, &$options, $value, &$params)
private function add_proxy(RequestInterface $request, &$options, $value, &$params, UriInterface &$uri)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objects are passed by reference

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but URIs are immutable, so I have to change the value by reference.

if (!isset($records[0]['ip'])) {
throw new ConnectException("Could not resolve IPv4 address for host '{$uri->getHost()}'", $request);
}
$uri = $uri->withHost($records[0]['ip']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly this is also not going to work as you expect. HTTP Messages are immutable, which means if you "modify" one them you will get a new object. The new URI will not be present in the request object. What you can do is saving the resolved IP address in the options array and passing it to the URI, then the URI to the request somewhere in the above code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean. I am not modifying the HTTP message here, only $uri that is passed by reference.

@c960657
Copy link
Author

c960657 commented Dec 14, 2016

If you have IPv6 connectivity, you can test the code using this script:

$c = new \GuzzleHttp\Client();
print (string) $c->get('http://icanhazip.com/s', ['force_ip_resolve' => 'v4'])->getBody();
print (string) $c->get('http://icanhazip.com/s', ['force_ip_resolve' => 'v6'])->getBody();

Copy link
Member

@sagikazarmark sagikazarmark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please also add StreamHandler to the list of supportet handlers in the note added in #1608?

if ('v4' === $options['force_ip_resolve']) {
$records = dns_get_record($uri->getHost(), DNS_A);
if (!isset($records[0]['ip'])) {
throw new ConnectException("Could not resolve IPv4 address for host '{$uri->getHost()}'", $request);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please use sprintf instead? I am not really fan of this kind of resolution.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

$this->lastHeaders = $http_response_header;
return $resource;
}
);
}

private function resolveUri(UriInterface $uri, array $options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call it resolveIp or resolveAddress? Since that's what we do here, isn't it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I chose the name resolveHost (we are resolving the hostname to an IP address).

Rename resolveUri() to resolveHost()
Check that URI host is not already an IP
Pass $request to ConnectException
@sagikazarmark sagikazarmark modified the milestone: 6.3.0 Jan 13, 2017
@sagikazarmark
Copy link
Member

Thanks for your work and patience. 6.3 is on it's way

@sagikazarmark sagikazarmark merged commit 8353b3f into guzzle:master May 1, 2017
@c960657 c960657 deleted the force-ip-resolve-stream branch May 1, 2017 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants