spec_request.rb 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  1. require 'stringio'
  2. require 'cgi'
  3. require 'rack/request'
  4. require 'rack/mock'
  5. describe Rack::Request do
  6. should "wrap the rack variables" do
  7. req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
  8. req.body.should.respond_to? :gets
  9. req.scheme.should.equal "http"
  10. req.request_method.should.equal "GET"
  11. req.should.be.get
  12. req.should.not.be.post
  13. req.should.not.be.put
  14. req.should.not.be.delete
  15. req.should.not.be.head
  16. req.should.not.be.patch
  17. req.script_name.should.equal ""
  18. req.path_info.should.equal "/"
  19. req.query_string.should.equal ""
  20. req.host.should.equal "example.com"
  21. req.port.should.equal 8080
  22. req.content_length.should.equal "0"
  23. req.content_type.should.be.nil
  24. end
  25. should "figure out the correct host" do
  26. req = Rack::Request.new \
  27. Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
  28. req.host.should.equal "www2.example.org"
  29. req = Rack::Request.new \
  30. Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
  31. req.host.should.equal "example.org"
  32. req = Rack::Request.new \
  33. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
  34. req.host.should.equal "example.org"
  35. env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292")
  36. env.delete("SERVER_NAME")
  37. req = Rack::Request.new(env)
  38. req.host.should.equal "192.168.1.1"
  39. env = Rack::MockRequest.env_for("/")
  40. env.delete("SERVER_NAME")
  41. req = Rack::Request.new(env)
  42. req.host.should.equal ""
  43. end
  44. should "figure out the correct port" do
  45. req = Rack::Request.new \
  46. Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
  47. req.port.should.equal 80
  48. req = Rack::Request.new \
  49. Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81")
  50. req.port.should.equal 81
  51. req = Rack::Request.new \
  52. Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
  53. req.port.should.equal 9292
  54. req = Rack::Request.new \
  55. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
  56. req.port.should.equal 9292
  57. req = Rack::Request.new \
  58. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org")
  59. req.port.should.equal 80
  60. req = Rack::Request.new \
  61. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on")
  62. req.port.should.equal 443
  63. req = Rack::Request.new \
  64. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https")
  65. req.port.should.equal 443
  66. req = Rack::Request.new \
  67. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393")
  68. req.port.should.equal 9393
  69. req = Rack::Request.new \
  70. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80")
  71. req.port.should.equal 9393
  72. req = Rack::Request.new \
  73. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
  74. req.port.should.equal 80
  75. end
  76. should "figure out the correct host with port" do
  77. req = Rack::Request.new \
  78. Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
  79. req.host_with_port.should.equal "www2.example.org"
  80. req = Rack::Request.new \
  81. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81")
  82. req.host_with_port.should.equal "localhost:81"
  83. req = Rack::Request.new \
  84. Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
  85. req.host_with_port.should.equal "example.org:9292"
  86. req = Rack::Request.new \
  87. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
  88. req.host_with_port.should.equal "example.org:9292"
  89. req = Rack::Request.new \
  90. Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
  91. req.host_with_port.should.equal "example.org"
  92. end
  93. should "parse the query string" do
  94. req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
  95. req.query_string.should.equal "foo=bar&quux=bla"
  96. req.GET.should.equal "foo" => "bar", "quux" => "bla"
  97. req.POST.should.be.empty
  98. req.params.should.equal "foo" => "bar", "quux" => "bla"
  99. end
  100. should "limit the keys from the GET query string" do
  101. env = Rack::MockRequest.env_for("/?foo=bar")
  102. old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
  103. begin
  104. req = Rack::Request.new(env)
  105. lambda { req.GET }.should.raise(RangeError)
  106. ensure
  107. Rack::Utils.key_space_limit = old
  108. end
  109. end
  110. should "limit the key size per nested params hash" do
  111. nested_query = Rack::MockRequest.env_for("/?foo[bar][baz][qux]=1")
  112. plain_query = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1")
  113. old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3
  114. begin
  115. lambda { Rack::Request.new(nested_query).GET }.should.not.raise(RangeError)
  116. lambda { Rack::Request.new(plain_query).GET }.should.raise(RangeError)
  117. ensure
  118. Rack::Utils.key_space_limit = old
  119. end
  120. end
  121. should "not unify GET and POST when calling params" do
  122. mr = Rack::MockRequest.env_for("/?foo=quux",
  123. "REQUEST_METHOD" => 'POST',
  124. :input => "foo=bar&quux=bla"
  125. )
  126. req = Rack::Request.new mr
  127. req.params
  128. req.GET.should.equal "foo" => "quux"
  129. req.POST.should.equal "foo" => "bar", "quux" => "bla"
  130. req.params.should.equal req.GET.merge(req.POST)
  131. end
  132. should "raise if rack.input is missing" do
  133. req = Rack::Request.new({})
  134. lambda { req.POST }.should.raise(RuntimeError)
  135. end
  136. should "parse POST data when method is POST and no Content-Type given" do
  137. req = Rack::Request.new \
  138. Rack::MockRequest.env_for("/?foo=quux",
  139. "REQUEST_METHOD" => 'POST',
  140. :input => "foo=bar&quux=bla")
  141. req.content_type.should.be.nil
  142. req.media_type.should.be.nil
  143. req.query_string.should.equal "foo=quux"
  144. req.GET.should.equal "foo" => "quux"
  145. req.POST.should.equal "foo" => "bar", "quux" => "bla"
  146. req.params.should.equal "foo" => "bar", "quux" => "bla"
  147. end
  148. should "limit the keys from the POST form data" do
  149. env = Rack::MockRequest.env_for("",
  150. "REQUEST_METHOD" => 'POST',
  151. :input => "foo=bar&quux=bla")
  152. old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
  153. begin
  154. req = Rack::Request.new(env)
  155. lambda { req.POST }.should.raise(RangeError)
  156. ensure
  157. Rack::Utils.key_space_limit = old
  158. end
  159. end
  160. should "parse POST data with explicit content type regardless of method" do
  161. req = Rack::Request.new \
  162. Rack::MockRequest.env_for("/",
  163. "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
  164. :input => "foo=bar&quux=bla")
  165. req.content_type.should.equal 'application/x-www-form-urlencoded;foo=bar'
  166. req.media_type.should.equal 'application/x-www-form-urlencoded'
  167. req.media_type_params['foo'].should.equal 'bar'
  168. req.POST.should.equal "foo" => "bar", "quux" => "bla"
  169. req.params.should.equal "foo" => "bar", "quux" => "bla"
  170. end
  171. should "not parse POST data when media type is not form-data" do
  172. req = Rack::Request.new \
  173. Rack::MockRequest.env_for("/?foo=quux",
  174. "REQUEST_METHOD" => 'POST',
  175. "CONTENT_TYPE" => 'text/plain;charset=utf-8',
  176. :input => "foo=bar&quux=bla")
  177. req.content_type.should.equal 'text/plain;charset=utf-8'
  178. req.media_type.should.equal 'text/plain'
  179. req.media_type_params['charset'].should.equal 'utf-8'
  180. req.POST.should.be.empty
  181. req.params.should.equal "foo" => "quux"
  182. req.body.read.should.equal "foo=bar&quux=bla"
  183. end
  184. should "parse POST data on PUT when media type is form-data" do
  185. req = Rack::Request.new \
  186. Rack::MockRequest.env_for("/?foo=quux",
  187. "REQUEST_METHOD" => 'PUT',
  188. "CONTENT_TYPE" => 'application/x-www-form-urlencoded',
  189. :input => "foo=bar&quux=bla")
  190. req.POST.should.equal "foo" => "bar", "quux" => "bla"
  191. req.body.read.should.equal "foo=bar&quux=bla"
  192. end
  193. should "rewind input after parsing POST data" do
  194. input = StringIO.new("foo=bar&quux=bla")
  195. req = Rack::Request.new \
  196. Rack::MockRequest.env_for("/",
  197. "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
  198. :input => input)
  199. req.params.should.equal "foo" => "bar", "quux" => "bla"
  200. input.read.should.equal "foo=bar&quux=bla"
  201. end
  202. should "clean up Safari's ajax POST body" do
  203. req = Rack::Request.new \
  204. Rack::MockRequest.env_for("/",
  205. 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
  206. req.POST.should.equal "foo" => "bar", "quux" => "bla"
  207. end
  208. should "get value by key from params with #[]" do
  209. req = Rack::Request.new \
  210. Rack::MockRequest.env_for("?foo=quux")
  211. req['foo'].should.equal 'quux'
  212. req[:foo].should.equal 'quux'
  213. end
  214. should "set value to key on params with #[]=" do
  215. req = Rack::Request.new \
  216. Rack::MockRequest.env_for("?foo=duh")
  217. req['foo'].should.equal 'duh'
  218. req[:foo].should.equal 'duh'
  219. req.params.should.equal 'foo' => 'duh'
  220. req['foo'] = 'bar'
  221. req.params.should.equal 'foo' => 'bar'
  222. req['foo'].should.equal 'bar'
  223. req[:foo].should.equal 'bar'
  224. req[:foo] = 'jaz'
  225. req.params.should.equal 'foo' => 'jaz'
  226. req['foo'].should.equal 'jaz'
  227. req[:foo].should.equal 'jaz'
  228. end
  229. should "return values for the keys in the order given from values_at" do
  230. req = Rack::Request.new \
  231. Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
  232. req.values_at('foo').should.equal ['baz']
  233. req.values_at('foo', 'wun').should.equal ['baz', 'der']
  234. req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der']
  235. end
  236. should "extract referrer correctly" do
  237. req = Rack::Request.new \
  238. Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
  239. req.referer.should.equal "/some/path"
  240. req = Rack::Request.new \
  241. Rack::MockRequest.env_for("/")
  242. req.referer.should.equal nil
  243. end
  244. should "extract user agent correctly" do
  245. req = Rack::Request.new \
  246. Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
  247. req.user_agent.should.equal "Mozilla/4.0 (compatible)"
  248. req = Rack::Request.new \
  249. Rack::MockRequest.env_for("/")
  250. req.user_agent.should.equal nil
  251. end
  252. should "treat missing content type as nil" do
  253. req = Rack::Request.new \
  254. Rack::MockRequest.env_for("/")
  255. req.content_type.should.equal nil
  256. end
  257. should "treat empty content type as nil" do
  258. req = Rack::Request.new \
  259. Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
  260. req.content_type.should.equal nil
  261. end
  262. should "return nil media type for empty content type" do
  263. req = Rack::Request.new \
  264. Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
  265. req.media_type.should.equal nil
  266. end
  267. should "cache, but invalidates the cache" do
  268. req = Rack::Request.new \
  269. Rack::MockRequest.env_for("/?foo=quux",
  270. "CONTENT_TYPE" => "application/x-www-form-urlencoded",
  271. :input => "foo=bar&quux=bla")
  272. req.GET.should.equal "foo" => "quux"
  273. req.GET.should.equal "foo" => "quux"
  274. req.env["QUERY_STRING"] = "bla=foo"
  275. req.GET.should.equal "bla" => "foo"
  276. req.GET.should.equal "bla" => "foo"
  277. req.POST.should.equal "foo" => "bar", "quux" => "bla"
  278. req.POST.should.equal "foo" => "bar", "quux" => "bla"
  279. req.env["rack.input"] = StringIO.new("foo=bla&quux=bar")
  280. req.POST.should.equal "foo" => "bla", "quux" => "bar"
  281. req.POST.should.equal "foo" => "bla", "quux" => "bar"
  282. end
  283. should "figure out if called via XHR" do
  284. req = Rack::Request.new(Rack::MockRequest.env_for(""))
  285. req.should.not.be.xhr
  286. req = Rack::Request.new \
  287. Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
  288. req.should.be.xhr
  289. end
  290. should "ssl detection" do
  291. request = Rack::Request.new(Rack::MockRequest.env_for("/"))
  292. request.scheme.should.equal "http"
  293. request.should.not.be.ssl?
  294. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTPS' => 'on'))
  295. request.scheme.should.equal "https"
  296. request.should.be.ssl?
  297. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https'))
  298. request.scheme.should.equal "https"
  299. request.should.be.ssl?
  300. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080'))
  301. request.scheme.should.equal "http"
  302. request.should.not.be.ssl?
  303. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'))
  304. request.scheme.should.equal "https"
  305. request.should.be.ssl?
  306. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on'))
  307. request.scheme.should.equal "https"
  308. request.should.be.ssl?
  309. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https'))
  310. request.scheme.should.equal "https"
  311. request.should.be.ssl?
  312. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https'))
  313. request.scheme.should.equal "https"
  314. request.should.be.ssl?
  315. request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http'))
  316. request.scheme.should.equal "https"
  317. request.should.be.ssl?
  318. end
  319. should "parse cookies" do
  320. req = Rack::Request.new \
  321. Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
  322. req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
  323. req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
  324. req.env.delete("HTTP_COOKIE")
  325. req.cookies.should.equal({})
  326. end
  327. should "always return the same hash object" do
  328. req = Rack::Request.new \
  329. Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
  330. hash = req.cookies
  331. req.env.delete("HTTP_COOKIE")
  332. req.cookies.should.equal(hash)
  333. req.env["HTTP_COOKIE"] = "zoo=m"
  334. req.cookies.should.equal(hash)
  335. end
  336. should "modify the cookies hash in place" do
  337. req = Rack::Request.new(Rack::MockRequest.env_for(""))
  338. req.cookies.should.equal({})
  339. req.cookies['foo'] = 'bar'
  340. req.cookies.should.equal 'foo' => 'bar'
  341. end
  342. should "raise any errors on every request" do
  343. req = Rack::Request.new Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
  344. 2.times { proc { req.cookies }.should.raise(ArgumentError) }
  345. end
  346. should "parse cookies according to RFC 2109" do
  347. req = Rack::Request.new \
  348. Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
  349. req.cookies.should.equal 'foo' => 'bar'
  350. end
  351. should 'parse cookies with quotes' do
  352. req = Rack::Request.new Rack::MockRequest.env_for('', {
  353. 'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"'
  354. })
  355. req.cookies.should.equal({
  356. '$Version' => '"1"',
  357. 'Customer' => '"WILE_E_COYOTE"',
  358. '$Path' => '"/acme"',
  359. 'Part_Number' => '"Rocket_Launcher_0001"',
  360. })
  361. end
  362. should "provide setters" do
  363. req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
  364. req.script_name.should.equal ""
  365. req.script_name = "/foo"
  366. req.script_name.should.equal "/foo"
  367. e["SCRIPT_NAME"].should.equal "/foo"
  368. req.path_info.should.equal "/"
  369. req.path_info = "/foo"
  370. req.path_info.should.equal "/foo"
  371. e["PATH_INFO"].should.equal "/foo"
  372. end
  373. should "provide the original env" do
  374. req = Rack::Request.new(e = Rack::MockRequest.env_for(""))
  375. req.env.should == e
  376. end
  377. should "restore the base URL" do
  378. Rack::Request.new(Rack::MockRequest.env_for("")).base_url.
  379. should.equal "http://example.org"
  380. Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url.
  381. should.equal "http://example.org"
  382. end
  383. should "restore the URL" do
  384. Rack::Request.new(Rack::MockRequest.env_for("")).url.
  385. should.equal "http://example.org/"
  386. Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
  387. should.equal "http://example.org/foo/"
  388. Rack::Request.new(Rack::MockRequest.env_for("/foo")).url.
  389. should.equal "http://example.org/foo"
  390. Rack::Request.new(Rack::MockRequest.env_for("?foo")).url.
  391. should.equal "http://example.org/?foo"
  392. Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).url.
  393. should.equal "http://example.org:8080/"
  394. Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).url.
  395. should.equal "https://example.org/"
  396. Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
  397. should.equal "https://example.com:8080/foo?foo"
  398. end
  399. should "restore the full path" do
  400. Rack::Request.new(Rack::MockRequest.env_for("")).fullpath.
  401. should.equal "/"
  402. Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
  403. should.equal "/foo/"
  404. Rack::Request.new(Rack::MockRequest.env_for("/foo")).fullpath.
  405. should.equal "/foo"
  406. Rack::Request.new(Rack::MockRequest.env_for("?foo")).fullpath.
  407. should.equal "/?foo"
  408. Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
  409. should.equal "/"
  410. Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).fullpath.
  411. should.equal "/"
  412. Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
  413. should.equal "/foo?foo"
  414. end
  415. should "handle multiple media type parameters" do
  416. req = Rack::Request.new \
  417. Rack::MockRequest.env_for("/",
  418. "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam')
  419. req.should.not.be.form_data
  420. req.media_type_params.should.include 'foo'
  421. req.media_type_params['foo'].should.equal 'BAR'
  422. req.media_type_params.should.include 'baz'
  423. req.media_type_params['baz'].should.equal 'bizzle dizzle'
  424. req.media_type_params.should.not.include 'BLING'
  425. req.media_type_params.should.include 'bling'
  426. req.media_type_params['bling'].should.equal 'bam'
  427. end
  428. should "parse with junk before boundry" do
  429. # Adapted from RFC 1867.
  430. input = <<EOF
  431. blah blah\r
  432. \r
  433. --AaB03x\r
  434. content-disposition: form-data; name="reply"\r
  435. \r
  436. yes\r
  437. --AaB03x\r
  438. content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
  439. Content-Type: image/jpeg\r
  440. Content-Transfer-Encoding: base64\r
  441. \r
  442. /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
  443. --AaB03x--\r
  444. EOF
  445. req = Rack::Request.new Rack::MockRequest.env_for("/",
  446. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  447. "CONTENT_LENGTH" => input.size,
  448. :input => input)
  449. req.POST.should.include "fileupload"
  450. req.POST.should.include "reply"
  451. req.should.be.form_data
  452. req.content_length.should.equal input.size
  453. req.media_type.should.equal 'multipart/form-data'
  454. req.media_type_params.should.include 'boundary'
  455. req.media_type_params['boundary'].should.equal 'AaB03x'
  456. req.POST["reply"].should.equal "yes"
  457. f = req.POST["fileupload"]
  458. f.should.be.kind_of Hash
  459. f[:type].should.equal "image/jpeg"
  460. f[:filename].should.equal "dj.jpg"
  461. f.should.include :tempfile
  462. f[:tempfile].size.should.equal 76
  463. end
  464. should "not infinite loop with a malformed HTTP request" do
  465. # Adapted from RFC 1867.
  466. input = <<EOF
  467. --AaB03x
  468. content-disposition: form-data; name="reply"
  469. yes
  470. --AaB03x
  471. content-disposition: form-data; name="fileupload"; filename="dj.jpg"
  472. Content-Type: image/jpeg
  473. Content-Transfer-Encoding: base64
  474. /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
  475. --AaB03x--
  476. EOF
  477. req = Rack::Request.new Rack::MockRequest.env_for("/",
  478. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  479. "CONTENT_LENGTH" => input.size,
  480. :input => input)
  481. lambda{req.POST}.should.raise(EOFError)
  482. end
  483. should "parse multipart form data" do
  484. # Adapted from RFC 1867.
  485. input = <<EOF
  486. --AaB03x\r
  487. content-disposition: form-data; name="reply"\r
  488. \r
  489. yes\r
  490. --AaB03x\r
  491. content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
  492. Content-Type: image/jpeg\r
  493. Content-Transfer-Encoding: base64\r
  494. \r
  495. /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
  496. --AaB03x--\r
  497. EOF
  498. req = Rack::Request.new Rack::MockRequest.env_for("/",
  499. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  500. "CONTENT_LENGTH" => input.size,
  501. :input => input)
  502. req.POST.should.include "fileupload"
  503. req.POST.should.include "reply"
  504. req.should.be.form_data
  505. req.content_length.should.equal input.size
  506. req.media_type.should.equal 'multipart/form-data'
  507. req.media_type_params.should.include 'boundary'
  508. req.media_type_params['boundary'].should.equal 'AaB03x'
  509. req.POST["reply"].should.equal "yes"
  510. f = req.POST["fileupload"]
  511. f.should.be.kind_of Hash
  512. f[:type].should.equal "image/jpeg"
  513. f[:filename].should.equal "dj.jpg"
  514. f.should.include :tempfile
  515. f[:tempfile].size.should.equal 76
  516. end
  517. should "parse big multipart form data" do
  518. input = <<EOF
  519. --AaB03x\r
  520. content-disposition: form-data; name="huge"; filename="huge"\r
  521. \r
  522. #{"x"*32768}\r
  523. --AaB03x\r
  524. content-disposition: form-data; name="mean"; filename="mean"\r
  525. \r
  526. --AaB03xha\r
  527. --AaB03x--\r
  528. EOF
  529. req = Rack::Request.new Rack::MockRequest.env_for("/",
  530. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  531. "CONTENT_LENGTH" => input.size,
  532. :input => input)
  533. req.POST["huge"][:tempfile].size.should.equal 32768
  534. req.POST["mean"][:tempfile].size.should.equal 10
  535. req.POST["mean"][:tempfile].read.should.equal "--AaB03xha"
  536. end
  537. should "detect invalid multipart form data" do
  538. input = <<EOF
  539. --AaB03x\r
  540. content-disposition: form-data; name="huge"; filename="huge"\r
  541. EOF
  542. req = Rack::Request.new Rack::MockRequest.env_for("/",
  543. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  544. "CONTENT_LENGTH" => input.size,
  545. :input => input)
  546. lambda { req.POST }.should.raise(EOFError)
  547. input = <<EOF
  548. --AaB03x\r
  549. content-disposition: form-data; name="huge"; filename="huge"\r
  550. \r
  551. foo\r
  552. EOF
  553. req = Rack::Request.new Rack::MockRequest.env_for("/",
  554. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  555. "CONTENT_LENGTH" => input.size,
  556. :input => input)
  557. lambda { req.POST }.should.raise(EOFError)
  558. input = <<EOF
  559. --AaB03x\r
  560. content-disposition: form-data; name="huge"; filename="huge"\r
  561. \r
  562. foo\r
  563. EOF
  564. req = Rack::Request.new Rack::MockRequest.env_for("/",
  565. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  566. "CONTENT_LENGTH" => input.size,
  567. :input => input)
  568. lambda { req.POST }.should.raise(EOFError)
  569. end
  570. should "correctly parse the part name from Content-Id header" do
  571. input = <<EOF
  572. --AaB03x\r
  573. Content-Type: text/xml; charset=utf-8\r
  574. Content-Id: <soap-start>\r
  575. Content-Transfer-Encoding: 7bit\r
  576. \r
  577. foo\r
  578. --AaB03x--\r
  579. EOF
  580. req = Rack::Request.new Rack::MockRequest.env_for("/",
  581. "CONTENT_TYPE" => "multipart/related, boundary=AaB03x",
  582. "CONTENT_LENGTH" => input.size,
  583. :input => input)
  584. req.params.keys.should.equal ["<soap-start>"]
  585. end
  586. should "not try to interpret binary as utf8" do
  587. if /regexp/.respond_to?(:kcode) # < 1.9
  588. begin
  589. original_kcode = $KCODE
  590. $KCODE='UTF8'
  591. input = <<EOF
  592. --AaB03x\r
  593. content-disposition: form-data; name="fileupload"; filename="junk.a"\r
  594. content-type: application/octet-stream\r
  595. \r
  596. #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
  597. --AaB03x--\r
  598. EOF
  599. req = Rack::Request.new Rack::MockRequest.env_for("/",
  600. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  601. "CONTENT_LENGTH" => input.size,
  602. :input => input)
  603. lambda{req.POST}.should.not.raise(EOFError)
  604. req.POST["fileupload"][:tempfile].size.should.equal 4
  605. ensure
  606. $KCODE = original_kcode
  607. end
  608. else # >= 1.9
  609. input = <<EOF
  610. --AaB03x\r
  611. content-disposition: form-data; name="fileupload"; filename="junk.a"\r
  612. content-type: application/octet-stream\r
  613. \r
  614. #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
  615. --AaB03x--\r
  616. EOF
  617. req = Rack::Request.new Rack::MockRequest.env_for("/",
  618. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  619. "CONTENT_LENGTH" => input.size,
  620. :input => input)
  621. lambda{req.POST}.should.not.raise(EOFError)
  622. req.POST["fileupload"][:tempfile].size.should.equal 4
  623. end
  624. end
  625. should "work around buggy 1.8.* Tempfile equality" do
  626. input = <<EOF
  627. --AaB03x\r
  628. content-disposition: form-data; name="huge"; filename="huge"\r
  629. \r
  630. foo\r
  631. --AaB03x--
  632. EOF
  633. rack_input = Tempfile.new("rackspec")
  634. rack_input.write(input)
  635. rack_input.rewind
  636. req = Rack::Request.new Rack::MockRequest.env_for("/",
  637. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  638. "CONTENT_LENGTH" => input.size,
  639. :input => rack_input)
  640. lambda{ req.POST }.should.not.raise
  641. lambda{ req.POST }.should.not.raise("input re-processed!")
  642. end
  643. should "conform to the Rack spec" do
  644. app = lambda { |env|
  645. content = Rack::Request.new(env).POST["file"].inspect
  646. size = content.respond_to?(:bytesize) ? content.bytesize : content.size
  647. [200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]]
  648. }
  649. input = <<EOF
  650. --AaB03x\r
  651. content-disposition: form-data; name="reply"\r
  652. \r
  653. yes\r
  654. --AaB03x\r
  655. content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
  656. Content-Type: image/jpeg\r
  657. Content-Transfer-Encoding: base64\r
  658. \r
  659. /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
  660. --AaB03x--\r
  661. EOF
  662. input.force_encoding("ASCII-8BIT") if input.respond_to? :force_encoding
  663. res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/",
  664. "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
  665. "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input)
  666. res.should.be.ok
  667. end
  668. should "parse Accept-Encoding correctly" do
  669. parser = lambda do |x|
  670. Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
  671. end
  672. parser.call(nil).should.equal([])
  673. parser.call("compress, gzip").should.equal([["compress", 1.0], ["gzip", 1.0]])
  674. parser.call("").should.equal([])
  675. parser.call("*").should.equal([["*", 1.0]])
  676. parser.call("compress;q=0.5, gzip;q=1.0").should.equal([["compress", 0.5], ["gzip", 1.0]])
  677. parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").should.equal([["gzip", 1.0], ["identity", 0.5], ["*", 0] ])
  678. parser.call("gzip ; q=0.9").should.equal([["gzip", 0.9]])
  679. parser.call("gzip ; deflate").should.equal([["gzip", 1.0]])
  680. end
  681. ip_app = lambda { |env|
  682. request = Rack::Request.new(env)
  683. response = Rack::Response.new
  684. response.write request.ip
  685. response.finish
  686. }
  687. should 'provide ip information' do
  688. mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
  689. res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4'
  690. res.body.should.equal '1.2.3.4'
  691. res = mock.get '/', 'REMOTE_ADDR' => 'fe80::202:b3ff:fe1e:8329'
  692. res.body.should.equal 'fe80::202:b3ff:fe1e:8329'
  693. res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6'
  694. res.body.should.equal '1.2.3.4'
  695. end
  696. should 'deals with proxies' do
  697. mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
  698. res = mock.get '/',
  699. 'REMOTE_ADDR' => '1.2.3.4',
  700. 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
  701. res.body.should.equal '1.2.3.4'
  702. res = mock.get '/',
  703. 'REMOTE_ADDR' => '127.0.0.1',
  704. 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
  705. res.body.should.equal '3.4.5.6'
  706. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6'
  707. res.body.should.equal '3.4.5.6'
  708. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6'
  709. res.body.should.equal '3.4.5.6'
  710. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6'
  711. res.body.should.equal '3.4.5.6'
  712. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6'
  713. res.body.should.equal '3.4.5.6'
  714. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '127.0.0.1, 3.4.5.6'
  715. res.body.should.equal '3.4.5.6'
  716. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
  717. res.body.should.equal 'unknown'
  718. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'other,unknown,192.168.0.1'
  719. res.body.should.equal 'unknown'
  720. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,localhost,192.168.0.1'
  721. res.body.should.equal 'unknown'
  722. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
  723. res.body.should.equal '3.4.5.6'
  724. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '::1,2620:0:1c00:0:812c:9583:754b:ca11'
  725. res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
  726. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,::1'
  727. res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
  728. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'fd5b:982e:9130:247f:0000:0000:0000:0000,2620:0:1c00:0:812c:9583:754b:ca11'
  729. res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
  730. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,fd5b:982e:9130:247f:0000:0000:0000:0000'
  731. res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
  732. res = mock.get '/',
  733. 'HTTP_X_FORWARDED_FOR' => '1.1.1.1, 127.0.0.1',
  734. 'HTTP_CLIENT_IP' => '1.1.1.1'
  735. res.body.should.equal '1.1.1.1'
  736. # Spoofing attempt
  737. res = mock.get '/',
  738. 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
  739. 'HTTP_CLIENT_IP' => '2.2.2.2'
  740. res.body.should.equal '1.1.1.1'
  741. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
  742. res.body.should.equal '9.9.9.9'
  743. res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, fe80::202:b3ff:fe1e:8329'
  744. res.body.should.equal 'fe80::202:b3ff:fe1e:8329'
  745. end
  746. class MyRequest < Rack::Request
  747. def params
  748. {:foo => "bar"}
  749. end
  750. end
  751. should "allow subclass request to be instantiated after parent request" do
  752. env = Rack::MockRequest.env_for("/?foo=bar")
  753. req1 = Rack::Request.new(env)
  754. req1.GET.should.equal "foo" => "bar"
  755. req1.params.should.equal "foo" => "bar"
  756. req2 = MyRequest.new(env)
  757. req2.GET.should.equal "foo" => "bar"
  758. req2.params.should.equal :foo => "bar"
  759. end
  760. should "allow parent request to be instantiated after subclass request" do
  761. env = Rack::MockRequest.env_for("/?foo=bar")
  762. req1 = MyRequest.new(env)
  763. req1.GET.should.equal "foo" => "bar"
  764. req1.params.should.equal :foo => "bar"
  765. req2 = Rack::Request.new(env)
  766. req2.GET.should.equal "foo" => "bar"
  767. req2.params.should.equal "foo" => "bar"
  768. end
  769. (0x20...0x7E).collect { |a|
  770. b = a.chr
  771. c = CGI.escape(b)
  772. should "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do
  773. url = "/?foo=#{c}bar#{c}"
  774. env = Rack::MockRequest.env_for(url)
  775. req2 = Rack::Request.new(env)
  776. req2.GET.should.equal "foo" => "#{b}bar#{b}"
  777. req2.params.should.equal "foo" => "#{b}bar#{b}"
  778. end
  779. }
  780. end