Skip to content

Commit

Permalink
Issue #88 Use local_aws middleware to manage requests with proxies
Browse files Browse the repository at this point in the history
  * Also fixes unit tests to be consistent if test server sets proxy in
    config.php.
  • Loading branch information
andrewmadden committed Jan 24, 2023
1 parent 2f4c4da commit 8e660b9
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 182 deletions.
85 changes: 14 additions & 71 deletions classes/esrequest.php
Expand Up @@ -24,6 +24,8 @@

namespace search_elastic;

use local_aws\local\guzzle_helper;

defined('MOODLE_INTERNAL') || die();

require_once($CFG->dirroot . '/local/aws/sdk/aws-autoloader.php');
Expand Down Expand Up @@ -57,67 +59,13 @@ public function __construct($handler = false) {
$this->config = get_config('search_elastic');
$this->signing = (isset($this->config->signing) ? (bool)$this->config->signing : false);

// Allow the caller to instansite the Guzzle client
// with a custom handler.
// Allow the caller to instantiate the Guzzle client with a custom handler.
$config = [];
if ($handler) {
$this->client = new \GuzzleHttp\Client(['handler' => $handler]);
} else {
$this->client = new \GuzzleHttp\Client();
$config['handler'] = $handler;
}
}

/**
* Constructs the Guzzle Proxy settings array
* based on Moodle's server proxy admin settings.
*
* @return array $proxy Proxy settings for Guzzle to use.
*/
private function proxyconstruct() {
global $CFG;
$proxy = array();
$options = array();
$protocol = 'tcp';
$auth = '';
$server = '';
$uri = '';

if (! empty ( $CFG->proxyhost )) {
// Set the server details.
if (empty ( $CFG->proxyport )) {
$server = $CFG->proxyhost;
} else {
$server = $CFG->proxyhost . ':' . $CFG->proxyport;
}

// Set the authentication details.
if (! empty ( $CFG->proxyuser ) and ! empty ( $CFG->proxypassword )) {
$auth = $CFG->proxyuser . ':' . $CFG->proxypassword . '@';
}

// Set the proxy type.
if (! empty ( $CFG->proxytype ) && $CFG->proxytype == 'SOCKS5') {
$protocol = 'socks5';
}

// Construct proxy URI.
$uri = $protocol . '://' . $auth . $server;

// Populate proxy options array.
$options['http'] = $uri;
$options['https'] = $uri;

// Set excluded domains.
if (! empty ($CFG->proxybypass) ) {
$nospace = preg_replace('/\s/', '', $CFG->proxybypass);
$options['no'] = explode(',', $nospace);
}

// Finally populate proxy settings array.
$proxy['proxy'] = $options;

}

return $proxy;
$this->client = new \GuzzleHttp\Client($config);
$this->client = guzzle_helper::configure_client_proxy($this->client);
}

