gin 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/History.rdoc +11 -0
  2. data/lib/gin.rb +1 -4
  3. data/lib/gin/app.rb +84 -31
  4. data/test/test_app.rb +60 -27
  5. metadata +3 -3
@@ -1,3 +1,14 @@
1
+ === 1.0.2 / 2013-03-12
2
+
3
+ * Minor Enhancements
4
+ * Routes have priority over static assets
5
+ * Rack::Session and Rack::Protection are off by default
6
+ * When running as middleware, don't run internal middleware
7
+ if going to next rack app
8
+
9
+ * Bugfixes
10
+ * Block on reloading to avoid multi-threading issues
11
+
1
12
  === 1.0.2 / 2013-03-08
2
13
 
3
14
  * Minor Enhancements
data/lib/gin.rb CHANGED
@@ -1,11 +1,8 @@
1
1
  require 'logger'
2
-
3
2
  require 'rack'
4
- require 'rack-protection'
5
-
6
3
 
7
4
  class Gin
8
- VERSION = '1.0.2'
5
+ VERSION = '1.0.3'
9
6
 
10
7
  LIB_DIR = File.expand_path("..", __FILE__) #:nodoc:
11
8
  PUBLIC_DIR = File.expand_path("../../public/", __FILE__) #:nodoc:
@@ -18,7 +18,11 @@ class Gin::App
18
18
 
19
19
  RACK_KEYS = { #:nodoc:
20
20
  :stack => 'gin.stack'.freeze,
21
+ :http_route => 'gin.http_route'.freeze,
21
22
  :path_params => 'gin.path_query_hash'.freeze,
23
+ :controller => 'gin.controller'.freeze,
24
+ :action => 'gin.action'.freeze,
25
+ :static => 'gin.static'.freeze,
22
26
  :reloaded => 'gin.reloaded'.freeze,
23
27
  :errors => 'gin.errors'.freeze
24
28
  }.freeze
@@ -312,11 +316,11 @@ class Gin::App
312
316
 
313
317
  ##
314
318
  # Use rack sessions or not. Supports assigning
315
- # hash for options. Defaults to true.
319
+ # hash for options. Defaults to false.
316
320
 
317
321
  def self.sessions opts=nil
318
322
  @session = opts unless opts.nil?
319
- @session = true if @session.nil?
323
+ @session = false if @session.nil?
320
324
  @session
321
325
  end
322
326
 
@@ -333,11 +337,11 @@ class Gin::App
333
337
 
334
338
  ##
335
339
  # Use rack-protection or not. Supports assigning
336
- # hash for options. Defaults to true.
340
+ # hash for options. Defaults to false.
337
341
 
338
342
  def self.protection opts=nil
339
343
  @protection = opts unless opts.nil?
340
- @protection = true if @protection.nil?
344
+ @protection = false if @protection.nil?
341
345
  @protection
342
346
  end
343
347
 
@@ -431,13 +435,17 @@ class Gin::App
431
435
 
432
436
  def reload!
433
437
  return unless autoreload
434
- self.class.erase! [self.class.source_file],
435
- [self.class.name.split("::").last],
436
- self.class.namespace
438
+ @mutex ||= Mutex.new
437
439
 
438
- self.class.erase_dependencies!
439
- Object.send(:require, self.class.source_file)
440
- @app = self.class.source_class.new @rack_app, @logger
440
+ @mutex.synchronize do
441
+ self.class.erase! [self.class.source_file],
442
+ [self.class.name.split("::").last],
443
+ self.class.namespace
444
+
445
+ self.class.erase_dependencies!
446
+ Object.send(:require, self.class.source_file)
447
+ @app = self.class.source_class.new @rack_app, @logger
448
+ end
441
449
  end
442
450
 
443
451
 
@@ -445,18 +453,40 @@ class Gin::App
445
453
  # Default Rack call method.
446
454
 
447
455
  def call env
448
- if filename = static?(env)
449
- return error_delegate.exec(self, env){ send_file filename }
456
+ try_autoreload(env)
457
+
458
+ if @app.route!(env)
459
+ @app.call!(env)
460
+
461
+ elsif @app.static!(env)
462
+ @app.call_static(env)
463
+
464
+ elsif @rack_app
465
+ @rack_app.call(env)
466
+
467
+ else
468
+ @app.call!(env)
450
469
  end
