diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2012-05-07 17:04:56 +0200 |
---|---|---|
committer | Ben Noordhuis <info@bnoordhuis.nl> | 2012-05-09 04:44:04 +0200 |
commit | ee437c0557c9f3a6a1960d3a8d64e09166d7048d (patch) | |
tree | bd95faf8d55355aca7728912ab6e3d96c886dad9 /src | |
parent | 8c758e127c9d81791cb55043abb418ce624dfcaf (diff) | |
download | node-ee437c0557c9f3a6a1960d3a8d64e09166d7048d.tar.gz |
zlib: fix error reporting
This commit is a back-port of the changes on the master branch.
Fixes #3230.
Diffstat (limited to 'src')
-rw-r--r-- | src/node_zlib.cc | 257 |
1 files changed, 200 insertions, 57 deletions
diff --git a/src/node_zlib.cc b/src/node_zlib.cc index fe7be7412..e34c5ce1a 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -20,15 +20,15 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include <v8.h> +#include "v8.h" #include <errno.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> -#include <zlib.h> -#include <node.h> -#include <node_buffer.h> +#include "zlib.h" +#include "node.h" +#include "node_buffer.h" namespace node { @@ -36,6 +36,7 @@ using namespace v8; static Persistent<String> callback_sym; +static Persistent<String> onerror_sym; enum node_zlib_mode { DEFLATE = 1, @@ -47,8 +48,6 @@ enum node_zlib_mode { UNZIP }; -template <node_zlib_mode mode> class ZCtx; - void InitZlib(v8::Handle<v8::Object> target); @@ -56,18 +55,19 @@ void InitZlib(v8::Handle<v8::Object> target); /** * Deflate/Inflate */ -template <node_zlib_mode mode> class ZCtx : public ObjectWrap { +class ZCtx : public ObjectWrap { public: - ZCtx() : ObjectWrap() { - } + ZCtx(node_zlib_mode mode) : ObjectWrap(), dictionary_(NULL), mode_(mode) {} ~ZCtx() { - if (mode == DEFLATE || mode == GZIP || mode == DEFLATERAW) { + if (mode_ == DEFLATE || mode_ == GZIP || mode_ == DEFLATERAW) { (void)deflateEnd(&strm_); - } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) { + } else if (mode_ == INFLATE || mode_ == GUNZIP || mode_ == INFLATERAW) { (void)inflateEnd(&strm_); } + + if (dictionary_ != NULL) delete[] dictionary_; } // write(flush, in, in_off, in_len, out, out_off, out_len) @@ -75,7 +75,7 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { HandleScope scope; assert(args.Length() == 7); - ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This()); + ZCtx *ctx = ObjectWrap::Unwrap<ZCtx>(args.This()); assert(ctx->init_done_ && "write before init"); assert(!ctx->write_in_progress_ && "write already in progress"); @@ -124,8 +124,8 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { uv_queue_work(uv_default_loop(), work_req, - ZCtx<mode>::Process, - ZCtx<mode>::After); + ZCtx::Process, + ZCtx::After); ctx->Ref(); @@ -138,28 +138,46 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { // for a single write() call, until all of the input bytes have // been consumed. static void Process(uv_work_t* work_req) { - ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_); + ZCtx *ctx = container_of(work_req, ZCtx, work_req_); // If the avail_out is left at 0, then it means that it ran out // of room. If there was avail_out left over, then it means // that all of the input was consumed. - int err; - switch (mode) { + switch (ctx->mode_) { case DEFLATE: case GZIP: case DEFLATERAW: - err = deflate(&ctx->strm_, ctx->flush_); + ctx->err_ = deflate(&ctx->strm_, ctx->flush_); break; case UNZIP: case INFLATE: case GUNZIP: case INFLATERAW: - err = inflate(&ctx->strm_, ctx->flush_); + ctx->err_ = inflate(&ctx->strm_, ctx->flush_); + + // If data was encoded with dictionary + if (ctx->err_ == Z_NEED_DICT) { + assert(ctx->dictionary_ != NULL && "Stream has no dictionary"); + if (ctx->dictionary_ != NULL) { + + // Load it + ctx->err_ = inflateSetDictionary(&ctx->strm_, + ctx->dictionary_, + ctx->dictionary_len_); + assert(ctx->err_ == Z_OK && "Failed to set dictionary"); + if (ctx->err_ == Z_OK) { + + // And try to decode again + ctx->err_ = inflate(&ctx->strm_, ctx->flush_); + } + } + } break; default: assert(0 && "wtf?"); } - assert(err != Z_STREAM_ERROR); + + // pass any errors back to the main thread to deal with. // now After will emit the output, and // either schedule another call to Process, @@ -169,7 +187,20 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { // v8 land! static void After(uv_work_t* work_req) { HandleScope scope; - ZCtx<mode> *ctx = container_of(work_req, ZCtx<mode>, work_req_); + ZCtx *ctx = container_of(work_req, ZCtx, work_req_); + + // Acceptable error states depend on the type of zlib stream. + switch (ctx->err_) { + case Z_OK: + case Z_STREAM_END: + case Z_BUF_ERROR: + // normal statuses, not fatal + break; + default: + // something else. + ZCtx::Error(ctx, "Zlib error"); + return; + } Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out); Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in); @@ -185,9 +216,37 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { ctx->Unref(); } + static void Error(ZCtx *ctx, const char *msg_) { + const char *msg; + if (ctx->strm_.msg != NULL) { + msg = ctx->strm_.msg; + } else { + msg = msg_; + } + + assert(ctx->handle_->Get(onerror_sym)->IsFunction() && + "Invalid error handler"); + HandleScope scope; + Local<Value> args[2] = { String::New(msg), + Local<Value>::New(Number::New(ctx->err_)) }; + MakeCallback(ctx->handle_, "onerror", ARRAY_SIZE(args), args); + + // no hope of rescue. + ctx->Unref(); + } + static Handle<Value> New(const Arguments& args) { HandleScope scope; - ZCtx<mode> *ctx = new ZCtx<mode>(); + if (args.Length() < 1 || !args[0]->IsInt32()) { + return ThrowException(Exception::TypeError(String::New("Bad argument"))); + } + node_zlib_mode mode = (node_zlib_mode) args[0]->Int32Value(); + + if (mode < DEFLATE || mode > UNZIP) { + return ThrowException(Exception::TypeError(String::New("Bad argument"))); + } + + ZCtx *ctx = new ZCtx(mode); ctx->Wrap(args.This()); return args.This(); } @@ -196,10 +255,10 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { static Handle<Value> Init(const Arguments& args) { HandleScope scope; - assert(args.Length() == 4 && - "init(windowBits, level, memLevel, strategy)"); + assert((args.Length() == 4 || args.Length() == 5) && + "init(windowBits, level, memLevel, strategy, [dictionary])"); - ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This()); + ZCtx *ctx = ObjectWrap::Unwrap<ZCtx>(args.This()); int windowBits = args[0]->Uint32Value(); assert((windowBits >= 8 && windowBits <= 15) && "invalid windowBits"); @@ -217,12 +276,35 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { strategy == Z_FIXED || strategy == Z_DEFAULT_STRATEGY) && "invalid strategy"); - Init(ctx, level, windowBits, memLevel, strategy); + char* dictionary = NULL; + size_t dictionary_len = 0; + if (args.Length() >= 5 && Buffer::HasInstance(args[4])) { + Local<Object> dictionary_ = args[4]->ToObject(); + + dictionary_len = Buffer::Length(dictionary_); + dictionary = new char[dictionary_len]; + + memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len); + } + + Init(ctx, level, windowBits, memLevel, strategy, + dictionary, dictionary_len); + SetDictionary(ctx); + return Undefined(); + } + + static Handle<Value> Reset(const Arguments &args) { + HandleScope scope; + + ZCtx *ctx = ObjectWrap::Unwrap<ZCtx>(args.This()); + + Reset(ctx); + SetDictionary(ctx); return Undefined(); } static void Init(ZCtx *ctx, int level, int windowBits, int memLevel, - int strategy) { + int strategy, char* dictionary, size_t dictionary_len) { ctx->level_ = level; ctx->windowBits_ = windowBits; ctx->memLevel_ = memLevel; @@ -234,43 +316,93 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { ctx->flush_ = Z_NO_FLUSH; - if (mode == GZIP || mode == GUNZIP) { + ctx->err_ = Z_OK; + + if (ctx->mode_ == GZIP || ctx->mode_ == GUNZIP) { ctx->windowBits_ += 16; } - if (mode == UNZIP) { + if (ctx->mode_ == UNZIP) { ctx->windowBits_ += 32; } - if (mode == DEFLATERAW || mode == INFLATERAW) { + if (ctx->mode_ == DEFLATERAW || ctx->mode_ == INFLATERAW) { ctx->windowBits_ *= -1; } - int err; - switch (mode) { + switch (ctx->mode_) { case DEFLATE: case GZIP: case DEFLATERAW: - err = deflateInit2(&(ctx->strm_), - ctx->level_, - Z_DEFLATED, - ctx->windowBits_, - ctx->memLevel_, - ctx->strategy_); + ctx->err_ = deflateInit2(&ctx->strm_, + ctx->level_, + Z_DEFLATED, + ctx->windowBits_, + ctx->memLevel_, + ctx->strategy_); break; case INFLATE: case GUNZIP: case INFLATERAW: case UNZIP: - err = inflateInit2(&(ctx->strm_), ctx->windowBits_); + ctx->err_ = inflateInit2(&ctx->strm_, ctx->windowBits_); break; default: assert(0 && "wtf?"); } + if (ctx->err_ != Z_OK) { + ZCtx::Error(ctx, "Init error"); + } + + + ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary); + ctx->dictionary_len_ = dictionary_len; + ctx->write_in_progress_ = false; ctx->init_done_ = true; - assert(err == Z_OK); + } + + static void SetDictionary(ZCtx* ctx) { + if (ctx->dictionary_ == NULL) return; + + ctx->err_ = Z_OK; + + switch (ctx->mode_) { + case DEFLATE: + case DEFLATERAW: + ctx->err_ = deflateSetDictionary(&ctx->strm_, + ctx->dictionary_, + ctx->dictionary_len_); + break; + default: + break; + } + + if (ctx->err_ != Z_OK) { + ZCtx::Error(ctx, "Failed to set dictionary"); + } + } + + static void Reset(ZCtx* ctx) { + ctx->err_ = Z_OK; + + switch (ctx->mode_) { + case DEFLATE: + case DEFLATERAW: + ctx->err_ = deflateReset(&ctx->strm_); + break; + case INFLATE: + case INFLATERAW: + ctx->err_ = inflateReset(&ctx->strm_); + break; + default: + break; + } + + if (ctx->err_ != Z_OK) { + ZCtx::Error(ctx, "Failed to reset stream"); + } } private: @@ -283,6 +415,11 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { int memLevel_; int strategy_; + int err_; + + Bytef* dictionary_; + size_t dictionary_len_; + int flush_; int chunk_size_; @@ -290,31 +427,26 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap { bool write_in_progress_; uv_work_t work_req_; + node_zlib_mode mode_; }; -#define NODE_ZLIB_CLASS(mode, name) \ - { \ - Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx<mode>::New); \ - z->InstanceTemplate()->SetInternalFieldCount(1); \ - NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx<mode>::Write); \ - NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx<mode>::Init); \ - z->SetClassName(String::NewSymbol(name)); \ - target->Set(String::NewSymbol(name), z->GetFunction()); \ - } - void InitZlib(Handle<Object> target) { HandleScope scope; - NODE_ZLIB_CLASS(INFLATE, "Inflate") - NODE_ZLIB_CLASS(DEFLATE, "Deflate") - NODE_ZLIB_CLASS(INFLATERAW, "InflateRaw") - NODE_ZLIB_CLASS(DEFLATERAW, "DeflateRaw") - NODE_ZLIB_CLASS(GZIP, "Gzip") - NODE_ZLIB_CLASS(GUNZIP, "Gunzip") - NODE_ZLIB_CLASS(UNZIP, "Unzip") + Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx::New); + + z->InstanceTemplate()->SetInternalFieldCount(1); + + NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write); + NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx::Init); + NODE_SET_PROTOTYPE_METHOD(z, "reset", ZCtx::Reset); + + z->SetClassName(String::NewSymbol("Zlib")); + target->Set(String::NewSymbol("Zlib"), z->GetFunction()); callback_sym = NODE_PSYMBOL("callback"); + onerror_sym = NODE_PSYMBOL("onerror"); NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH); NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH); @@ -322,6 +454,8 @@ void InitZlib(Handle<Object> target) { NODE_DEFINE_CONSTANT(target, Z_FULL_FLUSH); NODE_DEFINE_CONSTANT(target, Z_FINISH); NODE_DEFINE_CONSTANT(target, Z_BLOCK); + + // return/error codes NODE_DEFINE_CONSTANT(target, Z_OK); NODE_DEFINE_CONSTANT(target, Z_STREAM_END); NODE_DEFINE_CONSTANT(target, Z_NEED_DICT); @@ -331,6 +465,7 @@ void InitZlib(Handle<Object> target) { NODE_DEFINE_CONSTANT(target, Z_MEM_ERROR); NODE_DEFINE_CONSTANT(target, Z_BUF_ERROR); NODE_DEFINE_CONSTANT(target, Z_VERSION_ERROR); + NODE_DEFINE_CONSTANT(target, Z_NO_COMPRESSION); NODE_DEFINE_CONSTANT(target, Z_BEST_SPEED); NODE_DEFINE_CONSTANT(target, Z_BEST_COMPRESSION); @@ -342,6 +477,14 @@ void InitZlib(Handle<Object> target) { NODE_DEFINE_CONSTANT(target, Z_DEFAULT_STRATEGY); NODE_DEFINE_CONSTANT(target, ZLIB_VERNUM); + NODE_DEFINE_CONSTANT(target, DEFLATE); + NODE_DEFINE_CONSTANT(target, INFLATE); + NODE_DEFINE_CONSTANT(target, GZIP); + NODE_DEFINE_CONSTANT(target, GUNZIP); + NODE_DEFINE_CONSTANT(target, DEFLATERAW); + NODE_DEFINE_CONSTANT(target, INFLATERAW); + NODE_DEFINE_CONSTANT(target, UNZIP); + target->Set(String::NewSymbol("ZLIB_VERSION"), String::New(ZLIB_VERSION)); } |