femtows 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/bin/femtows.bat +0 -0
  3. data/bin/femtows.sh +2 -2
  4. data/gitc.bat +20 -0
  5. data/lib/femtows.rb +263 -262
  6. metadata +12 -13
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cb91e5bf92627376e1417d32a8c897ac196510f6
4
+ data.tar.gz: 5c1667b4230b70efbb72e565078765b1ccf6d0cd
5
+ SHA512:
6
+ metadata.gz: f4ff73c70c9654078d76304b054662e06be080d230b64cec0c1fffdab2a425ba8c3beda4f6db170cc1c3608dd8d21ce23262c0665880154c201a96523c3e1764
7
+ data.tar.gz: 77732c765e07894cfa92f266d9a8109b126dce1fbf41b9b39a8cafbf0cb59263c3e2f3cbbfa352e94061a88c630c2cb24e0fd47745551ce03d90f840916508b0
data/bin/femtows.bat CHANGED
File without changes
data/bin/femtows.sh CHANGED
@@ -1,2 +1,2 @@
1
- require 'femtows'
2
- cliweb((ARGV[1]||"./"),(ARGV[0]||"8080").to_i)
1
+ require 'femtows'
2
+ cliweb((ARGV[1]||"./"),(ARGV[0]||"8080").to_i)
data/gitc.bat ADDED
@@ -0,0 +1,20 @@
1
+ @echo off
2
+ IF DEFINED %1=="" (
3
+ call giti
4
+ ruby -e "a=File.read('VERSION').split('.') ; a[-1]=(a.last.to_i+1).to_s; puts r=a.join('.'); File.open('VERSION','w') {|f| f.write(r)}"
5
+ echo %1 %2 %3 %4 %5 %6 %7 %8 %9 >> CHANGELOG.txt
6
+ git commit -a -m "%1 %2 %3 %4 %5 %6 %7 %8 %9"
7
+ git push
8
+ echo
9
+ echo call gitc.bat without args for make/post rubygem
10
+ goto :eof
11
+ )
12
+ :gem
13
+ rem ==== no args, generate gem and push it to rubygems.org
14
+
15
+ ruby -e "a=File.read('VERSION').split('.');a.pop ; a[-1]=(a.last.to_i+1).to_s; puts r=(a+[0]).join('.'); File.open('VERSION','w') {|f| f.puts(r)}"
16
+ cat VERSION >> CHANGELOG.txt
17
+
18
+ ruby -e "Dir.glob('femtows*.gem').each {|f| File.delete(f) }"
19
+ call gem build femtows.gemspec
20
+ call gem push femtows*.gem
data/lib/femtows.rb CHANGED
@@ -1,262 +1,263 @@
1
- # encoding: utf-8
2
- # FemtoWebServer : 232 LOC web server
3
- #
4
- # $ws=WebserverRoot.new(port,"/home/www","femto ws",10,300)
5
- # ws.serve "/FOO" do |params|
6
- # data=params["HEAD-DATA"]|| ""
7
- # puts "Recu data len=#{data.length} : <#{data[0..1000]}>" if data
8
- # [200,".json",""]
9
- # end
10
-
11
-
12
- require 'thread'
13
- require 'socket'
14
- require 'timeout'
15
-
16
- #################### Tiny embeded webserver
17
-
18
-
19
-
20
-
21
- class WebserverAbstract
22
- def logg(*args)
23
- if @cb_log then @cb_log.call(@name,*args) else puts(args.join(" ")) end
24
- rescue
25
- puts(args.join(" "))
26
- end
27
- def info(txt) ; logg("nw>i>",txt) ; end
28
- def error(txt) ; logg("nw>e>",txt) ; end
29
- def unescape(string) ; string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2}))/n) { [$1.delete('%')].pack('H*') } ; end
30
- def escape(string) ; string.gsub(/([^ \/a-zA-Z0-9_.-]+)/) { '%' + $1.unpack('H2' * $1.size).join('%').upcase }.tr(' ', '+'); end
31
- def hescape(string) ; escape(string.gsub("/./","/").gsub("//","/")) ; end
32
- def observe(sleeping,delta)
33
- @tho=Thread.new do loop do
34
- sleep(sleeping)
35
- nowDelta=Time.now-delta
36
- l=@th.select { |th,tm| (tm[0]<nowDelta) }
37
- l.each { |th,tm| info("killing thread") ; th.kill; @th.delete(th) ; tm[1].close rescue nil }
38
- end ; end
39
- end
40
- def initialize(port,root,name,cadence,timeout,options)
41
- raise("tcp port illegal #{port}") unless port.to_i>=80
42
- raise("root not exist #{root}") unless File.exists?(root)
43
- @cb_log= options["logg"]
44
- @last_mtime=File.mtime(__FILE__)
45
- @port=port.to_i
46
- @root=root
47
- @name=name
48
- @rootd=root[-1,1]=="/" ? root : root+"/"
49
- @timeout=timeout
50
- @th={}
51
- @cb={}
52
- @redirect={}
53
- info(" serveur http #{port} on #{@rootd} ready!")
54
- observe(cadence,timeout*2)
55
- pool_create
56
- @thm=Thread.new {
57
- loop {
58
- nbError=0
59
- begin
60
- session=nil
61
- @server = TCPServer.new('0.0.0.0', @port)
62
- @server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
63
- while (session = @server.accept)
64
- nbError=0
65
- run(session)
66
- end
67
- rescue Exception => e
68
- nbError+=1
69
- error($!.to_s + " " + $!.backtrace[0..2].join(" "))
70
- session.close rescue nil
71
- @server.close rescue nil
72
- end
73
- sleep(3); info("restart accept")
74
- }
75
- }
76
- end
77
- def pool_create
78
- @queue=Queue.new
79
- ici=self
80
- 100.times { Thread.new { loop {
81
- param,bloc=@queue.pop
82
- bloc.call(param) rescue p $!
83
- } } }
84
- end
85
- def pool_get(param,&block)
86
- @queue.push([param,block])
87
- end
88
- def run(session)
89
- pool_get(session) do |sess|
90
- @th[Thread.current]=[Time.now,sess]
91
- request(sess)
92
- @th.delete(Thread.current)
93
- end
94
- end
95
- def serve(uri,&blk)
96
- @cb[uri] = blk
97
- end
98
- def request(session)
99
- request = session.gets
100
- uri = (request.split(/\s+/)+['','',''])[1]
101
- #info uri
102
- service,param,*bidon=(uri+"?").split(/\?/)
103
- params=Hash[*(param.split(/#/)[0].split(/[=&]/))] rescue {}
104
- params.each { |k,v| params[k]=unescape(v) }
105
- uri=unescape(service)[1..-1].gsub(/\.\./,"")
106
- userpass=nil
107
- if (buri=uri.split(/@/)).size>1
108
- uri=buri[1..-1].join("@")
109
- userpass=buri[0].split(/:/)
110
- end
111
- read_header(session,params)
112
- do_service(session,request,uri,userpass,params)
113
- rescue Exception => e
114
- error("Error Web get on #{request}: \n #{$!.to_s} \n #{$!.backtrace.join("\n ")}" ) rescue nil
115
- session.write "HTTP/1.0 501 NOK\r\nContent-type: text/html\r\n\r\n<html><head><title>WS</title></head><body>Error : #{$!}" rescue nil
116
- ensure
117
- session.close rescue nil
118
- end
119
- def read_header(session,params)
120
- head=session.gets("\r\n\r\n")
121
- head.split(/\r\n/m).each { |line| name,data=line.split(": ",2) ; params["HEAD-"+name.upcase]=data }
122
- if params["HEAD-CONTENT-LENGTH"]
123
- len= params["HEAD-CONTENT-LENGTH"].split(/\s+/).last.to_i
124
- params["HEAD-CONTENT-LENGTH"]=len
125
- data=""
126
- while len>0
127
- d=session.read(len>64*1024 ? 64*1024 : len)
128
- raise("closed") if !d
129
- len -= d.length
130
- data+=d
131
- end
132
- params["HEAD-DATA"]=data
133
- end
134
- end
135
-
136
- def redirect(o,d)
137
- @redirect[o]=d
138
- end
139
- def do_service(session,request,service,user_passwd,params)
140
- redir=@redirect["/"+service]
141
- service=redir.gsub(/^\//,"") if @redirect[redir]
142
- aservice=to_absolute(service)
143
- if redir && ! @redirect[redir]
144
- do_service(session,request,redir.gsub(/^\//,""),user_passwd,params)
145
- elsif @cb["/"+service]
146
- begin
147
- code,type,data= @cb["/"+service].call(params)
148
- if code==0 && data != '/'+service
149
- do_service(session,request,data[1..-1],user_passwd,params)
150
- else
151
- code==200 ? sendData(session,type,data) : sendError(session,code,data)
152
- end
153
- rescue
154
- logg session.peeraddr.last,"Error in get /#{service} : #{$!}"
155
- sendError(session,501,$!.to_s)
156
- end
157
- elsif service =~ /^stop/
158
- sendData(session,".html","Stopping...");
159
- Thread.new() { sleep(0.1); stop_browser() }
160
- elsif File.directory?(aservice)
161
- sendData(session,".html",makeIndex(aservice))
162
- elsif File.exists?(aservice)
163
- sendFile(session,aservice)
164
- else
165
- info("unknown request serv=#{service} params=#{params.inspect} #{File.exists?(service)}")
166
- sendError(session,500,"unknown request serv=#{aservice} params=#{params.inspect} #{File.exists?(service)}");
167
- end
168
- end
169
- def stop_browser
170
- info "exit on web demand !"
171
- [@tho,@thm].each { |th| th.kill }
172
- @server.close rescue nil
173
- end
174
- def makeIndex(adir)
175
- dir=to_relative(adir)
176
- dirs,files=Dir.glob(adir==@rootd ? "#{@rootd}*" : "#{adir}/*").sort.partition { |f| File.directory?(f)}
177
-
178
- updir = hescape( dir.split(/\//)[0..-2].join("/"))
179
- updir="/" if updir.length==0
180
- up=(dir!="/") ? "<input type='button' onclick='location.href=\"#{updir}\"' value='Parent'>" : ""
181
- "<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>"
182
- end
183
- def to_relative(f) f.gsub(/^#{@rootd}/,"/") end
184
- def to_absolute(f) "#{@rootd}#{f.gsub(/^\//,'')}" end
185
- def n3(n)
186
- u=" B"
187
- if n> 10000000
188
- n=n/(1024*1024)
189
- u=" MB"
190
- elsif n> 100000
191
- n=n/1024
192
- u=" KB"
193
- end
194
- "<div style='width:100px;text-align:right;'>#{(n.round.to_i.to_s.reverse.gsub(/(\d\d\d)(?=\d)/,'\1 ' ).reverse) +u} | </div>"
195
- end
196
- def to_table(l)
197
- "<table><tr>#{l.map {|s| "<td>#{s}</td>"}.join("</tr><tr>")}</tr></table>"
198
- end
199
- def to_tableb(l,&bl)
200
- "<table><tr>#{l.map {|s| "<td>#{bl.call(s).join("</td><td>")}</td>"}.join("</tr><tr>")}</tr></table>"
201
- end
202
- def sendError(sock,no,txt=nil)
203
- if txt
204
- txt="<html><body><code><pre></pre>#{txt}</code></body></html>"
205
- end
206
- sock.write "HTTP/1.0 #{no} NOK\r\nContent-type: #{mime(".html")}\r\n\r\n <html><p>Error #{no} : #{txt}</p></html>"
207
- end
208
- def sendData(sock,type,content)
209
- sock.write "HTTP/1.0 200 OK\r\nContent-Type: #{mime(type)}\r\nContent-Length: #{content.size}\r\n\r\n"
210
- sock.write(content)
211
- end
212
- def sendFile(sock,filename)
213
- s=File.size(filename)
214
- if s < 0 || s>60_000_000 || File.extname(filename).downcase==".lnk"
215
- logg @name,"Error reading file/File not downloadable #{File.basename(filename)} : (size=#{s})"
216
- sendError(sock,500,"Error reading file/File not downloadable #{filename} : (size=#{s})" )
217
- return
218
- end
219
- logg @name,filename," #{s/(1024*1024)} Mo" if s>10*1000_000
220
- timeout([s/(512*1024),30.0].max.to_i) {
221
- sock.write "HTTP/1.0 200 OK\r\nContent-Type: #{mime(filename)}\r\nContent-Length: #{File.size(filename)}\r\nLast-Modified: #{httpdate(File.mtime(filename))}\r\nDate: #{httpdate(Time.now)}\r\n\r\n"
222
- File.open(filename,"rb") do |f|
223
- f.binmode; sock.binmode;
224
- ( sock.write(f.read(32*1024)) while (! f.eof? && ! sock.closed?) ) rescue nil
225
- end
226
- }
227
- end
228
- def httpdate( aTime ); (aTime||Time.now).gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" ); end
229
- def mime(string)
230
- MIME[string.split(/\./).last] || "application/octet-stream"
231
- end
232
- LICON="&#9728;&#9731;&#9742;&#9745;&#9745;&#9760;&#9763;&#9774;&#9786;&#9730;".split(/;/).map {|c| c+";"}
233
- MIME={"png" => "image/png", "gif" => "image/gif", "html" => "text/html","htm" => "text/html",
234
- "js" => "text/javascript" ,"css" => "text/css","jpeg" => "image/jpeg" ,"jpg" => "image/jpeg",
235
- ".json" => "applicatipon/json",
236
- "pdf"=> "application/pdf" , "svg" => "image/svg+xml","svgz" => "image/svg+xml",
237
- "xml" => "text/xml" ,"xsl" => "text/xml" ,"bmp" => "image/bmp" ,"txt" => "text/plain" ,
238
- "rb" => "text/plain" ,"pas" => "text/plain" ,"tcl" => "text/plain" ,"java" => "text/plain" ,
239
- "c" => "text/plain" ,"h" => "text/plain" ,"cpp" => "text/plain", "xul" => "application/vnd.mozilla.xul+xml",
240
- "doc" => "application/msword", "docx" => "application/msword","dot"=> "application/msword",
241
- "xls" => "application/vnd.ms-excel","xla" => "application/vnd.ms-excel","xlt" => "application/vnd.ms-excel","xlsx" => "application/vnd.ms-excel",
242
- "ppt" => "application/vnd.ms-powerpoint", "pptx" => "application/vnd.ms-powerpoint"
243
- }
244
- end # 220 loc webserver :)
245
-
246
- class Webserver < WebserverAbstract
247
- def initialize(port=7080,cadence=10,timeout=120)
248
- super(port,Dir.getwd(),"",cadence,timeout)
249
- end
250
- end
251
- class WebserverRoot < WebserverAbstract
252
- def initialize(port=7080,root=".",name="wwww",cadence=10,timeout=120,options={})
253
- super(port,root,name,cadence,timeout,options)
254
- end
255
- end
256
- def cliweb(root=Dir.getwd,port=59999)
257
- Thread.abort_on_exception = false
258
- BasicSocket.do_not_reverse_lookup = true
259
- $ws=WebserverRoot.new(port,root,'femto ws',10,300, {});
260
- puts "Server root path #{root} with port #{port}"
261
- sleep
262
- end
1
+ # encoding: utf-8
2
+ # FemtoWebServer : 232 LOC web server
3
+ #
4
+ # $ws=WebserverRoot.new(port,"/home/www","femto ws",10,300)
5
+ # ws.serve "/FOO" do |params|
6
+ # data=params["HEAD-DATA"]|| ""
7
+ # puts "Recu data len=#{data.length} : <#{data[0..1000]}>" if data
8
+ # [200,".json",""]
9
+ # end
10
+
11
+
12
+ require 'thread'
13
+ require 'socket'
14
+ require 'timeout'
15
+
16
+ #################### Tiny embeded webserver
17
+
18
+
19
+
20
+
21
+ class WebserverAbstract
22
+ def logg(*args)
23
+ if @cb_log then @cb_log.call(@name,*args) else puts(args.join(" ")) end
24
+ rescue
25
+ puts(args.join(" "))
26
+ end
27
+ def info(txt) ; logg("nw>i>",txt) ; end
28
+ def error(txt) ; logg("nw>e>",txt) ; end
29
+ def unescape(string) ; string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2}))/n) { [$1.delete('%')].pack('H*') } end
30
+ def escape(string) ; string.gsub(/([^ \/a-zA-Z0-9_.\-]+)/) { '%' + $1.unpack('H2' * $1.size).join('%').upcase }.tr(' ', '+') end
31
+ def hescape(string) ; escape(string.gsub("/./","/").gsub("//","/")) ; end
32
+ def observe(sleeping,delta)
33
+ @tho=Thread.new do loop do
34
+ sleep(sleeping)
35
+ nowDelta=Time.now-delta
36
+ l=@th.select { |th,tm| (tm[0]<nowDelta) }
37
+ l.each { |th,tm| info("killing thread") ; th.kill; @th.delete(th) ; tm[1].close rescue nil }
38
+ end ; end
39
+ end
40
+ def initialize(port,root,name,cadence,timeout,options)
41
+ raise("tcp port illegal #{port}") unless port.to_i>=80
42
+ raise("root not exist #{root}") unless File.exists?(root)
43
+ @cb_log= options["logg"]
44
+ @last_mtime=File.mtime(__FILE__)
45
+ @port=port.to_i
46
+ @root=root
47
+ @name=name
48
+ @rootd=root[-1,1]=="/" ? root : root+"/"
49
+ @timeout=timeout
50
+ @th={}
51
+ @cb={}
52
+ @redirect={}
53
+ info(" serveur http #{port} on #{@rootd} ready!")
54
+ observe(cadence,timeout*2)
55
+ pool_create
56
+ @thm=Thread.new {
57
+ loop {
58
+ nbError=0
59
+ begin
60
+ session=nil
61
+ @server = TCPServer.new('0.0.0.0', @port)
62
+ @server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
63
+ while (session = @server.accept)
64
+ nbError=0
65
+ run(session)
66
+ end
67
+ rescue Exception => e
68
+ nbError+=1
69
+ error($!.to_s + " " + $!.backtrace[0..2].join(" "))
70
+ session.close rescue nil
71
+ @server.close rescue nil
72
+ end
73
+ sleep(3); info("restart accept")
74
+ }
75
+ }
76
+ end
77
+ def pool_create
78
+ @queue=Queue.new
79
+ ici=self
80
+ 100.times { Thread.new { loop {
81
+ param,bloc=@queue.pop
82
+ bloc.call(param) rescue p $!
83
+ } } }
84
+ end
85
+ def pool_get(param,&block)
86
+ @queue.push([param,block])
87
+ end
88
+ def run(session)
89
+ pool_get(session) do |sess|
90
+ @th[Thread.current]=[Time.now,sess]
91
+ request(sess)
92
+ @th.delete(Thread.current)
93
+ end
94
+ end
95
+ def serve(uri,&blk)
96
+ @cb[uri] = blk
97
+ end
98
+ def request(session)
99
+ request = session.gets
100
+ return unless request
101
+ uri = (request.split(/\s+/)+['','',''])[1]
102
+ #info uri
103
+ service,param,*bidon=(uri+"?").split(/\?/)
104
+ params=Hash[*(param.split(/#/)[0].split(/[=&]/))] rescue {}
105
+ params.each { |k,v| params[k]=unescape(v) }
106
+ uri=unescape(service)[1..-1].gsub(/\.\./,"")
107
+ userpass=nil
108
+ if (buri=uri.split(/@/)).size>1
109
+ uri=buri[1..-1].join("@")
110
+ userpass=buri[0].split(/:/)
111
+ end
112
+ read_header(session,params)
113
+ do_service(session,request,uri,userpass,params)
114
+ rescue Exception => e
115
+ error("Error Web get on #{request}: \n #{$!.to_s} \n #{$!.backtrace.join("\n ")}" ) rescue nil
116
+ session.write "HTTP/1.0 501 NOK\r\nContent-type: text/html\r\n\r\n<html><head><title>WS</title></head><body>Error : #{$!}" rescue nil
117
+ ensure
118
+ session.close rescue nil
119
+ end
120
+ def read_header(session,params)
121
+ head=session.gets("\r\n\r\n")
122
+ head.split(/\r\n/m).each { |line| name,data=line.split(": ",2) ; params["HEAD-"+name.upcase]=data }
123
+ if params["HEAD-CONTENT-LENGTH"]
124
+ len= params["HEAD-CONTENT-LENGTH"].split(/\s+/).last.to_i
125
+ params["HEAD-CONTENT-LENGTH"]=len
126
+ data=""
127
+ while len>0
128
+ d=session.read(len>64*1024 ? 64*1024 : len)
129
+ raise("closed") if !d
130
+ len -= d.length
131
+ data+=d
132
+ end
133
+ params["HEAD-DATA"]=data
134
+ end
135
+ end
136
+
137
+ def redirect(o,d)
138
+ @redirect[o]=d
139
+ end
140
+ def do_service(session,request,service,user_passwd,params)
141
+ redir=@redirect["/"+service]
142
+ service=redir.gsub(/^\//,"") if @redirect[redir]
143
+ aservice=to_absolute(service)
144
+ if redir && ! @redirect[redir]
145
+ do_service(session,request,redir.gsub(/^\//,""),user_passwd,params)
146
+ elsif @cb["/"+service]
147
+ begin
148
+ code,type,data= @cb["/"+service].call(params)
149
+ if code==0 && data != '/'+service
150
+ do_service(session,request,data[1..-1],user_passwd,params)
151
+ else
152
+ code==200 ? sendData(session,type,data) : sendError(session,code,data)
153
+ end
154
+ rescue
155
+ logg session.peeraddr.last,"Error in get /#{service} : #{$!}"
156
+ sendError(session,501,$!.to_s)
157
+ end
158
+ elsif service =~ /^stop/
159
+ sendData(session,".html","Stopping...");
160
+ Thread.new() { sleep(0.1); stop_browser() }
161
+ elsif File.directory?(aservice)
162
+ sendData(session,".html",makeIndex(aservice))
163
+ elsif File.exists?(aservice)
164
+ sendFile(session,aservice)
165
+ else
166
+ info("unknown request serv=#{service} params=#{params.inspect} #{File.exists?(service)}")
167
+ sendError(session,500,"unknown request serv=#{aservice} params=#{params.inspect} #{File.exists?(service)}");
168
+ end
169
+ end
170
+ def stop_browser
171
+ info "exit on web demand !"
172
+ [@tho,@thm].each { |th| th.kill }
173
+ @server.close rescue nil
174
+ end
175
+ def makeIndex(adir)
176
+ dir=to_relative(adir)
177
+ dirs,files=Dir.glob(adir==@rootd ? "#{@rootd}*" : "#{adir}/*").sort.partition { |f| File.directory?(f)}
178
+
179
+ updir = hescape( dir.split(/\//)[0..-2].join("/"))
180
+ updir="/" if updir.length==0
181
+ up=(dir!="/") ? "<input type='button' onclick='location.href=\"#{updir}\"' value='Parent'>" : ""
182
+ "<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>"
183
+ end
184
+ def to_relative(f) f.gsub(/^#{@rootd}/,"/") end
185
+ def to_absolute(f) "#{@rootd}#{f.gsub(/^\//,'')}" end
186
+ def n3(n)
187
+ u=" B"
188
+ if n> 10000000
189
+ n=n/(1024*1024)
190
+ u=" MB"
191
+ elsif n> 100000
192
+ n=n/1024
193
+ u=" KB"
194
+ end
195
+ "<div style='width:100px;text-align:right;'>#{(n.round.to_i.to_s.reverse.gsub(/(\d\d\d)(?=\d)/,'\1 ' ).reverse) +u} | </div>"
196
+ end
197
+ def to_table(l)
198
+ "<table><tr>#{l.map {|s| "<td>#{s}</td>"}.join("</tr><tr>")}</tr></table>"
199
+ end
200
+ def to_tableb(l,&bl)
201
+ "<table><tr>#{l.map {|s| "<td>#{bl.call(s).join("</td><td>")}</td>"}.join("</tr><tr>")}</tr></table>"
202
+ end
203
+ def sendError(sock,no,txt=nil)
204
+ if txt
205
+ txt="<html><body><code><pre></pre>#{txt}</code></body></html>"
206
+ end
207
+ sock.write "HTTP/1.0 #{no} NOK\r\nContent-type: #{mime(".html")}\r\n\r\n <html><p>Error #{no} : #{txt}</p></html>"
208
+ end
209
+ def sendData(sock,type,content)
210
+ sock.write "HTTP/1.0 200 OK\r\nContent-Type: #{mime(type)}\r\nContent-Length: #{content.size}\r\n\r\n"
211
+ sock.write(content)
212
+ end
213
+ def sendFile(sock,filename)
214
+ s=File.size(filename)
215
+ if s < 0 || s>60_000_000 || File.extname(filename).downcase==".lnk"
216
+ logg @name,"Error reading file/File not downloadable #{File.basename(filename)} : (size=#{s})"
217
+ sendError(sock,500,"Error reading file/File not downloadable #{filename} : (size=#{s})" )
218
+ return
219
+ end
220
+ logg @name,filename," #{s/(1024*1024)} Mo" if s>10*1000_000
221
+ timeout([s/(512*1024),30.0].max.to_i) {
222
+ sock.write "HTTP/1.0 200 OK\r\nContent-Type: #{mime(filename)}\r\nContent-Length: #{File.size(filename)}\r\nLast-Modified: #{httpdate(File.mtime(filename))}\r\nDate: #{httpdate(Time.now)}\r\n\r\n"
223
+ File.open(filename,"rb") do |f|
224
+ f.binmode; sock.binmode;
225
+ ( sock.write(f.read(32*1024)) while (! f.eof? && ! sock.closed?) ) rescue nil
226
+ end
227
+ }
228
+ end
229
+ def httpdate( aTime ); (aTime||Time.now).gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" ); end
230
+ def mime(string)
231
+ MIME[string.split(/\./).last] || "application/octet-stream"
232
+ end
233
+ LICON="&#9728;&#9731;&#9742;&#9745;&#9745;&#9760;&#9763;&#9774;&#9786;&#9730;".split(/;/).map {|c| c+";"}
234
+ MIME={"png" => "image/png", "gif" => "image/gif", "html" => "text/html","htm" => "text/html",
235
+ "js" => "text/javascript" ,"css" => "text/css","jpeg" => "image/jpeg" ,"jpg" => "image/jpeg",
236
+ ".json" => "applicatipon/json",
237
+ "pdf"=> "application/pdf" , "svg" => "image/svg+xml","svgz" => "image/svg+xml",
238
+ "xml" => "text/xml" ,"xsl" => "text/xml" ,"bmp" => "image/bmp" ,"txt" => "text/plain" ,
239
+ "rb" => "text/plain" ,"pas" => "text/plain" ,"tcl" => "text/plain" ,"java" => "text/plain" ,
240
+ "c" => "text/plain" ,"h" => "text/plain" ,"cpp" => "text/plain", "xul" => "application/vnd.mozilla.xul+xml",
241
+ "doc" => "application/msword", "docx" => "application/msword","dot"=> "application/msword",
242
+ "xls" => "application/vnd.ms-excel","xla" => "application/vnd.ms-excel","xlt" => "application/vnd.ms-excel","xlsx" => "application/vnd.ms-excel",
243
+ "ppt" => "application/vnd.ms-powerpoint", "pptx" => "application/vnd.ms-powerpoint"
244
+ }
245
+ end # 220 loc webserver :)
246
+
247
+ class Webserver < WebserverAbstract
248
+ def initialize(port=7080,cadence=10,timeout=120)
249
+ super(port,Dir.getwd(),"",cadence,timeout)
250
+ end
251
+ end
252
+ class WebserverRoot < WebserverAbstract
253
+ def initialize(port=7080,root=".",name="wwww",cadence=10,timeout=120,options={})
254
+ super(port,root,name,cadence,timeout,options)
255
+ end
256
+ end
257
+ def cliweb(root=Dir.getwd,port=59999)
258
+ Thread.abort_on_exception = false
259
+ BasicSocket.do_not_reverse_lookup = true
260
+ $ws=WebserverRoot.new(port,root,'femto ws',10,300, {});
261
+ puts "Server root path #{root} with port #{port}"
262
+ sleep
263
+ end
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: femtows
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
5
- prerelease:
4
+ version: 1.6.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Glurp man
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-07-17 00:00:00.000000000 Z
11
+ date: 2015-06-18 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: ! "a tiny web server, for local file transfert, \nembedded, http experimentations\n"
13
+ description: "a tiny web server, for local file transfert, \nembedded, http experimentations\n"
15
14
  email: regis.aubarede@gmail.com
16
15
  executables:
17
16
  - femtows.bat
@@ -19,33 +18,33 @@ executables:
19
18
  extensions: []
20
19
  extra_rdoc_files: []
21
20
  files:
21
+ - bin/femtows.bat
22
+ - bin/femtows.sh
22
23
  - demo.rb
23
- - lib/femtows.rb
24
+ - gitc.bat
24
25
  - guidemo.rb
25
- - bin/femtows.sh
26
- - bin/femtows.bat
26
+ - lib/femtows.rb
27
27
  homepage: http://github.com/glurp/femtows
28
28
  licenses: []
29
- post_install_message: ! "-------------------------------------------------------------------------------\nHello,
29
+ metadata: {}
30
+ post_install_message: "-------------------------------------------------------------------------------\nHello,
30
31
  welcome to Femto Web Server....\n\n$ femtows [port,[root-directory] \n\n-------------------------------------------------------------------------------\n"
31
32
  rdoc_options: []
32
33
  require_paths:
33
34
  - lib
34
35
  required_ruby_version: !ruby/object:Gem::Requirement
35
- none: false
36
36
  requirements:
37
- - - ! '>='
37
+ - - '>='
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  required_rubygems_version: !ruby/object:Gem::Requirement
41
- none: false
42
41
  requirements:
43
- - - ! '>='
42
+ - - '>='
44
43
  - !ruby/object:Gem::Version
45
44
  version: '0'
46
45
  requirements: []
47
46
  rubyforge_project:
48
- rubygems_version: 1.8.23
47
+ rubygems_version: 2.4.6
49
48
  signing_key:
50
49
  specification_version: 3
51
50
  summary: a tiny webserver