incubator-deltacloud-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mfoj...@redhat.com
Subject [PATCH core] Added ETag HTTP header to give client opportunity to check if content was changed. Added X-Runtime and X-Backend-Runtime headers for benchmarking.
Date Fri, 07 Jan 2011 13:24:31 GMT
From: Michal Fojtik <mfojtik@redhat.com>

---
 client/lib/deltacloud.rb                           |   17 +++++-
 .../lib/deltacloud/helpers/application_helper.rb   |   11 +++-
 server/lib/sinatra/rack_driver_select.rb           |   48 ++++++++--------
 server/lib/sinatra/rack_etag.rb                    |   60 ++++++++++++++++++++
 server/lib/sinatra/rack_runtime.rb                 |   28 +++++++++
 server/server.rb                                   |    6 ++-
 6 files changed, 141 insertions(+), 29 deletions(-)
 create mode 100644 server/lib/sinatra/rack_etag.rb
 create mode 100644 server/lib/sinatra/rack_runtime.rb

diff --git a/client/lib/deltacloud.rb b/client/lib/deltacloud.rb
index 6985d5c..e8d6375 100644
--- a/client/lib/deltacloud.rb
+++ b/client/lib/deltacloud.rb
@@ -34,9 +34,15 @@ module DeltaCloud
   # @param [String, password] API password
   # @param [String, user_name] API URL (eg. http://localhost:3001/api)
   # @return [DeltaCloud::API]
-  def self.new(user_name, password, api_url, opts={}, &block)
-    opts ||= {}
-    API.new(user_name, password, api_url, opts, &block)
+  #def self.new(user_name, password, api_url, opts={}, &block)
+  #  opts ||= {}
+  #  API.new(user_name, password, api_url, opts, &block)
+  #end
+
+  def self.new(opts={}, &block)
+    opts ||={}
+    client = API.new(opts[:username], opts[:password], opts[:url], opts, &block)
+    client.use_driver(opts[:driver], opts) if opts[:driver]
   end
 
   # Check given credentials if their are valid against
@@ -256,6 +262,11 @@ module DeltaCloud
       return self
     end
 
+    def use_config!(opts={})
+      @api_uri = URI.parse(opts[:url]) if opts[:url]
+      use_driver(opts[:driver], opts)
+    end
+
     def extended_headers
       headers = {}
       headers["X-Deltacloud-Driver"] = @api_driver.to_s if @api_driver
diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
index a734387..b018876 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -17,6 +17,8 @@
 
 # Methods added to this helper will be available to all templates in the application.
 
+require 'benchmark'
+
 module ApplicationHelper
 
   def bread_crumb
@@ -74,7 +76,10 @@ module ApplicationHelper
       filter.merge!(:state => params[:state]) if params[:state]
       filter = nil if filter.keys.size.eql?(0)
       singular = model.to_s.singularize.to_sym
-      @elements = driver.send(model.to_sym, credentials, filter)
+      @benchmark = Benchmark.measure do
+        @elements = driver.send(model.to_sym, credentials, filter)
+      end
+      headers['X-Backend-Runtime'] = @benchmark.real.to_s
       instance_variable_set(:"@#{model}", @elements)
       respond_to do |format|
         format.html { haml :"#{model}/index" }
@@ -84,7 +89,9 @@ module ApplicationHelper
   end
 
   def show(model)
-    @element = driver.send(model, credentials, { :id => params[:id]} )
+    @benchmark = Benchmark.measure do
+      @element = driver.send(model, credentials, { :id => params[:id]} )
+    end
     instance_variable_set("@#{model}", @element)
     if @element
       respond_to do |format|
diff --git a/server/lib/sinatra/rack_driver_select.rb b/server/lib/sinatra/rack_driver_select.rb
index 3e7d038..7b43d81 100644
--- a/server/lib/sinatra/rack_driver_select.rb
+++ b/server/lib/sinatra/rack_driver_select.rb
@@ -1,29 +1,31 @@
-class RackDriverSelect
+module Rack
+  class DriverSelect
 
-  def initialize(app, opts={})
-    @app = app
-    @opts = opts
-  end
+    def initialize(app, opts={})
+      @app = app
+      @opts = opts
+    end
 
