Skip to content

Instantly share code, notes, and snippets.

@simonx1
Created October 24, 2009 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save simonx1/217558 to your computer and use it in GitHub Desktop.
Save simonx1/217558 to your computer and use it in GitHub Desktop.
#--
# Copyright (c) 2009 Szymon Kurcab szymon.kurcab@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
require 'net/https'
require 'rack'
module HostConfiguration
# Volantis Systems Device Repository Web Service
DRWS = {
:host => 'rackcontest.volantis.com',
:port => 8443,
:path => '/drws',
:user => 'drws',
:passwd => 'drws'
}
TRANSCODERS = {
:google => {
:host => 'www.google.com',
:port => 80,
:path => '/gwt/n'
},
:volantis => {
:host => 'rackcontest.volantis.com',
:port => 8080,
:path => '/mlAdapt'
}
}
end
class Rack::ContentServant
include HostConfiguration
PC_STRING = 'PC'
def initialize(app, tc_engine)
@host = TRANSCODERS[tc_engine][:host]
@port = TRANSCODERS[tc_engine][:port]
@path = TRANSCODERS[tc_engine][:path]
@app = app
end
private
def timeout_response
[408, {"Content-Type" => "text/html"}, "408 Request Timeout"]
end
# detects client browser
def client_browser(env)
# Prepare HTTPS connection to the web service
http = Net::HTTP.new(DRWS[:host], DRWS[:port])
http.use_ssl = true
req = Net::HTTP::Post.new(DRWS[:path] + '/device/name-by-headers')
req.basic_auth(DRWS[:user], DRWS[:passwd])
req['content-type'] = 'text/html'
begin
http.start { |h|
response = h.request(req, "User-Agent: #{env["HTTP_USER_AGENT"]}")
return response.body.match(/<device-name[^>]*>(.*?)<[^>]*>/)[1]
}
rescue Exception
PC_STRING
end
end
def pc?(env)
client_browser(env).starts_with?(PC_STRING)
end
end
class GoogleContentServant < Rack::ContentServant
def initialize(app)
super(app, :google)
end
# :api: plugin
def call(env)
if from_transcoder?(env) || pc?(env)
@app.call(env)
else
get_transcoded_content(env)
end
end
private
def get_transcoded_content(env)
http = Net::HTTP.new(@host)
host = env['HTTP_X_FORWARDED_HOST'] || env['HTTP_HOST']
uri = URI.parse('http://' + host + env['REQUEST_PATH'])
uri.merge!(env['QUERY_STRING'])
query = URI.escape(uri.to_s)
begin
response = http.get(@path + '?u=' + query, proxy_headers(env))
[response.code.to_i, {"Content-Type" => response['content-type'] }, process_body(response.body)]
rescue TimeoutError
timeout_response
end
end
# adds <base> tag to the page header for proper link handling
def process_body(bdy)
bdy.gsub(/<head[^>]*>(.*?)<\/head>/,"<head>#{$1}#{base_tag}</head>")
end
# infinite loop protection
def from_transcoder?(env)
env['Content Location'].to_s.starts_with?(@path)
end
def proxy_headers(env)
h = {}
add_if_set(h, 'Accept', env['HTTP_ACCEPT'])
add_if_set(h, 'Accept Language', env['HTTP_ACCEPT_LANGUAGE'])
add_if_set(h, 'Accept Encoding', env['HTTP_ACCEPT_ENCODING'])
add_if_set(h, 'Cookie', env['HTTP_COOKIE'])
add_if_set(h, 'Accept Charset', env['HTTP_ACCEPT_CHARSET'])
add_if_set(h, 'User Agent', env['HTTP_USER_AGENT'])
h
end
def add_if_set(hash, key, value)
hash.merge!(key => value) unless value.blank?
end
def base_tag
"<base href=\"http://#{@host}\" />"
end
end
class VolantisContentServant < Rack::ContentServant
def initialize(app)
super(app, :volantis)
end
# :api: plugin
def call(env)
req_call = @app.call(env)
# return direct application response if PC client or redirection
if pc?(env) || (req_call.first / 100 == 3)
return req_call
else
get_transcoded_content(env, req_call)
end
end
private
def get_transcoded_content(env, req_call)
url = URI.parse("http://#{@host}:#{@port}#{@path}")
begin
Net::HTTP.start(url.host,url.port) {|http|
response = http.post(url.path, set_content(env, req_call), { 'X-RMSC-PROTOCOL' => 'WAP2.0' } )
processed = process_body(response.body)
[processed[:code], processed[:headers], processed[:body]]
}
rescue TimeoutError
timeout_response
end
end
def process_body(bdy)
ret = {}
content = bdy.split("\r\n\r\n")
ret[:body] = content.last
ret[:headers] = {}
content.first.split("\r\n").each_with_index { |e, i|
if i == 0
ret[:code] = e.split(' ')[1].to_i
else
k, v = e.split(':')
ret[:headers].merge!(k.to_s => v.to_s)
end
}
ret
end
def set_content(env, req_call)
status, headers, response = req_call
<<-CONTENT
#{env['REQUEST_METHOD']} / #{env['HTTP_VERSION']}
Host: #{env['HTTP_HOST']}
User-Agent: #{env['HTTP_USER_AGENT']}
Accept: #{env['HTTP_ACCEPT']}
Accept-Language: #{env['HTTP_ACCEPT_LANGUAGE']}
Accept-Encoding: #{env['HTTP_ACCEPT_ENCODING']}
Accept-Charset: #{env['HTTP_ACCEPT_CHARSET']}
Keep-Alive: #{env['HTTP_KEEP_ALIVE']}
Connection: #{env['HTTP_CONNECTION']}
Cookie: #{env['HTTP_COOKIE']}
HTTP/1.1 #{status} OK
Content-Length: #{::Rack::Utils.bytesize(response.body.to_s)}
Content-Type: #{headers['Content-Type']}
#{response.body}
CONTENT
end
end
class Rack::ContentServantFactory
def self.tc_engine(tc_engine = nil)
case tc_engine
when :google
GoogleContentServant
else
VolantisContentServant
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment