actionpack_csi 2.3.5.p6
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.
- data/CHANGELOG +5184 -0
- data/MIT-LICENSE +21 -0
- data/README +409 -0
- data/RUNNING_UNIT_TESTS +24 -0
- data/Rakefile +160 -0
- data/install.rb +30 -0
- data/lib/action_controller/assertions/dom_assertions.rb +55 -0
- data/lib/action_controller/assertions/model_assertions.rb +21 -0
- data/lib/action_controller/assertions/response_assertions.rb +160 -0
- data/lib/action_controller/assertions/routing_assertions.rb +146 -0
- data/lib/action_controller/assertions/selector_assertions.rb +638 -0
- data/lib/action_controller/assertions/tag_assertions.rb +127 -0
- data/lib/action_controller/base.rb +1423 -0
- data/lib/action_controller/benchmarking.rb +107 -0
- data/lib/action_controller/caching/actions.rb +177 -0
- data/lib/action_controller/caching/fragments.rb +120 -0
- data/lib/action_controller/caching/pages.rb +152 -0
- data/lib/action_controller/caching/sweeper.rb +45 -0
- data/lib/action_controller/caching/sweeping.rb +55 -0
- data/lib/action_controller/caching.rb +71 -0
- data/lib/action_controller/cgi_ext/cookie.rb +112 -0
- data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
- data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
- data/lib/action_controller/cgi_ext.rb +15 -0
- data/lib/action_controller/cgi_process.rb +77 -0
- data/lib/action_controller/cookies.rb +95 -0
- data/lib/action_controller/dispatcher.rb +133 -0
- data/lib/action_controller/failsafe.rb +86 -0
- data/lib/action_controller/filters.rb +680 -0
- data/lib/action_controller/flash.rb +171 -0
- data/lib/action_controller/headers.rb +33 -0
- data/lib/action_controller/helpers.rb +225 -0
- data/lib/action_controller/http_authentication.rb +309 -0
- data/lib/action_controller/integration.rb +692 -0
- data/lib/action_controller/layout.rb +286 -0
- data/lib/action_controller/middleware_stack.rb +119 -0
- data/lib/action_controller/middlewares.rb +14 -0
- data/lib/action_controller/mime_responds.rb +193 -0
- data/lib/action_controller/mime_type.rb +212 -0
- data/lib/action_controller/mime_types.rb +21 -0
- data/lib/action_controller/params_parser.rb +77 -0
- data/lib/action_controller/performance_test.rb +15 -0
- data/lib/action_controller/polymorphic_routes.rb +189 -0
- data/lib/action_controller/rack_lint_patch.rb +36 -0
- data/lib/action_controller/record_identifier.rb +104 -0
- data/lib/action_controller/reloader.rb +54 -0
- data/lib/action_controller/request.rb +493 -0
- data/lib/action_controller/request_forgery_protection.rb +113 -0
- data/lib/action_controller/rescue.rb +183 -0
- data/lib/action_controller/resources.rb +682 -0
- data/lib/action_controller/response.rb +239 -0
- data/lib/action_controller/routing/builder.rb +197 -0
- data/lib/action_controller/routing/optimisations.rb +130 -0
- data/lib/action_controller/routing/recognition_optimisation.rb +167 -0
- data/lib/action_controller/routing/route.rb +265 -0
- data/lib/action_controller/routing/route_set.rb +502 -0
- data/lib/action_controller/routing/routing_ext.rb +49 -0
- data/lib/action_controller/routing/segments.rb +343 -0
- data/lib/action_controller/routing.rb +388 -0
- data/lib/action_controller/session/abstract_store.rb +181 -0
- data/lib/action_controller/session/cookie_store.rb +221 -0
- data/lib/action_controller/session/mem_cache_store.rb +51 -0
- data/lib/action_controller/session_management.rb +54 -0
- data/lib/action_controller/status_codes.rb +88 -0
- data/lib/action_controller/streaming.rb +181 -0
- data/lib/action_controller/string_coercion.rb +29 -0
- data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_controller/templates/rescues/_trace.erb +26 -0
- data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
- data/lib/action_controller/templates/rescues/layout.erb +29 -0
- data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
- data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
- data/lib/action_controller/templates/rescues/template_error.erb +21 -0
- data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
- data/lib/action_controller/test_case.rb +209 -0
- data/lib/action_controller/test_process.rb +580 -0
- data/lib/action_controller/translation.rb +13 -0
- data/lib/action_controller/uploaded_file.rb +44 -0
- data/lib/action_controller/url_rewriter.rb +216 -0
- data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
- data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
- data/lib/action_controller/vendor/html-scanner.rb +16 -0
- data/lib/action_controller/verification.rb +130 -0
- data/lib/action_controller.rb +113 -0
- data/lib/action_pack/version.rb +9 -0
- data/lib/action_pack.rb +24 -0
- data/lib/action_view/base.rb +362 -0
- data/lib/action_view/erb/util.rb +44 -0
- data/lib/action_view/helpers/active_record_helper.rb +305 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +694 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
- data/lib/action_view/helpers/benchmark_helper.rb +54 -0
- data/lib/action_view/helpers/cache_helper.rb +39 -0
- data/lib/action_view/helpers/capture_helper.rb +136 -0
- data/lib/action_view/helpers/date_helper.rb +988 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +1074 -0
- data/lib/action_view/helpers/form_options_helper.rb +600 -0
- data/lib/action_view/helpers/form_tag_helper.rb +487 -0
- data/lib/action_view/helpers/javascript_helper.rb +208 -0
- data/lib/action_view/helpers/number_helper.rb +308 -0
- data/lib/action_view/helpers/prototype_helper.rb +1305 -0
- data/lib/action_view/helpers/raw_output_helper.rb +9 -0
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +58 -0
- data/lib/action_view/helpers/sanitize_helper.rb +259 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
- data/lib/action_view/helpers/tag_helper.rb +150 -0
- data/lib/action_view/helpers/text_helper.rb +587 -0
- data/lib/action_view/helpers/translation_helper.rb +39 -0
- data/lib/action_view/helpers/url_helper.rb +639 -0
- data/lib/action_view/helpers.rb +59 -0
- data/lib/action_view/inline_template.rb +19 -0
- data/lib/action_view/locale/en.yml +117 -0
- data/lib/action_view/partials.rb +240 -0
- data/lib/action_view/paths.rb +69 -0
- data/lib/action_view/reloadable_template.rb +117 -0
- data/lib/action_view/renderable.rb +95 -0
- data/lib/action_view/renderable_partial.rb +47 -0
- data/lib/action_view/safe_buffer.rb +28 -0
- data/lib/action_view/template.rb +252 -0
- data/lib/action_view/template_error.rb +99 -0
- data/lib/action_view/template_handler.rb +34 -0
- data/lib/action_view/template_handlers/builder.rb +17 -0
- data/lib/action_view/template_handlers/erb.rb +22 -0
- data/lib/action_view/template_handlers/rjs.rb +13 -0
- data/lib/action_view/template_handlers.rb +48 -0
- data/lib/action_view/test_case.rb +162 -0
- data/lib/action_view.rb +58 -0
- data/lib/actionpack.rb +2 -0
- data/test/abstract_unit.rb +61 -0
- data/test/active_record_unit.rb +104 -0
- data/test/activerecord/active_record_store_test.rb +174 -0
- data/test/activerecord/render_partial_with_record_identification_test.rb +188 -0
- data/test/adv_attr_test.rb +20 -0
- data/test/controller/action_pack_assertions_test.rb +543 -0
- data/test/controller/addresses_render_test.rb +37 -0
- data/test/controller/assert_select_test.rb +734 -0
- data/test/controller/base_test.rb +217 -0
- data/test/controller/benchmark_test.rb +32 -0
- data/test/controller/caching_test.rb +729 -0
- data/test/controller/capture_test.rb +66 -0
- data/test/controller/content_type_test.rb +168 -0
- data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
- data/test/controller/cookie_test.rb +134 -0
- data/test/controller/deprecation/deprecated_base_methods_test.rb +32 -0
- data/test/controller/dispatcher_test.rb +144 -0
- data/test/controller/dom_assertions_test.rb +53 -0
- data/test/controller/failsafe_test.rb +60 -0
- data/test/controller/fake_controllers.rb +33 -0
- data/test/controller/fake_models.rb +19 -0
- data/test/controller/filter_params_test.rb +52 -0
- data/test/controller/filters_test.rb +885 -0
- data/test/controller/flash_test.rb +147 -0
- data/test/controller/header_test.rb +14 -0
- data/test/controller/helper_test.rb +224 -0
- data/test/controller/html-scanner/cdata_node_test.rb +15 -0
- data/test/controller/html-scanner/document_test.rb +148 -0
- data/test/controller/html-scanner/node_test.rb +89 -0
- data/test/controller/html-scanner/sanitizer_test.rb +274 -0
- data/test/controller/html-scanner/tag_node_test.rb +238 -0
- data/test/controller/html-scanner/text_node_test.rb +50 -0
- data/test/controller/html-scanner/tokenizer_test.rb +131 -0
- data/test/controller/http_basic_authentication_test.rb +113 -0
- data/test/controller/http_digest_authentication_test.rb +254 -0
- data/test/controller/integration_test.rb +483 -0
- data/test/controller/layout_test.rb +215 -0
- data/test/controller/logging_test.rb +46 -0
- data/test/controller/middleware_stack_test.rb +90 -0
- data/test/controller/mime_responds_test.rb +536 -0
- data/test/controller/mime_type_test.rb +93 -0
- data/test/controller/polymorphic_routes_test.rb +297 -0
- data/test/controller/rack_test.rb +311 -0
- data/test/controller/record_identifier_test.rb +139 -0
- data/test/controller/redirect_test.rb +285 -0
- data/test/controller/reloader_test.rb +124 -0
- data/test/controller/render_test.rb +1762 -0
- data/test/controller/request/json_params_parsing_test.rb +65 -0
- data/test/controller/request/multipart_params_parsing_test.rb +162 -0
- data/test/controller/request/query_string_parsing_test.rb +120 -0
- data/test/controller/request/test_request_test.rb +35 -0
- data/test/controller/request/url_encoded_params_parsing_test.rb +146 -0
- data/test/controller/request/xml_params_parsing_test.rb +103 -0
- data/test/controller/request_forgery_protection_test.rb +265 -0
- data/test/controller/request_test.rb +395 -0
- data/test/controller/rescue_test.rb +536 -0
- data/test/controller/resources_test.rb +1393 -0
- data/test/controller/routing_test.rb +2591 -0
- data/test/controller/selector_test.rb +628 -0
- data/test/controller/send_file_test.rb +171 -0
- data/test/controller/session/cookie_store_test.rb +216 -0
- data/test/controller/session/mem_cache_store_test.rb +127 -0
- data/test/controller/session/test_session_test.rb +58 -0
- data/test/controller/test_test.rb +700 -0
- data/test/controller/translation_test.rb +26 -0
- data/test/controller/url_rewriter_test.rb +385 -0
- data/test/controller/verification_test.rb +270 -0
- data/test/controller/view_paths_test.rb +141 -0
- data/test/controller/webservice_test.rb +273 -0
- data/test/fixtures/_top_level_partial.html.erb +1 -0
- data/test/fixtures/_top_level_partial_only.erb +1 -0
- data/test/fixtures/addresses/list.erb +1 -0
- data/test/fixtures/alternate_helpers/foo_helper.rb +3 -0
- data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
- data/test/fixtures/companies.yml +24 -0
- data/test/fixtures/company.rb +10 -0
- data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
- data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
- data/test/fixtures/customers/_customer.html.erb +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +49 -0
- data/test/fixtures/developer.rb +9 -0
- data/test/fixtures/developers/_developer.erb +1 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +13 -0
- data/test/fixtures/failsafe/500.html +1 -0
- data/test/fixtures/fun/games/_game.erb +1 -0
- data/test/fixtures/fun/games/hello_world.erb +1 -0
- data/test/fixtures/fun/serious/games/_game.erb +1 -0
- data/test/fixtures/functional_caching/_partial.erb +3 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
- data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
- data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
- data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
- data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
- data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
- data/test/fixtures/helpers/abc_helper.rb +5 -0
- data/test/fixtures/helpers/fun/games_helper.rb +3 -0
- data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
- data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/alt/layouts/alt.rhtml +0 -0
- data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
- data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
- data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
- data/test/fixtures/layouts/_column.html.erb +2 -0
- data/test/fixtures/layouts/block_with_layout.erb +3 -0
- data/test/fixtures/layouts/builder.builder +3 -0
- data/test/fixtures/layouts/default_html.html.erb +1 -0
- data/test/fixtures/layouts/partial_with_layout.erb +3 -0
- data/test/fixtures/layouts/standard.erb +1 -0
- data/test/fixtures/layouts/talk_from_action.erb +2 -0
- data/test/fixtures/layouts/xhr.html.erb +2 -0
- data/test/fixtures/layouts/yield.erb +2 -0
- data/test/fixtures/mascot.rb +3 -0
- data/test/fixtures/mascots/_mascot.html.erb +1 -0
- data/test/fixtures/mascots.yml +4 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/boundary_problem_file +10 -0
- data/test/fixtures/multipart/bracketed_param +5 -0
- data/test/fixtures/multipart/empty +10 -0
- data/test/fixtures/multipart/hello.txt +1 -0
- data/test/fixtures/multipart/large_text_file +10 -0
- data/test/fixtures/multipart/mixed_files +0 -0
- data/test/fixtures/multipart/mona_lisa.jpg +0 -0
- data/test/fixtures/multipart/none +9 -0
- data/test/fixtures/multipart/single_parameter +5 -0
- data/test/fixtures/multipart/text_file +10 -0
- data/test/fixtures/override/test/hello_world.erb +1 -0
- data/test/fixtures/override2/layouts/test/sub.erb +1 -0
- data/test/fixtures/post_test/layouts/post.html.erb +1 -0
- data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
- data/test/fixtures/post_test/post/index.html.erb +1 -0
- data/test/fixtures/post_test/post/index.iphone.erb +1 -0
- data/test/fixtures/post_test/super_post/index.html.erb +1 -0
- data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
- data/test/fixtures/project.rb +3 -0
- data/test/fixtures/projects/_project.erb +1 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/public/404.html +1 -0
- data/test/fixtures/public/500.da.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/absolute/test.css +23 -0
- data/test/fixtures/public/absolute/test.js +63 -0
- data/test/fixtures/public/images/rails.png +0 -0
- data/test/fixtures/public/javascripts/application.js +1 -0
- data/test/fixtures/public/javascripts/bank.js +1 -0
- data/test/fixtures/public/javascripts/controls.js +1 -0
- data/test/fixtures/public/javascripts/dragdrop.js +1 -0
- data/test/fixtures/public/javascripts/effects.js +1 -0
- data/test/fixtures/public/javascripts/prototype.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
- data/test/fixtures/public/javascripts/version.1.0.js +1 -0
- data/test/fixtures/public/stylesheets/bank.css +1 -0
- data/test/fixtures/public/stylesheets/robber.css +1 -0
- data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
- data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
- data/test/fixtures/quiz/questions/_question.html.erb +1 -0
- data/test/fixtures/replies/_reply.erb +1 -0
- data/test/fixtures/replies.yml +15 -0
- data/test/fixtures/reply.rb +7 -0
- data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
- data/test/fixtures/respond_to/all_types_with_layout.js.rjs +1 -0
- data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
- data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
- data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
- data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
- data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
- data/test/fixtures/scope/test/modgreet.erb +1 -0
- data/test/fixtures/shared.html.erb +1 -0
- data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
- data/test/fixtures/test/_counter.html.erb +1 -0
- data/test/fixtures/test/_customer.erb +1 -0
- data/test/fixtures/test/_customer_counter.erb +1 -0
- data/test/fixtures/test/_customer_greeting.erb +1 -0
- data/test/fixtures/test/_customer_with_var.erb +1 -0
- data/test/fixtures/test/_form.erb +1 -0
- data/test/fixtures/test/_from_helper.erb +1 -0
- data/test/fixtures/test/_hash_greeting.erb +1 -0
- data/test/fixtures/test/_hash_object.erb +2 -0
- data/test/fixtures/test/_hello.builder +1 -0
- data/test/fixtures/test/_labelling_form.erb +1 -0
- data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
- data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
- data/test/fixtures/test/_local_inspector.html.erb +1 -0
- data/test/fixtures/test/_one.html.erb +1 -0
- data/test/fixtures/test/_partial.erb +1 -0
- data/test/fixtures/test/_partial.html.erb +1 -0
- data/test/fixtures/test/_partial.js.erb +1 -0
- data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
- data/test/fixtures/test/_partial_only.erb +1 -0
- data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
- data/test/fixtures/test/_person.erb +2 -0
- data/test/fixtures/test/_raise.html.erb +1 -0
- data/test/fixtures/test/_two.html.erb +1 -0
- data/test/fixtures/test/action_talk_to_layout.erb +2 -0
- data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
- data/test/fixtures/test/capturing.erb +4 -0
- data/test/fixtures/test/content_for.erb +2 -0
- data/test/fixtures/test/content_for_concatenated.erb +3 -0
- data/test/fixtures/test/content_for_with_parameter.erb +2 -0
- data/test/fixtures/test/delete_with_js.rjs +2 -0
- data/test/fixtures/test/dont_pick_me +1 -0
- data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
- data/test/fixtures/test/enum_rjs_test.rjs +6 -0
- data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.builder +1 -0
- data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
- data/test/fixtures/test/greeting.erb +1 -0
- data/test/fixtures/test/greeting.js.rjs +1 -0
- data/test/fixtures/test/hello.builder +4 -0
- data/test/fixtures/test/hello_world.da.html.erb +1 -0
- data/test/fixtures/test/hello_world.erb +1 -0
- data/test/fixtures/test/hello_world.erb~ +1 -0
- data/test/fixtures/test/hello_world.pt-BR.html.erb +1 -0
- data/test/fixtures/test/hello_world_container.builder +3 -0
- data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
- data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
- data/test/fixtures/test/hello_xml_world.builder +11 -0
- data/test/fixtures/test/hyphen-ated.erb +1 -0
- data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/malformed/malformed.en.html.erb~ +1 -0
- data/test/fixtures/test/malformed/malformed.erb~ +1 -0
- data/test/fixtures/test/malformed/malformed.html.erb~ +1 -0
- data/test/fixtures/test/nested_layout.erb +3 -0
- data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
- data/test/fixtures/test/potential_conflicts.erb +4 -0
- data/test/fixtures/test/render_explicit_html_template.js.rjs +1 -0
- data/test/fixtures/test/render_file_from_template.html.erb +1 -0
- data/test/fixtures/test/render_file_with_ivar.erb +1 -0
- data/test/fixtures/test/render_file_with_locals.erb +1 -0
- data/test/fixtures/test/render_implicit_html_template.js.rjs +1 -0
- data/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +1 -0
- data/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +1 -0
- data/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +1 -0
- data/test/fixtures/test/render_to_string_test.erb +1 -0
- data/test/fixtures/test/sub_template_raise.html.erb +1 -0
- data/test/fixtures/test/template.erb +1 -0
- data/test/fixtures/test/update_element_with_capture.erb +9 -0
- data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
- data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
- data/test/fixtures/test/utf8.html.erb +2 -0
- data/test/fixtures/topic.rb +3 -0
- data/test/fixtures/topics/_topic.html.erb +1 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/template/active_record_helper_i18n_test.rb +44 -0
- data/test/template/active_record_helper_test.rb +302 -0
- data/test/template/asset_tag_helper_test.rb +771 -0
- data/test/template/atom_feed_helper_test.rb +315 -0
- data/test/template/benchmark_helper_test.rb +86 -0
- data/test/template/compiled_templates_test.rb +204 -0
- data/test/template/date_helper_i18n_test.rb +121 -0
- data/test/template/date_helper_test.rb +2485 -0
- data/test/template/erb_util_test.rb +24 -0
- data/test/template/form_helper_test.rb +1393 -0
- data/test/template/form_options_helper_i18n_test.rb +27 -0
- data/test/template/form_options_helper_test.rb +807 -0
- data/test/template/form_tag_helper_test.rb +344 -0
- data/test/template/javascript_helper_test.rb +106 -0
- data/test/template/number_helper_i18n_test.rb +69 -0
- data/test/template/number_helper_test.rb +132 -0
- data/test/template/prototype_helper_test.rb +639 -0
- data/test/template/raw_output_helper_test.rb +21 -0
- data/test/template/record_tag_helper_test.rb +58 -0
- data/test/template/render_test.rb +290 -0
- data/test/template/sanitize_helper_test.rb +57 -0
- data/test/template/scriptaculous_helper_test.rb +90 -0
- data/test/template/tag_helper_test.rb +98 -0
- data/test/template/template_test.rb +32 -0
- data/test/template/test_test.rb +54 -0
- data/test/template/text_helper_test.rb +543 -0
- data/test/template/translation_helper_test.rb +32 -0
- data/test/template/url_helper_test.rb +622 -0
- data/test/testing_sandbox.rb +15 -0
- data/test/view/safe_buffer_test.rb +36 -0
- data/test/view/test_case_test.rb +176 -0
- metadata +531 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module ActionController
|
|
4
|
+
class Reloader
|
|
5
|
+
@@default_lock = Mutex.new
|
|
6
|
+
cattr_accessor :default_lock
|
|
7
|
+
|
|
8
|
+
class BodyWrapper
|
|
9
|
+
def initialize(body, lock)
|
|
10
|
+
@body = body
|
|
11
|
+
@lock = lock
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def close
|
|
15
|
+
@body.close if @body.respond_to?(:close)
|
|
16
|
+
ensure
|
|
17
|
+
Dispatcher.cleanup_application
|
|
18
|
+
@lock.unlock
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def method_missing(*args, &block)
|
|
22
|
+
@body.send(*args, &block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def respond_to?(symbol, include_private = false)
|
|
26
|
+
symbol == :close || @body.respond_to?(symbol, include_private)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.run(lock = @@default_lock)
|
|
31
|
+
lock.lock
|
|
32
|
+
begin
|
|
33
|
+
Dispatcher.reload_application
|
|
34
|
+
status, headers, body = yield
|
|
35
|
+
# We do not want to call 'cleanup_application' in an ensure block
|
|
36
|
+
# because the returned Rack response body may lazily generate its data. This
|
|
37
|
+
# is for example the case if one calls
|
|
38
|
+
#
|
|
39
|
+
# render :text => lambda { ... code here which refers to application models ... }
|
|
40
|
+
#
|
|
41
|
+
# in an ActionController.
|
|
42
|
+
#
|
|
43
|
+
# Instead, we will want to cleanup the application code after the request is
|
|
44
|
+
# completely finished. So we wrap the body in a BodyWrapper class so that
|
|
45
|
+
# when the Rack handler calls #close during the end of the request, we get to
|
|
46
|
+
# run our cleanup code.
|
|
47
|
+
[status, headers, BodyWrapper.new(body, lock)]
|
|
48
|
+
rescue Exception
|
|
49
|
+
lock.unlock
|
|
50
|
+
raise
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
require 'tempfile'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
require 'strscan'
|
|
4
|
+
|
|
5
|
+
require 'active_support/memoizable'
|
|
6
|
+
require 'action_controller/cgi_ext'
|
|
7
|
+
|
|
8
|
+
module ActionController
|
|
9
|
+
class Request < Rack::Request
|
|
10
|
+
|
|
11
|
+
%w[ AUTH_TYPE GATEWAY_INTERFACE
|
|
12
|
+
PATH_TRANSLATED REMOTE_HOST
|
|
13
|
+
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
|
14
|
+
SERVER_NAME SERVER_PROTOCOL
|
|
15
|
+
|
|
16
|
+
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
|
|
17
|
+
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
|
|
18
|
+
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
|
|
19
|
+
define_method(env.sub(/^HTTP_/n, '').downcase) do
|
|
20
|
+
@env[env]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def key?(key)
|
|
25
|
+
@env.key?(key)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
HTTP_METHODS = %w(get head put post delete options)
|
|
29
|
+
HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
|
|
30
|
+
|
|
31
|
+
# Returns the true HTTP request \method as a lowercase symbol, such as
|
|
32
|
+
# <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS
|
|
33
|
+
# constant above, an UnknownHttpMethod exception is raised.
|
|
34
|
+
def request_method
|
|
35
|
+
@request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns the HTTP request \method used for action processing as a
|
|
39
|
+
# lowercase symbol, such as <tt>:post</tt>. (Unlike #request_method, this
|
|
40
|
+
# method returns <tt>:get</tt> for a HEAD request because the two are
|
|
41
|
+
# functionally equivalent from the application's perspective.)
|
|
42
|
+
def method
|
|
43
|
+
request_method == :head ? :get : request_method
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Is this a GET (or HEAD) request? Equivalent to <tt>request.method == :get</tt>.
|
|
47
|
+
def get?
|
|
48
|
+
method == :get
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Is this a POST request? Equivalent to <tt>request.method == :post</tt>.
|
|
52
|
+
def post?
|
|
53
|
+
request_method == :post
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Is this a PUT request? Equivalent to <tt>request.method == :put</tt>.
|
|
57
|
+
def put?
|
|
58
|
+
request_method == :put
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Is this a DELETE request? Equivalent to <tt>request.method == :delete</tt>.
|
|
62
|
+
def delete?
|
|
63
|
+
request_method == :delete
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Is this a HEAD request? Since <tt>request.method</tt> sees HEAD as <tt>:get</tt>,
|
|
67
|
+
# this \method checks the actual HTTP \method directly.
|
|
68
|
+
def head?
|
|
69
|
+
request_method == :head
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Provides access to the request's HTTP headers, for example:
|
|
73
|
+
#
|
|
74
|
+
# request.headers["Content-Type"] # => "text/plain"
|
|
75
|
+
def headers
|
|
76
|
+
@headers ||= ActionController::Http::Headers.new(@env)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns the content length of the request as an integer.
|
|
80
|
+
def content_length
|
|
81
|
+
super.to_i
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The MIME type of the HTTP request, such as Mime::XML.
|
|
85
|
+
#
|
|
86
|
+
# For backward compatibility, the post \format is extracted from the
|
|
87
|
+
# X-Post-Data-Format HTTP header if present.
|
|
88
|
+
def content_type
|
|
89
|
+
@content_type ||= begin
|
|
90
|
+
if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
|
|
91
|
+
Mime::Type.lookup($1.strip.downcase)
|
|
92
|
+
else
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def media_type
|
|
99
|
+
content_type.to_s
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Returns the accepted MIME type for the request.
|
|
103
|
+
def accepts
|
|
104
|
+
@accepts ||= begin
|
|
105
|
+
header = @env['HTTP_ACCEPT'].to_s.strip
|
|
106
|
+
|
|
107
|
+
if header.empty?
|
|
108
|
+
[content_type, Mime::ALL].compact
|
|
109
|
+
else
|
|
110
|
+
Mime::Type.parse(header)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def if_modified_since
|
|
116
|
+
if since = env['HTTP_IF_MODIFIED_SINCE']
|
|
117
|
+
Time.rfc2822(since) rescue nil
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def if_none_match
|
|
122
|
+
env['HTTP_IF_NONE_MATCH']
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def not_modified?(modified_at)
|
|
126
|
+
if_modified_since && modified_at && if_modified_since >= modified_at
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def etag_matches?(etag)
|
|
130
|
+
if_none_match && if_none_match == etag
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Check response freshness (Last-Modified and ETag) against request
|
|
134
|
+
# If-Modified-Since and If-None-Match conditions. If both headers are
|
|
135
|
+
# supplied, both must match, or the request is not considered fresh.
|
|
136
|
+
def fresh?(response)
|
|
137
|
+
case
|
|
138
|
+
when if_modified_since && if_none_match
|
|
139
|
+
not_modified?(response.last_modified) && etag_matches?(response.etag)
|
|
140
|
+
when if_modified_since
|
|
141
|
+
not_modified?(response.last_modified)
|
|
142
|
+
when if_none_match
|
|
143
|
+
etag_matches?(response.etag)
|
|
144
|
+
else
|
|
145
|
+
false
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Returns the Mime type for the \format used in the request.
|
|
150
|
+
#
|
|
151
|
+
# GET /posts/5.xml | request.format => Mime::XML
|
|
152
|
+
# GET /posts/5.xhtml | request.format => Mime::HTML
|
|
153
|
+
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
|
|
154
|
+
def format
|
|
155
|
+
@format ||=
|
|
156
|
+
if parameters[:format]
|
|
157
|
+
Mime::Type.lookup_by_extension(parameters[:format])
|
|
158
|
+
elsif ActionController::Base.use_accept_header
|
|
159
|
+
accepts.first
|
|
160
|
+
elsif xhr?
|
|
161
|
+
Mime::Type.lookup_by_extension("js")
|
|
162
|
+
else
|
|
163
|
+
Mime::Type.lookup_by_extension("html")
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# Sets the \format by string extension, which can be used to force custom formats
|
|
169
|
+
# that are not controlled by the extension.
|
|
170
|
+
#
|
|
171
|
+
# class ApplicationController < ActionController::Base
|
|
172
|
+
# before_filter :adjust_format_for_iphone
|
|
173
|
+
#
|
|
174
|
+
# private
|
|
175
|
+
# def adjust_format_for_iphone
|
|
176
|
+
# request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
|
|
177
|
+
# end
|
|
178
|
+
# end
|
|
179
|
+
def format=(extension)
|
|
180
|
+
parameters[:format] = extension.to_s
|
|
181
|
+
@format = Mime::Type.lookup_by_extension(parameters[:format])
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Returns a symbolized version of the <tt>:format</tt> parameter of the request.
|
|
185
|
+
# If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
|
|
186
|
+
# otherwise.
|
|
187
|
+
def template_format
|
|
188
|
+
parameter_format = parameters[:format]
|
|
189
|
+
|
|
190
|
+
if parameter_format
|
|
191
|
+
parameter_format
|
|
192
|
+
elsif xhr?
|
|
193
|
+
:js
|
|
194
|
+
else
|
|
195
|
+
:html
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def cache_format
|
|
200
|
+
parameters[:format]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Returns true if the request's "X-Requested-With" header contains
|
|
204
|
+
# "XMLHttpRequest". (The Prototype Javascript library sends this header with
|
|
205
|
+
# every Ajax request.)
|
|
206
|
+
def xml_http_request?
|
|
207
|
+
!(@env['HTTP_X_REQUESTED_WITH'] !~ /XMLHttpRequest/i)
|
|
208
|
+
end
|
|
209
|
+
alias xhr? :xml_http_request?
|
|
210
|
+
|
|
211
|
+
# Which IP addresses are "trusted proxies" that can be stripped from
|
|
212
|
+
# the right-hand-side of X-Forwarded-For
|
|
213
|
+
TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
|
|
214
|
+
|
|
215
|
+
# Determines originating IP address. REMOTE_ADDR is the standard
|
|
216
|
+
# but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
|
|
217
|
+
# HTTP_X_FORWARDED_FOR are set by proxies so check for these if
|
|
218
|
+
# REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
|
|
219
|
+
# delimited list in the case of multiple chained proxies; the last
|
|
220
|
+
# address which is not trusted is the originating IP.
|
|
221
|
+
def remote_ip
|
|
222
|
+
remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].scan(/[^,\s]+/)
|
|
223
|
+
|
|
224
|
+
unless remote_addr_list.blank?
|
|
225
|
+
not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES}
|
|
226
|
+
return not_trusted_addrs.first unless not_trusted_addrs.empty?
|
|
227
|
+
end
|
|
228
|
+
remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',')
|
|
229
|
+
|
|
230
|
+
if @env.include? 'HTTP_CLIENT_IP'
|
|
231
|
+
if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
|
|
232
|
+
# We don't know which came from the proxy, and which from the user
|
|
233
|
+
raise ActionControllerError.new(<<EOM)
|
|
234
|
+
IP spoofing attack?!
|
|
235
|
+
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
|
|
236
|
+
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
|
|
237
|
+
EOM
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
return @env['HTTP_CLIENT_IP']
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
if remote_ips
|
|
244
|
+
while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
|
|
245
|
+
remote_ips.pop
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
return remote_ips.last.strip
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
@env['REMOTE_ADDR']
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Returns the lowercase name of the HTTP server software.
|
|
255
|
+
def server_software
|
|
256
|
+
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Returns the complete URL used for this request.
|
|
260
|
+
def url
|
|
261
|
+
protocol + host_with_port + request_uri
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
|
|
265
|
+
def protocol
|
|
266
|
+
ssl? ? 'https://' : 'http://'
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Is this an SSL request?
|
|
270
|
+
def ssl?
|
|
271
|
+
@env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Returns the \host for this request, such as "example.com".
|
|
275
|
+
def raw_host_with_port
|
|
276
|
+
if forwarded = env["HTTP_X_FORWARDED_HOST"]
|
|
277
|
+
forwarded.split(/,\s?/).last
|
|
278
|
+
else
|
|
279
|
+
env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Returns the host for this request, such as example.com.
|
|
284
|
+
def host
|
|
285
|
+
raw_host_with_port.sub(/:\d+$/, '')
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Returns a \host:\port string for this request, such as "example.com" or
|
|
289
|
+
# "example.com:8080".
|
|
290
|
+
def host_with_port
|
|
291
|
+
"#{host}#{port_string}"
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Returns the port number of this request as an integer.
|
|
295
|
+
def port
|
|
296
|
+
if raw_host_with_port =~ /:(\d+)$/
|
|
297
|
+
$1.to_i
|
|
298
|
+
else
|
|
299
|
+
standard_port
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Returns the standard \port number for this request's protocol.
|
|
304
|
+
def standard_port
|
|
305
|
+
case protocol
|
|
306
|
+
when 'https://' then 443
|
|
307
|
+
else 80
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Returns a \port suffix like ":8080" if the \port number of this request
|
|
312
|
+
# is not the default HTTP \port 80 or HTTPS \port 443.
|
|
313
|
+
def port_string
|
|
314
|
+
port == standard_port ? '' : ":#{port}"
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
|
|
318
|
+
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
|
|
319
|
+
def domain(tld_length = 1)
|
|
320
|
+
return nil unless named_host?(host)
|
|
321
|
+
|
|
322
|
+
host.split('.').last(1 + tld_length).join('.')
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
|
|
326
|
+
# returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
|
|
327
|
+
# such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
|
|
328
|
+
# in "www.rubyonrails.co.uk".
|
|
329
|
+
def subdomains(tld_length = 1)
|
|
330
|
+
return [] unless named_host?(host)
|
|
331
|
+
parts = host.split('.')
|
|
332
|
+
parts[0..-(tld_length+2)]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Returns the query string, accounting for server idiosyncrasies.
|
|
336
|
+
def query_string
|
|
337
|
+
@env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '')
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Returns the request URI, accounting for server idiosyncrasies.
|
|
341
|
+
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
|
|
342
|
+
def request_uri
|
|
343
|
+
if uri = @env['REQUEST_URI']
|
|
344
|
+
# Remove domain, which webrick puts into the request_uri.
|
|
345
|
+
(%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
|
|
346
|
+
else
|
|
347
|
+
# Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
|
|
348
|
+
uri = @env['PATH_INFO'].to_s
|
|
349
|
+
|
|
350
|
+
if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
|
|
351
|
+
uri = uri.sub(/#{script_filename}\//, '')
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
env_qs = @env['QUERY_STRING'].to_s
|
|
355
|
+
uri += "?#{env_qs}" unless env_qs.empty?
|
|
356
|
+
|
|
357
|
+
if uri.blank?
|
|
358
|
+
@env.delete('REQUEST_URI')
|
|
359
|
+
else
|
|
360
|
+
@env['REQUEST_URI'] = uri
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Returns the interpreted \path to requested resource after all the installation
|
|
366
|
+
# directory of this application was taken into account.
|
|
367
|
+
def path
|
|
368
|
+
path = request_uri.to_s[/\A[^\?]*/]
|
|
369
|
+
path.sub!(/\A#{ActionController::Base.relative_url_root}/, '')
|
|
370
|
+
path
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Read the request \body. This is useful for web services that need to
|
|
374
|
+
# work with raw requests directly.
|
|
375
|
+
def raw_post
|
|
376
|
+
unless @env.include? 'RAW_POST_DATA'
|
|
377
|
+
@env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i)
|
|
378
|
+
body.rewind if body.respond_to?(:rewind)
|
|
379
|
+
end
|
|
380
|
+
@env['RAW_POST_DATA']
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Returns both GET and POST \parameters in a single hash.
|
|
384
|
+
def parameters
|
|
385
|
+
@parameters ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
|
|
386
|
+
end
|
|
387
|
+
alias_method :params, :parameters
|
|
388
|
+
|
|
389
|
+
def path_parameters=(parameters) #:nodoc:
|
|
390
|
+
@env["action_controller.request.path_parameters"] = parameters
|
|
391
|
+
@symbolized_path_parameters = @parameters = nil
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
|
|
395
|
+
def symbolized_path_parameters
|
|
396
|
+
@symbolized_path_parameters ||= path_parameters.symbolize_keys
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Returns a hash with the \parameters used to form the \path of the request.
|
|
400
|
+
# Returned hash keys are strings:
|
|
401
|
+
#
|
|
402
|
+
# {'action' => 'my_action', 'controller' => 'my_controller'}
|
|
403
|
+
#
|
|
404
|
+
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
|
|
405
|
+
def path_parameters
|
|
406
|
+
@env["action_controller.request.path_parameters"] ||= {}
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
|
410
|
+
# variable is already set, wrap it in a StringIO.
|
|
411
|
+
def body
|
|
412
|
+
if raw_post = @env['RAW_POST_DATA']
|
|
413
|
+
raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
|
|
414
|
+
StringIO.new(raw_post)
|
|
415
|
+
else
|
|
416
|
+
@env['rack.input']
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def form_data?
|
|
421
|
+
FORM_DATA_MEDIA_TYPES.include?(content_type.to_s)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# Override Rack's GET method to support indifferent access
|
|
425
|
+
def GET
|
|
426
|
+
@env["action_controller.request.query_parameters"] ||= normalize_parameters(super)
|
|
427
|
+
end
|
|
428
|
+
alias_method :query_parameters, :GET
|
|
429
|
+
|
|
430
|
+
# Override Rack's POST method to support indifferent access
|
|
431
|
+
def POST
|
|
432
|
+
@env["action_controller.request.request_parameters"] ||= normalize_parameters(super)
|
|
433
|
+
end
|
|
434
|
+
alias_method :request_parameters, :POST
|
|
435
|
+
|
|
436
|
+
def body_stream #:nodoc:
|
|
437
|
+
@env['rack.input']
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def session
|
|
441
|
+
@env['rack.session'] ||= {}
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def session=(session) #:nodoc:
|
|
445
|
+
@env['rack.session'] = session
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def reset_session
|
|
449
|
+
@env['rack.session.options'].delete(:id)
|
|
450
|
+
@env['rack.session'] = {}
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def session_options
|
|
454
|
+
@env['rack.session.options'] ||= {}
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def session_options=(options)
|
|
458
|
+
@env['rack.session.options'] = options
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def server_port
|
|
462
|
+
@env['SERVER_PORT'].to_i
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
private
|
|
466
|
+
def named_host?(host)
|
|
467
|
+
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Convert nested Hashs to HashWithIndifferentAccess and replace
|
|
471
|
+
# file upload hashs with UploadedFile objects
|
|
472
|
+
def normalize_parameters(value)
|
|
473
|
+
case value
|
|
474
|
+
when Hash
|
|
475
|
+
if value.has_key?(:tempfile)
|
|
476
|
+
upload = value[:tempfile]
|
|
477
|
+
upload.extend(UploadedFile)
|
|
478
|
+
upload.original_path = value[:filename]
|
|
479
|
+
upload.content_type = value[:type]
|
|
480
|
+
upload
|
|
481
|
+
else
|
|
482
|
+
h = {}
|
|
483
|
+
value.each { |k, v| h[k] = normalize_parameters(v) }
|
|
484
|
+
h.with_indifferent_access
|
|
485
|
+
end
|
|
486
|
+
when Array
|
|
487
|
+
value.map { |e| normalize_parameters(e) }
|
|
488
|
+
else
|
|
489
|
+
value
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module ActionController #:nodoc:
|
|
2
|
+
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
module RequestForgeryProtection
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.class_eval do
|
|
8
|
+
helper_method :form_authenticity_token
|
|
9
|
+
helper_method :protect_against_forgery?
|
|
10
|
+
end
|
|
11
|
+
base.extend(ClassMethods)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
|
|
15
|
+
# forged link from another site, is done by embedding a token based on a random string stored in the session (which an attacker wouldn't know) in all
|
|
16
|
+
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
|
|
17
|
+
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
|
|
18
|
+
# scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway.
|
|
19
|
+
#
|
|
20
|
+
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
|
|
21
|
+
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
|
|
22
|
+
# production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
|
|
23
|
+
# applications.
|
|
24
|
+
#
|
|
25
|
+
# The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
|
|
26
|
+
# use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
|
|
27
|
+
# set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
|
|
28
|
+
# make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
|
|
29
|
+
#
|
|
30
|
+
# <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
|
|
31
|
+
#
|
|
32
|
+
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
|
|
33
|
+
# config/environments/test.rb:
|
|
34
|
+
#
|
|
35
|
+
# # Disable request forgery protection in test environment
|
|
36
|
+
# config.action_controller.allow_forgery_protection = false
|
|
37
|
+
#
|
|
38
|
+
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
|
|
39
|
+
#
|
|
40
|
+
# Here are some resources:
|
|
41
|
+
# * http://isc.sans.org/diary.html?storyid=1750
|
|
42
|
+
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
|
43
|
+
#
|
|
44
|
+
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
|
45
|
+
# There are a few guidelines you should follow:
|
|
46
|
+
#
|
|
47
|
+
# * Keep your GET requests safe and idempotent. More reading material:
|
|
48
|
+
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
|
49
|
+
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
|
50
|
+
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
|
|
51
|
+
#
|
|
52
|
+
module ClassMethods
|
|
53
|
+
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
|
|
54
|
+
#
|
|
55
|
+
# Example:
|
|
56
|
+
#
|
|
57
|
+
# class FooController < ApplicationController
|
|
58
|
+
# protect_from_forgery :except => :index
|
|
59
|
+
#
|
|
60
|
+
# # you can disable csrf protection on controller-by-controller basis:
|
|
61
|
+
# skip_before_filter :verify_authenticity_token
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# Valid Options:
|
|
65
|
+
#
|
|
66
|
+
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
|
67
|
+
def protect_from_forgery(options = {})
|
|
68
|
+
self.request_forgery_protection_token ||= :authenticity_token
|
|
69
|
+
before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
|
|
70
|
+
if options[:secret] || options[:digest]
|
|
71
|
+
ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
protected
|
|
77
|
+
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
|
78
|
+
def verify_authenticity_token
|
|
79
|
+
verified_request? || raise(ActionController::InvalidAuthenticityToken)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Returns true or false if a request is verified. Checks:
|
|
83
|
+
#
|
|
84
|
+
# * is the format restricted? By default, only HTML requests are checked.
|
|
85
|
+
# * is it a GET request? Gets should be safe and idempotent
|
|
86
|
+
# * Does the form_authenticity_token match the given token value from the params?
|
|
87
|
+
def verified_request?
|
|
88
|
+
!protect_against_forgery? ||
|
|
89
|
+
request.method == :get ||
|
|
90
|
+
request.xhr? ||
|
|
91
|
+
!verifiable_request_format? ||
|
|
92
|
+
form_authenticity_token == form_authenticity_param
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def form_authenticity_param
|
|
96
|
+
params[request_forgery_protection_token]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def verifiable_request_format?
|
|
100
|
+
!request.content_type.nil? && request.content_type.verify_request?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Sets the token value for the current session. Pass a <tt>:secret</tt> option
|
|
104
|
+
# in +protect_from_forgery+ to add a custom salt to the hash.
|
|
105
|
+
def form_authenticity_token
|
|
106
|
+
session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def protect_against_forgery?
|
|
110
|
+
allow_forgery_protection && request_forgery_protection_token
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|