mongrel_esi 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +15 -8
- data/bin/mongrel_esi +0 -0
- data/ext/esi/common.rl +32 -27
- data/ext/esi/esi_parser.c +11 -4
- data/ext/esi/parser.c +4536 -901
- data/ext/esi/parser.h +7 -0
- data/ext/esi/parser.rl +171 -53
- data/ext/esi/run-test.rb +0 -0
- data/ext/esi/test/common.rl +32 -27
- data/ext/esi/test/parser.c +322 -1342
- data/ext/esi/test/parser.h +7 -0
- data/ext/esi/test/parser.rl +171 -53
- data/ext/esi/test/test.c +13 -4
- data/ext/esi/test1.rb +35 -3
- data/lib/esi/cache.rb +2 -1
- data/lib/esi/config.rb +37 -0
- data/lib/esi/dispatcher.rb +3 -5
- data/lib/esi/invalidator.rb +3 -0
- data/lib/esi/logger.rb +0 -7
- data/lib/esi/parser.rb +70 -0
- data/lib/esi/processor.rb +88 -0
- data/lib/esi/proxy.rb +104 -0
- data/lib/esi/response.rb +106 -0
- data/lib/esi/router.rb +3 -0
- data/lib/esi/tag/attempt.rb +3 -0
- data/lib/esi/tag/base.rb +3 -1
- data/lib/esi/tag/except.rb +2 -0
- data/lib/esi/tag/include.rb +131 -132
- data/lib/esi/tag/invalidate.rb +2 -0
- data/lib/esi/tag/try.rb +3 -0
- data/test/integration/basic_test.rb +0 -1
- data/test/integration/docs/esi_max_age_varies.html +10 -0
- data/test/integration/handler_test.rb +31 -5
- data/test/integration/help.rb +13 -1
- data/test/load_test.rb +133 -0
- data/test/unit/include_request_test.rb +1 -1
- data/test/unit/parser_test.rb +41 -5
- data/test/unit/response_test.rb +184 -0
- metadata +50 -224
- data/doc/rdoc/classes/ESI/Cache.html +0 -178
- data/doc/rdoc/classes/ESI/Cache.src/M000060.html +0 -17
- data/doc/rdoc/classes/ESI/Cache.src/M000061.html +0 -20
- data/doc/rdoc/classes/ESI/Config/CacheConfig.html +0 -212
- data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000055.html +0 -19
- data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000056.html +0 -19
- data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000057.html +0 -18
- data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000058.html +0 -18
- data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000059.html +0 -18
- data/doc/rdoc/classes/ESI/Config/ConfigRouter.html +0 -187
- data/doc/rdoc/classes/ESI/Config/ConfigRouter.src/M000052.html +0 -19
- data/doc/rdoc/classes/ESI/Config/ConfigRouter.src/M000053.html +0 -21
- data/doc/rdoc/classes/ESI/Config/ConfigRouter.src/M000054.html +0 -21
- data/doc/rdoc/classes/ESI/Config.html +0 -291
- data/doc/rdoc/classes/ESI/Config.src/M000043.html +0 -18
- data/doc/rdoc/classes/ESI/Config.src/M000044.html +0 -18
- data/doc/rdoc/classes/ESI/Config.src/M000045.html +0 -35
- data/doc/rdoc/classes/ESI/Config.src/M000046.html +0 -38
- data/doc/rdoc/classes/ESI/Config.src/M000047.html +0 -23
- data/doc/rdoc/classes/ESI/Config.src/M000048.html +0 -18
- data/doc/rdoc/classes/ESI/Config.src/M000049.html +0 -20
- data/doc/rdoc/classes/ESI/Config.src/M000050.html +0 -18
- data/doc/rdoc/classes/ESI/Config.src/M000051.html +0 -24
- data/doc/rdoc/classes/ESI/Dispatcher.html +0 -172
- data/doc/rdoc/classes/ESI/Dispatcher.src/M000062.html +0 -23
- data/doc/rdoc/classes/ESI/Dispatcher.src/M000063.html +0 -20
- data/doc/rdoc/classes/ESI/Fragment.html +0 -218
- data/doc/rdoc/classes/ESI/Fragment.src/M000076.html +0 -21
- data/doc/rdoc/classes/ESI/Fragment.src/M000077.html +0 -18
- data/doc/rdoc/classes/ESI/Fragment.src/M000078.html +0 -18
- data/doc/rdoc/classes/ESI/Handler.html +0 -236
- data/doc/rdoc/classes/ESI/Handler.src/M000079.html +0 -19
- data/doc/rdoc/classes/ESI/Handler.src/M000080.html +0 -161
- data/doc/rdoc/classes/ESI/Handler.src/M000081.html +0 -24
- data/doc/rdoc/classes/ESI/Handler.src/M000082.html +0 -18
- data/doc/rdoc/classes/ESI/Handler.src/M000083.html +0 -26
- data/doc/rdoc/classes/ESI/Handler.src/M000084.html +0 -30
- data/doc/rdoc/classes/ESI/Invalidator.html +0 -131
- data/doc/rdoc/classes/ESI/Invalidator.src/M000004.html +0 -41
- data/doc/rdoc/classes/ESI/Log.html +0 -251
- data/doc/rdoc/classes/ESI/Log.src/M000005.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000006.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000007.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000008.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000009.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000010.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000011.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000012.html +0 -18
- data/doc/rdoc/classes/ESI/Log.src/M000013.html +0 -18
- data/doc/rdoc/classes/ESI/MemcachedCache.html +0 -314
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000034.html +0 -24
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000035.html +0 -22
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000036.html +0 -19
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000037.html +0 -23
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000038.html +0 -18
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000039.html +0 -19
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000040.html +0 -18
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000041.html +0 -18
- data/doc/rdoc/classes/ESI/MemcachedCache.src/M000042.html +0 -17
- data/doc/rdoc/classes/ESI/Router.html +0 -229
- data/doc/rdoc/classes/ESI/Router.src/M000073.html +0 -36
- data/doc/rdoc/classes/ESI/Router.src/M000074.html +0 -25
- data/doc/rdoc/classes/ESI/Router.src/M000075.html +0 -24
- data/doc/rdoc/classes/ESI/RubyCache.html +0 -278
- data/doc/rdoc/classes/ESI/RubyCache.src/M000064.html +0 -20
- data/doc/rdoc/classes/ESI/RubyCache.src/M000065.html +0 -22
- data/doc/rdoc/classes/ESI/RubyCache.src/M000066.html +0 -21
- data/doc/rdoc/classes/ESI/RubyCache.src/M000067.html +0 -22
- data/doc/rdoc/classes/ESI/RubyCache.src/M000068.html +0 -18
- data/doc/rdoc/classes/ESI/RubyCache.src/M000069.html +0 -22
- data/doc/rdoc/classes/ESI/RubyCache.src/M000070.html +0 -18
- data/doc/rdoc/classes/ESI/RubyCache.src/M000071.html +0 -18
- data/doc/rdoc/classes/ESI/RubyCache.src/M000072.html +0 -18
- data/doc/rdoc/classes/ESI/Tag/Attempt.html +0 -113
- data/doc/rdoc/classes/ESI/Tag/Base.html +0 -267
- data/doc/rdoc/classes/ESI/Tag/Base.src/M000028.html +0 -26
- data/doc/rdoc/classes/ESI/Tag/Base.src/M000029.html +0 -23
- data/doc/rdoc/classes/ESI/Tag/Base.src/M000030.html +0 -22
- data/doc/rdoc/classes/ESI/Tag/Base.src/M000031.html +0 -18
- data/doc/rdoc/classes/ESI/Tag/Base.src/M000032.html +0 -22
- data/doc/rdoc/classes/ESI/Tag/Base.src/M000033.html +0 -23
- data/doc/rdoc/classes/ESI/Tag/Except.html +0 -184
- data/doc/rdoc/classes/ESI/Tag/Except.src/M000020.html +0 -21
- data/doc/rdoc/classes/ESI/Tag/Except.src/M000021.html +0 -20
- data/doc/rdoc/classes/ESI/Tag/Except.src/M000022.html +0 -18
- data/doc/rdoc/classes/ESI/Tag/Include.html +0 -189
- data/doc/rdoc/classes/ESI/Tag/Include.src/M000017.html +0 -20
- data/doc/rdoc/classes/ESI/Tag/Include.src/M000018.html +0 -18
- data/doc/rdoc/classes/ESI/Tag/Include.src/M000019.html +0 -125
- data/doc/rdoc/classes/ESI/Tag/IncludeRequest/Error.html +0 -155
- data/doc/rdoc/classes/ESI/Tag/IncludeRequest/Error.src/M000016.html +0 -19
- data/doc/rdoc/classes/ESI/Tag/IncludeRequest.html +0 -199
- data/doc/rdoc/classes/ESI/Tag/IncludeRequest.src/M000014.html +0 -18
- data/doc/rdoc/classes/ESI/Tag/IncludeRequest.src/M000015.html +0 -42
- data/doc/rdoc/classes/ESI/Tag/Invalidate.html +0 -171
- data/doc/rdoc/classes/ESI/Tag/Invalidate.src/M000025.html +0 -19
- data/doc/rdoc/classes/ESI/Tag/Invalidate.src/M000026.html +0 -51
- data/doc/rdoc/classes/ESI/Tag/Invalidate.src/M000027.html +0 -19
- data/doc/rdoc/classes/ESI/Tag/Try.html +0 -161
- data/doc/rdoc/classes/ESI/Tag/Try.src/M000023.html +0 -40
- data/doc/rdoc/classes/ESI/Tag/Try.src/M000024.html +0 -18
- data/doc/rdoc/classes/ESI/Tag.html +0 -137
- data/doc/rdoc/classes/ESI.html +0 -169
- data/doc/rdoc/classes/MultiDirHandler.html +0 -198
- data/doc/rdoc/classes/MultiDirHandler.src/M000001.html +0 -20
- data/doc/rdoc/classes/MultiDirHandler.src/M000002.html +0 -28
- data/doc/rdoc/classes/MultiDirHandler.src/M000003.html +0 -22
- data/doc/rdoc/classes/Net/An/HTTP.html +0 -137
- data/doc/rdoc/classes/Net/An/HTTP.src/M000087.html +0 -17
- data/doc/rdoc/classes/Net/An/IORequest.html +0 -139
- data/doc/rdoc/classes/Net/An/IORequest.src/M000088.html +0 -17
- data/doc/rdoc/classes/Net/An/IOResponse.html +0 -139
- data/doc/rdoc/classes/Net/An/IOResponse.src/M000085.html +0 -17
- data/doc/rdoc/classes/Net/An/IOSocket.html +0 -137
- data/doc/rdoc/classes/Net/An/IOSocket.src/M000086.html +0 -17
- data/doc/rdoc/classes/Net/An.html +0 -114
- data/doc/rdoc/classes/Net.html +0 -119
- data/doc/rdoc/created.rid +0 -1
- data/doc/rdoc/files/COPYING.html +0 -168
- data/doc/rdoc/files/LICENSE.html +0 -605
- data/doc/rdoc/files/README.html +0 -361
- data/doc/rdoc/files/lib/esi/cache_rb.html +0 -113
- data/doc/rdoc/files/lib/esi/config_rb.html +0 -108
- data/doc/rdoc/files/lib/esi/dispatcher_rb.html +0 -109
- data/doc/rdoc/files/lib/esi/handler_rb.html +0 -121
- data/doc/rdoc/files/lib/esi/invalidator_rb.html +0 -117
- data/doc/rdoc/files/lib/esi/logger_rb.html +0 -108
- data/doc/rdoc/files/lib/esi/router_rb.html +0 -101
- data/doc/rdoc/files/lib/esi/tag/attempt_rb.html +0 -101
- data/doc/rdoc/files/lib/esi/tag/base_rb.html +0 -108
- data/doc/rdoc/files/lib/esi/tag/except_rb.html +0 -101
- data/doc/rdoc/files/lib/esi/tag/include_rb.html +0 -109
- data/doc/rdoc/files/lib/esi/tag/invalidate_rb.html +0 -109
- data/doc/rdoc/files/lib/esi/tag/try_rb.html +0 -108
- data/doc/rdoc/files/lib/multi_dirhandler_rb.html +0 -109
- data/doc/rdoc/files/lib/net/ahttp_rb.html +0 -109
- data/doc/rdoc/fr_class_index.html +0 -55
- data/doc/rdoc/fr_file_index.html +0 -44
- data/doc/rdoc/fr_method_index.html +0 -114
- data/doc/rdoc/index.html +0 -24
- data/doc/rdoc/rdoc-style.css +0 -208
- data/ext/esi/parser.rb +0 -49
- data/ext/esi/ruby_esi.rl +0 -135
- data/lib/esi/handler.rb +0 -221
- data/lib/net/ahttp.rb +0 -36
data/ext/esi/test/test.c
CHANGED
@@ -54,7 +54,7 @@ static int verify_string( const char *str, const char *str_value, int line, cons
|
|
54
54
|
int str_value_length = strlen( str_value );
|
55
55
|
|
56
56
|
if( str_len != str_value_length || strcmp( str, str_value ) ){
|
57
|
-
printf( "Strings are not equal\n\"%s\":%d\n!=\n\"%s\":%d\nat
|
57
|
+
printf( "Strings are not equal\n\t\"%s\":%d\n!=\t\n\"%s\":%d\nat\n%s:%d\n", str, str_len, str_value, str_value_length, test_name, line );
|
58
58
|
return 1;
|
59
59
|
}
|
60
60
|
return 0;
|
@@ -139,7 +139,6 @@ static void start_tag_handler( const void *data,
|
|
139
139
|
ESIAttribute *attributes,
|
140
140
|
void *user_data )
|
141
141
|
{
|
142
|
-
ESIAttribute *attr = attributes;
|
143
142
|
++start_tag_count;
|
144
143
|
detected_start_tags = add_detected_tag( detected_start_tags, tag_info_new( name_start, name_length, attributes ) );
|
145
144
|
}
|
@@ -244,7 +243,7 @@ static void test_simple_parser_input( ESIParser *parser )
|
|
244
243
|
{
|
245
244
|
TEST_INIT
|
246
245
|
|
247
|
-
feed_data( parser, "<p>some input</p><esi:include/>some more input\nsome input<esi:include timeout='10' src='hello'/>some more input" );
|
246
|
+
feed_data( parser, "<p>some input</p><esi:include />some more input\nsome input<esi:include timeout='10' src='hello'/>some more input" );
|
248
247
|
|
249
248
|
TEST_PREPARE_ASSERTIONS
|
250
249
|
|
@@ -304,7 +303,17 @@ static void test_sample_input( ESIParser *parser )
|
|
304
303
|
ASSERT_MATCH(" <div>some content</div>", output);
|
305
304
|
ASSERT_MATCH("<em>Support for em tags since they have an initial start sequence similar to and <esi: start/end sequence</em>", output );
|
306
305
|
ASSERT_NO_MATCH("<esi:", output);
|
306
|
+
/*
|
307
|
+
TagInfo *ptr = start_tags;
|
308
|
+
|
309
|
+
while( ptr ){
|
310
|
+
tag_info_show( ptr );
|
311
|
+
ptr = ptr->next;
|
312
|
+
}
|
307
313
|
|
314
|
+
printf( "start_tag_count: %d\n", start_tag_count );
|
315
|
+
printf( "end_tag_count: %d\n", end_tag_count );
|
316
|
+
*/
|
308
317
|
ASSERT_TRUE( start_tag_count == 13 );
|
309
318
|
ASSERT_TRUE( end_tag_count == 13 );
|
310
319
|
|
@@ -352,7 +361,7 @@ static void test_large_file( ESIParser *parser )
|
|
352
361
|
|
353
362
|
TEST_FINISH
|
354
363
|
}
|
355
|
-
|
364
|
+
|
356
365
|
int main( int argc, char **argv )
|
357
366
|
{
|
358
367
|
ESIParser *parser = esi_parser_new();
|
data/ext/esi/test1.rb
CHANGED
@@ -4,13 +4,16 @@ output = ""
|
|
4
4
|
|
5
5
|
p = ESI::CParser.new
|
6
6
|
|
7
|
+
start_tags = []
|
8
|
+
end_tags = []
|
9
|
+
|
7
10
|
|
8
11
|
p.start_tag_handler do|tag_name, attrs|
|
9
|
-
|
12
|
+
start_tags << {:name => tag_name, :attributes => attrs }
|
10
13
|
end
|
11
14
|
|
12
15
|
p.end_tag_handler do|tag_name|
|
13
|
-
|
16
|
+
end_tags << tag_name
|
14
17
|
end
|
15
18
|
|
16
19
|
p.output_handler do|data|
|
@@ -28,7 +31,36 @@ some inputsome more inputsome inputsome more input
|
|
28
31
|
some inputsome more input<p>some input</p>some more input
|
29
32
|
some inputsome more input</body></html>)
|
30
33
|
|
31
|
-
|
34
|
+
expected_start_tags = [
|
35
|
+
{:name => 'esi:include', :attributes => {"src"=>"hello", "max-age"=>"600+600", "timeout"=>"1"} },
|
36
|
+
{:name => 'esi:include', :attributes => {"src"=>"hello", "max-age"=>"600+600", "timeout"=>"1"} },
|
37
|
+
{:name => 'esi:include', :attributes => {"src"=>"hello", "max-age"=>"600+600", "timeout"=>"1"} },
|
38
|
+
{:name => 'esi:inline', :attributes => {"src"=>"hello", "max-age"=>"600+600", "timeout"=>"1"} },
|
39
|
+
{:name => 'esi:comment', :attributes => {"text"=>"hello", "src"=>"hello", "max-age"=>"600+600", "timeout"=>"1"} },
|
40
|
+
{:name => 'esi:include', :attributes => {"text"=>"hello", "src"=>"hello", "max-age"=>"600+600", "timeout"=>"1"} },
|
41
|
+
{:name => 'esi:include', :attributes => {"text"=>"hello", "src"=>"hello", "max-age"=>"600+600", "timeout"=>"1"} }
|
42
|
+
]
|
43
|
+
expected_end_tags =[ 'esi:include', 'esi:include', 'esi:include', 'esi:inline', 'esi:comment', 'esi:include', 'esi:include']
|
44
|
+
|
45
|
+
if( start_tags.size != expected_start_tags.size and end_tags.size != expected_end_tags.size )
|
46
|
+
puts "Failed expected start tags: #{expected_start_tags.size}, expected end tags: #{expected_end_tags.size}"
|
47
|
+
puts "Actual: start tags: #{start_tags.size}, end tags: #{end_tags.size}"
|
48
|
+
exit(1)
|
49
|
+
end
|
50
|
+
|
51
|
+
if expected_start_tags != start_tags
|
52
|
+
puts "Failed expected start tags: #{expected_start_tags.inspect}"
|
53
|
+
puts "Actual start tags: #{start_tags.inspect}"
|
54
|
+
exit(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
if expected_end_tags != end_tags
|
58
|
+
puts "Failed expected end tags: #{expected_end_tags.inspect}"
|
59
|
+
puts "Actual end tags: #{end_tags.inspect}"
|
60
|
+
exit(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
if( expected != output )
|
32
64
|
puts "Failed output was different from the expected"
|
33
65
|
puts "Expected: #{expected}"
|
34
66
|
puts "\n"
|
data/lib/esi/cache.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# Copyright (c) 2008 Todd A. Fisher
|
2
|
+
# see LICENSE
|
1
3
|
require 'digest/sha1'
|
2
4
|
require 'thread'
|
3
5
|
require 'ostruct'
|
@@ -12,7 +14,6 @@ module ESI
|
|
12
14
|
|
13
15
|
def initialize(uri,max_age,body)
|
14
16
|
@uri = uri
|
15
|
-
max_age.gsub!(/\+.*$/,'') if max_age.class == String
|
16
17
|
@max_age = Time.now.to_i + max_age.to_i
|
17
18
|
@body = body
|
18
19
|
end
|
data/lib/esi/config.rb
CHANGED
@@ -1,6 +1,43 @@
|
|
1
|
+
# Copyright (c) 2008 Todd A. Fisher
|
2
|
+
# see LICENSE
|
1
3
|
require 'ostruct'
|
2
4
|
|
3
5
|
module ESI
|
6
|
+
# This file provides configuration options to mongrel-esi
|
7
|
+
# Mongrel ESI is a proxy caching server with limited load balancing capablities
|
8
|
+
#
|
9
|
+
# ESI::Config.define(listeners) do|config|
|
10
|
+
#
|
11
|
+
# # define the caching rules globally for all routes, defaults to ruby
|
12
|
+
# config.cache do|c|
|
13
|
+
# c.memcached do|mc|
|
14
|
+
# mc.servers = ['localhost:11211'] # memcahed servers
|
15
|
+
# mc.namespace = 'mesi' # namespace for cache storage
|
16
|
+
# end
|
17
|
+
# c.ttl = 600 # default fragment time to live, when <esi:include does not include the max-age attribute
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # define rules for when to enable esi processing globally for all routes
|
21
|
+
# # using content type it is more flexible, but sometimes you will want to be
|
22
|
+
# # explicit about when to enable esi processing. For those cases, enable_for_surrogate_only will
|
23
|
+
# # require the presense of the Surrogate-Control header to contain the content="ESI/1.0" line.
|
24
|
+
# # see [http://www.w3.org/TR/edge-arch Edge Arch] for details.
|
25
|
+
# config.esi do|c|
|
26
|
+
# c.allowed_content_types = ['text/plain', 'text/html']
|
27
|
+
# #c.enable_for_surrogate_only = true # default is false
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # define request path routing rules, these rules match against request path to select a specific server
|
31
|
+
# config.routes do|s|
|
32
|
+
# #s.match( /content/ ) do|r|
|
33
|
+
# # r.servers = ['127.0.0.1:4000']
|
34
|
+
# #end
|
35
|
+
# s.default do|r|
|
36
|
+
# r.servers = ['127.0.0.1:3000']
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# end
|
4
41
|
class Config
|
5
42
|
|
6
43
|
attr_reader :config
|
data/lib/esi/dispatcher.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
require 'esi/
|
2
|
-
require 'mongrel'
|
1
|
+
require 'esi/proxy'
|
3
2
|
|
4
3
|
module ESI
|
5
4
|
|
6
|
-
class Dispatcher <
|
5
|
+
class Dispatcher < ESI::Proxy
|
7
6
|
attr_reader :config, :router
|
8
7
|
|
9
8
|
Thread.abort_on_exception = false
|
@@ -19,8 +18,7 @@ module ESI
|
|
19
18
|
|
20
19
|
def process(request, response)
|
21
20
|
@router = @config.router
|
22
|
-
|
23
|
-
handler.process(request, response)
|
21
|
+
super
|
24
22
|
end
|
25
23
|
|
26
24
|
end
|
data/lib/esi/invalidator.rb
CHANGED
data/lib/esi/logger.rb
CHANGED
data/lib/esi/parser.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Copyright (c) 2008 Todd A. Fisher
|
2
|
+
# see LICENSE
|
3
|
+
require 'esi/esi'
|
4
|
+
require 'esi/tag/base'
|
5
|
+
require 'esi/tag/include'
|
6
|
+
require 'esi/tag/invalidate'
|
7
|
+
require 'esi/tag/attempt'
|
8
|
+
require 'esi/tag/except'
|
9
|
+
require 'esi/tag/try'
|
10
|
+
|
11
|
+
module ESI
|
12
|
+
class Parser < CParser
|
13
|
+
attr_reader :response
|
14
|
+
|
15
|
+
def initialize( output, router, cache, max_depth )
|
16
|
+
super()
|
17
|
+
@router = router
|
18
|
+
@cache = cache
|
19
|
+
@max_depth = max_depth
|
20
|
+
@response = ESI::Response.new( output )
|
21
|
+
|
22
|
+
end_tag_handler do|tag_name|
|
23
|
+
if self.esi_tag.name == tag_name.gsub(/esi:/,'')
|
24
|
+
@response.close_active_buffer
|
25
|
+
tag_buffer = @response.reserve_buffer
|
26
|
+
tag = self.esi_tag.clone
|
27
|
+
thread = Thread.new(tag,tag_buffer) do|tag,buffer|
|
28
|
+
begin
|
29
|
+
tag.close(tag_buffer)
|
30
|
+
ensure
|
31
|
+
tag_buffer.close_write
|
32
|
+
end
|
33
|
+
end
|
34
|
+
self.esi_tag = nil
|
35
|
+
|
36
|
+
@response.wait_thread( thread )
|
37
|
+
else
|
38
|
+
self.esi_tag.close_child(self.output,tag_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def prepare( request_params, http_params, &block )
|
44
|
+
start_tag_handler do|tag_name, attrs|
|
45
|
+
tag = ESI::Tag::Base.create( @router,
|
46
|
+
request_params,
|
47
|
+
http_params,
|
48
|
+
tag_name.gsub(/esi:/,''),
|
49
|
+
attrs,
|
50
|
+
@cache )
|
51
|
+
# set the tag depth
|
52
|
+
tag.depth = self.depth if tag.respond_to?(:depth=)
|
53
|
+
tag.max_depth = @max_depth if tag.respond_to?(:max_depth=)
|
54
|
+
|
55
|
+
if self.esi_tag
|
56
|
+
self.esi_tag.add_child(tag)
|
57
|
+
else
|
58
|
+
self.esi_tag = tag
|
59
|
+
end
|
60
|
+
end
|
61
|
+
self.output=block if block_given?
|
62
|
+
end
|
63
|
+
|
64
|
+
def finish
|
65
|
+
super
|
66
|
+
@response.flush
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Copyright (c) 2008 Todd A. Fisher
|
2
|
+
require 'esi/parser'
|
3
|
+
require 'esi/response'
|
4
|
+
|
5
|
+
module ESI
|
6
|
+
class Processor
|
7
|
+
attr_reader :config, :router
|
8
|
+
|
9
|
+
include ESI::Log
|
10
|
+
|
11
|
+
def initialize( config, router )
|
12
|
+
@config = config
|
13
|
+
@router = router
|
14
|
+
@chunk_count = 0
|
15
|
+
@chunk_size = @config[:chunk_size] || 4096
|
16
|
+
@max_depth = @config[:max_depth] || 3
|
17
|
+
@chunk_buffer = StringIO.new # when buffer reaches chunk_size write to the http_response socket
|
18
|
+
@parser = ESI::Parser.new( @chunk_buffer, @router, @config.cache, @max_depth )
|
19
|
+
end
|
20
|
+
|
21
|
+
def send_head( http_response, status )
|
22
|
+
|
23
|
+
http_response.header["Transfer-Encoding"] = "chunked"
|
24
|
+
# this is the important part, rather then send the whole document back we send in chunks
|
25
|
+
# each fragment is roughly in it's own chunk, this does mean we require http 1.1, chunk size is still a limit
|
26
|
+
header = Mongrel::Const::STATUS_FORMAT % [status, Mongrel::HTTP_STATUS_CODES[status]]
|
27
|
+
header.gsub!(/Connection: close\r\n/,'')
|
28
|
+
http_response.header.out.rewind
|
29
|
+
header << http_response.header.out.read + Mongrel::Const::LINE_END
|
30
|
+
header.gsub!(/Status:.*?\r\n/,'')
|
31
|
+
http_response.write( header )
|
32
|
+
|
33
|
+
#print header
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def send_body( http_request, params, http_response, proxy_response )
|
38
|
+
|
39
|
+
@http_response = http_response
|
40
|
+
|
41
|
+
# prepare the parser given the raw request params and the preprocessed request params
|
42
|
+
@parser.prepare( http_request.params, params ) do|chars|
|
43
|
+
if @parser.response.active_buffer.closed_write?
|
44
|
+
@parser.response.open_active_buffer
|
45
|
+
@parser.response.send
|
46
|
+
end
|
47
|
+
@parser.response.active_buffer << chars
|
48
|
+
if @chunk_buffer.size > @chunk_size
|
49
|
+
send_chunk
|
50
|
+
@chunk_buffer = StringIO.new
|
51
|
+
@parser.response.update_output( @chunk_buffer )
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#timer = Time.now
|
56
|
+
# feed data to the parser
|
57
|
+
proxy_response.read_body { |data| @parser.process data }
|
58
|
+
#puts "processing time: #{Time.now - timer}"
|
59
|
+
|
60
|
+
#timer = Time.now
|
61
|
+
# finish the request
|
62
|
+
@parser.finish
|
63
|
+
#puts "finish time: #{Time.now - timer}"
|
64
|
+
|
65
|
+
send_chunk
|
66
|
+
|
67
|
+
@chunk_count
|
68
|
+
rescue => e
|
69
|
+
STDERR.puts "\n#{e.message}: #{e.backtrace.join("\n")}\n"
|
70
|
+
ensure
|
71
|
+
http_response.write( "0\r\n\r\n" )
|
72
|
+
http_response.done = true
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_chunk
|
76
|
+
@chunk_buffer.rewind
|
77
|
+
size = @chunk_buffer.size
|
78
|
+
chunk_header = "#{"%x" % size}" + Mongrel::Const::LINE_END
|
79
|
+
#puts chunk_header.inspect
|
80
|
+
@http_response.write( chunk_header ) # write the chunk size
|
81
|
+
#puts buffer.inspect
|
82
|
+
@http_response.write( @chunk_buffer.read + Mongrel::Const::LINE_END ) # write the chunk
|
83
|
+
|
84
|
+
@chunk_count += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
data/lib/esi/proxy.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# Copyright (c) 2008 Todd A. Fisher
|
2
|
+
# see LICENSE
|
3
|
+
require 'uri'
|
4
|
+
require 'timeout'
|
5
|
+
require 'net/http'
|
6
|
+
require 'mongrel'
|
7
|
+
require 'esi/logger'
|
8
|
+
require 'esi/cache'
|
9
|
+
require 'esi/config'
|
10
|
+
require 'esi/router'
|
11
|
+
require 'esi/processor'
|
12
|
+
|
13
|
+
module ESI
|
14
|
+
|
15
|
+
class Proxy < Mongrel::HttpHandler
|
16
|
+
attr_reader :config, :router
|
17
|
+
include ESI::Log
|
18
|
+
|
19
|
+
def process(request, response)
|
20
|
+
|
21
|
+
start = Time.now
|
22
|
+
status = 200
|
23
|
+
url = @router.url_for(request.params["REQUEST_URI"])
|
24
|
+
|
25
|
+
params = http_params(request.params)
|
26
|
+
|
27
|
+
log_debug "#{request.params["REQUEST_METHOD"]} => #{url}"
|
28
|
+
chunk_count = 0
|
29
|
+
uri = URI.parse(url)
|
30
|
+
|
31
|
+
path_with_query = uri.query ? "#{uri.path}?#{uri.query}" : uri.path
|
32
|
+
|
33
|
+
proxy_request = (request.params["REQUEST_METHOD"] == "POST") ?
|
34
|
+
Net::HTTP::Post.new( path_with_query, params ) :
|
35
|
+
Net::HTTP::Get.new( path_with_query, params )
|
36
|
+
|
37
|
+
proxy_connection = Net::HTTP.start(uri.host, uri.port)
|
38
|
+
|
39
|
+
# open the conneciton up so we can start to stream the connection
|
40
|
+
proxy_connection.request(proxy_request,request.body.read) do|proxy_response|
|
41
|
+
|
42
|
+
status = read_status( proxy_response )
|
43
|
+
|
44
|
+
copy_headers( response.header, proxy_response ) unless status >= 500
|
45
|
+
|
46
|
+
if status >= 500 or !@config.enable_esi_processor?( proxy_response )
|
47
|
+
response.start(status, true) do|head,out|
|
48
|
+
|
49
|
+
# proxy the 500 response
|
50
|
+
proxy_response.read_body do|fragment|
|
51
|
+
out << fragment
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
else
|
56
|
+
processor = Processor.new( @config, @router )
|
57
|
+
processor.send_head( response, status )
|
58
|
+
chunk_count = processor.send_body( request, params, response, proxy_response )
|
59
|
+
end
|
60
|
+
end # end request
|
61
|
+
|
62
|
+
rescue => e
|
63
|
+
STDERR.puts "\n#{e.message}: error at #{e.backtrace.first} msg at #{__FILE__}:#{__LINE__}\n"
|
64
|
+
ensure
|
65
|
+
log_request "\nCompleted => #{url}, #{Time.now - start} seconds with status #{status} and #{chunk_count} chunks\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
def read_status(response)
|
71
|
+
Net::HTTPResponse::CODE_TO_OBJ.select { |k,v| v == response.class }.first[0].to_i rescue 500
|
72
|
+
end
|
73
|
+
|
74
|
+
def http_params(params)
|
75
|
+
updated_params = {}
|
76
|
+
params.each do|k,v|
|
77
|
+
k = k.split('_').collect { |t| t.capitalize }.join('-')
|
78
|
+
if k[0,5] =='Http-'
|
79
|
+
k[0,5] = ''
|
80
|
+
updated_params[k] = v
|
81
|
+
end
|
82
|
+
end
|
83
|
+
updated_params
|
84
|
+
end
|
85
|
+
|
86
|
+
def copy_headers(head,response)
|
87
|
+
response.to_hash.each do |k,v|
|
88
|
+
# for Set-Cookie we need to split on ,
|
89
|
+
# some edge cases with , since things like expires might be a date with , in them.
|
90
|
+
k = k.split(/-/).map{|s| s.capitalize }.join('-')
|
91
|
+
if k == "Set-Cookie"
|
92
|
+
v.each do|cookie|
|
93
|
+
head["Set-Cookie"] = cookie.strip # mongrel is case sensitive about handling duplicates
|
94
|
+
end
|
95
|
+
else
|
96
|
+
head[k] = v unless k == "Content-Length" or k == "Surrogate-Control" or k == "Server"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
head["Server"] = "MongrelESI 0.4"
|
100
|
+
end
|
101
|
+
|
102
|
+
end # Handler
|
103
|
+
|
104
|
+
end # ESI
|