sinatra 2.0.0 → 2.2.0

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.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

data/lib/sinatra/base.rb CHANGED
@@ -43,12 +43,11 @@ module Sinatra
43
43
  end
44
44
 
45
45
  def preferred_type(*types)
46
- accepts = accept # just evaluate once
47
- return accepts.first if types.empty?
46
+ return accept.first if types.empty?
48
47
  types.flatten!
49
- return types.first if accepts.empty?
50
- accepts.detect do |pattern|
51
- type = types.detect { |t| File.fnmatch(pattern, t) }
48
+ return types.first if accept.empty?
49
+ accept.detect do |accept_header|
50
+ type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) }
52
51
  return type if type
53
52
  end
54
53
  end
@@ -78,11 +77,11 @@ module Sinatra
78
77
  def params
79
78
  super
80
79
  rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
81
- raise BadRequest, "Invalid query parameters: #{e.message}"
80
+ raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}"
81
+ rescue EOFError => e
82
+ raise BadRequest, "Invalid multipart/form-data: #{Rack::Utils.escape_html(e.message)}"
82
83
  end
83
84
 
84
- private
85
-
86
85
  class AcceptEntry
87
86
  attr_accessor :params
88
87
  attr_reader :entry
@@ -125,6 +124,35 @@ module Sinatra
125
124
  to_str.send(*args, &block)
126
125
  end
127
126
  end
127
+
128
+ class MimeTypeEntry
129
+ attr_reader :params
130
+
131
+ def initialize(entry)
132
+ params = entry.scan(HEADER_PARAM).map! do |s|
133
+ key, value = s.strip.split('=', 2)
134
+ value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"')
135
+ [key, value]
136
+ end
137
+
138
+ @type = entry[/[^;]+/].delete(' ')
139
+ @params = Hash[params]
140
+ end
141
+
142
+ def accepts?(entry)
143
+ File.fnmatch(entry, self) && matches_params?(entry.params)
144
+ end
145
+
146
+ def to_str
147
+ @type
148
+ end
149
+
150
+ def matches_params?(params)
151
+ return true if @params.empty?
152
+
153
+ params.all? { |k,v| !@params.has_key?(k) || @params[k] == v }
154
+ end
155
+ end
128
156
  end
129
157
 
130
158
  # The response object. See Rack::Response and Rack::Response::Helpers for
@@ -132,11 +160,7 @@ module Sinatra
132
160
  # http://rubydoc.info/github/rack/rack/master/Rack/Response
133
161
  # http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
134
162
  class Response < Rack::Response
135
- DROP_BODY_RESPONSES = [204, 205, 304]
136
- def initialize(*)
137
- super
138
- headers['Content-Type'] ||= 'text/html'
139
- end
163
+ DROP_BODY_RESPONSES = [204, 304]
140
164
 
141
165
  def body=(value)
142
166
  value = value.body while Rack::Response === value
@@ -163,10 +187,10 @@ module Sinatra
163
187
  if calculate_content_length?
164
188
  # if some other code has already set Content-Length, don't muck with it
165
189
  # currently, this would be the static file-handler
