rubysl-webrick 1.0.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/rubysl/webrick.rb +2 -0
  9. data/lib/rubysl/webrick/version.rb +5 -0
  10. data/lib/rubysl/webrick/webrick.rb +29 -0
  11. data/lib/webrick.rb +1 -0
  12. data/lib/webrick/accesslog.rb +67 -0
  13. data/lib/webrick/cgi.rb +257 -0
  14. data/lib/webrick/compat.rb +15 -0
  15. data/lib/webrick/config.rb +97 -0
  16. data/lib/webrick/cookie.rb +110 -0
  17. data/lib/webrick/htmlutils.rb +25 -0
  18. data/lib/webrick/httpauth.rb +45 -0
  19. data/lib/webrick/httpauth/authenticator.rb +79 -0
  20. data/lib/webrick/httpauth/basicauth.rb +65 -0
  21. data/lib/webrick/httpauth/digestauth.rb +343 -0
  22. data/lib/webrick/httpauth/htdigest.rb +91 -0
  23. data/lib/webrick/httpauth/htgroup.rb +61 -0
  24. data/lib/webrick/httpauth/htpasswd.rb +83 -0
  25. data/lib/webrick/httpauth/userdb.rb +29 -0
  26. data/lib/webrick/httpproxy.rb +254 -0
  27. data/lib/webrick/httprequest.rb +365 -0
  28. data/lib/webrick/httpresponse.rb +327 -0
  29. data/lib/webrick/https.rb +63 -0
  30. data/lib/webrick/httpserver.rb +210 -0
  31. data/lib/webrick/httpservlet.rb +22 -0
  32. data/lib/webrick/httpservlet/abstract.rb +71 -0
  33. data/lib/webrick/httpservlet/cgi_runner.rb +45 -0
  34. data/lib/webrick/httpservlet/cgihandler.rb +104 -0
  35. data/lib/webrick/httpservlet/erbhandler.rb +54 -0
  36. data/lib/webrick/httpservlet/filehandler.rb +398 -0
  37. data/lib/webrick/httpservlet/prochandler.rb +33 -0
  38. data/lib/webrick/httpstatus.rb +126 -0
  39. data/lib/webrick/httputils.rb +391 -0
  40. data/lib/webrick/httpversion.rb +49 -0
  41. data/lib/webrick/log.rb +88 -0
  42. data/lib/webrick/server.rb +200 -0
  43. data/lib/webrick/ssl.rb +126 -0
  44. data/lib/webrick/utils.rb +100 -0
  45. data/lib/webrick/version.rb +13 -0
  46. data/rubysl-webrick.gemspec +23 -0
  47. metadata +145 -0
@@ -0,0 +1,91 @@
1
+ #
2
+ # httpauth/htdigest.rb -- Apache compatible htdigest file
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
9
+
10
+ require 'webrick/httpauth/userdb'
11
+ require 'webrick/httpauth/digestauth'
12
+ require 'tempfile'
13
+
14
+ module WEBrick
15
+ module HTTPAuth
16
+ class Htdigest
17
+ include UserDB
18
+
19
+ def initialize(path)
20
+ @path = path
21
+ @mtime = Time.at(0)
22
+ @digest = Hash.new
23
+ @mutex = Mutex::new
24
+ @auth_type = DigestAuth
25
+ open(@path,"a").close unless File::exist?(@path)
26
+ reload
27
+ end
28
+
29
+ def reload
30
+ mtime = File::mtime(@path)
31
+ if mtime > @mtime
32
+ @digest.clear
33
+ open(@path){|io|
34
+ while line = io.gets
35
+ line.chomp!
36
+ user, realm, pass = line.split(/:/, 3)
37
+ unless @digest[realm]
38
+ @digest[realm] = Hash.new
39
+ end
40
+ @digest[realm][user] = pass
41
+ end
42
+ }
43
+ @mtime = mtime
44
+ end
45
+ end
46
+
47
+ def flush(output=nil)
48
+ output ||= @path
49
+ tmp = Tempfile.new("htpasswd", File::dirname(output))
50
+ begin
51
+ each{|item| tmp.puts(item.join(":")) }
52
+ tmp.close
53
+ File::rename(tmp.path, output)
54
+ rescue
55
+ tmp.close(true)
56
+ end
57
+ end
58
+
59
+ def get_passwd(realm, user, reload_db)
60
+ reload() if reload_db
61
+ if hash = @digest[realm]
62
+ hash[user]
63
+ end
64
+ end
65
+
66
+ def set_passwd(realm, user, pass)
67
+ @mutex.synchronize{
68
+ unless @digest[realm]
69
+ @digest[realm] = Hash.new
70
+ end
71
+ @digest[realm][user] = make_passwd(realm, user, pass)
72
+ }
73
+ end
74
+
75
+ def delete_passwd(realm, user)
76
+ if hash = @digest[realm]
77
+ hash.delete(user)
78
+ end
79
+ end
80
+
81
+ def each
82
+ @digest.keys.sort.each{|realm|
83
+ hash = @digest[realm]
84
+ hash.keys.sort.each{|user|
85
+ yield([user, realm, hash[user]])
86
+ }
87
+ }
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,61 @@
1
+ #
2
+ # httpauth/htgroup.rb -- Apache compatible htgroup file
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: htgroup.rb,v 1.1 2003/02/16 22:22:56 gotoyuzo Exp $
9
+
10
+ require 'tempfile'
11
+
12
+ module WEBrick
13
+ module HTTPAuth
14
+ class Htgroup
15
+ def initialize(path)
16
+ @path = path
17
+ @mtime = Time.at(0)
18
+ @group = Hash.new
19
+ open(@path,"a").close unless File::exist?(@path)
20
+ reload
21
+ end
22
+
23
+ def reload
24
+ if (mtime = File::mtime(@path)) > @mtime
25
+ @group.clear
26
+ open(@path){|io|
27
+ while line = io.gets
28
+ line.chomp!
29
+ group, members = line.split(/:\s*/)
30
+ @group[group] = members.split(/\s+/)
31
+ end
32
+ }
33
+ @mtime = mtime
34
+ end
35
+ end
36
+
37
+ def flush(output=nil)
38
+ output ||= @path
39
+ tmp = Tempfile.new("htgroup", File::dirname(output))
40
+ begin
41
+ @group.keys.sort.each{|group|
42
+ tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
43
+ }
44
+ tmp.close
45
+ File::rename(tmp.path, output)
46
+ rescue
47
+ tmp.close(true)
48
+ end
49
+ end
50
+
51
+ def members(group)
52
+ reload
53
+ @group[group] || []
54
+ end
55
+
56
+ def add(group, members)
57
+ @group[group] = members(group) | members
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,83 @@
1
+ #
2
+ # httpauth/htpasswd -- Apache compatible htpasswd file
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
9
+
10
+ require 'webrick/httpauth/userdb'
11
+ require 'webrick/httpauth/basicauth'
12
+ require 'tempfile'
13
+
14
+ module WEBrick
15
+ module HTTPAuth
16
+ class Htpasswd
17
+ include UserDB
18
+
19
+ def initialize(path)
20
+ @path = path
21
+ @mtime = Time.at(0)
22
+ @passwd = Hash.new
23
+ @auth_type = BasicAuth
24
+ open(@path,"a").close unless File::exist?(@path)
25
+ reload
26
+ end
27
+
28
+ def reload
29
+ mtime = File::mtime(@path)
30
+ if mtime > @mtime
31
+ @passwd.clear
32
+ open(@path){|io|
33
+ while line = io.gets
34
+ line.chomp!
35
+ case line
36
+ when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
37
+ user, pass = line.split(":")
38
+ when /:\$/, /:\{SHA\}/
39
+ raise NotImplementedError,
40
+ 'MD5, SHA1 .htpasswd file not supported'
41
+ else
42
+ raise StandardError, 'bad .htpasswd file'
43
+ end
44
+ @passwd[user] = pass
45
+ end
46
+ }
47
+ @mtime = mtime
48
+ end
49
+ end
50
+
51
+ def flush(output=nil)
52
+ output ||= @path
53
+ tmp = Tempfile.new("htpasswd", File::dirname(output))
54
+ begin
55
+ each{|item| tmp.puts(item.join(":")) }
56
+ tmp.close
57
+ File::rename(tmp.path, output)
58
+ rescue
59
+ tmp.close(true)
60
+ end
61
+ end
62
+
63
+ def get_passwd(realm, user, reload_db)
64
+ reload() if reload_db
65
+ @passwd[user]
66
+ end
67
+
68
+ def set_passwd(realm, user, pass)
69
+ @passwd[user] = make_passwd(realm, user, pass)
70
+ end
71
+
72
+ def delete_passwd(realm, user)
73
+ @passwd.delete(user)
74
+ end
75
+
76
+ def each
77
+ @passwd.keys.sort.each{|user|
78
+ yield([user, @passwd[user]])
79
+ }
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ #
2
+ # httpauth/userdb.rb -- UserDB mix-in module.
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: userdb.rb,v 1.2 2003/02/20 07:15:48 gotoyuzo Exp $
9
+
10
+ module WEBrick
11
+ module HTTPAuth
12
+ module UserDB
13
+ attr_accessor :auth_type # BasicAuth or DigestAuth
14
+
15
+ def make_passwd(realm, user, pass)
16
+ @auth_type::make_passwd(realm, user, pass)
17
+ end
18
+
19
+ def set_passwd(realm, user, pass)
20
+ self[user] = pass
21
+ end
22
+
23
+ def get_passwd(realm, user, reload_db=false)
24
+ # reload_db is dummy
25
+ make_passwd(realm, user, self[user])
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,254 @@
1
+ #
2
+ # httpproxy.rb -- HTTPProxy Class
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2002 GOTO Kentaro
6
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
10
+ # $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
11
+
12
+ require "webrick/httpserver"
13
+ require "net/http"
14
+
15
+ Net::HTTP::version_1_2 if RUBY_VERSION < "1.7"
16
+
17
+ module WEBrick
18
+ NullReader = Object.new
19
+ class << NullReader
20
+ def read(*args)
21
+ nil
22
+ end
23
+ alias gets read
24
+ end
25
+
26
+ class HTTPProxyServer < HTTPServer
27
+ def initialize(config)
28
+ super
29
+ c = @config
30
+ @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
31
+ end
32
+
33
+ def service(req, res)
34
+ if req.request_method == "CONNECT"
35
+ proxy_connect(req, res)
36
+ elsif req.unparsed_uri =~ %r!^http://!
37
+ proxy_service(req, res)
38
+ else
39
+ super(req, res)
40
+ end
41
+ end
42
+
43
+ def proxy_auth(req, res)
44
+ if proc = @config[:ProxyAuthProc]
45
+ proc.call(req, res)
46
+ end
47
+ req.header.delete("proxy-authorization")
48
+ end
49
+
50
+ # Some header fields should not be transferred.
51
+ HopByHop = %w( connection keep-alive proxy-authenticate upgrade
52
+ proxy-authorization te trailers transfer-encoding )
53
+ ShouldNotTransfer = %w( set-cookie proxy-connection )
54
+ def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
55
+
56
+ def choose_header(src, dst)
57
+ connections = split_field(src['connection'])
58
+ src.each{|key, value|
59
+ key = key.downcase
60
+ if HopByHop.member?(key) || # RFC2616: 13.5.1
61
+ connections.member?(key) || # RFC2616: 14.10
62
+ ShouldNotTransfer.member?(key) # pragmatics
63
+ @logger.debug("choose_header: `#{key}: #{value}'")
64
+ next
65
+ end
66
+ dst[key] = value
67
+ }
68
+ end
69
+
70
+ # Net::HTTP is stupid about the multiple header fields.
71
+ # Here is workaround:
72
+ def set_cookie(src, dst)
73
+ if str = src['set-cookie']
74
+ cookies = []
75
+ str.split(/,\s*/).each{|token|
76
+ if /^[^=]+;/o =~ token
77
+ cookies[-1] << ", " << token
78
+ elsif /=/o =~ token
79
+ cookies << token
80
+ else
81
+ cookies[-1] << ", " << token
82
+ end
83
+ }
84
+ dst.cookies.replace(cookies)
85
+ end
86
+ end
87
+
88
+ def set_via(h)
89
+ if @config[:ProxyVia]
90
+ if h['via']
91
+ h['via'] << ", " << @via
92
+ else
93
+ h['via'] = @via
94
+ end
95
+ end
96
+ end
97
+
98
+ def proxy_uri(req, res)
99
+ @config[:ProxyURI]
100
+ end
101
+
102
+ def proxy_service(req, res)
103
+ # Proxy Authentication
104
+ proxy_auth(req, res)
105
+
106
+ # Create Request-URI to send to the origin server
107
+ uri = req.request_uri
108
+ path = uri.path.dup
109
+ path << "?" << uri.query if uri.query
110
+
111
+ # Choose header fields to transfer
112
+ header = Hash.new
113
+ choose_header(req, header)
114
+ set_via(header)
115
+
116
+ # select upstream proxy server
117
+ if proxy = proxy_uri(req, res)
118
+ proxy_host = proxy.host
119
+ proxy_port = proxy.port
120
+ if proxy.userinfo
121
+ credentials = "Basic " + [proxy.userinfo].pack("m*")
122
+ credentials.chomp!
123
+ header['proxy-authorization'] = credentials
124
+ end
125
+ end
126
+
127
+ response = nil
128
+ begin
129
+ http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
130
+ http.start{
131
+ if @config[:ProxyTimeout]
132
+ ################################## these issues are
133
+ http.open_timeout = 30 # secs # necessary (maybe bacause
134
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
135
+ ##################################
136
+ end
137
+ case req.request_method
138
+ when "GET" then response = http.get(path, header)
139
+ when "POST" then response = http.post(path, req.body || "", header)
140
+ when "HEAD" then response = http.head(path, header)
141
+ else
142
+ raise HTTPStatus::MethodNotAllowed,
143
+ "unsupported method `#{req.request_method}'."
144
+ end
145
+ }
146
+ rescue => err
147
+ logger.debug("#{err.class}: #{err.message}")
148
+ raise HTTPStatus::ServiceUnavailable, err.message
149
+ end
150
+
151
+ # Persistent connction requirements are mysterious for me.
152
+ # So I will close the connection in every response.
153
+ res['proxy-connection'] = "close"
154
+ res['connection'] = "close"
155
+
156
+ # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPProxy
157
+ res.status = response.code.to_i
158
+ choose_header(response, res)
159
+ set_cookie(response, res)
160
+ set_via(res)
161
+ res.body = response.body
162
+
163
+ # Process contents
164
+ if handler = @config[:ProxyContentHandler]
165
+ handler.call(req, res)
166
+ end
167
+ end
168
+
169
+ def proxy_connect(req, res)
170
+ # Proxy Authentication
171
+ proxy_auth(req, res)
172
+
173
+ ua = Thread.current[:WEBrickSocket] # User-Agent
174
+ raise HTTPStatus::InternalServerError,
175
+ "[BUG] cannot get socket" unless ua
176
+
177
+ host, port = req.unparsed_uri.split(":", 2)
178
+ # Proxy authentication for upstream proxy server
179
+ if proxy = proxy_uri(req, res)
180
+ proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
181
+ if proxy.userinfo
182
+ credentials = "Basic " + [proxy.userinfo].pack("m*")
183
+ credentials.chomp!
184
+ end
185
+ host, port = proxy.host, proxy.port
186
+ end
187
+
188
+ begin
189
+ @logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.")
190
+ os = TCPSocket.new(host, port) # origin server
191
+
192
+ if proxy
193
+ @logger.debug("CONNECT: sending a Request-Line")
194
+ os << proxy_request_line << CRLF
195
+ @logger.debug("CONNECT: > #{proxy_request_line}")
196
+ if credentials
197
+ @logger.debug("CONNECT: sending a credentials")
198
+ os << "Proxy-Authorization: " << credentials << CRLF
199
+ end
200
+ os << CRLF
201
+ proxy_status_line = os.gets(LF)
202
+ @logger.debug("CONNECT: read a Status-Line form the upstream server")
203
+ @logger.debug("CONNECT: < #{proxy_status_line}")
204
+ if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line
205
+ while line = os.gets(LF)
206
+ break if /\A(#{CRLF}|#{LF})\z/om =~ line
207
+ end
208
+ else
209
+ raise HTTPStatus::BadGateway
210
+ end
211
+ end
212
+ @logger.debug("CONNECT #{host}:#{port}: succeeded")
213
+ res.status = HTTPStatus::RC_OK
214
+ rescue => ex
215
+ @logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'")
216
+ res.set_error(ex)
217
+ raise HTTPStatus::EOFError
218
+ ensure
219
+ if handler = @config[:ProxyContentHandler]
220
+ handler.call(req, res)
221
+ end
222
+ res.send_response(ua)
223
+ access_log(@config, req, res)
224
+
225
+ # Should clear request-line not to send the sesponse twice.
226
+ # see: HTTPServer#run
227
+ req.parse(NullReader) rescue nil
228
+ end
229
+
230
+ begin
231
+ while fds = IO::select([ua, os])
232
+ if fds[0].member?(ua)
233
+ buf = ua.sysread(1024);
234
+ @logger.debug("CONNECT: #{buf.size} byte from User-Agent")
235
+ os.syswrite(buf)
236
+ elsif fds[0].member?(os)
237
+ buf = os.sysread(1024);
238
+ @logger.debug("CONNECT: #{buf.size} byte from #{host}:#{port}")
239
+ ua.syswrite(buf)
240
+ end
241
+ end
242
+ rescue => ex
243
+ os.close
244
+ @logger.debug("CONNECT #{host}:#{port}: closed")
245
+ end
246
+
247
+ raise HTTPStatus::EOFError
248
+ end
249
+
250
+ def do_OPTIONS(req, res)
251
+ res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
252
+ end
253
+ end
254
+ end