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.
Files changed (184) hide show
  1. data/Rakefile +15 -8
  2. data/bin/mongrel_esi +0 -0
  3. data/ext/esi/common.rl +32 -27
  4. data/ext/esi/esi_parser.c +11 -4
  5. data/ext/esi/parser.c +4536 -901
  6. data/ext/esi/parser.h +7 -0
  7. data/ext/esi/parser.rl +171 -53
  8. data/ext/esi/run-test.rb +0 -0
  9. data/ext/esi/test/common.rl +32 -27
  10. data/ext/esi/test/parser.c +322 -1342
  11. data/ext/esi/test/parser.h +7 -0
  12. data/ext/esi/test/parser.rl +171 -53
  13. data/ext/esi/test/test.c +13 -4
  14. data/ext/esi/test1.rb +35 -3
  15. data/lib/esi/cache.rb +2 -1
  16. data/lib/esi/config.rb +37 -0
  17. data/lib/esi/dispatcher.rb +3 -5
  18. data/lib/esi/invalidator.rb +3 -0
  19. data/lib/esi/logger.rb +0 -7
  20. data/lib/esi/parser.rb +70 -0
  21. data/lib/esi/processor.rb +88 -0
  22. data/lib/esi/proxy.rb +104 -0
  23. data/lib/esi/response.rb +106 -0
  24. data/lib/esi/router.rb +3 -0
  25. data/lib/esi/tag/attempt.rb +3 -0
  26. data/lib/esi/tag/base.rb +3 -1
  27. data/lib/esi/tag/except.rb +2 -0
  28. data/lib/esi/tag/include.rb +131 -132
  29. data/lib/esi/tag/invalidate.rb +2 -0
  30. data/lib/esi/tag/try.rb +3 -0
  31. data/test/integration/basic_test.rb +0 -1
  32. data/test/integration/docs/esi_max_age_varies.html +10 -0
  33. data/test/integration/handler_test.rb +31 -5
  34. data/test/integration/help.rb +13 -1
  35. data/test/load_test.rb +133 -0
  36. data/test/unit/include_request_test.rb +1 -1
  37. data/test/unit/parser_test.rb +41 -5
  38. data/test/unit/response_test.rb +184 -0
  39. metadata +50 -224
  40. data/doc/rdoc/classes/ESI/Cache.html +0 -178
  41. data/doc/rdoc/classes/ESI/Cache.src/M000060.html +0 -17
  42. data/doc/rdoc/classes/ESI/Cache.src/M000061.html +0 -20
  43. data/doc/rdoc/classes/ESI/Config/CacheConfig.html +0 -212
  44. data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000055.html +0 -19
  45. data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000056.html +0 -19
  46. data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000057.html +0 -18
  47. data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000058.html +0 -18
  48. data/doc/rdoc/classes/ESI/Config/CacheConfig.src/M000059.html +0 -18
  49. data/doc/rdoc/classes/ESI/Config/ConfigRouter.html +0 -187
  50. data/doc/rdoc/classes/ESI/Config/ConfigRouter.src/M000052.html +0 -19
  51. data/doc/rdoc/classes/ESI/Config/ConfigRouter.src/M000053.html +0 -21
  52. data/doc/rdoc/classes/ESI/Config/ConfigRouter.src/M000054.html +0 -21
  53. data/doc/rdoc/classes/ESI/Config.html +0 -291
  54. data/doc/rdoc/classes/ESI/Config.src/M000043.html +0 -18
  55. data/doc/rdoc/classes/ESI/Config.src/M000044.html +0 -18
  56. data/doc/rdoc/classes/ESI/Config.src/M000045.html +0 -35
  57. data/doc/rdoc/classes/ESI/Config.src/M000046.html +0 -38
  58. data/doc/rdoc/classes/ESI/Config.src/M000047.html +0 -23
  59. data/doc/rdoc/classes/ESI/Config.src/M000048.html +0 -18
  60. data/doc/rdoc/classes/ESI/Config.src/M000049.html +0 -20
  61. data/doc/rdoc/classes/ESI/Config.src/M000050.html +0 -18
  62. data/doc/rdoc/classes/ESI/Config.src/M000051.html +0 -24
  63. data/doc/rdoc/classes/ESI/Dispatcher.html +0 -172
  64. data/doc/rdoc/classes/ESI/Dispatcher.src/M000062.html +0 -23
  65. data/doc/rdoc/classes/ESI/Dispatcher.src/M000063.html +0 -20
  66. data/doc/rdoc/classes/ESI/Fragment.html +0 -218
  67. data/doc/rdoc/classes/ESI/Fragment.src/M000076.html +0 -21
  68. data/doc/rdoc/classes/ESI/Fragment.src/M000077.html +0 -18
  69. data/doc/rdoc/classes/ESI/Fragment.src/M000078.html +0 -18
  70. data/doc/rdoc/classes/ESI/Handler.html +0 -236
  71. data/doc/rdoc/classes/ESI/Handler.src/M000079.html +0 -19
  72. data/doc/rdoc/classes/ESI/Handler.src/M000080.html +0 -161
  73. data/doc/rdoc/classes/ESI/Handler.src/M000081.html +0 -24
  74. data/doc/rdoc/classes/ESI/Handler.src/M000082.html +0 -18
  75. data/doc/rdoc/classes/ESI/Handler.src/M000083.html +0 -26
  76. data/doc/rdoc/classes/ESI/Handler.src/M000084.html +0 -30
  77. data/doc/rdoc/classes/ESI/Invalidator.html +0 -131
  78. data/doc/rdoc/classes/ESI/Invalidator.src/M000004.html +0 -41
  79. data/doc/rdoc/classes/ESI/Log.html +0 -251
  80. data/doc/rdoc/classes/ESI/Log.src/M000005.html +0 -18
  81. data/doc/rdoc/classes/ESI/Log.src/M000006.html +0 -18
  82. data/doc/rdoc/classes/ESI/Log.src/M000007.html +0 -18
  83. data/doc/rdoc/classes/ESI/Log.src/M000008.html +0 -18
  84. data/doc/rdoc/classes/ESI/Log.src/M000009.html +0 -18
  85. data/doc/rdoc/classes/ESI/Log.src/M000010.html +0 -18
  86. data/doc/rdoc/classes/ESI/Log.src/M000011.html +0 -18
  87. data/doc/rdoc/classes/ESI/Log.src/M000012.html +0 -18
  88. data/doc/rdoc/classes/ESI/Log.src/M000013.html +0 -18
  89. data/doc/rdoc/classes/ESI/MemcachedCache.html +0 -314
  90. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000034.html +0 -24
  91. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000035.html +0 -22
  92. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000036.html +0 -19
  93. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000037.html +0 -23
  94. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000038.html +0 -18
  95. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000039.html +0 -19
  96. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000040.html +0 -18
  97. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000041.html +0 -18
  98. data/doc/rdoc/classes/ESI/MemcachedCache.src/M000042.html +0 -17
  99. data/doc/rdoc/classes/ESI/Router.html +0 -229
  100. data/doc/rdoc/classes/ESI/Router.src/M000073.html +0 -36
  101. data/doc/rdoc/classes/ESI/Router.src/M000074.html +0 -25
  102. data/doc/rdoc/classes/ESI/Router.src/M000075.html +0 -24
  103. data/doc/rdoc/classes/ESI/RubyCache.html +0 -278
  104. data/doc/rdoc/classes/ESI/RubyCache.src/M000064.html +0 -20
  105. data/doc/rdoc/classes/ESI/RubyCache.src/M000065.html +0 -22
  106. data/doc/rdoc/classes/ESI/RubyCache.src/M000066.html +0 -21
  107. data/doc/rdoc/classes/ESI/RubyCache.src/M000067.html +0 -22
  108. data/doc/rdoc/classes/ESI/RubyCache.src/M000068.html +0 -18
  109. data/doc/rdoc/classes/ESI/RubyCache.src/M000069.html +0 -22
  110. data/doc/rdoc/classes/ESI/RubyCache.src/M000070.html +0 -18
  111. data/doc/rdoc/classes/ESI/RubyCache.src/M000071.html +0 -18
  112. data/doc/rdoc/classes/ESI/RubyCache.src/M000072.html +0 -18
  113. data/doc/rdoc/classes/ESI/Tag/Attempt.html +0 -113
  114. data/doc/rdoc/classes/ESI/Tag/Base.html +0 -267
  115. data/doc/rdoc/classes/ESI/Tag/Base.src/M000028.html +0 -26
  116. data/doc/rdoc/classes/ESI/Tag/Base.src/M000029.html +0 -23
  117. data/doc/rdoc/classes/ESI/Tag/Base.src/M000030.html +0 -22
  118. data/doc/rdoc/classes/ESI/Tag/Base.src/M000031.html +0 -18
  119. data/doc/rdoc/classes/ESI/Tag/Base.src/M000032.html +0 -22
  120. data/doc/rdoc/classes/ESI/Tag/Base.src/M000033.html +0 -23
  121. data/doc/rdoc/classes/ESI/Tag/Except.html +0 -184
  122. data/doc/rdoc/classes/ESI/Tag/Except.src/M000020.html +0 -21
  123. data/doc/rdoc/classes/ESI/Tag/Except.src/M000021.html +0 -20
  124. data/doc/rdoc/classes/ESI/Tag/Except.src/M000022.html +0 -18
  125. data/doc/rdoc/classes/ESI/Tag/Include.html +0 -189
  126. data/doc/rdoc/classes/ESI/Tag/Include.src/M000017.html +0 -20
  127. data/doc/rdoc/classes/ESI/Tag/Include.src/M000018.html +0 -18
  128. data/doc/rdoc/classes/ESI/Tag/Include.src/M000019.html +0 -125
  129. data/doc/rdoc/classes/ESI/Tag/IncludeRequest/Error.html +0 -155
  130. data/doc/rdoc/classes/ESI/Tag/IncludeRequest/Error.src/M000016.html +0 -19
  131. data/doc/rdoc/classes/ESI/Tag/IncludeRequest.html +0 -199
  132. data/doc/rdoc/classes/ESI/Tag/IncludeRequest.src/M000014.html +0 -18
  133. data/doc/rdoc/classes/ESI/Tag/IncludeRequest.src/M000015.html +0 -42
  134. data/doc/rdoc/classes/ESI/Tag/Invalidate.html +0 -171
  135. data/doc/rdoc/classes/ESI/Tag/Invalidate.src/M000025.html +0 -19
  136. data/doc/rdoc/classes/ESI/Tag/Invalidate.src/M000026.html +0 -51
  137. data/doc/rdoc/classes/ESI/Tag/Invalidate.src/M000027.html +0 -19
  138. data/doc/rdoc/classes/ESI/Tag/Try.html +0 -161
  139. data/doc/rdoc/classes/ESI/Tag/Try.src/M000023.html +0 -40
  140. data/doc/rdoc/classes/ESI/Tag/Try.src/M000024.html +0 -18
  141. data/doc/rdoc/classes/ESI/Tag.html +0 -137
  142. data/doc/rdoc/classes/ESI.html +0 -169
  143. data/doc/rdoc/classes/MultiDirHandler.html +0 -198
  144. data/doc/rdoc/classes/MultiDirHandler.src/M000001.html +0 -20
  145. data/doc/rdoc/classes/MultiDirHandler.src/M000002.html +0 -28
  146. data/doc/rdoc/classes/MultiDirHandler.src/M000003.html +0 -22
  147. data/doc/rdoc/classes/Net/An/HTTP.html +0 -137
  148. data/doc/rdoc/classes/Net/An/HTTP.src/M000087.html +0 -17
  149. data/doc/rdoc/classes/Net/An/IORequest.html +0 -139
  150. data/doc/rdoc/classes/Net/An/IORequest.src/M000088.html +0 -17
  151. data/doc/rdoc/classes/Net/An/IOResponse.html +0 -139
  152. data/doc/rdoc/classes/Net/An/IOResponse.src/M000085.html +0 -17
  153. data/doc/rdoc/classes/Net/An/IOSocket.html +0 -137
  154. data/doc/rdoc/classes/Net/An/IOSocket.src/M000086.html +0 -17
  155. data/doc/rdoc/classes/Net/An.html +0 -114
  156. data/doc/rdoc/classes/Net.html +0 -119
  157. data/doc/rdoc/created.rid +0 -1
  158. data/doc/rdoc/files/COPYING.html +0 -168
  159. data/doc/rdoc/files/LICENSE.html +0 -605
  160. data/doc/rdoc/files/README.html +0 -361
  161. data/doc/rdoc/files/lib/esi/cache_rb.html +0 -113
  162. data/doc/rdoc/files/lib/esi/config_rb.html +0 -108
  163. data/doc/rdoc/files/lib/esi/dispatcher_rb.html +0 -109
  164. data/doc/rdoc/files/lib/esi/handler_rb.html +0 -121
  165. data/doc/rdoc/files/lib/esi/invalidator_rb.html +0 -117
  166. data/doc/rdoc/files/lib/esi/logger_rb.html +0 -108
  167. data/doc/rdoc/files/lib/esi/router_rb.html +0 -101
  168. data/doc/rdoc/files/lib/esi/tag/attempt_rb.html +0 -101
  169. data/doc/rdoc/files/lib/esi/tag/base_rb.html +0 -108
  170. data/doc/rdoc/files/lib/esi/tag/except_rb.html +0 -101
  171. data/doc/rdoc/files/lib/esi/tag/include_rb.html +0 -109
  172. data/doc/rdoc/files/lib/esi/tag/invalidate_rb.html +0 -109
  173. data/doc/rdoc/files/lib/esi/tag/try_rb.html +0 -108
  174. data/doc/rdoc/files/lib/multi_dirhandler_rb.html +0 -109
  175. data/doc/rdoc/files/lib/net/ahttp_rb.html +0 -109
  176. data/doc/rdoc/fr_class_index.html +0 -55
  177. data/doc/rdoc/fr_file_index.html +0 -44
  178. data/doc/rdoc/fr_method_index.html +0 -114
  179. data/doc/rdoc/index.html +0 -24
  180. data/doc/rdoc/rdoc-style.css +0 -208
  181. data/ext/esi/parser.rb +0 -49
  182. data/ext/esi/ruby_esi.rl +0 -135
  183. data/lib/esi/handler.rb +0 -221
  184. 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 %s:%d\n", str, str_len, str_value, str_value_length, test_name, line );
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 &lt;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
- puts "Start: #{tag_name} #{attrs.inspect}"
12
+ start_tags << {:name => tag_name, :attributes => attrs }
10
13
  end
11
14
 
12
15
  p.end_tag_handler do|tag_name|
13
- puts "End: #{tag_name}"
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
- if( expected != output )
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
@@ -1,9 +1,8 @@
1
- require 'esi/handler'
2
- require 'mongrel'
1
+ require 'esi/proxy'
3
2
 
4
3
  module ESI
5
4
 
6
- class Dispatcher < Mongrel::HttpHandler
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
- handler = ESI::Handler.new(self)
23
- handler.process(request, response)
21
+ super
24
22
  end
25
23
 
26
24
  end
@@ -1,3 +1,6 @@
1
+ # Copyright (c) 2008 Todd A. Fisher
2
+ # see LICENSE
3
+
1
4
  # this is a separate thread that runs on port 4001
2
5
  # when requests come to this they must authenticate
3
6
  # and be of type POST
data/lib/esi/logger.rb CHANGED
@@ -35,12 +35,5 @@ module ESI
35
35
  log( $logger || STDERR, msg )
36
36
  end
37
37
 
38
- def log_info( msg )
39
- log( $logger || STDERR, msg )
40
- end
41
-
42
- def log_warn( msg )
43
- log( $logger || STDERR, msg )
44
- end
45
38
  end
46
39
  end
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