Class: WebserverAbstract
Overview
Tiny embeded webserver
Direct Known Subclasses
Constant Summary collapse
- LICON =
"☀☃☎☑☑☠☣☮☺☂".split(/;/).map {|c| c+";"}
- MIME =
{"png" => "image/png", "gif" => "image/gif", "html" => "text/html","htm" => "text/html", "js" => "text/javascript" ,"css" => "text/css","jpeg" => "image/jpeg" ,"jpg" => "image/jpeg" , "pdf"=> "application/pdf" , "svg" => "image/svg+xml","svgz" => "image/svg+xml", "xml" => "text/xml" ,"xsl" => "text/xml" ,"bmp" => "image/bmp" ,"txt" => "text/plain" , "rb" => "text/plain" ,"pas" => "text/plain" ,"tcl" => "text/plain" ,"java" => "text/plain" , "c" => "text/plain" ,"h" => "text/plain" ,"cpp" => "text/plain", "xul" => "application/vnd.mozilla.xul+xml", "doc" => "application/msword", "docx" => "application/msword","dot"=> "application/msword", "xls" => "application/vnd.ms-excel","xla" => "application/vnd.ms-excel","xlt" => "application/vnd.ms-excel","xlsx" => "application/vnd.ms-excel", "ppt" => "application/vnd.ms-powerpoint", "pptx" => "application/vnd.ms-powerpoint" }
Instance Method Summary collapse
- #do_service(session, request, service, user_passwd, params) ⇒ Object
- #error(txt) ⇒ Object
- #escape(string) ⇒ Object
- #hescape(string) ⇒ Object
- #httpdate(aTime) ⇒ Object
- #info(txt) ⇒ Object
-
#initialize(port, root, name, cadence, timeout, options) ⇒ WebserverAbstract
constructor
A new instance of WebserverAbstract.
- #logg(*args) ⇒ Object
- #makeIndex(adir) ⇒ Object
- #mime(string) ⇒ Object
- #n3(n) ⇒ Object
- #observe(sleeping, delta) ⇒ Object
- #redirect(o, d) ⇒ Object
- #request(session) ⇒ Object
- #run(session) ⇒ Object
- #sendData(sock, type, content) ⇒ Object
- #sendError(sock, no, txt = nil) ⇒ Object
- #sendFile(sock, filename) ⇒ Object
- #serve(uri, &blk) ⇒ Object
- #stop_browser ⇒ Object
- #to_absolute(f) ⇒ Object
- #to_relative(f) ⇒ Object
- #to_table(l) ⇒ Object
- #to_tableb(l, &bl) ⇒ Object
- #unescape(string) ⇒ Object
Constructor Details
#initialize(port, root, name, cadence, timeout, options) ⇒ WebserverAbstract
Returns a new instance of WebserverAbstract.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/plugins/httpd.rb', line 22 def initialize(port,root,name,cadence,timeout,) @cb_log= ["logg"] @last_mtime=File.mtime(__FILE__) @port=port @root=root @name=name @rootd=root[-1,1]=="/" ? root : root+"/" @timeout=timeout @th={} @cb={} @redirect={} @server = TCPServer.new('0.0.0.0', @port) @server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true) info(" serveur http #{port} on #{@rootd} ready!") observe(cadence,timeout*2) @thm=Thread.new { loop { begin while (session = @server.accept) run(session) end rescue error($!.to_s) session.close raise nil end sleep(10); info("restart accept") } } end |
Instance Method Details
#do_service(session, request, service, user_passwd, params) ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/plugins/httpd.rb', line 87 def do_service(session,request,service,user_passwd,params) logg(session.peeraddr.last,request) redir=@redirect["/"+service] service=redir.gsub(/^\//,"") if @redirect[redir] aservice=to_absolute(service) if redir && ! @redirect[redir] do_service(session,request,redir.gsub(/^\//,""),user_passwd,params) elsif @cb["/"+service] begin code,type,data= @cb["/"+service].call(params) if code==0 && data != '/'+service do_service(session,request,data[1..-1],user_passwd,params) else code==200 ? sendData(session,type,data) : sendError(session,code,data) end rescue logg session.peeraddr.last,"Error in get /#{service} : #{$!}" sendError(session,501,$!.to_s) end elsif service =~ /^stop/ sendData(session,".html","Stopping..."); Thread.new() { sleep(0.1); stop_browser() } elsif File.directory?(aservice) sendData(session,".html",makeIndex(aservice)) elsif File.exists?(aservice) sendFile(session,aservice) else info("unknown request serv=#{service} params=#{params.inspect} #{File.exists?(service)}") sendError(session,500); end end |
#error(txt) ⇒ Object
10 |
# File 'lib/plugins/httpd.rb', line 10 def error(txt) ; logg("nw>e>",txt) ; end |
#escape(string) ⇒ Object
12 |
# File 'lib/plugins/httpd.rb', line 12 def escape(string) ; string.gsub(/([^ \/a-zA-Z0-9_.-]+)/) { '%' + $1.unpack('H2' * $1.size).join('%').upcase }.tr(' ', '+'); end |
#hescape(string) ⇒ Object
13 |
# File 'lib/plugins/httpd.rb', line 13 def hescape(string) ; escape(string.gsub("/./","/").gsub("//","/")) ; end |
#httpdate(aTime) ⇒ Object
172 |
# File 'lib/plugins/httpd.rb', line 172 def httpdate( aTime ); (aTime||Time.now).gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" ); end |
#info(txt) ⇒ Object
9 |
# File 'lib/plugins/httpd.rb', line 9 def info(txt) ; logg("nw>i>",txt) ; end |
#logg(*args) ⇒ Object
8 |
# File 'lib/plugins/httpd.rb', line 8 def logg(*args) @cb_log && @cb_log.call(@name,*args) end |
#makeIndex(adir) ⇒ Object
123 124 125 126 127 128 129 130 |
# File 'lib/plugins/httpd.rb', line 123 def makeIndex(adir) dir=to_relative(adir) dirs,files=Dir.glob(adir==@rootd ? "#{@rootd}*" : "#{adir}/*").sort.partition { |f| File.directory?(f)} updir = hescape( dir.split(/\//)[0..-2].join("/")) updir="/" if updir.length==0 up=(dir!="/") ? "<input type='button' onclick='location.href=\"#{updir}\"' value='Parent'>" : "" "<html><head><title>#{dir}</title></head>\n<body><h3><center>#{@name} : #{dir[0..-1]}</center></h3>\n<hr>#{up}<br>#{to_table(dirs.map {|s| " <a href='#{hescape(to_relative(s))}'>"+File.basename(s)+"/"+"</a>\n"})}<hr>#{to_tableb(files) {|f| [" <a href='#{hescape(to_relative(f))}'>"+File.basename(f)+"</a>",n3(File.size(f)),File.mtime(f).strftime("%d/%m/%Y %H:%M:%S")]}}</body></html>" end |
#mime(string) ⇒ Object
173 174 175 |
# File 'lib/plugins/httpd.rb', line 173 def mime(string) MIME[string.split(/\./).last] || "application/octet-stream" end |
#n3(n) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/plugins/httpd.rb', line 133 def n3(n) u=" B" if n> 10000000 n=n/(1024*1024) u=" MB" elsif n> 100000 n=n/1024 u=" KB" end "<div style='width:100px;text-align:right;'>#{(n.round.to_i.to_s.reverse.gsub(/(\d\d\d)(?=\d)/,'\1 ' ).reverse) +u} | </div>" end |
#observe(sleeping, delta) ⇒ Object
14 15 16 17 18 19 20 21 |
# File 'lib/plugins/httpd.rb', line 14 def observe(sleeping,delta) @tho=Thread.new do loop do sleep(sleeping) nowDelta=Time.now-delta l=@th.select { |th,tm| (tm[0]<nowDelta) } l.each { |th,tm| info("killing thread") ; th.kill; @th.delete(th) ; tm[1].close rescue nil } end ; end end |
#redirect(o, d) ⇒ Object
84 85 86 |
# File 'lib/plugins/httpd.rb', line 84 def redirect(o,d) @redirect[o]=d end |
#request(session) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/plugins/httpd.rb', line 64 def request(session) request = session.gets uri = (request.split(/\s+/)+['','',''])[1] #info uri service,param,*bidon=(uri+"?").split(/\?/) params=Hash[*(param.split(/#/)[0].split(/[=&]/))] rescue {} params.each { |k,v| params[k]=unescape(v) } uri=unescape(service)[1..-1].gsub(/\.\./,"") userpass=nil if (buri=uri.split(/@/)).size>1 uri=buri[1..-1].join("@") userpass=buri[0].split(/:/) end do_service(session,request,uri,userpass,params) rescue error("Error Web get on #{request}: \n #{$!.to_s} \n #{$!.backtrace.join("\n ")}" ) rescue nil session.write "HTTP/1.1 501 NOK\r\nContent-type: text/html\r\n\r\n<html><head><title>WS</title></head><body>Error : #{$!}" rescue nil ensure session.close rescue nil end |
#run(session) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/plugins/httpd.rb', line 50 def run(session) if ! File.exists?(@root) sendError(session,500,txt="root directory unknown: #{@root}") else Thread.new(session) do |sess| @th[Thread.current]=[Time.now,sess] request(sess) @th.delete(Thread.current) end end end |
#sendData(sock, type, content) ⇒ Object
156 157 158 159 |
# File 'lib/plugins/httpd.rb', line 156 def sendData(sock,type,content) sock.write "HTTP/1.0 200 OK\r\nContent-type: #{mime(type)}\r\nContent-size: #{content.size}\r\n\r\n" sock.write(content) end |
#sendError(sock, no, txt = nil) ⇒ Object
150 151 152 153 154 155 |
# File 'lib/plugins/httpd.rb', line 150 def sendError(sock,no,txt=nil) if txt txt="<html><body><code><pre></pre>#{txt}</code></body></html>" end sock.write "HTTP/1.0 #{no} NOK\r\nContent-type: #{mime(".html")}\r\n\r\n <html><p>Error #{no} : #{txt}</p></html>" end |
#sendFile(sock, filename) ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/plugins/httpd.rb', line 160 def sendFile(sock,filename) s=File.size(filename) if s < 0 || File.extname(filename).downcase==".lnk" sendError(sock,500,"Error reading file #{filename} : (size=#{s})" ) return end sock.write "HTTP/1.0 200 OK\r\nContent-type: #{mime(filename)}\r\nContent-size: #{File.size(filename)}\r\nLast-Modified: #{httpdate(File.mtime(filename))}\r\nDate: #{httpdate(Time.now)}\r\n\r\n" File.open(filename,"rb") do |f| f.binmode; sock.binmode; ( sock.write(f.read(32000)) while (! f.eof? && ! sock.closed?) ) rescue nil end end |
#serve(uri, &blk) ⇒ Object
61 62 63 |
# File 'lib/plugins/httpd.rb', line 61 def serve(uri,&blk) @cb[uri] = blk end |
#stop_browser ⇒ Object
118 119 120 121 122 |
# File 'lib/plugins/httpd.rb', line 118 def stop_browser info "exit on web demand !" @serveur.close rescue nil [@tho,@thm].each { |th| th.kill } end |
#to_absolute(f) ⇒ Object
132 |
# File 'lib/plugins/httpd.rb', line 132 def to_absolute(f) "#{@rootd}#{f.gsub(/^\//,'')}" end |
#to_relative(f) ⇒ Object
131 |
# File 'lib/plugins/httpd.rb', line 131 def to_relative(f) f.gsub(/^#{@rootd}/,"/") end |
#to_table(l) ⇒ Object
144 145 146 |
# File 'lib/plugins/httpd.rb', line 144 def to_table(l) "<table><tr>#{l.map {|s| "<td>#{s}</td>"}.join("</tr><tr>")}</tr></table>" end |
#to_tableb(l, &bl) ⇒ Object
147 148 149 |
# File 'lib/plugins/httpd.rb', line 147 def to_tableb(l,&bl) "<table><tr>#{l.map {|s| "<td>#{bl.call(s).join("</td><td>")}</td>"}.join("</tr><tr>")}</tr></table>" end |
#unescape(string) ⇒ Object
11 |
# File 'lib/plugins/httpd.rb', line 11 def unescape(string) ; string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2}))/n) { [$1.delete('%')].pack('H*') } ; end |