From f89d6a5e8f41124a92fa27a457000d3c8068a6a2 Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Tue, 7 May 2013 16:12:26 -0700 Subject: [PATCH 1/9] Support for new service class name for Search API RC4 as per Drupal.org issue #1407282. --- .../Pantheon_Search_Api_Solr_Service.php | 209 ++++-------------- .../pantheon_apachesolr.install | 9 + 2 files changed, 48 insertions(+), 170 deletions(-) diff --git a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php index be4533bc954..6d9fa38da51 100644 --- a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php +++ b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php @@ -1,182 +1,51 @@ $host, - 'path' => $path, - 'port' => 449, - 'default_field' => 'id', +class PantheonSearchApiSolrConnection extends SearchApiSolrConnection { + + function __construct(array $options) { + + // Adding in custom settings for Pantheon + $options['scheme'] = 'https'; + $options['host'] = (variable_get('pantheon_hyperion_host')) ? variable_get('pantheon_hyperion_host') : 'index.' . variable_get('pantheon_tier', 'live') . '.getpantheon.com'; + $options['path'] = 'sites/self/environments/' . variable_get('pantheon_environment', 'dev') . '/index'; + $options['port'] = 449; + $this->context = stream_context_create( + array( + 'ssl' => array( + 'local_cert' => '../certs/binding.pem', + ) + ) ); - parent::__construct($options); - // Since /ping otherwise complains about missing default field. - $this->_pingUrl .= '?q=' . $options['default_field'] . ':1'; - - // As of July 2011, the newest release is r60, with Service.php having - // revision 59. Revision 40 is just anything between 22 (old) and that. - $this->newClient = trim(parent::SVN_REVISION, '$ :A..Za..z') > 40; - if ($this->newClient) { - $this->_httpTransport = new PanteheonSearchApiSolrHttpTransport(); - } - } - -} - - -/** - * Pantheon implementation of the HTTP transport interface. - * - * Uses curl() for sending the request with certificate auth - */ -class PanteheonSearchApiSolrHttpTransport extends Apache_Solr_HttpTransport_Abstract { - - public function __construct() { - // Nothing to see here. - } - - /** - * Perform a GET HTTP operation with an optional timeout and return the response - * contents, use getLastResponseHeaders to retrieve HTTP headers - * - * @param string $url - * @param float $timeout - * @return Apache_Solr_HttpTransport_Response HTTP response - */ - public function performGetRequest($url, $timeout = FALSE) { - return $this->performHttpRequest('GET', $url, $timeout); - } - - /** - * Perform a HEAD HTTP operation with an optional timeout and return the response - * headers - NOTE: head requests have no response body - * - * @param string $url - * @param float $timeout - * @return Apache_Solr_HttpTransport_Response HTTP response - */ - public function performHeadRequest($url, $timeout = FALSE) { - return $this->performHttpRequest('HEAD', $url, $timeout); - } - - /** - * Perform a POST HTTP operation with an optional timeout and return the response - * contents, use getLastResponseHeaders to retrieve HTTP headers - * - * @param string $url - * @param string $rawPost - * @param string $contentType - * @param float $timeout - * @return Apache_Solr_HttpTransport_Response HTTP response - */ - public function performPostRequest($url, $rawPost, $contentType, $timeout = FALSE) { - return $this->performHttpRequest('POST', $url, $timeout, $rawPost, $contentType); - } - - /** - * Helper method for making an HTTP request. - */ - protected function performHttpRequest($method, $url, $timeout, $rawPost = NULL, $contentType = NULL) { - // The _constructUrl() in Apache_Solr_Service hard codes http like a boss. - $url = str_replace('http://', 'https://', $url); - // Kludgy workaround of double-get-arging. - // https://index.live.getpantheon.com:449/sites/self/environments/dev/index/admin/ping?q=id:1?q=id:1 - // WHY ARG WHY!?!?! - $parts = explode('?', $url); - $url = $parts[0] .'?'. $parts[1]; - $client_cert = '../certs/binding.pem'; - $port = 449; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_SSLCERT, $client_cert); - $opts = pantheon_apachesolr_curlopts(); - $opts[CURLOPT_URL] = $url; - $opts[CURLOPT_PORT] = $port; + // Adding in general settings for Search API + $options += array( + 'scheme' => 'http', + 'host' => 'localhost', + 'port' => 8983, + 'path' => 'solr', + 'http_user' => NULL, + 'http_pass' => NULL, + 'http_method' => 'POST', + 'local_cert'=> NULL, + ); + $this->options = $options; - if ($timeout) { - $opts[CURLOPT_CONNECTTIMEOUT] = $timeout; - } - curl_setopt_array($ch, $opts); + $path = '/' . trim($options['path'], '/') . '/'; + $this->base_url = $options['scheme'] . '://' . $options['host'] . ':' . $options['port'] . $path; - // If we are doing a delete request... - if ($method == 'DELETE') { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); - } - // If we are doing a put request... - if ($method == 'PUT') { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); - } - // If we are doing a put request... - if ($method == 'POST') { - curl_setopt($ch, CURLOPT_POST, 1); - } - if ($rawPost) { - curl_setopt($ch, CURLOPT_POSTFIELDS, $rawPost); - } - - $response = curl_exec($ch); + // Make sure we always have a valid method set, default to POST. + $this->method = $options['http_method'] == 'GET' ? 'GET' : 'POST'; - if ($response == NULL) { - // TODO; better error handling - watchdog('pantheon_apachesolr', "Error !error connecting to !url on port !port", array('!error' => curl_error($ch), '!url' => $url, '!port' => $port), WATCHDOG_ERROR); - } - else { - // mimick the $result object from drupal_http_request() - // TODO; better error handling - $result = new stdClass(); - list($split, $result->data) = explode("\r\n\r\n", $response, 2); - $split = preg_split("/\r\n|\n|\r/", $split); - list($result->protocol, $result->code, $result->status_message) = explode(' ', trim(array_shift($split)), 3); - // Parse headers. - $result->headers = array(); - while ($line = trim(array_shift($split))) { - list($header, $value) = explode(':', $line, 2); - if (isset($result->headers[$header]) && $result->header == 'Set-Cookie') { - // RFC 2109: the Set-Cookie response header comprises the token Set- - // Cookie:, followed by a comma-separated list of one or more cookies. - $result->headers[$header] .= ',' . trim($value); - } - else { - $result->headers[$header] = trim($value); - } - } - } - - if (!isset($result->code) || $result->code < 0) { - $result->code = 0; - $result->status_message = 'Request failed'; - $result->protocol = 'HTTP/1.0'; - } - // Additional information may be in the error property. - if (isset($result->error)) { - $result->status_message .= ': ' . check_plain($result->error); + // Set HTTP Basic Authentication parameter, if login data was set. + if (strlen($options['http_user']) && strlen($options['http_pass'])) { + $this->http_auth = 'Basic ' . base64_encode($options['http_user'] . ':' . $options['http_pass']); } + } - if (!isset($result->data)) { - $result->data = ''; - $result->response = NULL; - } - else { - $response = json_decode($result->data); - if (is_object($response)) { - foreach ($response as $key => $value) { - $result->$key = $value; - } - } - } +} - // drupal_set_message("$url: $result->code"); +class PantheonSearchApiSolrService extends SearchApiSolrService { - $type = isset($result->headers['content-type']) ? $result->headers['content-type'] : 'text/xml'; - $body = isset($result->data) ? $result->data : NULL; - return new Apache_Solr_HttpTransport_Response($result->code, $type, $body); - } + protected $connection_class = 'PantheonSearchApiSolrConnection'; -} +} \ No newline at end of file diff --git a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install index bba310962ba..368de46639c 100644 --- a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install +++ b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install @@ -7,6 +7,7 @@ function pantheon_apachesolr_install() { // Make our Class(es) active. variable_set('apachesolr_service_class', 'PantheonApacheSolrService'); variable_set('search_api_solr_connection_class', 'PantheonSearchApiSolrService'); + variable_set('search_api_solr_service_class', 'PantheonSearchApiSolrService'); } function pantheon_apachesolr_enable() { @@ -20,6 +21,7 @@ function pantheon_apachesolr_disable() { // restore stock solrserviceclass variable_del('apachesolr_service_class'); variable_del('search_api_solr_connection_class'); + variable_def('search_api_solr_service_class'); } /** @@ -28,3 +30,10 @@ function pantheon_apachesolr_disable() { function pantheon_apachesolr_update_7001(&$sandbox) { variable_set('search_api_solr_connection_class', 'PantheonSearchApiSolrService'); } + +/** + * Update the Search API Solr Service Class Variable for Pantheon Compatibility + */ +function pantheon_apachesolr_update_7002(&$sandbox) { + variable_set('search_api_solr_service_class', 'PantheonSearchApiSolrService'); +} From b8f5e77d4841ccd29c28b883b8c80e56d2f0ed50 Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Tue, 7 May 2013 16:15:52 -0700 Subject: [PATCH 2/9] Fixing typoe in support for new service class name for Search API RC4 as per Drupal.org issue #1407282. --- .../pantheon/pantheon_apachesolr/pantheon_apachesolr.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install index 368de46639c..d01f9318faa 100644 --- a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install +++ b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install @@ -21,7 +21,7 @@ function pantheon_apachesolr_disable() { // restore stock solrserviceclass variable_del('apachesolr_service_class'); variable_del('search_api_solr_connection_class'); - variable_def('search_api_solr_service_class'); + variable_del('search_api_solr_service_class'); } /** From a6fded50077dcbb91f15015f5a6f01d688cf48a7 Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Wed, 8 May 2013 12:29:34 -0700 Subject: [PATCH 3/9] Revert "Fixing typoe in support for new service class name for Search API RC4 as per Drupal.org issue #1407282." This reverts commit b8f5e77d4841ccd29c28b883b8c80e56d2f0ed50. --- .../pantheon/pantheon_apachesolr/pantheon_apachesolr.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install index d01f9318faa..368de46639c 100644 --- a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install +++ b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install @@ -21,7 +21,7 @@ function pantheon_apachesolr_disable() { // restore stock solrserviceclass variable_del('apachesolr_service_class'); variable_del('search_api_solr_connection_class'); - variable_del('search_api_solr_service_class'); + variable_def('search_api_solr_service_class'); } /** From 44c3cafc600992b1aea0d7da4ac5172e2bfbebf6 Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Wed, 8 May 2013 12:30:06 -0700 Subject: [PATCH 4/9] Revert "Support for new service class name for Search API RC4 as per Drupal.org issue #1407282." This reverts commit f89d6a5e8f41124a92fa27a457000d3c8068a6a2. --- .../Pantheon_Search_Api_Solr_Service.php | 209 ++++++++++++++---- .../pantheon_apachesolr.install | 9 - 2 files changed, 170 insertions(+), 48 deletions(-) diff --git a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php index 6d9fa38da51..be4533bc954 100644 --- a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php +++ b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php @@ -1,51 +1,182 @@ context = stream_context_create( - array( - 'ssl' => array( - 'local_cert' => '../certs/binding.pem', - ) - ) + /** + * Constructor + */ + public function __construct(array $options) { + $host = variable_get('pantheon_hyperion_host', FALSE); + if (!$host) { + $host = 'index.'. variable_get('pantheon_tier', 'live') .'.getpantheon.com'; + } + $path = 'sites/self/environments/'. variable_get('pantheon_environment', 'dev') .'/index'; + $options = array( + 'host' => $host, + 'path' => $path, + 'port' => 449, + 'default_field' => 'id', ); + parent::__construct($options); + // Since /ping otherwise complains about missing default field. + $this->_pingUrl .= '?q=' . $options['default_field'] . ':1'; - // Adding in general settings for Search API - $options += array( - 'scheme' => 'http', - 'host' => 'localhost', - 'port' => 8983, - 'path' => 'solr', - 'http_user' => NULL, - 'http_pass' => NULL, - 'http_method' => 'POST', - 'local_cert'=> NULL, - ); - $this->options = $options; + // As of July 2011, the newest release is r60, with Service.php having + // revision 59. Revision 40 is just anything between 22 (old) and that. + $this->newClient = trim(parent::SVN_REVISION, '$ :A..Za..z') > 40; + if ($this->newClient) { + $this->_httpTransport = new PanteheonSearchApiSolrHttpTransport(); + } + } - $path = '/' . trim($options['path'], '/') . '/'; - $this->base_url = $options['scheme'] . '://' . $options['host'] . ':' . $options['port'] . $path; +} - // Make sure we always have a valid method set, default to POST. - $this->method = $options['http_method'] == 'GET' ? 'GET' : 'POST'; - // Set HTTP Basic Authentication parameter, if login data was set. - if (strlen($options['http_user']) && strlen($options['http_pass'])) { - $this->http_auth = 'Basic ' . base64_encode($options['http_user'] . ':' . $options['http_pass']); - } +/** + * Pantheon implementation of the HTTP transport interface. + * + * Uses curl() for sending the request with certificate auth + */ +class PanteheonSearchApiSolrHttpTransport extends Apache_Solr_HttpTransport_Abstract { + + public function __construct() { + // Nothing to see here. } -} + /** + * Perform a GET HTTP operation with an optional timeout and return the response + * contents, use getLastResponseHeaders to retrieve HTTP headers + * + * @param string $url + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performGetRequest($url, $timeout = FALSE) { + return $this->performHttpRequest('GET', $url, $timeout); + } + + /** + * Perform a HEAD HTTP operation with an optional timeout and return the response + * headers - NOTE: head requests have no response body + * + * @param string $url + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performHeadRequest($url, $timeout = FALSE) { + return $this->performHttpRequest('HEAD', $url, $timeout); + } + + /** + * Perform a POST HTTP operation with an optional timeout and return the response + * contents, use getLastResponseHeaders to retrieve HTTP headers + * + * @param string $url + * @param string $rawPost + * @param string $contentType + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performPostRequest($url, $rawPost, $contentType, $timeout = FALSE) { + return $this->performHttpRequest('POST', $url, $timeout, $rawPost, $contentType); + } + + /** + * Helper method for making an HTTP request. + */ + protected function performHttpRequest($method, $url, $timeout, $rawPost = NULL, $contentType = NULL) { + // The _constructUrl() in Apache_Solr_Service hard codes http like a boss. + $url = str_replace('http://', 'https://', $url); + // Kludgy workaround of double-get-arging. + // https://index.live.getpantheon.com:449/sites/self/environments/dev/index/admin/ping?q=id:1?q=id:1 + // WHY ARG WHY!?!?! + $parts = explode('?', $url); + $url = $parts[0] .'?'. $parts[1]; + $client_cert = '../certs/binding.pem'; + $port = 449; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_SSLCERT, $client_cert); + + $opts = pantheon_apachesolr_curlopts(); + $opts[CURLOPT_URL] = $url; + $opts[CURLOPT_PORT] = $port; + + if ($timeout) { + $opts[CURLOPT_CONNECTTIMEOUT] = $timeout; + } + curl_setopt_array($ch, $opts); -class PantheonSearchApiSolrService extends SearchApiSolrService { + // If we are doing a delete request... + if ($method == 'DELETE') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + } + // If we are doing a put request... + if ($method == 'PUT') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); + } + // If we are doing a put request... + if ($method == 'POST') { + curl_setopt($ch, CURLOPT_POST, 1); + } + if ($rawPost) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $rawPost); + } - protected $connection_class = 'PantheonSearchApiSolrConnection'; + $response = curl_exec($ch); -} \ No newline at end of file + if ($response == NULL) { + // TODO; better error handling + watchdog('pantheon_apachesolr', "Error !error connecting to !url on port !port", array('!error' => curl_error($ch), '!url' => $url, '!port' => $port), WATCHDOG_ERROR); + } + else { + // mimick the $result object from drupal_http_request() + // TODO; better error handling + $result = new stdClass(); + list($split, $result->data) = explode("\r\n\r\n", $response, 2); + $split = preg_split("/\r\n|\n|\r/", $split); + list($result->protocol, $result->code, $result->status_message) = explode(' ', trim(array_shift($split)), 3); + // Parse headers. + $result->headers = array(); + while ($line = trim(array_shift($split))) { + list($header, $value) = explode(':', $line, 2); + if (isset($result->headers[$header]) && $result->header == 'Set-Cookie') { + // RFC 2109: the Set-Cookie response header comprises the token Set- + // Cookie:, followed by a comma-separated list of one or more cookies. + $result->headers[$header] .= ',' . trim($value); + } + else { + $result->headers[$header] = trim($value); + } + } + } + + if (!isset($result->code) || $result->code < 0) { + $result->code = 0; + $result->status_message = 'Request failed'; + $result->protocol = 'HTTP/1.0'; + } + // Additional information may be in the error property. + if (isset($result->error)) { + $result->status_message .= ': ' . check_plain($result->error); + } + + if (!isset($result->data)) { + $result->data = ''; + $result->response = NULL; + } + else { + $response = json_decode($result->data); + if (is_object($response)) { + foreach ($response as $key => $value) { + $result->$key = $value; + } + } + } + + // drupal_set_message("$url: $result->code"); + + $type = isset($result->headers['content-type']) ? $result->headers['content-type'] : 'text/xml'; + $body = isset($result->data) ? $result->data : NULL; + return new Apache_Solr_HttpTransport_Response($result->code, $type, $body); + } + +} diff --git a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install index 368de46639c..bba310962ba 100644 --- a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install +++ b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.install @@ -7,7 +7,6 @@ function pantheon_apachesolr_install() { // Make our Class(es) active. variable_set('apachesolr_service_class', 'PantheonApacheSolrService'); variable_set('search_api_solr_connection_class', 'PantheonSearchApiSolrService'); - variable_set('search_api_solr_service_class', 'PantheonSearchApiSolrService'); } function pantheon_apachesolr_enable() { @@ -21,7 +20,6 @@ function pantheon_apachesolr_disable() { // restore stock solrserviceclass variable_del('apachesolr_service_class'); variable_del('search_api_solr_connection_class'); - variable_def('search_api_solr_service_class'); } /** @@ -30,10 +28,3 @@ function pantheon_apachesolr_disable() { function pantheon_apachesolr_update_7001(&$sandbox) { variable_set('search_api_solr_connection_class', 'PantheonSearchApiSolrService'); } - -/** - * Update the Search API Solr Service Class Variable for Pantheon Compatibility - */ -function pantheon_apachesolr_update_7002(&$sandbox) { - variable_set('search_api_solr_service_class', 'PantheonSearchApiSolrService'); -} From 09ca71f6ef305977c2ad207f29c74f3fb2602b2e Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Wed, 8 May 2013 12:45:33 -0700 Subject: [PATCH 5/9] Updated support for new service class name for Search API RC4. This uses hook_search_api_service_info_alter() to add the class and maintains legacy support for RC2 --- .../Pantheon_Search_Api_Solr_Service.php | 57 +++++++++++++++++++ .../pantheon_apachesolr.module | 4 ++ 2 files changed, 61 insertions(+) diff --git a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php index be4533bc954..088eb972c2e 100644 --- a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php +++ b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php @@ -1,4 +1,61 @@ context = stream_context_create( + array( + 'ssl' => array( + 'local_cert' => '../certs/binding.pem', + ) + ) + ); + + // Adding in general settings for Search API + $options += array( + 'scheme' => 'http', + 'host' => 'localhost', + 'port' => 8983, + 'path' => 'solr', + 'http_user' => NULL, + 'http_pass' => NULL, + 'http_method' => 'POST', + 'local_cert'=> NULL, + ); + $this->options = $options; + + $path = '/' . trim($options['path'], '/') . '/'; + $this->base_url = $options['scheme'] . '://' . $options['host'] . ':' . $options['port'] . $path; + + // Make sure we always have a valid method set, default to POST. + $this->method = $options['http_method'] == 'GET' ? 'GET' : 'POST'; + + // Set HTTP Basic Authentication parameter, if login data was set. + if (strlen($options['http_user']) && strlen($options['http_pass'])) { + $this->http_auth = 'Basic ' . base64_encode($options['http_user'] . ':' . $options['http_pass']); + } + } + +} + +class PantheonApachesolrSearchApiSolrService extends SearchApiSolrService { + + protected $connection_class = 'PantheonApachesolrSearchApiSolrConnection'; + +} + +/** + * Legacy Supported Class for RC2 + */ class PantheonSearchApiSolrService extends SearchApiSolrConnection { /** diff --git a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module index 654e50ffd29..e596752a1ec 100644 --- a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module +++ b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module @@ -19,6 +19,10 @@ function pantheon_apachesolr_menu() { return $items; } +function pantheon_apachesolr_search_api_service_info_alter(array &$service_info) { + $service_info['search_api_solr_service']['class'] = 'PantheonApachesolrSearchApiSolrService'; +} + function pantheon_apachesolr_curlopts() { return array( CURLOPT_HEADER => 1, From 7c49b0bf3f61c1dc8dbdb28af44505683b073f90 Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Wed, 8 May 2013 12:58:37 -0700 Subject: [PATCH 6/9] Conditionally wrapping legacy class extension so future versions that don't have said class won't break --- .../Pantheon_Search_Api_Solr_Service.php | 252 +++++++++--------- 1 file changed, 128 insertions(+), 124 deletions(-) diff --git a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php index 088eb972c2e..98ea2af74eb 100644 --- a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php +++ b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php @@ -93,147 +93,151 @@ public function __construct(array $options) { * * Uses curl() for sending the request with certificate auth */ -class PanteheonSearchApiSolrHttpTransport extends Apache_Solr_HttpTransport_Abstract { - public function __construct() { - // Nothing to see here. - } - - /** - * Perform a GET HTTP operation with an optional timeout and return the response - * contents, use getLastResponseHeaders to retrieve HTTP headers - * - * @param string $url - * @param float $timeout - * @return Apache_Solr_HttpTransport_Response HTTP response - */ - public function performGetRequest($url, $timeout = FALSE) { - return $this->performHttpRequest('GET', $url, $timeout); - } +if (class_exists('Apache_Solr_HttpTransport_Abstract')) { - /** - * Perform a HEAD HTTP operation with an optional timeout and return the response - * headers - NOTE: head requests have no response body - * - * @param string $url - * @param float $timeout - * @return Apache_Solr_HttpTransport_Response HTTP response - */ - public function performHeadRequest($url, $timeout = FALSE) { - return $this->performHttpRequest('HEAD', $url, $timeout); - } + class PanteheonSearchApiSolrHttpTransport extends Apache_Solr_HttpTransport_Abstract { - /** - * Perform a POST HTTP operation with an optional timeout and return the response - * contents, use getLastResponseHeaders to retrieve HTTP headers - * - * @param string $url - * @param string $rawPost - * @param string $contentType - * @param float $timeout - * @return Apache_Solr_HttpTransport_Response HTTP response - */ - public function performPostRequest($url, $rawPost, $contentType, $timeout = FALSE) { - return $this->performHttpRequest('POST', $url, $timeout, $rawPost, $contentType); - } - - /** - * Helper method for making an HTTP request. - */ - protected function performHttpRequest($method, $url, $timeout, $rawPost = NULL, $contentType = NULL) { - // The _constructUrl() in Apache_Solr_Service hard codes http like a boss. - $url = str_replace('http://', 'https://', $url); - // Kludgy workaround of double-get-arging. - // https://index.live.getpantheon.com:449/sites/self/environments/dev/index/admin/ping?q=id:1?q=id:1 - // WHY ARG WHY!?!?! - $parts = explode('?', $url); - $url = $parts[0] .'?'. $parts[1]; - $client_cert = '../certs/binding.pem'; - $port = 449; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_SSLCERT, $client_cert); - - $opts = pantheon_apachesolr_curlopts(); - $opts[CURLOPT_URL] = $url; - $opts[CURLOPT_PORT] = $port; - - if ($timeout) { - $opts[CURLOPT_CONNECTTIMEOUT] = $timeout; + public function __construct() { + // Nothing to see here. } - curl_setopt_array($ch, $opts); - // If we are doing a delete request... - if ($method == 'DELETE') { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); - } - // If we are doing a put request... - if ($method == 'PUT') { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); + /** + * Perform a GET HTTP operation with an optional timeout and return the response + * contents, use getLastResponseHeaders to retrieve HTTP headers + * + * @param string $url + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performGetRequest($url, $timeout = FALSE) { + return $this->performHttpRequest('GET', $url, $timeout); } - // If we are doing a put request... - if ($method == 'POST') { - curl_setopt($ch, CURLOPT_POST, 1); + + /** + * Perform a HEAD HTTP operation with an optional timeout and return the response + * headers - NOTE: head requests have no response body + * + * @param string $url + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performHeadRequest($url, $timeout = FALSE) { + return $this->performHttpRequest('HEAD', $url, $timeout); } - if ($rawPost) { - curl_setopt($ch, CURLOPT_POSTFIELDS, $rawPost); + + /** + * Perform a POST HTTP operation with an optional timeout and return the response + * contents, use getLastResponseHeaders to retrieve HTTP headers + * + * @param string $url + * @param string $rawPost + * @param string $contentType + * @param float $timeout + * @return Apache_Solr_HttpTransport_Response HTTP response + */ + public function performPostRequest($url, $rawPost, $contentType, $timeout = FALSE) { + return $this->performHttpRequest('POST', $url, $timeout, $rawPost, $contentType); } - $response = curl_exec($ch); + /** + * Helper method for making an HTTP request. + */ + protected function performHttpRequest($method, $url, $timeout, $rawPost = NULL, $contentType = NULL) { + // The _constructUrl() in Apache_Solr_Service hard codes http like a boss. + $url = str_replace('http://', 'https://', $url); + // Kludgy workaround of double-get-arging. + // https://index.live.getpantheon.com:449/sites/self/environments/dev/index/admin/ping?q=id:1?q=id:1 + // WHY ARG WHY!?!?! + $parts = explode('?', $url); + $url = $parts[0] .'?'. $parts[1]; + $client_cert = '../certs/binding.pem'; + $port = 449; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_SSLCERT, $client_cert); + + $opts = pantheon_apachesolr_curlopts(); + $opts[CURLOPT_URL] = $url; + $opts[CURLOPT_PORT] = $port; + + if ($timeout) { + $opts[CURLOPT_CONNECTTIMEOUT] = $timeout; + } + curl_setopt_array($ch, $opts); - if ($response == NULL) { - // TODO; better error handling - watchdog('pantheon_apachesolr', "Error !error connecting to !url on port !port", array('!error' => curl_error($ch), '!url' => $url, '!port' => $port), WATCHDOG_ERROR); - } - else { - // mimick the $result object from drupal_http_request() - // TODO; better error handling - $result = new stdClass(); - list($split, $result->data) = explode("\r\n\r\n", $response, 2); - $split = preg_split("/\r\n|\n|\r/", $split); - list($result->protocol, $result->code, $result->status_message) = explode(' ', trim(array_shift($split)), 3); - // Parse headers. - $result->headers = array(); - while ($line = trim(array_shift($split))) { - list($header, $value) = explode(':', $line, 2); - if (isset($result->headers[$header]) && $result->header == 'Set-Cookie') { - // RFC 2109: the Set-Cookie response header comprises the token Set- - // Cookie:, followed by a comma-separated list of one or more cookies. - $result->headers[$header] .= ',' . trim($value); - } - else { - $result->headers[$header] = trim($value); + // If we are doing a delete request... + if ($method == 'DELETE') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + } + // If we are doing a put request... + if ($method == 'PUT') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); + } + // If we are doing a put request... + if ($method == 'POST') { + curl_setopt($ch, CURLOPT_POST, 1); + } + if ($rawPost) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $rawPost); + } + + $response = curl_exec($ch); + + if ($response == NULL) { + // TODO; better error handling + watchdog('pantheon_apachesolr', "Error !error connecting to !url on port !port", array('!error' => curl_error($ch), '!url' => $url, '!port' => $port), WATCHDOG_ERROR); + } + else { + // mimick the $result object from drupal_http_request() + // TODO; better error handling + $result = new stdClass(); + list($split, $result->data) = explode("\r\n\r\n", $response, 2); + $split = preg_split("/\r\n|\n|\r/", $split); + list($result->protocol, $result->code, $result->status_message) = explode(' ', trim(array_shift($split)), 3); + // Parse headers. + $result->headers = array(); + while ($line = trim(array_shift($split))) { + list($header, $value) = explode(':', $line, 2); + if (isset($result->headers[$header]) && $result->header == 'Set-Cookie') { + // RFC 2109: the Set-Cookie response header comprises the token Set- + // Cookie:, followed by a comma-separated list of one or more cookies. + $result->headers[$header] .= ',' . trim($value); + } + else { + $result->headers[$header] = trim($value); + } } } - } - if (!isset($result->code) || $result->code < 0) { - $result->code = 0; - $result->status_message = 'Request failed'; - $result->protocol = 'HTTP/1.0'; - } - // Additional information may be in the error property. - if (isset($result->error)) { - $result->status_message .= ': ' . check_plain($result->error); - } + if (!isset($result->code) || $result->code < 0) { + $result->code = 0; + $result->status_message = 'Request failed'; + $result->protocol = 'HTTP/1.0'; + } + // Additional information may be in the error property. + if (isset($result->error)) { + $result->status_message .= ': ' . check_plain($result->error); + } - if (!isset($result->data)) { - $result->data = ''; - $result->response = NULL; - } - else { - $response = json_decode($result->data); - if (is_object($response)) { - foreach ($response as $key => $value) { - $result->$key = $value; + if (!isset($result->data)) { + $result->data = ''; + $result->response = NULL; + } + else { + $response = json_decode($result->data); + if (is_object($response)) { + foreach ($response as $key => $value) { + $result->$key = $value; + } } } - } - // drupal_set_message("$url: $result->code"); + // drupal_set_message("$url: $result->code"); - $type = isset($result->headers['content-type']) ? $result->headers['content-type'] : 'text/xml'; - $body = isset($result->data) ? $result->data : NULL; - return new Apache_Solr_HttpTransport_Response($result->code, $type, $body); - } + $type = isset($result->headers['content-type']) ? $result->headers['content-type'] : 'text/xml'; + $body = isset($result->data) ? $result->data : NULL; + return new Apache_Solr_HttpTransport_Response($result->code, $type, $body); + } + } } From 08b5d5d510fbdb00dbcd81f2f7a6c252c2060814 Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Wed, 8 May 2013 14:07:11 -0700 Subject: [PATCH 7/9] Conditionally overriding the service class extension only in cases where Search RC4 or later is being used --- .../pantheon/pantheon_apachesolr/pantheon_apachesolr.module | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module index e596752a1ec..c4174d381a7 100644 --- a/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module +++ b/modules/pantheon/pantheon_apachesolr/pantheon_apachesolr.module @@ -20,7 +20,11 @@ function pantheon_apachesolr_menu() { } function pantheon_apachesolr_search_api_service_info_alter(array &$service_info) { - $service_info['search_api_solr_service']['class'] = 'PantheonApachesolrSearchApiSolrService'; + // Change the Search API Solr Service class to our custom class. This approach is used for + // Search API RC4 and greater which no longer uses the SolrPHP Client library. + if (!function_exists('_search_api_solr_solrphpclient_path')) { + $service_info['search_api_solr_service']['class'] = 'PantheonApachesolrSearchApiSolrService'; + } } function pantheon_apachesolr_curlopts() { From 9058cb225112a9dbe4a11752a325aaab47309caf Mon Sep 17 00:00:00 2001 From: Matt Cheney Date: Mon, 13 May 2013 10:20:21 -0700 Subject: [PATCH 8/9] Updating way that Stream Context is set to keep up with the latest patch in http://drupal.org/node/1990422#comment-7407400 --- .../Pantheon_Search_Api_Solr_Service.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php index 98ea2af74eb..c2e98e516d7 100644 --- a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php +++ b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php @@ -1,7 +1,7 @@ context = stream_context_create( - array( - 'ssl' => array( - 'local_cert' => '../certs/binding.pem', - ) + $this->setStreamContext( + stream_context_create( + array( + 'ssl' => array( + 'local_cert' => '../certs/binding.pem', + ) + ) ) ); From 97081cc9bb5d94588f297794e772a99df62c5fec Mon Sep 17 00:00:00 2001 From: Josh Koenig Date: Fri, 17 May 2013 18:28:13 -0700 Subject: [PATCH 9/9] Override makeHttpRequest to add a timeout --- .../Pantheon_Search_Api_Solr_Service.php | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php index c2e98e516d7..5f2adce1daa 100644 --- a/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php +++ b/modules/pantheon/pantheon_apachesolr/Pantheon_Search_Api_Solr_Service.php @@ -47,6 +47,55 @@ function __construct(array $options) { } } + /** + * Sends an HTTP request to Solr. + * + * This is just a wrapper around drupal_http_request(). + * + * Overridden by Pantheon to set a timeout and possibly other improvements. + */ + protected function makeHttpRequest($url, array $options = array()) { + if (empty($options['method']) || $options['method'] == 'GET' || $options['method'] == 'HEAD') { + // Make sure we are not sending a request body. + $options['data'] = NULL; + } + if ($this->http_auth) { + $options['headers']['Authorization'] = $this->http_auth; + } + if ($this->stream_context) { + $options['context'] = $this->stream_context; + } + // Customize timout. + $options['timeout'] = 5; + + $result = drupal_http_request($url, $options); + + if (!isset($result->code) || $result->code < 0) { + $result->code = 0; + $result->status_message = 'Request failed'; + $result->protocol = 'HTTP/1.0'; + } + // Additional information may be in the error property. + if (isset($result->error)) { + $result->status_message .= ': ' . check_plain($result->error); + } + + if (!isset($result->data)) { + $result->data = ''; + $result->response = NULL; + } + else { + $response = json_decode($result->data); + if (is_object($response)) { + foreach ($response as $key => $value) { + $result->$key = $value; + } + } + } + + return $result; + } + } class PantheonApachesolrSearchApiSolrService extends SearchApiSolrService {