ev/net.rb in rwdmovies-0.6 vs ev/net.rb in rwdmovies-0.7
- old
+ new
@@ -1,750 +1,750 @@
-require "ev/ruby"
-require "ev/ftools"
-require "net/http"
-require "socket"
-require "uri"
-require "cgi"
-require "md5"
-require "thread"
-
-$proxy = ENV["PROXY"] if $proxy.nil?
-
-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)
- 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
- s
-end
-
-class TCPServer
- def self.freeport(from, to, remote=false)
- if windows? or cygwin?
- TCPServer.freeport_windows(from, to, remote)
- else
- TCPServer.freeport_linux(from, to, remote)
- end
- end
-
- def self.freeport_linux(from, to, remote)
- ports = (from..to).to_a
- port = nil
- res = nil
-
- while res.nil? and not ports.empty?
- begin
- port = ports[0]
- ports.delete(port)
-
- io = TCPServer.new(remote ? "0.0.0.0" : "localhost", port)
-
- res = [port, io]
- rescue
- end
- end
-
- res = [nil, nil] if res.nil?
-
- port, io = res
-
- return port, io
- end
-
- def self.freeport_windows(from, to, remote)
- ports = (from..to).to_a
- port = nil
- res = nil
-
- while res.nil? and not ports.empty?
- begin
- port = ports.any
- ports.delete(port)
-
- io = TCPSocket.new("localhost", port)
- io.close
- rescue
- res = port
- end
- end
-
- port, io = res
-
- return port, io
- end
-
- def self.freeport_windows2(from, to, remote)
- res = nil
- port = from
-
- while res.nil? and port <= to
- begin
- io = TCPSocket.new("localhost", port)
- io.close
-
- port += 1
- rescue
- res = port
- end
- end
-
- return res
- end
-
- def self.usedports(from, to)
- threads = []
- res = []
-
- from.upto(to) do |port|
- threads << Thread.new do
- begin
- io = TCPSocket.new("localhost", port)
- io.close
-
- port
- rescue
- nil
- end
- end
- end
-
- threads.each do |thread|
- port = thread.value
- res << port unless port.nil?
- end
-
- return res
- end
-end
-
-class EVURI
- attr_reader :protocol
- attr_writer :protocol
- attr_reader :userpass
- attr_writer :userpass
- attr_reader :host
- attr_writer :host
- attr_reader :port
- attr_writer :port
- attr_reader :path
- attr_writer :path
- attr_reader :vars
- attr_writer :vars
- attr_reader :anchor
- attr_writer :anchor
-
- def initialize(url)
- begin
- @protocol, @userpass, @host, @port, d1, @path, d2, @vars, @anchor = URI.split(url.to_s)
- rescue
- end
-
- @protocol = "" if @protocol.nil?
- @userpass = "" if @userpass.nil?
- @host = "" if @host.nil?
- @port = 0 if @port.nil?
- @path = "" if @path.nil?
- @vars = "" if @vars.nil?
- @anchor = "" if @anchor.nil?
-
- res = {}
- @varsvolgorde = []
- @vars.split(/&/).each{|var| k, v = var.split(/=/) ; res[k] = v ; @varsvolgorde << k}
- @vars = res
-
- @port = @port.to_i
- end
-
- 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
- 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?
-
- res = URI::HTTP.new(@protocol, @userpass, @host, port, nil, @path, nil, vars, @anchor).to_s.from_html
-
- res.gsub!(/@/, "") if (@userpass.nil? or @userpass.empty?)
-
- res.gsub!(/\#$/, "")
-
- return res
- end
-
- def varstring
- res = []
- vars = @vars.dup
-
- @varsvolgorde.each do |k|
- if vars.include?(k)
- v = vars[k]
- vars.delete(k)
-
- res << (v.nil? ? k : "#{k}=#{v}")
- end
- end
-
- res.concat(vars.collect{|k, v| v.nil? ? k : "#{k}=#{v}"})
-
- return res.join("&")
- end
-end
-
-class HTTPClient
- @@versie = 1
- @@mutex = Mutex.new
- @@hosts = {}
-
- class Header
- attr_reader :header
- attr_reader :protocol
- attr_reader :code
- attr_reader :text
-
- def initialize(header)
- @header = {}
-
- if not header.nil?
- firstline, rest = header.split(/\r*\n/, 2)
-
- @protocol, @code, @text = firstline.split(/ */, 3)
-
- @code = @code.to_i
-
- if not rest.nil?
- rest.split(/\r*\n/).each do |line|
- key, value = line.split(/ /, 2)
- @header[key.sub(/:$/, "").downcase] = value
- end
- end
- end
- end
-
- def to_s
- res = ""
-
- res << "%s %s %s\n" % [@protocol, @code, @text]
-
- @header.each do |k, v|
- res << "%s=%s\n" % [k, v]
- end
-
- return res
- end
- end
-
- class Chunk
- def initialize(data)
- @data = ""
- line, data = data.split(/\r*\n/, 2)
- size, ext = line.split(/;/, 2)
- size = size.hex
- while not size.zero? and not data.nil?
- @data += data[0..(size-1)]
- data = data[size..-1]
- if not data.nil?
- data.gsub!(/^\r*\n/, "")
- line, data = data.split(/\r*\n/, 2)
- size, ext = line.split(/;/, 2)
- size = size.hex
- end
- end
- end
-
- def to_s
- @data
- end
- 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
- io = nil
-
- @@mutex.synchronize do
- @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
- io = TCPSocket.new(@@hosts[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(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
- 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
- header = Header.new(nil)
- end
-
- return header
- end
-
- def self.get(uri, 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
-
- io = nil
- @@mutex.synchronize do
- @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
- io = TCPSocket.new(@@hosts[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(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 "Accept-Encoding: deflate\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?
- io.write "\r\n"
- io.write post unless post.empty?
-
- io.close_write
-
- res = io.read
- header, data = nil, nil
- header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil?
-
- header = Header.new(header)
-
- if header.header["location"] != uri.to_s
- uri = EVURI.new(uri) + header.header["location"]
- else
- uri = nil
- end
-
- if header.header["transfer-encoding"] == "chunked"
- data = Chunk.new(data).to_s if not data.nil?
- end
-
- data = nil unless header.code == 200
- end
- rescue
- data = nil
- end
-
- return data
- end
-
- def self.head_from_cache(uri, form={})
- from_cache("head", uri, form)
- end
-
- def self.get_from_cache(uri, form={})
- from_cache("get", uri, form)
- end
-
- def self.from_cache(action, uri, form)
- loc = uri.to_s + form.sort.inspect
- hash = MD5.new("#{@@versie} #{loc}")
-
- dir = "#{temp}/evcache.#{user}/httpclient.#{action}"
- file = "#{dir}/#{hash}"
- data = nil
-
- Dir.mkdirrec(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
- File.open(file, "rb") {|f| data = f.read}
- end
- else
- data = method(action).call(uri, form)
-
- if not data.nil?
- @@mutex.synchronize do
- File.open(file, "wb") {|f| f.write data}
- end
- end
- end
-
- return data
- end
-end
-
-class RequestGet < Hash
- def initialize(data)
- CGI.parse(data).each do |k, v|
- self[k] = v.join(" ")
- end
- end
-end
-
-class RequestPost < Hash
- def initialize(data)
- CGI.parse(data).each do |k, v|
- self[k] = v.join(" ")
- end
- end
-end
-
-class RequestRequest
- attr_reader :method
- attr_reader :uri
- attr_reader :path
- attr_reader :data
- attr_reader :protocol
-
- def initialize(firstline)
- @method, @uri, @protocol = firstline.split(/ /)
- @path, @data = @uri.split(/\?/)
- @data = "" if @data.nil? # TODO
-
-# i = @path.index(/%[[:digit:]]{2}/)
-# while not i.nil?
-# @path = @path[0..(i-1)] + @path[(i+1)..(i+2)].unpack('H2').shift.to_i.chr + @path[(i+3)..-1]
-# i = @path.index(/%[[:digit:]]{2}/)
-# end
- end
-
- def to_s
- "#{@method} #{@uri} #{@protocol}\r\n"
- end
-
- def inspect
- "(RequestRequest: %s)" % [@method, @path, @data, @protocol].join(", ")
- end
-end
-
-class Request < Hash
- attr_reader :peeraddr
- attr_reader :request
- attr_reader :cookies
- attr_reader :vars
-
- def initialize(io)
- @io = io
-
- firstline = @io.gets
-
- return if firstline.nil?
-
- @request = RequestRequest.new(firstline.strip)
-
- line = @io.gets
- line = line.strip unless line.nil?
- while not line.nil? and not line.empty?
- key, value = line.split(" ", 2)
- self[key.sub(/:$/, "").downcase] = value
-
- line = @io.gets
- line = line.strip unless line.nil?
- end
-
- cookie = self["cookie"]
- cookie = "" if cookie.nil?
- @cookies = {}
- cookie.split(/;/).each do |s|
- k, v = s.strip.split(/=/, 2)
- @cookies[k] = v
- end
-
- if not @request.method.nil?
- case @request.method.upcase
- when "HEAD"
- when "GET"
- @vars = RequestGet.new(@request.data.nil? ? "" : @request.data)
- when "POST"
- data = (@io.read(self["content-length"].to_i) or "")
- @vars = RequestPost.new((self["content-type"] == "application/x-www-form-urlencoded") ? data : "")
- else
- $stderr.puts "Unknown request ('#{firstline}')."
- end
- end
-
- @peeraddr = @io.peeraddr
-
- @pda = false
- @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("windows ce"))
- @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("handhttp"))
-
- @io.close_read
- end
-
- def pda?
- @pda
- end
-
- def to_s
- res = @request.to_s
- self.each do |k, v|
- res << "#{k}: #{v}\r\n"
- end
- res
- end
-
- def inspect
- "(Request: %s)" % [@peeraddr, @request.inspect, @vars.inspect, @cookies.inspect, super].join(", ")
- end
-end
-
-class Response < Hash
- attr_writer :response
- attr_reader :cookies
- attr_reader :stop
- attr_reader :at_stop
-
- def initialize(io)
- @io = io
- @response = "HTTP/1.0 200 OK"
- @cookies = {}
- @data = ""
- @syncd = false
- @stop = false
- @at_stop = lambda{}
- end
-
- def flush
- sync
- @io.close
- end
-
- def to_s
- res = "#{@response}\r\n"
- self.each do |k, v|
- res << "#{k}: #{v}\r\n"
- end
-
- @cookies.each do |k, v|
- res << "Set-Cookie: %s=%s;\r\n" % [k, v]
- end
-
- res
- end
-
- def sync
- @io.write("#{to_s}\r\n") unless @syncd
- @io.write(@data)
- @data = ""
- @syncd = true
- end
-
- def << (s)
- @data << s
- end
-
- def inspect
- "(Response: %s)" % [@response, @data].join(", ")
- end
-
- def stop(&block)
- @stop = true
- @at_stop = block unless block.nil?
- end
-
- def stop?
- @stop
- end
-end
-
-class HTTPServerException < Exception
-end
-
-class HTTPServer
- def self.serve(portio=80, remote=false, auth=nil, realm="ev/net")
- port, server = portio
-
- begin
- server = TCPServer.new(remote ? "0.0.0.0" : "localhost", port) if server.nil?
-
- $stderr.puts "Just point your browser to http://localhost:#{port}/ ..."
- rescue
- server = nil
-
- $stderr.puts "Port #{port} is in use."
- end
-
- if not server.nil?
- count = 0
-
- at_exit do
- $stderr.puts "Received #{count} requests"
- end
-
- serverthread =
- Thread.new do
- mutex = Mutex.new
-
- Thread.current["threads"] = []
-
- every(1, Thread.current) do |thread|
- mutex.synchronize do
- thread["threads"].delete_if{|t| (not t.alive?)}
- end
- end
-
- loop do
- io = server.accept
- count += 1
-
- thread =
- Thread.new(Thread.current, count) do |parentthread, count2|
- stop = false
-
- begin
- begin
- req = Request.new(io)
- resp = Response.new(io)
- rescue
- raise HTTPServerException
- end
-
- begin
- ip = req.peeraddr[3]
- 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}"
-
- 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
- end
-
- stop = true if resp.stop?
- end
-
- begin
- resp.flush
- rescue
- raise HTTPServerException
- end
- rescue HTTPServerException
- end
-
- parentthread["stop"] = resp if stop
- end
-
- mutex.synchronize do
- Thread.current["threads"] << thread
- end
- end
- end
-
- sleep 0.1 while not serverthread["stop"]
-
- serverthread["threads"].each {|t| t.join}
-
- serverthread["stop"].at_stop.call
-
- serverthread.kill
- end
- end
-
- def self.authenticate(auth, realm, req, resp)
- if auth.kind_of? String
- file = "#{home}/#{auth}"
- auths = {}
- auths = Hash.file(file) if File.file?(file)
- else
- auths = auth
- end
-
- authuserpassword = req["authorization"]
- if not authuserpassword.nil?
- authtype, userpassword = authuserpassword.split(/ /)
- if authtype == "Basic" and not userpassword.nil?
- u, p = userpassword.unpack("m").shift.split(/:/)
- end
- end
-
- ok = (auths.include?(u) and auths[u] == p)
-
- if ok
-
- else
- resp["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
- resp.response = "HTTP/1.0 401 Unauthorized"
- end
-
- return ok
- end
-end
+require "ev/ruby"
+require "ev/ftools"
+require "net/http"
+require "socket"
+require "uri"
+require "cgi"
+require "md5"
+require "thread"
+
+$proxy = ENV["PROXY"] if $proxy.nil?
+
+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)
+ 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
+ s
+end
+
+class TCPServer
+ def self.freeport(from, to, remote=false)
+ if windows? or cygwin?
+ TCPServer.freeport_windows(from, to, remote)
+ else
+ TCPServer.freeport_linux(from, to, remote)
+ end
+ end
+
+ def self.freeport_linux(from, to, remote)
+ ports = (from..to).to_a
+ port = nil
+ res = nil
+
+ while res.nil? and not ports.empty?
+ begin
+ port = ports[0]
+ ports.delete(port)
+
+ io = TCPServer.new(remote ? "0.0.0.0" : "localhost", port)
+
+ res = [port, io]
+ rescue
+ end
+ end
+
+ res = [nil, nil] if res.nil?
+
+ port, io = res
+
+ return port, io
+ end
+
+ def self.freeport_windows(from, to, remote)
+ ports = (from..to).to_a
+ port = nil
+ res = nil
+
+ while res.nil? and not ports.empty?
+ begin
+ port = ports.any
+ ports.delete(port)
+
+ io = TCPSocket.new("localhost", port)
+ io.close
+ rescue
+ res = port
+ end
+ end
+
+ port, io = res
+
+ return port, io
+ end
+
+ def self.freeport_windows2(from, to, remote)
+ res = nil
+ port = from
+
+ while res.nil? and port <= to
+ begin
+ io = TCPSocket.new("localhost", port)
+ io.close
+
+ port += 1
+ rescue
+ res = port
+ end
+ end
+
+ return res
+ end
+
+ def self.usedports(from, to)
+ threads = []
+ res = []
+
+ from.upto(to) do |port|
+ threads << Thread.new do
+ begin
+ io = TCPSocket.new("localhost", port)
+ io.close
+
+ port
+ rescue
+ nil
+ end
+ end
+ end
+
+ threads.each do |thread|
+ port = thread.value
+ res << port unless port.nil?
+ end
+
+ return res
+ end
+end
+
+class EVURI
+ attr_reader :protocol
+ attr_writer :protocol
+ attr_reader :userpass
+ attr_writer :userpass
+ attr_reader :host
+ attr_writer :host
+ attr_reader :port
+ attr_writer :port
+ attr_reader :path
+ attr_writer :path
+ attr_reader :vars
+ attr_writer :vars
+ attr_reader :anchor
+ attr_writer :anchor
+
+ def initialize(url)
+ begin
+ @protocol, @userpass, @host, @port, d1, @path, d2, @vars, @anchor = URI.split(url.to_s)
+ rescue
+ end
+
+ @protocol = "" if @protocol.nil?
+ @userpass = "" if @userpass.nil?
+ @host = "" if @host.nil?
+ @port = 0 if @port.nil?
+ @path = "" if @path.nil?
+ @vars = "" if @vars.nil?
+ @anchor = "" if @anchor.nil?
+
+ res = {}
+ @varsvolgorde = []
+ @vars.split(/&/).each{|var| k, v = var.split(/=/) ; res[k] = v ; @varsvolgorde << k}
+ @vars = res
+
+ @port = @port.to_i
+ end
+
+ 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
+ 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?
+
+ res = URI::HTTP.new(@protocol, @userpass, @host, port, nil, @path, nil, vars, @anchor).to_s.from_html
+
+ res.gsub!(/@/, "") if (@userpass.nil? or @userpass.empty?)
+
+ res.gsub!(/\#$/, "")
+
+ return res
+ end
+
+ def varstring
+ res = []
+ vars = @vars.dup
+
+ @varsvolgorde.each do |k|
+ if vars.include?(k)
+ v = vars[k]
+ vars.delete(k)
+
+ res << (v.nil? ? k : "#{k}=#{v}")
+ end
+ end
+
+ res.concat(vars.collect{|k, v| v.nil? ? k : "#{k}=#{v}"})
+
+ return res.join("&")
+ end
+end
+
+class HTTPClient
+ @@versie = 1
+ @@mutex = Mutex.new
+ @@hosts = {}
+
+ class Header
+ attr_reader :header
+ attr_reader :protocol
+ attr_reader :code
+ attr_reader :text
+
+ def initialize(header)
+ @header = {}
+
+ if not header.nil?
+ firstline, rest = header.split(/\r*\n/, 2)
+
+ @protocol, @code, @text = firstline.split(/ */, 3)
+
+ @code = @code.to_i
+
+ if not rest.nil?
+ rest.split(/\r*\n/).each do |line|
+ key, value = line.split(/ /, 2)
+ @header[key.sub(/:$/, "").downcase] = value
+ end
+ end
+ end
+ end
+
+ def to_s
+ res = ""
+
+ res << "%s %s %s\n" % [@protocol, @code, @text]
+
+ @header.each do |k, v|
+ res << "%s=%s\n" % [k, v]
+ end
+
+ return res
+ end
+ end
+
+ class Chunk
+ def initialize(data)
+ @data = ""
+ line, data = data.split(/\r*\n/, 2)
+ size, ext = line.split(/;/, 2)
+ size = size.hex
+ while not size.zero? and not data.nil?
+ @data += data[0..(size-1)]
+ data = data[size..-1]
+ if not data.nil?
+ data.gsub!(/^\r*\n/, "")
+ line, data = data.split(/\r*\n/, 2)
+ size, ext = line.split(/;/, 2)
+ size = size.hex
+ end
+ end
+ end
+
+ def to_s
+ @data
+ end
+ 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
+ io = nil
+
+ @@mutex.synchronize do
+ @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
+ io = TCPSocket.new(@@hosts[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(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
+ 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
+ header = Header.new(nil)
+ end
+
+ return header
+ end
+
+ def self.get(uri, 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
+
+ io = nil
+ @@mutex.synchronize do
+ @@hosts[host] = IPSocket.getaddress(host) if not @@hosts.include?(host)
+ io = TCPSocket.new(@@hosts[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(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 "Accept-Encoding: deflate\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?
+ io.write "\r\n"
+ io.write post unless post.empty?
+
+ io.close_write
+
+ res = io.read
+ header, data = nil, nil
+ header, data = res.split(/\r*\n\r*\n/, 2) if not res.nil?
+
+ header = Header.new(header)
+
+ if header.header["location"] != uri.to_s
+ uri = EVURI.new(uri) + header.header["location"]
+ else
+ uri = nil
+ end
+
+ if header.header["transfer-encoding"] == "chunked"
+ data = Chunk.new(data).to_s if not data.nil?
+ end
+
+ data = nil unless header.code == 200
+ end
+ rescue
+ data = nil
+ end
+
+ return data
+ end
+
+ def self.head_from_cache(uri, form={})
+ from_cache("head", uri, form)
+ end
+
+ def self.get_from_cache(uri, form={})
+ from_cache("get", uri, form)
+ end
+
+ def self.from_cache(action, uri, form)
+ loc = uri.to_s + form.sort.inspect
+ hash = MD5.new("#{@@versie} #{loc}")
+
+ dir = "#{temp}/evcache.#{user}/httpclient.#{action}"
+ file = "#{dir}/#{hash}"
+ data = nil
+
+ Dir.mkdirrec(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
+ File.open(file, "rb") {|f| data = f.read}
+ end
+ else
+ data = method(action).call(uri, form)
+
+ if not data.nil?
+ @@mutex.synchronize do
+ File.open(file, "wb") {|f| f.write data}
+ end
+ end
+ end
+
+ return data
+ end
+end
+
+class RequestGet < Hash
+ def initialize(data)
+ CGI.parse(data).each do |k, v|
+ self[k] = v.join(" ")
+ end
+ end
+end
+
+class RequestPost < Hash
+ def initialize(data)
+ CGI.parse(data).each do |k, v|
+ self[k] = v.join(" ")
+ end
+ end
+end
+
+class RequestRequest
+ attr_reader :method
+ attr_reader :uri
+ attr_reader :path
+ attr_reader :data
+ attr_reader :protocol
+
+ def initialize(firstline)
+ @method, @uri, @protocol = firstline.split(/ /)
+ @path, @data = @uri.split(/\?/)
+ @data = "" if @data.nil? # TODO
+
+# i = @path.index(/%[[:digit:]]{2}/)
+# while not i.nil?
+# @path = @path[0..(i-1)] + @path[(i+1)..(i+2)].unpack('H2').shift.to_i.chr + @path[(i+3)..-1]
+# i = @path.index(/%[[:digit:]]{2}/)
+# end
+ end
+
+ def to_s
+ "#{@method} #{@uri} #{@protocol}\r\n"
+ end
+
+ def inspect
+ "(RequestRequest: %s)" % [@method, @path, @data, @protocol].join(", ")
+ end
+end
+
+class Request < Hash
+ attr_reader :peeraddr
+ attr_reader :request
+ attr_reader :cookies
+ attr_reader :vars
+
+ def initialize(io)
+ @io = io
+
+ firstline = @io.gets
+
+ return if firstline.nil?
+
+ @request = RequestRequest.new(firstline.strip)
+
+ line = @io.gets
+ line = line.strip unless line.nil?
+ while not line.nil? and not line.empty?
+ key, value = line.split(" ", 2)
+ self[key.sub(/:$/, "").downcase] = value
+
+ line = @io.gets
+ line = line.strip unless line.nil?
+ end
+
+ cookie = self["cookie"]
+ cookie = "" if cookie.nil?
+ @cookies = {}
+ cookie.split(/;/).each do |s|
+ k, v = s.strip.split(/=/, 2)
+ @cookies[k] = v
+ end
+
+ if not @request.method.nil?
+ case @request.method.upcase
+ when "HEAD"
+ when "GET"
+ @vars = RequestGet.new(@request.data.nil? ? "" : @request.data)
+ when "POST"
+ data = (@io.read(self["content-length"].to_i) or "")
+ @vars = RequestPost.new((self["content-type"] == "application/x-www-form-urlencoded") ? data : "")
+ else
+ $stderr.puts "Unknown request ('#{firstline}')."
+ end
+ end
+
+ @peeraddr = @io.peeraddr
+
+ @pda = false
+ @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("windows ce"))
+ @pda = true if (self.include?("user-agent") and self["user-agent"].downcase.include?("handhttp"))
+
+ @io.close_read
+ end
+
+ def pda?
+ @pda
+ end
+
+ def to_s
+ res = @request.to_s
+ self.each do |k, v|
+ res << "#{k}: #{v}\r\n"
+ end
+ res
+ end
+
+ def inspect
+ "(Request: %s)" % [@peeraddr, @request.inspect, @vars.inspect, @cookies.inspect, super].join(", ")
+ end
+end
+
+class Response < Hash
+ attr_writer :response
+ attr_reader :cookies
+ attr_reader :stop
+ attr_reader :at_stop
+
+ def initialize(io)
+ @io = io
+ @response = "HTTP/1.0 200 OK"
+ @cookies = {}
+ @data = ""
+ @syncd = false
+ @stop = false
+ @at_stop = lambda{}
+ end
+
+ def flush
+ sync
+ @io.close
+ end
+
+ def to_s
+ res = "#{@response}\r\n"
+ self.each do |k, v|
+ res << "#{k}: #{v}\r\n"
+ end
+
+ @cookies.each do |k, v|
+ res << "Set-Cookie: %s=%s;\r\n" % [k, v]
+ end
+
+ res
+ end
+
+ def sync
+ @io.write("#{to_s}\r\n") unless @syncd
+ @io.write(@data)
+ @data = ""
+ @syncd = true
+ end
+
+ def << (s)
+ @data << s
+ end
+
+ def inspect
+ "(Response: %s)" % [@response, @data].join(", ")
+ end
+
+ def stop(&block)
+ @stop = true
+ @at_stop = block unless block.nil?
+ end
+
+ def stop?
+ @stop
+ end
+end
+
+class HTTPServerException < Exception
+end
+
+class HTTPServer
+ def self.serve(portio=80, remote=false, auth=nil, realm="ev/net")
+ port, server = portio
+
+ begin
+ server = TCPServer.new(remote ? "0.0.0.0" : "localhost", port) if server.nil?
+
+ $stderr.puts "Just point your browser to http://localhost:#{port}/ ..."
+ rescue
+ server = nil
+
+ $stderr.puts "Port #{port} is in use."
+ end
+
+ if not server.nil?
+ count = 0
+
+ at_exit do
+ $stderr.puts "Received #{count} requests"
+ end
+
+ serverthread =
+ Thread.new do
+ mutex = Mutex.new
+
+ Thread.current["threads"] = []
+
+ every(1, Thread.current) do |thread|
+ mutex.synchronize do
+ thread["threads"].delete_if{|t| (not t.alive?)}
+ end
+ end
+
+ loop do
+ io = server.accept
+ count += 1
+
+ thread =
+ Thread.new(Thread.current, count) do |parentthread, count2|
+ stop = false
+
+ begin
+ begin
+ req = Request.new(io)
+ resp = Response.new(io)
+ rescue
+ raise HTTPServerException
+ end
+
+ begin
+ ip = req.peeraddr[3]
+ 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}"
+
+ 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
+ end
+
+ stop = true if resp.stop?
+ end
+
+ begin
+ resp.flush
+ rescue
+ raise HTTPServerException
+ end
+ rescue HTTPServerException
+ end
+
+ parentthread["stop"] = resp if stop
+ end
+
+ mutex.synchronize do
+ Thread.current["threads"] << thread
+ end
+ end
+ end
+
+ sleep 0.1 while not serverthread["stop"]
+
+ serverthread["threads"].each {|t| t.join}
+
+ serverthread["stop"].at_stop.call
+
+ serverthread.kill
+ end
+ end
+
+ def self.authenticate(auth, realm, req, resp)
+ if auth.kind_of? String
+ file = "#{home}/#{auth}"
+ auths = {}
+ auths = Hash.file(file) if File.file?(file)
+ else
+ auths = auth
+ end
+
+ authuserpassword = req["authorization"]
+ if not authuserpassword.nil?
+ authtype, userpassword = authuserpassword.split(/ /)
+ if authtype == "Basic" and not userpassword.nil?
+ u, p = userpassword.unpack("m").shift.split(/:/)
+ end
+ end
+
+ ok = (auths.include?(u) and auths[u] == p)
+
+ if ok
+
+ else
+ resp["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
+ resp.response = "HTTP/1.0 401 Unauthorized"
+ end
+
+ return ok
+ end
+end