470
+ end
451
471
 
452
- if autoreload && !env[RACK_KEYS[:reloaded]]
453
- env[RACK_KEYS[:reloaded]] = true
454
- reload!
455
- @app.call env
456
472
 
457
- elsif env[RACK_KEYS[:stack]]
473
+ ##
474
+ # Check if autoreload is needed and reload.
475
+
476
+ def try_autoreload env
477
+ return if env[RACK_KEYS[:reloaded]]
478
+ env[RACK_KEYS[:reloaded]] = true
479
+ reload!
480
+ end
481
+
482
+
483
+ ##
484
+ # Call App instance stack without static file lookup or reloading.
485
+
486
+ def call! env
487
+ if env[RACK_KEYS[:stack]]
458
488
  env.delete RACK_KEYS[:stack]
459
- @app.call! env
489
+ dispatch env, env[RACK_KEYS[:controller]], env[RACK_KEYS[:action]]
460
490
 
461
491
  else
462
492
  env[RACK_KEYS[:stack]] = true
@@ -466,29 +496,53 @@ class Gin::App
466
496
 
467
497
 
468
498
  ##
469
- # Call App instance without internal middleware or reloading.
470
-
471
- def call! env
472
- ctrl, action, env[RACK_KEYS[:path_params]] =
473
- router.resources_for env['REQUEST_METHOD'], env['PATH_INFO']
499
+ # Returns a static file Rack response Array from the given gin.static
500
+ # env filename.
474
501
 
475
- dispatch env, ctrl, action
502
+ def call_static env
503
+ error_delegate.exec(self, env){ send_file env[RACK_KEYS[:static]] }
476
504
  end
477
505
 
478
506
 
479
- STATIC_PATH_CLEANER = %r{\.+/|/\.+} #:nodoc:
507
+ ##
508
+ # Check if the request is for a static file and set the gin.static env
509
+ # variable to the filepath.
510
+
511
+ def static! env
512
+ filepath = %w{GET HEAD}.include?(env['REQUEST_METHOD']) &&
513
+ asset(env['PATH_INFO'])
514
+
515
+ filepath ? (env[RACK_KEYS[:static]] = filepath) :
516
+ env.delete(RACK_KEYS[:static])
517
+
518
+ !!env[RACK_KEYS[:static]]
519
+ end
520
+
480
521
 
481
522
  ##
482
- # Check if the request is for a static file.
523
+ # Check if the request routes to a controller and action and set
524
+ # gin.controller, gin.action, gin.path_query_hash,
525
+ # and gin.http_route env variables.
483
526
 
484
- def static? env
485
- %w{GET HEAD}.include?(env['REQUEST_METHOD']) && asset(env['PATH_INFO'])
527
+ def route! env
528
+ http_route = "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
529
+ return true if env[RACK_KEYS[:http_route]] == http_route
530
+
531
+ env[RACK_KEYS[:controller]], env[RACK_KEYS[:action]], env[RACK_KEYS[:path_params]] =
532
+ router.resources_for env['REQUEST_METHOD'], env['PATH_INFO']
533
+
534
+ env[RACK_KEYS[:http_route]] = http_route
535
+
536
+ !!(env[RACK_KEYS[:controller]] && env[RACK_KEYS[:action]])
486
537
  end
487
538
 
488
539
 
540
+ STATIC_PATH_CLEANER = %r{\.+/|/\.+} #:nodoc:
541
+
489
542
  ##
490
543
  # Check if an asset exists.
491
544
  # Returns the full path to the asset if found, otherwise nil.
545
+ # Does not support ./ or ../ for security reasons.
492
546
 
493
547
  def asset path
494
548
  path = path.gsub STATIC_PATH_CLEANER, ""
@@ -511,9 +565,6 @@ class Gin::App
511
565
 
512
566
  ctrl.new(self, env).call_action action
513
567
 
514
- rescue Gin::NotFound => err
515
- @rack_app ? @rack_app.call(env) : handle_error(err, env)
516
-
517
568
  rescue ::Exception => err