166
- headers["Content-Length"] = body.inject(0) { |l, p| l + p.bytesize }.to_s
190
+ headers["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s
167
191
  end
168
192
 
169
- [status.to_i, headers, result]
193
+ [status, headers, result]
170
194
  end
171
195
 
172
196
  private
@@ -176,15 +200,15 @@ module Sinatra
176
200
  end
177
201
 
178
202
  def drop_content_info?
179
- status.to_i / 100 == 1 or drop_body?
203
+ informational? or drop_body?
180
204
  end
181
205
 
182
206
  def drop_body?
183
- DROP_BODY_RESPONSES.include?(status.to_i)
207
+ DROP_BODY_RESPONSES.include?(status)
184
208
  end
185
209
  end
186
210
 
187
- # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
211
+ # Some Rack handlers (Rainbows!) implement an extended body object protocol, however,
188
212
  # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
189
213
  # This middleware will detect an extended body object and will make sure it reaches the
190
214
  # handler directly. We do this here, so our middleware and middleware set up by the app will
@@ -360,7 +384,7 @@ module Sinatra
360
384
  # Set the Content-Disposition to "attachment" with the specified filename,
361
385
  # instructing the user agents to prompt to save.
362
386
  def attachment(filename = nil, disposition = :attachment)
363
- response['Content-Disposition'] = disposition.to_s
387
+ response['Content-Disposition'] = disposition.to_s.dup
364
388
  if filename
365
389
  params = '; filename="%s"' % File.basename(filename)
366
390
  response['Content-Disposition'] << params
@@ -451,7 +475,7 @@ module Sinatra
451
475
  #
452
476
  # The close parameter specifies whether Stream#close should be called
453
477
  # after the block has been executed. This is only relevant for evented
454
- # servers like Thin or Rainbows.
478
+ # servers like Rainbows.
455
479
  def stream(keep_open = false)
456
480
  scheduler = env['async.callback'] ? EventMachine : Stream
457
481
  current = @params.dup
@@ -461,7 +485,7 @@ module Sinatra
461
485
  # Specify response freshness policy for HTTP caches (Cache-Control header).
462
486
  # Any number of non-value directives (:public, :private, :no_cache,
463
487
  # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
464
- # a Hash of value directives (:max_age, :min_stale, :s_maxage).
488
+ # a Hash of value directives (:max_age, :s_maxage).
465
489
  #
466
490
  # cache_control :public, :must_revalidate, :max_age => 60
467
491
  # => Cache-Control: public, must-revalidate, max-age=60
@@ -647,8 +671,6 @@ module Sinatra
647
671
  end
648
672
  end
649
673
 
650
- private
651
-
652
674
  # Template rendering methods. Each method takes the name of a template
653
675
  # to render as a Symbol and returns a String with the rendered output,
654
676
  # as well as an optional hash with additional options.
@@ -722,6 +744,7 @@ module Sinatra
722
744
  end
723
745
 
724
746
  def markdown(template, options = {}, locals = {})
747
+ options[:exclude_outvar] = true
725
748
  render :markdown, template, options, locals
726
749
  end
727
750
 
@@ -786,15 +809,8 @@ module Sinatra
786
809
  def find_template(views, name, engine)
787
810
  yield ::File.join(views, "#{name}.#{@preferred_extension}")
788
811
 
789
- if Tilt.respond_to?(:mappings)
790
- Tilt.mappings.each do |ext, engines|
791
- next unless ext != @preferred_extension and engines.include? engine
792
- yield ::File.join(views, "#{name}.#{ext}")
793
- end
794
- else
795
- Tilt.default_mapping.extensions_for(engine).each do |ext|
796
- yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
797
- end
812
+ Tilt.default_mapping.extensions_for(engine).each do |ext|
813
+ yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
798
814
  end
799
815
  end
800
816
 
@@ -825,10 +841,11 @@ module Sinatra
825
841
  content_type = options.delete(:content_type) || content_type
826
842
  layout_engine = options.delete(:layout_engine) || engine
827
843
  scope = options.delete(:scope) || self
844
+ exclude_outvar = options.delete(:exclude_outvar)
828
845
  options.delete(:layout)
829
846
 
830
847
  # set some defaults
831
- options[:outvar] ||= '@_out_buf'
848
+ options[:outvar] ||= '@_out_buf' unless exclude_outvar
832
849
  options[:default_encoding] ||= settings.default_encoding
833
850
 
834
851
  # compile and render template
@@ -854,12 +871,12 @@ module Sinatra
854
871
 
855
872
  def compile_template(engine, data, options, views)
856
873
  eat_errors = options.delete :eat_errors
857
- template_cache.fetch engine, data, options, views do
858
- template = Tilt[engine]
859
- raise "Template engine not found: #{engine}" if template.nil?
874
+ template = Tilt[engine]
875
+ raise "Template engine not found: #{engine}" if template.nil?
860
876
 
861
- case data
862
- when Symbol
877
+ case data
878
+ when Symbol
879
+ template_cache.fetch engine, data, options, views do
863
880
  body, path, line = settings.templates[data]
864
881
  if body
865
882
  body = body.call if body.respond_to?(:call)
@@ -877,17 +894,24 @@ module Sinatra
877
894
  throw :layout_missing if eat_errors and not found
878
895
  template.new(path, 1, options)
879
896
  end
880
- when Proc, String
881
- body = data.is_a?(String) ? Proc.new { data } : data
882
- caller = settings.caller_locations.first
883
- path = options[:path] || caller[0]
884
- line = options[:line] || caller[1]
885
- template.new(path, line.to_i, options, &body)
886
- else
887
- raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
888
897
  end
898
+ when Proc
899
+ compile_block_template(template, options, &data)
900
+ when String
901
+ template_cache.fetch engine, data, options, views do
902
+ compile_block_template(template, options) { data }
903
+ end
904
+ else
905
+ raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
889
906
  end
890
907
  end
908
+
909
+ def compile_block_template(template, options, &body)
910
+ caller = settings.caller_locations.first
911
+ path = options[:path] || caller[0]
912
+ line = options[:line] || caller[1]
913
+ template.new(path, line.to_i, options, &body)
914
+ end
891
915
  end
892
916
 
893
917
  # Base class for all Sinatra applications and middleware.
@@ -901,10 +925,11 @@ module Sinatra
901
925
  attr_accessor :app, :env, :request, :response, :params
902
926
  attr_reader :template_cache
903
927
 
904
- def initialize(app = nil)
928
+ def initialize(app = nil, **kwargs)
905
929
  super()
906
930
  @app = app
907
931
  @template_cache = Tilt::Cache.new
932
+ @pinned_response = nil # whether a before! filter pinned the content-type
908
933
  yield self if block_given?
909
934
  end
910
935
 
@@ -915,19 +940,20 @@ module Sinatra
915
940
 
916
941
  def call!(env) # :nodoc:
917
942
  @env = env
943
+ @params = IndifferentHash.new
918
944
  @request = Request.new(env)
919
945
  @response = Response.new
946
+ @pinned_response = nil
920
947
  template_cache.clear if settings.reload_templates
921
948
 
922
- @response['Content-Type'] = nil
923
949
  invoke { dispatch! }
924
950
  invoke { error_block!(response.status) } unless @env['sinatra.error']
925
951
 
926
952
  unless @response['Content-Type']
927
- if Array === body and body[0].respond_to? :content_type
953
+ if Array === body && body[0].respond_to?(:content_type)
928
954
  content_type body[0].content_type
929
- else
930
- content_type :html
955
+ elsif default = settings.default_content_type
956
+ content_type default
931
957
  end
932
958
  end
933
959
 
@@ -977,15 +1003,21 @@ module Sinatra
977
1003
  private
978
1004
 
979
1005
  # Run filters defined on the class and all superclasses.
980
- def filter!(type, base = settings)
981
- filter! type, base.superclass if base.superclass.respond_to?(:filters)
982
- base.filters[type].each { |args| process_route(*args) }
1006
+ # Accepts an optional block to call after each filter is applied.
1007
+ def filter!(type, base = settings, &block)
1008
+ filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters)
1009
+ base.filters[type].each do |args|
1010
+ result = process_route(*args)
1011
+ block.call(result) if block_given?
1012
+ end
983
1013
  end
