yajl-ruby 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. data/.gitignore +5 -0
  2. data/CHANGELOG.md +164 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +264 -0
  5. data/Rakefile +29 -0
  6. data/VERSION.yml +4 -0
  7. data/benchmark/encode.rb +46 -0
  8. data/benchmark/encode_json_and_marshal.rb +35 -0
  9. data/benchmark/encode_json_and_yaml.rb +47 -0
  10. data/benchmark/http.rb +30 -0
  11. data/benchmark/parse.rb +49 -0
  12. data/benchmark/parse_json_and_marshal.rb +47 -0
  13. data/benchmark/parse_json_and_yaml.rb +56 -0
  14. data/benchmark/parse_stream.rb +48 -0
  15. data/benchmark/subjects/contacts.json +1 -0
  16. data/benchmark/subjects/contacts.marshal_dump +0 -0
  17. data/benchmark/subjects/contacts.yml +114685 -0
  18. data/benchmark/subjects/item.json +1 -0
  19. data/benchmark/subjects/ohai.json +1216 -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/http/twitter_search_api.rb +15 -0
  24. data/examples/http/twitter_stream_api.rb +25 -0
  25. data/examples/parsing/from_file.rb +14 -0
  26. data/examples/parsing/from_stdin.rb +9 -0
  27. data/examples/parsing/from_string.rb +15 -0
  28. data/ext/api/yajl_common.h +85 -0
  29. data/ext/api/yajl_gen.h +123 -0
  30. data/ext/api/yajl_parse.h +182 -0
  31. data/ext/extconf.rb +8 -0
  32. data/ext/yajl.c +157 -0
  33. data/ext/yajl_alloc.c +65 -0
  34. data/ext/yajl_alloc.h +50 -0
  35. data/ext/yajl_buf.c +119 -0
  36. data/ext/yajl_buf.h +73 -0
  37. data/ext/yajl_bytestack.h +85 -0
  38. data/ext/yajl_encode.c +179 -0
  39. data/ext/yajl_encode.h +44 -0
  40. data/ext/yajl_ext.c +774 -0
  41. data/ext/yajl_ext.h +74 -0
  42. data/ext/yajl_gen.c +290 -0
  43. data/ext/yajl_lex.c +744 -0
  44. data/ext/yajl_lex.h +135 -0
  45. data/ext/yajl_parser.c +447 -0
  46. data/ext/yajl_parser.h +79 -0
  47. data/lib/yajl.rb +80 -0
  48. data/lib/yajl/bzip2.rb +11 -0
  49. data/lib/yajl/bzip2/stream_reader.rb +29 -0
  50. data/lib/yajl/bzip2/stream_writer.rb +15 -0
  51. data/lib/yajl/deflate.rb +6 -0
  52. data/lib/yajl/deflate/stream_reader.rb +37 -0
  53. data/lib/yajl/deflate/stream_writer.rb +21 -0
  54. data/lib/yajl/gzip.rb +6 -0
  55. data/lib/yajl/gzip/stream_reader.rb +28 -0
  56. data/lib/yajl/gzip/stream_writer.rb +14 -0
  57. data/lib/yajl/http_stream.rb +101 -0
  58. data/lib/yajl/json_gem.rb +69 -0
  59. data/spec/encoding/encoding_spec.rb +186 -0
  60. data/spec/http/fixtures/http.bzip2.dump +0 -0
  61. data/spec/http/fixtures/http.deflate.dump +0 -0
  62. data/spec/http/fixtures/http.gzip.dump +0 -0
  63. data/spec/http/fixtures/http.raw.dump +12 -0
  64. data/spec/http/http_spec.rb +94 -0
  65. data/spec/json_gem_compatibility/compatibility_spec.rb +170 -0
  66. data/spec/parsing/active_support_spec.rb +68 -0
  67. data/spec/parsing/chunked_spec.rb +98 -0
  68. data/spec/parsing/fixtures/fail.15.json +1 -0
  69. data/spec/parsing/fixtures/fail.16.json +1 -0
  70. data/spec/parsing/fixtures/fail.17.json +1 -0
  71. data/spec/parsing/fixtures/fail.26.json +1 -0
  72. data/spec/parsing/fixtures/fail11.json +1 -0
  73. data/spec/parsing/fixtures/fail12.json +1 -0
  74. data/spec/parsing/fixtures/fail13.json +1 -0
  75. data/spec/parsing/fixtures/fail14.json +1 -0
  76. data/spec/parsing/fixtures/fail19.json +1 -0
  77. data/spec/parsing/fixtures/fail20.json +1 -0
  78. data/spec/parsing/fixtures/fail21.json +1 -0
  79. data/spec/parsing/fixtures/fail22.json +1 -0
  80. data/spec/parsing/fixtures/fail23.json +1 -0
  81. data/spec/parsing/fixtures/fail24.json +1 -0
  82. data/spec/parsing/fixtures/fail25.json +1 -0
  83. data/spec/parsing/fixtures/fail27.json +2 -0
  84. data/spec/parsing/fixtures/fail28.json +2 -0
  85. data/spec/parsing/fixtures/fail3.json +1 -0
  86. data/spec/parsing/fixtures/fail4.json +1 -0
  87. data/spec/parsing/fixtures/fail5.json +1 -0
  88. data/spec/parsing/fixtures/fail6.json +1 -0
  89. data/spec/parsing/fixtures/fail9.json +1 -0
  90. data/spec/parsing/fixtures/pass.array.json +6 -0
  91. data/spec/parsing/fixtures/pass.codepoints_from_unicode_org.json +1 -0
  92. data/spec/parsing/fixtures/pass.contacts.json +1 -0
  93. data/spec/parsing/fixtures/pass.db100.xml.json +1 -0
  94. data/spec/parsing/fixtures/pass.db1000.xml.json +1 -0
  95. data/spec/parsing/fixtures/pass.dc_simple_with_comments.json +11 -0
  96. data/spec/parsing/fixtures/pass.deep_arrays.json +1 -0
  97. data/spec/parsing/fixtures/pass.difficult_json_c_test_case.json +1 -0
  98. data/spec/parsing/fixtures/pass.difficult_json_c_test_case_with_comments.json +1 -0
  99. data/spec/parsing/fixtures/pass.doubles.json +1 -0
  100. data/spec/parsing/fixtures/pass.empty_array.json +1 -0
  101. data/spec/parsing/fixtures/pass.empty_string.json +1 -0
  102. data/spec/parsing/fixtures/pass.escaped_bulgarian.json +4 -0
  103. data/spec/parsing/fixtures/pass.escaped_foobar.json +1 -0
  104. data/spec/parsing/fixtures/pass.item.json +1 -0
  105. data/spec/parsing/fixtures/pass.json-org-sample1.json +23 -0
  106. data/spec/parsing/fixtures/pass.json-org-sample2.json +11 -0
  107. data/spec/parsing/fixtures/pass.json-org-sample3.json +26 -0
  108. data/spec/parsing/fixtures/pass.json-org-sample4-nows.json +88 -0
  109. data/spec/parsing/fixtures/pass.json-org-sample4.json +89 -0
  110. data/spec/parsing/fixtures/pass.json-org-sample5.json +27 -0
  111. data/spec/parsing/fixtures/pass.map-spain.xml.json +1 -0
  112. data/spec/parsing/fixtures/pass.ns-invoice100.xml.json +1 -0
  113. data/spec/parsing/fixtures/pass.ns-soap.xml.json +1 -0
  114. data/spec/parsing/fixtures/pass.numbers-fp-4k.json +6 -0
  115. data/spec/parsing/fixtures/pass.numbers-fp-64k.json +61 -0
  116. data/spec/parsing/fixtures/pass.numbers-int-4k.json +11 -0
  117. data/spec/parsing/fixtures/pass.numbers-int-64k.json +154 -0
  118. data/spec/parsing/fixtures/pass.twitter-search.json +1 -0
  119. data/spec/parsing/fixtures/pass.twitter-search2.json +1 -0
  120. data/spec/parsing/fixtures/pass.unicode.json +3315 -0
  121. data/spec/parsing/fixtures/pass.yelp.json +1 -0
  122. data/spec/parsing/fixtures/pass1.json +56 -0
  123. data/spec/parsing/fixtures/pass2.json +1 -0
  124. data/spec/parsing/fixtures/pass3.json +6 -0
  125. data/spec/parsing/fixtures_spec.rb +45 -0
  126. data/spec/parsing/one_off_spec.rb +58 -0
  127. data/spec/spec_helper.rb +11 -0
  128. data/yajl-ruby.gemspec +176 -0
  129. metadata +196 -0
