sinatra 1.4.8 → 2.0.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +77 -47
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +37 -49
- data/MAINTENANCE.md +42 -0
- data/README.de.md +5 -5
- data/README.es.md +5 -5
- data/README.fr.md +9 -9
- data/README.hu.md +3 -3
- data/README.ja.md +19 -8
- data/README.ko.md +8 -8
- data/README.md +90 -61
- data/README.pt-br.md +3 -3
- data/README.pt-pt.md +2 -2
- data/README.ru.md +42 -26
- data/README.zh.md +8 -8
- data/Rakefile +0 -6
- data/SECURITY.md +35 -0
- data/lib/sinatra/base.rb +113 -161
- data/lib/sinatra/main.rb +1 -0
- data/lib/sinatra/show_exceptions.rb +8 -8
- data/lib/sinatra/version.rb +1 -1
- data/sinatra.gemspec +7 -4
- metadata +34 -168
- data/lib/sinatra/ext.rb +0 -17
- data/test/asciidoctor_test.rb +0 -72
- data/test/base_test.rb +0 -167
- data/test/builder_test.rb +0 -91
- data/test/coffee_test.rb +0 -96
- data/test/compile_test.rb +0 -183
- data/test/contest.rb +0 -91
- data/test/creole_test.rb +0 -65
- data/test/delegator_test.rb +0 -160
- data/test/encoding_test.rb +0 -20
- data/test/erb_test.rb +0 -116
- data/test/extensions_test.rb +0 -98
- data/test/filter_test.rb +0 -487
- data/test/haml_test.rb +0 -109
- data/test/helper.rb +0 -132
- data/test/helpers_test.rb +0 -1917
- data/test/integration/app.rb +0 -79
- data/test/integration_helper.rb +0 -236
- data/test/integration_test.rb +0 -104
- data/test/less_test.rb +0 -69
- data/test/liquid_test.rb +0 -77
- data/test/mapped_error_test.rb +0 -285
- data/test/markaby_test.rb +0 -80
- data/test/markdown_test.rb +0 -85
- data/test/mediawiki_test.rb +0 -68
- data/test/middleware_test.rb +0 -68
- data/test/nokogiri_test.rb +0 -67
- data/test/public/favicon.ico +0 -0
- data/test/public/hello+world.txt +0 -1
- data/test/rabl_test.rb +0 -89
- data/test/rack_test.rb +0 -45
- data/test/radius_test.rb +0 -59
- data/test/rdoc_test.rb +0 -66
- data/test/readme_test.rb +0 -130
- data/test/request_test.rb +0 -100
- data/test/response_test.rb +0 -63
- data/test/result_test.rb +0 -76
- data/test/route_added_hook_test.rb +0 -59
- data/test/routing_test.rb +0 -1456
- data/test/sass_test.rb +0 -115
- data/test/scss_test.rb +0 -88
- data/test/server_test.rb +0 -56
- data/test/settings_test.rb +0 -582
- data/test/sinatra_test.rb +0 -12
- data/test/slim_test.rb +0 -102
- data/test/static_test.rb +0 -266
- data/test/streaming_test.rb +0 -149
- data/test/stylus_test.rb +0 -90
- data/test/templates_test.rb +0 -382
- data/test/textile_test.rb +0 -65
- data/test/views/a/in_a.str +0 -1
- data/test/views/ascii.erb +0 -2
- data/test/views/b/in_b.str +0 -1
- data/test/views/calc.html.erb +0 -1
- data/test/views/error.builder +0 -3
- data/test/views/error.erb +0 -3
- data/test/views/error.haml +0 -3
- data/test/views/error.sass +0 -2
- data/test/views/explicitly_nested.str +0 -1
- data/test/views/foo/hello.test +0 -1
- data/test/views/hello.asciidoc +0 -1
- data/test/views/hello.builder +0 -1
- data/test/views/hello.coffee +0 -1
- data/test/views/hello.creole +0 -1
- data/test/views/hello.erb +0 -1
- data/test/views/hello.haml +0 -1
- data/test/views/hello.less +0 -5
- data/test/views/hello.liquid +0 -1
- data/test/views/hello.mab +0 -1
- data/test/views/hello.md +0 -1
- data/test/views/hello.mediawiki +0 -1
- data/test/views/hello.nokogiri +0 -1
- data/test/views/hello.rabl +0 -2
- data/test/views/hello.radius +0 -1
- data/test/views/hello.rdoc +0 -1
- data/test/views/hello.sass +0 -2
- data/test/views/hello.scss +0 -3
- data/test/views/hello.slim +0 -1
- data/test/views/hello.str +0 -1
- data/test/views/hello.styl +0 -2
- data/test/views/hello.test +0 -1
- data/test/views/hello.textile +0 -1
- data/test/views/hello.wlang +0 -1
- data/test/views/hello.yajl +0 -1
- data/test/views/layout2.builder +0 -3
- data/test/views/layout2.erb +0 -2
- data/test/views/layout2.haml +0 -2
- data/test/views/layout2.liquid +0 -2
- data/test/views/layout2.mab +0 -2
- data/test/views/layout2.nokogiri +0 -3
- data/test/views/layout2.rabl +0 -3
- data/test/views/layout2.radius +0 -2
- data/test/views/layout2.slim +0 -3
- data/test/views/layout2.str +0 -2
- data/test/views/layout2.test +0 -1
- data/test/views/layout2.wlang +0 -2
- data/test/views/nested.str +0 -1
- data/test/views/utf8.erb +0 -2
- data/test/wlang_test.rb +0 -87
- data/test/yajl_test.rb +0 -86
data/README.pt-br.md
CHANGED
@@ -180,7 +180,7 @@ end
|
|
180
180
|
Rotas podem casar com expressões regulares:
|
181
181
|
|
182
182
|
```ruby
|
183
|
-
get
|
183
|
+
get /\/ola\/([\w]+)/ do
|
184
184
|
"Olá, #{params['captures'].first}!"
|
185
185
|
end
|
186
186
|
```
|
@@ -362,7 +362,7 @@ end
|
|
362
362
|
Ou, usando algo mais denso à frente:
|
363
363
|
|
364
364
|
```ruby
|
365
|
-
get %r{
|
365
|
+
get %r{(?!/index)} do
|
366
366
|
# ...
|
367
367
|
end
|
368
368
|
```
|
@@ -1417,7 +1417,7 @@ configure do
|
|
1417
1417
|
end
|
1418
1418
|
```
|
1419
1419
|
|
1420
|
-
Rodando somente quando o ambiente (`
|
1420
|
+
Rodando somente quando o ambiente (`APP_ENV` environment variável) é
|
1421
1421
|
setado para `:production`:
|
1422
1422
|
|
1423
1423
|
```ruby
|
data/README.pt-pt.md
CHANGED
@@ -88,7 +88,7 @@ end
|
|
88
88
|
Rotas correspondem-se com expressões regulares:
|
89
89
|
|
90
90
|
```ruby
|
91
|
-
get
|
91
|
+
get /\/ola\/([\w]+)/ do
|
92
92
|
"Olá, #{params['captures'].first}!"
|
93
93
|
end
|
94
94
|
```
|
@@ -477,7 +477,7 @@ configure do
|
|
477
477
|
end
|
478
478
|
```
|
479
479
|
|
480
|
-
Correndo somente quando o ambiente (`
|
480
|
+
Correndo somente quando o ambiente (`APP_ENV` environment variável) é
|
481
481
|
definido para `:production`:
|
482
482
|
|
483
483
|
```ruby
|
data/README.ru.md
CHANGED
@@ -42,6 +42,7 @@
|
|
42
42
|
* [Фильтры](#Фильтры)
|
43
43
|
* [Методы-помощники](#Методы-помощники)
|
44
44
|
* [Использование сессий](#Использование-сессий)
|
45
|
+
* [Выбор вашей собственной "прослойки" сессии](#Выбор-вашей-собственной-прослойки-сессий)
|
45
46
|
* [Прерывание](#Прерывание)
|
46
47
|
* [Передача](#Передача)
|
47
48
|
* [Вызов другого маршрута](#Вызов-другого-маршрута)
|
@@ -208,7 +209,7 @@ end
|
|
208
209
|
Регулярные выражения в качестве шаблонов маршрутов:
|
209
210
|
|
210
211
|
```ruby
|
211
|
-
get
|
212
|
+
get /\/hello\/([\w]+)/ do
|
212
213
|
"Hello, #{params['captures'].first}!"
|
213
214
|
end
|
214
215
|
```
|
@@ -377,7 +378,7 @@ end
|
|
377
378
|
Или с использованием негативного просмотра вперед:
|
378
379
|
|
379
380
|
```ruby
|
380
|
-
get %r{
|
381
|
+
get %r{(?!/index)} do
|
381
382
|
# ...
|
382
383
|
end
|
383
384
|
```
|
@@ -1341,25 +1342,6 @@ get '/:value' do
|
|
1341
1342
|
end
|
1342
1343
|
```
|
1343
1344
|
|
1344
|
-
Заметьте, что при использовании `enable :sessions` все данные сохраняются в
|
1345
|
-
куках (cookies). Это может быть не совсем то, что вы хотите (например,
|
1346
|
-
сохранение больших объемов данных увеличит ваш трафик). В таком случае вы
|
1347
|
-
можете использовать альтернативную Rack "прослойку" (middleware), реализующую
|
1348
|
-
механизм сессий. Для этого *не надо* вызывать `enable :sessions`, вместо этого
|
1349
|
-
следует подключить ее так же, как и любую другую "прослойку":
|
1350
|
-
|
1351
|
-
```ruby
|
1352
|
-
use Rack::Session::Pool, :expire_after => 2592000
|
1353
|
-
|
1354
|
-
get '/' do
|
1355
|
-
"value = " << session['value'].inspect
|
1356
|
-
end
|
1357
|
-
|
1358
|
-
get '/:value' do
|
1359
|
-
session['value'] = params['value']
|
1360
|
-
end
|
1361
|
-
```
|
1362
|
-
|
1363
1345
|
Для повышения безопасности данные сессии в куках подписываются секретным
|
1364
1346
|
ключом. Секретный ключ генерируется Sinatra. Тем не менее, так как этот ключ
|
1365
1347
|
будет меняться с каждым запуском приложения, вы, возможно, захотите установить
|
@@ -1384,6 +1366,40 @@ foo.com, добавьте *.* перед доменом:
|
|
1384
1366
|
set :sessions, :domain => '.foo.com'
|
1385
1367
|
```
|
1386
1368
|
|
1369
|
+
#### Выбор вашей собственной "прослойки" сессии
|
1370
|
+
|
1371
|
+
Заметьте, что при использовании `enable :sessions` все данные сохраняются в
|
1372
|
+
куках (cookies). Это может быть не совсем то, что вы хотите (например,
|
1373
|
+
сохранение больших объемов данных увеличит ваш трафик). В таком случае вы
|
1374
|
+
можете использовать альтернативную Rack "прослойку" (middleware), реализующую
|
1375
|
+
механизм сессий. Для этого используете один из способов ниже:
|
1376
|
+
|
1377
|
+
```ruby
|
1378
|
+
enable :sessions
|
1379
|
+
set :session_store, Rack::Session::Pool
|
1380
|
+
```
|
1381
|
+
|
1382
|
+
Или установите параметры сессии с помощью хеша опций:
|
1383
|
+
|
1384
|
+
```ruby
|
1385
|
+
set :sessions, :expire_after => 2592000
|
1386
|
+
set :session_store, Rack::Session::Pool
|
1387
|
+
```
|
1388
|
+
|
1389
|
+
Вы так же можете не вызывать `enable :sessions`, а вместо этого вызывать
|
1390
|
+
необходимую вам прослойку так же, как вы это обычно делаете. Очень важно
|
1391
|
+
обратить внимание на то, что когда вы используете этот метод, основной способ
|
1392
|
+
защиты сессии **не будет включен по умолчанию**. Если вы хотите включить защиту,
|
1393
|
+
вам нужно добавить следующие строчки:
|
1394
|
+
|
1395
|
+
```ruby
|
1396
|
+
use Rack::Session::Pool, :expire_after => 2592000
|
1397
|
+
use Rack::Protection::RemoteToken
|
1398
|
+
use Rack::Protection::SessionHijacking
|
1399
|
+
```
|
1400
|
+
|
1401
|
+
Смотрите секцию "Настройка защиты от атак" для более подробной информации.
|
1402
|
+
|
1387
1403
|
### Прерывание
|
1388
1404
|
|
1389
1405
|
Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута,
|
@@ -2022,7 +2038,7 @@ configure do
|
|
2022
2038
|
end
|
2023
2039
|
```
|
2024
2040
|
|
2025
|
-
Будет запущено, когда окружение (
|
2041
|
+
Будет запущено, когда окружение (APP_ENV переменная) `:production`:
|
2026
2042
|
|
2027
2043
|
```ruby
|
2028
2044
|
configure :production do
|
@@ -2121,8 +2137,8 @@ set :protection, :except => [:path_traversal, :session_hijacking]
|
|
2121
2137
|
|
2122
2138
|
<dt>environment</dt>
|
2123
2139
|
<dd>
|
2124
|
-
текущее окружение, по умолчанию, значение <tt>ENV['
|
2125
|
-
<tt>"development"</tt>, если <tt>ENV['
|
2140
|
+
текущее окружение, по умолчанию, значение <tt>ENV['APP_ENV']</tt> или
|
2141
|
+
<tt>"development"</tt>, если <tt>ENV['APP_ENV']</tt> недоступна.
|
2126
2142
|
</dd>
|
2127
2143
|
|
2128
2144
|
<dt>logging</dt>
|
@@ -2249,7 +2265,7 @@ set :protection, :except => [:path_traversal, :session_hijacking]
|
|
2249
2265
|
## Режим, окружение
|
2250
2266
|
|
2251
2267
|
Есть 3 предопределенных режима, окружения: `"development"`, `"production"` и
|
2252
|
-
`"test"`. Режим может быть задан через переменную окружения `
|
2268
|
+
`"test"`. Режим может быть задан через переменную окружения `APP_ENV`.
|
2253
2269
|
Значение по умолчанию — `"development"`. В этом режиме работы все шаблоны
|
2254
2270
|
перезагружаются между запросами. А также задаются специальные обработчики
|
2255
2271
|
`not_found` и `error`, чтобы вы могли увидеть стек вызовов. В окружениях
|
@@ -2412,7 +2428,7 @@ class MyAppTest < Minitest::Test
|
|
2412
2428
|
assert_equal 'Hello Frank!', last_response.body
|
2413
2429
|
end
|
2414
2430
|
|
2415
|
-
def
|
2431
|
+
def test_with_user_agent
|
2416
2432
|
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
|
2417
2433
|
assert_equal "You're using Songbird!", last_response.body
|
2418
2434
|
end
|
data/README.zh.md
CHANGED
@@ -205,7 +205,7 @@ end
|
|
205
205
|
通过正则表达式匹配路由:
|
206
206
|
|
207
207
|
```ruby
|
208
|
-
get
|
208
|
+
get /\/hello\/([\w]+)/ do
|
209
209
|
"Hello, #{params['captures'].first}!"
|
210
210
|
end
|
211
211
|
```
|
@@ -372,7 +372,7 @@ end
|
|
372
372
|
或者,使用消极向前查找:
|
373
373
|
|
374
374
|
```ruby
|
375
|
-
get %r{
|
375
|
+
get %r{(?!/index)} do
|
376
376
|
# ...
|
377
377
|
end
|
378
378
|
```
|
@@ -1933,7 +1933,7 @@ configure do
|
|
1933
1933
|
end
|
1934
1934
|
```
|
1935
1935
|
|
1936
|
-
只有当环境 (`
|
1936
|
+
只有当环境 (`APP_ENV` 环境变量) 被设定为 `:production` 时才运行:
|
1937
1937
|
|
1938
1938
|
```ruby
|
1939
1939
|
configure :production do
|
@@ -2033,8 +2033,8 @@ set :protection, :session => true
|
|
2033
2033
|
|
2034
2034
|
<dt>environment</dt>
|
2035
2035
|
<dd>
|
2036
|
-
当前环境,默认是 <tt>ENV['
|
2037
|
-
或者 <tt>"development"</tt> (如果 ENV['
|
2036
|
+
当前环境,默认是 <tt>ENV['APP_ENV']</tt>,
|
2037
|
+
或者 <tt>"development"</tt> (如果 ENV['APP_ENV'] 不可用)。
|
2038
2038
|
</dd>
|
2039
2039
|
|
2040
2040
|
<dt>logging</dt>
|
@@ -2145,15 +2145,15 @@ set :protection, :session => true
|
|
2145
2145
|
## 环境
|
2146
2146
|
|
2147
2147
|
Sinatra 中有三种预先定义的环境:"development"、"production" 和 "test"。
|
2148
|
-
环境可以通过 `
|
2148
|
+
环境可以通过 `APP_ENV` 环境变量设置。默认值为 "development"。
|
2149
2149
|
在开发环境下,每次请求都会重新加载所有模板,
|
2150
2150
|
特殊的 `not_found` 和 `error` 错误处理器会在浏览器中显示 stack trace。
|
2151
2151
|
在测试和生产环境下,模板默认会缓存。
|
2152
2152
|
|
2153
|
-
在不同的环境下运行,设置 `
|
2153
|
+
在不同的环境下运行,设置 `APP_ENV` 环境变量:
|
2154
2154
|
|
2155
2155
|
```shell
|
2156
|
-
|
2156
|
+
APP_ENV=production ruby my_app.rb
|
2157
2157
|
```
|
2158
2158
|
|
2159
2159
|
可以使用预定义的三种方法: `development?`、`test?` 和 `production?` 来检查当前环境:
|
data/Rakefile
CHANGED
data/SECURITY.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Reporting a security bug
|
2
|
+
|
3
|
+
All security bugs in Sinatra should be reported to the core team through our private mailing list [sinatra-security@googlegroups.com](https://groups.google.com/group/sinatra-security). Your report will be acknowledged within 24 hours, and you’ll receive a more detailed response to your email within 48 hours indicating the next steps in handling your report.
|
4
|
+
|
5
|
+
After the initial reply to your report the security team will endeavor to keep you informed of the progress being made towards a fix and full announcement. These updates will be sent at least every five days, in reality this is more likely to be every 24-48 hours.
|
6
|
+
|
7
|
+
If you have not received a reply to your email within 48 hours, or have not heard from the security team for the past five days there are a few steps you can take:
|
8
|
+
|
9
|
+
* Contact the current security coordinator [Zachary Scott](mailto:zzak@ruby-lang.org) directly
|
10
|
+
|
11
|
+
## Disclosure Policy
|
12
|
+
|
13
|
+
Sinatra has a 5 step disclosure policy, that is upheld to the best of our ability.
|
14
|
+
|
15
|
+
1. Security report received and is assigned a primary handler. This person will coordinate the fix and release process.
|
16
|
+
2. Problem is confirmed and, a list of all affected versions is determined. Code is audited to find any potential similar problems.
|
17
|
+
3. Fixes are prepared for all releases which are still supported. These fixes are not committed to the public repository but rather held locally pending the announcement.
|
18
|
+
4. A suggested embargo date for this vulnerability is chosen and distros@openwall is notified. This notification will include patches for all versions still under support and a contact address for packagers who need advice back-porting patches to older versions.
|
19
|
+
5. On the embargo date, the [mailing list][mailing-list] and [security list][security-list] are sent a copy of the announcement. The changes are pushed to the public repository and new gems released to rubygems.
|
20
|
+
|
21
|
+
Typically the embargo date will be set 72 hours from the time vendor-sec is first notified, however this may vary depending on the severity of the bug or difficulty in applying a fix.
|
22
|
+
|
23
|
+
This process can take some time, especially when coordination is required with maintainers of other projects. Every effort will be made to handle the bug in as timely a manner as possible, however it’s important that we follow the release process above to ensure that the disclosure is handled in a consistent manner.
|
24
|
+
|
25
|
+
## Security Updates
|
26
|
+
|
27
|
+
Security updates will be posted on the [mailing list][mailing-list] and [security list][security-list].
|
28
|
+
|
29
|
+
## Comments on this Policy
|
30
|
+
|
31
|
+
If you have any suggestions to improve this policy, please send an email the core team at [sinatrarb@googlegroups.com](https://groups.google.com/group/sinatrarb).
|
32
|
+
|
33
|
+
|
34
|
+
[mailing-list]: http://groups.google.com/group/sinatrarb/topics
|
35
|
+
[security-list]: http://groups.google.com/group/sinatra-security/topics
|
data/lib/sinatra/base.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# external dependencies
|
2
4
|
require 'rack'
|
3
5
|
require 'tilt'
|
4
6
|
require 'rack/protection'
|
7
|
+
require 'mustermann'
|
8
|
+
require 'mustermann/sinatra'
|
9
|
+
require 'mustermann/regular'
|
5
10
|
|
6
11
|
# stdlib dependencies
|
7
12
|
require 'thread'
|
@@ -10,7 +15,6 @@ require 'uri'
|
|
10
15
|
|
11
16
|
# other files we need
|
12
17
|
require 'sinatra/show_exceptions'
|
13
|
-
require 'sinatra/ext'
|
14
18
|
require 'sinatra/version'
|
15
19
|
|
16
20
|
module Sinatra
|
@@ -69,6 +73,12 @@ module Sinatra
|
|
69
73
|
request_method == "UNLINK"
|
70
74
|
end
|
71
75
|
|
76
|
+
def params
|
77
|
+
super
|
78
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
79
|
+
raise BadRequest, "Invalid query parameters: #{e.message}"
|
80
|
+
end
|
81
|
+
|
72
82
|
private
|
73
83
|
|
74
84
|
class AcceptEntry
|
@@ -151,7 +161,7 @@ module Sinatra
|
|
151
161
|
if calculate_content_length?
|
152
162
|
# if some other code has already set Content-Length, don't muck with it
|
153
163
|
# currently, this would be the static file-handler
|
154
|
-
headers["Content-Length"] = body.inject(0) { |l, p| l +
|
164
|
+
headers["Content-Length"] = body.inject(0) { |l, p| l + p.bytesize }.to_s
|
155
165
|
end
|
156
166
|
|
157
167
|
[status.to_i, headers, result]
|
@@ -221,6 +231,10 @@ module Sinatra
|
|
221
231
|
end
|
222
232
|
end
|
223
233
|
|
234
|
+
class BadRequest < TypeError #:nodoc:
|
235
|
+
def http_status; 400 end
|
236
|
+
end
|
237
|
+
|
224
238
|
class NotFound < NameError #:nodoc:
|
225
239
|
def http_status; 404 end
|
226
240
|
end
|
@@ -229,7 +243,7 @@ module Sinatra
|
|
229
243
|
module Helpers
|
230
244
|
# Set or retrieve the response status code.
|
231
245
|
def status(value = nil)
|
232
|
-
response.status = value if value
|
246
|
+
response.status = Rack::Utils.status_code(value) if value
|
233
247
|
response.status
|
234
248
|
end
|
235
249
|
|
@@ -240,7 +254,11 @@ module Sinatra
|
|
240
254
|
def block.each; yield(call) end
|
241
255
|
response.body = block
|
242
256
|
elsif value
|
243
|
-
|
257
|
+
# Rack 2.0 returns a Rack::File::Iterator here instead of
|
258
|
+
# Rack::File as it was in the previous API.
|
259
|
+
unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream)
|
260
|
+
headers.delete 'Content-Length'
|
261
|
+
end
|
244
262
|
response.body = value
|
245
263
|
else
|
246
264
|
response.body
|
@@ -264,8 +282,8 @@ module Sinatra
|
|
264
282
|
# Generates the absolute URI for a given path in the app.
|
265
283
|
# Takes Rack routers and reverse proxies into account.
|
266
284
|
def uri(addr = nil, absolute = true, add_script_name = true)
|
267
|
-
return addr if addr =~ /\A[
|
268
|
-
uri = [host =
|
285
|
+
return addr if addr =~ /\A[a-z][a-z0-9\+\.\-]*:/i
|
286
|
+
uri = [host = String.new]
|
269
287
|
if absolute
|
270
288
|
host << "http#{'s' if request.secure?}://"
|
271
289
|
if request.forwarded? or request.port != (request.secure? ? 443 : 80)
|
@@ -339,7 +357,7 @@ module Sinatra
|
|
339
357
|
|
340
358
|
# Set the Content-Disposition to "attachment" with the specified filename,
|
341
359
|
# instructing the user agents to prompt to save.
|
342
|
-
def attachment(filename = nil, disposition =
|
360
|
+
def attachment(filename = nil, disposition = :attachment)
|
343
361
|
response['Content-Disposition'] = disposition.to_s
|
344
362
|
if filename
|
345
363
|
params = '; filename="%s"' % File.basename(filename)
|
@@ -357,19 +375,19 @@ module Sinatra
|
|
357
375
|
|
358
376
|
disposition = opts[:disposition]
|
359
377
|
filename = opts[:filename]
|
360
|
-
disposition =
|
361
|
-
filename = path
|
378
|
+
disposition = :attachment if disposition.nil? and filename
|
379
|
+
filename = path if filename.nil?
|
362
380
|
attachment(filename, disposition) if disposition
|
363
381
|
|
364
382
|
last_modified opts[:last_modified] if opts[:last_modified]
|
365
383
|
|
366
|
-
file
|
367
|
-
file.
|
368
|
-
|
384
|
+
file = Rack::File.new(File.dirname(settings.app_file))
|
385
|
+
result = file.serving(request, path)
|
386
|
+
|
369
387
|
result[1].each { |k,v| headers[k] ||= v }
|
370
388
|
headers['Content-Length'] = result[1]['Content-Length']
|
371
389
|
opts[:status] &&= Integer(opts[:status])
|
372
|
-
halt opts[:status] || result[0], result[2]
|
390
|
+
halt (opts[:status] || result[0]), result[2]
|
373
391
|
rescue Errno::ENOENT
|
374
392
|
not_found
|
375
393
|
end
|
@@ -441,7 +459,7 @@ module Sinatra
|
|
441
459
|
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
442
460
|
# Any number of non-value directives (:public, :private, :no_cache,
|
443
461
|
# :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
|
444
|
-
# a Hash of value directives (:max_age, :min_stale, :
|
462
|
+
# a Hash of value directives (:max_age, :min_stale, :s_maxage).
|
445
463
|
#
|
446
464
|
# cache_control :public, :must_revalidate, :max_age => 60
|
447
465
|
# => Cache-Control: public, must-revalidate, max-age=60
|
@@ -460,7 +478,7 @@ module Sinatra
|
|
460
478
|
values.map! { |value| value.to_s.tr('_','-') }
|
461
479
|
hash.each do |key, value|
|
462
480
|
key = key.to_s.tr('_', '-')
|
463
|
-
value = value.to_i if
|
481
|
+
value = value.to_i if ['max-age', 's-maxage'].include? key
|
464
482
|
values << "#{key}=#{value}"
|
465
483
|
end
|
466
484
|
|
@@ -473,7 +491,7 @@ module Sinatra
|
|
473
491
|
# "values" arguments are passed to the #cache_control helper:
|
474
492
|
#
|
475
493
|
# expires 500, :public, :must_revalidate
|
476
|
-
# => Cache-Control: public, must-revalidate, max-age=
|
494
|
+
# => Cache-Control: public, must-revalidate, max-age=500
|
477
495
|
# => Expires: Mon, 08 Jun 2009 08:50:17 GMT
|
478
496
|
#
|
479
497
|
def expires(amount, *values)
|
@@ -585,6 +603,11 @@ module Sinatra
|
|
585
603
|
status.between? 500, 599
|
586
604
|
end
|
587
605
|
|
606
|
+
# whether or not the status is set to 400
|
607
|
+
def bad_request?
|
608
|
+
status == 400
|
609
|
+
end
|
610
|
+
|
588
611
|
# whether or not the status is set to 404
|
589
612
|
def not_found?
|
590
613
|
status == 404
|
@@ -593,22 +616,12 @@ module Sinatra
|
|
593
616
|
# Generates a Time object from the given value.
|
594
617
|
# Used by #expires and #last_modified.
|
595
618
|
def time_for(value)
|
596
|
-
if value.
|
597
|
-
value.to_time
|
598
|
-
elsif value.is_a? Time
|
599
|
-
value
|
600
|
-
elsif value.respond_to? :new_offset
|
601
|
-
# DateTime#to_time does the same on 1.9
|
602
|
-
d = value.new_offset 0
|
603
|
-
t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
|
604
|
-
t.getlocal
|
605
|
-
elsif value.respond_to? :mday
|
606
|
-
# Date#to_time does the same on 1.9
|
607
|
-
Time.local(value.year, value.mon, value.mday)
|
608
|
-
elsif value.is_a? Numeric
|
619
|
+
if value.is_a? Numeric
|
609
620
|
Time.at value
|
610
|
-
|
621
|
+
elsif value.respond_to? :to_s
|
611
622
|
Time.parse value.to_s
|
623
|
+
else
|
624
|
+
value.to_time
|
612
625
|
end
|
613
626
|
rescue ArgumentError => boom
|
614
627
|
raise boom
|
@@ -806,7 +819,8 @@ module Sinatra
|
|
806
819
|
layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false)
|
807
820
|
layout = @default_layout if layout.nil? or layout == true
|
808
821
|
layout_options = options.delete(:layout_options) || {}
|
809
|
-
content_type = options.delete(:
|
822
|
+
content_type = options.delete(:default_content_type)
|
823
|
+
content_type = options.delete(:content_type) || content_type
|
810
824
|
layout_engine = options.delete(:layout_engine) || engine
|
811
825
|
scope = options.delete(:scope) || self
|
812
826
|
options.delete(:layout)
|
@@ -863,7 +877,9 @@ module Sinatra
|
|
863
877
|
end
|
864
878
|
when Proc, String
|
865
879
|
body = data.is_a?(String) ? Proc.new { data } : data
|
866
|
-
|
880
|
+
caller = settings.caller_locations.first
|
881
|
+
path = options[:path] || caller[0]
|
882
|
+
line = options[:line] || caller[1]
|
867
883
|
template.new(path, line.to_i, options, &body)
|
868
884
|
else
|
869
885
|
raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
|
@@ -878,7 +894,7 @@ module Sinatra
|
|
878
894
|
include Helpers
|
879
895
|
include Templates
|
880
896
|
|
881
|
-
URI_INSTANCE = URI
|
897
|
+
URI_INSTANCE = URI::Parser.new
|
882
898
|
|
883
899
|
attr_accessor :app, :env, :request, :response, :params
|
884
900
|
attr_reader :template_cache
|
@@ -899,9 +915,7 @@ module Sinatra
|
|
899
915
|
@env = env
|
900
916
|
@request = Request.new(env)
|
901
917
|
@response = Response.new
|
902
|
-
@params = indifferent_params(@request.params)
|
903
918
|
template_cache.clear if settings.reload_templates
|
904
|
-
force_encoding(@params)
|
905
919
|
|
906
920
|
@response['Content-Type'] = nil
|
907
921
|
invoke { dispatch! }
|
@@ -969,9 +983,9 @@ module Sinatra
|
|
969
983
|
# Run routes defined on the class and all superclasses.
|
970
984
|
def route!(base = settings, pass_block = nil)
|
971
985
|
if routes = base.routes[@request.request_method]
|
972
|
-
routes.each do |pattern,
|
973
|
-
returned_pass_block = process_route(pattern,
|
974
|
-
env['sinatra.route'] =
|
986
|
+
routes.each do |pattern, conditions, block|
|
987
|
+
returned_pass_block = process_route(pattern, conditions) do |*args|
|
988
|
+
env['sinatra.route'] = "#{@request.request_method} #{pattern}"
|
975
989
|
route_eval { block[*args] }
|
976
990
|
end
|
977
991
|
|
@@ -999,21 +1013,29 @@ module Sinatra
|
|
999
1013
|
# Revert params afterwards.
|
1000
1014
|
#
|
1001
1015
|
# Returns pass block.
|
1002
|
-
def process_route(pattern,
|
1016
|
+
def process_route(pattern, conditions, block = nil, values = [])
|
1003
1017
|
route = @request.path_info
|
1004
1018
|
route = '/' if route.empty? and not settings.empty_path_info?
|
1005
|
-
return unless
|
1006
|
-
values += match.captures.map! { |v| force_encoding URI_INSTANCE.unescape(v) if v }
|
1019
|
+
return unless params = pattern.params(route)
|
1007
1020
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1021
|
+
params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
|
1022
|
+
original, @params = @params, @params.merge(params) if params.any?
|
1023
|
+
|
1024
|
+
if pattern.is_a? Mustermann::Regular
|
1025
|
+
captures = pattern.match(route).captures
|
1026
|
+
values += captures
|
1027
|
+
@params[:captures] = captures
|
1028
|
+
else
|
1029
|
+
values += params.values.flatten
|
1011
1030
|
end
|
1012
1031
|
|
1013
1032
|
catch(:pass) do
|
1014
1033
|
conditions.each { |c| throw :pass if c.bind(self).call == false }
|
1015
1034
|
block ? block[self, values] : yield(self, values)
|
1016
1035
|
end
|
1036
|
+
rescue
|
1037
|
+
@env['sinatra.error.params'] = @params
|
1038
|
+
raise
|
1017
1039
|
ensure
|
1018
1040
|
@params = original if original
|
1019
1041
|
end
|
@@ -1065,8 +1087,9 @@ module Sinatra
|
|
1065
1087
|
# Run the block with 'throw :halt' support and apply result to the response.
|
1066
1088
|
def invoke
|
1067
1089
|
res = catch(:halt) { yield }
|
1068
|
-
|
1069
|
-
if
|
1090
|
+
|
1091
|
+
res = [res] if Fixnum === res or String === res
|
1092
|
+
if Array === res and Fixnum === res.first
|
1070
1093
|
res = res.dup
|
1071
1094
|
status(res.shift)
|
1072
1095
|
body(res.pop)
|
@@ -1079,6 +1102,9 @@ module Sinatra
|
|
1079
1102
|
|
1080
1103
|
# Dispatch a request with error handling.
|
1081
1104
|
def dispatch!
|
1105
|
+
@params = indifferent_params(@request.params)
|
1106
|
+
force_encoding(@params)
|
1107
|
+
|
1082
1108
|
invoke do
|
1083
1109
|
static! if settings.static? && (request.get? || request.head?)
|
1084
1110
|
filter! :before
|
@@ -1096,6 +1122,9 @@ module Sinatra
|
|
1096
1122
|
|
1097
1123
|
# Error handling during requests.
|
1098
1124
|
def handle_exception!(boom)
|
1125
|
+
if error_params = @env['sinatra.error.params']
|
1126
|
+
@params = @params.merge(error_params)
|
1127
|
+
end
|
1099
1128
|
@env['sinatra.error'] = boom
|
1100
1129
|
|
1101
1130
|
if boom.respond_to? :http_status
|
@@ -1111,11 +1140,12 @@ module Sinatra
|
|
1111
1140
|
if server_error?
|
1112
1141
|
dump_errors! boom if settings.dump_errors?
|
1113
1142
|
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
|
1114
|
-
|
1115
|
-
|
1116
|
-
if not_found?
|
1143
|
+
elsif not_found?
|
1117
1144
|
headers['X-Cascade'] = 'pass' if settings.x_cascade?
|
1118
1145
|
body '<h1>Not Found</h1>'
|
1146
|
+
elsif bad_request?
|
1147
|
+
dump_errors! boom if settings.dump_errors?
|
1148
|
+
halt status
|
1119
1149
|
end
|
1120
1150
|
|
1121
1151
|
res = error_block!(boom.class, boom) || error_block!(status, boom)
|
@@ -1223,7 +1253,7 @@ module Sinatra
|
|
1223
1253
|
case value
|
1224
1254
|
when Proc
|
1225
1255
|
getter = value
|
1226
|
-
when Symbol,
|
1256
|
+
when Symbol, Fixnum, FalseClass, TrueClass, NilClass
|
1227
1257
|
getter = value.inspect
|
1228
1258
|
when Hash
|
1229
1259
|
setter = proc do |val|
|
@@ -1252,16 +1282,16 @@ module Sinatra
|
|
1252
1282
|
# class, or an HTTP status code to specify which errors should be
|
1253
1283
|
# handled.
|
1254
1284
|
def error(*codes, &block)
|
1255
|
-
args = compile! "ERROR",
|
1285
|
+
args = compile! "ERROR", /.*/, block
|
1256
1286
|
codes = codes.map { |c| Array(c) }.flatten
|
1257
1287
|
codes << Exception if codes.empty?
|
1288
|
+
codes << Sinatra::NotFound if codes.include?(404)
|
1258
1289
|
codes.each { |c| (@errors[c] ||= []) << args }
|
1259
1290
|
end
|
1260
1291
|
|
1261
1292
|
# Sugar for `error(404) { ... }`
|
1262
1293
|
def not_found(&block)
|
1263
1294
|
error(404, &block)
|
1264
|
-
error(Sinatra::NotFound, &block)
|
1265
1295
|
end
|
1266
1296
|
|
1267
1297
|
# Define a named template. The block must return the template source.
|
@@ -1299,7 +1329,7 @@ module Sinatra
|
|
1299
1329
|
data.each_line do |line|
|
1300
1330
|
lines += 1
|
1301
1331
|
if line =~ /^@@\s*(.*\S)\s*$/
|
1302
|
-
template = force_encoding(
|
1332
|
+
template = force_encoding(String.new, encoding)
|
1303
1333
|
templates[$1.to_sym] = [template, file, lines]
|
1304
1334
|
elsif template
|
1305
1335
|
template << line
|
@@ -1328,21 +1358,20 @@ module Sinatra
|
|
1328
1358
|
# Define a before filter; runs before all requests within the same
|
1329
1359
|
# context as route handlers and may access/modify the request and
|
1330
1360
|
# response.
|
1331
|
-
def before(path =
|
1361
|
+
def before(path = /.*/, **options, &block)
|
1332
1362
|
add_filter(:before, path, options, &block)
|
1333
1363
|
end
|
1334
1364
|
|
1335
1365
|
# Define an after filter; runs after all requests within the same
|
1336
1366
|
# context as route handlers and may access/modify the request and
|
1337
1367
|
# response.
|
1338
|
-
def after(path =
|
1368
|
+
def after(path = /.*/, **options, &block)
|
1339
1369
|
add_filter(:after, path, options, &block)
|
1340
1370
|
end
|
1341
1371
|
|
1342
1372
|
# add a filter
|
1343
|
-
def add_filter(type, path =
|
1344
|
-
|
1345
|
-
filters[type] << compile!(type, path || //, block, options)
|
1373
|
+
def add_filter(type, path = /.*/, **options, &block)
|
1374
|
+
filters[type] << compile!(type, path, block, options)
|
1346
1375
|
end
|
1347
1376
|
|
1348
1377
|
# Add a route condition. The route is considered non-matching when the
|
@@ -1422,7 +1451,7 @@ module Sinatra
|
|
1422
1451
|
return unless running?
|
1423
1452
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
1424
1453
|
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
|
1425
|
-
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless
|
1454
|
+
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless supress_messages?
|
1426
1455
|
set :running_server, nil
|
1427
1456
|
set :handler_name, nil
|
1428
1457
|
end
|
@@ -1504,7 +1533,7 @@ module Sinatra
|
|
1504
1533
|
# Starts the server by running the Rack Handler.
|
1505
1534
|
def start_server(handler, server_settings, handler_name)
|
1506
1535
|
handler.run(self, server_settings) do |server|
|
1507
|
-
unless
|
1536
|
+
unless supress_messages?
|
1508
1537
|
$stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
|
1509
1538
|
end
|
1510
1539
|
|
@@ -1517,6 +1546,10 @@ module Sinatra
|
|
1517
1546
|
end
|
1518
1547
|
end
|
1519
1548
|
|
1549
|
+
def supress_messages?
|
1550
|
+
handler_name =~ /cgi/i || quiet
|
1551
|
+
end
|
1552
|
+
|
1520
1553
|
def setup_traps
|
1521
1554
|
if traps?
|
1522
1555
|
at_exit { quit! }
|
@@ -1534,8 +1567,7 @@ module Sinatra
|
|
1534
1567
|
|
1535
1568
|
# Dynamically defines a method on settings.
|
1536
1569
|
def define_singleton(name, content = Proc.new)
|
1537
|
-
|
1538
|
-
(class << self; self; end).class_eval do
|
1570
|
+
singleton_class.class_eval do
|
1539
1571
|
undef_method(name) if method_defined? name
|
1540
1572
|
String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
|
1541
1573
|
end
|
@@ -1599,108 +1631,22 @@ module Sinatra
|
|
1599
1631
|
method
|
1600
1632
|
end
|
1601
1633
|
|
1602
|
-
def compile!(verb, path, block, options
|
1634
|
+
def compile!(verb, path, block, **options)
|
1603
1635
|
options.each_pair { |option, args| send(option, *args) }
|
1636
|
+
|
1637
|
+
pattern = compile(path)
|
1604
1638
|
method_name = "#{verb} #{path}"
|
1605
1639
|
unbound_method = generate_method(method_name, &block)
|
1606
|
-
pattern, keys = compile path
|
1607
1640
|
conditions, @conditions = @conditions, []
|
1608
|
-
|
1609
1641
|
wrapper = block.arity != 0 ?
|
1610
1642
|
proc { |a,p| unbound_method.bind(a).call(*p) } :
|
1611
1643
|
proc { |a,p| unbound_method.bind(a).call }
|
1612
|
-
wrapper.instance_variable_set(:@route_name, method_name)
|
1613
1644
|
|
1614
|
-
[ pattern,
|
1645
|
+
[ pattern, conditions, wrapper ]
|
1615
1646
|
end
|
1616
1647
|
|
1617
1648
|
def compile(path)
|
1618
|
-
|
1619
|
-
keys = []
|
1620
|
-
|
1621
|
-
# Split the path into pieces in between forward slashes.
|
1622
|
-
# A negative number is given as the second argument of path.split
|
1623
|
-
# because with this number, the method does not ignore / at the end
|
1624
|
-
# and appends an empty string at the end of the return value.
|
1625
|
-
#
|
1626
|
-
segments = path.split('/', -1).map! do |segment|
|
1627
|
-
ignore = []
|
1628
|
-
|
1629
|
-
# Special character handling.
|
1630
|
-
#
|
1631
|
-
pattern = segment.to_str.gsub(/[^\?\%\\\/\:\*\w]|:(?!\w)/) do |c|
|
1632
|
-
ignore << escaped(c).join if c.match(/[\.@]/)
|
1633
|
-
patt = encoded(c)
|
1634
|
-
patt.gsub(/%[\da-fA-F]{2}/) do |match|
|
1635
|
-
match.split(//).map! { |char| char == char.downcase ? char : "[#{char}#{char.downcase}]" }.join
|
1636
|
-
end
|
1637
|
-
end
|
1638
|
-
|
1639
|
-
ignore = ignore.uniq.join
|
1640
|
-
|
1641
|
-
# Key handling.
|
1642
|
-
#
|
1643
|
-
pattern.gsub(/((:\w+)|\*)/) do |match|
|
1644
|
-
if match == "*"
|
1645
|
-
keys << 'splat'
|
1646
|
-
"(.*?)"
|
1647
|
-
else
|
1648
|
-
keys << $2[1..-1]
|
1649
|
-
ignore_pattern = safe_ignore(ignore)
|
1650
|
-
|
1651
|
-
ignore_pattern
|
1652
|
-
end
|
1653
|
-
end
|
1654
|
-
end
|
1655
|
-
|
1656
|
-
# Special case handling.
|
1657
|
-
#
|
1658
|
-
if last_segment = segments[-1] and last_segment.match(/\[\^\\\./)
|
1659
|
-
parts = last_segment.rpartition(/\[\^\\\./)
|
1660
|
-
parts[1] = '[^'
|
1661
|
-
segments[-1] = parts.join
|
1662
|
-
end
|
1663
|
-
[/\A#{segments.join('/')}\z/, keys]
|
1664
|
-
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
1665
|
-
[path, path.keys]
|
1666
|
-
elsif path.respond_to?(:names) && path.respond_to?(:match)
|
1667
|
-
[path, path.names]
|
1668
|
-
elsif path.respond_to? :match
|
1669
|
-
[path, []]
|
1670
|
-
else
|
1671
|
-
raise TypeError, path
|
1672
|
-
end
|
1673
|
-
end
|
1674
|
-
|
1675
|
-
def encoded(char)
|
1676
|
-
enc = URI_INSTANCE.escape(char)
|
1677
|
-
enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
|
1678
|
-
enc = "(?:#{enc}|#{encoded('+')})" if char == " "
|
1679
|
-
enc
|
1680
|
-
end
|
1681
|
-
|
1682
|
-
def escaped(char, enc = URI_INSTANCE.escape(char))
|
1683
|
-
[Regexp.escape(enc), URI_INSTANCE.escape(char, /./)]
|
1684
|
-
end
|
1685
|
-
|
1686
|
-
def safe_ignore(ignore)
|
1687
|
-
unsafe_ignore = []
|
1688
|
-
ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
|
1689
|
-
unsafe_ignore << hex[1..2]
|
1690
|
-
''
|
1691
|
-
end
|
1692
|
-
unsafe_patterns = unsafe_ignore.map! do |unsafe|
|
1693
|
-
chars = unsafe.split(//).map! do |char|
|
1694
|
-
char == char.downcase ? char : char + char.downcase
|
1695
|
-
end
|
1696
|
-
|
1697
|
-
"|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])"
|
1698
|
-
end
|
1699
|
-
if unsafe_patterns.length > 0
|
1700
|
-
"((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)"
|
1701
|
-
else
|
1702
|
-
"([^#{ignore}/?#]+)"
|
1703
|
-
end
|
1649
|
+
Mustermann.new(path)
|
1704
1650
|
end
|
1705
1651
|
|
1706
1652
|
def setup_default_middleware(builder)
|
@@ -1745,10 +1691,16 @@ module Sinatra
|
|
1745
1691
|
def setup_protection(builder)
|
1746
1692
|
return unless protection?
|
1747
1693
|
options = Hash === protection ? protection.dup : {}
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1694
|
+
options = {
|
1695
|
+
img_src: "'self' data:",
|
1696
|
+
font_src: "'self'"
|
1697
|
+
}.merge options
|
1698
|
+
|
1699
|
+
protect_session = options.fetch(:session) { sessions? }
|
1700
|
+
options[:without_session] = !protect_session
|
1701
|
+
|
1751
1702
|
options[:reaction] ||= :drop_session
|
1703
|
+
|
1752
1704
|
builder.use Rack::Protection, options
|
1753
1705
|
end
|
1754
1706
|
|
@@ -1757,7 +1709,7 @@ module Sinatra
|
|
1757
1709
|
options = {}
|
1758
1710
|
options[:secret] = session_secret if session_secret?
|
1759
1711
|
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
1760
|
-
builder.use
|
1712
|
+
builder.use session_store, options
|
1761
1713
|
end
|
1762
1714
|
|
1763
1715
|
def detect_rack_handler
|
@@ -1766,8 +1718,6 @@ module Sinatra
|
|
1766
1718
|
begin
|
1767
1719
|
return Rack::Handler.get(server_name.to_s)
|
1768
1720
|
rescue LoadError, NameError
|
1769
|
-
rescue ArgumentError
|
1770
|
-
Sinatra::Ext.get_handler(server_name.to_s)
|
1771
1721
|
end
|
1772
1722
|
end
|
1773
1723
|
fail "Server handler (#{servers.join(',')}) not found."
|
@@ -1826,11 +1776,12 @@ module Sinatra
|
|
1826
1776
|
|
1827
1777
|
reset!
|
1828
1778
|
|
1829
|
-
set :environment, (ENV['RACK_ENV'] || :development).to_sym
|
1779
|
+
set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
|
1830
1780
|
set :raise_errors, Proc.new { test? }
|
1831
1781
|
set :dump_errors, Proc.new { !test? }
|
1832
1782
|
set :show_exceptions, Proc.new { development? }
|
1833
1783
|
set :sessions, false
|
1784
|
+
set :session_store, Rack::Session::Cookie
|
1834
1785
|
set :logging, false
|
1835
1786
|
set :protection, true
|
1836
1787
|
set :method_override, false
|
@@ -1861,6 +1812,7 @@ module Sinatra
|
|
1861
1812
|
set :server, %w[HTTP webrick]
|
1862
1813
|
set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
|
1863
1814
|
set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
|
1815
|
+
set :quiet, false
|
1864
1816
|
|
1865
1817
|
ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
1866
1818
|
|
@@ -1936,7 +1888,7 @@ module Sinatra
|
|
1936
1888
|
</style>
|
1937
1889
|
</head>
|
1938
1890
|
<body>
|
1939
|
-
<h2>Sinatra doesn
|
1891
|
+
<h2>Sinatra doesn’t know this ditty.</h2>
|
1940
1892
|
<img src='#{uri "/__sinatra__/404.png"}'>
|
1941
1893
|
<div id="c">
|
1942
1894
|
Try this:
|