984
1014
 
985
1015
  # Run routes defined on the class and all superclasses.
986
1016
  def route!(base = settings, pass_block = nil)
987
1017
  if routes = base.routes[@request.request_method]
988
1018
  routes.each do |pattern, conditions, block|
1019
+ response.delete_header('Content-Type') unless @pinned_response
1020
+
989
1021
  returned_pass_block = process_route(pattern, conditions) do |*args|
990
1022
  env['sinatra.route'] = "#{@request.request_method} #{pattern}"
991
1023
  route_eval { block[*args] }
@@ -1022,12 +1054,14 @@ module Sinatra
1022
1054
  return unless params = pattern.params(route)
1023
1055
 
1024
1056
  params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
1025
- original, @params = @params, @params.merge(params) if params.any?
1057
+ force_encoding(params)
1058
+ @params = @params.merge(params) if params.any?
1026
1059
 
1027
- if pattern.is_a? Mustermann::Regular
1028
- captures = pattern.match(route).captures
1060
+ regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} )
1061
+ if regexp_exists
1062
+ captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
1029
1063
  values += captures
1030
- @params[:captures] = captures
1064
+ @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty?
1031
1065
  else
1032
1066
  values += params.values.flatten
1033
1067
  end
@@ -1040,7 +1074,8 @@ module Sinatra
1040
1074
  @env['sinatra.error.params'] = @params
1041
1075
  raise
1042
1076
  ensure
1043
- @params = original if original
1077
+ params ||= {}
1078
+ params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params']
1044
1079
  end
1045
1080
 
1046
1081
  # No matching route was found or all routes passed. The default
@@ -1052,7 +1087,7 @@ module Sinatra
1052
1087
  if @app
1053
1088
  forward
1054
1089
  else
1055
- raise NotFound
1090
+ raise NotFound, "#{request.request_method} #{request.path_info}"
1056
1091
  end
1057
1092
  end
1058
1093
 
