yajl-ruby 1.0.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of yajl-ruby might be problematic. Click here for more details.

Files changed (152) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG.md +327 -0
  4. data/Gemfile +3 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +362 -0
  7. data/Rakefile +2 -0
  8. data/benchmark/encode.rb +72 -0
  9. data/benchmark/encode_json_and_marshal.rb +42 -0
  10. data/benchmark/encode_json_and_yaml.rb +53 -0
  11. data/benchmark/http.rb +32 -0
  12. data/benchmark/parse.rb +94 -0
  13. data/benchmark/parse_json_and_marshal.rb +50 -0
  14. data/benchmark/parse_json_and_yaml.rb +55 -0
  15. data/benchmark/parse_stream.rb +54 -0
  16. data/benchmark/subjects/item.json +1 -0
  17. data/benchmark/subjects/ohai.json +1216 -0
  18. data/benchmark/subjects/ohai.marshal_dump +0 -0
  19. data/benchmark/subjects/ohai.yml +975 -0
  20. data/benchmark/subjects/twitter_search.json +1 -0
  21. data/benchmark/subjects/twitter_stream.json +430 -0
  22. data/benchmark/subjects/unicode.json +1 -0
  23. data/examples/encoding/chunked_encoding.rb +27 -0
  24. data/examples/encoding/one_shot.rb +13 -0
  25. data/examples/encoding/to_an_io.rb +12 -0
  26. data/examples/http/twitter_search_api.rb +12 -0
  27. data/examples/http/twitter_stream_api.rb +26 -0
  28. data/examples/parsing/from_file.rb +14 -0
  29. data/examples/parsing/from_stdin.rb +9 -0
  30. data/examples/parsing/from_string.rb +13 -0
  31. data/ext/yajl/api/yajl_common.h +89 -0
  32. data/ext/yajl/api/yajl_gen.h +161 -0
  33. data/ext/yajl/api/yajl_parse.h +196 -0
  34. data/ext/yajl/api/yajl_version.h +23 -0
  35. data/ext/yajl/extconf.rb +7 -0
  36. data/ext/yajl/yajl.c +164 -0
  37. data/ext/yajl/yajl_alloc.c +65 -0
  38. data/ext/yajl/yajl_alloc.h +50 -0
  39. data/ext/yajl/yajl_buf.c +119 -0
  40. data/ext/yajl/yajl_buf.h +73 -0
  41. data/ext/yajl/yajl_bytestack.h +85 -0
  42. data/ext/yajl/yajl_encode.c +201 -0
  43. data/ext/yajl/yajl_encode.h +52 -0
  44. data/ext/yajl/yajl_ext.c +905 -0
  45. data/ext/yajl/yajl_ext.h +135 -0
  46. data/ext/yajl/yajl_gen.c +344 -0
  47. data/ext/yajl/yajl_lex.c +748 -0
  48. data/ext/yajl/yajl_lex.h +135 -0
  49. data/ext/yajl/yajl_parser.c +450 -0
  50. data/ext/yajl/yajl_parser.h +82 -0
  51. data/ext/yajl/yajl_version.c +7 -0
  52. data/lib/yajl.rb +75 -0
  53. data/lib/yajl/1.8/yajl.so +0 -0
  54. data/lib/yajl/1.9/yajl.so +0 -0
  55. data/lib/yajl/bzip2.rb +11 -0
  56. data/lib/yajl/bzip2/stream_reader.rb +31 -0
  57. data/lib/yajl/bzip2/stream_writer.rb +14 -0
  58. data/lib/yajl/deflate.rb +6 -0
  59. data/lib/yajl/deflate/stream_reader.rb +43 -0
  60. data/lib/yajl/deflate/stream_writer.rb +20 -0
  61. data/lib/yajl/gzip.rb +6 -0
  62. data/lib/yajl/gzip/stream_reader.rb +30 -0
  63. data/lib/yajl/gzip/stream_writer.rb +13 -0
  64. data/lib/yajl/http_stream.rb +212 -0
  65. data/lib/yajl/json_gem.rb +15 -0
  66. data/lib/yajl/json_gem/encoding.rb +51 -0
  67. data/lib/yajl/json_gem/parsing.rb +26 -0
  68. data/lib/yajl/version.rb +3 -0
  69. data/lib/yajl/yajl.rb +2 -0
  70. data/spec/encoding/encoding_spec.rb +271 -0
  71. data/spec/global/global_spec.rb +54 -0
  72. data/spec/http/fixtures/http.bzip2.dump +0 -0
  73. data/spec/http/fixtures/http.chunked.dump +11 -0
  74. data/spec/http/fixtures/http.deflate.dump +0 -0
  75. data/spec/http/fixtures/http.error.dump +12 -0
  76. data/spec/http/fixtures/http.gzip.dump +0 -0
  77. data/spec/http/fixtures/http.html.dump +1220 -0
  78. data/spec/http/fixtures/http.raw.dump +1226 -0
  79. data/spec/http/http_delete_spec.rb +98 -0
  80. data/spec/http/http_error_spec.rb +32 -0
  81. data/spec/http/http_get_spec.rb +109 -0
  82. data/spec/http/http_post_spec.rb +123 -0
  83. data/spec/http/http_put_spec.rb +105 -0
  84. data/spec/http/http_stream_options_spec.rb +27 -0
  85. data/spec/json_gem_compatibility/compatibility_spec.rb +203 -0
  86. data/spec/parsing/active_support_spec.rb +64 -0
  87. data/spec/parsing/chunked_spec.rb +96 -0
  88. data/spec/parsing/fixtures/fail.15.json +1 -0
  89. data/spec/parsing/fixtures/fail.16.json +1 -0
  90. data/spec/parsing/fixtures/fail.17.json +1 -0
  91. data/spec/parsing/fixtures/fail.26.json +1 -0
  92. data/spec/parsing/fixtures/fail11.json +1 -0
  93. data/spec/parsing/fixtures/fail12.json +1 -0
  94. data/spec/parsing/fixtures/fail13.json +1 -0
  95. data/spec/parsing/fixtures/fail14.json +1 -0
  96. data/spec/parsing/fixtures/fail19.json +1 -0
  97. data/spec/parsing/fixtures/fail20.json +1 -0
  98. data/spec/parsing/fixtures/fail21.json +1 -0
  99. data/spec/parsing/fixtures/fail22.json +1 -0
  100. data/spec/parsing/fixtures/fail23.json +1 -0
  101. data/spec/parsing/fixtures/fail24.json +1 -0
  102. data/spec/parsing/fixtures/fail25.json +1 -0
  103. data/spec/parsing/fixtures/fail27.json +2 -0
  104. data/spec/parsing/fixtures/fail28.json +2 -0
  105. data/spec/parsing/fixtures/fail3.json +1 -0
  106. data/spec/parsing/fixtures/fail4.json +1 -0
  107. data/spec/parsing/fixtures/fail5.json +1 -0
  108. data/spec/parsing/fixtures/fail6.json +1 -0
  109. data/spec/parsing/fixtures/fail9.json +1 -0
  110. data/spec/parsing/fixtures/pass.array.json +6 -0
  111. data/spec/parsing/fixtures/pass.codepoints_from_unicode_org.json +1 -0
  112. data/spec/parsing/fixtures/pass.contacts.json +1 -0
  113. data/spec/parsing/fixtures/pass.db100.xml.json +1 -0
  114. data/spec/parsing/fixtures/pass.db1000.xml.json +1 -0
  115. data/spec/parsing/fixtures/pass.dc_simple_with_comments.json +11 -0
  116. data/spec/parsing/fixtures/pass.deep_arrays.json +1 -0
  117. data/spec/parsing/fixtures/pass.difficult_json_c_test_case.json +1 -0
  118. data/spec/parsing/fixtures/pass.difficult_json_c_test_case_with_comments.json +1 -0
  119. data/spec/parsing/fixtures/pass.doubles.json +1 -0
  120. data/spec/parsing/fixtures/pass.empty_array.json +1 -0
  121. data/spec/parsing/fixtures/pass.empty_string.json +1 -0
  122. data/spec/parsing/fixtures/pass.escaped_bulgarian.json +4 -0
  123. data/spec/parsing/fixtures/pass.escaped_foobar.json +1 -0
  124. data/spec/parsing/fixtures/pass.item.json +1 -0
  125. data/spec/parsing/fixtures/pass.json-org-sample1.json +23 -0
  126. data/spec/parsing/fixtures/pass.json-org-sample2.json +11 -0
  127. data/spec/parsing/fixtures/pass.json-org-sample3.json +26 -0
  128. data/spec/parsing/fixtures/pass.json-org-sample4-nows.json +88 -0
  129. data/spec/parsing/fixtures/pass.json-org-sample4.json +89 -0
  130. data/spec/parsing/fixtures/pass.json-org-sample5.json +27 -0
  131. data/spec/parsing/fixtures/pass.map-spain.xml.json +1 -0
  132. data/spec/parsing/fixtures/pass.ns-invoice100.xml.json +1 -0
  133. data/spec/parsing/fixtures/pass.ns-soap.xml.json +1 -0
  134. data/spec/parsing/fixtures/pass.numbers-fp-4k.json +6 -0
  135. data/spec/parsing/fixtures/pass.numbers-fp-64k.json +61 -0
  136. data/spec/parsing/fixtures/pass.numbers-int-4k.json +11 -0
  137. data/spec/parsing/fixtures/pass.numbers-int-64k.json +154 -0
  138. data/spec/parsing/fixtures/pass.twitter-search.json +1 -0
  139. data/spec/parsing/fixtures/pass.twitter-search2.json +1 -0
  140. data/spec/parsing/fixtures/pass.unicode.json +3315 -0
  141. data/spec/parsing/fixtures/pass.yelp.json +1 -0
  142. data/spec/parsing/fixtures/pass1.json +56 -0
  143. data/spec/parsing/fixtures/pass2.json +1 -0
  144. data/spec/parsing/fixtures/pass3.json +6 -0
  145. data/spec/parsing/fixtures_spec.rb +40 -0
  146. data/spec/parsing/one_off_spec.rb +85 -0
  147. data/spec/rcov.opts +3 -0
  148. data/spec/spec_helper.rb +16 -0
  149. data/tasks/compile.rake +35 -0
  150. data/tasks/rspec.rake +16 -0
  151. data/yajl-ruby.gemspec +24 -0
  152. metadata +335 -0