/**
Expand Down Expand Up @@ -163,9 +111,9 @@ private function signrequest($request) {
* @param array $proxy
* @return \GuzzleHttp\Psr7\Response
*/
private function http_action($psr7request, $proxy) {
private function http_action($psr7request) {
try {
$response = $this->client->send($psr7request, $proxy);
$response = $this->client->send($psr7request);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
$response = $e->getResponse();
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
Expand All @@ -184,13 +132,12 @@ private function http_action($psr7request, $proxy) {
*/
public function get($url) {
$psr7request = new \GuzzleHttp\Psr7\Request('GET', $url);
$proxy = $this->proxyconstruct();

if ($this->signing) {
$psr7request = $this->signrequest($psr7request);
}

$response = $this->http_action($psr7request, $proxy);
$response = $this->http_action($psr7request);

return $response;

Expand All @@ -206,13 +153,12 @@ public function get($url) {
public function put($url, $params=null) {
$headers = ['content-type' => 'application/json'];
$psr7request = new \GuzzleHttp\Psr7\Request('PUT', $url, $headers, $params);
$proxy = $this->proxyconstruct();

if ($this->signing) {
$psr7request = $this->signrequest($psr7request);
}

$response = $this->http_action($psr7request, $proxy);
$response = $this->http_action($psr7request);

return $response;

Expand All @@ -227,13 +173,12 @@ public function put($url, $params=null) {
public function post($url, $params) {
$headers = ['content-type' => 'application/json'];
$psr7request = new \GuzzleHttp\Psr7\Request('POST', $url, $headers, $params);
$proxy = $this->proxyconstruct();

if ($this->signing) {
$psr7request = $this->signrequest($psr7request);
}

$response = $this->http_action($psr7request, $proxy);
$response = $this->http_action($psr7request);

return $response;

Expand All @@ -257,9 +202,8 @@ public function postfile($url, $file) {
]);

$psr7request = new \GuzzleHttp\Psr7\Request('POST', $url, $headers, $multipart);
$proxy = $this->proxyconstruct();

$response = $this->http_action($psr7request, $proxy);
$response = $this->http_action($psr7request);

return $response;

Expand All @@ -273,13 +217,12 @@ public function postfile($url, $file) {
*/
public function delete($url) {
$psr7request = new \GuzzleHttp\Psr7\Request('DELETE', $url);
$proxy = $this->proxyconstruct();

if ($this->signing) {
$psr7request = $this->signrequest($psr7request);
}

$response = $this->http_action($psr7request, $proxy);
$response = $this->http_action($psr7request);

return $response;

Expand Down
119 changes: 11 additions & 108 deletions tests/esrequest_test.php
Expand Up @@ -361,109 +361,15 @@ public function test_signed_delete() {

}

/**
* Test that Guzzle proxy array is correctly constructed
* from Moodle Proxy settings.
*/
public function test_proxy_construct() {
$this->resetAfterTest(true);
set_config('proxyhost', 'localhost');
set_config('proxyport', 3128);
set_config('proxybypass', 'localhost, 127.0.0.1');

// We're testing a private method, so we need to setup reflector magic.
$method = new ReflectionMethod('\search_elastic\esrequest', 'proxyconstruct');
$method->setAccessible(true); // Allow accessing of private method.
$proxy = $method->invoke(new \search_elastic\esrequest); // Get result of invoked method.

$expected = ['proxy' => ['http' => 'tcp://localhost:3128',
'https' => 'tcp://localhost:3128',
'no' => ['localhost', '127.0.0.1']]];

$this->assertEquals($expected, $proxy, $canonicalize = true);
}

/**
* Test that Guzzle proxy array is correctly constructed
* from Moodle Proxy settings.
* With proxy authentication.
*/
public function test_proxy_construct_auth() {
$this->resetAfterTest(true);
set_config('proxyhost', 'localhost');
set_config('proxyport', 3128);
set_config('proxybypass', 'localhost, 127.0.0.1');
set_config('proxyuser', 'user1');
set_config('proxypassword', 'password');

// We're testing a private method, so we need to setup reflector magic.
$method = new ReflectionMethod('\search_elastic\esrequest', 'proxyconstruct');
$method->setAccessible(true); // Allow accessing of private method.
$proxy = $method->invoke(new \search_elastic\esrequest); // Get result of invoked method.

$expected = ['proxy' => ['http' => 'tcp://user1:password@localhost:3128',
'https' => 'tcp://user1:password@localhost:3128',
'no' => ['localhost', '127.0.0.1']]];

$this->assertEquals($expected, $proxy, $canonicalize = true);
}

/**
* Test that Guzzle proxy array is correctly constructed
* from Moodle Proxy settings.
* With proxy authentication and no proxy bypass.
*/
public function test_proxy_construct_no_bypass() {
$this->resetAfterTest(true);
set_config('proxyhost', 'localhost');
set_config('proxyport', 3128);
set_config('proxybypass', '');
set_config('proxyuser', 'user1');
set_config('proxypassword', 'password');

// We're testing a private method, so we need to setup reflector magic.
$method = new ReflectionMethod('\search_elastic\esrequest', 'proxyconstruct');
$method->setAccessible(true); // Allow accessing of private method.
$proxy = $method->invoke(new \search_elastic\esrequest); // Get result of invoked method.

$expected = ['proxy' => ['http' => 'tcp://user1:password@localhost:3128',
'https' => 'tcp://user1:password@localhost:3128']];

$this->assertEquals($expected, $proxy, $canonicalize = true);
}

/**
* Test that Guzzle proxy array is correctly constructed
* from Moodle Proxy settings.
* Using socks as the protocol.
*/
public function test_proxy_construct_socks() {
$this->resetAfterTest(true);
set_config('proxyhost', 'localhost');
set_config('proxyport', 3128);
set_config('proxybypass', 'localhost, 127.0.0.1');
set_config('proxytype', 'SOCKS5');

// We're testing a private method, so we need to setup reflector magic.
$method = new ReflectionMethod('\search_elastic\esrequest', 'proxyconstruct');
$method->setAccessible(true); // Allow accessing of private method.
$proxy = $method->invoke(new \search_elastic\esrequest); // Get result of invoked method.

$expected = ['proxy' => ['http' => 'socks5://localhost:3128',
'https' => 'socks5://localhost:3128',
'no' => ['localhost', '127.0.0.1']]];

$this->assertEquals($expected, $proxy, $canonicalize = true);
}

/**
* Test esrequest get with proxy functionality
*/
public function test_proxy_get() {
global $CFG;
$this->resetAfterTest(true);
set_config('proxyhost', 'localhost');
set_config('proxyport', 3128);
set_config('proxybypass', 'localhost, 127.0.0.1');
$CFG->proxyhost = 'proxy.com';
$CFG->proxyport = 3128;
$CFG->proxybypass = 'localhost, 127.0.0.1';

$container = [];
$history = Middleware::history($container);
Expand All @@ -477,24 +383,21 @@ public function test_proxy_get() {
// Add the history middleware to the handler stack.
$stack->push($history);

$url = 'http://localhost:8080/foo?bar=blerg';
$url = 'http://example.com:8080/foo?bar=blerg';
$client = new \search_elastic\esrequest($stack);
$response = $client->get($url);
$client->get($url);
$request = $container[0]['request'];
$hostheader = $request->getHeader('Host');

$proxy = $container[0]['options']['proxy'];
$expected = ['http' => 'tcp://localhost:3128',
'https' => 'tcp://localhost:3128',
'no' => ['localhost', '127.0.0.1']];
$lastrequestoptions = $mock->getLastOptions();
$this->assertArrayHasKey('proxy', $lastrequestoptions);
$expected = 'proxy.com:3128';

// Check the results.
$this->assertEquals('http', $request->getUri()->getScheme());
$this->assertEquals('localhost', $request->getUri()->getHost());
$this->assertEquals('example.com', $request->getUri()->getHost());
$this->assertEquals('8080', $request->getUri()->getPort());
$this->assertEquals('/foo', $request->getUri()->getPath());
$this->assertEquals('bar=blerg', $request->getUri()->getQuery());
$this->assertEquals($expected, $proxy, $canonicalize = true);

$this->assertEquals($expected, $lastrequestoptions['proxy']);
}
}
6 changes: 3 additions & 3 deletions version.php
Expand Up @@ -24,12 +24,12 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2022011600;
$plugin->release = '3.10 Build (2022011600)'; // Build same as version.
$plugin->version = 2022011601;
$plugin->release = '3.10 Build (2022011601)'; // Build same as version.
$plugin->requires = 2016052304;
$plugin->component = 'search_elastic';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = array(
'local_aws' => 2020061500
'local_aws' => 2023010900,
);
$plugin->supported = [310, 311]; // A range of branch numbers of supported moodle versions.

0 comments on commit 8e660b9

Please sign in to comment.