1
0
Fork 0
mirror of synced 2024-07-05 22:51:24 +12:00

Resolve merge conflict

This commit is contained in:
Khushboo Verma 2024-07-02 15:02:58 +05:30
commit aab4665701
8 changed files with 466 additions and 91 deletions

View file

@ -333,13 +333,6 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$body = $execution['responseBody'] ?? '';
$encodingKey = \array_search('x-open-runtimes-encoding', \array_column($execution['responseHeaders'], 'name'));
if ($encodingKey !== false) {
if (($execution['responseHeaders'][$encodingKey]['value'] ?? '') === 'base64') {
$body = \base64_decode($body);
}
}
$contentType = 'text/plain';
foreach ($execution['responseHeaders'] as $header) {
if (\strtolower($header['name']) === 'content-type') {

160
composer.lock generated
View file

@ -1126,86 +1126,6 @@
},
"time": "2022-03-17T08:00:35+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-06-19T12:30:46+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.30.0",
@ -5417,6 +5337,86 @@
],
"time": "2024-05-31T15:07:36+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-06-19T12:30:46+00:00"
},
{
"name": "textalk/websocket",
"version": "1.5.7",

View file

@ -0,0 +1,147 @@
<?php
namespace Appwrite\Utopia\Fetch;
class BodyMultipart
{
/**
* @var array<string, mixed> $parts
*/
private array $parts = [];
private string $boundary = "";
public function __construct(string $boundary = null)
{
if (is_null($boundary)) {
$this->boundary = self::generateBoundary();
} else {
$this->boundary = $boundary;
}
}
public static function generateBoundary(): string
{
return '-----------------------------' . \uniqid();
}
public function load(string $body): self
{
$eol = "\r\n";
$sections = \explode('--' . $this->boundary, $body);
foreach ($sections as $section) {
if (empty($section)) {
continue;
}
if (strpos($section, $eol) === 0) {
$section = substr($section, \strlen($eol));
}
if (substr($section, -2) === $eol) {
$section = substr($section, 0, -1 * \strlen($eol));
}
if ($section == '--') {
continue;
}
$partChunks = \explode($eol . $eol, $section, 2);
if (\count($partChunks) < 2) {
continue; // Broken part
}
[ $partHeaders, $partBody ] = $partChunks;
$partHeaders = \explode($eol, $partHeaders);
$partName = "";
foreach ($partHeaders as $partHeader) {
if (!empty($partName)) {
break;
}
$partHeaderArray = \explode(':', $partHeader, 2);
$partHeaderName = \strtolower($partHeaderArray[0] ?? '');
$partHeaderValue = $partHeaderArray[1] ?? '';
if ($partHeaderName == "content-disposition") {
$dispositionChunks = \explode("; ", $partHeaderValue);
foreach ($dispositionChunks as $dispositionChunk) {
$dispositionChunkValues = \explode("=", $dispositionChunk, 2);
if (\count($dispositionChunkValues) >= 2) {
if ($dispositionChunkValues[0] === "name") {
$partName = \trim($dispositionChunkValues[1], "\"");
break;
}
}
}
}
}
if (!empty($partName)) {
$this->parts[$partName] = $partBody;
}
}
return $this;
}
/**
* @return array<string, mixed>
*/
public function getParts(): array
{
return $this->parts ?? [];
}
public function getPart(string $key, mixed $default = ''): mixed
{
return $this->parts[$key] ?? $default;
}
public function setPart(string $key, mixed $value): self
{
$this->parts[$key] = $value;
return $this;
}
public function getBoundary(): string
{
return $this->boundary;
}
public function setBoundary(string $boundary): self
{
$this->boundary = $boundary;
return $this;
}
public function exportHeader(): string
{
return 'multipart/form-data; boundary=' . $this->boundary;
}
public function exportBody(): string
{
$eol = "\r\n";
$query = '--' . $this->boundary;
foreach ($this->parts as $key => $value) {
$query .= $eol . 'Content-Disposition: form-data; name="' . $key . '"';
if (\is_array($value)) {
$query .= $eol . 'Content-Type: application/json';
$value = \json_encode($value);
}
$query .= $eol . $eol;
$query .= $value . $eol;
$query .= '--' . $this->boundary;
}
$query .= "--" . $eol;
return $query;
}
}

View file

@ -3,6 +3,7 @@
namespace Executor;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Utopia\Fetch\BodyMultipart;
use Exception;
use Utopia\System\System;
@ -211,7 +212,7 @@ class Executor
$requestTimeout = $timeout + 15;
}
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $requestTimeout);
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId, 'content-type' => 'multipart/form-data', 'accept' => 'multipart/form-data' ], $params, true, $requestTimeout);
$status = $response['headers']['status-code'];
if ($status >= 400) {
@ -219,6 +220,11 @@ class Executor
throw new \Exception($message, $status);
}
$response['body']['headers'] = \json_decode($response['body']['headers'] ?? '{}', true);
$response['body']['statusCode'] = \intval($response['body']['statusCode'] ?? 500);
$response['body']['duration'] = \intval($response['body']['duration'] ?? 0);
$response['body']['startTime'] = \intval($response['body']['startTime'] ?? \microtime(true));
return $response['body'];
}
@ -250,7 +256,13 @@ class Executor
break;
case 'multipart/form-data':
$query = $this->flatten($params);
$multipart = new BodyMultipart();
foreach ($params as $key => $value) {
$multipart->setPart($key, $value);
}
$headers['content-type'] = $multipart->exportHeader();
$query = $multipart->exportBody();
break;
default:
@ -317,7 +329,16 @@ class Executor
$curlErrorMessage = curl_error($ch);
if ($decode) {
switch (substr($responseType, 0, strpos($responseType, ';'))) {
$strpos = strpos($responseType, ';');
$strpos = \is_bool($strpos) ? \strlen($responseType) : $strpos;
switch (substr($responseType, 0, $strpos)) {
case 'multipart/form-data':
$boundary = \explode('boundary=', $responseHeaders['content-type'] ?? '')[1] ?? '';
$multipartResponse = new BodyMultipart($boundary);
$multipartResponse->load(\is_bool($responseBody) ? '' : $responseBody);
$responseBody = $multipartResponse->getParts();
break;
case 'application/json':
$json = json_decode($responseBody, true);

View file

@ -163,7 +163,7 @@ class Client
* @return array
* @throws Exception
*/
public function call(string $method, string $path = '', array $headers = [], array $params = [], bool $decode = true): array
public function call(string $method, string $path = '', array $headers = [], mixed $params = [], bool $decode = true): array
{
$headers = array_merge($this->headers, $headers);
$ch = curl_init($this->endpoint . $path . (($method == self::METHOD_GET && !empty($params)) ? '?' . http_build_query($params) : ''));
@ -174,6 +174,7 @@ class Client
'application/json' => json_encode($params),
'multipart/form-data' => $this->flatten($params),
'application/graphql' => $params[0],
'text/plain' => $params,
default => http_build_query($params),
};

View file

@ -1805,4 +1805,205 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(204, $response['headers']['status-code']);
}
public function testFunctionsDomainBianryResponse()
{
$timeout = 15;
$code = realpath(__DIR__ . '/../../../resources/functions') . "/php-binary-response/code.tar.gz";
$this->packageCode('php-binary-response');
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => ID::unique(),
'name' => 'Test PHP Binary executions',
'runtime' => 'php-8.0',
'entrypoint' => 'index.php',
'timeout' => $timeout,
'execute' => ['any']
]);
$functionId = $function['body']['$id'] ?? '';
$this->assertEquals(201, $function['headers']['status-code']);
$rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('resourceId', [$functionId])->toString(),
Query::equal('resourceType', ['function'])->toString(),
],
]);
$this->assertEquals(200, $rules['headers']['status-code']);
$this->assertEquals(1, $rules['body']['total']);
$this->assertCount(1, $rules['body']['rules']);
$this->assertNotEmpty($rules['body']['rules'][0]['domain']);
$domain = $rules['body']['rules'][0]['domain'];
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'entrypoint' => 'index.php',
'code' => new CURLFile($code, 'application/x-gzip', basename($code)),
'activate' => true
]);
$deploymentId = $deployment['body']['$id'] ?? '';
$this->assertEquals(202, $deployment['headers']['status-code']);
// Poll until deployment is built
while (true) {
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
if (
$deployment['headers']['status-code'] >= 400
|| \in_array($deployment['body']['status'], ['ready', 'failed'])
) {
break;
}
\sleep(1);
}
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(200, $deployment['headers']['status-code']);
// Wait a little for activation to finish
sleep(5);
$proxyClient = new Client();
$proxyClient->setEndpoint('http://' . $domain);
$response = $proxyClient->call(Client::METHOD_GET, '/', [], [], false);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$bytes = unpack('C*byte', $response['body']);
$this->assertCount(3, $bytes);
$this->assertEquals(0, $bytes['byte1']);
$this->assertEquals(10, $bytes['byte2']);
$this->assertEquals(255, $bytes['byte3']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], []);
$this->assertEquals(204, $response['headers']['status-code']);
}
public function testFunctionsDomainBianryRequest()
{
$timeout = 15;
$code = realpath(__DIR__ . '/../../../resources/functions') . "/php-binary-request/code.tar.gz";
$this->packageCode('php-binary-request');
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => ID::unique(),
'name' => 'Test PHP Binary executions',
'runtime' => 'php-8.0',
'entrypoint' => 'index.php',
'timeout' => $timeout,
'execute' => ['any']
]);
$functionId = $function['body']['$id'] ?? '';
$this->assertEquals(201, $function['headers']['status-code']);
$rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('resourceId', [$functionId])->toString(),
Query::equal('resourceType', ['function'])->toString(),
],
]);
$this->assertEquals(200, $rules['headers']['status-code']);
$this->assertEquals(1, $rules['body']['total']);
$this->assertCount(1, $rules['body']['rules']);
$this->assertNotEmpty($rules['body']['rules'][0]['domain']);
$domain = $rules['body']['rules'][0]['domain'];
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'entrypoint' => 'index.php',
'code' => new CURLFile($code, 'application/x-gzip', basename($code)),
'activate' => true
]);
$deploymentId = $deployment['body']['$id'] ?? '';
$this->assertEquals(202, $deployment['headers']['status-code']);
// Poll until deployment is built
while (true) {
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
if (
$deployment['headers']['status-code'] >= 400
|| \in_array($deployment['body']['status'], ['ready', 'failed'])
) {
break;
}
\sleep(1);
}
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(200, $deployment['headers']['status-code']);
// Wait a little for activation to finish
sleep(5);
$proxyClient = new Client();
$proxyClient->setEndpoint('http://' . $domain);
$bytes = pack('C*', ...[0,20,255]);
$response = $proxyClient->call(Client::METHOD_POST, '/', [ 'content-type' => 'text/plain' ], $bytes, false);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(\md5($bytes), $response['body']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], []);
$this->assertEquals(204, $response['headers']['status-code']);
}
}

View file

@ -0,0 +1,6 @@
<?php
return function ($context) {
$hash = md5($context->req->bodyBinary);
return $context->res->send($hash);
};

View file

@ -0,0 +1,6 @@
<?php
return function ($context) {
$bytes = pack('C*', ...[0, 10, 255]);
return $context->res->binary($bytes);
};