michaelyta-thin 1.2.2

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 (137) hide show
  1. data/CHANGELOG +257 -0
  2. data/COPYING +18 -0
  3. data/README +69 -0
  4. data/Rakefile +13 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +79 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/async_app.ru +126 -0
  11. data/example/async_chat.ru +247 -0
  12. data/example/async_tailer.ru +100 -0
  13. data/example/config.ru +23 -0
  14. data/example/monit_sockets +20 -0
  15. data/example/monit_unixsock +20 -0
  16. data/example/myapp.rb +1 -0
  17. data/example/ramaze.ru +12 -0
  18. data/example/thin.god +80 -0
  19. data/example/thin_solaris_smf.erb +36 -0
  20. data/example/thin_solaris_smf.readme.txt +150 -0
  21. data/example/vlad.rake +64 -0
  22. data/ext/thin_parser/common.rl +55 -0
  23. data/ext/thin_parser/ext_help.h +14 -0
  24. data/ext/thin_parser/extconf.rb +6 -0
  25. data/ext/thin_parser/parser.c +452 -0
  26. data/ext/thin_parser/parser.h +49 -0
  27. data/ext/thin_parser/parser.rl +157 -0
  28. data/ext/thin_parser/thin.c +433 -0
  29. data/lib/rack/adapter/loader.rb +79 -0
  30. data/lib/rack/adapter/rails.rb +175 -0
  31. data/lib/thin.rb +49 -0
  32. data/lib/thin/backends/base.rb +141 -0
  33. data/lib/thin/backends/swiftiply_client.rb +56 -0
  34. data/lib/thin/backends/tcp_server.rb +68 -0
  35. data/lib/thin/backends/unix_server.rb +51 -0
  36. data/lib/thin/command.rb +53 -0
  37. data/lib/thin/connection.rb +214 -0
  38. data/lib/thin/controllers/cluster.rb +127 -0
  39. data/lib/thin/controllers/controller.rb +183 -0
  40. data/lib/thin/controllers/service.rb +75 -0
  41. data/lib/thin/controllers/service.sh.erb +39 -0
  42. data/lib/thin/daemonizing.rb +174 -0
  43. data/lib/thin/headers.rb +39 -0
  44. data/lib/thin/logging.rb +54 -0
  45. data/lib/thin/request.rb +158 -0
  46. data/lib/thin/response.rb +101 -0
  47. data/lib/thin/runner.rb +209 -0
  48. data/lib/thin/server.rb +247 -0
  49. data/lib/thin/stats.html.erb +216 -0
  50. data/lib/thin/stats.rb +52 -0
  51. data/lib/thin/statuses.rb +43 -0
  52. data/lib/thin/version.rb +32 -0
  53. data/spec/backends/swiftiply_client_spec.rb +66 -0
  54. data/spec/backends/tcp_server_spec.rb +33 -0
  55. data/spec/backends/unix_server_spec.rb +37 -0
  56. data/spec/command_spec.rb +25 -0
  57. data/spec/configs/cluster.yml +9 -0
  58. data/spec/configs/single.yml +9 -0
  59. data/spec/connection_spec.rb +105 -0
  60. data/spec/controllers/cluster_spec.rb +235 -0
  61. data/spec/controllers/controller_spec.rb +129 -0
  62. data/spec/controllers/service_spec.rb +50 -0
  63. data/spec/daemonizing_spec.rb +192 -0
  64. data/spec/headers_spec.rb +40 -0
  65. data/spec/logging_spec.rb +46 -0
  66. data/spec/perf/request_perf_spec.rb +50 -0
  67. data/spec/perf/response_perf_spec.rb +19 -0
  68. data/spec/perf/server_perf_spec.rb +39 -0
  69. data/spec/rack/loader_spec.rb +29 -0
  70. data/spec/rack/rails_adapter_spec.rb +106 -0
  71. data/spec/rails_app/app/controllers/application.rb +10 -0
  72. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  73. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  74. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  75. data/spec/rails_app/config/boot.rb +109 -0
  76. data/spec/rails_app/config/environment.rb +64 -0
  77. data/spec/rails_app/config/environments/development.rb +18 -0
  78. data/spec/rails_app/config/environments/production.rb +19 -0
  79. data/spec/rails_app/config/environments/test.rb +22 -0
  80. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  81. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  82. data/spec/rails_app/config/routes.rb +35 -0
  83. data/spec/rails_app/public/404.html +30 -0
  84. data/spec/rails_app/public/422.html +30 -0
  85. data/spec/rails_app/public/500.html +30 -0
  86. data/spec/rails_app/public/dispatch.cgi +10 -0
  87. data/spec/rails_app/public/dispatch.fcgi +24 -0
  88. data/spec/rails_app/public/dispatch.rb +10 -0
  89. data/spec/rails_app/public/favicon.ico +0 -0
  90. data/spec/rails_app/public/images/rails.png +0 -0
  91. data/spec/rails_app/public/index.html +277 -0
  92. data/spec/rails_app/public/javascripts/application.js +2 -0
  93. data/spec/rails_app/public/javascripts/controls.js +963 -0
  94. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  95. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  96. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  97. data/spec/rails_app/public/robots.txt +5 -0
  98. data/spec/rails_app/script/about +3 -0
  99. data/spec/rails_app/script/console +3 -0
  100. data/spec/rails_app/script/destroy +3 -0
  101. data/spec/rails_app/script/generate +3 -0
  102. data/spec/rails_app/script/performance/benchmarker +3 -0
  103. data/spec/rails_app/script/performance/profiler +3 -0
  104. data/spec/rails_app/script/performance/request +3 -0
  105. data/spec/rails_app/script/plugin +3 -0
  106. data/spec/rails_app/script/process/inspector +3 -0
  107. data/spec/rails_app/script/process/reaper +3 -0
  108. data/spec/rails_app/script/process/spawner +3 -0
  109. data/spec/rails_app/script/runner +3 -0
  110. data/spec/rails_app/script/server +3 -0
  111. data/spec/request/mongrel_spec.rb +39 -0
  112. data/spec/request/parser_spec.rb +215 -0
  113. data/spec/request/persistent_spec.rb +35 -0
  114. data/spec/request/processing_spec.rb +45 -0
  115. data/spec/response_spec.rb +91 -0
  116. data/spec/runner_spec.rb +168 -0
  117. data/spec/server/builder_spec.rb +44 -0
  118. data/spec/server/pipelining_spec.rb +110 -0
  119. data/spec/server/robustness_spec.rb +34 -0
  120. data/spec/server/stopping_spec.rb +55 -0
  121. data/spec/server/swiftiply.yml +6 -0
  122. data/spec/server/swiftiply_spec.rb +32 -0
  123. data/spec/server/tcp_spec.rb +57 -0
  124. data/spec/server/threaded_spec.rb +27 -0
  125. data/spec/server/unix_socket_spec.rb +26 -0
  126. data/spec/server_spec.rb +96 -0
  127. data/spec/spec_helper.rb +219 -0
  128. data/tasks/announce.rake +22 -0
  129. data/tasks/deploy.rake +16 -0
  130. data/tasks/email.erb +30 -0
  131. data/tasks/ext.rake +42 -0
  132. data/tasks/gem.rake +108 -0
  133. data/tasks/rdoc.rake +25 -0
  134. data/tasks/site.rake +15 -0
  135. data/tasks/spec.rake +48 -0
  136. data/tasks/stats.rake +28 -0
  137. metadata +254 -0
