/vendor/jruby-1.1.6RC1/lib/ruby/1.8/webrick/httputils.rb

https://bitbucket.org/nicksieger/advent-jruby · Ruby · 399 lines · 341 code · 42 blank · 16 comment · 29 complexity · 6f00cb41bc3cf727bba6e1bb4436eb2a MD5 · raw file

  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. require 'socket'
  11. require 'tempfile'
  12. module WEBrick
  13. CR = "\x0d"
  14. LF = "\x0a"
  15. CRLF = "\x0d\x0a"
  16. module HTTPUtils
  17. def normalize_path(path)
  18. raise "abnormal path `#{path}'" if path[0] != ?/
  19. ret = path.dup
  20. ret.gsub!(%r{/+}o, '/') # // => /
  21. while ret.sub!(%r{/\.(/|\Z)}o, '/'); end # /. => /
  22. begin # /foo/.. => /foo
  23. match = ret.sub!(%r{/([^/]+)/\.\.(/|\Z)}o){
  24. if $1 == ".."
  25. raise "abnormal path `#{path}'"
  26. else
  27. "/"
  28. end
  29. }
  30. end while match
  31. raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
  32. ret
  33. end
  34. module_function :normalize_path
  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. "lha" => "application/octet-stream",
  61. "lzh" => "application/octet-stream",
  62. "mov" => "video/quicktime",
  63. "mpe" => "video/mpeg",
  64. "mpeg" => "video/mpeg",
  65. "mpg" => "video/mpeg",
  66. "pbm" => "image/x-portable-bitmap",
  67. "pdf" => "application/pdf",
  68. "pgm" => "image/x-portable-graymap",
  69. "png" => "image/png",
  70. "pnm" => "image/x-portable-anymap",
  71. "ppm" => "image/x-portable-pixmap",
  72. "ppt" => "application/vnd.ms-powerpoint",
  73. "ps" => "application/postscript",
  74. "qt" => "video/quicktime",
  75. "ras" => "image/x-cmu-raster",
  76. "rb" => "text/plain",
  77. "rd" => "text/plain",
  78. "rtf" => "application/rtf",
  79. "sgm" => "text/sgml",
  80. "sgml" => "text/sgml",
  81. "tif" => "image/tiff",
  82. "tiff" => "image/tiff",
  83. "txt" => "text/plain",
  84. "xbm" => "image/x-xbitmap",
  85. "xls" => "application/vnd.ms-excel",
  86. "xml" => "text/xml",
  87. "xpm" => "image/x-xpixmap",
  88. "xwd" => "image/x-xwindowdump",
  89. "zip" => "application/zip",
  90. }
  91. # Load Apache compatible mime.types file.
  92. def load_mime_types(file)
  93. open(file){ |io|
  94. hash = Hash.new
  95. io.each{ |line|
  96. next if /^#/ =~ line
  97. line.chomp!
  98. mimetype, ext0 = line.split(/\s+/, 2)
  99. next unless ext0
  100. next if ext0.empty?
  101. ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
  102. }
  103. hash
  104. }
  105. end
  106. module_function :load_mime_types
  107. def mime_type(filename, mime_tab)
  108. suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
  109. suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
  110. mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
  111. end
  112. module_function :mime_type
  113. #####
  114. def parse_header(raw)
  115. header = Hash.new([].freeze)
  116. field = nil
  117. raw.each{|line|
  118. case line
  119. when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om
  120. field, value = $1, $2
  121. field.downcase!
  122. header[field] = [] unless header.has_key?(field)
  123. header[field] << value
  124. when /^\s+(.*?)\s*\z/om
  125. value = $1
  126. unless field
  127. raise "bad header '#{line.inspect}'."
  128. end
  129. header[field][-1] << " " << value
  130. else
  131. raise "bad header '#{line.inspect}'."
  132. end
  133. }
  134. header.each{|key, values|
  135. values.each{|value|
  136. value.strip!
  137. value.gsub!(/\s+/, " ")
  138. }
  139. }
  140. header
  141. end
  142. module_function :parse_header
  143. def split_header_value(str)
  144. str.scan(/((?:"(?:\\.|[^"])+?"|[^",]+)+)
  145. (?:,\s*|\Z)/xn).collect{|v| v[0] }
  146. end
  147. module_function :split_header_value
  148. def parse_range_header(ranges_specifier)
  149. if /^bytes=(.*)/ =~ ranges_specifier
  150. byte_range_set = split_header_value($1)
  151. byte_range_set.collect{|range_spec|
  152. case range_spec
  153. when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i
  154. when /^(\d+)-/ then $1.to_i .. -1
  155. when /^-(\d+)/ then -($1.to_i) .. -1
  156. else return nil
  157. end
  158. }
  159. end
  160. end
  161. module_function :parse_range_header
  162. def parse_qvalues(value)
  163. tmp = []
  164. if value
  165. parts = value.split(/,\s*/)
  166. parts.each {|part|
  167. if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
  168. val = m[1]
  169. q = (m[2] or 1).to_f
  170. tmp.push([val, q])
  171. end
  172. }
  173. tmp = tmp.sort_by{|val, q| -q}
  174. tmp.collect!{|val, q| val}
  175. end
  176. return tmp
  177. end
  178. module_function :parse_qvalues
  179. #####
  180. def dequote(str)
  181. ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
  182. ret.gsub!(/\\(.)/, "\\1")
  183. ret
  184. end
  185. module_function :dequote
  186. def quote(str)
  187. '"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
  188. end
  189. module_function :quote
  190. #####
  191. class FormData < String
  192. EmptyRawHeader = [].freeze
  193. EmptyHeader = {}.freeze
  194. attr_accessor :name, :filename, :next_data
  195. protected :next_data
  196. def initialize(*args)
  197. @name = @filename = @next_data = nil
  198. if args.empty?
  199. @raw_header = []
  200. @header = nil
  201. super("")
  202. else
  203. @raw_header = EmptyRawHeader
  204. @header = EmptyHeader
  205. super(args.shift)
  206. unless args.empty?
  207. @next_data = self.class.new(*args)
  208. end
  209. end
  210. end
  211. def [](*key)
  212. begin
  213. @header[key[0].downcase].join(", ")
  214. rescue StandardError, NameError
  215. super
  216. end
  217. end
  218. def <<(str)
  219. if @header
  220. super
  221. elsif str == CRLF
  222. @header = HTTPUtils::parse_header(@raw_header)
  223. if cd = self['content-disposition']
  224. if /\s+name="(.*?)"/ =~ cd then @name = $1 end
  225. if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end
  226. end
  227. else
  228. @raw_header << str
  229. end
  230. self
  231. end
  232. def append_data(data)
  233. tmp = self
  234. while tmp
  235. unless tmp.next_data
  236. tmp.next_data = data
  237. break
  238. end
  239. tmp = tmp.next_data
  240. end
  241. self
  242. end
  243. def each_data
  244. tmp = self
  245. while tmp
  246. next_data = tmp.next_data
  247. yield(tmp)
  248. tmp = next_data
  249. end
  250. end
  251. def list
  252. ret = []
  253. each_data{|data|
  254. ret << data.to_s
  255. }
  256. ret
  257. end
  258. alias :to_ary :list
  259. def to_s
  260. String.new(self)
  261. end
  262. end
  263. def parse_query(str)
  264. query = Hash.new
  265. if str
  266. str.split(/[&;]/).each{|x|
  267. next if x.empty?
  268. key, val = x.split(/=/,2)
  269. key = unescape_form(key)
  270. val = unescape_form(val.to_s)
  271. val = FormData.new(val)
  272. val.name = key
  273. if query.has_key?(key)
  274. query[key].append_data(val)
  275. next
  276. end
  277. query[key] = val
  278. }
  279. end
  280. query
  281. end
  282. module_function :parse_query
  283. def parse_form_data(io, boundary)
  284. boundary_regexp = /\A--#{boundary}(--)?#{CRLF}\z/
  285. form_data = Hash.new
  286. return form_data unless io
  287. data = nil
  288. io.each{|line|
  289. if boundary_regexp =~ line
  290. if data
  291. data.chop!
  292. key = data.name
  293. if form_data.has_key?(key)
  294. form_data[key].append_data(data)
  295. else
  296. form_data[key] = data
  297. end
  298. end
  299. data = FormData.new
  300. next
  301. else
  302. if data
  303. data << line
  304. end
  305. end
  306. }
  307. return form_data
  308. end
  309. module_function :parse_form_data
  310. #####
  311. reserved = ';/?:@&=+$,'
  312. num = '0123456789'
  313. lowalpha = 'abcdefghijklmnopqrstuvwxyz'
  314. upalpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  315. mark = '-_.!~*\'()'
  316. unreserved = num + lowalpha + upalpha + mark
  317. control = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f"
  318. space = " "
  319. delims = '<>#%"'
  320. unwise = '{}|\\^[]`'
  321. nonascii = (0x80..0xff).collect{|c| c.chr }.join
  322. module_function
  323. def _make_regex(str) /([#{Regexp.escape(str)}])/n end
  324. def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end
  325. def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1[0] } end
  326. def _unescape(str, regex) str.gsub(regex){ $1.hex.chr } end
  327. UNESCAPED = _make_regex(control+space+delims+unwise+nonascii)
  328. UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii)
  329. NONASCII = _make_regex(nonascii)
  330. ESCAPED = /%([0-9a-fA-F]{2})/
  331. UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,")
  332. def escape(str)
  333. _escape(str, UNESCAPED)
  334. end
  335. def unescape(str)
  336. _unescape(str, ESCAPED)
  337. end
  338. def escape_form(str)
  339. ret = _escape(str, UNESCAPED_FORM)
  340. ret.gsub!(/ /, "+")
  341. ret
  342. end
  343. def unescape_form(str)
  344. _unescape(str.gsub(/\+/, " "), ESCAPED)
  345. end
  346. def escape_path(str)
  347. result = ""
  348. str.scan(%r{/([^/]*)}).each{|i|
  349. result << "/" << _escape(i[0], UNESCAPED_PCHAR)
  350. }
  351. return result
  352. end
  353. def escape8bit(str)
  354. _escape(str, NONASCII)
  355. end
  356. end
  357. end