ev/net.rb in rwdtinker-1.67 vs ev/net.rb in rwdtinker-1.68

- old
+ new

@@ -1,7 +1,8 @@ require "ev/ruby" require "ev/ftools" +require "ev/mime" require "net/http" require "socket" require "uri" require "cgi" require "md5" @@ -11,15 +12,15 @@ file = "#{home}/.evnet" if File.file?(file) Hash.file(file).each do |k, v| eval "$#{k} = '#{v}'" unless k=~ /^\#/ - #$proxy_auth = [$proxy_auth].pack("m").chomp if k == "proxy_auth" end end def uri2txt(s) + # ??? Werkt niet goed i = s.index(/%[[:digit:]]{2}/) while not i.nil? s = s[0..(i-1)] + s[(i+1)..(i+2)].unpack('H2').shift.to_i.chr + s[(i+3)..-1] i = s.index(/%[[:digit:]]{2}/) end @@ -145,10 +146,12 @@ begin @protocol, @userpass, @host, @port, d1, @path, d2, @vars, @anchor = URI.split(url.to_s) rescue end + @path = "/" if (not @path.nil? and @path.empty? and @protocol == "http") + @protocol = "" if @protocol.nil? @userpass = "" if @userpass.nil? @host = "" if @host.nil? @port = 0 if @port.nil? @path = "" if @path.nil? @@ -161,22 +164,22 @@ @vars = res @port = @port.to_i end - def + (url2) + def +(url2) url1 = self.to_s url2 = url2.to_s if url2.kind_of?(self.class) return EVURI.new((URI::Generic.new(*URI.split(url1)) + URI::Generic.new(*URI.split(url2))).to_s) rescue nil end def to_s protocol = @protocol userpass = @userpass host = @host - port = @port.to_s + port = @port path = @path vars = varstring anchor = @anchor protocol = nil if @protocol.empty? @@ -194,10 +197,37 @@ res.gsub!(/\#$/, "") return res end + def localname + protocol = @protocol + userpass = @userpass + host = @host + port = @port + path = @path + vars = varstring + anchor = @anchor + + protocol = nil if @protocol.empty? + userpass = nil if @userpass.empty? + host = nil if @host.empty? + port = nil if @port.zero? + path = nil if @path.empty? + vars = nil if @vars.empty? + anchor = nil if @anchor.empty? + + path = "#{path}." if path =~ /[\/\\]$/ + + f = MD5.new(protocol.to_s + userpass.to_s + host.to_s + port.to_s + File.dirname(path.to_s) + vars.to_s).to_s + e = File.basename(path.to_s).gsub(/[^\w\.\-]/, "_").gsub(/_+/, "_") + res = f + "." + e + res.gsub!(/[^\w]+$/, "") + + return res + end + def varstring res = [] vars = @vars.dup @varsvolgorde.each do |k| @@ -279,115 +309,146 @@ def to_s @data end end + class NoAddressException < StandardError + end + + def self.getaddress(host) + if not @@hosts.include?(host) + @@hosts[host] = "" + evtimeout(5) do # ??? Doet 'ut niet?... + @@hosts[host] = IPSocket.getaddress(host) + end + end + + raise NoAddressException, host if @@hosts[host].empty? + + @@hosts[host] + end + def self.head(uri, form={}, recursive=true) header = Header.new(nil) begin while not uri.nil? - if $proxy.nil? or $proxy.empty? - uri = EVURI.new(uri) if uri.kind_of? String - host = uri.host - port = uri.port + uri = EVURI.new(uri) if uri.kind_of? String + host = uri.host + port = uri.port + + if $proxy.nil? or $proxy.empty? or host == "localhost" io = nil @@mutex.synchronize do - @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host) - io = TCPSocket.new(@@hosts[host], port.zero? ? 80 : port) + io = TCPSocket.new(getaddress(host), port.zero? ? 80 : port) end io.write("HEAD #{uri.path or '/'}#{uri.varstring.empty? ? '' : '?' + uri.varstring} HTTP/1.0\r\nHost: #{host}\r\n\r\n") else proxy = EVURI.new($proxy) - host = proxy.host - port = proxy.port + io = TCPSocket.new(proxy.host, proxy.port.zero? ? 8080 : proxy.port) - io = TCPSocket.new(host, port.zero? ? 8080 : port) - io.write("HEAD #{uri} HTTP/1.0\r\n#{"Proxy-Authorization: Basic "+$proxy_auth+"\r\n" if not $proxy_auth.nil?}\r\n\r\n") end io.close_write - res = io.read + res = io.read + + io.close_read + header, data = nil, nil header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil? header = Header.new(header) if recursive and header.header["location"] != uri.to_s uri = EVURI.new(uri) + header.header["location"] else uri = nil end end - rescue + rescue Errno::ECONNRESET, Errno::EHOSTUNREACH => e + $stderr.puts e.message + sleep 1 + retry + rescue Errno::ECONNREFUSED => e + data = nil + rescue NoAddressException => e + $stderr.puts e.message header = Header.new(nil) end + GC.start + return header end - def self.get(uri, form={}) + def self.get(uri, httpheader={}, form={}) post = Array.new form.each_pair do |var, value| post << "#{var.to_html}=#{value.to_html}" end post = post.join("?") data = nil begin while not uri.nil? - if $proxy.nil? or $proxy.empty? - uri = EVURI.new(uri) if uri.kind_of? String - host = uri.host - port = uri.port + uri = EVURI.new(uri) if uri.kind_of? String + host = uri.host + port = uri.port + if $proxy.nil? or $proxy.empty? or host == "localhost" io = nil @@mutex.synchronize do - @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host) - io = TCPSocket.new(@@hosts[host], port.zero? ? 80 : port) + io = TCPSocket.new(getaddress(host), port.zero? ? 80 : port) end if post.empty? io.write "GET %s%s HTTP/1.0\r\n" % [(uri.path or '/'), (uri.varstring.empty? ? '' : '?' + uri.varstring)] else io.write "POST %s%s HTTP/1.0\r\n" % [(uri.path or '/'), (uri.varstring.empty? ? '' : '?' + uri.varstring)] end else proxy = EVURI.new($proxy) - host = proxy.host - port = proxy.port + io = TCPSocket.new(proxy.host, proxy.port.zero? ? 8080 : proxy.port) - io = TCPSocket.new(host, port.zero? ? 8080 : port) - if post.empty? io.write "GET %s HTTP/1.0\r\n" % uri else io.write "POST %s HTTP/1.0\r\n" % uri end end io.write "Host: %s\r\n" % host - io.write "User-Agent: evwget\r\n" - io.write "Proxy-Authorization: Basic %s\r\n" % $proxy_auth unless $proxy_auth.nil? + io.write "User-Agent: xyz\r\n" + io.write "Proxy-Authorization: Basic %s\r\n" % $proxy_auth unless $proxy_auth.nil? #io.write "Accept-Encoding: deflate\r\n" - #io.write "Connection: close\r\n" + #io.write "Accept-Charset: ISO-8859-1\r\n" + io.write "Connection: close\r\n" io.write "Content-Type: application/x-www-form-urlencoded\r\n" unless post.empty? io.write "Content-Length: %s\r\n" % post.length unless post.empty? + httpheader.each do |k, v| + $stderr.puts "%s: %s\r\n" % [k, v] + io.write "%s: %s\r\n" % [k, v] + end io.write "\r\n" io.write post unless post.empty? io.close_write res = io.read + + io.close_read + header, data = nil, nil header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil? header = Header.new(header) + length = header.header["content-length"] + data = "" if length == "0" if header.header["location"] != uri.to_s uri = EVURI.new(uri) + header.header["location"] else uri = nil @@ -395,16 +456,29 @@ if header.header["transfer-encoding"] == "chunked" data = Chunk.new(data).to_s if not data.nil? end + #if header.header["content-encoding"] == "gzip" + #data = "gzip -d".exec(data) if not data.nil? + #end + data = nil unless header.code == 200 end - rescue + rescue Errno::ECONNRESET, Errno::EHOSTUNREACH => e + $stderr.puts e.message + sleep 1 + retry + rescue Errno::ECONNREFUSED => e data = nil + rescue NoAddressException, Errno::ECONNREFUSED => e + $stderr.puts e.message + data = nil end + GC.start + return data end def self.head_from_cache(uri, form={}) from_cache("head", uri, form) @@ -420,11 +494,11 @@ dir = "#{temp}/evcache.#{user}/httpclient.#{action}" file = "#{dir}/#{hash}" data = nil - Dir.mkdirrec(dir) + File.mkpath(dir) expire = 356*24*60*60 if File.file?(file) and (Time.new.to_f - File.stat(file).mtime.to_f < expire) @@mutex.synchronize do @@ -445,19 +519,19 @@ end class RequestGet < Hash def initialize(data) CGI.parse(data).each do |k, v| - self[k] = v.join(" ") + self[k] = v end end end class RequestPost < Hash def initialize(data) CGI.parse(data).each do |k, v| - self[k] = v.join(" ") + self[k] = v end end end class RequestRequest @@ -491,10 +565,12 @@ class Request < Hash attr_reader :peeraddr attr_reader :request attr_reader :cookies attr_reader :vars + attr_reader :user + attr_writer :user def initialize(io) @io = io firstline = @io.gets @@ -560,10 +636,11 @@ end end class Response < Hash attr_writer :response + attr_writer :file attr_reader :cookies attr_reader :stop attr_reader :at_stop def initialize(io) @@ -572,14 +649,24 @@ @cookies = {} @data = "" @syncd = false @stop = false @at_stop = lambda{} + @file = nil end def flush sync + + if @file + File.open(@file, "rb") do |f| + while data = f.read(10_000) + @io.write data + end + end + end + @io.close end def to_s res = "#{@response}\r\n" @@ -593,20 +680,39 @@ res end def sync + size = (@data or "").length + + if @file + ext = @file.scan(/\.[^\.]*$/) + ext = ext.shift + ext = ext[1..-1] unless ext.nil? + mimetype = EVMime::MimeType[ext] + + self["Content-Type"] = mimetype unless mimetype.nil? + + size += File.size(@file) if File.file?(@file) + end + + self["Content-Length"] = size + @io.write("#{to_s}\r\n") unless @syncd @io.write(@data) @data = "" @syncd = true end def << (s) @data << s end + def clean + @data = "" + end + def inspect "(Response: %s)" % [@response, @data].join(", ") end def stop(&block) @@ -638,11 +744,11 @@ if not server.nil? count = 0 at_exit do - $stderr.puts "Received #{count} requests" + #$stderr.puts "Received #{count} requests" end serverthread = Thread.new do mutex = Mutex.new @@ -676,19 +782,29 @@ rescue NameError raise HTTPServerException end if (not remote) or (remote and (auth.nil? or auth.empty? or authenticate(auth, realm, req, resp))) - $stderr.puts "#{count2}, #{Time.new.strftime("%Y-%m-%d.%H:%M:%S")}, #{ip}, #{req.request.to_s.strip}" + $stderr.puts "#{count2} #{Time.new.strftime("%Y-%m-%d %H:%M:%S")} #{ip} #{req.user} #{req.request.to_s.strip}" begin yield(req, resp) rescue Exception => e mutex.synchronize do $stderr.puts e.class.to_s + ": " + e.message $stderr.puts e.backtrace.collect{|s| "\t"+s}.join("\n") end + resp["Content-Type"] = "text/plain" + resp.response = "HTTP/1.0 200 ???" + resp.clean + resp << e.class.to_s + ": " + e.message + resp << "\n" + resp << "\n" + resp << e.backtrace.collect{|s| "\t"+s}.join("\n") + resp << "\n" + resp << "\n" + resp << "(You can use the back button and stop the application properly, if appropriate.)" end stop = true if resp.stop? end @@ -736,15 +852,15 @@ end end ok = (auths.include?(u) and auths[u] == p) - if ok - - else + unless ok resp["WWW-Authenticate"] = "Basic realm=\"#{realm}\"" resp.response = "HTTP/1.0 401 Unauthorized" end + + req.user = u return ok end end