From 5bfd12f19e5242bee4914c6c1c25e7a6ace61127 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 13:35:56 -0800 Subject: [PATCH 1/9] =?UTF-8?q?=E2=9C=82=EF=B8=8F=20whitespace=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ext/yajl/yajl_ext.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index f9e91c14..0a7c94be 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -664,7 +664,7 @@ static VALUE rb_yajl_projector_filter_subtree(yajl_event_stream_t parser, VALUE } rb_raise(cParseError, "expected left bracket or brace, actually read %s", yajl_tok_name(event.token)); - + return Qnil; } @@ -751,7 +751,7 @@ static VALUE rb_yajl_projector_filter_object_subtree(yajl_event_stream_t parser, } else { val = rb_yajl_projector_build_simple_value(parser, value_event); } - + rb_hash_aset(hsh, key, val); peek_comma: @@ -858,7 +858,7 @@ static VALUE rb_yajl_projector_build_simple_value(yajl_event_stream_t parser, ya val = rb_cstr2inum(buf, 10); } free(buf); - + return val; case yajl_tok_string:; From c4c174950da597d0deb357de8d06740ca1fef656 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 13:51:47 -0800 Subject: [PATCH 2/9] freeze string keys --- ext/yajl/yajl_ext.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index 0a7c94be..277bccfc 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -752,6 +752,7 @@ static VALUE rb_yajl_projector_filter_object_subtree(yajl_event_stream_t parser, val = rb_yajl_projector_build_simple_value(parser, value_event); } + rb_str_freeze(key); rb_hash_aset(hsh, key, val); peek_comma: From b86a2609de1e07bb8b33046d3a82679195f3b3d7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 14:03:25 -0800 Subject: [PATCH 3/9] Fix leak when non-object or array is passed to parser If something besides an object or array is passed to the parser, it will raise an exception and the lexer isn't free'd. This patch moves the token checks up a little so that we can free the lexer before raising an exception. This program will leak memory without bounds: ```ruby require 'yajl' loop do begin stream = StringIO.new('foo') projector = Yajl::Projector.new(stream) projector.project({"name" => nil}) rescue Yajl::ParseError end end ``` --- ext/yajl/yajl_ext.c | 31 +++++++++++++++++++------------ spec/projection/projection.rb | 10 +++++++++- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index 277bccfc..1caffda4 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -663,8 +663,6 @@ static VALUE rb_yajl_projector_filter_subtree(yajl_event_stream_t parser, VALUE return rb_yajl_projector_filter_object_subtree(parser, schema, event); } - rb_raise(cParseError, "expected left bracket or brace, actually read %s", yajl_tok_name(event.token)); - return Qnil; } @@ -739,14 +737,14 @@ static VALUE rb_yajl_projector_filter_object_subtree(yajl_event_stream_t parser, yajl_event_t value_event = yajl_event_stream_next(parser, 1); VALUE val; - if (value_event.token == yajl_tok_left_bracket || value_event.token == yajl_tok_left_brace) { - VALUE key_schema; - if (schema == Qnil) { - key_schema = Qnil; - } else { - key_schema = rb_hash_aref(schema, key); - } + VALUE key_schema; + if (schema == Qnil) { + key_schema = Qnil; + } else { + key_schema = rb_hash_aref(schema, key); + } + if (value_event.token == yajl_tok_left_bracket || value_event.token == yajl_tok_left_brace) { val = rb_yajl_projector_filter_subtree(parser, key_schema, value_event); } else { val = rb_yajl_projector_build_simple_value(parser, value_event); @@ -936,13 +934,22 @@ static VALUE rb_yajl_projector_project(VALUE self, VALUE schema) { .lexer = yajl_lex_alloc(&allocFuncs, 0, 1), }; - VALUE result = rb_yajl_projector_filter_subtree(&parser, schema, yajl_event_stream_next(&parser, 1)); - - yajl_lex_free(parser.lexer); + yajl_event_t event = yajl_event_stream_next(&parser, 1); RB_GC_GUARD(stream); RB_GC_GUARD(buffer); + VALUE result; + + if (event.token == yajl_tok_left_brace || event.token == yajl_tok_left_bracket) { + result = rb_yajl_projector_filter_subtree(&parser, schema, event); + } else { + yajl_lex_free(parser.lexer); + rb_raise(cParseError, "expected left bracket or brace, actually read %s", yajl_tok_name(event.token)); + } + + yajl_lex_free(parser.lexer); + return result; } diff --git a/spec/projection/projection.rb b/spec/projection/projection.rb index 09a34cb0..e232b50e 100644 --- a/spec/projection/projection.rb +++ b/spec/projection/projection.rb @@ -18,6 +18,14 @@ expect(projection['age']).to eql(nil) end + it "should raise an exception and not leak memory" do + stream = StringIO.new('foo') + projector = Yajl::Projector.new(stream) + expect { + projector.project({"name" => nil}) + }.to raise_error(Yajl::ParseError) + end + def project(schema, over: "", json: nil, stream: nil) if stream.nil? if json.nil? @@ -438,4 +446,4 @@ def project(schema, over: "", json: nil, stream: nil) it "handles objects whose second key has escape sequences" do expect(project(nil, json: '{"foo": "bar", "\ud83d\ude00": "grinning face"}')).to eql({"foo" => "bar", "😀" => "grinning face"}) end -end \ No newline at end of file +end From 6e69b5a4e23be91b5fb39e5920e36470f3331990 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 14:19:20 -0800 Subject: [PATCH 4/9] Pull token conditionals in to one place This commit removes token type check duplication. Before this commit, all callers of `rb_yajl_projector_filter` would do the same token type checks that the body of the function would do. This commit simply pushes the type checks down to the body of the function. --- ext/yajl/yajl_ext.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index 1caffda4..22ae2f68 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -652,18 +652,19 @@ static void rb_yajl_projector_ignore_container(yajl_event_stream_t parser); static VALUE rb_yajl_projector_build_simple_value(yajl_event_stream_t parser, yajl_event_t event); static VALUE rb_yajl_projector_build_string(yajl_event_stream_t parser, yajl_event_t event); -static VALUE rb_yajl_projector_filter_subtree(yajl_event_stream_t parser, VALUE schema, yajl_event_t event) { +static VALUE rb_yajl_projector_filter(yajl_event_stream_t parser, VALUE schema, yajl_event_t event) { assert(parser->stream); - if (event.token == yajl_tok_left_brace) { - return rb_yajl_projector_filter_array_subtree(parser, schema, event); - } - - if (event.token == yajl_tok_left_bracket) { - return rb_yajl_projector_filter_object_subtree(parser, schema, event); + switch(event.token) { + case yajl_tok_left_brace: + return rb_yajl_projector_filter_array_subtree(parser, schema, event); + break; + case yajl_tok_left_bracket: + return rb_yajl_projector_filter_object_subtree(parser, schema, event); + break; + default: + return rb_yajl_projector_build_simple_value(parser, event); } - - return Qnil; } static VALUE rb_yajl_projector_filter_array_subtree(yajl_event_stream_t parser, VALUE schema, yajl_event_t event) { @@ -678,12 +679,7 @@ static VALUE rb_yajl_projector_filter_array_subtree(yajl_event_stream_t parser, break; } - VALUE val; - if (event.token == yajl_tok_left_brace || event.token == yajl_tok_left_bracket) { - val = rb_yajl_projector_filter_subtree(parser, schema, event); - } else { - val = rb_yajl_projector_build_simple_value(parser, event); - } + VALUE val = rb_yajl_projector_filter(parser, schema, event); rb_ary_push(ary, val); event = yajl_event_stream_next(parser, 0); @@ -736,7 +732,6 @@ static VALUE rb_yajl_projector_filter_object_subtree(yajl_event_stream_t parser, yajl_event_t value_event = yajl_event_stream_next(parser, 1); - VALUE val; VALUE key_schema; if (schema == Qnil) { key_schema = Qnil; @@ -744,11 +739,7 @@ static VALUE rb_yajl_projector_filter_object_subtree(yajl_event_stream_t parser, key_schema = rb_hash_aref(schema, key); } - if (value_event.token == yajl_tok_left_bracket || value_event.token == yajl_tok_left_brace) { - val = rb_yajl_projector_filter_subtree(parser, key_schema, value_event); - } else { - val = rb_yajl_projector_build_simple_value(parser, value_event); - } + VALUE val = rb_yajl_projector_filter(parser, key_schema, value_event); rb_str_freeze(key); rb_hash_aset(hsh, key, val); @@ -942,7 +933,7 @@ static VALUE rb_yajl_projector_project(VALUE self, VALUE schema) { VALUE result; if (event.token == yajl_tok_left_brace || event.token == yajl_tok_left_bracket) { - result = rb_yajl_projector_filter_subtree(&parser, schema, event); + result = rb_yajl_projector_filter(&parser, schema, event); } else { yajl_lex_free(parser.lexer); rb_raise(cParseError, "expected left bracket or brace, actually read %s", yajl_tok_name(event.token)); From 3ba7fcdd23fa92affa060b37fd6799a79e5c7e22 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 14:46:09 -0800 Subject: [PATCH 5/9] fix segv on bad "simple values" The simple value de-serialization method seems to be missing some branches in its switch statement. This adds a missing branch --- ext/yajl/yajl_ext.c | 3 +++ spec/projection/projection.rb | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index 22ae2f68..a05bd292 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -858,6 +858,9 @@ static VALUE rb_yajl_projector_build_simple_value(yajl_event_stream_t parser, ya case yajl_tok_eof:; rb_raise(cParseError, "unexpected eof while constructing value"); + case yajl_tok_comma: + rb_raise(cParseError, "unexpected comma while constructing value"); + default:; assert(0); } diff --git a/spec/projection/projection.rb b/spec/projection/projection.rb index e232b50e..2d9354ea 100644 --- a/spec/projection/projection.rb +++ b/spec/projection/projection.rb @@ -26,6 +26,14 @@ }.to raise_error(Yajl::ParseError) end + it "should raise an exception and not segv" do + stream = StringIO.new('[,,,,]') + projector = Yajl::Projector.new(stream) + expect { + projector.project({"name" => nil}) + }.to raise_error(Yajl::ParseError) + end + def project(schema, over: "", json: nil, stream: nil) if stream.nil? if json.nil? From a09dd19e99516ee074513ff15eb7a4c3b3387026 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 14:48:30 -0800 Subject: [PATCH 6/9] Fix memory leak by protecting calls to the parser This program would exhibit unbounded memory growth: ```ruby loop do begin stream = StringIO.new('[,,,,,]') projector = Yajl::Projector.new(stream) projector.project({"name" => nil}) rescue Yajl::ParseError end end ``` This patch fixes it --- ext/yajl/yajl_ext.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index a05bd292..f558df60 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -905,6 +905,13 @@ static VALUE rb_yajl_projector_build_string(yajl_event_stream_t parser, yajl_eve } } +static VALUE rb_protected_yajl_projector_filter(VALUE pointer) { + VALUE *args = (VALUE *)pointer; + return rb_yajl_projector_filter((struct yajl_event_stream_s *)args[0], + args[1], + *(yajl_event_t *)args[2]); +} + /* * Document-method: project */ @@ -934,15 +941,23 @@ static VALUE rb_yajl_projector_project(VALUE self, VALUE schema) { RB_GC_GUARD(buffer); VALUE result; + int state = 0; if (event.token == yajl_tok_left_brace || event.token == yajl_tok_left_bracket) { - result = rb_yajl_projector_filter(&parser, schema, event); + VALUE args[3]; + args[0] = &parser; + args[1] = schema; + args[2] = &event; + result = rb_protect(rb_protected_yajl_projector_filter, + (VALUE)args, + &state); } else { yajl_lex_free(parser.lexer); rb_raise(cParseError, "expected left bracket or brace, actually read %s", yajl_tok_name(event.token)); } yajl_lex_free(parser.lexer); + if (state) rb_jump_tag(state); return result; } From db10c28805ebc41019071372cf152cac26bb917c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 15:00:51 -0800 Subject: [PATCH 7/9] fix some clang warnings --- ext/yajl/yajl_ext.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index f558df60..786d705f 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -45,12 +45,12 @@ static void yajl_check_and_fire_callback(void * ctx) { /* No need to do any of this if the callback isn't even setup */ if (wrapper->parse_complete_callback != Qnil) { - int len = RARRAY_LEN(wrapper->builderStack); + long len = RARRAY_LEN(wrapper->builderStack); if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) { rb_funcall(wrapper->parse_complete_callback, intern_call, 1, rb_ary_pop(wrapper->builderStack)); } } else { - int len = RARRAY_LEN(wrapper->builderStack); + long len = RARRAY_LEN(wrapper->builderStack); if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) { wrapper->objectsFound++; if (wrapper->objectsFound > 1) { @@ -82,7 +82,7 @@ static char *yajl_raise_encode_error_for_status(yajl_gen_status status, VALUE ob static void yajl_set_static_value(void * ctx, VALUE val) { yajl_parser_wrapper * wrapper; VALUE lastEntry, hash; - int len; + long len; GetParser((VALUE)ctx, wrapper); @@ -204,7 +204,7 @@ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) { case T_BIGNUM: str = rb_funcall(obj, intern_to_s, 0); cptr = RSTRING_PTR(str); - len = RSTRING_LEN(str); + len = (unsigned int)RSTRING_LEN(str); if (memcmp(cptr, "NaN", 3) == 0 || memcmp(cptr, "Infinity", 8) == 0 || memcmp(cptr, "-Infinity", 9) == 0) { rb_raise(cEncodeError, "'%s' is an invalid number", cptr); } @@ -212,7 +212,7 @@ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) { break; case T_STRING: cptr = RSTRING_PTR(obj); - len = RSTRING_LEN(obj); + len = (unsigned int)RSTRING_LEN(obj); CHECK_STATUS(yajl_gen_string(w->encoder, (const unsigned char *)cptr, len)); break; default: @@ -220,13 +220,13 @@ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) { str = rb_funcall(obj, intern_to_json, 0); Check_Type(str, T_STRING); cptr = RSTRING_PTR(str); - len = RSTRING_LEN(str); + len = (unsigned int)RSTRING_LEN(str); CHECK_STATUS(yajl_gen_number(w->encoder, cptr, len)); } else { str = rb_funcall(obj, intern_to_s, 0); Check_Type(str, T_STRING); cptr = RSTRING_PTR(str); - len = RSTRING_LEN(str); + len = (unsigned int)RSTRING_LEN(str); CHECK_STATUS(yajl_gen_string(w->encoder, (const unsigned char *)cptr, len)); } break; @@ -495,13 +495,13 @@ static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) { if (TYPE(input) == T_STRING) { cptr = RSTRING_PTR(input); - len = RSTRING_LEN(input); + len = (unsigned int)RSTRING_LEN(input); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } else if (rb_respond_to(input, intern_io_read)) { VALUE parsed = rb_str_new(0, FIX2LONG(rbufsize)); while (rb_funcall(input, intern_io_read, 2, rbufsize, parsed) != Qnil) { cptr = RSTRING_PTR(parsed); - len = RSTRING_LEN(parsed); + len = (unsigned int)RSTRING_LEN(parsed); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } } else { @@ -541,7 +541,7 @@ static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) { if (wrapper->parse_complete_callback != Qnil) { const char * cptr = RSTRING_PTR(chunk); - len = RSTRING_LEN(chunk); + len = (unsigned int)RSTRING_LEN(chunk); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } else { rb_raise(cParseError, "The on_parse_complete callback isn't setup, parsing useless."); @@ -616,11 +616,11 @@ static yajl_event_t yajl_event_stream_next(yajl_event_stream_t parser, int pop) yajl_tok token; if (pop == 0) { //printf("peeking %p %ld %d\n", RSTRING_PTR(parser->buffer), RSTRING_LEN(parser->buffer), parser->offset); - token = yajl_lex_peek(parser->lexer, (const unsigned char *)RSTRING_PTR(parser->buffer), RSTRING_LEN(parser->buffer), parser->offset); + token = yajl_lex_peek(parser->lexer, (const unsigned char *)RSTRING_PTR(parser->buffer), (unsigned int)RSTRING_LEN(parser->buffer), parser->offset); //printf("peeked event %d\n", token); if (token == yajl_tok_eof) { - parser->offset = RSTRING_LEN(parser->buffer); + parser->offset = (unsigned int)RSTRING_LEN(parser->buffer); continue; } @@ -630,7 +630,7 @@ static yajl_event_t yajl_event_stream_next(yajl_event_stream_t parser, int pop) } //printf("popping\n"); - token = yajl_lex_lex(parser->lexer, (const unsigned char *)RSTRING_PTR(parser->buffer), RSTRING_LEN(parser->buffer), &parser->offset, (const unsigned char **)&event.buf, &event.len); + token = yajl_lex_lex(parser->lexer, (const unsigned char *)RSTRING_PTR(parser->buffer), (unsigned int)RSTRING_LEN(parser->buffer), &parser->offset, (const unsigned char **)&event.buf, &event.len); //printf("popped event %d\n", token); if (token == yajl_tok_eof) { @@ -930,7 +930,7 @@ static VALUE rb_yajl_projector_project(VALUE self, VALUE schema) { .stream = stream, .buffer = buffer, - .offset = buffer_size, + .offset = (unsigned int)buffer_size, .lexer = yajl_lex_alloc(&allocFuncs, 0, 1), }; @@ -945,9 +945,9 @@ static VALUE rb_yajl_projector_project(VALUE self, VALUE schema) { if (event.token == yajl_tok_left_brace || event.token == yajl_tok_left_bracket) { VALUE args[3]; - args[0] = &parser; + args[0] = (VALUE)&parser; args[1] = schema; - args[2] = &event; + args[2] = (VALUE)&event; result = rb_protect(rb_protected_yajl_projector_filter, (VALUE)args, &state); From a5c4030e2650544c23997558787c766429fdc3af Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 15:13:53 -0800 Subject: [PATCH 8/9] fix another segv --- ext/yajl/yajl_ext.c | 7 +++++-- spec/projection/projection.rb | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index 786d705f..dc31f7c2 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -220,13 +220,13 @@ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) { str = rb_funcall(obj, intern_to_json, 0); Check_Type(str, T_STRING); cptr = RSTRING_PTR(str); - len = (unsigned int)RSTRING_LEN(str); + len = (unsigned int)RSTRING_LEN(str); CHECK_STATUS(yajl_gen_number(w->encoder, cptr, len)); } else { str = rb_funcall(obj, intern_to_s, 0); Check_Type(str, T_STRING); cptr = RSTRING_PTR(str); - len = (unsigned int)RSTRING_LEN(str); + len = (unsigned int)RSTRING_LEN(str); CHECK_STATUS(yajl_gen_string(w->encoder, (const unsigned char *)cptr, len)); } break; @@ -861,6 +861,9 @@ static VALUE rb_yajl_projector_build_simple_value(yajl_event_stream_t parser, ya case yajl_tok_comma: rb_raise(cParseError, "unexpected comma while constructing value"); + case yajl_tok_colon: + rb_raise(cParseError, "unexpected colon while constructing value"); + default:; assert(0); } diff --git a/spec/projection/projection.rb b/spec/projection/projection.rb index 2d9354ea..0e15f750 100644 --- a/spec/projection/projection.rb +++ b/spec/projection/projection.rb @@ -34,6 +34,14 @@ }.to raise_error(Yajl::ParseError) end + it "should raise an exception and not segv on colons" do + stream = StringIO.new('[::::]') + projector = Yajl::Projector.new(stream) + expect { + projector.project({"name" => nil}) + }.to raise_error(Yajl::ParseError) + end + def project(schema, over: "", json: nil, stream: nil) if stream.nil? if json.nil? From 1e505293a2e04b05d573020e9d575d85dbae1082 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 12 Dec 2017 15:27:28 -0800 Subject: [PATCH 9/9] Make projection parser behave the same as regular parser The projection parser should raise the same exceptions that the regular JSON parser raises when parsing a bad document --- ext/yajl/yajl_ext.c | 2 +- spec/projection/projection.rb | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/ext/yajl/yajl_ext.c b/ext/yajl/yajl_ext.c index dc31f7c2..a8faea0f 100644 --- a/ext/yajl/yajl_ext.c +++ b/ext/yajl/yajl_ext.c @@ -792,7 +792,7 @@ static void rb_yajl_projector_ignore_value(yajl_event_stream_t parser) { return; } - rb_raise(cStandardError, "unknown value type to ignore %s", yajl_tok_name(value_event.token)); + rb_raise(cParseError, "unknown value type to ignore %s", yajl_tok_name(value_event.token)); } /* diff --git a/spec/projection/projection.rb b/spec/projection/projection.rb index 0e15f750..9558507c 100644 --- a/spec/projection/projection.rb +++ b/spec/projection/projection.rb @@ -42,6 +42,39 @@ }.to raise_error(Yajl::ParseError) end + it "should behave the same way as the regular parser on bad tokens like comma" do + bad_json = '{"name": "keith", "age":, 27}' + stream = StringIO.new(bad_json) + projector = Yajl::Projector.new(stream) + expect { + projector.project({"name" => nil}) + }.to raise_error(capture_exception_for(bad_json).class) + end + + it "should behave the same way as the regular parser on bad tokens like colon" do + bad_json = '{"name": "keith", "age":: 27}' + stream = StringIO.new(bad_json) + projector = Yajl::Projector.new(stream) + expect { + projector.project({"name" => nil}) + }.to raise_error(capture_exception_for(bad_json).class) + end + + it "should behave the same way as the regular parser on not enough json" do + bad_json = '{"name": "keith", "age":' + stream = StringIO.new(bad_json) + projector = Yajl::Projector.new(stream) + expect { + projector.project({"name" => nil}) + }.to raise_error(capture_exception_for(bad_json).class) + end + + def capture_exception_for(bad_json) + Yajl::Parser.new.parse(bad_json) + rescue Exception => e + e + end + def project(schema, over: "", json: nil, stream: nil) if stream.nil? if json.nil?