@@ -1060,7 +1095,11 @@ module Sinatra
1060
1095
  # a matching file is found, returns nil otherwise.
1061
1096
  def static!(options = {})
1062
1097
  return if (public_dir = settings.public_folder).nil?
1063
- path = File.expand_path("#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" )
1098
+ path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
1099
+ return unless valid_path?(path)
1100
+
1101
+ path = File.expand_path(path)
1102
+ return unless path.start_with?(File.expand_path(public_dir) + '/')
1064
1103
  return unless File.file?(path)
1065
1104
 
1066
1105
  env['sinatra.static_file'] = path
@@ -1086,11 +1125,18 @@ module Sinatra
1086
1125
 
1087
1126
  # Dispatch a request with error handling.
1088
1127
  def dispatch!
1089
- force_encoding(@params = IndifferentHash[@request.params])
1128
+ # Avoid passing frozen string in force_encoding
1129
+ @params.merge!(@request.params).each do |key, val|
1130
+ next unless val.respond_to?(:force_encoding)
1131
+ val = val.dup if val.frozen?
1132
+ @params[key] = force_encoding(val)
1133
+ end
1090
1134
 
1091
1135
  invoke do
1092
1136
  static! if settings.static? && (request.get? || request.head?)
1093
- filter! :before
1137
+ filter! :before do
1138
+ @pinned_response = !response['Content-Type'].nil?
1139
+ end
1094
1140
  route!
1095
1141
  end
1096
1142
  rescue ::Exception => boom
@@ -1110,7 +1156,7 @@ module Sinatra
1110
1156
  end
1111
1157
  @env['sinatra.error'] = boom
1112
1158
 
1113
- if boom.respond_to? :http_status
1159
+ if boom.respond_to? :http_status and boom.http_status.between? 400, 599
1114
1160
  status(boom.http_status)
1115
1161
  elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
1116
1162
  status(boom.code)
@@ -1118,21 +1164,27 @@ module Sinatra
1118
1164
  status(500)
1119
1165
  end
1120
1166
 
1121
- status(500) unless status.between? 400, 599
1122
-
1123
- boom_message = boom.message if boom.message && boom.message != boom.class.name
1124
1167
  if server_error?
1125
1168
  dump_errors! boom if settings.dump_errors?
1126
1169
  raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
1127
1170
  elsif not_found?
1128
1171
  headers['X-Cascade'] = 'pass' if settings.x_cascade?
1129
- body boom_message || '<h1>Not Found</h1>'
1130
- elsif bad_request?
1131
- body boom_message || '<h1>Bad Request</h1>'
1132
1172
  end
1133
1173
 
1134
- res = error_block!(boom.class, boom) || error_block!(status, boom)
1135
- return res if res or not server_error?
1174
+ if res = error_block!(boom.class, boom) || error_block!(status, boom)
1175
+ return res
1176
+ end
1177
+
1178
+ if not_found? || bad_request?
1179
+ if boom.message && boom.message != boom.class.name
1180
+ body Rack::Utils.escape_html(boom.message)
1181
+ else
1182
+ content_type 'text/html'
1183
+ body '<h1>' + (not_found? ? 'Not Found' : 'Bad Request') + '</h1>'
1184
+ end
1185
+ end
1186
+
1187
+ return unless server_error?
1136
1188
  raise boom if settings.raise_errors? or settings.show_exceptions?
1137
1189
  error_block! Exception, boom
1138
1190
  end
@@ -1160,12 +1212,12 @@ module Sinatra
1160
1212
 
1161
1213
  class << self
1162
1214
  CALLERS_TO_IGNORE = [ # :nodoc:
1163
- /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1215
+ /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1164
1216
  /lib\/tilt.*\.rb$/, # all tilt code
1165
1217
  /^\(.*\)$/, # generated code
1166
1218
  /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
1167
1219
  /active_support/, # active_support require hacks
1168
- /bundler(\/runtime)?\.rb/, # bundler require hacks
1220
+ /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
1169
1221
  /<internal:/, # internal in ruby >= 1.9.2
1170
1222
  /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
1171
1223
  ]
@@ -1245,8 +1297,8 @@ module Sinatra
1245
1297
  end
1246
1298
  end
1247
1299
 
1248
- define_singleton("#{option}=", setter) if setter
1249
- define_singleton(option, getter) if getter
1300
+ define_singleton("#{option}=", setter)
1301
+ define_singleton(option, getter)
1250
1302
  define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
1251
1303
  self
