class Object
Public Instance Methods
minimal_http_server(options={})
click to toggle source
# File lib/minimal-http-ruby.rb, line 18 def minimal_http_server options={} prev_t={} cache={} ports=['127.0.0.1'] if Socket.method_defined? :getifaddrs ports=Socket.getifaddrs.map { |i| i.addr.ip_address if i.addr.ipv4? }.compact puts "Starting HTTP services at port #{options[:http_port]}, server IPs: #{ports}" else puts "Starting HTTP services at port #{options[:http_port]}." end if options[:http_path] $http_dir=options[:http_path] elsif File.directory? './http' $http_dir="http/" else $http_dir = File.join( Gem.loaded_specs['minimal-http-ruby'].full_gem_path, 'http/') end if not File.directory? $http_dir puts "Warning: Http-path does not exist? -- Please create and populate!\nYou can use utility 'minimal-http-init.rb'" end puts "Serving pages from home directory: '#{$http_dir}'" statuses={ "200" => "OK", "404" => "Not Found", "500" => "Internal Server Error"} Thread.new(options[:http_port],options[:app_name]) do |http_port,http_app| server = TCPServer.new("0.0.0.0",http_port) @http_sessions={} http_session_counter=1 loop do Thread.start(server.accept) do |client| begin @start=Time.now client_port= client.peeraddr[1] client_ip= client.peeraddr[2] raw=client.gets if not raw puts "#{client_ip}:#{client_port} #{Time.now.iso8601} ?? nil request?" if options[:verbose] client.close next end raw=raw.chop method,req,http_proto = raw.split " " #puts "method:#{method}" #pp req status="200" type="text/html" req="/#{http_app||'index'}.html" if req=="/" or req=="/index.htm" or req=="/index.html" args={} argss=nil if method=="GET" req,argss=req.split "\?" else len=0 while line = client.gets puts "POST>#{line}" len=$1.to_i if line[/Content-Length: (\d+)/] break if line=="\r\n" end argss="" while argss.size<len argss+=client.readpartial(len-argss.size) puts argss end #argss=URI.decode(argss) puts argss end if argss argss.split("&").each do |a| #puts ">>><found '#{a}'" if a k,v=a.split "=" next if not v next if not k k=CGI.unescape(k).force_encoding("UTF-8") v=CGI.unescape(v).force_encoding("UTF-8") #args[k]=CGI.unescape(CGI.unescape(v)) #.force_encoding("UTF-8") if k[/(.+)\[\]$/] #puts "IS ARRAY--------------------- #{k} #{$1}" args[$1]=[] if not args[$1] args[$1]<<v else args[k]=v #CGI.unescape(v) #.force_encoding("UTF-8") end #puts "'#{v}' --> #{args[k]}" end end #puts "args:" #pp args #puts "------" end if req[/\.html$/] and File.file?(fn="#{$http_dir}haml#{req.gsub('.html','.haml')}") contents = File.read(fn) # never cached -- may be dynamically generated response=Haml::Engine.new(contents).render elsif req[/\.js$/] and File.file?(fn="#{$http_dir}coffee#{req.gsub('.js','.coffee')}") type="application/javascript" t=File.mtime(fn) if not prev_t[fn] or prev_t[fn]<t contents = File.read(fn) begin response=CoffeeScript.compile contents.force_encoding("UTF-8") prev_t[fn]=t cache[fn]=response rescue => e puts "**** COFFEE #{fn} failed: #{e}" pp e.backtrace type="text/html" status="500" response="Coffee Compile error: #{e}" end else response=cache[fn] end elsif req[/^\/(.+)\.json$/] and File.file?(fn="#{$http_dir}json#{req.gsub('.json','.rb')}") req[/\/(.+).json$/] act=$1 t=File.mtime(fn) if not prev_t[fn] or prev_t[fn]<t begin load_ok=load fn prev_t[fn]=t rescue Exception => e puts "**** RELOAD #{fn} failed: #{e}" pp e.backtrace response=[{act: :error, msg:"Error loading JSON",alert: "Load error #{e} in #{fn}"}].to_json type="application/json" status="404" end end if type!="text/event-stream" and status=="200" begin type,response=eval "json_#{act} [method,req,http_proto],args,0,0" #event handlers get called with zero session => init :) response=response.to_json rescue => e puts "**** AJAX EXEC #{fn} failed: #{e}" pp e.backtrace response=[{act: :error, msg:"Error executing JSON",alert: "Syntax error '#{e}' in '#{fn}'"}].to_json type="application/json" end end elsif File.file?(fnc="#{$http_dir}#{req}") type=MimeMagic.by_path(req) t=File.mtime(fnc) if not prev_t[fnc] or prev_t[fnc]<t contents = File.read(fnc) response=contents prev_t[fnc]=t cache[fnc]=response else response=cache[fnc] end else status="404" response="Not Found: #{req}" end client.print "HTTP/1.1 #{status} #{statuses[status]||'???'}\r\nContent-Type: #{type}\r\n" if type!="text/event-stream" client.print "Content-Length: #{response.bytesize}\r\n" client.print "Connection: close\r\n" client.print "Access-Control-Allow-Origin: *\r\n" client.print "\r\n" client.print response else client.print "Expires: -1\r\n" client.print "Access-Control-Allow-Origin: *\r\n" client.print "\r\n" my_session=nil begin my_session=client.peeraddr[1] if not @http_sessions[my_session] @http_sessions[my_session]={client_port:client.peeraddr[1],client_ip:client.peeraddr[2] , log_position:0 } end $sessions[my_session]={} my_event=0 loop do begin type,response=eval "json_#{act} raw,args,my_session,my_event" my_event+=1 rescue => e puts "**** AJAX EXEC #{fn} failed: #{e}" puts "#{e.backtrace[0..2]}" pp e.backtrace response=[{act: :error, msg:"Error executing JSON",alert: "Syntax error '#{e}' in '#{fn}'"}].to_json end if not response or response==[] or response=={} else client.print "retry: 1000\n" client.print "data: #{response.to_json}\n\n" end sleep 0.001 break if my_event>10000 end rescue Errno::EPIPE #quite normal ;) rescue => e puts "stream #{client} died #{e}" pp e.backtrace end if my_session and $sessions[my_session] and $sessions[my_session][:thread] and $sessions[my_session][:thread].status puts "HEY SOMETHING WAS RUNNING" $sessions[my_session][:thread].kill end end dur=sprintf "%.2f",(Time.now.to_f-@start.to_f) puts "#{client_ip}:#{client_port} #{Time.now.iso8601} \"#{method} #{req}\" #{status} #{response.bytesize} \"#{type}\" #{dur}" if options[:verbose] client.close rescue Exception =>e response="Error '#{e}'" status="500" type="text/html" dur=sprintf "%.2f",(Time.now.to_f-@start.to_f) puts "#{client_ip}:#{client_port} #{Time.now.iso8601} \"#{method} #{req}\" #{status} #{response.bytesize} \"#{type}\" #{dur}" client.print "HTTP/1.1 #{status} #{statuses[status]||'???'}\r\nContent-Type: #{type}\r\n\r\n" client.print response client.close puts e pp e.backtrace end end end end end