mongrel_esi 0.4.1 → 0.5.0

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.
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