if (EXPECTED((*s)->ops->dtor)) {
(*s)->ops->dtor(*s);
}
+
if (EXPECTED(ss = (*s)->ops->init(*s))) {
+ ss->flags &= ~PHP_HTTP_ENCODING_STREAM_DIRTY;
*s = ss;
return SUCCESS;
}
ZEND_RESULT_CODE php_http_encoding_stream_update(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len)
{
- if (UNEXPECTED(!s->ops->update)) {
- return FAILURE;
+ ZEND_RESULT_CODE rc = FAILURE;
+
+ if (EXPECTED(s->ops->update)) {
+ rc = s->ops->update(s, in_str, in_len, out_str, out_len);
}
- return s->ops->update(s, in_str, in_len, out_str, out_len);
+
+ s->flags |= PHP_HTTP_ENCODING_STREAM_DIRTY;
+
+ return rc;
}
ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *out_len)
zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s)
{
if (!s->ops->done) {
- return 0;
+ return !(s->flags & PHP_HTTP_ENCODING_STREAM_DIRTY);
}
return s->ops->done(s);
}
if (!s->ops->finish) {
*out_str = NULL;
*out_len = 0;
+
+ s->flags &= ~PHP_HTTP_ENCODING_STREAM_DIRTY;
+
return SUCCESS;
}
return s->ops->finish(s, out_str, out_len);
php_http_encoding_stream_object_t *new_obj, *old_obj = PHP_HTTP_OBJ(NULL, object);
php_http_encoding_stream_t *cpy = php_http_encoding_stream_copy(old_obj->stream, NULL);
+ if (!cpy) {
+ return NULL;
+ }
+
new_obj = php_http_encoding_stream_object_new_ex(old_obj->zo.ce, cpy);
zend_objects_clone_members(&new_obj->zo, &old_obj->zo);
extern PHP_MINIT_FUNCTION(http_encoding);
#define PHP_HTTP_ENCODING_STREAM_PERSISTENT 0x01000000
+#define PHP_HTTP_ENCODING_STREAM_DIRTY 0x02000000
#define PHP_HTTP_ENCODING_STREAM_FLUSH_NONE 0x00000000
#define PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC 0x00100000
} else {
*encoded = NULL;
*encoded_len = 0;
+ php_http_buffer_dtor(&out);
}
return SUCCESS;
}
static ZEND_RESULT_CODE enbrotli_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len)
{
- return enbrotli_flush_ex(s, BROTLI_OPERATION_FINISH, encoded, encoded_len);
+ ZEND_RESULT_CODE rc;
+
+ do {
+ rc = enbrotli_flush_ex(s, BROTLI_OPERATION_FINISH, encoded, encoded_len);
+ } while (SUCCESS == rc && !BrotliEncoderIsFinished(s->ctx));
+
+ return rc;
}
static zend_bool enbrotli_done(php_http_encoding_stream_t *s)
{
- return BrotliEncoderIsFinished(s->ctx);
+ return !(s->flags & PHP_HTTP_ENCODING_STREAM_DIRTY)
+ || BrotliEncoderIsFinished(s->ctx);
}
static void enbrotli_dtor(php_http_encoding_stream_t *s)
*decoded = out.data;
*decoded_len = out.used;
} else {
+ php_http_buffer_dtor(&out);
*decoded = NULL;
*decoded_len = 0;
}
php_http_buffer_dtor(&out);
- php_error_docref(NULL, E_WARNING, "Could not brotli decode data: %s", BrotliDecoderErrorString(rc));
+ php_error_docref(NULL, E_WARNING, "Could not brotli decode data: %s", BrotliDecoderErrorString(BrotliDecoderGetErrorCode(s->ctx)));
return FAILURE;
}
static zend_bool debrotli_done(php_http_encoding_stream_t *s)
{
- return BrotliDecoderIsFinished(s->ctx);
+ return !BrotliDecoderIsUsed(s->ctx) || BrotliDecoderIsFinished(s->ctx);
}
static void debrotli_dtor(php_http_encoding_stream_t *s)
size_t len;
zend_long flags = PHP_HTTP_ENBROTLI_MODE_GENERIC | PHP_HTTP_ENBROTLI_WBITS_DEF | PHP_HTTP_ENBROTLI_LEVEL_DEF;
- if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len, &flags)) {
+ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &len, &flags)) {
char *enc_str = NULL;
size_t enc_len;
ulong hexlen;
} PHP_HTTP_FILTER_BUFFER(chunked_decode);
-typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(zlib);
+typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(stream);
static PHP_HTTP_FILTER_FUNCTION(chunked_decode)
{
return PSFS_PASS_ON;
}
-static PHP_HTTP_FILTER_OPS(chunked_decode) = {
- PHP_HTTP_FILTER_FUNC(chunked_decode),
- PHP_HTTP_FILTER_DTOR(chunked_decode),
- "http.chunked_decode"
-};
-
-static PHP_HTTP_FILTER_OPS(chunked_encode) = {
- PHP_HTTP_FILTER_FUNC(chunked_encode),
- NULL,
- "http.chunked_encode"
-};
-
-static PHP_HTTP_FILTER_FUNCTION(zlib)
+static PHP_HTTP_FILTER_FUNCTION(stream)
{
php_stream_bucket *ptr, *nxt;
- PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract);
+ PHP_HTTP_FILTER_BUFFER(stream) *buffer = Z_PTR(this->abstract);
if (bytes_consumed) {
*bytes_consumed = 0;
}
#if DBG_FILTER
- fprintf(stderr, "update: deflate (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos);
+ fprintf(stderr, "update: compress (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos);
#endif
if (encoded) {
}
#if DBG_FILTER
- fprintf(stderr, "flush: deflate (-> %zu)\n", encoded_len);
+ fprintf(stderr, "flush: compress (-> %zu)\n", encoded_len);
#endif
if (encoded) {
}
#if DBG_FILTER
- fprintf(stderr, "finish: deflate (-> %zu)\n", encoded_len);
+ fprintf(stderr, "finish: compress (-> %zu)\n", encoded_len);
#endif
if (encoded) {
return PSFS_PASS_ON;
}
-static PHP_HTTP_FILTER_DESTRUCTOR(zlib)
+
+static PHP_HTTP_FILTER_DESTRUCTOR(stream)
{
- PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract);
+ PHP_HTTP_FILTER_BUFFER(stream) *buffer = Z_PTR(this->abstract);
php_http_encoding_stream_free(&buffer);
}
+static PHP_HTTP_FILTER_OPS(chunked_decode) = {
+ PHP_HTTP_FILTER_FUNC(chunked_decode),
+ PHP_HTTP_FILTER_DTOR(chunked_decode),
+ "http.chunked_decode"
+};
+
+static PHP_HTTP_FILTER_OPS(chunked_encode) = {
+ PHP_HTTP_FILTER_FUNC(chunked_encode),
+ NULL,
+ "http.chunked_encode"
+};
+
static PHP_HTTP_FILTER_OPS(deflate) = {
- PHP_HTTP_FILTER_FUNC(zlib),
- PHP_HTTP_FILTER_DTOR(zlib),
+ PHP_HTTP_FILTER_FUNC(stream),
+ PHP_HTTP_FILTER_DTOR(stream),
"http.deflate"
};
static PHP_HTTP_FILTER_OPS(inflate) = {
- PHP_HTTP_FILTER_FUNC(zlib),
- PHP_HTTP_FILTER_DTOR(zlib),
+ PHP_HTTP_FILTER_FUNC(stream),
+ PHP_HTTP_FILTER_DTOR(stream),
"http.inflate"
};
+static PHP_HTTP_FILTER_OPS(brotli_encode) = {
+ PHP_HTTP_FILTER_FUNC(stream),
+ PHP_HTTP_FILTER_DTOR(stream),
+ "http.brotli_encode"
+};
+
+static PHP_HTTP_FILTER_OPS(brotli_decode) = {
+ PHP_HTTP_FILTER_FUNC(stream),
+ PHP_HTTP_FILTER_DTOR(stream),
+ "http.brotli_decode"
+};
+
#if PHP_VERSION_ID >= 70200
static php_stream_filter *http_filter_create(const char *name, zval *params, uint8_t p)
#else
} else
if (!strcasecmp(name, "http.inflate")) {
- PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
+ PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags))) {
if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) {
} else
if (!strcasecmp(name, "http.deflate")) {
- PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
+ PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags))) {
if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) {
php_http_encoding_stream_free(&b);
}
}
+ } else
+
+ if (!strcasecmp(name, "http.brotli_encode")) {
+ PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
+
+ if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_enbrotli_ops(), flags))) {
+ if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(brotli_encode), b, p))) {
+ php_http_encoding_stream_free(&b);
+ }
+ }
+ } else
+
+ if (!strcasecmp(name, "http.brotli_decode")) {
+ PHP_HTTP_FILTER_BUFFER(stream) *b = NULL;
+
+ if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_debrotli_ops(), flags))) {
+ if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(brotli_decode), b, p))) {
+ php_http_encoding_stream_free(&b);
+ }
+ }
}
return f;
--- /dev/null
+--TEST--
+encoding stream brotli static
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$file = file_get_contents(__FILE__);
+var_dump($file ===
+ http\Encoding\Stream\Debrotli::decode(
+ http\Encoding\Stream\Enbrotli::encode(
+ $file, http\Encoding\Stream\Enbrotli::MODE_GENERIC
+ )
+ )
+);
+var_dump($file ===
+ http\Encoding\Stream\Debrotli::decode(
+ http\Encoding\Stream\Enbrotli::encode(
+ $file, http\Encoding\Stream\Enbrotli::MODE_TEXT
+ )
+ )
+);
+var_dump($file ===
+ http\Encoding\Stream\Debrotli::decode(
+ http\Encoding\Stream\Enbrotli::encode(
+ $file, http\Encoding\Stream\Enbrotli::MODE_FONT
+ )
+ )
+);
+
+?>
+DONE
+--EXPECT--
+Test
+bool(true)
+bool(true)
+bool(true)
+DONE
--- /dev/null
+--TEST--
+encoding stream brotli auto flush
+--SKIPIF--
+<?php
+include "skipif.inc";
+class_exists("http\\Encoding\\Stream\\Enbrotli") or die("SKIP need brotli support");
+
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$defl = new http\Encoding\Stream\Enbrotli(http\Encoding\Stream::FLUSH_FULL);
+$infl = new http\Encoding\Stream\Debrotli;
+
+for ($f = fopen(__FILE__, "rb"); !feof($f); $data = fread($f, 0x100)) {
+ if (isset($data)) {
+ if ($data !== $d=$infl->update($defl->update($data))) {
+ printf("uh-oh »%s« != »%s«\n", $data, $d);
+ }
+ }
+}
+
+echo $infl->update($defl->finish());
+echo $infl->finish();
+
+var_dump($infl->done(), $defl->done());
+?>
+DONE
+--EXPECT--
+Test
+bool(true)
+bool(true)
+DONE
--- /dev/null
+--TEST--
+encoding stream brotli without flush
+--SKIPIF--
+<?php
+include "skipif.inc";
+class_exists("http\\Encoding\\Stream\\Enbrotli") or die("SKIP need brotli support");
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$defl = new http\Encoding\Stream\Enbrotli;
+$infl = new http\Encoding\Stream\Debrotli;
+$file = file(__FILE__);
+$data = "";
+foreach ($file as $line) {
+ if (strlen($temp = $defl->update($line))) {
+ foreach(str_split($temp) as $byte) {
+ $data .= $infl->update($byte);
+ }
+ }
+}
+if (strlen($temp = $defl->finish())) {
+ $data .= $infl->update($temp);
+}
+$data .= $infl->finish();
+var_dump(implode("", $file) === $data);
+?>
+DONE
+--EXPECT--
+Test
+bool(true)
+DONE
--- /dev/null
+--TEST--
+encoding stream brotli with explicit flush
+--SKIPIF--
+<?php
+include "skipif.inc";
+class_exists("http\\Encoding\\Stream\\Enbrotli") or die("SKIP need brotli support");
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$enc = new http\Encoding\Stream\Enbrotli;
+$dec = new http\Encoding\Stream\Debrotli;
+$file = file(__FILE__);
+$data = "";
+foreach ($file as $line) {
+ $data .= $dec->flush();
+ if (strlen($temp = $enc->update($line))) {
+ $data .= $dec->update($temp);
+ $data .= $dec->flush();
+ }
+ if (strlen($temp = $enc->flush())) {
+ $data .= $dec->update($temp);
+ $data .= $dec->flush();
+ }
+}
+if (strlen($temp = $enc->finish())) {
+ $data .= $dec->update($temp);
+}
+var_dump($enc->done());
+$data .= $dec->finish();
+var_dump($dec->done());
+var_dump(implode("", $file) === $data);
+
+?>
+DONE
+--EXPECT--
+Test
+bool(true)
+bool(true)
+bool(true)
+DONE
--- /dev/null
+--TEST--
+encoding stream brotli error
+--SKIPIF--
+<?php
+include "skipif.inc";
+class_exists("http\\Encoding\\Stream\\Enbrotli") or die("SKIP need brotli support");
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+var_dump(http\Encoding\Stream\Debrotli::decode("if this goes through, something's pretty wrong"));
+
+?>
+DONE
+--EXPECTF--
+Test
+
+Warning: http\Encoding\Stream\Debrotli::decode(): Could not brotli decode data: %s in %s on line %d
+bool(false)
+DONE
--- /dev/null
+--TEST--
+brotli filter
+--SKIPIF--
+<?php
+include "skipif.inc";
+class_exists("http\\Encoding\\Stream\\Enbrotli", false) or die("SKIP need brotli support");
+?>
+--FILE--
+<?php
+list($in, $out) = stream_socket_pair(
+ STREAM_PF_UNIX,
+ STREAM_SOCK_STREAM,
+ STREAM_IPPROTO_IP
+);
+stream_filter_append($in, "http.brotli_decode", STREAM_FILTER_READ);
+stream_filter_append($out, "http.brotli_encode", STREAM_FILTER_WRITE,
+ http\Encoding\Stream\Enbrotli::LEVEL_MAX);
+
+$file = file(__FILE__);
+foreach ($file as $line) {
+ fwrite($out, $line);
+ fflush($out);
+}
+fclose($out);
+if (implode("",$file) !== ($read = fread($in, filesize(__FILE__)))) {
+ echo "got: $read\n";
+}
+fclose($in);
+?>
+DONE
+--EXPECT--
+DONE