sproutcore 1.6.0.1 → 1.7.1.beta
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.
- data/CHANGELOG +21 -0
- data/Gemfile +5 -0
- data/Rakefile +26 -13
- data/VERSION.yml +2 -2
- data/lib/Buildfile +43 -4
- data/lib/buildtasks/build.rake +10 -0
- data/lib/buildtasks/helpers/file_rule.rb +22 -0
- data/lib/buildtasks/helpers/file_rule_list.rb +137 -0
- data/lib/buildtasks/manifest.rake +133 -122
- data/lib/frameworks/sproutcore/CHANGELOG.md +69 -2
- data/lib/frameworks/sproutcore/apps/tests/english.lproj/strings.js +1 -0
- data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +28 -22
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +9 -5
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/controller.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controls/button.js +18 -13
- data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/bind.js +5 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/collection.js +2 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/action_support.js +80 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/template_helpers/text_field_support.js +84 -116
- data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane.js +8 -5
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +157 -157
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +5 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +6 -6
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/sparse_array.js +10 -7
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/action_support.js +106 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/collection.js +18 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/handlebars.js +71 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/attribute_bindings_test.js +38 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/class_name_bindings_test.js +47 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutChildViews.js +18 -18
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +42 -10
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/keyboard.js +26 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +14 -8
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +158 -1
- data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +15 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +108 -108
- data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/error_methods.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/single_attribute.js +26 -0
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/query/builders.js +7 -0
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/record_array/error_methods.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +4 -1
- data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/tests/system/datetime.js +6 -0
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +26 -5
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +97 -96
- data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +4 -3
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +17 -4
- data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +7 -7
- data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +7 -5
- data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +12 -3
- data/lib/frameworks/sproutcore/frameworks/desktop/views/web.js +23 -14
- data/lib/frameworks/sproutcore/frameworks/experimental/Buildfile +5 -1
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/render_delegates/menu_scroller.js +28 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/tests/menu/scroll.js +235 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroll.js +363 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroller.js +250 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/desktop_scroller.js +92 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/native_scroll.js +25 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/scroll.js +33 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/touch_scroller.js +76 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/integration.js +50 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/methods.js +143 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/ui.js +258 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroll.js +1164 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroller.js +332 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroll.js +236 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroller.js +347 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroll.js +15 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroller.js +10 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroll.js +804 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroller.js +133 -0
- data/lib/frameworks/sproutcore/frameworks/foundation/resources/text_field.css +3 -3
- data/lib/frameworks/sproutcore/frameworks/foundation/validators/number.js +3 -1
- data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/media_slider.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/video.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/routing/system/routes.js +29 -3
- data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/replace.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/private/property_chain.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +1 -1
- data/lib/frameworks/sproutcore/themes/ace/resources/collection/normal/list_item.css +2 -2
- data/lib/frameworks/sproutcore/themes/legacy_theme/english.lproj/segmented.css +1 -1
- data/lib/gen/app/templates/apps/@target_name@/Buildfile +3 -5
- data/lib/gen/app/templates/apps/@target_name@/resources/_theme.css +18 -0
- data/lib/gen/project/templates/@filename@/Buildfile +2 -2
- data/lib/sproutcore/builders/chance_file.rb +9 -16
- data/lib/sproutcore/builders/html.rb +2 -1
- data/lib/sproutcore/builders/minify.rb +4 -35
- data/lib/sproutcore/builders/module.rb +38 -1
- data/lib/sproutcore/builders/split.rb +63 -0
- data/lib/sproutcore/builders/strings.rb +7 -1
- data/lib/sproutcore/builders.rb +1 -0
- data/lib/sproutcore/helpers/css_split.rb +190 -0
- data/lib/sproutcore/helpers/entry_sorter.rb +2 -0
- data/lib/sproutcore/helpers/minifier.rb +40 -16
- data/lib/sproutcore/helpers/static_helper.rb +35 -17
- data/lib/sproutcore/helpers.rb +1 -1
- data/lib/sproutcore/models/manifest.rb +26 -0
- data/lib/sproutcore/models/target.rb +12 -1
- data/lib/sproutcore/rack/proxy.rb +244 -225
- data/lib/sproutcore/rack/restrict_ip.rb +67 -0
- data/lib/sproutcore/rack/service.rb +8 -2
- data/lib/sproutcore/rack.rb +1 -0
- data/lib/sproutcore/tools/build.rb +91 -43
- data/lib/sproutcore/tools/gen.rb +2 -3
- data/lib/sproutcore/tools/manifest.rb +22 -16
- data/lib/sproutcore/tools/server.rb +21 -0
- data/lib/sproutcore/tools.rb +102 -46
- data/lib/sproutcore.rb +30 -5
- data/spec/buildtasks/helpers/accept_list +22 -0
- data/spec/buildtasks/helpers/accept_list.rb +128 -0
- data/spec/buildtasks/helpers/list.json +11 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/chance_2x_spec.rb +1 -39
- data/spec/buildtasks/manifest/prepare_build_tasks/chance_spec.rb +0 -38
- data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +4 -4
- data/spec/buildtasks/manifest/prepare_build_tasks/module_spec.rb +2 -2
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_indirect_spec.rb +7 -16
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_spec.rb +7 -17
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +11 -6
- data/spec/fixtures/builder_tests/Buildfile +2 -1
- data/spec/fixtures/builder_tests/apps/module_test/modules/required_module/core.js +0 -0
- data/spec/lib/builders/module_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/sproutcore.gemspec +4 -9
- data/vendor/chance/lib/chance/factory.rb +45 -0
- data/vendor/chance/lib/chance/instance/data_url.rb +0 -29
- data/vendor/chance/lib/chance/instance/slicing.rb +57 -4
- data/vendor/chance/lib/chance/instance/spriting.rb +112 -21
- data/vendor/chance/lib/chance/instance.rb +173 -28
- data/vendor/chance/lib/chance/parser.rb +80 -52
- data/vendor/chance/lib/chance.rb +25 -6
- data/vendor/sproutcore/SCCompiler.jar +0 -0
- data/vendor/sproutcore/lib/args4j-2.0.12.jar +0 -0
- data/vendor/sproutcore/lib/yuicompressor-2.4.2.jar +0 -0
- metadata +97 -38
|
@@ -14,299 +14,318 @@ rescue LoadError => e
|
|
|
14
14
|
SC::HTTPS_ENABLED = false
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
require '
|
|
19
|
-
require '
|
|
20
|
-
|
|
17
|
+
begin
|
|
18
|
+
require 'eventmachine'
|
|
19
|
+
require 'em-http'
|
|
20
|
+
require 'thin'
|
|
21
|
+
|
|
22
|
+
SC::PROXY_ENABLED = true
|
|
23
|
+
rescue LoadError => e
|
|
24
|
+
SC::PROXY_ENABLED = false
|
|
25
|
+
end
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
if SC::PROXY_ENABLED
|
|
28
|
+
# There are cases where we cannot load the proxy and don't need to (build environment)
|
|
29
|
+
|
|
30
|
+
module SC
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
module Rack
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
class DeferrableBody
|
|
35
|
+
include EM::Deferrable
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
def initialize(options = {})
|
|
38
|
+
@options = options
|
|
39
|
+
end
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
def call(body)
|
|
42
|
+
body.each do |chunk|
|
|
43
|
+
@body_callback.call(prepare_chunk(chunk))
|
|
44
|
+
end
|
|
36
45
|
end
|
|
37
|
-
end
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
def prepare_chunk(chunk)
|
|
48
|
+
if chunked?
|
|
49
|
+
size = chunk.respond_to?(:bytesize) ? chunk.bytesize : chunk.length
|
|
50
|
+
"#{size.to_s(16)}\r\n#{chunk}\r\n"
|
|
51
|
+
else
|
|
52
|
+
# Thin doesn't like null bodies
|
|
53
|
+
chunk || ''
|
|
54
|
+
end
|
|
46
55
|
end
|
|
47
|
-
end
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
def each(&blk)
|
|
58
|
+
@body_callback = blk
|
|
59
|
+
end
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
private
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
def chunked?
|
|
64
|
+
@options[:chunked]
|
|
65
|
+
end
|
|
57
66
|
end
|
|
58
|
-
end
|
|
59
67
|
|
|
60
68
|
|
|
61
69
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def initialize(project)
|
|
66
|
-
@project = project
|
|
67
|
-
@proxies = project.buildfile.proxies
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def call(env)
|
|
71
|
-
path = env['PATH_INFO']
|
|
72
|
-
|
|
73
|
-
@proxies.each do |proxy, value|
|
|
74
|
-
# If the url matches a proxied url, handle it
|
|
75
|
-
if path.match(/^#{Regexp.escape(proxy.to_s)}/)
|
|
76
|
-
handle_proxy(value, proxy.to_s, env)
|
|
70
|
+
# Rack application proxies requests as needed for the given project.
|
|
71
|
+
class Proxy
|
|
77
72
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
def initialize(project)
|
|
74
|
+
@project = project
|
|
75
|
+
@proxies = project.buildfile.proxies
|
|
81
76
|
end
|
|
82
77
|
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
def call(env)
|
|
79
|
+
path = env['PATH_INFO']
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
@proxies.each do |proxy, value|
|
|
82
|
+
# If the url matches a proxied url, handle it
|
|
83
|
+
if path.match(/^#{Regexp.escape(proxy.to_s)}/)
|
|
84
|
+
handle_proxy(value, proxy.to_s, env)
|
|
89
85
|
|
|
90
|
-
|
|
86
|
+
# Don't block waiting for a response
|
|
87
|
+
throw :async
|
|
88
|
+
end
|
|
89
|
+
end
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
SC.logger << "~ WARNING: HTTPS is not supported on your system, using HTTP instead.\n"
|
|
94
|
-
SC.logger << " If you are using Ubuntu, you can run `apt-get install libopenssl-ruby`\n"
|
|
95
|
-
proxy[:secure] = false
|
|
91
|
+
return [404, {}, "not found"]
|
|
96
92
|
end
|
|
97
93
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
def chunked?(headers)
|
|
95
|
+
headers['Transfer-Encoding'] == "chunked"
|
|
96
|
+
end
|
|
101
97
|
|
|
102
|
-
|
|
103
|
-
protocol = proxy[:secure] ? 'https' : 'http'
|
|
98
|
+
def handle_proxy(proxy, proxy_url, env)
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
if proxy[:secure] && !SC::HTTPS_ENABLED
|
|
101
|
+
SC.logger << "~ WARNING: HTTPS is not supported on your system, using HTTP instead.\n"
|
|
102
|
+
SC.logger << " If you are using Ubuntu, you can run `apt-get install libopenssl-ruby`\n"
|
|
103
|
+
proxy[:secure] = false
|
|
104
|
+
end
|
|
109
105
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
url += '?' + params unless params.empty?
|
|
106
|
+
headers = request_headers(env, proxy) # ex. {"Host"=>"localhost:4020", "Connection"=>"...
|
|
107
|
+
path = env['PATH_INFO'] # ex. /contacts
|
|
108
|
+
params = env['QUERY_STRING'] # ex. since=yesterday&unread=true
|
|
114
109
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
req_body.rewind # May not be necessary but can't hurt
|
|
110
|
+
# Switch to https if proxy[:secure] configured
|
|
111
|
+
protocol = proxy[:secure] ? 'https' : 'http'
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
# Adjust the path if proxy[:url] configured
|
|
114
|
+
if proxy[:url]
|
|
115
|
+
path = path.sub(/^#{Regexp.escape proxy_url}/, proxy[:url])
|
|
116
|
+
end
|
|
121
117
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
request_options[:timeout] = proxy[:timeout] if proxy[:timeout]
|
|
127
|
-
request_options[:redirects] = 10 if proxy[:redirect] != false
|
|
128
|
-
|
|
129
|
-
EventMachine.run {
|
|
130
|
-
body = nil
|
|
131
|
-
conn = EM::HttpRequest.new(url)
|
|
132
|
-
chunked = false
|
|
133
|
-
headers = {}
|
|
134
|
-
method = env['REQUEST_METHOD'].upcase
|
|
135
|
-
status = 0
|
|
136
|
-
|
|
137
|
-
case method
|
|
138
|
-
when 'GET'
|
|
139
|
-
http = conn.get request_options
|
|
140
|
-
when 'POST'
|
|
141
|
-
http = conn.post request_options
|
|
142
|
-
when 'PUT'
|
|
143
|
-
http = conn.put request_options
|
|
144
|
-
when 'DELETE'
|
|
145
|
-
http = conn.delete request_options
|
|
146
|
-
else
|
|
147
|
-
http = conn.head request_options
|
|
148
|
-
end
|
|
118
|
+
# The endpoint URL
|
|
119
|
+
url = protocol + '://' + proxy[:to]
|
|
120
|
+
url += path unless path.empty?
|
|
121
|
+
url += '?' + params unless params.empty?
|
|
149
122
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
path = env['PATH_INFO']
|
|
154
|
-
url = http.last_effective_url
|
|
155
|
-
SC.logger << "~ PROXY FAILED: #{method} #{path} -> #{status} #{url}\n"
|
|
156
|
-
|
|
157
|
-
# If a body has been sent use it, otherwise respond with generic message
|
|
158
|
-
if !body
|
|
159
|
-
body = "Unable to proxy to #{url}. Received status: #{status}"
|
|
160
|
-
size = body.respond_to?(:bytesize) ? body.bytesize : body.length
|
|
161
|
-
headers = { 'Content-Length' => size.to_s }
|
|
162
|
-
body = [body]
|
|
163
|
-
end
|
|
123
|
+
if env['CONTENT_LENGTH'] || env['HTTP_TRANSFER_ENCODING']
|
|
124
|
+
req_body = env['rack.input']
|
|
125
|
+
req_body.rewind # May not be necessary but can't hurt
|
|
164
126
|
|
|
165
|
-
|
|
166
|
-
|
|
127
|
+
req_body = req_body.read
|
|
128
|
+
end
|
|
167
129
|
|
|
168
|
-
#
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
130
|
+
# Options for the request
|
|
131
|
+
request_options = {}
|
|
132
|
+
request_options[:head] = headers
|
|
133
|
+
request_options[:body] = req_body if !!req_body
|
|
134
|
+
request_options[:timeout] = proxy[:timeout] if proxy[:timeout]
|
|
135
|
+
request_options[:redirects] = 10 if proxy[:redirect] != false
|
|
136
|
+
request_options[:decoding] = false # don't decode gzipped content
|
|
137
|
+
|
|
138
|
+
EventMachine.run {
|
|
139
|
+
body = nil
|
|
140
|
+
conn = EM::HttpRequest.new(url)
|
|
141
|
+
chunked = false
|
|
142
|
+
headers = {}
|
|
143
|
+
method = env['REQUEST_METHOD'].upcase
|
|
144
|
+
status = 0
|
|
145
|
+
|
|
146
|
+
case method
|
|
147
|
+
when 'GET'
|
|
148
|
+
http = conn.get request_options
|
|
149
|
+
when 'POST'
|
|
150
|
+
http = conn.post request_options
|
|
151
|
+
when 'PUT'
|
|
152
|
+
http = conn.put request_options
|
|
153
|
+
when 'DELETE'
|
|
154
|
+
http = conn.delete request_options
|
|
155
|
+
else
|
|
156
|
+
http = conn.head request_options
|
|
183
157
|
end
|
|
184
158
|
|
|
185
|
-
|
|
159
|
+
# Received error
|
|
160
|
+
http.errback {
|
|
161
|
+
status = http.response_header.status
|
|
186
162
|
path = env['PATH_INFO']
|
|
187
163
|
url = http.last_effective_url
|
|
188
|
-
SC.logger << "~ PROXY:
|
|
189
|
-
end
|
|
190
|
-
}
|
|
164
|
+
SC.logger << "~ PROXY FAILED: #{method} #{path} -> #{status} #{url}\n"
|
|
191
165
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
166
|
+
# If a body has been sent use it, otherwise respond with generic message
|
|
167
|
+
if !body
|
|
168
|
+
body = "Unable to proxy to #{url}. Received status: #{status}"
|
|
169
|
+
size = body.respond_to?(:bytesize) ? body.bytesize : body.length
|
|
170
|
+
headers = { 'Content-Length' => size.to_s }
|
|
171
|
+
body = [body]
|
|
172
|
+
end
|
|
195
173
|
|
|
196
|
-
|
|
174
|
+
env['async.callback'].call [502, headers, body]
|
|
175
|
+
}
|
|
197
176
|
|
|
198
|
-
#
|
|
199
|
-
|
|
177
|
+
# Received response
|
|
178
|
+
http.callback {
|
|
200
179
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
elsif !headers['Location']
|
|
205
|
-
body = "Unable to proxy to #{url}. Received redirect with no Location."
|
|
180
|
+
# Too many redirects
|
|
181
|
+
if redirect? status
|
|
182
|
+
body = "Unable to proxy to #{url}. Too many redirects."
|
|
206
183
|
size = body.respond_to?(:bytesize) ? body.bytesize : body.length
|
|
207
184
|
headers = { 'Content-Length' => size.to_s }
|
|
208
185
|
|
|
209
|
-
|
|
186
|
+
env['async.callback'].call [502, headers, [body]]
|
|
187
|
+
else
|
|
188
|
+
# Terminate the deferred body (which may have been chunked)
|
|
189
|
+
if body
|
|
190
|
+
body.call ['']
|
|
191
|
+
body.succeed
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Log the initial path and the final url
|
|
195
|
+
path = env['PATH_INFO']
|
|
196
|
+
url = http.last_effective_url
|
|
197
|
+
SC.logger << "~ PROXY: #{method} #{path} -> #{status} #{url}\n"
|
|
198
|
+
end
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# Received headers
|
|
202
|
+
http.headers { |hash|
|
|
203
|
+
status = http.response_header.status
|
|
204
|
+
|
|
205
|
+
headers = response_headers(hash)
|
|
206
|
+
|
|
207
|
+
# Don't respond on redirection, but fail out on bad redirects
|
|
208
|
+
if redirect? status
|
|
209
|
+
|
|
210
|
+
if status == 304
|
|
211
|
+
env["async.callback"].call [status, headers, ['']]
|
|
212
|
+
SC.logger << "~ PROXY: #{method} #{path} -> #{status} #{url}\n"
|
|
213
|
+
elsif !headers['Location']
|
|
214
|
+
body = "Unable to proxy to #{url}. Received redirect with no Location."
|
|
215
|
+
size = body.respond_to?(:bytesize) ? body.bytesize : body.length
|
|
216
|
+
headers = { 'Content-Length' => size.to_s }
|
|
217
|
+
|
|
218
|
+
http.close
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
else
|
|
222
|
+
# Stream the body right across in the format it was sent
|
|
223
|
+
chunked = chunked?(headers)
|
|
224
|
+
body = DeferrableBody.new({ :chunked => chunked })
|
|
225
|
+
|
|
226
|
+
# Start responding to the client immediately
|
|
227
|
+
env["async.callback"].call [status, headers, body]
|
|
210
228
|
end
|
|
229
|
+
}
|
|
211
230
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
231
|
+
# Received chunk of data
|
|
232
|
+
http.stream { |chunk|
|
|
233
|
+
# Ignore body of redirects
|
|
234
|
+
if !redirect? status
|
|
235
|
+
body.call [chunk]
|
|
236
|
+
end
|
|
237
|
+
}
|
|
216
238
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
239
|
+
# If the client disconnects early, make sure we close our other connection too
|
|
240
|
+
# TODO: this is waiting for changes not yet available in em-http
|
|
241
|
+
# Test with: curl http://0.0.0.0:4020/stream.twitter.com/1/statuses/sample.json -uTWITTER_USERNAME:TWITTER_PASSWORD
|
|
242
|
+
# env["async.close"].callback {
|
|
243
|
+
# conn.close
|
|
244
|
+
# }
|
|
221
245
|
|
|
222
|
-
# Received chunk of data
|
|
223
|
-
http.stream { |chunk|
|
|
224
|
-
# Ignore body of redirects
|
|
225
|
-
if !redirect? status
|
|
226
|
-
body.call [chunk]
|
|
227
|
-
end
|
|
228
246
|
}
|
|
229
|
-
|
|
230
|
-
end
|
|
247
|
+
end
|
|
231
248
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
249
|
+
def redirect?(status)
|
|
250
|
+
status >= 300 && status < 400
|
|
251
|
+
end
|
|
235
252
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
253
|
+
# collect headers...
|
|
254
|
+
def request_headers(env, proxy)
|
|
255
|
+
result = {}
|
|
256
|
+
env.each do |key, value|
|
|
257
|
+
next unless key =~ /^HTTP_/
|
|
241
258
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
259
|
+
key = headerize(key)
|
|
260
|
+
if !key.eql? "Version"
|
|
261
|
+
result[key] = value
|
|
262
|
+
end
|
|
245
263
|
end
|
|
246
|
-
end
|
|
247
264
|
|
|
248
|
-
|
|
249
|
-
|
|
265
|
+
# Rack documentation says CONTENT_TYPE and CONTENT_LENGTH aren't prefixed by HTTP_
|
|
266
|
+
result['Content-Type'] = env['CONTENT_TYPE'] if env['CONTENT_TYPE']
|
|
250
267
|
|
|
251
|
-
|
|
252
|
-
|
|
268
|
+
length = env['CONTENT_LENGTH']
|
|
269
|
+
result['Content-Length'] = length if length
|
|
253
270
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
271
|
+
# added 4/23/09 per Charles Jolley, corrects problem
|
|
272
|
+
# when making requests to virtual hosts
|
|
273
|
+
result['Host'] = proxy[:to]
|
|
257
274
|
|
|
258
|
-
|
|
259
|
-
|
|
275
|
+
result
|
|
276
|
+
end
|
|
260
277
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
278
|
+
# construct and display specific response headers
|
|
279
|
+
def response_headers(hash)
|
|
280
|
+
result = {}
|
|
264
281
|
|
|
265
|
-
|
|
266
|
-
|
|
282
|
+
hash.each do |key, value|
|
|
283
|
+
key = headerize(key)
|
|
267
284
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
285
|
+
# Because Set-Cookie header can appear more the once in the response body,
|
|
286
|
+
# but Rack only accepts a hash of headers, we store it in a line break separated string
|
|
287
|
+
# for Ruby 1.9 and as an Array for Ruby 1.8
|
|
288
|
+
# See http://groups.google.com/group/rack-devel/browse_thread/thread/e8759b91a82c5a10/a8dbd4574fe97d69?#a8dbd4574fe97d69
|
|
289
|
+
if key.downcase == 'set-cookie'
|
|
290
|
+
cookies = []
|
|
274
291
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
292
|
+
case value
|
|
293
|
+
when Array then value.each { |c| cookies << strip_domain(c) }
|
|
294
|
+
when Hash then value.each { |_, c| cookies << strip_domain(c) }
|
|
295
|
+
else cookies << strip_domain(value)
|
|
296
|
+
end
|
|
280
297
|
|
|
281
|
-
|
|
282
|
-
|
|
298
|
+
# Remove nil values
|
|
299
|
+
result['Set-Cookie'] = [result['Set-Cookie'], cookies].compact
|
|
283
300
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
301
|
+
if Thin.ruby_18?
|
|
302
|
+
result['Set-Cookie'].flatten!
|
|
303
|
+
else
|
|
304
|
+
result['Set-Cookie'] = result['Set-Cookie'].join("\n")
|
|
305
|
+
end
|
|
288
306
|
end
|
|
307
|
+
|
|
308
|
+
SC.logger << " #{key}: #{value}\n"
|
|
309
|
+
result[key] = value
|
|
289
310
|
end
|
|
290
311
|
|
|
291
|
-
|
|
292
|
-
result[key] = value
|
|
312
|
+
::Rack::Utils::HeaderHash.new(result)
|
|
293
313
|
end
|
|
294
314
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
parts = str.gsub(/^HTTP_/, '').split('_')
|
|
301
|
-
parts.map! { |p| p.capitalize }.join('-')
|
|
302
|
-
end
|
|
315
|
+
# remove HTTP_, dasherize and titleize
|
|
316
|
+
def headerize(str)
|
|
317
|
+
parts = str.gsub(/^HTTP_/, '').split('_')
|
|
318
|
+
parts.map! { |p| p.capitalize }.join('-')
|
|
319
|
+
end
|
|
303
320
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
321
|
+
# Strip out the domain of passed in cookie. This technically may
|
|
322
|
+
# break certain scenarios where services try to set cross-domain
|
|
323
|
+
# cookies, but those services should not be doing that anyway...
|
|
324
|
+
def strip_domain(cookie)
|
|
325
|
+
cookie.to_s.gsub!(/domain=[^\;]+\;? ?/,'')
|
|
326
|
+
end
|
|
309
327
|
end
|
|
310
328
|
end
|
|
311
329
|
end
|
|
312
|
-
|
|
330
|
+
|
|
331
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# ===========================================================================
|
|
2
|
+
# Project: Abbot - SproutCore Build Tools
|
|
3
|
+
# Copyright: ©2009-2011 Apple Inc.
|
|
4
|
+
# portions copyright @2006-2011 Strobe Inc.
|
|
5
|
+
# and contributors
|
|
6
|
+
# ===========================================================================
|
|
7
|
+
|
|
8
|
+
# For all those working in internet cafes...
|
|
9
|
+
# We feel for you. Go to a real cafe instead. They have internet, too.
|
|
10
|
+
module SC
|
|
11
|
+
module Rack
|
|
12
|
+
class RestrictIP
|
|
13
|
+
def initialize(app, allow_ips=[])
|
|
14
|
+
@app = app
|
|
15
|
+
@allow = allow_ips
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# checks if an IP, such as 127.0.0.1, matches a mask, such as 127.*.*.*
|
|
19
|
+
def ip_is_valid(ip, mask)
|
|
20
|
+
ip_parts = ip.split('.')
|
|
21
|
+
mask_parts = mask.split('.')
|
|
22
|
+
|
|
23
|
+
if mask_parts.length != 4
|
|
24
|
+
SC.logger.fatal "Invalid IP mask: #{mask}\n"
|
|
25
|
+
exit
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ip_idx = 0
|
|
29
|
+
mask_parts.each {|mask_part|
|
|
30
|
+
ip_part = ip_parts[ip_idx]
|
|
31
|
+
|
|
32
|
+
# * means anything matches
|
|
33
|
+
if mask_part == '*'
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if ip_part != mask_part
|
|
38
|
+
return false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
ip_idx = ip_idx + 1
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def call(env)
|
|
48
|
+
ip = env['REMOTE_ADDR']
|
|
49
|
+
|
|
50
|
+
is_valid = false
|
|
51
|
+
@allow.each {|mask|
|
|
52
|
+
if ip_is_valid(ip, mask)
|
|
53
|
+
is_valid = true
|
|
54
|
+
break
|
|
55
|
+
end
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if is_valid
|
|
59
|
+
return @app.call(env)
|
|
60
|
+
else
|
|
61
|
+
SC.logger << "Blocked connection attempt by ip: #{ip}\n"
|
|
62
|
+
return [403, { 'Content-Type' => 'text/plain' }, "YOU CANNOT BEEZ HERE."]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -59,7 +59,7 @@ module SC
|
|
|
59
59
|
self.filesystem = opts[:Filesystem]
|
|
60
60
|
|
|
61
61
|
projects = opts.delete(:projects) || [opts.delete(:project)].compact
|
|
62
|
-
app = self.new(*projects)
|
|
62
|
+
app = self.new(*projects, opts)
|
|
63
63
|
|
|
64
64
|
opts[:Host] ||= opts[:host] # allow either case.
|
|
65
65
|
opts[:Port] ||= opts[:port] || '4020'
|
|
@@ -93,7 +93,7 @@ module SC
|
|
|
93
93
|
server.run app, opts
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
def initialize(*projects)
|
|
96
|
+
def initialize(*projects, opts)
|
|
97
97
|
@projects = projects.flatten
|
|
98
98
|
|
|
99
99
|
# Get apps for each project & cascade if needed
|
|
@@ -105,6 +105,12 @@ module SC
|
|
|
105
105
|
@app = ::Rack::ConditionalGet.new(@app)
|
|
106
106
|
#@app = ::Rack::Deflater.new(@app)
|
|
107
107
|
|
|
108
|
+
# preprocess IPs so we can restrict properly
|
|
109
|
+
ips = opts[:allow_from_ips] || '127.0.0.1'
|
|
110
|
+
SC.logger << "Allowing access only from IPs: #{ips}. Use --allow-from-ips='*.*.*.*' to allow all\n"
|
|
111
|
+
ips = ips.split(',')
|
|
112
|
+
|
|
113
|
+
@app = SC::Rack::RestrictIP.new(@app, ips)
|
|
108
114
|
end
|
|
109
115
|
|
|
110
116
|
def call(env); @app.call(env); end
|
data/lib/sproutcore/rack.rb
CHANGED