@@ -0,0 +1,79 @@
1
+ module Rack
2
+ class AdapterNotFound < RuntimeError; end
3
+
4
+ # Mapping used to guess which adapter to use in <tt>Adapter.for</tt>.
5
+ # Framework <name> => <file unique to this framework> in order they will
6
+ # be tested.
7
+ # +nil+ for value to never guess.
8
+ # NOTE: If a framework has a file that is not unique, make sure to place
9
+ # it at the end.
10
+ ADAPTERS = [
11
+ [:rails, 'config/environment.rb'],
12
+ [:ramaze, 'start.rb'],
13
+ [:halcyon, 'runner.ru'],
14
+ [:merb, 'config/init.rb'],
15
+ [:mack, 'config/app_config/default.yml'],
16
+ [:mack, 'config/configatron/default.rb'],
17
+ [:file, nil]
18
+ ]
19
+
20
+ module Adapter
21
+ # Guess which adapter to use based on the directory structure
22
+ # or file content.
23
+ # Returns a symbol representing the name of the adapter to use
24
+ # to load the application under <tt>dir/</tt>.
25
+ def self.guess(dir)
26
+ ADAPTERS.each do |adapter, file|
27
+ return adapter if file && ::File.exist?(::File.join(dir, file))
28
+ end
29
+ raise AdapterNotFound, "No adapter found for #{dir}"
30
+ end
31
+
32
+ # Loads an adapter identified by +name+ using +options+ hash.
33
+ def self.for(name, options={})
34
+ case name.to_sym
35
+ when :rails
36
+ return Rails.new(options.merge(:root => options[:chdir]))
37
+
38
+ when :ramaze
39
+ require "#{options[:chdir]}/start"
40
+
41
+ Ramaze.trait[:essentials].delete Ramaze::Adapter
42
+ Ramaze.start :force => true
43
+
44
+ return Ramaze::Adapter::Base
45
+
46
+ when :merb
47
+ require 'merb-core'
48
+
49
+ Merb::Config.setup(:merb_root => options[:chdir],
50
+ :environment => options[:environment])
51
+ Merb.environment = Merb::Config[:environment]
52
+ Merb.root = Merb::Config[:merb_root]
53
+ Merb::BootLoader.run
54
+
55
+ return Merb::Rack::Application.new
56
+
57
+ when :halcyon
58
+ require 'halcyon'
59
+
60
+ $:.unshift(Halcyon.root/'lib')
61
+
62
+ return Halcyon::Runner.new
63
+
64
+ when :mack
65
+ ENV["MACK_ENV"] = options[:environment]
66
+ load(::File.join(options[:chdir], "Rakefile"))
67
+ require 'mack'
68
+ return Mack::Utils::Server.build_app
69
+
70
+ when :file
71
+ return Rack::File.new(options[:chdir])
72
+
73
+ else
74
+ raise AdapterNotFound, "Adapter not found: #{name}"
75
+
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,175 @@
1
+ require 'cgi'
2
+
3
+ # Adapter to run a Rails app with any supported Rack handler.
4
+ # By default it will try to load the Rails application in the
5
+ # current directory in the development environment.
6
+ #
7
+ # Options:
8
+ # root: Root directory of the Rails app
9
+ # environment: Rails environment to run in (development [default], production or test)
10
+ # prefix: Set the relative URL root.
11
+ #
12
+ # Based on http://fuzed.rubyforge.org/ Rails adapter
13
+ module Rack
14
+ module Adapter
15
+ class Rails
16
+ FILE_METHODS = %w(GET HEAD).freeze
17
+
18
+ def initialize(options={})
19
+ @root = options[:root] || Dir.pwd
20
+ @env = options[:environment] || 'development'
21
+ @prefix = options[:prefix]
22
+
23
+ load_application
24
+
25
+ @rails_app = if ActionController::Dispatcher.instance_methods.include?(:call)
26
+ ActionController::Dispatcher.new
27
+ else
28
+ CgiApp.new
29
+ end
30
+
31
+ @file_app = Rack::File.new(::File.join(RAILS_ROOT, "public"))
32
+ end
33
+
34
+ def load_application
35
+ ENV['RAILS_ENV'] = @env
36
+
37
+ require "#{@root}/config/environment"
38
+ require 'dispatcher'
39
+
40
+ if @prefix
41
+ if ActionController::Base.respond_to?(:relative_url_root=)
42
+ ActionController::Base.relative_url_root = @prefix # Rails 2.1.1
43
+ else
44
+ ActionController::AbstractRequest.relative_url_root = @prefix
45
+ end
46
+ end
47
+ end
48
+
49
+ def file_exist?(path)
50
+ full_path = ::File.join(@file_app.root, Utils.unescape(path))
51
+ ::File.file?(full_path) && ::File.readable_real?(full_path)
52
+ end
53
+
54
+ def call(env)
55
+ path = env['PATH_INFO'].chomp('/')
56
+ method = env['REQUEST_METHOD']
57
+ cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
58
+
59
+ if FILE_METHODS.include?(method)
60
+ if file_exist?(path) # Serve the file if it's there
61
+ return @file_app.call(env)
62
+ elsif file_exist?(cached_path) # Serve the page cache if it's there
63
+ env['PATH_INFO'] = cached_path
64
+ return @file_app.call(env)
65
+ end
66
+ end
67
+
68
+ # No static file, let Rails handle it
69
+ @rails_app.call(env)
70
+ end
71
+
72
+ protected
73
+ # For Rails pre Rack (2.3)
74
+ class CgiApp
75
+ def call(env)
76
+ request = Request.new(env)
77
+ response = Response.new
78
+ session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
79
+ cgi = CGIWrapper.new(request, response)
80
+
81
+ Dispatcher.dispatch(cgi, session_options, response)
82
+
83
+ response.finish
84
+ end
85
+ end
86
+
87
+ class CGIWrapper < ::CGI
88
+ def initialize(request, response, *args)
89
+ @request = request
90
+ @response = response
91
+ @args = *args
92
+ @input = request.body
93
+
94
+ super *args
95
+ end
96
+
97
+ def header(options = "text/html")
98
+ if options.is_a?(String)
99
+ @response['Content-Type'] = options unless @response['Content-Type']
100
+ else
101
+ @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
102
+
103
+ @response['Content-Type'] = options.delete('type') || "text/html"
104
+ @response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
105
+
106
+ @response['Content-Language'] = options.delete('language') if options['language']
107
+ @response['Expires'] = options.delete('expires') if options['expires']
108
+
109
+ @response.status = options.delete('Status') if options['Status']
110
+
111
+ # Convert 'cookie' header to 'Set-Cookie' headers.
112
+ # Because Set-Cookie header can appear more the once in the response body,
113
+ # we store it in a line break seperated string that will be translated to
114
+ # multiple Set-Cookie header by the handler.
115
+ if cookie = options.delete('cookie')
116
+ cookies = []
117
+
118
+ case cookie
119
+ when Array then cookie.each { |c| cookies << c.to_s }
120
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
121
+ else cookies << cookie.to_s
122
+ end
123
+
124
+ @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
125
+
126
+ @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact
127
+ # See http://groups.google.com/group/rack-devel/browse_thread/thread/e8759b91a82c5a10/a8dbd4574fe97d69?#a8dbd4574fe97d69
128
+ if Thin.ruby_18?
129
+ @response['Set-Cookie'].flatten!
130
+ else
131
+ @response['Set-Cookie'] = @response['Set-Cookie'].join("\n")
132
+ end
133
+ end
134
+
135
+ options.each { |k,v| @response[k] = v }
136
+ end
137
+
138
+ ""
139
+ end
140
+
141
+ def params
142
+ @params ||= @request.params
143
+ end
144
+
145
+ def cookies
146
+ @request.cookies
147
+ end
148
+
149
+ def query_string
150
+ @request.query_string
151
+ end
152
+
153
+ # Used to wrap the normal args variable used inside CGI.
154
+ def args
155
+ @args
156
+ end
157
+
158
+ # Used to wrap the normal env_table variable used inside CGI.
159
+ def env_table
160
+ @request.env
161
+ end
162
+
163
+ # Used to wrap the normal stdinput variable used inside CGI.
164
+ def stdinput
165
+ @input
166
+ end
167
+
168
+ def stdoutput
169
+ STDERR.puts "stdoutput should not be used."
170
+ @response.body
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
data/lib/thin.rb ADDED
@@ -0,0 +1,49 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'fileutils'
4
+ require 'timeout'
5
+ require 'stringio'
6
+ require 'time'
7
+ require 'forwardable'
8
+
9
+ require 'rubygems'
10
+ require 'openssl'
11
+ require 'eventmachine'
12
+
13
+ require 'thin/version'
14
+ require 'thin/statuses'
15
+
16
+ module Thin
17
+ autoload :Command, 'thin/command'
18
+ autoload :Connection, 'thin/connection'
19
+ autoload :Daemonizable, 'thin/daemonizing'
20
+ autoload :Logging, 'thin/logging'
21
+ autoload :Headers, 'thin/headers'
22
+ autoload :Request, 'thin/request'
23
+ autoload :Response, 'thin/response'
24
+ autoload :Runner, 'thin/runner'
25
+ autoload :Server, 'thin/server'
26
+ autoload :Stats, 'thin/stats'
27
+
28
+ module Backends
29
+ autoload :Base, 'thin/backends/base'
30
+ autoload :SwiftiplyClient, 'thin/backends/swiftiply_client'
31
+ autoload :TcpServer, 'thin/backends/tcp_server'
32
+ autoload :UnixServer, 'thin/backends/unix_server'
33
+ end
34
+
35
+ module Controllers
36
+ autoload :Cluster, 'thin/controllers/cluster'
37
+ autoload :Controller, 'thin/controllers/controller'
38
+ autoload :Service, 'thin/controllers/service'
39
+ end
40
+ end
41
+
42
+ require 'rack'
43
+ require 'rack/adapter/loader'
44
+
45
+ module Rack
46
+ module Adapter
47
+ autoload :Rails, 'rack/adapter/rails'
48
+ end
49
+ end
@@ -0,0 +1,141 @@
1
+ module Thin
2
+ module Backends
3
+ # A Backend connects the server to the client. It handles:
4
+ # * connection/disconnection to the server
5
+ # * initialization of the connections
6
+ # * manitoring of the active connections.
7
+ #
8
+ # == Implementing your own backend
9
+ # You can create your own minimal backend by inheriting this class and
10
+ # defining the +connect+ and +disconnect+ method.
11
+ # If your backend is not based on EventMachine you also need to redefine
12
+ # the +start+, +stop+, <tt>stop!</tt> and +config+ methods.
13
+ class Base
14
+ # Server serving the connections throught the backend
15
+ attr_accessor :server
16
+
17
+ # Maximum time for incoming data to arrive
18
+ attr_accessor :timeout
19
+
20
+ # Maximum number of file or socket descriptors that the server may open.
21
+ attr_accessor :maximum_connections
22
+
23
+ # Maximum number of connections that can be persistent
24
+ attr_accessor :maximum_persistent_connections
25
+
26
+ # Allow using threads in the backend.
27
+ attr_writer :threaded
28
+ def threaded?; @threaded end
29
+
30
+ # Number of persistent connections currently opened
31
+ attr_accessor :persistent_connection_count
32
+
33
+ # Disable the use of epoll under Linux
34
+ attr_accessor :no_epoll
35
+
36
+ def initialize
37
+ @connections = []
38
+ @timeout = Server::DEFAULT_TIMEOUT
39
+ @persistent_connection_count = 0
40
+ @maximum_connections = Server::DEFAULT_MAXIMUM_CONNECTIONS
41
+ @maximum_persistent_connections = Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
42
+ @no_epoll = false
43
+ end
44
+
45
+ # Start the backend and connect it.
46
+ def start
47
+ @stopping = false
48
+ starter = proc do
49
+ connect
50
+ @running = true
51
+ end
52
+
53
+ # Allow for early run up of eventmachine.
54
+ if EventMachine.reactor_running?
55
+ starter.call
56
+ else
57
+ EventMachine.run(&starter)
58
+ end
59
+ end
60
+
61
+ # Stop of the backend from accepting new connections.
62
+ def stop
63
+ @running = false
64
+ @stopping = true
65
+
66
+ # Do not accept anymore connection
67
+ disconnect
68
+ stop! if @connections.empty?
69
+ end
70
+
71
+ # Force stop of the backend NOW, too bad for the current connections.
72
+ def stop!
73
+ @running = false
74
+ @stopping = false
75
+
76
+ EventMachine.stop if EventMachine.reactor_running?
77
+ @connections.each { |connection| connection.close_connection }
78
+ close
79
+ end
80
+
81
+ # Configure the backend. This method will be called before droping superuser privileges,
82
+ # so you can do crazy stuff that require godlike powers here.
83
+ def config
84
+ # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
85
+ EventMachine.epoll unless @no_epoll
86
+
87
+ # Set the maximum number of socket descriptors that the server may open.
88
+ # The process needs to have required privilege to set it higher the 1024 on
89
+ # some systems.
90
+ @maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
91
+ end
92
+
93
+ # Free up resources used by the backend.
94
+ def close
95
+ end
96
+
97
+ # Returns +true+ if the backend is connected and running.
98
+ def running?
99
+ @running
100
+ end
101
+
102
+ # Called by a connection when it's unbinded.
103
+ def connection_finished(connection)
104
+ @persistent_connection_count -= 1 if connection.can_persist?
105
+ @connections.delete(connection)
106
+
107
+ # Finalize gracefull stop if there's no more active connection.
108
+ stop! if @stopping && @connections.empty?
109
+ end
110
+
111
+ # Returns +true+ if no active connection.
112
+ def empty?
113
+ @connections.empty?
114
+ end
115
+
116
+ # Number of active connections.
117
+ def size
118
+ @connections.size
119
+ end
120
+
121
+ protected
122
+ # Initialize a new connection to a client.
123
+ def initialize_connection(connection)
124
+ connection.backend = self
125
+ connection.app = @server.app
126
+ connection.comm_inactivity_timeout = @timeout
127
+ connection.threaded = @threaded
128
+
129
+ # We control the number of persistent connections by keeping
130
+ # a count of the total one allowed yet.
131
+ if @persistent_connection_count < @maximum_persistent_connections
132
+ connection.can_persist!
133
+ @persistent_connection_count += 1
134
+ end
135
+
136
+ @connections << connection
137
+ end
138
+
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,56 @@
1
+ module Thin
2
+ module Backends
3
+ # Backend to act as a Swiftiply client (http://swiftiply.swiftcore.org).
4
+ class SwiftiplyClient < Base
5
+ attr_accessor :key
6
+
7
+ attr_accessor :host, :port
8
+
9
+ def initialize(host, port, options={})
10
+ @host = host
11
+ @port = port.to_i
12
+ @key = options[:swiftiply].to_s
13
+ super()
14
+ end
15
+
16
+ # Connect the server
17
+ def connect
18
+ EventMachine.connect(@host, @port, SwiftiplyConnection, &method(:initialize_connection))
19
+ end
20
+
21
+ # Stops the server
22
+ def disconnect
23
+ EventMachine.stop
24
+ end
25
+
26
+ def to_s
27
+ "#{@host}:#{@port} swiftiply"
28
+ end
29
+ end
30
+ end
31
+
32
+ class SwiftiplyConnection < Connection
33
+ def connection_completed
34
+ send_data swiftiply_handshake(@backend.key)
35
+ end
36
+
37
+ def persistent?
38
+ true
39
+ end
40
+
41
+ def unbind
42
+ super
43
+ EventMachine.add_timer(rand(2)) { reconnect(@backend.host, @backend.port) } if @backend.running?
44
+ end
45
+
46
+ protected
47
+ def swiftiply_handshake(key)
48
+ 'swiftclient' << host_ip.collect { |x| sprintf('%02x', x.to_i)}.join << sprintf('%04x', @backend.port) << sprintf('%02x', key.length) << key
49
+ end
50
+
51
+ # For some reason Swiftiply request the current host
52
+ def host_ip
53
+ Socket.gethostbyname(@backend.host)[3].unpack('CCCC') rescue [0,0,0,0]
54
+ end
55
+ end
56
+ end