Class: Yajl::HttpStream
Overview
This module is for making HTTP requests to which the response bodies (and possibly requests in the near future) are streamed directly into Yajl.
Defined Under Namespace
Classes: InvalidContentType
Constant Summary collapse
- ALLOWED_MIME_TYPES =
The mime-type we expect the response to be. If it’s anything else, we can’t parse it and an InvalidContentType is raised.
["application/json", "text/plain"]
Class Method Summary collapse
-
.get(uri, opts = {}, &block) ⇒ Object
Makes a basic HTTP GET request to the URI provided 1.
-
.post(uri, opts = {}, &block) ⇒ Object
Makes a basic HTTP POST request to the URI provided 1.
Class Method Details
.get(uri, opts = {}, &block) ⇒ Object
Makes a basic HTTP GET request to the URI provided
-
a raw socket is opened to the server/host provided
-
the request is made using HTTP/1.0, Accept-encoding: gzip (deflate support coming soon, too)
-
the response is read until the end of the headers
-
the _socket itself_ is passed directly to Yajl, for direct parsing off the stream; As it’s being received over the wire!
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/yajl/http_stream.rb', line 23 def self.get(uri, opts = {}, &block) user_agent = opts.has_key?('User-Agent') ? opts.delete(['User-Agent']) : "Yajl::HttpStream #{Yajl::VERSION}" socket = TCPSocket.new(uri.host, uri.port) request = "GET #{uri.path}#{uri.query ? "?"+uri.query : nil} HTTP/1.1\r\n" request << "Host: #{uri.host}\r\n" request << "Authorization: Basic #{[uri.userinfo].pack('m')}\r\n" unless uri.userinfo.nil? request << "User-Agent: #{user_agent}\r\n" request << "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" request << "Connection: close\r\n" encodings = [] encodings << "bzip2" if defined?(Yajl::Bzip2) encodings << "gzip" if defined?(Yajl::Gzip) encodings << "deflate" if defined?(Yajl::Deflate) request << "Accept-Encoding: #{encodings.join(',')}\r\n" if encodings.any? request << "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" request << "\r\n\r\n" socket.write(request) response_head = {} response_head[:headers] = {} socket.each_line do |line| if line == "\r\n" # end of the headers break else header = line.split(": ") if header.size == 1 header = header[0].split(" ") response_head[:version] = header[0] response_head[:code] = header[1].to_i response_head[:msg] = header[2] # this is the response code line else response_head[:headers][header[0]] = header[1].strip end end end parser = Yajl::Parser.new(opts) if response_head[:headers]["Transfer-Encoding"] == 'chunked' if block_given? parser.on_parse_complete = block chunkLeft = 0 while !socket.eof? && (size = socket.gets.hex) next if size == 0 json = socket.read(size) chunkLeft = size-json.size if chunkLeft == 0 parser << json else # received only part of the chunk, grab the rest parser << socket.read(chunkLeft) end end else raise Exception, "Chunked responses detected, but no block given to handle the chunks." end else content_type = response_head[:headers]["Content-Type"].split(';') content_type = content_type.first if ALLOWED_MIME_TYPES.include?(content_type) case response_head[:headers]["Content-Encoding"] when "gzip" return Yajl::Gzip::StreamReader.parse(socket, opts) when "deflate" return Yajl::Deflate::StreamReader.parse(socket, opts.merge({:deflate_options => -Zlib::MAX_WBITS})) when "bzip2" return Yajl::Bzip2::StreamReader.parse(socket, opts) else return Yajl::Parser.new(opts).parse(socket) end else raise InvalidContentType, "The response MIME type #{content_type}" end end ensure socket.close end |
.post(uri, opts = {}, &block) ⇒ Object
Makes a basic HTTP POST request to the URI provided
-
a raw socket is opened to the server/host provided
-
the request is made using HTTP/1.0, Accept-encoding: gzip (deflate support coming soon, too)
-
the response is read until the end of the headers
-
the _socket itself_ is passed directly to Yajl, for direct parsing off the stream; As it’s being received over the wire!
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/yajl/http_stream.rb', line 106 def self.post(uri, opts = {}, &block) user_agent = opts.has_key?('User-Agent') ? opts.delete(['User-Agent']) : "Yajl::HttpStream #{Yajl::VERSION}" socket = TCPSocket.new(uri.host, uri.port) request = "POST #{uri.path}#{uri.query ? "?"+uri.query : nil} HTTP/1.1\r\n" request << "Host: #{uri.host}\r\n" request << "Authorization: Basic #{[uri.userinfo].pack('m')}\r\n" unless uri.userinfo.nil? request << "User-Agent: #{user_agent}\r\n" request << "Content-Length: #{opts['body'].length}" request << "Content-Type: application/x-www-form-urlencoded" request << "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" request << "Connection: close\r\n" encodings = [] encodings << "bzip2" if defined?(Yajl::Bzip2) encodings << "gzip" if defined?(Yajl::Gzip) encodings << "deflate" if defined?(Yajl::Deflate) request << "Accept-Encoding: #{encodings.join(',')}\r\n" if encodings.any? request << "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" request << opts['body'] request << "\r\n\r\n" socket.write(request) response_head = {} response_head[:headers] = {} socket.each_line do |line| if line == "\r\n" # end of the headers break else header = line.split(": ") if header.size == 1 header = header[0].split(" ") response_head[:version] = header[0] response_head[:code] = header[1].to_i response_head[:msg] = header[2] # this is the response code line else response_head[:headers][header[0]] = header[1].strip end end end parser = Yajl::Parser.new(opts) if response_head[:headers]["Transfer-Encoding"] == 'chunked' if block_given? parser.on_parse_complete = block chunkLeft = 0 while !socket.eof? && (size = socket.gets.hex) next if size == 0 json = socket.read(size) chunkLeft = size-json.size if chunkLeft == 0 parser << json else # received only part of the chunk, grab the rest parser << socket.read(chunkLeft) end end else raise Exception, "Chunked responses detected, but no block given to handle the chunks." end else content_type = response_head[:headers]["Content-Type"].split(';') content_type = content_type.first if ALLOWED_MIME_TYPES.include?(content_type) case response_head[:headers]["Content-Encoding"] when "gzip" return Yajl::Gzip::StreamReader.parse(socket, opts) when "deflate" return Yajl::Deflate::StreamReader.parse(socket, opts.merge({:deflate_options => -Zlib::MAX_WBITS})) when "bzip2" return Yajl::Bzip2::StreamReader.parse(socket, opts) else return Yajl::Parser.new(opts).parse(socket) end else raise InvalidContentType, "The response MIME type #{content_type}" end end ensure socket.close end |