-  HEADER_TO_ENV_MAP = {
-    'HTTP_X_DELTACLOUD_DRIVER' => :driver,
-    'HTTP_X_DELTACLOUD_PROVIDER' => :provider
-  }
-  
-  def call(env)
-    original_settings = { }
-    HEADER_TO_ENV_MAP.each do |header, name|
-      original_settings[name] = Thread.current[name]
-      new_setting = extract_header(env, header)
-      Thread.current[name] = new_setting if new_setting
+    HEADER_TO_ENV_MAP = {
+      'HTTP_X_DELTACLOUD_DRIVER' => :driver,
+      'HTTP_X_DELTACLOUD_PROVIDER' => :provider
+    }
+    
+    def call(env)
+      original_settings = { }
+      HEADER_TO_ENV_MAP.each do |header, name|
+        original_settings[name] = Thread.current[name]
+        new_setting = extract_header(env, header)
+        Thread.current[name] = new_setting if new_setting
+      end
+      @app.call(env)
+    ensure
+      original_settings.each { |name, value| Thread.current[name] = value }
     end
-    @app.call(env)
-  ensure
-    original_settings.each { |name, value| Thread.current[name] = value }
-  end
 
-  def extract_header(env, header)
-    env[header].downcase if env[header]
-  end
+    def extract_header(env, header)
+      env[header].downcase if env[header]
+    end
 
+  end
 end
diff --git a/server/lib/sinatra/rack_etag.rb b/server/lib/sinatra/rack_etag.rb
new file mode 100644
index 0000000..fc5cf0a
--- /dev/null
+++ b/server/lib/sinatra/rack_etag.rb
@@ -0,0 +1,60 @@
+require 'digest/md5'
+
+module Rack
+  # Automatically sets the ETag header on all String bodies.
+  #
+  # The ETag header is skipped if ETag or Last-Modified headers are sent or if
+  # a sendfile body (body.responds_to :to_path) is given (since such cases
+  # should be handled by apache/nginx).
+  #
+  # On initialization, you can pass two parameters: a Cache-Control directive
+  # used when Etag is absent and a directive when it is present. The first
+  # defaults to nil, while the second defaults to "max-age=0, privaute, must-revalidate"
+  class ETag
+    DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
+
+    def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL)
+      @app = app
+      @cache_control = cache_control
+      @no_cache_control = no_cache_control
+    end
+
+    def call(env)
+      status, headers, body = @app.call(env)
+
+      if etag_status?(status) && etag_body?(body) && !http_caching?(headers)
+        digest, body = digest_body(body)
+        headers['ETag'] = %("#{digest}") if digest
+      end
+
+      unless headers['Cache-Control']
+        headers['Cache-Control'] = digest ? @cache_control : @no_cache_control
+      end
+
+      [status, headers, body]
+    end
+
+    private
+
+      def etag_status?(status)
+        status == 200 || status == 201
+      end
+
+      def etag_body?(body)
+        !body.respond_to?(:to_path)
+      end
+
+      def http_caching?(headers)
+        headers.key?('ETag') || headers.key?('Last-Modified')
+      end
+
+      def digest_body(body)
+        parts = []
+        body.each { |part| parts << part }
+        string_body = parts.join
+        digest = Digest::MD5.hexdigest(string_body) unless string_body.empty?
+        [digest, parts]
+      end
+  end
+end
+
diff --git a/server/lib/sinatra/rack_runtime.rb b/server/lib/sinatra/rack_runtime.rb
new file mode 100644
index 0000000..5da3940
--- /dev/null
+++ b/server/lib/sinatra/rack_runtime.rb
@@ -0,0 +1,28 @@
+module Rack
+  # Sets an "X-Runtime" response header, indicating the response
+  # time of the request, in seconds
+  #
+  # You can put it right before the application to see the processing
+  # time, or before all the other middlewares to include time for them,
+  # too.
+  class Runtime
+    def initialize(app, name = nil)
+      @app = app
+      @header_name = "X-Runtime"
+      @header_name << "-#{name}" if name
+    end
+
+    def call(env)
+      start_time = Time.now
+      status, headers, body = @app.call(env)
+      request_time = Time.now - start_time
+
+      if !headers.has_key?(@header_name)
+        headers[@header_name] = "%0.6f" % request_time
+      end
+      
+      [status, headers, body]
+    end
+  end
+end
+
diff --git a/server/server.rb b/server/server.rb
index 8c3b72c..097ff0f 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -28,11 +28,15 @@ require 'haml'
 require 'open3'
 require 'lib/deltacloud/helpers/blob_stream'
 require 'sinatra/rack_driver_select'
+require 'sinatra/rack_runtime'
+require 'sinatra/rack_etag'
 
 set :version, '0.1.0'
 
 
-use RackDriverSelect
+use Rack::DriverSelect
+use Rack::ETag
+use Rack::Runtime
 
 configure do
   set :raise_errors => false
-- 
1.7.3.4


Mime
View raw message