const char *e_ptr = encoded;
*decoded_len = 0;
- *decoded = ecalloc(1, encoded_len);
+ *decoded = ecalloc(1, encoded_len + 1);
while ((encoded + encoded_len - e_ptr) > 0) {
ulong chunk_len = 0, rest;
php_http_error(HE_NOTICE, PHP_HTTP_E_ENCODING, "Data does not seem to be chunked encoded");
memcpy(*decoded, encoded, encoded_len);
*decoded_len = encoded_len;
+ decoded[*decoded_len] = '\0';
return encoded + encoded_len;
} else {
efree(*decoded);
{
TSRMLS_FETCH_FROM_CTX(from->ts);
- if (!from->ops->copy) {
- return NULL;
+ if (from->ops->copy) {
+ int freeme;
+ php_http_encoding_stream_t *ns;
+
+ if ((freeme = !to)) {
+ to = pemalloc(sizeof(*to), (from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+ }
+ memset(to, 0, sizeof(*to));
+
+ to->flags = from->flags;
+ to->ops = from->ops;
+ TSRMLS_SET_CTX(to->ts);
+
+ if ((ns = to->ops->copy(from, to))) {
+ return ns;
+ } else {
+ return to;
+ }
+
+ if (freeme) {
+ pefree(to, (to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT));
+ }
}
- return from->ops->copy(from, php_http_encoding_stream_init(to, from->ops, from->flags TSRMLS_CC));
+ return NULL;
}
PHP_HTTP_API STATUS php_http_encoding_stream_reset(php_http_encoding_stream_t **s)
static php_http_encoding_stream_t *deflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
{
- z_streamp from_ctx = from->ctx, to_ctx = to->ctx;
-
- deflateCopy(to_ctx, from_ctx);
- php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER_VAL(from_ctx->opaque), PHP_HTTP_BUFFER_LEN(from_ctx->opaque));
+ int status, p = to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
+ z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p);
+ TSRMLS_FETCH_FROM_CTX(from->ts);
- return to;
+ if (Z_OK == (status = deflateCopy(to_ctx, from_ctx))) {
+ if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
+ php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER_VAL(from_ctx->opaque), PHP_HTTP_BUFFER_LEN(from_ctx->opaque));
+ to->ctx = to_ctx;
+ return to;
+ }
+ deflateEnd(to_ctx);
+ status = Z_MEM_ERROR;
+ }
+ php_http_error(HE_WARNING, PHP_HTTP_E_ENCODING, "Failed to copy deflate encoding stream: %s", zError(status));
+ return NULL;
}
static php_http_encoding_stream_t *inflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
{
- z_streamp from_ctx = from->ctx, to_ctx = to->ctx;
-
- inflateCopy(to_ctx, from_ctx);
- php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER_VAL(from_ctx->opaque), PHP_HTTP_BUFFER_LEN(from_ctx->opaque));
+ int status, p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
+ z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p);
+ TSRMLS_FETCH_FROM_CTX(from->ts);
- return to;
+ if (Z_OK == (status = inflateCopy(to_ctx, from_ctx))) {
+ if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) {
+ php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER_VAL(from_ctx->opaque), PHP_HTTP_BUFFER_LEN(from_ctx->opaque));
+ to->ctx = to_ctx;
+ return to;
+ }
+ inflateEnd(to_ctx);
+ status = Z_MEM_ERROR;
+ }
+ php_http_error(HE_WARNING, PHP_HTTP_E_ENCODING, "Failed to copy inflate encoding stream: %s", zError(status));
+ return NULL;
}
static php_http_encoding_stream_t *dechunk_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to)
{
- struct dechunk_ctx *from_ctx = from->ctx, *to_ctx = to->ctx;
-
- to_ctx->hexlen = from_ctx->hexlen;
- to_ctx->zeroed = from_ctx->zeroed;
- php_http_buffer_append(&to_ctx->buffer, PHP_HTTP_BUFFER_VAL(&from_ctx->buffer), PHP_HTTP_BUFFER_LEN(&from_ctx->buffer));
+ int p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT;
+ struct dechunk_ctx *from_ctx = from->ctx, *to_ctx = pemalloc(sizeof(*to_ctx), p);
+ TSRMLS_FETCH_FROM_CTX(from->ts);
- return to;
+ if (php_http_buffer_init_ex(&to_ctx->buffer, PHP_HTTP_BUFFER_DEFAULT_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0)) {
+ to_ctx->hexlen = from_ctx->hexlen;
+ to_ctx->zeroed = from_ctx->zeroed;
+ php_http_buffer_append(&to_ctx->buffer, PHP_HTTP_BUFFER_VAL(&from_ctx->buffer), PHP_HTTP_BUFFER_LEN(&from_ctx->buffer));
+ to->ctx = to_ctx;
+ return to;
+ }
+ pefree(to_ctx, p);
+ php_http_error(HE_WARNING, PHP_HTTP_E_ENCODING, "Failed to copy inflate encoding stream: out of memory");
+ return NULL;
}
static STATUS deflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len)
if (SUCCESS == php_http_encoding_stream_finish(obj->stream, &encoded_str, &encoded_len)) {
if (SUCCESS == php_http_encoding_stream_reset(&obj->stream)) {
- RETURN_STRINGL(encoded_str, encoded_len, 0);
+ if (encoded_str) {
+ RETURN_STRINGL(encoded_str, encoded_len, 0);
+ } else {
+ RETURN_EMPTY_STRING();
+ }
} else {
STR_FREE(encoded_str);
}
{
char *str;
int len;
- zval *zlen;
+ zval *zlen = NULL;
if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &str, &len, &zlen)) {
const char *end_ptr;
--- /dev/null
+<?php
+
+class EncodingStreamTest extends PHPUnit_Framework_TestCase {
+ function testChunkStatic() {
+ $file = file(__FILE__);
+ $cenc = array_reduce(
+ $file,
+ function($data, $line) {
+ return $data . sprintf("%lx\r\n%s\r\n", strlen($line), $line);
+ }
+ ) . "0\r\n";
+
+ $this->assertEquals(implode("", $file), http\Encoding\Stream\Dechunk::decode($cenc));
+ }
+
+ function testChunkNoteEncoded() {
+ $s = "this is apparently not encodded\n";
+ $this->assertEquals($s, @http\Encoding\Stream\Dechunk::decode($s));
+ }
+
+ function testChunkNotEncodedNotice() {
+ $this->setExpectedException("PHPUnit_Framework_Error_Notice",
+ "Data does not seem to be chunked encoded");
+ $s = "this is apparently not encodded\n";
+ http\Encoding\Stream\Dechunk::decode($s);
+ }
+
+ function testChunkNotEncodedFail() {
+ $s = "3\nis \nbetter than\n1\n";
+ $this->assertNotEquals($s, @http\Encoding\Stream\Dechunk::decode($s));
+ }
+
+ function testChunkNotEncodedWarning1() {
+ $this->setExpectedException("PHPUnit_Framework_Error_Warning",
+ "Expected LF at pos 8 of 20 but got 0x74");
+ $s = "3\nis \nbetter than\n1\n";
+ http\Encoding\Stream\Dechunk::decode($s);
+ }
+
+ function testChunkNotEncodedWarning2() {
+ $this->setExpectedException("PHPUnit_Framework_Error_Warning",
+ "Expected CRLF at pos 10 of 24 but got 0x74 0x74");
+ $s = "3\r\nis \r\nbetter than\r\n1\r\n";
+ http\Encoding\Stream\Dechunk::decode($s);
+ }
+
+ function testChunkNotEncodedWarning3() {
+ $this->setExpectedException("PHPUnit_Framework_Error_Warning",
+ "Expected chunk size at pos 6 of 27 but got trash");
+ $s = "3\nis \nreally better than\n1\n";
+ http\Encoding\Stream\Dechunk::decode($s);
+ }
+
+ function testChunkFlush() {
+ $dech = new http\Encoding\Stream\Dechunk(http\Encoding\Stream::FLUSH_FULL);
+ $file = file(__FILE__);
+ $data = "";
+ foreach ($file as $i => $line) {
+ $dech = clone $dech;
+ if ($i % 2) {
+ $data .= $dech->update(sprintf("%lx\r\n%s\r\n", strlen($line), $line));
+ } else {
+ $data .= $dech->update(sprintf("%lx\r\n", strlen($line)));
+ $data .= $dech->update($line);
+ $data .= $dech->update("\r\n");
+ }
+ $this->assertFalse($dech->done());
+ }
+ $data .= $dech->update("0\r\n");
+ $this->assertTrue($dech->done());
+ $data .= $dech->finish();
+ $this->assertEquals(implode("", $file), $data);
+ }
+
+ function testZlibStatic() {
+ $file = file_get_contents(__FILE__);
+ $this->assertEquals($file,
+ http\Encoding\Stream\Inflate::decode(
+ http\Encoding\Stream\Deflate::encode(
+ $file, http\Encoding\Stream\Deflate::TYPE_GZIP
+ )
+ )
+ );
+ $this->assertEquals($file,
+ http\Encoding\Stream\Inflate::decode(
+ http\Encoding\Stream\Deflate::encode(
+ $file, http\Encoding\Stream\Deflate::TYPE_ZLIB
+ )
+ )
+ );
+ $this->assertEquals($file,
+ http\Encoding\Stream\Inflate::decode(
+ http\Encoding\Stream\Deflate::encode(
+ $file, http\Encoding\Stream\Deflate::TYPE_RAW
+ )
+ )
+ );
+ }
+
+ function testZlibAutoFlush() {
+ $defl = new http\Encoding\Stream\Deflate(http\Encoding\Stream::FLUSH_FULL);
+ $infl = new http\Encoding\Stream\Inflate;
+
+ for ($f = fopen(__FILE__, "rb"); !feof($f); $data = fread($f, 0x100)) {
+ $infl = clone $infl;
+ $defl = clone $defl;
+ if (isset($data)) {
+ $this->assertEquals($data, $infl->update($defl->update($data)));
+ }
+ }
+
+ echo $infl->update($defl->finish());
+ echo $infl->finish();
+ }
+
+ function testZlibWithoutFlush() {
+ $defl = new http\Encoding\Stream\Deflate;
+ $infl = new http\Encoding\Stream\Inflate;
+ $file = file(__FILE__);
+ $data = "";
+ foreach ($file as $line) {
+ $infl = clone $infl;
+ $defl = clone $defl;
+ 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();
+ $this->assertEquals(implode("", $file), $data);
+ }
+
+ function testZlibWithExplicitFlush() {
+ $defl = new http\Encoding\Stream\Deflate;
+ $infl = new http\Encoding\Stream\Inflate;
+ $file = file(__FILE__);
+ $data = "";
+ foreach ($file as $line) {
+ if (strlen($temp = $defl->update($line))) {
+ $data .= $infl->update($temp);
+ }
+ if (strlen($temp = $defl->flush())) {
+ $data .= $infl->update($temp);
+ }
+ $this->assertTrue($defl->done());
+ }
+ if (strlen($temp = $defl->finish())) {
+ $data .= $infl->update($temp);
+ }
+ $this->assertTrue($defl->done());
+ $data .= $infl->finish();
+ $this->assertTrue($infl->done());
+ $this->assertEquals(implode("", $file), $data);
+ }
+
+ function testInflateError() {
+ $this->setExpectedException("PHPUnit_Framework_Error_Warning",
+ "Could not inflate data: data error");
+ http\Encoding\Stream\Inflate::decode("if this goes through, something's pretty wrong");
+ }
+}