incubator-deltacloud-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Michal Fojtik <mfoj...@redhat.com>
Subject Re: [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 Wed, 26 Jan 2011 14:40:49 GMT
On 07/01/11 14:24 +0100, mfojtik@redhat.com wrote:

Pushing this to master.

   -- Michal

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

-- 
--------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org
--------------------------------------------------------

Mime
View raw message