1252
1304
  end
@@ -1342,19 +1394,19 @@ module Sinatra
1342
1394
  # context as route handlers and may access/modify the request and
1343
1395
  # response.
1344
1396
  def before(path = /.*/, **options, &block)
1345
- add_filter(:before, path, options, &block)
1397
+ add_filter(:before, path, **options, &block)
1346
1398
  end
1347
1399
 
1348
1400
  # Define an after filter; runs after all requests within the same
1349
1401
  # context as route handlers and may access/modify the request and
1350
1402
  # response.
1351
1403
  def after(path = /.*/, **options, &block)
1352
- add_filter(:after, path, options, &block)
1404
+ add_filter(:after, path, **options, &block)
1353
1405
  end
1354
1406
 
1355
1407
  # add a filter
1356
1408
  def add_filter(type, path = /.*/, **options, &block)
1357
- filters[type] << compile!(type, path, block, options)
1409
+ filters[type] << compile!(type, path, block, **options)
1358
1410
  end
1359
1411
 
1360
1412
  # Add a route condition. The route is considered non-matching when the
@@ -1428,13 +1480,14 @@ module Sinatra
1428
1480
  @prototype = nil
1429
1481
  @middleware << [middleware, args, block]
1430
1482
  end
1483
+ ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
1431
1484
 
1432
1485
  # Stop the self-hosted server if running.
1433
1486
  def quit!
1434
1487
  return unless running?
1435
1488
  # Use Thin's hard #stop! if available, otherwise just #stop.
1436
1489
  running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
1437
- $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless supress_messages?
1490
+ $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
1438
1491
  set :running_server, nil
1439
1492
  set :handler_name, nil
1440
1493
  end
@@ -1442,12 +1495,12 @@ module Sinatra
1442
1495
  alias_method :stop!, :quit!
1443
1496
 
1444
1497
  # Run the Sinatra app as a self-hosted server using
1445
- # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1498
+ # Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1446
1499
  # with the constructed handler once we have taken the stage.
1447
1500
  def run!(options = {}, &block)
1448
1501
  return if running?
1449
1502
  set options
1450
- handler = detect_rack_handler
1503
+ handler = Rack::Handler.pick(server)
1451
1504
  handler_name = handler.name.gsub(/.*::/, '')
1452
1505
  server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
1453
1506
  server_settings.merge!(:Port => port, :Host => bind)
@@ -1480,8 +1533,8 @@ module Sinatra
1480
1533
  # Create a new instance of the class fronted by its middleware
1481
1534
  # pipeline. The object is guaranteed to respond to #call but may not be
1482
1535
  # an instance of the class new was called on.
1483
- def new(*args, &bk)
1484
- instance = new!(*args, &bk)
1536
+ def new(*args, **kwargs, &bk)
1537
+ instance = new!(*args, **kwargs, &bk)
1485
1538
  Wrapper.new(build(instance).to_app, instance)
1486
1539
  end
1487
1540
 
@@ -1519,8 +1572,8 @@ module Sinatra
1519
1572
  # behavior, by ensuring an instance exists:
1520
1573
  prototype
1521
1574
  # Run the instance we created:
1522
- handler.run(self, server_settings) do |server|
1523
- unless supress_messages?
1575
+ handler.run(self, **server_settings) do |server|
1576
+ unless suppress_messages?
1524
1577
  $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
1525
1578
  end
1526
1579
 
@@ -1533,7 +1586,7 @@ module Sinatra
1533
1586
  end
1534
1587
  end
1535
1588
 
1536
- def supress_messages?
1589
+ def suppress_messages?
1537
1590
  handler_name =~ /cgi/i || quiet
1538
1591
  end
1539
1592
 
@@ -1598,7 +1651,7 @@ module Sinatra
1598
1651
 
1599
1652
  def route(verb, path, options = {}, &block)
1600
1653
  enable :empty_path_info if path == "" and empty_path_info.nil?
1601
- signature = compile!(verb, path, block, options)
1654
+ signature = compile!(verb, path, block, **options)
1602
1655
  (@routes[verb] ||= []) << signature
1603
1656
  invoke_hook(:route_added, verb, path, block)
1604
1657
  signature
@@ -1635,7 +1688,7 @@ module Sinatra
1635
1688
  end
1636
1689
 
1637
1690
  def compile(path, route_mustermann_opts = {})
1638
- Mustermann.new(path, mustermann_opts.merge(route_mustermann_opts))
1691
+ Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts))
1639
1692
  end
