femtows 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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