All pastes #97365 Raw Edit

tango_

public text v1 · immutable
#97365 ·published 2006-07-23 17:17 UTC
rendered paste body
Index: lib/rbot/httputil.rb
===================================================================
--- lib/rbot/httputil.rb	(revision 247)
+++ lib/rbot/httputil.rb	(working copy)
@@ -29,9 +29,16 @@
     BotConfig.register BotConfigIntegerValue.new('http.max_redir',
       :default => 5,
       :desc => "Maximum number of redirections to be used when getting a document")
+    BotConfig.register BotConfigIntegerValue.new('http.expire_time',
+      :default => 60,
+      :desc => "After how many minutes since last use a cached document is considered to be expired")
+    BotConfig.register BotConfigIntegerValue.new('http.max_cache_time',
+      :default => 60*24,
+      :desc => "After how many minutes since first use a cached document is considered to be expired")
 
   def initialize(bot)
     @bot = bot
+    @cache = Hash.new
     @headers = {
       'User-Agent' => "rbot http util #{$version} (http://linuxbrit.co.uk/rbot/)",
     }
@@ -120,9 +127,10 @@
   # readtimeout:: timeout for reading the response
   # opentimeout:: timeout for opening the connection
   #
-  # simple get request, returns response body if the status code is 200 and
-  # the request doesn't timeout.
-  def get(uri, readtimeout=10, opentimeout=5, redirs=0)
+  # simple get request, returns (if possible) response body following redirs
+  # and caching if requested
+  # it yields the urls it gets redirected to, for future uses
+  def get(uri, readtimeout=10, opentimeout=5, max_redir=@bot.config["http.max_redir"], cache=false)
     proxy = get_proxy(uri)
     proxy.open_timeout = opentimeout
     proxy.read_timeout = readtimeout
@@ -130,16 +138,31 @@
     begin
       proxy.start() {|http|
         resp = http.get(uri.request_uri(), @headers)
-        case resp.code
-        when "200"
+        case resp
+        when Net::HTTPSuccess
+          if cache
+            k = uri.to_s
+            @cache[k] = Hash.new
+            @cache[k][:body] = body
+            @cache[k][:last_mod] = Time.httpdate(resp['last-modified']) if resp.key?('last-modified')
+            if resp.key?('date')
+              @cache[k][:first_use] = Time.httpdate(resp['date'])
+              @cache[k][:last_use] = Time.httpdate(resp['date'])
+            else
+              now = Time.new
+              @cache[k][:first_use] = now
+              @cache[k][:last_use] = now
+            end
+            @cache[k][:count] = 1
+          end
           return resp.body
-        when "302"
+        when Net::HTTPRedirection
           debug "Redirecting #{uri} to #{resp['location']}"
-          if redirs < @bot.config["http.max_redir"]
-            return get( URI.parse(resp['location']), readtimeout, opentimeout, redirs+1 )
+          yield resp['location']
+          if max_redir > 0
+            return get( URI.parse(resp['location']), readtimeout, opentimeout, max_redir-1, cache)
           else
             warning "Max redirection reached, not going to #{resp['location']}"
-            return nil
           end
         else
           debug "HttpUtil.get return code #{resp.code} #{resp.body}"
@@ -151,6 +174,88 @@
     end
     return nil
   end
+
+  # just like the above, but only gets the head
+  def head(uri, readtimeout=10, opentimeout=5, max_redir=@bot.config["http.max_redir"])
+    proxy = get_proxy(uri)
+    proxy.open_timeout = opentimeout
+    proxy.read_timeout = readtimeout
+
+    begin
+      proxy.start() {|http|
+        resp = http.head(uri.request_uri(), @headers)
+        case resp
+        when Net::HTTPSuccess
+          return resp
+        when Net::HTTPRedirection
+          debug "Redirecting #{uri} to #{resp['location']}"
+          yield resp['location']
+          if max_redir > 0
+            return head( URI.parse(resp['location']), readtimeout, opentimeout, max_redir-1)
+          else
+            warning "Max redirection reached, not going to #{resp['location']}"
+          end
+        else
+          debug "HttpUtil.head return code #{resp.code}"
+        end
+        return nil
+      }
+    rescue StandardError, Timeout::Error => e
+      error "HttpUtil.head exception: #{e.inspect}, while trying to get #{uri}"
+    end
+    return nil
+  end
+
+  # gets a page from the cache if it's still (assumed to be) valid
+  # TODO remove stale cached pages, except when called with noexpire=true
+  def get_cached(uri, readtimeout=10, opentimeout=5, max_redir=@bot.config['http.max_redir'], noexpire=false)
+    k = uri.to_s
+    return get(uri, readtimeout, opentimeout, max_redir, true) if !@cache.key?(k)
+    now = Time.new
+    begin
+      h = head(uri, readtimeout, opentimeout, max_redir)
+      # See if the last-modified header can be used
+      # Assumption: the page was not modified if both the header
+      # and the cached copy have the last-modified value, and it's the same time
+      # If only one of the cached copy and the header have the value, or if the
+      # value is different, we assume that the cached copyis invalid and therefore
+      # get a new one
+      if h.key?('last-modified')
+        if @cache[k].key?(:last_mod)
+          if Time.httpdate(h['last-modified']) == @cache[k][:last_mod]
+            if resp.key?('date')
+              @cache[k][:last_use] = Time.httpdate(resp['date'])
+            else
+              @cache[k][:last_use] = now
+            end
+            @cache[k][:count] += 1
+            return @cache[k][:body]
+          end
+          return get(uri, readtimeout, opentimeout, max_redir, true)
+        end
+        return get(uri, readtimeout, opentimeout, max_redir, true)
+      else
+        return get(uri, readtimeout, opentimeout, max_redir, true) if @cache[k].key?(:last_mod)
+      end
+    rescue => e
+      warning "Error #{e.inspect} getting the page #{uri}, using cache"
+      return @cache[k][:body]
+    end
+    # If we still haven't returned, we are dealing with a non-redirected document
+    # that doesn't have the last-modified attribute
+    debug "Could not use last-modified attribute for URL #{uri}, guessing cache validity"
+    if noexpire or (
+      (now - @cache[k][:last_time] < @bot.config['http.expire_time']*60) &&
+      (now - @cache[k][:first_time] < @bot.config['http.max_cache_time']*60)
+      )
+      @cache[k][:count] += 1
+      @cache[k][:last_time] = now
+      return @cache[k][:body]
+    end
+    @cache.delete(k)
+    return get(uri, readtimeout, opentimeout, max_redir, true)
+  end
+
 end
 end
 end
Index: data/rbot/plugins/urban.rb
===================================================================
--- data/rbot/plugins/urban.rb	(revision 249)
+++ data/rbot/plugins/urban.rb	(working copy)
@@ -37,7 +37,7 @@
       uri = URI.parse( "http://www.urbandictionary.com/define.php?term=#{ URI.escape query}" )
     end
 
-    soup = BeautifulSoup.new( @bot.httputil.get( uri ) )
+    soup = BeautifulSoup.new( @bot.httputil.get_cached( uri ) )
     if titleNavi = soup.find_all( 'td', :attrs => { 'class' => 'def_word' } )[0] then
       title = titleNavi.contents
       results = soup.find_all( 'div', :attrs => { 'class' => 'def_p' } )