@@ -0,0 +1,82 @@
1
+ /*
2
+ * Copyright 2010, Lloyd Hilaiel.
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions are
6
+ * met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in
13
+ * the documentation and/or other materials provided with the
14
+ * distribution.
15
+ *
16
+ * 3. Neither the name of Lloyd Hilaiel nor the names of its
17
+ * contributors may be used to endorse or promote products derived
18
+ * from this software without specific prior written permission.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ * POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+ #ifndef __YAJL_PARSER_H__
34
+ #define __YAJL_PARSER_H__
35
+
36
+ #include "api/yajl_parse.h"
37
+ #include "yajl_bytestack.h"
38
+ #include "yajl_buf.h"
39
+
40
+
41
+ typedef enum {
42
+ yajl_state_start = 0,
43
+ yajl_state_parse_complete,
44
+ yajl_state_parse_error,
45
+ yajl_state_lexical_error,
46
+ yajl_state_map_start,
47
+ yajl_state_map_sep,
48
+ yajl_state_map_need_val,
49
+ yajl_state_map_got_val,
50
+ yajl_state_map_need_key,
51
+ yajl_state_array_start,
52
+ yajl_state_array_got_val,
53
+ yajl_state_array_need_val
54
+ } yajl_state;
55
+
56
+ struct yajl_handle_t {
57
+ const yajl_callbacks * callbacks;
58
+ void * ctx;
59
+ yajl_lexer lexer;
60
+ const char * parseError;
61
+ /* the number of bytes consumed from the last client buffer,
62
+ * in the case of an error this will be an error offset, in the
63
+ * case of an error this can be used as the error offset */
64
+ unsigned int bytesConsumed;
65
+ /* temporary storage for decoded strings */
66
+ yajl_buf decodeBuf;
67
+ /* a stack of states. access with yajl_state_XXX routines */
68
+ yajl_bytestack stateStack;
69
+ /* memory allocation routines */
70
+ yajl_alloc_funcs alloc;
71
+ };
72
+
73
+ yajl_status
74
+ yajl_do_parse(yajl_handle handle, const unsigned char * jsonText,
75
+ unsigned int jsonTextLen);
76
+
77
+ unsigned char *
78
+ yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
79
+ unsigned int jsonTextLen, int verbose);
80
+
81
+
82
+ #endif
@@ -0,0 +1,7 @@
1
+ #include "api/yajl_version.h"
2
+
3
+ int yajl_version(void)
4
+ {
5
+ return YAJL_VERSION;
6
+ }
7
+
data/lib/yajl.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'yajl/yajl'
2
+
3
+ # = Extras
4
+ # We're not going to load these auotmatically, because you might not need them ;)
5
+ #
6
+ # require 'yajl/http_stream.rb' unless defined?(Yajl::HttpStream)
7
+ # require 'yajl/gzip.rb' unless defined?(Yajl::Gzip)
8
+ # require 'yajl/deflate.rb' unless defined?(Yajl::Deflate)
9
+ # require 'yajl/bzip2.rb' unless defined?(Yajl::Bzip2)
10
+
11
+ # = Yajl
12
+ #
13
+ # Ruby bindings to the excellent Yajl (Yet Another JSON Parser) ANSI C library.
14
+ module Yajl
15
+ # For compatibility, has the same signature of Yajl::Parser.parse
16
+ def self.load(str_or_io, options={}, read_bufsize=nil, &block)
17
+ Parser.parse(str_or_io, options, read_bufsize, &block)
18
+ end
19
+
20
+ # For compatibility, has the same signature of Yajl::Encoder.encode
21
+ def self.dump(obj, *args, &block)
22
+ Encoder.encode(obj, args, &block)
23
+ end
24
+
25
+ class Parser
26
+ # A helper method for parse-and-forget use-cases
27
+ #
28
+ # +io+ is the stream to parse JSON from
29
+ #
30
+ # The +options+ hash allows you to set two parsing options - :allow_comments and :check_utf8
31
+ #
32
+ # :allow_comments accepts a boolean will enable/disable checks for in-line comments in the JSON stream
33
+ #
34
+ # :check_utf8 accepts a boolean will enable/disable UTF8 validation for the JSON stream
35
+ def self.parse(str_or_io, options={}, read_bufsize=nil, &block)
36
+ new(options).parse(str_or_io, read_bufsize, &block)
37
+ end
38
+ end
39
+
40
+ class Encoder
41
+ # A helper method for encode-and-forget use-cases
42
+ #
43
+ # Examples:
44
+ # Yajl::Encoder.encode(obj[, io, :pretty => true, :indent => "\t", &block])
45
+ #
46
+ # output = Yajl::Encoder.encode(obj[, :pretty => true, :indent => "\t", &block])
47
+ #
48
+ # +obj+ is a ruby object to encode to JSON format
49
+ #
50
+ # +io+ is the optional IO stream to encode the ruby object to.
51
+ # If +io+ isn't passed, the resulting JSON string is returned. If +io+ is passed, nil is returned.
52
+ #
53
+ # The +options+ hash allows you to set two encoding options - :pretty and :indent
54
+ #
55
+ # :pretty accepts a boolean and will enable/disable "pretty printing" the resulting output
56
+ #
57
+ # :indent accepts a string and will be used as the indent character(s) during the pretty print process
58
+ #
59
+ # If a block is passed, it will be used as (and work the same as) the +on_progress+ callback
60
+ def self.encode(obj, *args, &block)
61
+ # TODO: this code smells, any ideas?
62
+ args.flatten!
63
+ options = {}
64
+ io = nil
65
+ args.each do |arg|
66
+ if arg.is_a?(Hash)
67
+ options = arg
68
+ elsif arg.respond_to?(:read)
69
+ io = arg
70
+ end
71
+ end if args.any?
72
+ new(options).encode(obj, io, &block)
73
+ end
74
+ end
75
+ end
Binary file
Binary file
data/lib/yajl/bzip2.rb ADDED
@@ -0,0 +1,11 @@
1
+ puts "DEPRECATION WARNING: Yajl's Bzip2 support is going to be removed in 2.0"
2
+
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+
5
+ begin
6
+ require 'bzip2' unless defined?(Bzip2)
7
+ require 'yajl/bzip2/stream_reader.rb'
8
+ require 'yajl/bzip2/stream_writer.rb'
9
+ rescue LoadError => e
10
+ raise "Unable to load the bzip2 library. Is the bzip2-ruby gem installed?"
11
+ end
@@ -0,0 +1,31 @@
1
+ module Yajl
2
+ module Bzip2
3
+ # This is a wrapper around Bzip::Reader to allow it's #read method to adhere
4
+ # to the IO spec, allowing for two parameters (length, and buffer)
5
+ class StreamReader < ::Bzip2::Reader
6
+ # A helper method to allow use similar to IO#read
7
+ def read(len=nil, buffer=nil)
8
+ if val = super(len)
9
+ unless buffer.nil?
10
+ buffer.replace(val)
11
+ return buffer
12
+ end
13
+ super(len)
14
+ else
15
+ nil
16
+ end
17
+ end
18
+
19
+ # Helper method for one-off parsing from a bzip2-compressed stream
20
+ #
21
+ # See Yajl::Parser#parse for parameter documentation
22
+ def self.parse(input, options={}, buffer_size=nil, &block)
23
+ if input.is_a?(String)
24
+ input = StringIO.new(input)
25
+ end
26
+
27
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ module Yajl
2
+ module Bzip2
3
+ # A wrapper around the Bzip2::Writer class for easier JSON stream encoding
4
+ class StreamWriter < ::Bzip2::Writer
5
+
6
+ # A helper method for encoding to a bzip2-compressed stream
7
+ #
8
+ # Look up Yajl::Encoder#encode for parameter documentation
9
+ def self.encode(obj, io)
10
+ Yajl::Encoder.new.encode(obj, new(io))
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ puts "DEPRECATION WARNING: Yajl's Deflate support is going to be removed in 2.0"
2
+
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+ require 'zlib' unless defined?(Zlib)
5
+ require 'yajl/deflate/stream_reader.rb'
6
+ require 'yajl/deflate/stream_writer.rb'
@@ -0,0 +1,43 @@
1
+ module Yajl
2
+ module Deflate
3
+ # This is a wrapper around Zlib::Inflate, creating a #read method that adheres
4
+ # to the IO spec, allowing for two parameters (length, and buffer)
5
+ class StreamReader < ::Zlib::Inflate
6
+
7
+ # Wrapper to the initialize method so we can set the initial IO to parse from.
8
+ def initialize(io, options)
9
+ @io = io
10
+ super(options)
11
+ end
12
+
13
+ # A helper method to allow use similar to IO#read
14
+ def read(len=nil, buffer=nil)
15
+ if val = @io.read(len)
16
+ unless buffer.nil?
17
+ buffer.replace(inflate(val))
18
+ return buffer
19
+ end
20
+ inflate(@io.read(len))
21
+ else
22
+ nil
23
+ end
24
+ end
25
+
26
+ # Helper method for one-off parsing from a deflate-compressed stream
27
+ #
28
+ # See Yajl::Parser#parse for parameter documentation
29
+ def self.parse(input, options={}, buffer_size=nil, &block)
30
+ if input.is_a?(String)
31
+ input = StringIO.new(input)
32
+ end
33
+
34
+ if options.is_a?(Hash)
35
+ deflate_options = options.delete(:deflate_options)
36
+ Yajl::Parser.new(options).parse(new(input, deflate_options), buffer_size, &block)
37
+ elsif options.is_a?(Fixnum)
38
+ Yajl::Parser.new.parse(new(input, options), buffer_size, &block)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,20 @@
1
+ module Yajl
2
+ module Deflate
3
+ # A wrapper around the Zlib::Deflate class for easier JSON stream parsing
4
+ class StreamWriter < ::Zlib::Deflate
5
+
6
+ # A helper method to allow use similar to IO#write
7
+ def write(str)
8
+ deflate(str)
9
+ str.size unless str.nil?
10
+ end
11
+
12
+ # A helper method for one-off encoding to a deflate-compressed stream
13
+ #
14
+ # Look up Yajl::Encoder#encode for parameter documentation
15
+ def self.encode(obj, io)
16
+ Yajl::Encoder.new.encode(obj, new(io))
17
+ end
18
+ end
19
+ end
20
+ end
data/lib/yajl/gzip.rb ADDED
@@ -0,0 +1,6 @@
1
+ puts "DEPRECATION WARNING: Yajl's Gzip support is going to be removed in 2.0"
2
+
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+ require 'zlib' unless defined?(Zlib)
5
+ require 'yajl/gzip/stream_reader.rb'
6
+ require 'yajl/gzip/stream_writer.rb'
@@ -0,0 +1,30 @@
1
+ module Yajl
2
+ module Gzip
3
+ # This is a wrapper around Zlib::GzipReader to allow it's #read method to adhere
4
+ # to the IO spec, allowing for two parameters (length, and buffer)
5
+ class StreamReader < ::Zlib::GzipReader
6
+ # A helper method to allow use similar to IO#read
7
+ def read(len=nil, buffer=nil)
8
+ if val = super(len)
9
+ unless buffer.nil?
10
+ buffer.replace(val)
11
+ return buffer
12
+ end
13
+ super(len)
14
+ else
15
+ nil
16
+ end
17
+ end
18
+
19
+ # Helper method for one-off parsing from a gzip-compressed stream
20
+ #
21
+ # See Yajl::Parser#parse for parameter documentation
22
+ def self.parse(input, options={}, buffer_size=nil, &block)
23
+ if input.is_a?(String)
24
+ input = StringIO.new(input)
25
+ end
26
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Yajl
2
+ module Gzip
3
+ # Wraper around the Zlib::GzipWriter class
4
+ class StreamWriter < ::Zlib::GzipWriter
5
+ # A helper method for one-off encoding to a gzip-compressed stream
6
+ #
7
+ # Look up Yajl::Encoder#encode for parameter documentation
8
+ def self.encode(obj, io)
9
+ Yajl::Encoder.new.encode(obj, new(io))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,212 @@
1
+ puts "DEPRECATION WARNING: Yajl::HttpStream is going to be removed in 2.0"
2
+
3
+ require 'socket'
4
+ require 'yajl'
5
+ require 'yajl/version' unless defined? Yajl::VERSION
6
+ require 'uri'
7
+
8
+ module Yajl
9
+ # This module is for making HTTP requests to which the response bodies (and possibly requests in the near future)
10
+ # are streamed directly into Yajl.
11
+ class HttpStream
12
+
13
+ # This Exception is thrown when an HTTP response isn't in ALLOWED_MIME_TYPES
14
+ # and therefore cannot be parsed.
15
+ class InvalidContentType < Exception; end
16
+ class HttpError < StandardError
17
+
18
+ attr_reader :message, :headers
19
+
20
+ def initialize(message, headers)
21
+ @message = message
22
+ @headers = headers
23
+ end
24
+ end
25
+
26
+ # The mime-type we expect the response to be. If it's anything else, we can't parse it
27
+ # and an InvalidContentType is raised.
28
+ ALLOWED_MIME_TYPES = ["application/json", "text/plain"]
29
+
30
+ # Makes a basic HTTP GET request to the URI provided
31
+ def self.get(uri, opts = {}, &block)
32
+ request("GET", uri, opts, &block)
33
+ end
34
+
35
+ # Makes a basic HTTP GET request to the URI provided allowing the user to terminate the connection
36
+ def get(uri, opts = {}, &block)
37
+ initialize_socket(uri, opts)
38
+ HttpStream::get(uri, opts, &block)
39
+ rescue IOError => e
40
+ raise e unless @intentional_termination
41
+ end
42
+
43
+ # Makes a basic HTTP POST request to the URI provided
44
+ def self.post(uri, body, opts = {}, &block)
45
+ request("POST", uri, opts.merge({:body => body}), &block)
46
+ end
47
+
48
+ # Makes a basic HTTP POST request to the URI provided allowing the user to terminate the connection
49
+ def post(uri, body, opts = {}, &block)
50
+ initialize_socket(uri, opts)
51
+ HttpStream::post(uri, body, opts, &block)
52
+ rescue IOError => e
53
+ raise e unless @intentional_termination
54
+ end
55
+
56
+ # Makes a basic HTTP PUT request to the URI provided
57
+ def self.put(uri, body, opts = {}, &block)
58
+ request("PUT", uri, opts.merge({:body => body}), &block)
59
+ end
60
+
61
+ # Makes a basic HTTP PUT request to the URI provided allowing the user to terminate the connection
62
+ def put(uri, body, opts = {}, &block)
63
+ initialize_socket(uri, opts)
64
+ HttpStream::put(uri, body, opts, &block)
65
+ rescue IOError => e
66
+ raise e unless @intentional_termination
67
+ end
68
+
69
+ # Makes a basic HTTP DELETE request to the URI provided
70
+ def self.delete(uri, opts = {}, &block)
71
+ request("DELETE", uri, opts, &block)
72
+ end
73
+
74
+ # Makes a basic HTTP DELETE request to the URI provided allowing the user to terminate the connection
75
+ def delete(uri, opts = {}, &block)
76
+ initialize_socket(uri, opts)
77
+ HttpStream::delete(uri, opts, &block)
78
+ rescue IOError => e
79
+ raise e unless @intentional_termination
80
+ end
81
+
82
+ # Terminate a running HTTPStream instance
83
+ def terminate
84
+ @intentional_termination = true
85
+ @socket.close
86
+ end
87
+
88
+ protected
89
+ def self.request(method, uri, opts = {}, &block)
90
+ if uri.is_a?(String)
91
+ uri = URI.parse(uri)
92
+ end
93
+
94
+ default_headers = {
95
+ "User-Agent" => opts["User-Agent"] || "Yajl::HttpStream #{Yajl::VERSION}",
96
+ "Accept" => "*/*",
97
+ "Accept-Charset" => "utf-8"
98
+ }
99
+
100
+ if method == "POST" || method == "PUT"
101
+ default_headers["Content-Type"] = opts["Content-Type"] || "application/x-www-form-urlencoded"
102
+ body = opts.delete(:body)
103
+ if body.is_a?(Hash)
104
+ body = body.keys.collect {|param| "#{URI.escape(param.to_s)}=#{URI.escape(body[param].to_s)}"}.join('&')
105
+ end
106
+ default_headers["Content-Length"] = body.length
107
+ end
108
+
109
+ unless uri.userinfo.nil?
110
+ default_headers["Authorization"] = "Basic #{[uri.userinfo].pack('m').strip!}\r\n"
111
+ end
112
+
113
+ encodings = []
114
+ encodings << "bzip2" if defined?(Yajl::Bzip2)
115
+ encodings << "gzip" if defined?(Yajl::Gzip)
116
+ encodings << "deflate" if defined?(Yajl::Deflate)
117
+ if encodings.any?
118
+ default_headers["Accept-Encoding"] = "#{encodings.join(',')}\r\n"
119
+ end
120
+
121
+ headers = default_headers.merge(opts[:headers] || {})
122
+
123
+ socket = opts.delete(:socket) || TCPSocket.new(uri.host, uri.port)
124
+ request = "#{method} #{uri.path}#{uri.query ? "?"+uri.query : nil} HTTP/1.1\r\n"
125
+ request << "Host: #{uri.host}\r\n"
126
+ headers.each do |k, v|
127
+ request << "#{k}: #{v}\r\n"
128
+ end
129
+ request << "\r\n"
130
+ if method == "POST" || method == "PUT"
131
+ request << body
132
+ end
133
+ socket.write(request)
134
+ response_head = {}
135
+ response_head[:headers] = {}
136
+
137
+ socket.each_line do |line|
138
+ if line == "\r\n" # end of the headers
139
+ break
140
+ else
141
+ header = line.split(": ")
142
+ if header.size == 1
143
+ header = header[0].split(" ")
144
+ response_head[:version] = header[0]
145
+ response_head[:code] = header[1].to_i
146
+ response_head[:msg] = header[2]
147
+ # this is the response code line
148
+ else
149
+ response_head[:headers][header[0]] = header[1].strip
150
+ end
151
+ end
152
+ end
153
+
154
+ if (response_head[:code] != 200)
155
+ raise HttpError.new("Code 200 expected got #{response_head[:code]}", response_head[:headers])
156
+ end
157
+
158
+ parser = Yajl::Parser.new(opts)
159
+ parser.on_parse_complete = block if block_given?
160
+ if response_head[:headers]["Transfer-Encoding"] == 'chunked'
161
+ if block_given?
162
+ chunkLeft = 0
163
+ while !socket.eof? && (line = socket.gets)
164
+ break if line.match /^0.*?\r\n/
165
+ next if line == "\r\n"
166
+ size = line.hex
167
+ json = socket.read(size)
168
+ next if json.nil?
169
+ chunkLeft = size-json.size
170
+ if chunkLeft == 0
171
+ parser << json
172
+ else
173
+ # received only part of the chunk, grab the rest
174
+ parser << socket.read(chunkLeft)
175
+ end
176
+ end
177
+ else
178
+ raise Exception, "Chunked responses detected, but no block given to handle the chunks."
179
+ end
180
+ else
181
+ content_type = response_head[:headers]["Content-Type"].split(';')
182
+ content_type = content_type.first
183
+ if ALLOWED_MIME_TYPES.include?(content_type)
184
+ case response_head[:headers]["Content-Encoding"]
185
+ when "gzip"
186
+ return Yajl::Gzip::StreamReader.parse(socket, opts, &block)
187
+ when "deflate"
188
+ return Yajl::Deflate::StreamReader.parse(socket, opts.merge({:deflate_options => -Zlib::MAX_WBITS}), &block)
189
+ when "bzip2"
190
+ return Yajl::Bzip2::StreamReader.parse(socket, opts, &block)
191
+ else
192
+ return parser.parse(socket)
193
+ end
194
+ else
195
+ raise InvalidContentType, "The response MIME type #{content_type}"
196
+ end
197
+ end
198
+ ensure
199
+ socket.close if !socket.nil? and !socket.closed?
200
+ end
201
+
202
+ private
203
+ # Initialize socket and add it to the opts
204
+ def initialize_socket(uri, opts = {})
205
+ return if opts[:socket]
206
+
207
+ @socket = TCPSocket.new(uri.host, uri.port)
208
+ opts.merge!({:socket => @socket})
209
+ @intentional_termination = false
210
+ end
211
+ end
212
+ end