require'active_support/core_ext/class/attribute_accessors'require'monitor'moduleActionDispatch# :nodoc:# Represents an HTTP response generated by a controller action. Use it to# retrieve the current state of the response, or customize the response. It can# either represent a real HTTP response (i.e. one that is meant to be sent# back to the web browser) or a TestResponse (i.e. one that is generated# from integration tests).## \Response is mostly a Ruby on \Rails framework implementation detail, and# should never be used directly in controllers. Controllers should use the# methods defined in ActionController::Base instead. For example, if you want# to set the HTTP response's content MIME type, then use# ActionControllerBase#headers instead of Response#headers.## Nevertheless, integration tests may want to inspect controller responses in# more detail, and that's when \Response can be useful for application# developers. Integration test methods such as# ActionDispatch::Integration::Session#get and# ActionDispatch::Integration::Session#post return objects of type# TestResponse (which are of course also of type \Response).## For example, the following demo integration test prints the body of the# controller response to the console:## class DemoControllerTest < ActionDispatch::IntegrationTest# def test_print_root_path_to_console# get('/')# puts response.body# end# endclassResponseattr_accessor:request,:headerattr_reader:statusattr_writer:sending_filealias_method:headers=,:header=alias_method:headers,:headerdelegate:[],:[]=,:to=>:@headerdelegate:each,:to=>:@stream# Sets the HTTP response's content MIME type. For example, in the controller# you could write this:## response.content_type = "text/plain"## If a character set has been defined for this response (see charset=) then# the character set information will also be included in the content type# information.attr_accessor:charsetattr_reader:content_typeCONTENT_TYPE="Content-Type".freezeSET_COOKIE="Set-Cookie".freezeLOCATION="Location".freezeNO_CONTENT_CODES=[204,304]cattr_accessor(:default_charset){"utf-8"}cattr_accessor(:default_headers)includeRack::Response::HelpersincludeActionDispatch::Http::FilterRedirectincludeActionDispatch::Http::Cache::ResponseincludeMonitorMixinclassBuffer# :nodoc:definitialize(response,buf)@response=response@buf=buf@closed=falseenddefwrite(string)raiseIOError,"closed stream"ifclosed?@response.commit!@buf.pushstringenddefeach(&block)@buf.each(&block)enddefclose@response.commit!@closed=trueenddefclosed?@closedendendattr_reader:streamdefinitialize(status=200,header={},body=[])super()header=merge_default_headers(header,self.class.default_headers)self.body,self.header,self.status=body,header,status@sending_file=false@blank=false@cv=new_cond@committed=false@content_type=nil@charset=nilifcontent_type=self[CONTENT_TYPE]type,charset=content_type.split(/;\s*charset=/)@content_type=Mime::Type.lookup(type)@charset=charset||self.class.default_charsetendprepare_cache_control!yieldselfifblock_given?enddefawait_commitsynchronizedo@cv.wait_until{@committed}endenddefcommit!synchronizedo@committed=true@cv.broadcastendenddefcommitted?@committedend# Sets the HTTP status code.defstatus=(status)@status=Rack::Utils.status_code(status)enddefcontent_type=(content_type)@content_type=content_type.to_send# The response code of the request.defresponse_code@statusend# Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.defcode@status.to_send# Returns the corresponding message for the current HTTP status code:## response.status = 200# response.message # => "OK"## response.status = 404# response.message # => "Not Found"#defmessageRack::Utils::HTTP_STATUS_CODES[@status]endalias_method:status_message,:messagedefrespond_to?(method)ifmethod.to_s=='to_path'stream.respond_to?(:to_path)elsesuperendenddefto_pathstream.to_pathend# Returns the content of the response as a string. This contains the contents# of any calls to <tt>render</tt>.defbodystrings=[]each{|part|strings<<part.to_s}strings.joinendEMPTY=" "# Allows you to manually set or override the response body.defbody=(body)@blank=trueifbody==EMPTYifbody.respond_to?(:to_path)@stream=bodyelsesynchronizedo@stream=build_bufferself,munge_body_object(body)endendenddefbody_partsparts=[]@stream.each{|x|parts<<x}partsenddefset_cookie(key,value)::Rack::Utils.set_cookie_header!(header,key,value)enddefdelete_cookie(key,value={})::Rack::Utils.delete_cookie_header!(header,key,value)enddeflocationheaders[LOCATION]endalias_method:redirect_url,:locationdeflocation=(url)headers[LOCATION]=urlenddefclosestream.closeifstream.respond_to?(:close)enddefto_arack_response@status,@header.to_hashendaliasprepare!to_aaliasto_aryto_a# For implicit splat on 1.9.2# Returns the response cookies, converted to a Hash of (name => value) pairs## assert_equal 'AuthorOfNewPage', r.cookies['author']defcookiescookies={}ifheader=self[SET_COOKIE]header=header.split("\n")ifheader.respond_to?(:to_str)header.eachdo|cookie|ifpair=cookie.split(';').firstkey,value=pair.split("=").map{|v|Rack::Utils.unescape(v)}cookies[key]=valueendendendcookiesendprivatedefmerge_default_headers(original,default)returnoriginalunlessdefault.respond_to?(:merge)default.merge(original)enddefbuild_buffer(response,body)Buffer.newresponse,bodyenddefmunge_body_object(body)body.respond_to?(:each)?body:[body]enddefassign_default_content_type_and_charset!(headers)returnifheaders[CONTENT_TYPE].present?@content_type||=Mime::HTML@charset||=self.class.default_charsetunless@charset==falsetype=@content_type.to_s.duptype<<"; charset=#{@charset}"ifappend_charset?headers[CONTENT_TYPE]=typeenddefappend_charset?!@sending_file&&@charset!=falseenddefrack_response(status,header)assign_default_content_type_and_charset!(header)handle_conditional_get!header[SET_COOKIE]=header[SET_COOKIE].join("\n")ifheader[SET_COOKIE].respond_to?(:join)ifNO_CONTENT_CODES.include?(@status)header.deleteCONTENT_TYPE[status,header,[]]else[status,header,self]endendendend