518
569
  handle_error(err, env)
519
570
  end
@@ -564,6 +615,8 @@ class Gin::App
564
615
 
565
616
  def setup_protection builder
566
617
  return unless protection
618
+ require 'rack-protection' unless defined?(Rack::Protection)
619
+
567
620
  options = Hash === protection ? protection.dup : {}
568
621
  options[:except] = Array options[:except]
569
622
  options[:except] += [:session_hijacking, :remote_token] unless sessions
@@ -134,7 +134,7 @@ class AppTest < Test::Unit::TestCase
134
134
 
135
135
 
136
136
  def test_protection
137
- assert_equal true, FooApp.protection
137
+ assert_equal false, FooApp.protection
138
138
 
139
139
  FooApp.protection(test: "thing")
140
140
  assert_equal({test:"thing"}, FooApp.protection)
@@ -171,7 +171,7 @@ class AppTest < Test::Unit::TestCase
171
171
 
172
172
 
173
173
  def test_sessions
174
- assert_equal true, FooApp.sessions
174
+ assert_equal false, FooApp.sessions
175
175
 
176
176
  FooApp.sessions(test: "thing")
177
177
  assert_equal({test:"thing"}, FooApp.sessions)
@@ -247,7 +247,7 @@ class AppTest < Test::Unit::TestCase
247
247
  assert FooMiddleware.called?
248
248
 
249
249
  FooMiddleware.reset!
250
- myapp.call!({'rack.input' => "", 'PATH_INFO' => '/foo', 'REQUEST_METHOD' => 'GET'})
250
+ myapp.dispatch({'rack.input' => "", 'PATH_INFO' => '/foo', 'REQUEST_METHOD' => 'GET'}, FooController, :index)
251
251
  assert !FooMiddleware.called?
252
252
  end
253
253
 
@@ -273,6 +273,18 @@ class AppTest < Test::Unit::TestCase
273
273
  end
274
274
 
275
275
 
276
+ def test_call_rack_app
277
+ env = {'rack.input' => "", 'PATH_INFO' => '/bad', 'REQUEST_METHOD' => 'GET'}
278
+ expected = [200, {'Content-Length'=>"5"}, "AHOY!"]
279
+ myapp = lambda{|env| expected }
280
+ @app = FooApp.new myapp
281
+
282
+ resp = @app.call env
283
+ assert_equal expected, resp
284
+ end
285
+
286
+
287
+
276
288
  def test_call!
277
289
  resp = @app.call! 'rack.input' => "",
278
290
  'PATH_INFO' => '/foo',
@@ -324,17 +336,6 @@ class AppTest < Test::Unit::TestCase
324
336
  end
325
337
 
326
338
 
327
- def test_dispatch_rack_app
328
- env = {'rack.input' => "", 'PATH_INFO' => '/bad', 'REQUEST_METHOD' => 'GET'}
329
- expected = [200, {'Content-Length'=>"5"}, "AHOY!"]
330
- myapp = lambda{|env| expected }
331
- @app = FooApp.new myapp
332
-
333
- resp = @app.dispatch env, nil, nil
334
- assert_equal expected, resp
335
- end
336
-
337
-
338
339
  def test_dispatch_error
339
340
  FooApp.environment 'test'
340
341
  env = {'rack.input' => "", 'PATH_INFO' => '/bad', 'REQUEST_METHOD' => 'GET'}
@@ -457,33 +458,65 @@ class AppTest < Test::Unit::TestCase
457
458
  FooApp.public_dir "./test/mock_config"
458
459
  assert @app.asset("backend.yml") =~ %r{/gin/test/mock_config/backend\.yml$}
459
460
  assert @app.asset("500.html") =~ %r{/gin/public/500\.html$}
461
+
462
+ assert !@app.asset("foo/../../mock_config/backend.yml")
463
+ assert !@app.asset("foo/../../public/500.html")
464
+ end
465
+
466
+
467
+ def test_bad_asset
468
+ FooApp.public_dir "./test/mock_config"
460
469
  assert_nil @app.asset("bad_file")
461
470
  assert_nil @app.asset("../../History.rdoc")