data/ext/yajl_parser.h ADDED
@@ -0,0 +1,79 @@
1
+ /*
2
+ * Copyright 2007-2009, 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
+ unsigned int errorOffset;
62
+ /* temporary storage for decoded strings */
63
+ yajl_buf decodeBuf;
64
+ /* a stack of states. access with yajl_state_XXX routines */
65
+ yajl_bytestack stateStack;
66
+ /* memory allocation routines */
67
+ yajl_alloc_funcs alloc;
68
+ };
69
+
70
+ yajl_status
71
+ yajl_do_parse(yajl_handle handle, unsigned int * offset,
72
+ const unsigned char * jsonText, unsigned int jsonTextLen);
73
+
74
+ unsigned char *
75
+ yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
76
+ unsigned int jsonTextLen, int verbose);
77
+
78
+
79
+ #endif
data/lib/yajl.rb ADDED
@@ -0,0 +1,80 @@
1
+ # encoding: UTF-8
2
+ require 'yajl_ext'
3
+
4
+ # = Extras
5
+ # We're not going to load these auotmatically, because you might not need them ;)
6
+ #
7
+ # require 'yajl/http_stream.rb' unless defined?(Yajl::HttpStream)
8
+ # require 'yajl/gzip.rb' unless defined?(Yajl::Gzip)
9
+ # require 'yajl/deflate.rb' unless defined?(Yajl::Deflate)
10
+ # require 'yajl/bzip2.rb' unless defined?(Yajl::Bzip2)
11
+
12
+ # = Yajl
13
+ #
14
+ # Ruby bindings to the excellent Yajl (Yet Another JSON Parser) ANSI C library.
15
+ module Yajl
16
+ VERSION = "0.5.5"
17
+
18
+ class Parser
19
+ # A helper method for parse-and-forget use-cases
20
+ #
21
+ # +io+ is the stream to parse JSON from
22
+ #
23
+ # The +options+ hash allows you to set two parsing options - :allow_comments and :check_utf8
24
+ #
25
+ # :allow_comments accepts a boolean will enable/disable checks for in-line comments in the JSON stream
26
+ #
27
+ # :check_utf8 accepts a boolean will enable/disable UTF8 validation for the JSON stream
28
+ def self.parse(io, options={}, read_bufsize=nil, &block)
29
+ new(options).parse(io, read_bufsize, &block)
30
+ end
31
+ end
32
+
33
+ class Encoder
34
+ # A helper method for encode-and-forget use-cases
35
+ #
36
+ # Examples:
37
+ # Yajl::Encoder.encode(obj[, io, :pretty => true, :indent => "\t"])
38
+ #
39
+ # output = Yajl::Encoder.encode(obj[, :pretty => true, :indent => "\t"])
40
+ #
41
+ # +obj+ is a ruby object to encode to JSON format
42
+ #
43
+ # +io+ is the optional IO stream to encode the ruby object to.
44
+ # If +io+ isn't passed, the resulting JSON string is returned. If +io+ is passed, nil is returned.
45
+ #
46
+ # The +options+ hash allows you to set two encoding options - :pretty and :indent
47
+ #
48
+ # :pretty accepts a boolean and will enable/disable "pretty printing" the resulting output
49
+ #
50
+ # :indent accepts a string and will be used as the indent character(s) during the pretty print process
51
+ def self.encode(obj, *args, &block)
52
+ # TODO: this code smells, any ideas?
53
+ options = {}
54
+ io = nil
55
+ args.each do |arg|
56
+ if arg.is_a?(Hash)
57
+ options = arg
58
+ elsif arg.respond_to?(:read)
59
+ io = arg
60
+ end
61
+ end if args.any?
62
+ new(options).encode(obj, io, &block)
63
+ end
64
+ end
65
+
66
+ # DEPRECATED - See Yajl::Parser and Yajl::Encoder
67
+ module Stream
68
+ # DEPRECATED - See Yajl::Parser
69
+ def self.parse(io)
70
+ STDERR.puts "WARNING: Yajl::Stream has be deprecated and will most likely be gone in the next release. Use the Yajl::Parser class instead."
71
+ Parser.new.parse(io)
72
+ end
73
+
74
+ # DEPRECATED - See Yajl::Encoder
75
+ def self.encode(obj, io)
76
+ STDERR.puts "WARNING: Yajl::Stream has be deprecated and will most likely be gone in the next release. Use the Yajl::Encoder class instead."
77
+ Encoder.new.encode(obj, io)
78
+ end
79
+ end
80
+ end
data/lib/yajl/bzip2.rb ADDED
@@ -0,0 +1,11 @@
1
+ # encoding: UTF-8
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,29 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Bzip2
4
+ # This is a wrapper around Bzip::Reader to allow it's #read method to adhere
5
+ # to the IO spec, allowing for two parameters (length, and buffer)
6
+ class StreamReader < ::Bzip2::Reader
7
+
8
+ # A helper method to allow use similar to IO#read
9
+ def read(len=nil, buffer=nil)
10
+ unless buffer.nil?
11
+ buffer.replace super(len)
12
+ return buffer
13
+ end
14
+ super(len)
15
+ end
16
+
17
+ # Helper method for one-off parsing from a bzip2-compressed stream
18
+ #
19
+ # See Yajl::Parser#parse for parameter documentation
20
+ def self.parse(input, options={}, buffer_size=nil, &block)
21
+ if input.is_a?(String)
22
+ input = StringIO.new(input)
23
+ end
24
+
25
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Bzip2
4
+ # A wrapper around the Bzip2::Writer class for easier JSON stream encoding
5
+ class StreamWriter < ::Bzip2::Writer
6
+
7
+ # A helper method for encoding to a bzip2-compressed stream
8
+ #
9
+ # Look up Yajl::Encoder#encode for parameter documentation
10
+ def self.encode(obj, io)
11
+ Yajl::Encoder.new.encode(obj, new(io))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
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,37 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Deflate
4
+ # This is a wrapper around Zlib::Inflate, creating a #read method that adheres
5
+ # to the IO spec, allowing for two parameters (length, and buffer)
6
+ class StreamReader < ::Zlib::Inflate
7
+
8
+ # Wrapper to the initialize method so we can set the initial IO to parse from.
9
+ def initialize(io, options)
10
+ @io = io
11
+ super(options)
12
+ end
13
+
14
+ # A helper method to allow use similar to IO#read
15
+ def read(len=nil, buffer=nil)
16
+ buffer.replace inflate(@io.read(len)) and return unless buffer.nil?
17
+ inflate(@io.read(len))
18
+ end
19
+ alias :eof? :finished?
20
+
21
+ # Helper method for one-off parsing from a deflate-compressed stream
22
+ #
23
+ # See Yajl::Parser#parse for parameter documentation
24
+ def self.parse(input, options={}, buffer_size=nil, &block)
25
+ if input.is_a?(String)
26
+ input = StringIO.new(input)
27
+ end
28
+
29
+ if options.is_a?(Hash)
30
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
31
+ elsif options.is_a?(Fixnum)
32
+ Yajl::Parser.new.parse(new(input, options), buffer_size, &block)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Deflate
4
+ # A wrapper around the Zlib::Deflate class for easier JSON stream parsing
5
+ class StreamWriter < ::Zlib::Deflate
6
+
7
+ # A helper method to allow use similar to IO#write
8
+ def write(str)
9
+ deflate(str)
10
+ str.size unless str.nil?
11
+ end
12
+
13
+ # A helper method for one-off encoding to a deflate-compressed stream
14
+ #
15
+ # Look up Yajl::Encoder#encode for parameter documentation
16
+ def self.encode(obj, io)
17
+ Yajl::Encoder.new.encode(obj, new(io))
18
+ end
19
+ end
20
+ end
21
+ end
data/lib/yajl/gzip.rb ADDED
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
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,28 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Gzip
4
+ # This is a wrapper around Zlib::GzipReader to allow it's #read method to adhere
5
+ # to the IO spec, allowing for two parameters (length, and buffer)
6
+ class StreamReader < ::Zlib::GzipReader
7
+
8
+ # Wrapper method to allow use similar to IO#read
9
+ def read(len=nil, buffer=nil)
10
+ unless buffer.nil?
11
+ buffer.replace super(len)
12
+ return buffer
13
+ end
14
+ super(len)
15
+ end
16
+
17
+ # Helper method for one-off parsing from a gzip-compressed stream
18
+ #
19
+ # See Yajl::Parser#parse for parameter documentation
20
+ def self.parse(input, options={}, buffer_size=nil, &block)
21
+ if input.is_a?(String)
22
+ input = StringIO.new(input)
23
+ end
24
+ Yajl::Parser.new(options).parse(new(input), buffer_size, &block)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ module Yajl
3
+ module Gzip
4
+ # Wraper around the Zlib::GzipWriter class
5
+ class StreamWriter < ::Zlib::GzipWriter
6
+ # A helper method for one-off encoding to a gzip-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,101 @@
1
+ # encoding: UTF-8
2
+ require 'socket' unless defined?(Socket)
3
+ require 'yajl' unless defined?(Yajl::Parser)
4
+
5
+ module Yajl
6
+ # This module is for making HTTP requests to which the response bodies (and possibly requests in the near future)
7
+ # are streamed directly into Yajl.
8
+ class HttpStream
9
+
10
+ # This Exception is thrown when an HTTP response isn't in ALLOWED_MIME_TYPES
11
+ # and therefore cannot be parsed.
12
+ class InvalidContentType < Exception; end
13
+
14
+ # The mime-type we expect the response to be. If it's anything else, we can't parse it
15
+ # and an InvalidContentType is raised.
16
+ ALLOWED_MIME_TYPES = ["application/json", "text/plain"]
17
+
18
+ # Makes a basic HTTP GET request to the URI provided
19
+ # 1. a raw socket is opened to the server/host provided
20
+ # 2. the request is made using HTTP/1.0, Accept-encoding: gzip (deflate support coming soon, too)
21
+ # 3. the response is read until the end of the headers
22
+ # 4. the _socket itself_ is passed directly to Yajl, for direct parsing off the stream; As it's being received over the wire!
23
+ def self.get(uri, opts = {}, &block)
24
+ user_agent = opts.has_key?(['User-Agent']) ? opts['User-Agent'] : "Yajl::HttpStream #{Yajl::VERSION}"
25
+
26
+ socket = TCPSocket.new(uri.host, uri.port)
27
+ request = "GET #{uri.path}#{uri.query ? "?"+uri.query : nil} HTTP/1.1\r\n"
28
+ request << "Host: #{uri.host}\r\n"
29
+ request << "Authorization: Basic #{[uri.userinfo].pack('m')}\r\n" unless uri.userinfo.nil?
30
+ request << "User-Agent: #{user_agent}\r\n"
31
+ request << "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
32
+ request << "Connection: close\r\n"
33
+ encodings = []
34
+ encodings << "bzip2" if defined?(Yajl::Bzip2)
35
+ encodings << "gzip" if defined?(Yajl::Gzip)
36
+ encodings << "deflate" if defined?(Yajl::Deflate)
37
+ request << "Accept-Encoding: #{encodings.join(',')}\r\n" if encodings.any?
38
+ request << "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
39
+ request << "\r\n\r\n"
40
+ socket.write(request)
41
+ response_head = {}
42
+ response_head[:headers] = {}
43
+
44
+ socket.each_line do |line|
45
+ if line == "\r\n" # end of the headers
46
+ break
47
+ else
48
+ header = line.split(": ")
49
+ if header.size == 1
50
+ header = header[0].split(" ")
51
+ response_head[:version] = header[0]
52
+ response_head[:code] = header[1].to_i
53
+ response_head[:msg] = header[2]
54
+ # this is the response code line
55
+ else
56
+ response_head[:headers][header[0]] = header[1].strip
57
+ end
58
+ end
59
+ end
60
+ parser = Yajl::Parser.new
61
+ if response_head[:headers]["Transfer-Encoding"] == 'chunked'
62
+ if block_given?
63
+ parser.on_parse_complete = block
64
+ chunkLeft = 0
65
+ while !socket.eof? && (size = socket.gets.hex)
66
+ next if size == 0
67
+ json = socket.read(size)
68
+ chunkLeft = size-json.size
69
+ if chunkLeft == 0
70
+ parser << json
71
+ else
72
+ # received only part of the chunk, grab the rest
73
+ parser << socket.read(chunkLeft)
74
+ end
75
+ end
76
+ else
77
+ raise Exception, "Chunked responses detected, but no block given to handle the chunks."
78
+ end
79
+ else
80
+ content_type = response_head[:headers]["Content-Type"].split('; ')
81
+ content_type = content_type.first
82
+ if ALLOWED_MIME_TYPES.include?(content_type)
83
+ case response_head[:headers]["Content-Encoding"]
84
+ when "gzip"
85
+ return Yajl::Gzip::StreamReader.parse(socket)
86
+ when "deflate"
87
+ return Yajl::Deflate::StreamReader.parse(socket, -Zlib::MAX_WBITS)
88
+ when "bzip2"
89
+ return Yajl::Bzip2::StreamReader.parse(socket)
90
+ else
91
+ return Yajl::Parser.new.parse(socket)
92
+ end
93
+ else
94
+ raise InvalidContentType, "The response MIME type #{content_type}"
95
+ end
96
+ end
97
+ ensure
98
+ socket.close
99
+ end
100
+ end
101
+ end