aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Graaff <hans@degraaff.org>2012-10-28 16:35:06 +0100
committerHans de Graaff <hans@degraaff.org>2012-10-28 16:35:06 +0100
commit24ec62ba8791fb4b6f9aa648440010a0f9fd1f52 (patch)
tree07569960da29bc712443bb78fd6a12cfdec5df38
parentFix a few compilation warnings. (diff)
downloadgorg-rack.tar.gz
gorg-rack.tar.bz2
gorg-rack.zip
Initial rudimentary Rack-based handler.rack
-rw-r--r--config.ru17
-rw-r--r--lib/gorg/application.rb169
-rw-r--r--spec/gorg/application_spec.rb27
3 files changed, 213 insertions, 0 deletions
diff --git a/config.ru b/config.ru
new file mode 100644
index 0000000..144a7dc
--- /dev/null
+++ b/config.ru
@@ -0,0 +1,17 @@
+require 'gorg/base'
+require 'gorg/application'
+
+# TODO: refactor Gorg init and config code to avoid a nasty class
+# construction like this.
+class Fake
+ extend Gorg
+ gorgInit
+end
+
+map '/images' do
+ mounts = $Config['mounts']
+ mount_options = Hash[*mounts.flatten]
+ run Rack::File.new(mount_options['/images'])
+end
+
+run Gorg::Application.new
diff --git a/lib/gorg/application.rb b/lib/gorg/application.rb
new file mode 100644
index 0000000..663618e
--- /dev/null
+++ b/lib/gorg/application.rb
@@ -0,0 +1,169 @@
+
+# Rack Application class
+
+require 'gorg/cgi'
+
+module Gorg
+
+ class Application
+ # TODO: should be a class instance
+ include Gorg
+
+ def call(environment)
+ request = Rack::Request.new(environment)
+ response = Rack::Response.new
+
+ hit = "#{$Config["root"]}#{request.path}"
+ cacheName = request.path
+ if FileTest.directory?(hit) and FileTest.exist?(hit+"/index.xml") then
+ # Use $URI/index.xml for directories that have an index.xml file
+ hit << "/index.xml"
+ cacheName << "/index.xml"
+ end
+ hit.squeeze!('/')
+ cacheName.squeeze!('/')
+ if FileTest.directory?(hit) then
+ return Rack::Directory.new($Config['root']).call(environment)
+ else
+ if hit !~ /\.(xml)|(rdf)|(rss)$/ then
+ puts "Try to server a file for #{hit}"
+ return Rack::File.new($Config['root']).call(environment)
+ else
+ if not FileTest.exist?(hit) then
+ return Rack::File.new($Config['root']).call(environment)
+ else
+ # Parse If-None-Match and If-Modified-Since request header fields if any
+ ims=inm=nil
+ begin
+ ims = Time.parse(req['if-modified-since']) if req['if-modified-since']
+ inm = split_header_etags(req['if-none-match']) if req['if-none-match']
+ rescue
+ # Just ignore ill-formated data
+ nil
+ end
+ begin
+ response['Charset'] = 'UTF-8'
+ # Process xml file or return xml file if passthru=1
+ if $Config['passthru'] && request.params && request.params["passthru"] && request.params["passthru"] != "0" then
+ # passthru allowed by config and requested by visitor, return file as text/plain
+ mstat = File.stat(hit)
+ raise Gorg::Status::NotModified.new(mstat) if notModified?(mstat, inm, ims)
+ debug("Passthru granted for #{hit}")
+ body = IO.read(hit)
+ # If client accepts gzip encoding and we support it, return gzipped file
+ if $Config["zipLevel"] > 0 and (req.accept_encoding.include?("gzip") or req.accept_encoding.include?("x-gzip")) then
+ res.body = gzip(body, $Config["zipLevel"])
+ response['Content-Encoding'] = "gzip"
+ response['Vary'] = "Accept-Encoding"
+ else
+ res.body = body
+ end
+ response['Content-Type'] = 'text/plain'
+ else
+ query_params = request.params.dup
+ # Get cookies and add them to the parameters
+ if $Config["acceptCookies"] then
+ # We need CGI:Cookie objects to be compatible with our cgi modules (stupid WEBrick)
+ puts "PENDING: raw header access for Cookie"
+# ck = req.raw_header.find{|l| l =~ /^cookie: /i}
+ ck = false
+ if ck then
+ query_params.merge!(cookies_to_params(CGI::Cookie.parse($'.strip)))
+ debug "query params are " + query_params.inspect
+ end
+ end
+ if $Config["httphost"] then
+ # Add HTTP_HOST to stylesheet params
+ query_params["httphost"] = if $Config["httphost"][0] == '*' then
+ request.host||""
+ elsif $Config["httphost"].include?('*') then
+ $Config["httphost"][0]
+ elsif $Config["httphost"].include?(request.host) then
+ $Config["httphost"][0]
+ else
+ request.host||""
+ end
+ end
+
+ bodyZ = nil
+ body, mstat, extrameta = Gorg::Cache.hit(cacheName, query_params, inm, ims)
+ if body.nil? then
+ xml_query = query_params.dup
+ if $Config["linkParam"] then
+ xml_query[$Config["linkParam"]] = request.path
+ end
+ # Cache miss, process file and cache result
+ puts xml_query.inspect
+ err, body, filelist, extrameta = xproc(hit, xml_query, true)
+ puts err.inspect, extrameta.inspect
+ warn("#{err.collect{|e|e.join(':')}.join('; ')}") if err["xmlErrLevel"] == 1
+ error("#{err.collect{|e|e.join(':')}.join('; ')}") if err["xmlErrLevel"] > 1
+ # Display error message if any, just like the cgi/fcgi versions
+ raise ("#{err.collect{|e|e.join(':')}.join('<br/>')}") if err["xmlErrLevel"] > 0
+ # Cache output
+ mstat, bodyZ = Gorg::Cache.store(body, cacheName, query_params, filelist, extrameta)
+ else
+ if $Config["zipLevel"] > 0 then
+ bodyZ = body
+ body = nil
+ end
+ end
+ # If client accepts gzip encoding and we support it, return gzipped file
+ if bodyZ and $Config["zipLevel"] > 0 and (request.accept_encoding.include?("gzip") or request.accept_encoding.include?("x-gzip")) then
+ res.body = bodyZ
+ response['Content-Encoding'] = "gzip"
+ response['Vary'] = "Accept-Encoding"
+ else
+ if body then
+ response.write(body)
+ else
+ # We need to unzip bodyZ into body, i.e. we cached zipped data but client does not support gzip
+ response.write(gunzip(bodyZ))
+ end
+ end
+ # Add cookies to http header
+ cookies = makeCookies(extrameta)
+ if cookies then
+ cookies.each{|c| res.cookies << c.to_s}
+ end
+ # Add Content-Type to header based on actual content.
+ ct = setContentType(body)
+ if ct then
+ # Turn application/xhtml+xml into text/html if browser does not accept it
+ if request['HTTP_ACCEPT'].to_s !~ /application\/xhtml\+xml/ and ct =~ /application\/xhtml\+xml(.*)$/ then
+ response['Content-Type'] = "text/html#{$1}"
+ else
+ response['Content-Type'] = ct
+ end
+ else
+ response['Content-Type'] = 'text/plain'
+ end
+ end
+ if mstat then
+ response['ETag'] = makeETag(mstat)
+ response['Last-Modified'] = mstat.mtime.httpdate
+ end
+ rescue => ex
+ if ex.respond_to?(:errCode) then
+ # One of ours (Gorg::Status::HTTPStatus)
+ res.body = ex.html
+ res.status = ex.errCode
+ ex.header.each {|k,v| response[k]=v unless k =~ /status|cookie/i}
+ else
+ raise
+ # Some ruby exceptions occurred, make it a syserr
+ syserr = Gorg::Status::SysError.new
+ response.write(syserr.html(ex))
+ response.status = syserr.errCode
+ end
+ end
+ end
+ end
+ end
+
+ response.finish
+ end
+
+ end
+
+end
diff --git a/spec/gorg/application_spec.rb b/spec/gorg/application_spec.rb
new file mode 100644
index 0000000..91a26fa
--- /dev/null
+++ b/spec/gorg/application_spec.rb
@@ -0,0 +1,27 @@
+
+
+require 'spec_helper'
+require 'gorg/application'
+require 'rack'
+
+describe Gorg::Application do
+ subject { Gorg::Application.new }
+
+ let(:environment) { mock('environment') }
+
+ before do
+ $Config = {'root' => 'root path'}
+ end
+
+ it { should_not be_nil }
+
+ pending "More fine-grained code that can be tested piece by piece" do
+ specify { subject.call(environment).should be_a_kind_of Array }
+ specify { subject.call(environment).size.should == 3 }
+
+ specify { subject.call(environment)[0].should be_a_kind_of Integer }
+ specify { subject.call(environment)[1].should be_a_kind_of Hash }
+ specify { subject.call(environment)[2].should be_a_kind_of Array }
+ end
+
+end