471
+
472
+ path = File.join(FooApp.public_dir, "../.././test/mock_config/../../History.rdoc")
473
+ assert File.file?(path)
474
+ assert_nil @app.asset("../.././test/mock_config/../../History.rdoc")
462
475
  end
463
476
 
464
477
 
465
- def test_static
478
+ def test_static_no_file
466
479
  env = {'rack.input' => "", 'PATH_INFO' => '/foo', 'REQUEST_METHOD' => 'GET'}
467
- assert !@app.static?(env)
480
+ assert !@app.static!(env)
481
+ end
482
+
468
483
 
484
+ def test_static
485
+ env = {'rack.input' => "", 'REQUEST_METHOD' => 'GET'}
469
486
  env['PATH_INFO'] = '/500.html'
470
- assert @app.static?(env) =~ %r{/gin/public/500\.html$}
487
+ assert @app.static!(env)
488
+ assert env['gin.static'] =~ %r{/gin/public/500\.html$}
489
+ end
471
490
 
472
- env['PATH_INFO'] = '../../../500.html'
473
- assert @app.static?(env) =~ %r{/gin/public/500\.html$}
474
491
 
492
+ def test_static_updir
493
+ env = {'rack.input' => "", 'REQUEST_METHOD' => 'GET'}
494
+ env['PATH_INFO'] = '../../gin/public/500.html'
495
+ assert !@app.static!(env)
496
+ assert !env['gin.static']
497
+ end
498
+
499
+
500
+ def test_static_head
501
+ env = {'rack.input' => "", 'REQUEST_METHOD' => 'GET'}
475
502
  env['REQUEST_METHOD'] = 'HEAD'
476
- assert @app.static?(env) =~ %r{/gin/public/500\.html$}
503
+ env['PATH_INFO'] = '/500.html'
504
+ assert @app.static!(env)
505
+ assert env['gin.static'] =~ %r{/gin/public/500\.html$}
506
+ end
477
507
 
508
+
509
+ def test_non_static_verbs
510
+ env = {'rack.input' => "", 'REQUEST_METHOD' => 'GET'}
478
511
  env['PATH_INFO'] = '/backend.yml'
479
- assert !@app.static?(env)
480
512
 
481
513
  FooApp.public_dir "./test/mock_config"
482
- assert @app.static?(env) =~ %r{/gin/test/mock_config/backend\.yml$}
514
+ assert @app.static!(env)
515
+ assert env['gin.static'] =~ %r{/gin/test/mock_config/backend\.yml$}
483
516
 
484
517
  %w{POST PUT DELETE TRACE OPTIONS}.each do |verb|
485
518
  env['REQUEST_METHOD'] = verb
486
- assert !@app.static?(env), "#{verb} should not be a static request"
519
+ assert !@app.static!(env), "#{verb} should not be a static request"
487
520
  end
488
521
  end
489
522
 
@@ -568,7 +601,10 @@ class AppTest < Test::Unit::TestCase
568
601
  end
569
602
 
570
603
 
571
- def test_build_default_middleware
604
+ def test_build_w_middleware
605
+ FooApp.sessions true
606
+ FooApp.protection true
607
+ @app = FooApp.new
572
608
  stack = @app.instance_variable_get("@stack")
573
609
  assert Rack::Session::Cookie === stack
574
610
  assert Rack::Protection::FrameOptions === stack.instance_variable_get("@app")
@@ -583,9 +619,6 @@ class AppTest < Test::Unit::TestCase
583
619
 
584
620
 
585
621
  def test_build_no_middleware
586
- FooApp.sessions false
587
- FooApp.protection false
588
- @app = FooApp.new
589
622
  stack = @app.instance_variable_get("@stack")
590
623
  assert_equal @app, stack
591
624
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-08 00:00:00.000000000 Z
12
+ date: 2013-03-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -150,7 +150,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
150
150
  version: '0'
151
151
  segments:
152
152
  - 0
153
- hash: -3176863341583704067
153
+ hash: 3408999320760955315
154
154
  required_rubygems_version: !ruby/object:Gem::Requirement
155
155
  none: false
156
156
  requirements: