Set proxy in Guzzle - php

I have a problem with set proxy in guzzle that a blank page was shown while with curl everything works perfect. The code that I used in guzzle and curl came below.
What is wrong with this code:
Guzzle:
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
require_once "vendor/autoload.php";
try {
$client = new Client();
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$response = $client->send($request, [
'timeout' => 30,
'curl' => [
'CURLOPT_PROXY' => '*.*.*.*',
'CURLOPT_PROXYPORT' => *,
'CURLOPT_PROXYUSERPWD' => '*:*',
],
]);
echo '</pre>';
echo($response->getBody());
exit;
} catch (RequestException $e) {
echo $e->getRequest();
if ($e->hasResponse()) {
echo $e->getResponse();
}
}
And The code with CURL:
$url = 'http://httpbin.org';
$ch = curl_init($url);
curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13');
curl_setopt($ch, CURLOPT_PROXY, '*.*.*.*');
curl_setopt($ch, CURLOPT_PROXYPORT, *);
curl_setopt($ch, CURLOPT_PROXYUSERPWD, '*:*');
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$page = curl_exec($ch);
echo $page;
Thanks.

As for Guzzle 6.
Guzzle docs give info about setting proxy for a single request
$client->request('GET', '/', ['proxy' => 'tcp://localhost:8125']);
But you can set it to all requests when initializing client
$client = new Client([
'base_uri' => 'http://doma.in/',
'timeout' => 10.0,
'cookie' => true,
'proxy' => 'tcp://12.34.56.78:3128',
]);
UPD. I don't know why, but I face a strange behaviour. One server with guzzle version 6.2.2 works great with config as above, and the other one with the same version receives 400 Bad Request HTTP error from a proxy. It is solved with another config structure (found in docs for guzzle 3)
$client = new Client([
'base_uri' => 'http://doma.in/',
'timeout' => 10.0,
'cookie' => true,
'request.options' => [
'proxy' => 'tcp://12.34.56.78:3128',
],
]);

for a proxy, if you have the username and password, you can use:
$client = new GuzzleHttp\Client();
$res = $client->request("POST", "https://endpoint.com", [
"proxy" => "http://username:password#192.168.16.1:10",
]);
this worked with guzzle in php.

For Guzzle6, I think the best way is to implement a middleware for setting proxy.
From Guzzle6 docs:
request-options.proxy
handlers-and-middleware
We can set proxy as below:
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
use Util\Api;
function add_proxy_callback($proxy_callback) {
return function (callable $handler) use ($proxy_callback) {
return function (RequestInterface $request,$options) use ($handler,$proxy_callback) {
$ip = $proxy_callback();
$options['proxy'] = $ip;
return $handler($request,$options);
};
};
}
$stack = new HandlerStack();
$stack->setHandler(new CurlHandler());
$stack->push(add_proxy_callback(function() {
return Api::getIp(); //function return a ip
}));
$client = new Client(['handler'=>$stack]);
$response = $client->request('GET','http://httpbin.org/ip');
var_dump((string)$response->getBody());

Had the same problem right now , and all i needed to do was use curl array keys as constants instead of strings ..
$response = $client->send($request, [
'timeout' => 30,
'curl' => [
CURLOPT_PROXY => '*.*.*.*',
CURLOPT_PROXYPORT => *,
CURLOPT_PROXYUSERPWD => '*:*',
],
]);
See CURL options Keys , they are not strings anymore.

The procedure for psr-7 may be different, but if you're using
the standard way to instantiate a client,
path\to\project\vendor\guzzlehttp\guzzle\src\Client.php, lines 164-170 includes code to read the environment variables to see if HTTP_PROXY and HTTPS_PROXY are set on the current machine, and if yes, Guzzle will use those values.
Additionally, I had to set my HTTPS_PROXY = http://ip:port (not https), because our workplace proxy seems to handle both https and http requests via the http protocol.
The advantage of this configuration is that you don't have to chnge proxy settings in your source code.

$response = \Drupal::httpClient()->post($settings['base_url'] . 'api/search/', [
'verify' => true,
'body' => $post_data,
'headers' => [
'Content-type' => 'application/json',
],
'curl' => [
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2,
CURLOPT_PROXY => 'proxyip:58080'],
]
)->getBody()->getContents();
Set proxy/https in Guzzle and SSL its work perfect.

Related

How can I get Guzzle to work with CURLOPT_USERPWD?

I have the following PHP curl code to generate a security token within an Application.
When I run this code it works perfect.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://host.com/api/v1/auth/keys');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{}");
curl_setopt($ch, CURLOPT_USERPWD, 'admin' . ':' . 'password');
$headers = array();
$headers[] = 'Content-Type: application/json';
$headers[] = 'Accept: application/json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
But at the moment I need a Guzzle version of this Curl script and that's where this issue begins.
I found different ways to handle the Authentication within Guzzle but so far nothing works.
This is what I came up with.
use GuzzleHttp\Client;
include "../vendor/autoload.php";
try{
$client = new Client(['base_uri' => 'https://host.com/api/v1/']);
$client->request('POST', 'auth/keys',[
'config' => [
'curl' => [
// CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => 'admin:password'
]
],
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]
]);
print_r($client->getBody()->getContents());
}catch (Exception $e) {
print_r([
"status" => false,
"message" => $e->getMessage()
]);
}
Instead of returning a security token, I get an error message "401 Unauthorized` response" - which means no correct authentication received.
What exactly am I doing wrong?
Thank you in advance.
I believe you are not well aware of how to use curl options within guzzle
Only use curl as request option no need to use config.(See docs)
<?php
require "../vendor/autoload.php";
use GuzzleHttp\Client;
try{
$client = new Client(['base_uri' => 'https://host.com/api/v1/']);
$guzzleResponse = $client->request('POST', 'auth/keys', [
'curl' => [
CURLOPT_USERPWD => 'admin:password'
],
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]
]);
print_r($guzzleResponse->getBody()->getContents());
} catch (GuzzleHttp\Exception\RequestException $e) {
print_r([
"status" => false,
"message" => $e->getMessage(),
]);// you can use logs here like monolog library
} catch(Exception $e){
print_r([
"status" => false,
"message" => $e->getMessage(),
]);
}
method 2
Refering to this answer, I believe you can also use Basic Auth http header.
$encodedAuth = base64_encode( $usename.":".$passwd);
// ... other same as above
$guzzleResponse = $client->request('POST', 'auth/keys', [
'headers' => [
'Authorization' => 'Bearer '. $encodedAuth,
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]
]);
// ... other same as above

How to reproduce OAuth1.0 Request Headers in PHP CURL

I am trying to make Twitter verify_credentials request on webserver using PHP 7.4.
I get a http 200 code and proper response only when I set OAuth1.0 Request Headers settings in Postman like that:
Any other way to make request with the same data returns me an error with 401 http status code
{"errors":[{"code":32,"message":"Could not authenticate you."}
I need to convert this Postman settings in PHP CURL of GUZZLE or another http request client code. But when I import CURL examples from Postman, it always throws the same 401 exception. So I tried different ways:
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.twitter.com/1.1/account/verify_credentials.json",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"Authorization: OAuth oauth_consumer_key=\"oauth_consumer_key\",oauth_token=\"oauth_token\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1605187800\",oauth_nonce=\"hmkiezWh6xqlfJYpK55rDVgcGydQkuBH\",oauth_version=\"1.0\",oauth_callback=\"http%3A%2F%2Fmyurl.com\",oauth_signature=\"signature\""
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
Or another one:
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
$client = new Client;
$headers =[
'Authorization' => 'OAuth oauth_consumer_key="oauth_consumer_key",oauth_token="oauth_token",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1605187800",oauth_nonce="hmkiezWh6xqlfJYpK55rDVgcGydQkuBH",oauth_version="1.0",oauth_callback="http%3A%2F%2Furl.com",oauth_signature="SdB60Nr6AhJzOdAIWlW%2FwdmeJM4%3D"',
];
$request = new Request('GET', 'https://api.twitter.com/1.1/account/verify_credentials.json', $headers);
$client->send($request);
$response = $client->getResponse();
echo $response->getBody();
Or that way:
// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.twitter.com/1.1/account/verify_credentials.json');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
$headers = array();
$headers[] = 'Authorization: OAuth oauth_consumer_key=\"oauth_consumer_key\",oauth_token=\"oauth_token\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1605187800\",oauth_nonce=\"hmkiezWh6xqlfJYpK55rDVgcGydQkuBH\",oauth_version=\"1.0\",oauth_callback=\"http%3A%2F%2Furl.com\",oauth_signature=\"H%2FpmcdPUnlMD8RN42RpfBs%2Fs7Cc%3D\"';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
Every time get an 401 error. So, how can I set up all OAuth1.0 properties in PHP CURL to reproduce the same request with the same headers which works in Postman?
P.S. I have already tried abraham/twitteroauth, laravel/socialite and other solutions with the same result
If you are using guzzle 6 or above, then you can directly use guzzlehttp/oauth-subscriber package created by guzzle itself to handle it (otherwise it is a long process),
Add the following to your composer.json:
{
"require": {
"guzzlehttp/oauth-subscriber": "0.4.*"
}
}
I have taken the example from their docs,
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
$stack = HandlerStack::create();
$middleware = new Oauth1([
'consumer_key' => 'my_key',
'consumer_secret' => 'my_secret',
'token' => 'my_token',
'token_secret' => 'my_token_secret'
]);
$stack->push($middleware);
$client = new Client([
'base_uri' => 'https://api.twitter.com/1.1/',
'handler' => $stack
]);
// Set the "auth" request option to "oauth" to sign using oauth
$res = $client->get('account/verify_credentials.json', ['auth' => 'oauth']);
You can follow the docs(https://github.com/guzzle/oauth-subscriber) of guzzle/oauth-subscriber for more info.
The cause was not in CURL options but in invalid signature, because it depends on timestamp as I understand it. So I cannot use one signature with different timestamps

How to use dynamic user-agent header using PHP Curl or Guzzel Client

I am new to PHP and trying to call a REST Service. I could do that using either Curl or Guzzle Client in PHP. Later I am calling this from Mozilla and Chrome Browser.
The problem is Guzzle and Curl are not forwarding the actual User-Agent header as Request Header to the backend services.
The default Guzzle User-Agent header is Guzzle/ver curl/ver PHP/ver
I know we can add custom/hardcoded headers in both Curl and Guzzle. But I dont want to hardcode.
<?php
require './vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'http://sample.com');
$data = json_decode($res->getBody(), true);
//echo $res->getBody()
?>
<html>
<body>
<p>Body is <?php echo $res->getBody() ?> </p>
</body>
</html>
When I call the PHP service from either Chrome/Mozilla/Mobile/Safari, I want the respective user-agent headers to be sent as request headers to backend services.
If there a way to do this in any way?
PHP has a build-in array which stores data from request -$_SERVER['HTTP_USER_AGENT'].
You can then set the user-agent guzzle uses with the headers option.
$client->request('GET', '/get', [
'headers' => [
'User-Agent' => $_SERVER['HTTP_USER_AGENT'],
]
]);
For GuzzleHttp\Client:
$client = new GuzzleHttp\Client([
'headers' => [
'User-Agent' => $_SERVER['HTTP_USER_AGENT'],
]
]);
$res = $client->request('GET', 'http://sample.com');
or
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'http://sample.com', [
'headers' => [
'User-Agent' => $_SERVER['HTTP_USER_AGENT'],
]
]);
For php cUrl:
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'http://sample.com',
CURLOPT_TIMEOUT => 0,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
));
curl_setopt($curl, CURLOPT_USERAGENT, $agent);
$response = curl_exec($curl);
curl_close($curl);
echo $response;
Hope this helps for someone

cURL POST request works, Guzzle 6 does not

