Commit 2a61e45e authored by Marc Egger's avatar Marc Egger

Rest Client: Switch to Curl

parent 18321a1d
Pipeline #3813 passed with stages
in 4 minutes and 27 seconds
......@@ -1599,7 +1599,7 @@ Example::
, 'y:hello world (link)|t:content direct (link)' AS _link
, CONCAT('F:', p.pathFileName,'|t:File (yank)|o:', p.pathFileName) AS _yank
, CONCAT('y|F:', p.pathFileName,'|t:File (link)|o:', p.pathFileName) AS _link
FROM Person AS p  
FROM Person AS p
.. _api_call_qfq_report:
......@@ -1668,7 +1668,7 @@ Example::
+===================+================================+============================================================================+
| n | n:https://www.dummy.ord/rest/person | |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| method | method:POST | GET or POST |
| method | method:POST | GET, POST, PUT or DELETE |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| content | content:{"name":"John";"surname":"Doe"} | Depending on the REST server JSON might be expected |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
......@@ -1676,12 +1676,10 @@ Example::
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| timeout | timeout:5 | Default: 5 seconds. |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| ssl | ssl:{"verify_peer":true,"allow_self_signed":false} | JSON config for SSL settings |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
**Header**
* Each header must be separated by ``\r\n``.
* Each header must be separated by ``\r\n`` or `\n`.
* An explicit given header will overwrite the named default header.
* Default header:
......@@ -1696,12 +1694,12 @@ Example::
* The variable ``{{http-status:C}}`` shows the `HTTP status code<https://en.wikipedia.org/wiki/List_of_HTTP_status_codes>`_.
A value starting with '2..' shows success.
* In case of an error, ``{{error-message:C:allbut}}`` shows some details.
* In case the returned answer is a valid JSON string, it's automatically copied STORE_CLIENT with corresponding key names.
* In case the returned answer is a valid JSON string, it is flattened and automatically copied to STORE_CLIENT with corresponding key names.
JSON answer example::
Answer from Server: { 'name' : 'John'; 'street': 'Milky road' }
Retrieve the values via: {{name:C:alnumx}}, {{street:C:alnumx}}
Answer from Server: { 'name' : 'John', 'address' : {'city' : 'Bern'} }
Retrieve the values via: {{name:C:alnumx}}, {{city:C:alnumx}}
.. _special-sql-functions:
......@@ -1718,7 +1716,7 @@ In general this function should be used when there is a chance that unplanned '|
Example::
10.sql = SELECT CONCAT('p:notes|t:Information: ', QBAR(Note.title), '|b') AS _link FROM Note  
10.sql = SELECT CONCAT('p:notes|t:Information: ', QBAR(Note.title), '|b') AS _link FROM Note
In case 'Note.title' contains a '|' (like 'fruit | numbers'), it will confuse the '... AS _link' class. Therefore it's
necessary to 'escape' (adding a '\' in front of the problematic character) the bar which is done by using `QBAR()`.
......@@ -1740,7 +1738,7 @@ on a HTML page with correctly displayed linefeed.
Example::
10.sql = SELECT QNL2BR(Note.title) FROM Note  
10.sql = SELECT QNL2BR(Note.title) FROM Note
One possibility how `LF` comes into the database is with form elements of type `textarea` if the user presses `enter` inside.
......
......@@ -39,43 +39,31 @@ class RestClient {
$recv = array();
$param = $this->parseArgument($str);
$options = array(
'http' => array(
'header' => $param[TOKEN_L_HEADER],
'method' => strtoupper($param[TOKEN_L_METHOD]),
'timeout' => $param[TOKEN_L_TIMEOUT],
)
);
if (isset($param[TOKEN_L_SSL])) {
$options['ssl'] = json_decode($param[TOKEN_L_SSL]);
}
// Add content only if there is one.
if (!empty($param[TOKEN_L_CONTENT])) {
$options['http']['content'] = $param[TOKEN_L_CONTENT];
}
// if (isset($param[TOKEN_L_SSL])) {
// $options['ssl'] = json_decode($param[TOKEN_L_SSL], true);
// }
$context = stream_context_create($options);
// Send request
try {
if (false === ($recvBuffer = file_get_contents($param[TOKEN_REST_CLIENT], false, $context))) {
$recv[HTTP_STATUS] = 400;
$recv[ERROR_MESSAGE] = implode(", ", $http_response_header);
} else {
// If $recBuffer is no json - don't care, $recv will be null then.
$recv = json_decode($recvBuffer, true);
$recv[HTTP_STATUS] = 200;
list($http_status, $recvBuffer) = self::callApiCurl(strtoupper($param[TOKEN_L_METHOD]), $param[TOKEN_REST_CLIENT], $data = $param[TOKEN_L_CONTENT] ?? '', $param[TOKEN_L_HEADER], $param[TOKEN_L_TIMEOUT]);
$recv[HTTP_STATUS] = $http_status;
if ($http_status >= 300) {
$recv[ERROR_MESSAGE] = ' Api error: ' . $param[TOKEN_REST_CLIENT] . ' HTTP code: ' . $http_status . ' Message: ' . print_r(json_decode($recvBuffer ?? '', true), true);
}
} catch (Exception $e) {
$recvBuffer = '';
$rcvBufferDecoded = json_decode($recvBuffer, true);
if ($rcvBufferDecoded !== Null) {
array_walk_recursive($rcvBufferDecoded, function ($value, $key) use (&$recv) {
$recv[$key] = $value;
});
}
} catch (\Exception $e) {
$recv[HTTP_STATUS] = $e->getCode();
$recv[ERROR_MESSAGE] = $e->getMessage();
}
// Copy new values to STORE_CLIENT
$this->store::setStore($recv, STORE_CLIENT, true);
return $recvBuffer;
}
......@@ -109,29 +97,82 @@ class RestClient {
$param[TOKEN_L_TIMEOUT] = 5;
}
// split header at "\r\n" and "\n". $header must be an array of single headers!!
if (!empty($param[TOKEN_L_HEADER])) {
$header = str_replace("\r\n", '$$SEP$$', $param[TOKEN_L_HEADER]);
$header = str_replace("\n", '$$SEP$$', $header);
$header = explode('$$SEP$$', $header);
} else {
$header = [];
}
if (strpos($param[TOKEN_L_HEADER], 'content-type:') === false) {
$mime = (($param[TOKEN_L_CONTENT][0] ?? '') == '{') ? 'application/json' : 'text/plain';
$header[] = 'content-type: ' . $mime . '; charset=utf-8';
}
if (strpos($param[TOKEN_L_HEADER], 'connection:') === false) {
$header[] = 'connection: close';
}
// If 'Host' is missing in header: define - useful for Firewall/ Proxy
// CR: if a header 'host' is given, REST calls fails always.
// $header = KeyValueStringParser::parse($param[TOKEN_L_HEADER], ':', '\r\n');
// if (empty($header['host'])) {
// if (strpos($param[TOKEN_L_HEADER], 'host:') === false) {
// $urlParts = parse_url($param[TOKEN_REST_CLIENT]);
// $header['host'] = $urlParts['host'];
// $header[] = 'host: ' . $urlParts['host'];
// }
// If 'Content-type' is missing in header: define.
if (empty($header['content-type'])) {
// Poor man guess: if no 'content-type' is explicit given and string starts with '{' >> 'application/json'
$mime = (($param[TOKEN_L_CONTENT][0] ?? '') == '{') ? 'application/json' : 'text/plain';
$header['content-type'] = $mime . '; charset: utf-8';
}
$param[TOKEN_L_HEADER] = $header;
return $param;
}
// If 'Connection' is missing in Header: define
if (empty($header['connection'])) {
$header['connection'] = 'close';
}
private static function callApiCurl(string $method, string $url, string $data = '', array $header = [], int $timeout = 5) {
// Join all header arguments to one string
$param[TOKEN_L_HEADER] = KeyValueStringParser::unparse($header, ': ', '\r\n') . '\r\n';
return $param;
$ch = curl_init();
$curlConfig = array(
CURLOPT_RETURNTRANSFER => true,
// CURLINFO_HEADER_OUT => $debug
);
switch ($method) {
case "POST":
$curlConfig[CURLOPT_POST] = true;
if (!empty($data)) {
$dataJson = $data;
$curlConfig[CURLOPT_POSTFIELDS] = $dataJson;
$header[] = 'Content-Length: ' . strlen($dataJson);
}
break;
case "PUT":
$curlConfig[CURLOPT_CUSTOMREQUEST] = 'PUT';
if (!empty($data)) {
$dataJson = $data;
$curlConfig[CURLOPT_POSTFIELDS] = $dataJson;
$header[] = 'Content-Length: ' . strlen($dataJson);
}
break;
case "DELETE":
$curlConfig[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if (!empty($data)) {
$url = sprintf("%s?%s", $url, http_build_query(json_decode($data, true)));
}
break;
default:
if (!empty($data)) {
$url = sprintf("%s?%s", $url, http_build_query(json_decode($data, true)));
}
}
$curlConfig[CURLOPT_URL] = $url;
$curlConfig[CURLOPT_TIMEOUT] = $timeout;
curl_setopt_array($ch, $curlConfig);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
// send request
$output = curl_exec($ch);
if ($output === false) {
throw new Exception(curl_error($ch), curl_errno($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [$httpCode, $output];
}
}
\ No newline at end of file
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment