webrick 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of webrick might be problematic. Click here for more details.
- data/README.txt +21 -0
- data/lib/webrick.rb +227 -0
- data/lib/webrick/accesslog.rb +151 -0
- data/lib/webrick/cgi.rb +260 -0
- data/lib/webrick/compat.rb +35 -0
- data/lib/webrick/config.rb +121 -0
- data/lib/webrick/cookie.rb +110 -0
- data/lib/webrick/htmlutils.rb +28 -0
- data/lib/webrick/httpauth.rb +95 -0
- data/lib/webrick/httpauth/authenticator.rb +112 -0
- data/lib/webrick/httpauth/basicauth.rb +108 -0
- data/lib/webrick/httpauth/digestauth.rb +392 -0
- data/lib/webrick/httpauth/htdigest.rb +128 -0
- data/lib/webrick/httpauth/htgroup.rb +93 -0
- data/lib/webrick/httpauth/htpasswd.rb +121 -0
- data/lib/webrick/httpauth/userdb.rb +52 -0
- data/lib/webrick/httpproxy.rb +305 -0
- data/lib/webrick/httprequest.rb +461 -0
- data/lib/webrick/httpresponse.rb +399 -0
- data/lib/webrick/https.rb +64 -0
- data/lib/webrick/httpserver.rb +264 -0
- data/lib/webrick/httpservlet.rb +22 -0
- data/lib/webrick/httpservlet/abstract.rb +153 -0
- data/lib/webrick/httpservlet/cgi_runner.rb +46 -0
- data/lib/webrick/httpservlet/cgihandler.rb +108 -0
- data/lib/webrick/httpservlet/erbhandler.rb +87 -0
- data/lib/webrick/httpservlet/filehandler.rb +470 -0
- data/lib/webrick/httpservlet/prochandler.rb +33 -0
- data/lib/webrick/httpstatus.rb +184 -0
- data/lib/webrick/httputils.rb +394 -0
- data/lib/webrick/httpversion.rb +49 -0
- data/lib/webrick/log.rb +136 -0
- data/lib/webrick/server.rb +218 -0
- data/lib/webrick/ssl.rb +127 -0
- data/lib/webrick/utils.rb +241 -0
- data/lib/webrick/version.rb +13 -0
- data/sample/webrick/demo-app.rb +66 -0
- data/sample/webrick/demo-multipart.cgi +12 -0
- data/sample/webrick/demo-servlet.rb +6 -0
- data/sample/webrick/demo-urlencoded.cgi +12 -0
- data/sample/webrick/hello.cgi +11 -0
- data/sample/webrick/hello.rb +8 -0
- data/sample/webrick/httpd.rb +23 -0
- data/sample/webrick/httpproxy.rb +25 -0
- data/sample/webrick/httpsd.rb +33 -0
- data/test/openssl/utils.rb +313 -0
- data/test/ruby/envutil.rb +208 -0
- data/test/webrick/test_cgi.rb +134 -0
- data/test/webrick/test_cookie.rb +131 -0
- data/test/webrick/test_filehandler.rb +285 -0
- data/test/webrick/test_httpauth.rb +167 -0
- data/test/webrick/test_httpproxy.rb +282 -0
- data/test/webrick/test_httprequest.rb +411 -0
- data/test/webrick/test_httpresponse.rb +49 -0
- data/test/webrick/test_httpserver.rb +305 -0
- data/test/webrick/test_httputils.rb +96 -0
- data/test/webrick/test_httpversion.rb +40 -0
- data/test/webrick/test_server.rb +67 -0
- data/test/webrick/test_utils.rb +64 -0
- data/test/webrick/utils.rb +58 -0
- data/test/webrick/webrick.cgi +36 -0
- data/test/webrick/webrick_long_filename.cgi +36 -0
- metadata +106 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
#
|
2
|
+
# prochandler.rb -- ProcHandler Class
|
3
|
+
#
|
4
|
+
# Author: IPR -- Internet Programming with Ruby -- writers
|
5
|
+
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
|
6
|
+
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
|
7
|
+
# reserved.
|
8
|
+
#
|
9
|
+
# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
|
10
|
+
|
11
|
+
require 'webrick/httpservlet/abstract.rb'
|
12
|
+
|
13
|
+
module WEBrick
|
14
|
+
module HTTPServlet
|
15
|
+
|
16
|
+
class ProcHandler < AbstractServlet
|
17
|
+
def get_instance(server, *options)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(proc)
|
22
|
+
@proc = proc
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_GET(request, response)
|
26
|
+
@proc.call(request, response)
|
27
|
+
end
|
28
|
+
|
29
|
+
alias do_POST do_GET
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
#--
|
2
|
+
# httpstatus.rb -- HTTPStatus Class
|
3
|
+
#
|
4
|
+
# Author: IPR -- Internet Programming with Ruby -- writers
|
5
|
+
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
|
6
|
+
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
|
7
|
+
# reserved.
|
8
|
+
#
|
9
|
+
# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $
|
10
|
+
|
11
|
+
module WEBrick
|
12
|
+
|
13
|
+
##
|
14
|
+
# This module is used to manager HTTP status codes.
|
15
|
+
#
|
16
|
+
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for more
|
17
|
+
# information.
|
18
|
+
module HTTPStatus
|
19
|
+
|
20
|
+
##
|
21
|
+
# Root of the HTTP status class hierarchy
|
22
|
+
class Status < StandardError
|
23
|
+
def initialize(*args) # :nodoc:
|
24
|
+
args[0] = AccessLog.escape(args[0]) unless args.empty?
|
25
|
+
super(*args)
|
26
|
+
end
|
27
|
+
class << self
|
28
|
+
attr_reader :code, :reason_phrase # :nodoc:
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the HTTP status code
|
32
|
+
def code() self::class::code end
|
33
|
+
|
34
|
+
# Returns the HTTP status description
|
35
|
+
def reason_phrase() self::class::reason_phrase end
|
36
|
+
|
37
|
+
alias to_i code # :nodoc:
|
38
|
+
end
|
39
|
+
|
40
|
+
# Root of the HTTP info statuses
|
41
|
+
class Info < Status; end
|
42
|
+
# Root of the HTTP sucess statuses
|
43
|
+
class Success < Status; end
|
44
|
+
# Root of the HTTP redirect statuses
|
45
|
+
class Redirect < Status; end
|
46
|
+
# Root of the HTTP error statuses
|
47
|
+
class Error < Status; end
|
48
|
+
# Root of the HTTP client error statuses
|
49
|
+
class ClientError < Error; end
|
50
|
+
# Root of the HTTP server error statuses
|
51
|
+
class ServerError < Error; end
|
52
|
+
|
53
|
+
class EOFError < StandardError; end
|
54
|
+
|
55
|
+
# HTTP status codes and descriptions
|
56
|
+
StatusMessage = { # :nodoc:
|
57
|
+
100 => 'Continue',
|
58
|
+
101 => 'Switching Protocols',
|
59
|
+
200 => 'OK',
|
60
|
+
201 => 'Created',
|
61
|
+
202 => 'Accepted',
|
62
|
+
203 => 'Non-Authoritative Information',
|
63
|
+
204 => 'No Content',
|
64
|
+
205 => 'Reset Content',
|
65
|
+
206 => 'Partial Content',
|
66
|
+
300 => 'Multiple Choices',
|
67
|
+
301 => 'Moved Permanently',
|
68
|
+
302 => 'Found',
|
69
|
+
303 => 'See Other',
|
70
|
+
304 => 'Not Modified',
|
71
|
+
305 => 'Use Proxy',
|
72
|
+
307 => 'Temporary Redirect',
|
73
|
+
400 => 'Bad Request',
|
74
|
+
401 => 'Unauthorized',
|
75
|
+
402 => 'Payment Required',
|
76
|
+
403 => 'Forbidden',
|
77
|
+
404 => 'Not Found',
|
78
|
+
405 => 'Method Not Allowed',
|
79
|
+
406 => 'Not Acceptable',
|
80
|
+
407 => 'Proxy Authentication Required',
|
81
|
+
408 => 'Request Timeout',
|
82
|
+
409 => 'Conflict',
|
83
|
+
410 => 'Gone',
|
84
|
+
411 => 'Length Required',
|
85
|
+
412 => 'Precondition Failed',
|
86
|
+
413 => 'Request Entity Too Large',
|
87
|
+
414 => 'Request-URI Too Large',
|
88
|
+
415 => 'Unsupported Media Type',
|
89
|
+
416 => 'Request Range Not Satisfiable',
|
90
|
+
417 => 'Expectation Failed',
|
91
|
+
500 => 'Internal Server Error',
|
92
|
+
501 => 'Not Implemented',
|
93
|
+
502 => 'Bad Gateway',
|
94
|
+
503 => 'Service Unavailable',
|
95
|
+
504 => 'Gateway Timeout',
|
96
|
+
505 => 'HTTP Version Not Supported'
|
97
|
+
}
|
98
|
+
|
99
|
+
# Maps a status code to the corresponding Status class
|
100
|
+
CodeToError = {} # :nodoc:
|
101
|
+
|
102
|
+
# Creates a status or error class for each status code and
|
103
|
+
# populates the CodeToError map.
|
104
|
+
StatusMessage.each{|code, message|
|
105
|
+
message.freeze
|
106
|
+
var_name = message.gsub(/[ \-]/,'_').upcase
|
107
|
+
err_name = message.gsub(/[ \-]/,'')
|
108
|
+
|
109
|
+
case code
|
110
|
+
when 100...200; parent = Info
|
111
|
+
when 200...300; parent = Success
|
112
|
+
when 300...400; parent = Redirect
|
113
|
+
when 400...500; parent = ClientError
|
114
|
+
when 500...600; parent = ServerError
|
115
|
+
end
|
116
|
+
|
117
|
+
const_set("RC_#{var_name}", code)
|
118
|
+
err_class = Class.new(parent)
|
119
|
+
err_class.instance_variable_set(:@code, code)
|
120
|
+
err_class.instance_variable_set(:@reason_phrase, message)
|
121
|
+
const_set(err_name, err_class)
|
122
|
+
CodeToError[code] = err_class
|
123
|
+
}
|
124
|
+
|
125
|
+
##
|
126
|
+
# Returns the description corresponding to the HTTP status +code+
|
127
|
+
#
|
128
|
+
# WEBrick::HTTPStatus.reason_phrase 404
|
129
|
+
# => "Not Found"
|
130
|
+
def reason_phrase(code)
|
131
|
+
StatusMessage[code.to_i]
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# Is +code+ an informational status?
|
136
|
+
def info?(code)
|
137
|
+
code.to_i >= 100 and code.to_i < 200
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Is +code+ a successful status?
|
142
|
+
def success?(code)
|
143
|
+
code.to_i >= 200 and code.to_i < 300
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Is +code+ a redirection status?
|
148
|
+
def redirect?(code)
|
149
|
+
code.to_i >= 300 and code.to_i < 400
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# Is +code+ an error status?
|
154
|
+
def error?(code)
|
155
|
+
code.to_i >= 400 and code.to_i < 600
|
156
|
+
end
|
157
|
+
|
158
|
+
##
|
159
|
+
# Is +code+ a client error status?
|
160
|
+
def client_error?(code)
|
161
|
+
code.to_i >= 400 and code.to_i < 500
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Is +code+ a server error status?
|
166
|
+
def server_error?(code)
|
167
|
+
code.to_i >= 500 and code.to_i < 600
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Returns the status class corresponding to +code+
|
172
|
+
#
|
173
|
+
# WEBrick::HTTPStatus[302]
|
174
|
+
# => WEBrick::HTTPStatus::NotFound
|
175
|
+
#
|
176
|
+
def self.[](code)
|
177
|
+
CodeToError[code]
|
178
|
+
end
|
179
|
+
|
180
|
+
module_function :reason_phrase
|
181
|
+
module_function :info?, :success?, :redirect?, :error?
|
182
|
+
module_function :client_error?, :server_error?
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,394 @@
|
|
1
|
+
#
|
2
|
+
# httputils.rb -- HTTPUtils Module
|
3
|
+
#
|
4
|
+
# Author: IPR -- Internet Programming with Ruby -- writers
|
5
|
+
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
|
6
|
+
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
|
7
|
+
# reserved.
|
8
|
+
#
|
9
|
+
# $IPR: httputils.rb,v 1.34 2003/06/05 21:34:08 gotoyuzo Exp $
|
10
|
+
|
11
|
+
require 'socket'
|
12
|
+
require 'tempfile'
|
13
|
+
|
14
|
+
module WEBrick
|
15
|
+
CR = "\x0d"
|
16
|
+
LF = "\x0a"
|
17
|
+
CRLF = "\x0d\x0a"
|
18
|
+
|
19
|
+
module HTTPUtils
|
20
|
+
|
21
|
+
def normalize_path(path)
|
22
|
+
raise "abnormal path `#{path}'" if path[0] != ?/
|
23
|
+
ret = path.dup
|
24
|
+
|
25
|
+
ret.gsub!(%r{/+}o, '/') # // => /
|
26
|
+
while ret.sub!(%r'/\.(?:/|\Z)', '/'); end # /. => /
|
27
|
+
while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo
|
28
|
+
|
29
|
+
raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
|
30
|
+
ret
|
31
|
+
end
|
32
|
+
module_function :normalize_path
|
33
|
+
|
34
|
+
#####
|
35
|
+
|
36
|
+
DefaultMimeTypes = {
|
37
|
+
"ai" => "application/postscript",
|
38
|
+
"asc" => "text/plain",
|
39
|
+
"avi" => "video/x-msvideo",
|
40
|
+
"bin" => "application/octet-stream",
|
41
|
+
"bmp" => "image/bmp",
|
42
|
+
"class" => "application/octet-stream",
|
43
|
+
"cer" => "application/pkix-cert",
|
44
|
+
"crl" => "application/pkix-crl",
|
45
|
+
"crt" => "application/x-x509-ca-cert",
|
46
|
+
#"crl" => "application/x-pkcs7-crl",
|
47
|
+
"css" => "text/css",
|
48
|
+
"dms" => "application/octet-stream",
|
49
|
+
"doc" => "application/msword",
|
50
|
+
"dvi" => "application/x-dvi",
|
51
|
+
"eps" => "application/postscript",
|
52
|
+
"etx" => "text/x-setext",
|
53
|
+
"exe" => "application/octet-stream",
|
54
|
+
"gif" => "image/gif",
|
55
|
+
"htm" => "text/html",
|
56
|
+
"html" => "text/html",
|
57
|
+
"jpe" => "image/jpeg",
|
58
|
+
"jpeg" => "image/jpeg",
|
59
|
+
"jpg" => "image/jpeg",
|
60
|
+
"js" => "application/javascript",
|
61
|
+
"lha" => "application/octet-stream",
|
62
|
+
"lzh" => "application/octet-stream",
|
63
|
+
"mov" => "video/quicktime",
|
64
|
+
"mpe" => "video/mpeg",
|
65
|
+
"mpeg" => "video/mpeg",
|
66
|
+
"mpg" => "video/mpeg",
|
67
|
+
"pbm" => "image/x-portable-bitmap",
|
68
|
+
"pdf" => "application/pdf",
|
69
|
+
"pgm" => "image/x-portable-graymap",
|
70
|
+
"png" => "image/png",
|
71
|
+
"pnm" => "image/x-portable-anymap",
|
72
|
+
"ppm" => "image/x-portable-pixmap",
|
73
|
+
"ppt" => "application/vnd.ms-powerpoint",
|
74
|
+
"ps" => "application/postscript",
|
75
|
+
"qt" => "video/quicktime",
|
76
|
+
"ras" => "image/x-cmu-raster",
|
77
|
+
"rb" => "text/plain",
|
78
|
+
"rd" => "text/plain",
|
79
|
+
"rtf" => "application/rtf",
|
80
|
+
"sgm" => "text/sgml",
|
81
|
+
"sgml" => "text/sgml",
|
82
|
+
"svg" => "image/svg+xml",
|
83
|
+
"tif" => "image/tiff",
|
84
|
+
"tiff" => "image/tiff",
|
85
|
+
"txt" => "text/plain",
|
86
|
+
"xbm" => "image/x-xbitmap",
|
87
|
+
"xhtml" => "text/html",
|
88
|
+
"xls" => "application/vnd.ms-excel",
|
89
|
+
"xml" => "text/xml",
|
90
|
+
"xpm" => "image/x-xpixmap",
|
91
|
+
"xwd" => "image/x-xwindowdump",
|
92
|
+
"zip" => "application/zip",
|
93
|
+
}
|
94
|
+
|
95
|
+
# Load Apache compatible mime.types file.
|
96
|
+
def load_mime_types(file)
|
97
|
+
open(file){ |io|
|
98
|
+
hash = Hash.new
|
99
|
+
io.each{ |line|
|
100
|
+
next if /^#/ =~ line
|
101
|
+
line.chomp!
|
102
|
+
mimetype, ext0 = line.split(/\s+/, 2)
|
103
|
+
next unless ext0
|
104
|
+
next if ext0.empty?
|
105
|
+
ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
|
106
|
+
}
|
107
|
+
hash
|
108
|
+
}
|
109
|
+
end
|
110
|
+
module_function :load_mime_types
|
111
|
+
|
112
|
+
def mime_type(filename, mime_tab)
|
113
|
+
suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
|
114
|
+
suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
|
115
|
+
mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
|
116
|
+
end
|
117
|
+
module_function :mime_type
|
118
|
+
|
119
|
+
#####
|
120
|
+
|
121
|
+
def parse_header(raw)
|
122
|
+
header = Hash.new([].freeze)
|
123
|
+
field = nil
|
124
|
+
raw.each_line{|line|
|
125
|
+
case line
|
126
|
+
when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om
|
127
|
+
field, value = $1, $2
|
128
|
+
field.downcase!
|
129
|
+
header[field] = [] unless header.has_key?(field)
|
130
|
+
header[field] << value
|
131
|
+
when /^\s+(.*?)\s*\z/om
|
132
|
+
value = $1
|
133
|
+
unless field
|
134
|
+
raise HTTPStatus::BadRequest, "bad header '#{line}'."
|
135
|
+
end
|
136
|
+
header[field][-1] << " " << value
|
137
|
+
else
|
138
|
+
raise HTTPStatus::BadRequest, "bad header '#{line}'."
|
139
|
+
end
|
140
|
+
}
|
141
|
+
header.each{|key, values|
|
142
|
+
values.each{|value|
|
143
|
+
value.strip!
|
144
|
+
value.gsub!(/\s+/, " ")
|
145
|
+
}
|
146
|
+
}
|
147
|
+
header
|
148
|
+
end
|
149
|
+
module_function :parse_header
|
150
|
+
|
151
|
+
def split_header_value(str)
|
152
|
+
str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]+)+)
|
153
|
+
(?:,\s*|\Z)'xn).flatten
|
154
|
+
end
|
155
|
+
module_function :split_header_value
|
156
|
+
|
157
|
+
def parse_range_header(ranges_specifier)
|
158
|
+
if /^bytes=(.*)/ =~ ranges_specifier
|
159
|
+
byte_range_set = split_header_value($1)
|
160
|
+
byte_range_set.collect{|range_spec|
|
161
|
+
case range_spec
|
162
|
+
when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i
|
163
|
+
when /^(\d+)-/ then $1.to_i .. -1
|
164
|
+
when /^-(\d+)/ then -($1.to_i) .. -1
|
165
|
+
else return nil
|
166
|
+
end
|
167
|
+
}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
module_function :parse_range_header
|
171
|
+
|
172
|
+
def parse_qvalues(value)
|
173
|
+
tmp = []
|
174
|
+
if value
|
175
|
+
parts = value.split(/,\s*/)
|
176
|
+
parts.each {|part|
|
177
|
+
if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
|
178
|
+
val = m[1]
|
179
|
+
q = (m[2] or 1).to_f
|
180
|
+
tmp.push([val, q])
|
181
|
+
end
|
182
|
+
}
|
183
|
+
tmp = tmp.sort_by{|val, q| -q}
|
184
|
+
tmp.collect!{|val, q| val}
|
185
|
+
end
|
186
|
+
return tmp
|
187
|
+
end
|
188
|
+
module_function :parse_qvalues
|
189
|
+
|
190
|
+
#####
|
191
|
+
|
192
|
+
def dequote(str)
|
193
|
+
ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
|
194
|
+
ret.gsub!(/\\(.)/, "\\1")
|
195
|
+
ret
|
196
|
+
end
|
197
|
+
module_function :dequote
|
198
|
+
|
199
|
+
def quote(str)
|
200
|
+
'"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
|
201
|
+
end
|
202
|
+
module_function :quote
|
203
|
+
|
204
|
+
#####
|
205
|
+
|
206
|
+
class FormData < String
|
207
|
+
EmptyRawHeader = [].freeze
|
208
|
+
EmptyHeader = {}.freeze
|
209
|
+
|
210
|
+
attr_accessor :name, :filename, :next_data
|
211
|
+
protected :next_data
|
212
|
+
|
213
|
+
def initialize(*args)
|
214
|
+
@name = @filename = @next_data = nil
|
215
|
+
if args.empty?
|
216
|
+
@raw_header = []
|
217
|
+
@header = nil
|
218
|
+
super("")
|
219
|
+
else
|
220
|
+
@raw_header = EmptyRawHeader
|
221
|
+
@header = EmptyHeader
|
222
|
+
super(args.shift)
|
223
|
+
unless args.empty?
|
224
|
+
@next_data = self.class.new(*args)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def [](*key)
|
230
|
+
begin
|
231
|
+
@header[key[0].downcase].join(", ")
|
232
|
+
rescue StandardError, NameError
|
233
|
+
super
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def <<(str)
|
238
|
+
if @header
|
239
|
+
super
|
240
|
+
elsif str == CRLF
|
241
|
+
@header = HTTPUtils::parse_header(@raw_header.join)
|
242
|
+
if cd = self['content-disposition']
|
243
|
+
if /\s+name="(.*?)"/ =~ cd then @name = $1 end
|
244
|
+
if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end
|
245
|
+
end
|
246
|
+
else
|
247
|
+
@raw_header << str
|
248
|
+
end
|
249
|
+
self
|
250
|
+
end
|
251
|
+
|
252
|
+
def append_data(data)
|
253
|
+
tmp = self
|
254
|
+
while tmp
|
255
|
+
unless tmp.next_data
|
256
|
+
tmp.next_data = data
|
257
|
+
break
|
258
|
+
end
|
259
|
+
tmp = tmp.next_data
|
260
|
+
end
|
261
|
+
self
|
262
|
+
end
|
263
|
+
|
264
|
+
def each_data
|
265
|
+
tmp = self
|
266
|
+
while tmp
|
267
|
+
next_data = tmp.next_data
|
268
|
+
yield(tmp)
|
269
|
+
tmp = next_data
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def list
|
274
|
+
ret = []
|
275
|
+
each_data{|data|
|
276
|
+
ret << data.to_s
|
277
|
+
}
|
278
|
+
ret
|
279
|
+
end
|
280
|
+
|
281
|
+
alias :to_ary :list
|
282
|
+
|
283
|
+
def to_s
|
284
|
+
String.new(self)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def parse_query(str)
|
289
|
+
query = Hash.new
|
290
|
+
if str
|
291
|
+
str.split(/[&;]/).each{|x|
|
292
|
+
next if x.empty?
|
293
|
+
key, val = x.split(/=/,2)
|
294
|
+
key = unescape_form(key)
|
295
|
+
val = unescape_form(val.to_s)
|
296
|
+
val = FormData.new(val)
|
297
|
+
val.name = key
|
298
|
+
if query.has_key?(key)
|
299
|
+
query[key].append_data(val)
|
300
|
+
next
|
301
|
+
end
|
302
|
+
query[key] = val
|
303
|
+
}
|
304
|
+
end
|
305
|
+
query
|
306
|
+
end
|
307
|
+
module_function :parse_query
|
308
|
+
|
309
|
+
def parse_form_data(io, boundary)
|
310
|
+
boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/
|
311
|
+
form_data = Hash.new
|
312
|
+
return form_data unless io
|
313
|
+
data = nil
|
314
|
+
io.each_line{|line|
|
315
|
+
if boundary_regexp =~ line
|
316
|
+
if data
|
317
|
+
data.chop!
|
318
|
+
key = data.name
|
319
|
+
if form_data.has_key?(key)
|
320
|
+
form_data[key].append_data(data)
|
321
|
+
else
|
322
|
+
form_data[key] = data
|
323
|
+
end
|
324
|
+
end
|
325
|
+
data = FormData.new
|
326
|
+
next
|
327
|
+
else
|
328
|
+
if data
|
329
|
+
data << line
|
330
|
+
end
|
331
|
+
end
|
332
|
+
}
|
333
|
+
return form_data
|
334
|
+
end
|
335
|
+
module_function :parse_form_data
|
336
|
+
|
337
|
+
#####
|
338
|
+
|
339
|
+
reserved = ';/?:@&=+$,'
|
340
|
+
num = '0123456789'
|
341
|
+
lowalpha = 'abcdefghijklmnopqrstuvwxyz'
|
342
|
+
upalpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
343
|
+
mark = '-_.!~*\'()'
|
344
|
+
unreserved = num + lowalpha + upalpha + mark
|
345
|
+
control = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f"
|
346
|
+
space = " "
|
347
|
+
delims = '<>#%"'
|
348
|
+
unwise = '{}|\\^[]`'
|
349
|
+
nonascii = (0x80..0xff).collect{|c| c.chr }.join
|
350
|
+
|
351
|
+
module_function
|
352
|
+
|
353
|
+
def _make_regex(str) /([#{Regexp.escape(str)}])/n end
|
354
|
+
def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end
|
355
|
+
def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1.ord } end
|
356
|
+
def _unescape(str, regex) str.gsub(regex){ $1.hex.chr } end
|
357
|
+
|
358
|
+
UNESCAPED = _make_regex(control+space+delims+unwise+nonascii)
|
359
|
+
UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii)
|
360
|
+
NONASCII = _make_regex(nonascii)
|
361
|
+
ESCAPED = /%([0-9a-fA-F]{2})/
|
362
|
+
UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,")
|
363
|
+
|
364
|
+
def escape(str)
|
365
|
+
_escape(str, UNESCAPED)
|
366
|
+
end
|
367
|
+
|
368
|
+
def unescape(str)
|
369
|
+
_unescape(str, ESCAPED)
|
370
|
+
end
|
371
|
+
|
372
|
+
def escape_form(str)
|
373
|
+
ret = _escape(str, UNESCAPED_FORM)
|
374
|
+
ret.gsub!(/ /, "+")
|
375
|
+
ret
|
376
|
+
end
|
377
|
+
|
378
|
+
def unescape_form(str)
|
379
|
+
_unescape(str.gsub(/\+/, " "), ESCAPED)
|
380
|
+
end
|
381
|
+
|
382
|
+
def escape_path(str)
|
383
|
+
result = ""
|
384
|
+
str.scan(%r{/([^/]*)}).each{|i|
|
385
|
+
result << "/" << _escape(i[0], UNESCAPED_PCHAR)
|
386
|
+
}
|
387
|
+
return result
|
388
|
+
end
|
389
|
+
|
390
|
+
def escape8bit(str)
|
391
|
+
_escape(str, NONASCII)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|