lib/knj/http2.rb in knjrbfw-0.0.33 vs lib/knj/http2.rb in knjrbfw-0.0.34

- old
+ new

@@ -1,6 +1,7 @@ require "#{$knjpath}web" +require "socket" #This class tries to emulate a browser in Ruby without any visual stuff. Remember cookies, keep sessions alive, reset connections according to keep-alive rules and more. #===Examples # Knj::Http2.new(:host => "www.somedomain.com", :port => 80, :ssl => false, :debug => false) do |http| # res = http.get("index.rhtml?show=some_page") @@ -19,12 +20,14 @@ raise "Arguments wasnt a hash." if !args.is_a?(Hash) @args = args @cookies = {} @debug = @args[:debug] - @mutex = Mutex.new + require "monitor" + @mutex = Monitor.new + if !@args[:port] if @args[:ssl] @args[:port] = 443 else @args[:port] = 80 @@ -153,29 +156,24 @@ #Returns a result-object based on the arguments. #===Examples # res = http.get("somepage.html") # print res.body #=> <String>-object containing the HTML gotten. def get(addr, args = {}) - begin - @mutex.synchronize do - args[:addr] = addr - header_str = "GET /#{addr} HTTP/1.1#{@nl}" - header_str << self.header_str(self.default_headers(args), args) - header_str << "#{@nl}" - - print "Http2: Writing headers.\n" if @debug - self.write(header_str) - - print "Http2: Reading response.\n" if @debug - resp = self.read_response(args) - - print "Http2: Done with get request.\n" if @debug - return resp - end - rescue Knj::Errors::Retry => e - print "Redirecting to: #{e.message}\n" if @debug - return self.get(e.message, args) + @mutex.synchronize do + args[:addr] = addr + header_str = "GET /#{addr} HTTP/1.1#{@nl}" + header_str << self.header_str(self.default_headers(args), args) + header_str << "#{@nl}" + + print "Http2: Writing headers.\n" if @debug + self.write(header_str) + + print "Http2: Reading response.\n" if @debug + resp = self.read_response(args) + + print "Http2: Done with get request.\n" if @debug + return resp end end #Tries to write a string to the socket. If it fails it reconnects and tries again. def write(str) @@ -240,76 +238,75 @@ #Posts to a certain page. #===Examples # res = http.post("login.php", {"username" => "John Doe", "password" => 123) def post(addr, pdata = {}, args = {}) - begin - @mutex.synchronize do - praw = Knj::Http2.post_convert_data(pdata) - - header_str = "POST /#{addr} HTTP/1.1#{@nl}" - header_str << self.header_str(self.default_headers(args).merge("Content-Length" => praw.length), args) - header_str << "#{@nl}" - header_str << praw - - self.write(header_str) - return self.read_response(args) - end - rescue Knj::Errors::Retry => e - return self.get(e.message, args) + @mutex.synchronize do + print "Doing post.\n" if @debug + + praw = Knj::Http2.post_convert_data(pdata) + + header_str = "POST /#{addr} HTTP/1.1#{@nl}" + header_str << self.header_str(self.default_headers(args).merge("Content-Length" => praw.length), args) + header_str << "#{@nl}" + header_str << praw + + print "Header str: #{header_str}\n" if @debug + + self.write(header_str) + return self.read_response(args) end end #Posts to a certain page using the multipart-method. #===Examples # res = http.post_multipart("upload.php", {"normal_value" => 123, "file" => Tempfile.new(?)}) def post_multipart(addr, pdata, args = {}) - begin - @mutex.synchronize do - boundary = Digest::MD5.hexdigest(Time.now.to_f.to_s) + require "digest" + + @mutex.synchronize do + boundary = Digest::MD5.hexdigest(Time.now.to_f.to_s) + + praw = "" + pdata.each do |key, val| + praw << "--#{boundary}#{@nl}" - praw = "" - pdata.each do |key, val| - praw << "--#{boundary}#{@nl}" - - if val.class.name == "Tempfile" and val.respond_to?("original_filename") - praw << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{val.original_filename}\";#{@nl}" - praw << "Content-Length: #{val.bytesize}#{@nl}" - elsif val.is_a?(Hash) and val[:filename] - praw << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{val[:filename]}\";#{@nl}" - praw << "Content-Length: #{val[:content].bytesize}#{@nl}" - else - praw << "Content-Disposition: form-data; name=\"#{key}\";#{@nl}" - praw << "Content-Length: #{val.bytesize}#{@nl}" - end - - praw << "Content-Type: text/plain#{@nl}" - praw << @nl - - if val.is_a?(StringIO) - praw << val.read - elsif val.is_a?(Hash) and val[:content] - praw << val[:content].to_s - else - praw << val.to_s - end - - praw << @nl + if val.class.name == "Tempfile" and val.respond_to?("original_filename") + praw << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{val.original_filename}\";#{@nl}" + praw << "Content-Length: #{val.bytesize}#{@nl}" + elsif val.is_a?(Hash) and val[:filename] + praw << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{val[:filename]}\";#{@nl}" + praw << "Content-Length: #{val[:content].bytesize}#{@nl}" + else + praw << "Content-Disposition: form-data; name=\"#{key}\";#{@nl}" + praw << "Content-Length: #{val.bytesize}#{@nl}" end - header_str = "POST /#{addr} HTTP/1.1#{@nl}" - header_str << "Content-Type: multipart/form-data; boundary=#{boundary}#{@nl}" - header_str << self.header_str(self.default_headers(args).merge("Content-Length" => praw.bytesize), args) - header_str << "#{@nl}" - header_str << praw - header_str << "--#{boundary}--" + praw << "Content-Type: text/plain#{@nl}" + praw << @nl - self.write(header_str) - return self.read_response(args) + if val.is_a?(StringIO) + praw << val.read + elsif val.is_a?(Hash) and val[:content] + praw << val[:content].to_s + else + praw << val.to_s + end + + praw << @nl end - rescue Knj::Errors::Retry => e - return self.get(e.message, args) + + header_str = "POST /#{addr} HTTP/1.1#{@nl}" + header_str << self.header_str(self.default_headers(args).merge("Content-Type" => "multipart/form-data; boundary=#{boundary}", "Content-Length" => praw.bytesize), args) + header_str << "#{@nl}" + header_str << praw + header_str << "--#{boundary}--" + + print "Headerstr: #{header_str}\n" if @debug + + self.write(header_str) + return self.read_response(args) end end #Returns a header-string which normally would be used for a request in the given state. def header_str(headers_hash, args = {}) @@ -351,19 +348,22 @@ if @length and @length > 0 and @mode == "body" line = @sock.read(@length) else line = @sock.gets end + + print "<#{@mode}>: '#{line}'\n" if @debug rescue Errno::ECONNRESET print "Http2: The connection was reset while reading - breaking gently...\n" if @debug - line = "" @sock = nil + break end break if line.to_s == "" if @mode == "headers" and line == @nl + print "Changing mode to body!\n" if @debug break if @length == 0 @mode = "body" next end @@ -404,21 +404,26 @@ @mode = nil raise "No status-code was received from the server.\n\nHeaders:\n#{Knj::Php.print_r(resp.headers, true)}\n\nBody:\n#{resp.args[:body]}" if !resp.args[:code] if resp.args[:code].to_s == "302" and resp.header?("location") and (!@args.key?(:follow_redirects) or @args[:follow_redirects]) + require "uri" uri = URI.parse(resp.header("location")) + url = uri.path + url << "?#{uri.query}" if uri.query.to_s.length > 0 args = {:host => uri.host} args[:ssl] = true if uri.scheme == "https" args[:port] = uri.port if uri.port + print "Redirecting from location-header to '#{url}'.\n" if @debug + if !args[:host] or args[:host] == @args[:host] - raise Knj::Errors::Retry, resp.header("location") + return self.get(url) else http = Knj::Http2.new(args) - return http.get(uri.path) + return http.get(url) end elsif resp.args[:code].to_s == "500" raise "500 - Internal server error: '#{args[:addr]}':\n\n#{resp.body}" elsif resp.args[:code].to_s == "403" raise Knj::Errors::NoAccess @@ -474,10 +479,10 @@ @resp.headers[key] << match[2] elsif match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/) @resp.args[:code] = match[2] @resp.args[:http_version] = match[1] else - raise "Could not understand header string: '#{line}'." + raise "Could not understand header string: '#{line}'.\n\n#{@sock.read(409600)}" end end #Parses the body based on given headers and saves it to the result-object. # http.parse_body(str) \ No newline at end of file