Skip to content

Commit

Permalink
support zstd (#5599)
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanFreeman authored Dec 6, 2024
1 parent 5359616 commit 3cdb863
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 29 deletions.
12 changes: 12 additions & 0 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ PHP_ARG_WITH([brotli_dir],
[AS_HELP_STRING([[--with-brotli-dir[=DIR]]],
[Include Brotli support])], [no], [no])

PHP_ARG_ENABLE([zstd],
[enable zstd support],
[AS_HELP_STRING([[--enable-zstd]],
[Use zstd])], [no], [no])

PHP_ARG_WITH([nghttp2_dir],
[dir of nghttp2],
[AS_HELP_STRING([[--with-nghttp2-dir[=DIR]]],
Expand Down Expand Up @@ -873,6 +878,13 @@ EOF
fi
fi

if test "$PHP_ZSTD" = "yes"; then
PKG_CHECK_MODULES([ZSTD], [libzstd >= 1.4.0])
AC_DEFINE(SW_HAVE_ZSTD, 1, [have zstd])
PHP_EVAL_LIBLINE($ZSTD_LIBS, SWOOLE_SHARED_LIBADD)
PHP_EVAL_INCLINE($ZSTD_CFLAGS)
fi

PHP_ADD_LIBRARY(pthread)
PHP_SUBST(SWOOLE_SHARED_LIBADD)

Expand Down
10 changes: 10 additions & 0 deletions ext-src/php_swoole.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ END_EXTERN_C()
#include <brotli/encode.h>
#include <brotli/decode.h>
#endif
#ifdef SW_HAVE_ZSTD
#include <zstd.h>
#endif

#ifdef SW_USE_CARES
#include <ares.h>
Expand Down Expand Up @@ -928,6 +931,13 @@ PHP_MINFO_FUNCTION(swoole) {
snprintf(buf, sizeof(buf), "E%u/D%u", BrotliEncoderVersion(), BrotliDecoderVersion());
php_info_print_table_row(2, "brotli", buf);
#endif
#ifdef SW_HAVE_ZSTD
#ifdef ZSTD_VERSION_NUMBER
php_info_print_table_row(2, "zstd", ZSTD_VERSION_STRING);
#else
php_info_print_table_row(2, "zstd", "enabled");
#endif
#endif
#ifdef HAVE_MUTEX_TIMEDLOCK
php_info_print_table_row(2, "mutex_timedlock", "enabled");
#endif
Expand Down
10 changes: 10 additions & 0 deletions ext-src/php_swoole_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@
#define SW_ZLIB_ENCODING_ANY 0x2f
#endif

#ifdef SW_HAVE_BROTLI
#include <brotli/encode.h>
#include <brotli/decode.h>
#endif

#ifdef SW_HAVE_ZSTD
#include <zstd.h>
#endif

#include <nghttp2/nghttp2.h>

enum swHttpHeaderFlag {
Expand All @@ -52,6 +61,7 @@ enum swHttpCompressMethod {
HTTP_COMPRESS_GZIP,
HTTP_COMPRESS_DEFLATE,
HTTP_COMPRESS_BR,
HTTP_COMPRESS_ZSTD,
};

namespace swoole {
Expand Down
72 changes: 61 additions & 11 deletions ext-src/swoole_http_client_coro.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,11 @@
SW_EXTERN_C_BEGIN

#include "thirdparty/swoole_http_parser.h"
#include "stubs/php_swoole_http_client_coro_arginfo.h"

#include "ext/standard/base64.h"

#ifdef SW_HAVE_ZLIB
#include <zlib.h>
#endif

#include "stubs/php_swoole_http_client_coro_arginfo.h"

SW_EXTERN_C_END

#ifdef SW_HAVE_BROTLI
#include <brotli/decode.h>
#endif

using swoole::File;
using swoole::String;
using swoole::coroutine::Socket;
Expand Down Expand Up @@ -156,6 +146,9 @@ class Client {
#endif
#ifdef SW_HAVE_BROTLI
BrotliDecoderState *brotli_decoder_state = nullptr;
#endif
#ifdef SW_HAVE_ZSTD
ZSTD_DStream *zstd_stream = nullptr;
#endif
bool bind(std::string address, int port = 0);
bool connect();
Expand Down Expand Up @@ -456,6 +449,11 @@ static int http_parser_on_header_value(swoole_http_parser *parser, const char *a
} else if (SW_STR_ISTARTS_WITH(at, length, "deflate")) {
http->compress_method = HTTP_COMPRESS_DEFLATE;
}
#endif
#ifdef SW_HAVE_ZSTD
else if (SW_STR_ISTARTS_WITH(at, length, "zstd")) {
http->compress_method = HTTP_COMPRESS_ZSTD;
}
#endif
}
#endif
Expand Down Expand Up @@ -689,6 +687,48 @@ bool Client::decompress_response(const char *in, size_t in_len) {
body->length = reserved_body_length;
return false;
}
#endif
#ifdef SW_HAVE_ZSTD
case HTTP_COMPRESS_ZSTD: {
size_t zstd_result = 0;
if (zstd_stream == nullptr) {
zstd_stream = ZSTD_createDStream();
if (!zstd_stream) {
swoole_warning("ZSTD_createDStream() failed, can not create ZSTD stream");
return false;
}

zstd_result = ZSTD_initDStream(zstd_stream);
if (ZSTD_isError(zstd_result)) {
swoole_warning("ZSTD_initDStream() failed, Error: [%s]", ZSTD_getErrorName(zstd_result));
return false;
}
}

size_t recommended_size = ZSTD_DStreamOutSize();
ZSTD_inBuffer in_buffer = {in, in_len, 0};
ZSTD_outBuffer out_buffer = {body->str + body->length, body->size - body->length, 0};
while (in_buffer.pos < in_buffer.size) {
if (sw_unlikely(out_buffer.pos == out_buffer.size)) {
if (!body->extend(recommended_size + body->size)) {
swoole_warning("ZSTD_decompressStream() failed, no memory is available");
return false;
}

body->length += out_buffer.pos;
out_buffer = {body->str + body->length, body->size - body->length, 0};
}

zstd_result = ZSTD_decompressStream(zstd_stream, &out_buffer, &in_buffer);
if (ZSTD_isError(zstd_result)) {
swoole_warning("ZSTD_decompressStream() failed, Error: [%s]", ZSTD_getErrorName(zstd_result));
return false;
}
}

body->length += out_buffer.pos;
return true;
}
#endif
default:
break;
Expand Down Expand Up @@ -1112,6 +1152,10 @@ bool Client::send_request() {
#else
#ifdef SW_HAVE_BROTLI
ZEND_STRL("br")
#else
#ifdef SW_HAVE_ZSTD
ZEND_STRL("zstd")
#endif
#endif
#endif
#endif
Expand Down Expand Up @@ -1627,6 +1671,12 @@ void Client::reset() {
BrotliDecoderDestroyInstance(brotli_decoder_state);
brotli_decoder_state = nullptr;
}
#endif
#ifdef SW_HAVE_ZSTD
if (zstd_stream) {
ZSTD_freeDStream(zstd_stream);
zstd_stream = nullptr;
}
#endif
if (has_upload_files) {
zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("uploadFiles"));
Expand Down
18 changes: 10 additions & 8 deletions ext-src/swoole_http_request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ SW_EXTERN_C_BEGIN
#include "thirdparty/php/main/SAPI.h"
SW_EXTERN_C_END

#ifdef SW_HAVE_ZLIB
#include <zlib.h>
#endif

#ifdef SW_HAVE_BROTLI
#include <brotli/encode.h>
#endif

enum http_upload_errno {
HTTP_UPLOAD_ERR_OK = 0,
HTTP_UPLOAD_ERR_INI_SIZE,
Expand Down Expand Up @@ -833,6 +825,11 @@ void HttpContext::set_compression_method(const char *accept_encoding, size_t len
} else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("deflate")) >= 0) {
accept_compression = 1;
compression_method = HTTP_COMPRESS_DEFLATE;
#ifdef SW_HAVE_ZSTD
} else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("zstd")) >= 0) {
accept_compression = 1;
compression_method = HTTP_COMPRESS_ZSTD;
#endif
} else {
accept_compression = 0;
}
Expand All @@ -848,6 +845,11 @@ const char *HttpContext::get_content_encoding() {
else if (compression_method == HTTP_COMPRESS_BR) {
return "br";
}
#endif
#ifdef SW_HAVE_ZSTD
else if (compression_method == HTTP_COMPRESS_ZSTD) {
return "zstd";
}
#endif
else {
return nullptr;
Expand Down
33 changes: 24 additions & 9 deletions ext-src/swoole_http_response.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,8 @@
*/

#include "php_swoole_http_server.h"

#include "swoole_util.h"

#ifdef SW_HAVE_ZLIB
#include <zlib.h>
#endif

#ifdef SW_HAVE_BROTLI
#include <brotli/encode.h>
#endif

BEGIN_EXTERN_C()
#include "stubs/php_swoole_http_response_arginfo.h"
END_EXTERN_C()
Expand Down Expand Up @@ -565,6 +556,30 @@ bool HttpContext::compress(const char *data, size_t length) {
return true;
}
}
#endif
#ifdef SW_HAVE_ZSTD
else if (compression_method == HTTP_COMPRESS_ZSTD) {
int zstd_compress_level = compression_level;
int zstd_max_level = ZSTD_maxCLevel();
int zstd_min_level = ZSTD_minCLevel();
zstd_compress_level = (zstd_compress_level > zstd_max_level)
? zstd_max_level
: (zstd_compress_level < zstd_min_level ? zstd_min_level : zstd_compress_level);

size_t compress_size = ZSTD_compressBound(length);
zlib_buffer = std::make_shared<String>(compress_size);;
size_t zstd_compress_result =
ZSTD_compress((void *) zlib_buffer->str, compress_size, (void *) data, length, zstd_compress_level);

if (ZSTD_isError(zstd_compress_result)) {
swoole_warning("ZSTD_compress() failed, Error: [%s]", ZSTD_getErrorName(zstd_compress_result));
return false;
}

zlib_buffer->length = zstd_compress_result;
content_compressed = 1;
return true;
}
#endif
else {
swoole_warning("Unknown compression method");
Expand Down
1 change: 1 addition & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2652,6 +2652,7 @@
<configureoption default="no" name="enable-swoole-curl" prompt="enable curl support?"/>
<configureoption default="no" name="enable-cares" prompt="enable cares support?"/>
<configureoption default="yes" name="enable-brotli" prompt="enable brotli support?"/>
<configureoption default="no" name="enable-zstd" prompt="enable zstd support?"/>
<configureoption default="no" name="enable-swoole-pgsql" prompt="enable PostgreSQL database support?"/>
<configureoption default="no" name="with-swoole-odbc" prompt="enable ODBC database support?"/>
<configureoption default="no" name="with-swoole-oracle" prompt="enable Oracle database support?"/>
Expand Down
2 changes: 2 additions & 0 deletions scripts/docker-compile-with-thread.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ cd "${__DIR__}" && cd ..
./scripts/clear.sh
phpize
./configure \
--enable-brotli \
--enable-zstd \
--enable-openssl \
--enable-sockets \
--enable-mysqlnd \
Expand Down
2 changes: 2 additions & 0 deletions scripts/docker-compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ cd "${__DIR__}" && cd ..
./scripts/clear.sh
phpize
./configure \
--enable-brotli \
--enable-zstd \
--enable-openssl \
--enable-sockets \
--enable-mysqlnd \
Expand Down
2 changes: 1 addition & 1 deletion scripts/library.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh -e
apt update
apt install -y libaio-dev libaio1 sqlite3 libsqlite3-dev unixodbc unixodbc-dev odbc-mariadb
apt install -y libaio-dev libaio1 sqlite3 libsqlite3-dev unixodbc unixodbc-dev odbc-mariadb libzstd-dev
wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip
unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip
wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip
Expand Down
48 changes: 48 additions & 0 deletions tests/swoole_http_server/zstd.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
--TEST--
swoole_http_server: support zstd compress
--SKIPIF--
<?php require __DIR__ . '/../include/skipif.inc'; ?>
--FILE--
<?php
require __DIR__ . '/../include/bootstrap.php';
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
use SwooleTest\ProcessManager;
use Swoole\Coroutine\Http\Client;
use function Swoole\Coroutine\run;

$pm = new ProcessManager();
$data = base64_encode(random_bytes(1024 * 1024));

$pm->parentFunc = function ($pid) use ($pm, $data) {
run(function () use ($pm, $data) {
$client = new Client('127.0.0.1', $pm->getFreePort());
$client->setHeaders(['Accept-Encoding' => 'zstd']);
$client->get('/');
Assert::true($client->body == $data);
Assert::true($client->headers['content-encoding'] == 'zstd');
Assert::true($client->headers['content-length'] != strlen($client->body));
});
echo "DONE\n";
$pm->kill();
};

$pm->childFunc = function () use ($pm, $data) {
$serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort());
$serv->set([
'compression_level' => 20
]);
$serv->on("workerStart", function ($serv) use ($pm) {
$pm->wakeup();
});
$serv->on('request', function ($req, $resp) use ($data) {
$resp->end($data);
});
$serv->start();
};
$pm->childFirst();
$pm->run();
?>
--EXPECT--
DONE

0 comments on commit 3cdb863

Please sign in to comment.