sinatra 1.3.0.f → 1.3.0.g

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

data/CHANGES CHANGED
@@ -12,7 +12,7 @@
12
12
  * Added support for HTTP PATCH requests. (Konstantin Haase)
13
13
 
14
14
  * Use rack-protection to defend against common opportunistic attacks.
15
- (Konstantin Haase)
15
+ (Josh Lane, Jacob Burkhart, Konstantin Haase)
16
16
 
17
17
  * Support for Creole templates, Creole is a standardized wiki markup,
18
18
  supported by many wiki implementations. (Konstanin Haase)
@@ -41,7 +41,7 @@
41
41
  * The sessions setting may be an options hash now. (Konstantin Haase)
42
42
 
43
43
  * Important: Ruby 1.8.6 support has been dropped. This version also depends
44
- on at least Rack 1.3.0. This means that it is incompatible to Rails prior
44
+ on at least Rack 1.3.0. This means that it is incompatible with Rails prior
45
45
  to 3.1.0. Please use 1.2.x if you require an earlier version of Ruby or
46
46
  Rack, which we will continue to supply with bug fixes. (Konstantin Haase)
47
47
 
@@ -82,6 +82,11 @@
82
82
  * Conditional requests on `etag` helper now work properly for unsafe HTTP
83
83
  methods. (Matthew Schinckel, Konstantin Haase)
84
84
 
85
+ * The `last_modified` helper does not stop execution and change the status code
86
+ if the status code is something different than 200. (Konstantin Haase)
87
+
88
+ * Added support for If-Unmodified-Since header. (Konstantin Haase)
89
+
85
90
  * `Sinatra::Base.run!` now prints to stderr rather than stdout. (Andrew
86
91
  Armenia)
87
92
 
data/Gemfile CHANGED
@@ -30,13 +30,7 @@ gem 'sass'
30
30
  gem 'builder'
31
31
  gem 'erubis'
32
32
  gem 'less', '~> 1.0'
33
-
34
- if RUBY_ENGINE == "maglev"
35
- gem 'liquid', :git => "https://github.com/Shopify/liquid.git"
36
- else
37
- gem 'liquid'
38
- end
39
-
33
+ gem 'liquid'
40
34
  gem 'slim', '~> 1.0'
41
35
  gem 'temple', '!= 0.3.3'
42
36
  gem 'RedCloth' if RUBY_VERSION < "1.9.3" and not RUBY_ENGINE.start_with? 'ma'
@@ -49,7 +43,7 @@ gem 'creole'
49
43
  if RUBY_ENGINE == 'jruby'
50
44
  gem 'nokogiri', '!= 1.5.0'
51
45
  gem 'jruby-openssl'
52
- elsif RUBY_ENGINE != 'maglev'
46
+ else
53
47
  gem 'nokogiri'
54
48
  end
55
49
 
@@ -62,16 +56,10 @@ unless RUBY_ENGINE == 'jruby' && JRUBY_VERSION < "1.6.1" && !ENV['TRAVIS']
62
56
  #gem 'bluecloth'
63
57
  end
64
58
 
65
- if RUBY_ENGINE == 'maglev'
66
- gem 'json', :git => "https://github.com/MagLev/json.git"
59
+ platforms :ruby_18, :jruby do
60
+ gem 'json'
67
61
  gem 'markaby'
68
62
  gem 'radius'
69
- else
70
- platforms :ruby_18, :jruby do
71
- gem 'json'
72
- gem 'markaby'
73
- gem 'radius'
74
- end
75
63
  end
76
64
 
77
65
  platforms :mri_18 do
@@ -1055,6 +1055,23 @@ Usá la configuración <tt>:static_cache_control</tt> para agregar el encabezado
1055
1055
  <tt>Cache-Control</tt> a archivos estáticos (ver la sección de configuración
1056
1056
  para más detalles).
1057
1057
 
1058
+ De acuerdo con la RFC 2616 tu aplicación debería comportarse diferente si a las
1059
+ cabeceras If-Match o If-None-Match se le asigna el valor <tt>*</tt> cuando el
1060
+ recurso solicitado ya existe. Sinatra asume para peticiones seguras (como get)
1061
+ e idempotentes (como put) que el recurso existe, mientras que para el resto
1062
+ (como post), que no. Podes cambiar este comportamiento con la opción
1063
+ <tt>:new_resource</tt>:
1064
+
1065
+ get '/crear' do
1066
+ etag '', :new_resource => true
1067
+ Articulo.create
1068
+ erb :nuevo_articulo
1069
+ end
1070
+
1071
+ Si querés seguir usando una weak ETag, indicalo con la opción <tt>:kind</tt>:
1072
+
1073
+ etag '', :new_resource => true, :kind => :weak
1074
+
1058
1075
  === Enviando Archivos
1059
1076
 
1060
1077
  Para enviar archivos, podés usar el método <tt>send_file</tt>:
@@ -1018,6 +1018,23 @@ try {rack-cache}[http://rtomayko.github.com/rack-cache/]:
1018
1018
  Use the <tt>:static_cache_control</tt> setting (see below) to add
1019
1019
  <tt>Cache-Control</tt> header info to static files.
1020
1020
 
1021
+ According to RFC 2616 your application should behave differently if the If-Match
1022
+ or If-None-Match header is set to <tt>*</tt> depending on whether the resource
1023
+ requested is already in existence. Sinatra assumes resources for safe (like get)
1024
+ and idempotent (like put) requests are already in existence, whereas other
1025
+ resources (for instance for post requests), are treated as new resources. You
1026
+ can change this behavior by passing in a <tt>:new_resource</tt> option:
1027
+
1028
+ get '/create' do
1029
+ etag '', :new_resource => true
1030
+ Article.create
1031
+ erb :new_article
1032
+ end
1033
+
1034
+ If you still want to use a weak ETag, pass in a <tt>:kind</tt> option:
1035
+
1036
+ etag '', :new_resource => true, :kind => :weak
1037
+
1021
1038
  === Sending Files
1022
1039
 
1023
1040
  For sending files, you can use the <tt>send_file</tt> helper method:
@@ -355,8 +355,19 @@ module Sinatra
355
355
  return unless time
356
356
  time = time_for time
357
357
  response['Last-Modified'] = time.httpdate
358
- # compare based on seconds since epoch
359
- halt 304 if Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i >= time.to_i
358
+ return if env['HTTP_IF_NONE_MATCH']
359
+
360
+ if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
361
+ # compare based on seconds since epoch
362
+ since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
363
+ halt 304 if since >= time.to_i
364
+ end
365
+
366
+ if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
367
+ # compare based on seconds since epoch
368
+ since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
369
+ halt 412 if since < time.to_i
370
+ end
360
371
  rescue ArgumentError
361
372
  end
362
373
 
@@ -369,18 +380,27 @@ module Sinatra
369
380
  # When the current request includes an 'If-None-Match' header with a
370
381
  # matching etag, execution is immediately halted. If the request method is
371
382
  # GET or HEAD, a '304 Not Modified' response is sent.
372
- def etag(value, kind = :strong)
373
- raise ArgumentError, ":strong or :weak expected" unless [:strong,:weak].include?(kind)
383
+ def etag(value, options = {})
384
+ # Before touching this code, please double check RFC 2616 14.24 and 14.26.
385
+ options = {:kind => options} unless Hash === options
386
+ kind = options[:kind] || :strong
387
+ new_resource = options.fetch(:new_resource) { request.post? }
388
+
389
+ unless [:strong, :weak].include?(kind)
390
+ raise ArgumentError, ":strong or :weak expected"
391
+ end
392
+
374
393
  value = '"%s"' % value
375
394
  value = 'W/' + value if kind == :weak
376
395
  response['ETag'] = value
377
396
 
378
- if etags = env['HTTP_IF_NONE_MATCH']
379
- etags = etags.split(/\s*,\s*/)
380
- if etags.include?(value) or etags.include?('*')
381
- halt 304 if request.safe?
382
- else
383
- halt 412 unless request.safe?
397
+ if success? or status == 304
398
+ if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
399
+ halt(request.safe? ? 304 : 412)
400
+ end
401
+
402
+ if env['HTTP_IF_MATCH']
403
+ halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
384
404
  end
385
405
  end
386
406
  end
@@ -445,6 +465,14 @@ module Sinatra
445
465
  rescue Exception
446
466
  raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
447
467
  end
468
+
469
+ private
470
+
471
+ # Helper method checking if a ETag value list includes the current ETag.
472
+ def etag_matches?(list, new_resource = request.post?)
473
+ return !new_resource if list == '*'
474
+ list.to_s.split(/\s*,\s*/).include? response['ETag']
475
+ end
448
476
  end
449
477
 
450
478
  private
@@ -634,7 +662,7 @@ module Sinatra
634
662
  path, line = settings.caller_locations.first
635
663
  template.new(path, line.to_i, options, &body)
636
664
  else
637
- raise ArgumentError
665
+ raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
638
666
  end
639
667
  end
640
668
  end
@@ -1184,7 +1212,6 @@ module Sinatra
1184
1212
  def compile(path)
1185
1213
  keys = []
1186
1214
  if path.respond_to? :to_str
1187
- special_chars = %w{. + ( ) $}
1188
1215
  pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
1189
1216
  pattern.gsub! /((:\w+)|\*)/ do |match|
1190
1217
  if match == "*"
@@ -1333,7 +1360,7 @@ module Sinatra
1333
1360
 
1334
1361
  def setup_protection(builder)
1335
1362
  return unless protection?
1336
- options = Hash === protection ? protection.dup : {}
1363
+ options = Hash === protection ? protection.dup : {:except => [:escaped_params]}
1337
1364
  options[:except] = Array options[:except]
1338
1365
  options[:except] += [:session_hijacking, :remote_token] unless sessions?
1339
1366
  builder.use Rack::Protection, options
@@ -8,7 +8,7 @@ module Sinatra
8
8
  # on this path by default.
9
9
  set :app_file, caller_files.first || $0
10
10
 
11
- set :run, Proc.new { $0 == app_file }
11
+ set :run, Proc.new { File.expand_path($0) == File.expand_path(app_file) }
12
12
 
13
13
  if run? && ARGV.any?
14
14
  require 'optparse'
@@ -1,3 +1,3 @@
1
1
  module Sinatra
2
- VERSION = '1.3.0.f'
2
+ VERSION = '1.3.0.g'
3
3
  end
@@ -97,6 +97,17 @@ class BeforeFilterTest < Test::Unit::TestCase
97
97
  assert_equal 'cool', body
98
98
  end
99
99
 
100
+ it "properly unescapes parameters" do
101
+ mock_app {
102
+ before { @foo = params['foo'] }
103
+ get('/foo') { @foo }
104
+ }
105
+
106
+ get '/foo?foo=bar%3Abaz%2Fbend'
107
+ assert ok?
108
+ assert_equal 'bar:baz/bend', body
109
+ end
110
+
100
111
  it "runs filters defined in superclasses" do
101
112
  base = Class.new(Sinatra::Base)
102
113
  base.before { @foo = 'hello from superclass' }
@@ -72,6 +72,10 @@ class Test::Unit::TestCase
72
72
  assert_equal value.lstrip.gsub(/\s*\n\s*/, ""), body.lstrip.gsub(/\s*\n\s*/, "")
73
73
  end
74
74
 
75
+ def assert_status(expected)
76
+ assert_equal Integer(expected), Integer(status)
77
+ end
78
+
75
79
  def assert_like(a,b)
76
80
  pattern = /id=['"][^"']*["']|\s+/
77
81
  assert_equal a.strip.gsub(pattern, ""), b.strip.gsub(pattern, "")
@@ -858,6 +858,20 @@ class HelpersTest < Test::Unit::TestCase
858
858
  assert ! response['Last-Modified']
859
859
  end
860
860
 
861
+ it 'does not change a status other than 200' do
862
+ mock_app do
863
+ get '/' do
864
+ status 299
865
+ last_modified Time.at(0)
866
+ 'ok'
867
+ end
868
+ end
869
+
870
+ get('/', {}, 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT')
871
+ assert_status 299
872
+ assert_body 'ok'
873
+ end
874
+
861
875
  [Time.now, DateTime.now, Date.today, Time.now.to_i,
862
876
  Struct.new(:to_time).new(Time.now) ].each do |last_modified_time|
863
877
  describe "with #{last_modified_time.class.name}" do
@@ -955,74 +969,641 @@ class HelpersTest < Test::Unit::TestCase
955
969
  assert_equal '', body
956
970
  end
957
971
  end
972
+
973
+ context "If-Unmodified-Since" do
974
+ it 'results in 200 if resource has not been modified' do
975
+ get '/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT' }
976
+ assert_equal 200, status
977
+ assert_equal 'Boo!', body
978
+ end
979
+
980
+ it 'results in 412 if resource has been modified' do
981
+ get '/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => Time.at(0).httpdate }
982
+ assert_equal 412, status
983
+ assert_equal '', body
984
+ end
985
+ end
958
986
  end
959
987
  end
960
988
  end
961
989
 
962
990
  describe 'etag' do
963
- setup do
964
- mock_app {
965
- get '/' do
966
- body { 'Hello World' }
967
- etag 'FOO'
968
- 'Boo!'
991
+ context "safe requests" do
992
+ it 'returns 200 for normal requests' do
993
+ mock_app do
994
+ get '/' do
995
+ etag 'foo'
996
+ 'ok'
997
+ end
969
998
  end
970
999
 
971
- post '/' do
972
- etag 'FOO'
973
- 'Matches!'
1000
+ get('/')
1001
+ assert_status 200
1002
+ assert_body 'ok'
1003
+ end
1004
+
1005
+ context "If-None-Match" do
1006
+ it 'returns 304 when If-None-Match is *' do
1007
+ mock_app do
1008
+ get '/' do
1009
+ etag 'foo'
1010
+ 'ok'
1011
+ end
1012
+ end
1013
+
1014
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1015
+ assert_status 304
1016
+ assert_body ''
974
1017
  end
975
- }
976
- end
977
1018
 
978
- it 'sets the ETag header' do
979
- get '/'
980
- assert_equal '"FOO"', response['ETag']
981
- end
1019
+ it 'returns 200 when If-None-Match is * for new resources' do
1020
+ mock_app do
1021
+ get '/' do
1022
+ etag 'foo', :new_resource => true
1023
+ 'ok'
1024
+ end
1025
+ end
982
1026
 
983
- it 'returns a body when conditional get misses' do
984
- get '/'
985
- assert_equal 200, status
986
- assert_equal 'Boo!', body
987
- end
1027
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1028
+ assert_status 200
1029
+ assert_body 'ok'
1030
+ end
988
1031
 
989
- it 'returns a body when posting with no If-None-Match header' do
990
- post '/'
991
- assert_equal 200, status
992
- assert_equal 'Matches!', body
993
- end
1032
+ it 'returns 304 when If-None-Match is * for existing resources' do
1033
+ mock_app do
1034
+ get '/' do
1035
+ etag 'foo', :new_resource => false
1036
+ 'ok'
1037
+ end
1038
+ end
994
1039
 
995
- it 'returns a body when conditional post matches' do
996
- post '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
997
- assert_equal 200, status
998
- assert_equal 'Matches!', body
999
- end
1040
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1041
+ assert_status 304
1042
+ assert_body ''
1043
+ end
1000
1044
 
1001
- it 'halts with 412 when conditional post misses' do
1002
- post '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR"' }
1003
- assert_equal 412, status
1004
- assert_equal '', body
1045
+ it 'returns 304 when If-None-Match is the etag' do
1046
+ mock_app do
1047
+ get '/' do
1048
+ etag 'foo'
1049
+ 'ok'
1050
+ end
1051
+ end
1052
+
1053
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1054
+ assert_status 304
1055
+ assert_body ''
1056
+ end
1057
+
1058
+ it 'returns 304 when If-None-Match includes the etag' do
1059
+ mock_app do
1060
+ get '/' do
1061
+ etag 'foo'
1062
+ 'ok'
1063
+ end
1064
+ end
1065
+
1066
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
1067
+ assert_status 304
1068
+ assert_body ''
1069
+ end
1070
+
1071
+ it 'returns 200 when If-None-Match does not include the etag' do
1072
+ mock_app do
1073
+ get '/' do
1074
+ etag 'foo'
1075
+ 'ok'
1076
+ end
1077
+ end
1078
+
1079
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1080
+ assert_status 200
1081
+ assert_body 'ok'
1082
+ end
1083
+
1084
+ it 'ignores If-Modified-Since if If-None-Match does not match' do
1085
+ mock_app do
1086
+ get '/' do
1087
+ etag 'foo'
1088
+ last_modified Time.at(0)
1089
+ 'ok'
1090
+ end
1091
+ end
1092
+
1093
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1094
+ assert_status 200
1095
+ assert_body 'ok'
1096
+ end
1097
+
1098
+ it 'does not change a status code other than 2xx or 304' do
1099
+ mock_app do
1100
+ get '/' do
1101
+ status 499
1102
+ etag 'foo'
1103
+ 'ok'
1104
+ end
1105
+ end
1106
+
1107
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1108
+ assert_status 499
1109
+ assert_body 'ok'
1110
+ end
1111
+
1112
+ it 'does change 2xx status codes' do
1113
+ mock_app do
1114
+ get '/' do
1115
+ status 299
1116
+ etag 'foo'
1117
+ 'ok'
1118
+ end
1119
+ end
1120
+
1121
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1122
+ assert_status 304
1123
+ assert_body ''
1124
+ end
1125
+
1126
+ it 'does not send a body on 304 status codes' do
1127
+ mock_app do
1128
+ get '/' do
1129
+ status 304
1130
+ etag 'foo'
1131
+ 'ok'
1132
+ end
1133
+ end
1134
+
1135
+ get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1136
+ assert_status 304
1137
+ assert_body ''
1138
+ end
1139
+ end
1140
+
1141
+ context "If-Match" do
1142
+ it 'returns 200 when If-Match is the etag' do
1143
+ mock_app do
1144
+ get '/' do
1145
+ etag 'foo'
1146
+ 'ok'
1147
+ end
1148
+ end
1149
+
1150
+ get('/', {}, 'HTTP_IF_MATCH' => '"foo"')
1151
+ assert_status 200
1152
+ assert_body 'ok'
1153
+ end
1154
+
1155
+ it 'returns 200 when If-Match includes the etag' do
1156
+ mock_app do
1157
+ get '/' do
1158
+ etag 'foo'
1159
+ 'ok'
1160
+ end
1161
+ end
1162
+
1163
+ get('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
1164
+ assert_status 200
1165
+ assert_body 'ok'
1166
+ end
1167
+
1168
+ it 'returns 200 when If-Match is *' do
1169
+ mock_app do
1170
+ get '/' do
1171
+ etag 'foo'
1172
+ 'ok'
1173
+ end
1174
+ end
1175
+
1176
+ get('/', {}, 'HTTP_IF_MATCH' => '*')
1177
+ assert_status 200
1178
+ assert_body 'ok'
1179
+ end
1180
+
1181
+ it 'returns 412 when If-Match is * for new resources' do
1182
+ mock_app do
1183
+ get '/' do
1184
+ etag 'foo', :new_resource => true
1185
+ 'ok'
1186
+ end
1187
+ end
1188
+
1189
+ get('/', {}, 'HTTP_IF_MATCH' => '*')
1190
+ assert_status 412
1191
+ assert_body ''
1192
+ end
1193
+
1194
+ it 'returns 200 when If-Match is * for existing resources' do
1195
+ mock_app do
1196
+ get '/' do
1197
+ etag 'foo', :new_resource => false
1198
+ 'ok'
1199
+ end
1200
+ end
1201
+
1202
+ get('/', {}, 'HTTP_IF_MATCH' => '*')
1203
+ assert_status 200
1204
+ assert_body 'ok'
1205
+ end
1206
+
1207
+ it 'returns 412 when If-Match does not include the etag' do
1208
+ mock_app do
1209
+ get '/' do
1210
+ etag 'foo'
1211
+ 'ok'
1212
+ end
1213
+ end
1214
+
1215
+ get('/', {}, 'HTTP_IF_MATCH' => '"bar"')
1216
+ assert_status 412
1217
+ assert_body ''
1218
+ end
1219
+ end
1005
1220
  end
1006
1221
 
1007
- it 'halts when a conditional GET matches' do
1008
- get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
1009
- assert_equal 304, status
1010
- assert_equal '', body
1222
+ context "idempotent requests" do
1223
+ it 'returns 200 for normal requests' do
1224
+ mock_app do
1225
+ put '/' do
1226
+ etag 'foo'
1227
+ 'ok'
1228
+ end
1229
+ end
1230
+
1231
+ put('/')
1232
+ assert_status 200
1233
+ assert_body 'ok'
1234
+ end
1235
+
1236
+ context "If-None-Match" do
1237
+ it 'returns 412 when If-None-Match is *' do
1238
+ mock_app do
1239
+ put '/' do
1240
+ etag 'foo'
1241
+ 'ok'
1242
+ end
1243
+ end
1244
+
1245
+ put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1246
+ assert_status 412
1247
+ assert_body ''
1248
+ end
1249
+
1250
+ it 'returns 200 when If-None-Match is * for new resources' do
1251
+ mock_app do
1252
+ put '/' do
1253
+ etag 'foo', :new_resource => true
1254
+ 'ok'
1255
+ end
1256
+ end
1257
+
1258
+ put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1259
+ assert_status 200
1260
+ assert_body 'ok'
1261
+ end
1262
+
1263
+ it 'returns 412 when If-None-Match is * for existing resources' do
1264
+ mock_app do
1265
+ put '/' do
1266
+ etag 'foo', :new_resource => false
1267
+ 'ok'
1268
+ end
1269
+ end
1270
+
1271
+ put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1272
+ assert_status 412
1273
+ assert_body ''
1274
+ end
1275
+
1276
+ it 'returns 412 when If-None-Match is the etag' do
1277
+ mock_app do
1278
+ put '/' do
1279
+ etag 'foo'
1280
+ 'ok'
1281
+ end
1282
+ end
1283
+
1284
+ put('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1285
+ assert_status 412
1286
+ assert_body ''
1287
+ end
1288
+
1289
+ it 'returns 412 when If-None-Match includes the etag' do
1290
+ mock_app do
1291
+ put '/' do
1292
+ etag 'foo'
1293
+ 'ok'
1294
+ end
1295
+ end
1296
+
1297
+ put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
1298
+ assert_status 412
1299
+ assert_body ''
1300
+ end
1301
+
1302
+ it 'returns 200 when If-None-Match does not include the etag' do
1303
+ mock_app do
1304
+ put '/' do
1305
+ etag 'foo'
1306
+ 'ok'
1307
+ end
1308
+ end
1309
+
1310
+ put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1311
+ assert_status 200
1312
+ assert_body 'ok'
1313
+ end
1314
+
1315
+ it 'ignores If-Modified-Since if If-None-Match does not match' do
1316
+ mock_app do
1317
+ put '/' do
1318
+ etag 'foo'
1319
+ last_modified Time.at(0)
1320
+ 'ok'
1321
+ end
1322
+ end
1323
+
1324
+ put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1325
+ assert_status 200
1326
+ assert_body 'ok'
1327
+ end
1328
+ end
1329
+
1330
+ context "If-Match" do
1331
+ it 'returns 200 when If-Match is the etag' do
1332
+ mock_app do
1333
+ put '/' do
1334
+ etag 'foo'
1335
+ 'ok'
1336
+ end
1337
+ end
1338
+
1339
+ put('/', {}, 'HTTP_IF_MATCH' => '"foo"')
1340
+ assert_status 200
1341
+ assert_body 'ok'
1342
+ end
1343
+
1344
+ it 'returns 200 when If-Match includes the etag' do
1345
+ mock_app do
1346
+ put '/' do
1347
+ etag 'foo'
1348
+ 'ok'
1349
+ end
1350
+ end
1351
+
1352
+ put('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
1353
+ assert_status 200
1354
+ assert_body 'ok'
1355
+ end
1356
+
1357
+ it 'returns 200 when If-Match is *' do
1358
+ mock_app do
1359
+ put '/' do
1360
+ etag 'foo'
1361
+ 'ok'
1362
+ end
1363
+ end
1364
+
1365
+ put('/', {}, 'HTTP_IF_MATCH' => '*')
1366
+ assert_status 200
1367
+ assert_body 'ok'
1368
+ end
1369
+
1370
+ it 'returns 412 when If-Match is * for new resources' do
1371
+ mock_app do
1372
+ put '/' do
1373
+ etag 'foo', :new_resource => true
1374
+ 'ok'
1375
+ end
1376
+ end
1377
+
1378
+ put('/', {}, 'HTTP_IF_MATCH' => '*')
1379
+ assert_status 412
1380
+ assert_body ''
1381
+ end
1382
+
1383
+ it 'returns 200 when If-Match is * for existing resources' do
1384
+ mock_app do
1385
+ put '/' do
1386
+ etag 'foo', :new_resource => false
1387
+ 'ok'
1388
+ end
1389
+ end
1390
+
1391
+ put('/', {}, 'HTTP_IF_MATCH' => '*')
1392
+ assert_status 200
1393
+ assert_body 'ok'
1394
+ end
1395
+
1396
+ it 'returns 412 when If-Match does not include the etag' do
1397
+ mock_app do
1398
+ put '/' do
1399
+ etag 'foo'
1400
+ 'ok'
1401
+ end
1402
+ end
1403
+
1404
+ put('/', {}, 'HTTP_IF_MATCH' => '"bar"')
1405
+ assert_status 412
1406
+ assert_body ''
1407
+ end
1408
+ end
1011
1409
  end
1012
1410
 
1013
- it 'should handle multiple ETag values in If-None-Match header' do
1014
- get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
1015
- assert_equal 304, status
1016
- assert_equal '', body
1411
+ context "post requests" do
1412
+ it 'returns 200 for normal requests' do
1413
+ mock_app do
1414
+ post '/' do
1415
+ etag 'foo'
1416
+ 'ok'
1417
+ end
1418
+ end
1419
+
1420
+ post('/')
1421
+ assert_status 200
1422
+ assert_body 'ok'
1423
+ end
1424
+
1425
+ context "If-None-Match" do
1426
+ it 'returns 200 when If-None-Match is *' do
1427
+ mock_app do
1428
+ post '/' do
1429
+ etag 'foo'
1430
+ 'ok'
1431
+ end
1432
+ end
1433
+
1434
+ post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1435
+ assert_status 200
1436
+ assert_body 'ok'
1437
+ end
1438
+
1439
+ it 'returns 200 when If-None-Match is * for new resources' do
1440
+ mock_app do
1441
+ post '/' do
1442
+ etag 'foo', :new_resource => true
1443
+ 'ok'
1444
+ end
1445
+ end
1446
+
1447
+ post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1448
+ assert_status 200
1449
+ assert_body 'ok'
1450
+ end
1451
+
1452
+ it 'returns 412 when If-None-Match is * for existing resources' do
1453
+ mock_app do
1454
+ post '/' do
1455
+ etag 'foo', :new_resource => false
1456
+ 'ok'
1457
+ end
1458
+ end
1459
+
1460
+ post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1461
+ assert_status 412
1462
+ assert_body ''
1463
+ end
1464
+
1465
+ it 'returns 412 when If-None-Match is the etag' do
1466
+ mock_app do
1467
+ post '/' do
1468
+ etag 'foo'
1469
+ 'ok'
1470
+ end
1471
+ end
1472
+
1473
+ post('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1474
+ assert_status 412
1475
+ assert_body ''
1476
+ end
1477
+
1478
+ it 'returns 412 when If-None-Match includes the etag' do
1479
+ mock_app do
1480
+ post '/' do
1481
+ etag 'foo'
1482
+ 'ok'
1483
+ end
1484
+ end
1485
+
1486
+ post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
1487
+ assert_status 412
1488
+ assert_body ''
1489
+ end
1490
+
1491
+ it 'returns 200 when If-None-Match does not include the etag' do
1492
+ mock_app do
1493
+ post '/' do
1494
+ etag 'foo'
1495
+ 'ok'
1496
+ end
1497
+ end
1498
+
1499
+ post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1500
+ assert_status 200
1501
+ assert_body 'ok'
1502
+ end
1503
+
1504
+ it 'ignores If-Modified-Since if If-None-Match does not match' do
1505
+ mock_app do
1506
+ post '/' do
1507
+ etag 'foo'
1508
+ last_modified Time.at(0)
1509
+ 'ok'
1510
+ end
1511
+ end
1512
+
1513
+ post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1514
+ assert_status 200
1515
+ assert_body 'ok'
1516
+ end
1517
+ end
1518
+
1519
+ context "If-Match" do
1520
+ it 'returns 200 when If-Match is the etag' do
1521
+ mock_app do
1522
+ post '/' do
1523
+ etag 'foo'
1524
+ 'ok'
1525
+ end
1526
+ end
1527
+
1528
+ post('/', {}, 'HTTP_IF_MATCH' => '"foo"')
1529
+ assert_status 200
1530
+ assert_body 'ok'
1531
+ end
1532
+
1533
+ it 'returns 200 when If-Match includes the etag' do
1534
+ mock_app do
1535
+ post '/' do
1536
+ etag 'foo'
1537
+ 'ok'
1538
+ end
1539
+ end
1540
+
1541
+ post('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
1542
+ assert_status 200
1543
+ assert_body 'ok'
1544
+ end
1545
+
1546
+ it 'returns 412 when If-Match is *' do
1547
+ mock_app do
1548
+ post '/' do
1549
+ etag 'foo'
1550
+ 'ok'
1551
+ end
1552
+ end
1553
+
1554
+ post('/', {}, 'HTTP_IF_MATCH' => '*')
1555
+ assert_status 412
1556
+ assert_body ''
1557
+ end
1558
+
1559
+ it 'returns 412 when If-Match is * for new resources' do
1560
+ mock_app do
1561
+ post '/' do
1562
+ etag 'foo', :new_resource => true
1563
+ 'ok'
1564
+ end
1565
+ end
1566
+
1567
+ post('/', {}, 'HTTP_IF_MATCH' => '*')
1568
+ assert_status 412
1569
+ assert_body ''
1570
+ end
1571
+
1572
+ it 'returns 200 when If-Match is * for existing resources' do
1573
+ mock_app do
1574
+ post '/' do
1575
+ etag 'foo', :new_resource => false
1576
+ 'ok'
1577
+ end
1578
+ end
1579
+
1580
+ post('/', {}, 'HTTP_IF_MATCH' => '*')
1581
+ assert_status 200
1582
+ assert_body 'ok'
1583
+ end
1584
+
1585
+ it 'returns 412 when If-Match does not include the etag' do
1586
+ mock_app do
1587
+ post '/' do
1588
+ etag 'foo'
1589
+ 'ok'
1590
+ end
1591
+ end
1592
+
1593
+ post('/', {}, 'HTTP_IF_MATCH' => '"bar"')
1594
+ assert_status 412
1595
+ assert_body ''
1596
+ end
1597
+ end
1017
1598
  end
1018
1599
 
1019
1600
  it 'uses a weak etag with the :weak option' do
1020
- mock_app {
1601
+ mock_app do
1021
1602
  get '/' do
1022
1603
  etag 'FOO', :weak
1023
1604
  "that's weak, dude."
1024
1605
  end
1025
- }
1606
+ end
1026
1607
  get '/'
1027
1608
  assert_equal 'W/"FOO"', response['ETag']
1028
1609
  end
@@ -0,0 +1,45 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+ require 'rack'
3
+
4
+ class RackTest < Test::Unit::TestCase
5
+ setup do
6
+ @foo = Sinatra.new { get('/foo') { 'foo' }}
7
+ @bar = Sinatra.new { get('/bar') { 'bar' }}
8
+ end
9
+
10
+ def build(*middleware)
11
+ endpoint = middleware.pop
12
+ @app = Rack::Builder.app do
13
+ middleware.each { |m| use m }
14
+ run endpoint
15
+ end
16
+ end
17
+
18
+ def check(*middleware)
19
+ build(*middleware)
20
+ assert get('/foo').ok?
21
+ assert_body 'foo'
22
+ assert get('/bar').ok?
23
+ assert_body 'bar'
24
+ end
25
+
26
+ it 'works as middleware in front of Rack::Lock, with lock enabled' do
27
+ @foo.enable :lock
28
+ check(@foo, Rack::Lock, @bar)
29
+ end
30
+
31
+ it 'works as middleware behind Rack::Lock, with lock enabled' do
32
+ @foo.enable :lock
33
+ check(Rack::Lock, @foo, @bar)
34
+ end
35
+
36
+ it 'works as middleware in front of Rack::Lock, with lock disabled' do
37
+ @foo.disable :lock
38
+ check(@foo, Rack::Lock, @bar)
39
+ end
40
+
41
+ it 'works as middleware behind Rack::Lock, with lock disabled' do
42
+ @foo.disable :lock
43
+ check(Rack::Lock, @foo, @bar)
44
+ end
45
+ end
metadata CHANGED
@@ -1,16 +1,10 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: sinatra
3
- version: !ruby/object:Gem::Version
4
- hash: 585748570905747555
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0.g
5
5
  prerelease: 6
6
- segments:
7
- - 1
8
- - 3
9
- - 0
10
- - f
11
- version: 1.3.0.f
12
6
  platform: ruby
13
- authors:
7
+ authors:
14
8
  - Blake Mizerany
15
9
  - Ryan Tomayko
16
10
  - Simon Rozet
@@ -18,62 +12,47 @@ authors:
18
12
  autorequire:
19
13
  bindir: bin
20
14
  cert_chain: []
21
-
22
- date: 2011-09-11 00:00:00 -07:00
23
- default_executable:
24
- dependencies:
25
- - !ruby/object:Gem::Dependency
15
+ date: 2011-09-25 00:00:00.000000000Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
26
18
  name: rack
27
- prerelease: false
28
- requirement: &id001 !ruby/object:Gem::Requirement
19
+ requirement: &2158234720 !ruby/object:Gem::Requirement
29
20
  none: false
30
- requirements:
21
+ requirements:
31
22
  - - ~>
32
- - !ruby/object:Gem::Version
33
- hash: 2609263317959306961
34
- segments:
35
- - 1
36
- - 3
37
- version: "1.3"
23
+ - !ruby/object:Gem::Version
24
+ version: '1.3'
38
25
  type: :runtime
39
- version_requirements: *id001
40
- - !ruby/object:Gem::Dependency
41
- name: rack-protection
42
26
  prerelease: false
43
- requirement: &id002 !ruby/object:Gem::Requirement
27
+ version_requirements: *2158234720
28
+ - !ruby/object:Gem::Dependency
29
+ name: rack-protection
30
+ requirement: &2158234220 !ruby/object:Gem::Requirement
44
31
  none: false
45
- requirements:
32
+ requirements:
46
33
  - - ~>
47
- - !ruby/object:Gem::Version
48
- hash: 3882215148898798703
49
- segments:
50
- - 1
51
- - 1
52
- version: "1.1"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.1'
53
36
  type: :runtime
54
- version_requirements: *id002
55
- - !ruby/object:Gem::Dependency
56
- name: tilt
57
37
  prerelease: false
58
- requirement: &id003 !ruby/object:Gem::Requirement
38
+ version_requirements: *2158234220
39
+ - !ruby/object:Gem::Dependency
40
+ name: tilt
41
+ requirement: &2158233760 !ruby/object:Gem::Requirement
59
42
  none: false
60
- requirements:
43
+ requirements:
61
44
  - - ~>
62
- - !ruby/object:Gem::Version
63
- hash: 2609263317959306961
64
- segments:
65
- - 1
66
- - 3
67
- version: "1.3"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.3'
68
47
  type: :runtime
69
- version_requirements: *id003
70
- description: Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort.
48
+ prerelease: false
49
+ version_requirements: *2158233760
50
+ description: Sinatra is a DSL for quickly creating web applications in Ruby with minimal
51
+ effort.
71
52
  email: sinatrarb@googlegroups.com
72
53
  executables: []
73
-
74
54
  extensions: []
75
-
76
- extra_rdoc_files:
55
+ extra_rdoc_files:
77
56
  - README.de.rdoc
78
57
  - README.es.rdoc
79
58
  - README.fr.rdoc
@@ -85,7 +64,7 @@ extra_rdoc_files:
85
64
  - README.ru.rdoc
86
65
  - README.zh.rdoc
87
66
  - LICENSE
88
- files:
67
+ files:
89
68
  - .gitignore
90
69
  - .travis.yml
91
70
  - .yardopts
@@ -133,6 +112,7 @@ files:
133
112
  - test/middleware_test.rb
134
113
  - test/nokogiri_test.rb
135
114
  - test/public/favicon.ico
115
+ - test/rack_test.rb
136
116
  - test/radius_test.rb
137
117
  - test/rdoc_test.rb
138
118
  - test/readme_test.rb
@@ -191,48 +171,40 @@ files:
191
171
  - test/views/layout2.test
192
172
  - test/views/nested.str
193
173
  - test/views/utf8.erb
194
- has_rdoc: true
195
174
  homepage: http://www.sinatrarb.com/
196
175
  licenses: []
197
-
198
176
  post_install_message:
199
- rdoc_options:
177
+ rdoc_options:
200
178
  - --line-numbers
201
179
  - --inline-source
202
180
  - --title
203
181
  - Sinatra
204
182
  - --main
205
183
  - README.rdoc
206
- require_paths:
184
+ require_paths:
207
185
  - lib
208
- required_ruby_version: !ruby/object:Gem::Requirement
186
+ required_ruby_version: !ruby/object:Gem::Requirement
209
187
  none: false
210
- requirements:
211
- - - ">="
212
- - !ruby/object:Gem::Version
213
- hash: 2002549777813010636
214
- segments:
188
+ requirements:
189
+ - - ! '>='
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ segments:
215
193
  - 0
216
- version: "0"
217
- required_rubygems_version: !ruby/object:Gem::Requirement
194
+ hash: -519161154716123522
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
218
196
  none: false
219
- requirements:
220
- - - ">"
221
- - !ruby/object:Gem::Version
222
- hash: 1960248522488440439
223
- segments:
224
- - 1
225
- - 3
226
- - 1
197
+ requirements:
198
+ - - ! '>'
199
+ - !ruby/object:Gem::Version
227
200
  version: 1.3.1
228
201
  requirements: []
229
-
230
202
  rubyforge_project:
231
- rubygems_version: 1.5.2
203
+ rubygems_version: 1.8.6
232
204
  signing_key:
233
205
  specification_version: 3
234
206
  summary: Classy web-development dressed in a DSL
235
- test_files:
207
+ test_files:
236
208
  - test/base_test.rb
237
209
  - test/builder_test.rb
238
210
  - test/coffee_test.rb
@@ -251,6 +223,7 @@ test_files:
251
223
  - test/markdown_test.rb
252
224
  - test/middleware_test.rb
253
225
  - test/nokogiri_test.rb
226
+ - test/rack_test.rb
254
227
  - test/radius_test.rb
255
228
  - test/rdoc_test.rb
256
229
  - test/readme_test.rb