From f8afbbbf3c61323fac66bf4199539f702391e5f1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 12 Aug 2025 16:14:15 +0530 Subject: [PATCH 1/5] feat: allow HEAD requests and add test for it --- docker-compose.yml | 2 +- tests/e2e/Client.php | 18 +++++++- .../Functions/FunctionsCustomClientTest.php | 46 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9b5e7a1fd7..10da8e91d4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -963,7 +963,7 @@ services: hostname: exc1 <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:0.7.22 + image: openruntimes/executor:executor-local restart: unless-stopped networks: - appwrite diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 278d1cd0d5..412c124e9e 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -216,6 +216,19 @@ class Client return $len; }); + // Special handling for HEAD requests + if ($method === self::METHOD_HEAD) { + curl_setopt($ch, CURLOPT_NOBODY, true); // This is crucial for HEAD requests + curl_setopt($ch, CURLOPT_HEADER, false); // We handle headers via HEADERFUNCTION + } else { + curl_setopt($ch, CURLOPT_NOBODY, false); + } + + // Only set POST fields for non-GET and non-HEAD requests + if ($method != self::METHOD_GET && $method != self::METHOD_HEAD) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $query); + } + if ($method != self::METHOD_GET) { curl_setopt($ch, CURLOPT_POSTFIELDS, $query); } @@ -229,7 +242,7 @@ class Client $responseType = $responseHeaders['content-type'] ?? ''; $responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if ($decode) { + if ($decode && $method !== self::METHOD_HEAD) { $strpos = strpos($responseType, ';'); $strpos = \is_bool($strpos) ? \strlen($responseType) : $strpos; switch (substr($responseType, 0, $strpos)) { @@ -255,6 +268,9 @@ class Client $json = null; break; } + } elseif ($method === self::METHOD_HEAD) { + // For HEAD requests, always set body to empty string regardless of decode flag + $responseBody = ''; } if ((curl_errno($ch)/* || 200 != $responseStatus*/)) { diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index ccabc2a79e..835d5de994 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -74,6 +74,52 @@ class FunctionsCustomClientTest extends Scope $this->cleanupFunction($functionId); } + public function testCreateHeadExecution() + { + + /** + * Test for SUCCESS + */ + $functionId = $this->setupFunction([ + 'functionId' => ID::unique(), + 'name' => 'Test', + 'execute' => [Role::user($this->getUser()['$id'])->toString()], + 'runtime' => 'node-22', + 'entrypoint' => 'index.js', + 'events' => [ + 'users.*.create', + 'users.*.delete', + ], + 'timeout' => 10, + ]); + $this->setupDeployment($functionId, [ + 'code' => $this->packageFunction('basic'), + 'activate' => true + ]); + + // Deny create async execution as guest + $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'async' => true, + ]); + $this->assertEquals(401, $execution['headers']['status-code']); + + // Allow create async execution as user + $execution = $this->client->call(Client::METHOD_HEAD, '/functions/' . $functionId . '/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'async' => true, + ]); + fwrite(STDOUT, json_encode($execution, JSON_PRETTY_PRINT)); + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEmpty($execution['body']); + + $this->cleanupFunction($functionId); + } + public function testCreateCustomExecution(): array { /** From 0e636f67be700b0403a65f50367f38801fa9bef4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 12 Aug 2025 16:24:01 +0530 Subject: [PATCH 2/5] update client --- tests/e2e/Client.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 412c124e9e..e411b68454 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -216,23 +216,18 @@ class Client return $len; }); - // Special handling for HEAD requests + if ($method === self::METHOD_HEAD) { curl_setopt($ch, CURLOPT_NOBODY, true); // This is crucial for HEAD requests - curl_setopt($ch, CURLOPT_HEADER, false); // We handle headers via HEADERFUNCTION + curl_setopt($ch, CURLOPT_HEADER, false); } else { curl_setopt($ch, CURLOPT_NOBODY, false); } - // Only set POST fields for non-GET and non-HEAD requests if ($method != self::METHOD_GET && $method != self::METHOD_HEAD) { curl_setopt($ch, CURLOPT_POSTFIELDS, $query); } - if ($method != self::METHOD_GET) { - curl_setopt($ch, CURLOPT_POSTFIELDS, $query); - } - // Allow self-signed certificates if ($this->selfSigned) { curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); From 2f801646675ffa127b54baf0a3c53c36af8990da Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 12 Aug 2025 17:52:52 +0530 Subject: [PATCH 3/5] update executor --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 10da8e91d4..7b201f4ac4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -963,7 +963,7 @@ services: hostname: exc1 <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:executor-local + image: openruntimes/executor:0.8.1 restart: unless-stopped networks: - appwrite From f9d0746e13072087f4ceef8b06a1f371020b9423 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 12 Aug 2025 17:54:45 +0530 Subject: [PATCH 4/5] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/e2e/Services/Functions/FunctionsCustomClientTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 835d5de994..909a4c9450 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -76,7 +76,6 @@ class FunctionsCustomClientTest extends Scope public function testCreateHeadExecution() { - /** * Test for SUCCESS */ From 73549a59a2aac33c263916d78b7a1b1c8f19c1a2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 12 Aug 2025 17:54:54 +0530 Subject: [PATCH 5/5] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/e2e/Services/Functions/FunctionsCustomClientTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 909a4c9450..a60c9e59cf 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -112,7 +112,6 @@ class FunctionsCustomClientTest extends Scope ], $this->getHeaders()), [ 'async' => true, ]); - fwrite(STDOUT, json_encode($execution, JSON_PRETTY_PRINT)); $this->assertEquals(200, $execution['headers']['status-code']); $this->assertEmpty($execution['body']);