diff --git a/README.md b/README.md
index 1555fe4c..25c05cf3 100644
--- a/README.md
+++ b/README.md
@@ -2264,6 +2264,15 @@ set :protection, :session => true
used for built-in server.
+
default_content_type
+
+ Content-Type to assume if unknown (defaults to "text/html"). Set
+ to nil to not set a default Content-Type on every response; when
+ configured so, you must set the Content-Type manually when emitting content
+ or the user-agent will have to sniff it (or, if nosniff is enabled
+ in Rack::Protection::XSSHeader, assume application/octet-stream).
+
+
default_encoding
Encoding to assume if unknown (defaults to "utf-8").
diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb
index b0a52b90..26f872ee 100644
--- a/lib/sinatra/base.rb
+++ b/lib/sinatra/base.rb
@@ -159,10 +159,6 @@ module Sinatra
# http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
class Response < Rack::Response
DROP_BODY_RESPONSES = [204, 304]
- def initialize(*)
- super
- headers['Content-Type'] ||= 'text/html'
- end
def body=(value)
value = value.body while Rack::Response === value
@@ -176,6 +172,8 @@ module Sinatra
def finish
result = body
+ headers.delete "Content-Type" if headers["Content-Type"].nil?
+
if drop_content_info?
headers.delete "Content-Length"
headers.delete "Content-Type"
@@ -189,7 +187,7 @@ module Sinatra
if calculate_content_length?
# if some other code has already set Content-Length, don't muck with it
# currently, this would be the static file-handler
- headers["Content-Length"] = body.inject(0) { |l, p| l + p.bytesize }.to_s
+ headers["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s
end
[status.to_i, headers, result]
@@ -940,15 +938,14 @@ module Sinatra
@response = Response.new
template_cache.clear if settings.reload_templates
- @response['Content-Type'] = nil
invoke { dispatch! }
invoke { error_block!(response.status) } unless @env['sinatra.error']
unless @response['Content-Type']
- if Array === body and body[0].respond_to? :content_type
+ if Array === body && body[0].respond_to?(:content_type)
content_type body[0].content_type
- else
- content_type :html
+ elsif default = settings.default_content_type
+ content_type default
end
end
@@ -1089,10 +1086,10 @@ module Sinatra
# Attempt to serve static files from public directory. Throws :halt when
# a matching file is found, returns nil otherwise.
def static!(options = {})
- return if (public_dir = settings.public_folder).nil?
+ return if (public_dir = settings.public_folder).nil?
path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
return unless valid_path?(path)
-
+
path = File.expand_path(path)
return unless File.file?(path)
@@ -1160,19 +1157,27 @@ module Sinatra
status(500) unless status.between? 400, 599
- boom_message = boom.message if boom.message && boom.message != boom.class.name
if server_error?
dump_errors! boom if settings.dump_errors?
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
elsif not_found?
headers['X-Cascade'] = 'pass' if settings.x_cascade?
- body boom_message || 'Not Found
'
- elsif bad_request?
- body boom_message || 'Bad Request
'
end
- res = error_block!(boom.class, boom) || error_block!(status, boom)
- return res if res or not server_error?
+ if res = error_block!(boom.class, boom) || error_block!(status, boom)
+ return res
+ end
+
+ if not_found? || bad_request?
+ if boom.message && boom.message != boom.class.name
+ body boom.message
+ else
+ content_type 'text/html'
+ body '' + (not_found? ? 'Not Found' : 'Bad Request') + '
'
+ end
+ end
+
+ return unless server_error?
raise boom if settings.raise_errors? or settings.show_exceptions?
error_block! Exception, boom
end
@@ -1813,6 +1818,7 @@ module Sinatra
set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
settings.add_charset << /^text\//
set :mustermann_opts, {}
+ set :default_content_type, 'text/html'
# explicitly generating a session secret eagerly to play nice with preforking
begin
diff --git a/test/response_test.rb b/test/response_test.rb
index a5d360b4..c5bb32fa 100644
--- a/test/response_test.rb
+++ b/test/response_test.rb
@@ -3,7 +3,7 @@
require File.expand_path('../helper', __FILE__)
class ResponseTest < Minitest::Test
- setup { @response = Sinatra::Response.new }
+ setup { @response = Sinatra::Response.new([], 200, { 'Content-Type' => 'text/html' }) }
def assert_same_body(a, b)
assert_equal a.to_enum(:each).to_a, b.to_enum(:each).to_a
diff --git a/test/settings_test.rb b/test/settings_test.rb
index dcf7b2aa..6ba8d2bf 100644
--- a/test/settings_test.rb
+++ b/test/settings_test.rb
@@ -163,6 +163,41 @@ class SettingsTest < Minitest::Test
assert_equal :foo, @base.settings.environment
end
+ describe 'default_content_type' do
+ it 'defaults to html' do
+ assert_equal 'text/html', @base.default_content_type
+ end
+
+ it 'can be changed' do
+ @base.set :default_content_type, 'application/json'
+ @base.get('/') { '{"a":1}' }
+ @app = @base
+ get '/'
+ assert_equal 200, status
+ assert_equal 'application/json', response.content_type
+ end
+
+ it 'can be disabled' do
+ @base.set :default_content_type, nil
+ @base.error(404) { "" }
+ @app = @base
+ get '/'
+ assert_equal 404, status
+ assert_nil response.content_type
+ assert_empty body
+ end
+
+ it 'may emit content without a content-type (to be sniffed)' do
+ @base.set :default_content_type, nil
+ @base.get('/') { raise Sinatra::BadRequest, "This is a drill" }
+ @app = @base
+ get '/'
+ assert_equal 400, status
+ assert_nil response.content_type
+ assert_body "This is a drill"
+ end
+ end
+
describe 'methodoverride' do
it 'is disabled on Base' do
assert ! @base.method_override?