## RSence
# Copyright 2007 Riassence Inc.
# http://riassence.com/
#
# You should have received a copy of the GNU General Public License along
# with this software package. If not, contact licensing@riassence.com
##
module ClientPkgServe
def broker_urls
::RSence.config[:broker_urls]
end
def match( uri, request_type )
uri.match( /^#{broker_urls[:h]}/ )
end
# Helper method to return the time formatted according to the HTTP RFC
def httime(time)
return time.gmtime.strftime('%a, %d %b %Y %H:%M:%S %Z')
end
def build_busy_wait
while @build_busy
puts "-- build not finished, waiting.. --"
sleep 0.4
end
end
def set_headers( response )
# Sets the response date header to the current time:
response['Date'] = httime( Time.now )
# Controls caching with headers based on the configuration
if ::RSence.config[:cache_maximize]
response['Expires'] = httime(Time.now+::RSence.config[:cache_expire])
else
response['Cache-Control'] = 'no-cache'
end
end
def check_ua( request )
support_gzip = (request.header.has_key?('Accept-Encoding') and request.header['Accept-Encoding'].include?('gzip'))
support_gzip = false if ::RSence.config[:no_gzip]
if request.header.has_key?('User-Agent')
ua = request.header['User-Agent']
is_symbian = ua.include?("SymbianOS")
is_safari = ((not is_symbian) and ua.include?("WebKit"))
is_msie = ((not ua.include?("Opera")) and ua.include?("MSIE"))
is_msie6 = ((not ua.include?("Opera")) and ua.include?("MSIE 6.0"))
end
if is_safari
version = ua.split( 'WebKit/' )[1].split('.')[0].to_i
else
version = 0 # not used for others
end
return {
:symbian => is_symbian,
:safari => is_safari,
:msie => is_msie,
:msie6 => is_msie6,
:version => version
}
end
def support_gzip?( ua )
doesnt_support = ( ua[:msie6] or ua[:symbian] or (ua[:safari] and ua[:version] < 533) )
return ( not doesnt_support )
end
def serve_htc( request, response, request_path )
req_file = request_path[3]
# this file is a part of iefix, it injects calls to
# the ie rendering engine to override stupid behavior
# when changing element properties
if request_path.include?('ie_css_element.htc')
response.status = 200
response['Content-Type'] = 'text/x-component'
## Usually, we don't need it, because the client framework does calls when needed
response.body = %{}
## Enable it to call iefix automatically whenever element properties are changed
#response.body = %{\r\n\r\n}
# this file is a part of iefix, it injects calls to
# the ie rendering engine to override stupid behavior
# when changing style properties
elsif request_path.include?('ie_css_style.htc')
response.status = 200
response['Content-Type'] = 'text/x-component'
## Usually, we don't need it, because the client framework does calls when needed
response.body = %{}
## Enable it to call iefix automatically whenever element properties are changed
#response.body = %{\r\n\r\n}
# Other htc requests are invalid
else
response.status = 503
response.body = '503 - Invalid Request'
end
end
def serve_js( request, response, request_path, ua )
# the file-specific identifier ('core', 'basic' etc)
req_file = request_path[3][0..-4]
if not @client_cache.js_cache.has_key?( req_file )
response.status = 404
response.body = '/* 404 - Not Found */'
else
response.status = 200
response['Content-Type'] = 'text/javascript; charset=utf-8'
# these browsers have issues with gzipped js content
support_gzip = support_gzip?( ua )
if support_gzip
#response['Transfer-Encoding'] = 'chunked,gzip'
response['Last-Modified'] = @client_cache.last_modified
body = @client_cache.gz_cache[ req_file ]+"\r\n\r\n"
response['Content-Length'] = body.bytesize.to_s
response['Content-Encoding'] = 'gzip'
response.body = body
else
response['Last-Modified'] = @client_cache.last_modified
body = @client_cache.js_cache[ req_file ]
response['Content-Length'] = body.bytesize.to_s
response.body = body
end
end
end
def serve_theme( request, response, request_path, ua )
# Get the name of the theme
theme_name = request_path[3]
# Get the theme resource type (html/css/gfx)
theme_part = request_path[4].to_sym
# Get the theme resource identifier
req_file = request_path[5]
# checks for theme name
has_theme = @client_cache.theme_cache.has_key?( theme_name )
# checks for theme part (css/html/gfx)
has_theme_part = ( has_theme and @client_cache.theme_cache[theme_name].has_key?( theme_part ) )
# checks for theme file
has_theme_file = ( has_theme_part and @client_cache.theme_cache[theme_name][theme_part].has_key?( req_file ) )
if not has_theme_file and req_file == 'common.css'
response.status = 200
response['Content-Type'] = 'text/css'
response.body = ''
end
if not has_theme
response.status = 404
response.body = '404 - Theme Not Found'
puts "Theme #{theme_name} not found, avail: #{@client_cache.theme_cache.keys.join(', ')}" if RSence.args[:verbose]
elsif not has_theme_part
response.status = 503
response.body = '503 - Invalid Theme Part Request'
elsif not has_theme_file
response.status = 404
response.body = '404 - Theme Resource Not Found'
puts "File not found, avail: #{@client_cache.theme_cache[theme_name][theme_part].keys.join(', ')}" if RSence.args[:verbose]
else
response.status = 200
file_ext = req_file.split('.')[-1]
response['Content-Type'] = {
'html' => 'text/html; charset=utf-8',
'css' => 'text/css; charset=utf-8',
'png' => 'image/png',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'swf' => 'application/x-shockwave-flash'
}[file_ext]
if theme_part == :gfx
support_gzip = false
else
support_gzip = support_gzip?( ua )
end
if support_gzip
response['Last-Modified'] = @client_cache.last_modified
body = @client_cache.theme_cache[theme_name][theme_part][ req_file+'.gz' ]
response['Content-Length'] = body.bytesize.to_s
response['Content-Encoding'] = 'gzip'
response.body = body
else
# Special IE6 condition to serve gifs instead of png's, because it works much better
# than using the ActiveX alpha filter hack
if ua[:msie6] and req_file[-4..-1] == '.png'
ie6_req_png2gif = req_file.gsub('.png','-ie6.gif')
req_file = ie6_req_png2gif if @client_cache.theme_cache[theme_name][theme_part].include?(ie6_req_png2gif)
end
response['Last-Modified'] = @client_cache.last_modified
body = @client_cache.theme_cache[theme_name][theme_part][ req_file ]
if body.nil?
warn "ClientPkgServe#get: empty body for #{request.path}"
body = ''
end
response['Content-Length'] = body.bytesize.to_s
response.body = body
end
end
end
## Responds to get-requests
def get( request, response, session )
build_busy_wait
ua = check_ua( request )
set_headers( response )
## Split path into an array for determining what to serve
request_uri = '/'+request.path.match( /^#{::RSence.config[:broker_urls][:h]}(.*)$/ )[1]
request_path = request_uri.split( '/' )
## Requested type of client resource (js/themes)
req_type = request_path[2]
unless ['js','themes'].include? req_type
req_rev = req_type
req_type = request_path[3]
request_path.delete_at(2)
end
## Special rules for the special browser
if request.path.end_with?('.htc')
serve_htc( request, response, request_path )
## Serve compiled client javascript component files:
elsif req_type == 'js'
serve_js( request, response, request_path, ua )
## Serve client theme files
elsif req_type == 'themes'
serve_theme( request, response, request_path, ua )
end
end
end