Skip to content

Commit

Permalink
DRYed up the Yajl::HttpStream methods and added PUT and DELETE support
Browse files Browse the repository at this point in the history
  • Loading branch information
brianmario committed Aug 20, 2009
1 parent 789c393 commit 18709db
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 235 deletions.
241 changes: 91 additions & 150 deletions lib/yajl/http_stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,172 +16,113 @@ class InvalidContentType < Exception; end
ALLOWED_MIME_TYPES = ["application/json", "text/plain"]

# Makes a basic HTTP GET request to the URI provided
# 1. a raw socket is opened to the server/host provided
# 2. the request is made using HTTP/1.0, Accept-encoding: gzip (deflate support coming soon, too)
# 3. the response is read until the end of the headers
# 4. the _socket itself_ is passed directly to Yajl, for direct parsing off the stream; As it's being received over the wire!
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
request("GET", uri, opts, &block)
end

# Makes a basic HTTP POST request to the URI provided
# 1. a raw socket is opened to the server/host provided
# 2. the request is made using HTTP/1.0, Accept-encoding: gzip (deflate support coming soon, too)
# 3. the response is read until the end of the headers
# 4. the _socket itself_ is passed directly to Yajl, for direct parsing off the stream; As it's being received over the wire!
def self.post(uri, body, 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')}"
request << "User-Agent: #{user_agent}\r\n"
request << "Content-Length: #{body.length}\r\n"
request << "Content-Type: application/x-www-form-urlencoded\r\n"
request << "Accept: */*\r\n"
request("POST", uri, opts.merge({:body => body}), &block)
end

# Makes a basic HTTP PUT request to the URI provided
def self.put(uri, body, opts = {}, &block)
request("PUT", uri, opts.merge({:body => body}), &block)
end

# Makes a basic HTTP DELETE request to the URI provided
def self.delete(uri, opts = {}, &block)
request("DELETE", uri, opts, &block)
end

protected
def self.request(method, uri, opts = {}, &block)
user_agent = opts.has_key?('User-Agent') ? opts.delete(['User-Agent']) : "Yajl::HttpStream #{Yajl::VERSION}"
if method == "POST" || method == "PUT"
content_type = opts.has_key?('Content-Type') ? opts.delete(['Content-Type']) : "application/x-www-form-urlencoded"
body = opts.delete(:body)
end

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\r\n"
request << body
socket = TCPSocket.new(uri.host, uri.port)
request = "#{method} #{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: */*\r\n"
if method == "POST" || method == "PUT"
request << "Content-Length: #{body.length}\r\n"
request << "Content-Type: #{content_type}\r\n"
end
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: utf-8\r\n"
request << "\r\n\r\n"
if method == "POST" || method == "PUT"
request << body
end
socket.write(request)
response_head = {}
response_head[:headers] = {}

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
socket.each_line do |line|
if line == "\r\n" # end of the headers
break
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
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
# received only part of the chunk, grab the rest
parser << socket.read(chunkLeft)
response_head[:headers][header[0]] = header[1].strip
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)
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
return Yajl::Parser.new(opts).parse(socket)
raise Exception, "Chunked responses detected, but no block given to handle the chunks."
end
else
raise InvalidContentType, "The response MIME type #{content_type}"
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
ensure
socket.close
end
end
end
85 changes: 0 additions & 85 deletions spec/http/http_spec.rb

This file was deleted.

0 comments on commit 18709db

Please sign in to comment.