1640
1693
 
1641
1694
  def setup_default_middleware(builder)
@@ -1701,17 +1754,6 @@ module Sinatra
1701
1754
  builder.use session_store, options
1702
1755
  end
1703
1756
 
1704
- def detect_rack_handler
1705
- servers = Array(server)
1706
- servers.each do |server_name|
1707
- begin
1708
- return Rack::Handler.get(server_name.to_s)
1709
- rescue LoadError, NameError
1710
- end
1711
- end
1712
- fail "Server handler (#{servers.join(',')}) not found."
1713
- end
1714
-
1715
1757
  def inherited(subclass)
1716
1758
  subclass.reset!
1717
1759
  subclass.set :app_file, caller_files.first unless subclass.app_file?
@@ -1740,29 +1782,22 @@ module Sinatra
1740
1782
  end
1741
1783
  end
1742
1784
 
1743
- # Fixes encoding issues by
1744
- # * defaulting to UTF-8
1745
- # * casting params to Encoding.default_external
1746
- #
1747
- # The latter might not be necessary if Rack handles it one day.
1748
- # Keep an eye on Rack's LH #100.
1749
- def force_encoding(*args) settings.force_encoding(*args) end
1750
- if defined? Encoding
1751
- def self.force_encoding(data, encoding = default_encoding)
1752
- return if data == settings || data.is_a?(Tempfile)
1753
- if data.respond_to? :force_encoding
1754
- data.force_encoding(encoding).encode!
1755
- elsif data.respond_to? :each_value
1756
- data.each_value { |v| force_encoding(v, encoding) }
1757
- elsif data.respond_to? :each
1758
- data.each { |v| force_encoding(v, encoding) }
1759
- end
1760
- data
1785
+ # Force data to specified encoding. It defaults to settings.default_encoding
1786
+ # which is UTF-8 by default
1787
+ def self.force_encoding(data, encoding = default_encoding)
1788
+ return if data == settings || data.is_a?(Tempfile)
1789
+ if data.respond_to? :force_encoding
1790
+ data.force_encoding(encoding).encode!
1791
+ elsif data.respond_to? :each_value
1792
+ data.each_value { |v| force_encoding(v, encoding) }
1793
+ elsif data.respond_to? :each
1794
+ data.each { |v| force_encoding(v, encoding) }
1761
1795
  end
1762
- else
1763
- def self.force_encoding(data, *) data end
1796
+ data
1764
1797
  end
1765
1798
 
1799
+ def force_encoding(*args) settings.force_encoding(*args) end
1800
+
1766
1801
  reset!
1767
1802
 
1768
1803
  set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
@@ -1780,6 +1815,7 @@ module Sinatra
1780
1815
  set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
1781
1816
  settings.add_charset << /^text\//
1782
1817
  set :mustermann_opts, {}
1818
+ set :default_content_type, 'text/html'
1783
1819
 
1784
1820
  # explicitly generating a session secret eagerly to play nice with preforking
1785
1821
  begin
@@ -1810,10 +1846,9 @@ module Sinatra
1810
1846
  server.unshift 'control_tower'
1811
1847
  else
1812
1848
  server.unshift 'reel'
1849
+ server.unshift 'puma'
1813
1850
  server.unshift 'mongrel' if ruby_engine.nil?
1814
- server.unshift 'puma' if ruby_engine != 'rbx'
1815
1851
  server.unshift 'thin' if ruby_engine != 'jruby'
1816
- server.unshift 'puma' if ruby_engine == 'rbx'
1817
1852
  server.unshift 'trinidad' if ruby_engine == 'jruby'
1818
1853
  end
1819
1854
 
@@ -1841,7 +1876,7 @@ module Sinatra
1841
1876
 
1842
1877
  configure :development do
1843
1878
  get '/__sinatra__/:image.png' do
1844
- filename = File.dirname(__FILE__) + "/images/#{params[:image].to_i}.png"
1879
+ filename = __dir__ + "/images/#{params[:image].to_i}.png"
1845
1880
  content_type :png
1846
1881
  send_file filename
1847
1882
  end
@@ -1922,6 +1957,8 @@ module Sinatra
1922
1957
  return super(*args, &block) if respond_to? method_name
1923
1958
  Delegator.target.send(method_name, *args, &block)
1924
1959
  end
1960
+ # ensure keyword argument passing is compatible with ruby >= 2.7
1961
+ ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true)
1925
1962
  private method_name
1926
1963
  end
1927
1964
  end