swiftiply 0.6.1.1 → 1.0.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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTORS +2 -0
  3. data/README.md +62 -0
  4. data/bin/{mongrel_rails → evented_mongrel_rails} +6 -14
  5. data/bin/swiftiplied_mongrel_rails +246 -0
  6. data/bin/swiftiply +136 -116
  7. data/bin/swiftiply_mongrel_rails +2 -2
  8. data/bin/swiftiplyctl +283 -0
  9. data/cleanup.sh +5 -0
  10. data/ext/deque/extconf.rb +162 -0
  11. data/ext/deque/swiftcore/rubymain.cpp +435 -0
  12. data/ext/fastfilereader/extconf.rb +2 -2
  13. data/ext/fastfilereader/mapper.cpp +2 -0
  14. data/ext/map/extconf.rb +161 -0
  15. data/ext/map/rubymain.cpp +500 -0
  16. data/ext/splaytree/extconf.rb +161 -0
  17. data/ext/splaytree/swiftcore/rubymain.cpp +580 -0
  18. data/ext/splaytree/swiftcore/splay_map.h +635 -0
  19. data/ext/splaytree/swiftcore/splay_set.h +575 -0
  20. data/ext/splaytree/swiftcore/splay_tree.h +1127 -0
  21. data/external/httpclient.rb +231 -0
  22. data/external/package.rb +13 -13
  23. data/setup.rb +18 -2
  24. data/src/swiftcore/Swiftiply.rb +417 -773
  25. data/src/swiftcore/Swiftiply/backend_protocol.rb +213 -0
  26. data/src/swiftcore/Swiftiply/cache_base.rb +49 -0
  27. data/src/swiftcore/Swiftiply/cache_base_mixin.rb +52 -0
  28. data/src/swiftcore/Swiftiply/cluster_managers/rest_based_cluster_manager.rb +9 -0
  29. data/src/swiftcore/Swiftiply/cluster_protocol.rb +70 -0
  30. data/src/swiftcore/Swiftiply/config.rb +370 -0
  31. data/src/swiftcore/Swiftiply/config/rest_updater.rb +26 -0
  32. data/src/swiftcore/Swiftiply/constants.rb +101 -0
  33. data/src/swiftcore/Swiftiply/content_cache_entry.rb +44 -0
  34. data/src/swiftcore/Swiftiply/content_response.rb +45 -0
  35. data/src/swiftcore/Swiftiply/control_protocol.rb +49 -0
  36. data/src/swiftcore/Swiftiply/dynamic_request_cache.rb +41 -0
  37. data/src/swiftcore/Swiftiply/etag_cache.rb +64 -0
  38. data/src/swiftcore/Swiftiply/file_cache.rb +46 -0
  39. data/src/swiftcore/Swiftiply/hash_cache_base.rb +22 -0
  40. data/src/swiftcore/Swiftiply/http_recognizer.rb +267 -0
  41. data/src/swiftcore/Swiftiply/loggers/Analogger.rb +21 -0
  42. data/src/swiftcore/Swiftiply/loggers/stderror.rb +13 -0
  43. data/src/swiftcore/Swiftiply/mocklog.rb +10 -0
  44. data/src/swiftcore/Swiftiply/proxy.rb +15 -0
  45. data/src/swiftcore/Swiftiply/proxy_backends/keepalive.rb +286 -0
  46. data/src/swiftcore/Swiftiply/proxy_backends/traditional.rb +286 -0
  47. data/src/swiftcore/Swiftiply/proxy_backends/traditional/redis_directory.rb +87 -0
  48. data/src/swiftcore/Swiftiply/proxy_backends/traditional/static_directory.rb +69 -0
  49. data/src/swiftcore/Swiftiply/proxy_bag.rb +716 -0
  50. data/src/swiftcore/Swiftiply/rest_based_cluster_manager.rb +15 -0
  51. data/src/swiftcore/Swiftiply/splay_cache_base.rb +21 -0
  52. data/src/swiftcore/Swiftiply/support_pagecache.rb +6 -3
  53. data/src/swiftcore/Swiftiply/swiftiply_2_http_proxy.rb +7 -0
  54. data/src/swiftcore/Swiftiply/swiftiply_client.rb +20 -5
  55. data/src/swiftcore/Swiftiply/version.rb +5 -0
  56. data/src/swiftcore/evented_mongrel.rb +26 -8
  57. data/src/swiftcore/hash.rb +43 -0
  58. data/src/swiftcore/method_builder.rb +28 -0
  59. data/src/swiftcore/streamer.rb +46 -0
  60. data/src/swiftcore/swiftiplied_mongrel.rb +91 -23
  61. data/src/swiftcore/types.rb +20 -3
  62. data/swiftiply.gemspec +14 -8
  63. data/test/TC_Deque.rb +152 -0
  64. data/test/TC_ProxyBag.rb +147 -166
  65. data/test/TC_Swiftiply.rb +576 -169
  66. data/test/TC_Swiftiply/mongrel/evented_hello.rb +1 -1
  67. data/test/TC_Swiftiply/mongrel/swiftiplied_hello.rb +1 -1
  68. data/test/TC_Swiftiply/test_serve_static_file_xsendfile/sendfile_client.rb +27 -0
  69. data/test/TC_Swiftiply/test_ssl/bin/validate_ssl_capability.rb +21 -0
  70. data/test/TC_Swiftiply/test_ssl/test.cert +16 -0
  71. data/test/TC_Swiftiply/test_ssl/test.key +15 -0
  72. data/{bin → test/bin}/echo_client +0 -0
  73. metadata +136 -94
  74. data/README +0 -126
  75. data/ext/swiftiply_parse/parse.rl +0 -90
@@ -0,0 +1,26 @@
1
+ # Encoding:ascii-8bit
2
+
3
+ module Swiftcore
4
+ module Swiftiply
5
+ class Config
6
+ class RestUpdater
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ def start
13
+ host = @config[Chost] || '127.0.0.1'
14
+ port = @config[Cport] || 9949
15
+ end
16
+
17
+ class RestUpdaterProtocol < EventMachine::Connection
18
+ def receive_data data
19
+
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,101 @@
1
+ module Swiftcore
2
+
3
+ Deque = Array unless HasDeque or const_defined?(:Deque)
4
+
5
+ module Swiftiply
6
+
7
+ # Yeah, these constants look kind of tacky. Inside of tight loops,
8
+ # though, using them makes a small but measurable difference, and those
9
+ # small differences add up....
10
+ C_asterisk = '*'.freeze
11
+ C_colon = ':'.freeze
12
+ C_empty = ''.freeze
13
+ C_header_close = 'HTTP/1.1 200 OK\r\nConnection: close\r\n'.freeze
14
+ C_header_keepalive = 'HTTP/1.1 200 OK\r\n'.freeze
15
+ C_localhost = '127.0.0.1'.freeze
16
+ C_minus = '-'.freeze
17
+ C_plus = '+'.freeze
18
+ C_slash = '/'.freeze
19
+ C_slashindex_html = '/index.html'.freeze
20
+ C1_0 = '1.0'.freeze
21
+ C1_1 = '1.1'.freeze
22
+ C_304 = "HTTP/1.1 304 Not Modified\r\n".freeze
23
+ C_date_header_range = 6..-5
24
+ C80 = '80'.freeze
25
+ Caos = 'application/octet-stream'.freeze
26
+ Cat = 'at'.freeze
27
+ Ccache_directory = 'cache_directory'.freeze
28
+ Ccache_extensions = 'cache_extensions'.freeze
29
+ Ccallsite = 'callsite'.freeze
30
+ Cclassname = 'classname'.freeze
31
+ Ccluster_address = 'cluster_address'.freeze
32
+ Ccluster_port = 'cluster_port'.freeze
33
+ Ccluster_server = 'cluster_server'.freeze
34
+ CConnection_close = "Connection: close\r\n".freeze
35
+ CConnection_KeepAlive = "Connection: Keep-Alive\r\n".freeze
36
+ CBackendAddress = 'BackendAddress'.freeze
37
+ CBackendPort = 'BackendPort'.freeze
38
+ Cbackup = 'backup'.freeze
39
+ Ccertfile = 'certfile'.freeze
40
+ Cchunked_encoding_threshold = 'chunked_encoding_threshold'.freeze
41
+ Cxforwardedfor = 'xforwardedfor'.freeze
42
+ Cdaemonize = 'daemonize'.freeze
43
+ Cdefault = 'default'.freeze
44
+ CDELETE = 'DELETE'.freeze
45
+ Cdescriptor_cache = 'descriptor_cache_threshold'.freeze
46
+ Cdescriptors = 'descriptors'.freeze
47
+ Cdirectory = 'directory'.freeze
48
+ Cdocroot = 'docroot'.freeze
49
+ Cdynamic_request_cache = 'dynamic_request_cache'.freeze
50
+ Cenable_sendfile_404 = 'enable_sendfile_404'.freeze
51
+ Cepoll = 'epoll'.freeze
52
+ Cepoll_descriptors = 'epoll_descriptors'.freeze
53
+ Cetag_cache = 'etag_cache'.freeze
54
+ Cfile_cache = 'file_cache'.freeze
55
+ CGET = 'GET'.freeze
56
+ Cgroup = 'group'.freeze
57
+ CHEAD = 'HEAD'.freeze
58
+ Chost = 'host'.freeze
59
+ Cincoming = 'incoming'.freeze
60
+ Cinfo = 'info'.freeze
61
+ Ckeepalive = 'keepalive'.freeze
62
+ Ckey = 'key'.freeze
63
+ Ckeyfile = 'keyfile'.freeze
64
+ Cmanager = 'manager'.freeze
65
+ Cmap = 'map'.freeze
66
+ Cmax_cache_size = 'max_cache_size'.freeze
67
+ Cmsg_expired = 'browser connection expired'.freeze
68
+ Coutgoing = 'outgoing'.freeze
69
+ Cparams = 'params'.freeze
70
+ Cport = 'port'.freeze
71
+ CPOST = 'POST'.freeze
72
+ Cproxy = 'proxy'.freeze
73
+ CPUT = 'PUT'.freeze
74
+ Credeployable = 'redeployable'.freeze
75
+ Credeployment_sizelimit = 'redeployment_sizelimit'.freeze
76
+ Crequire = 'require'.freeze
77
+ Csendfileroot = 'sendfileroot'.freeze
78
+ Cservers = 'servers'.freeze
79
+ Cssl = 'ssl'.freeze
80
+ Csize = 'size'.freeze
81
+ Cstaticmask = 'staticmask'.freeze
82
+ Cswiftclient = 'swiftclient'.freeze
83
+ Cthreshold = 'threshold'.freeze
84
+ Ctimeslice = 'timeslice'.freeze
85
+ Ctimeout = 'timeout'.freeze
86
+ Cupdates = 'updates'.freeze
87
+ Curl = 'url'.freeze
88
+ Cuser = 'user'.freeze
89
+ Cwindow = 'window'.freeze
90
+
91
+ C_fsep = File::SEPARATOR
92
+
93
+ UnknownSocket = Socket::pack_sockaddr_in(0,'0.0.0.0')
94
+
95
+ RunningConfig = {}
96
+
97
+ class EMStartServerError < RuntimeError; end
98
+ class SwiftiplyLoggerNotFound < RuntimeError; end
99
+
100
+ end
101
+ end
@@ -0,0 +1,44 @@
1
+ # This was inspired by Paul Sadauskas' Resourceful library: http://github.com/paul/resourceful
2
+ module Swiftcore
3
+ module Swiftiply
4
+ class ContentCacheEntry < ::Hash
5
+
6
+ # @param [Resourceful::Request] request
7
+ # The request whose response we are storing in the cache.
8
+ # @param response<Resourceful::Response>
9
+ # The Response obhect to be stored.
10
+ def initialize(request, response)
11
+ super()
12
+ self[:request_uri] = request.uri
13
+ self[:request_time] = request.request_time
14
+ self[:request_vary_headers] = select_request_headers(request, response)
15
+ self[:response] = response
16
+ end
17
+
18
+ # Returns true if this entry may be used to fullfil the given request,
19
+ # according to the vary headers.
20
+ #
21
+ # @param request<Resourceful::Request>
22
+ # The request to do the lookup on.
23
+ def valid_for?(request)
24
+ request.uri == self[:request_uri] && self[:request_vary_headers].all? {|key, value| request.header[key] == value }
25
+ end
26
+
27
+ # Selects the headers from the request named by the response's Vary header
28
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.6
29
+ #
30
+ # @param [Resourceful::Request] request
31
+ # The request used to obtain the response.
32
+ # @param [Resourceful::Response] response
33
+ # The response obtained from the request.
34
+ def select_request_headers(request, response)
35
+ headers = {}
36
+
37
+ # Broken
38
+ self[:response].header['Vary'].each { |name| header[name] = request.header[name] if request.header[name] } if response.header['Vary']
39
+
40
+ header
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,45 @@
1
+ require 'digest/md5'
2
+
3
+ module Swiftcore
4
+ module Swiftiply
5
+ class ContentResponse < ::Hash
6
+ # data,etag,mtime,path,cookies,headers
7
+ def initialize(uri, headers, cacheable_data)
8
+ parse_basic_http_data headers
9
+ self[:uri] = uri
10
+ self[:headers] = headers
11
+ self[:data] = cacheable_data
12
+ self[:ETag] ||= Digest::MD5.hexdigest(cacheable_data)
13
+ end
14
+
15
+ def parse_basic_http_data(headers)
16
+ # The basic interesting pieces are ETag, Date, Expires, Vary, and Cache-Control.
17
+ # I can hear you thinking right now that a line of 'if' statements is gross.
18
+ # You're right. It's also a heaping hell of a lot faster than using something
19
+ # like #scan. So, for this code, you'll just have to accept it.
20
+ # Thanks for your consideration.
21
+ if headers =~ /ETag:\s+(.*)/
22
+ self[:ETag] = $1
23
+ end
24
+ if headers =~ /Date:\s+(.*)/
25
+ self[:Date] = $1
26
+ end
27
+ if headers =~ /Expires:\s+(.*)/
28
+ self[:Expires] = $1
29
+ end
30
+ if headers =~ /Vary:\s+(.*)/
31
+ self[:Vary] = $1
32
+ end
33
+ if headers =~ /Cache-Control:\s+(.*)/
34
+ self[:Cache_Control] = $1
35
+ end
36
+ end
37
+
38
+ def header(header_name)
39
+ name_symbol = header_name.to_sym
40
+ self[name_symbol] || (self[:headers] =~ /#{header_name}:\s+(.*)/ && self[name_symbol] = $1)
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ require "swiftcore/Swiftiply/http_recognizer"
2
+
3
+ module Swiftcore
4
+ module Swiftiply
5
+
6
+ # The ControlProtocol implements a simple REST HTTP handler that can be
7
+ # used to affect the running configuration of Swiftiply.
8
+
9
+ class ControlProtocol < HttpRecognizer
10
+
11
+ # Should be able to use this to control all aspects of Swuftiply configuration and behavior.
12
+ # - Query current performance information and statistics
13
+ # * GET /status
14
+ # * GET /status/DOMAIN
15
+ # * GET /domains
16
+ # * GET /config/DOMAIN
17
+ # - Supply new config sections (json payload?)
18
+ # * POST /config/DOMAIN
19
+ # * PUT /config/DOMAIN
20
+ # (There is currently no useful differentiation between the use of
21
+ # POST and PUT in the API; they are both idempotent operations
22
+ # that place the provided configuration into Swiftiply.)
23
+ # - Remove config sections
24
+ # * DELETE /config/DOMAIN
25
+ def push
26
+ case @request_method
27
+ when CGET
28
+ case @uri
29
+ when /\/status(\/(.*))$/
30
+ when /\/domains/
31
+ when /\/config\/(.+)$/
32
+ end
33
+ when CPOST, CPUT
34
+ case @uri
35
+ when /\/config\/(.+)$/
36
+ end
37
+ when CDELETE
38
+ case @uri
39
+ when /\/config\/(.+)$/
40
+ end
41
+ else
42
+ # No supported method; discard
43
+ close_connection
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ begin
2
+ load_attempted ||= false
3
+ require 'swiftcore/Swiftiply/cache_base'
4
+ rescue LoadError => e
5
+ if !load_attempted
6
+ load_attempted = true
7
+ begin
8
+ require 'rubygems'
9
+ rescue LoadError
10
+ raise e
11
+ end
12
+ retry
13
+ end
14
+ raise e
15
+ end
16
+
17
+ module Swiftcore
18
+ module Swiftiply
19
+ class DynamicRequestCache < CacheBase
20
+ attr_accessor :one_client_name
21
+
22
+ def initialize(docroot, vw, ts, maxsize)
23
+ @docroot = docroot
24
+ super(vw,ts,maxsize)
25
+ end
26
+
27
+ def verify(path)
28
+ if @docroot && self[path]
29
+ if ProxyBag.find_static_file(@docroot,path,@one_client_name)
30
+ self.delete path
31
+ false
32
+ else
33
+ true
34
+ end
35
+ else
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,64 @@
1
+ begin
2
+ load_attempted ||= false
3
+ require 'swiftcore/Swiftiply/cache_base'
4
+ require 'digest/md5'
5
+ rescue LoadError => e
6
+ if !load_attempted
7
+ load_attempted = true
8
+ begin
9
+ require 'rubygems'
10
+ rescue LoadError
11
+ raise e
12
+ end
13
+ retry
14
+ end
15
+ raise e
16
+ end
17
+
18
+ module Swiftcore
19
+ module Swiftiply
20
+
21
+ ReadMode = 'rb'.freeze
22
+
23
+ class EtagCache < CacheBase
24
+
25
+ def etag_mtime(path)
26
+ self[path] || self[path] = self.calculate_etag(path)
27
+ end
28
+
29
+ def etag(path)
30
+ self[path] && self[path].first || (self[path] = self.calculate_etag(path)).first
31
+ end
32
+
33
+ def mtime(path)
34
+ self[path] && self[path].last || (self[path] = self.calculate_etag(path)).last
35
+ end
36
+
37
+ def verify(path)
38
+ if et = self[path] and File.exist?(path)
39
+ mt = File.mtime(path)
40
+ if mt == et.last
41
+ true
42
+ else
43
+ (self[path] = self.calculate_etag(path)).first
44
+ end
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ def calculate_etag(path)
51
+ digest = Digest::MD5.new
52
+ buffer = ''
53
+ # This seems to be the fastest of several different ways to read a file for generating a digest.
54
+ File.open(path,ReadMode) {|fh| digest << buffer while fh.read(4096,buffer)}
55
+ etag = digest.hexdigest
56
+ unless self[path]
57
+ add_to_verification_queue(path)
58
+ ProxyBag.log(owner_hash).log('info',"Adding ETag #{etag} for #{path} to ETag cache") if ProxyBag.level(owner_hash) > 2
59
+ end
60
+ [etag,File.mtime(path)]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,46 @@
1
+ begin
2
+ load_attempted ||= false
3
+ require 'swiftcore/Swiftiply/cache_base'
4
+ rescue LoadError => e
5
+ if !load_attempted
6
+ load_attempted = true
7
+ begin
8
+ require 'rubygems'
9
+ rescue LoadError
10
+ raise e
11
+ end
12
+ retry
13
+ end
14
+ raise e
15
+ end
16
+
17
+ module Swiftcore
18
+ module Swiftiply
19
+ class FileCache < CacheBase
20
+
21
+ def add(path_info,path,data,etag,mtime,cookie,header)
22
+ unless self[path_info]
23
+ add_to_verification_queue(path_info)
24
+ ProxyBag.log(owner_hash).log('info',"Adding file #{path} to file cache as #{path_info}") if ProxyBag.level(owner_hash) > 2
25
+ end
26
+ self[path_info] = [data,etag,mtime,path,cookie,header]
27
+ end
28
+
29
+ def verify(path_info)
30
+ if f = self[path_info]
31
+ if File.exist?(f[3]) and File.mtime(f[3]) == f[2]
32
+ true
33
+ else
34
+ ProxyBag.log(owner_hash).log('info',"Removing file #{path_info} from file cache") if ProxyBag.level(owner_hash) > 2
35
+ false
36
+ end
37
+ else
38
+ # It was in the verification queue, but not in the file cache.
39
+ false
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,22 @@
1
+ require 'swiftcore/Swiftiply/cache_base_mixin'
2
+
3
+ module Swiftcore
4
+ # Use Array instead of Deque, if Deque wasn't available.
5
+ Deque = Array unless HasDeque or const_defined?(:Deque)
6
+
7
+ module Swiftiply
8
+ class CacheBase < Hash
9
+ include CacheBaseMixin
10
+
11
+ def initialize(vw = 900, time_limit = 0.05, maxsize = nil)
12
+ @vw = vw
13
+ @tl = time_limit
14
+ @wvtl = vw * time_limit
15
+ @old_vql = 0
16
+ @vq = Deque.new
17
+ @maxsize = maxsize #max size is irrelevant for a vanilla hash, but it'll be tracked anyway
18
+ super()
19
+ end
20
+ end
21
+ end
22
+ end