Context:
I've been working on figuring out how to make this work for a while, and I simply don't understand why Guzzle isn't working for this particular request. The same initialization and request structure works in some basic unit tests I have, but when it comes to API to API communication, Guzzle just is not cooperating.
Problem:
What I mean by that is, it's not including the headers I'm setting in the $headers array, and the request body is empty.
Desired result:
To confirm this is an issue with Guzzle, I've written out the request with typical cURL syntax, and the request goes through fine. I just need some guidance on how to make this work with Guzzle, as I like the abstraction Guzzle offers over verbose cURL requests.
Working cURL request:
$headers = array(
'Authorization: Bearer '.$sharedSecret,
'Content-Type: application/x-www-form-urlencoded',
'Accept: application/json',
'Content-Length: '.strlen($loginDetails),
);
$curlOptions = array(
CURLOPT_URL => API_URL.'member/SessionManager',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => FALSE,
CURLOPT_HEADER => FALSE,
CURLOPT_FOLLOWLOCATION => FALSE,
CURLOPT_ENCODING => "",
CURLOPT_USERAGENT => "PORTAL",
CURLOPT_AUTOREFERER => TRUE,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $loginDetails,
CURLOPT_SSL_VERIFYHOST => FALSE,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_VERBOSE => FALSE
);
try {
$ch = curl_init();
curl_setopt_array($ch,$curlOptions);
$content = curl_exec($ch);
$err = curl_errno($ch);
$errmsg = curl_error($ch);
$response = curl_getinfo($ch);
curl_close($ch);
if ($content === FALSE) {
throw new Exception(curl_error($ch), curl_errno($ch));
} else {
return true;
}
} catch(Exception $e) {
trigger_error(sprintf('Curl failed with error #%d: %s', $e->getCode(), $e->getMessage()), E_USER_ERROR);
}
The Guzzle client is initialized in a global file that creates a container which stores various objects we use throughout the application. I'm including it in case I missed something vital that isn't in Guzzle's documentation.
Guzzle initialization:
$container['client'] = function ($c) {
return new \GuzzleHttp\Client([
'base_uri' => API_URL,
'timeout' => 30.0,
'allow_redirects' => true,
'verify' => false,
'verbose' => true,
[
'headers' => [
'Authorization' => 'Bearer '.$sharedSecret,
]
],
]);
};
Non-working Guzzle Request:
$headers = array(
'Authorization' => 'Bearer '.$sharedSecret,
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
'Content-Length'=> strlen($loginDetails),
);
try {
$response = $this->client->post('/api/member/SessionManager',
['debug' => true],
['headers' => $headers],
['body' => $loginDetails]
);
if($response) {
$this->handleResponse($response);
}
} catch (GuzzleHttp\Exception\ClientException $e) {
$response->getResponse();
$responseBodyAsString = $response->getBody()->getContents();
}
Whether I remove the headers array in the Guzzle initialization or not, it doesn't matter. The Authorization header is nowhere to be found in the request (confirmed with tcpdump, Wireshark, and receiving API error logging), and Guzzle's debug output shows no indication of my headers, nor my request body being anywhere in the request.
I'm stumped as to why this isn't working, and would very much like to understand. I can go the route of not using Guzzle, but would really prefer to due to brevity. Any input would be greatly appreciated.
In cURL request, the API URL being called is
API_URL.'member/SessionManager'
While in Guzzle request, the API URL being called has extra text "/api/"
(assuming API_URL is defined same in both cases)
new \GuzzleHttp\Client([
'base_uri' => API_URL,
...
$this->client->post('/api/member/SessionManager',
Appreciate that the question is old, but I thought I'd share my experience as I also struggled with this. The equivalent of:
CURLOPT_POSTFIELDS => $loginDetails,
in Guzzle is:
"query" => $loginDetails
In addition, I have found that if the base_uri does not end with a /, Guzzle will misunderstand it.
With all that in mind, your POST request should be as follows:
$response = $this->client->post('api/member/SessionManager', // Removed the first / as it's part of the base_uri now
['debug' => true],
['headers' => $headers],
['query' => $loginDetails] // Replaced "body" with "query"
);

PHP - https stream through http proxy

I'm trying to get the content of a stream over HTTPS, but I have to go over an HTTP proxy.
I'd like not to use cURL but rather use fopen with a context argument.
The thing is, I can't make it work over HTTPS (HTTP is working fine though).
This DOES NOT work :
$stream = stream_context_create(Array("http" => Array("method" => "GET",
"timeout" => 20,
"proxy" => "tcp://my-proxy:3128",
'request_fulluri' => True
)));
echo file_get_contents('https://my-stream', false, $context);
This DOES work (cURL) :
$url = 'https://my-stream';
$proxy = 'my-proxy:3128';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_PROXY, $proxy);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
$curl_scraped_page = curl_exec($ch);
curl_close($ch);
echo $curl_scraped_page;
Does somebody know what is wrong with the first piece of code ? if it works with cURL there has to be a way to make it work with a context.
I tried to change the context options to a bunch of different values woth no luck.
Any help would be greatly appreciated !
Thanks.
You did not specifiy the exact error message, try adding ignore_errors => true. But if you are getting a 400 Bad Request from Apache, the problem you are probably hitting, is a Server Name Indication & host header mismatch. There is also a PHP bug related to this: https://bugs.php.net/bug.php?id=63519
Try the following fix until this bug is resolved:
$stream = stream_context_create(array(
'http' => array(
'timeout' => 20,
'proxy' => 'tcp://my-proxy:3128',
'request_fulluri' => true
),
'ssl' => array(
'SNI_enabled' => false // Disable SNI for https over http proxies
)
));
echo file_get_contents('https://my-stream', false, $context);

Categories