Class: WebserverAbstract

Inherits:
Object show all
Defined in:
lib/plugins/httpd.rb

Overview

Tiny embeded webserver

Direct Known Subclasses

Webserver, WebserverRoot

Constant Summary

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)

Constructor Details

- (WebserverAbstract) initialize(port, root, name, cadence, timeout, options)



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,options)
  @cb_log= options["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

- (Object) do_service(session, request, service, user_passwd, params)



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

- (Object) error(txt)



10
# File 'lib/plugins/httpd.rb', line 10

def error(txt) ; logg("nw>e>",txt) ; end

- (Object) escape(string)



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

- (Object) hescape(string)



13
# File 'lib/plugins/httpd.rb', line 13

def hescape(string) ;  escape(string.gsub("/./","/").gsub("//","/")) ; end

- (Object) httpdate(aTime)



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

- (Object) info(txt)



9
# File 'lib/plugins/httpd.rb', line 9

def info(txt) ; logg("nw>i>",txt) ;  end

- (Object) logg(*args)



8
# File 'lib/plugins/httpd.rb', line 8

def logg(*args)  @cb_log && @cb_log.call(@name,*args) end

- (Object) makeIndex(adir)



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

- (Object) mime(string)



173
174
175
# File 'lib/plugins/httpd.rb', line 173

def mime(string)
MIME[string.split(/\./).last] || "application/octet-stream"
end

- (Object) n3(n)



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

- (Object) observe(sleeping, delta)



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

- (Object) redirect(o, d)



84
85
86
# File 'lib/plugins/httpd.rb', line 84

def redirect(o,d)
 @redirect[o]=d
end

- (Object) request(session)



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

- (Object) run(session)



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

- (Object) sendData(sock, type, content)



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

- (Object) sendError(sock, no, txt = nil)



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

- (Object) sendFile(sock, filename)



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

- (Object) serve(uri, &blk)



61
62
63
# File 'lib/plugins/httpd.rb', line 61

def serve(uri,&blk)
	@cb[uri] = blk
end

- (Object) stop_browser



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

- (Object) to_absolute(f)



132
# File 'lib/plugins/httpd.rb', line 132

def to_absolute(f)  "#{@rootd}#{f.gsub(/^\//,'')}" end

- (Object) to_relative(f)



131
# File 'lib/plugins/httpd.rb', line 131

def to_relative(f)  f.gsub(/^#{@rootd}/,"/") end

- (Object) to_table(l)



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

- (Object) to_tableb(l, &bl)



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

- (Object) unescape(string)



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