require 'spiderfw/controller/spider_controller' require 'spiderfw/controller/session/memory_session' require 'spiderfw/controller/session/file_session' module Spider class HTTPController < Controller include HTTPMixin def initialize(request, response, scene=nil) response.status = Spider::HTTP::OK response.headers = { 'Content-Type' => 'text/plain', 'Connection' => 'close' } @previous_stdout = $stdout Thread.current[:stdout] = response.server_output $out = ThreadOut $stdout = ThreadOut if Spider.conf.get('http.seize_stdout') request.extend(HTTPRequest) super(request, response, scene) end def before(action='', *arguments) if (@request.env['HTTP_TRANSFER_ENCODING'] == 'Chunked' && !@request.server.supports?(:chunked_request)) raise HTTPStatus.NOT_IMPLEMENTED end @request.cookies = Spider::HTTP.parse_query(@request.env['HTTP_COOKIE'], ';') @request.session = Session.get(@request.cookies['sid']) @response.cookies['sid'] = @request.session.sid @response.cookies['sid'].path = '/' @request.params = {} @uploaded_files = [] if (@request.env['QUERY_STRING']) @request.params = Spider::HTTP.parse_query(@request.env['QUERY_STRING']) @request.get = @request.params end if @request.env['REQUEST_METHOD'] == 'POST' && @request.env['HTTP_CONTENT_TYPE'] if @request.env['HTTP_CONTENT_TYPE'].include?('application/x-www-form-urlencoded') @request.post = Spider::HTTP.parse_query(@request.read_body) @request.params.merge!(@request.post) elsif @request.env['HTTP_CONTENT_TYPE'] =~ Spider::HTTP::MULTIPART_REGEXP multipart_params, multipart_files = Spider::HTTP.parse_multipart(@request.body, $1, @request.env['CONTENT_LENGTH'].to_i) @request.params.merge!(multipart_params) @uploaded_files = multipart_files end end @request.http_method = @request.env['REQUEST_METHOD'].upcase.to_sym @request.http_host = @request.env['HTTP_HOST'] @request.ssl = true if @request.env['HTTPS'] == 'on' if @request.http_host =~ /(.+)\:(\d+)/ @request.domain = $1 @request.port = $2.to_i else @request.domain = @request.http_host @request.port = @request.ssl? ? 443 : 80 end unless Spider.site port = @request.ssl? ? nil : @request.port Spider.site = Spider::Site.new(@request.domain, port) Spider.site._auto = true Spider.site.save_cache end if @request.ssl? && Spider.site.auto? && !Spider.site.ssl_port Spider.site.ssl_port = @request.port Spider.site.save_cache end if !@request.ssl? && Spider.site.auto? && !Spider.site.port Spider.site.port = @request.port Spider.site.save_cache end if @request.env['HTTP_CACHE_CONTROL'] parts = @request.env['HTTP_CACHE_CONTROL'].split(';') parts.each do |part| if part == 'no-cache' @request.cache_control[:no_cache] = true else key, val = part.split('=') @request.cache_control[key] = val end end end if @request.env['HTTP_PRAGMA'] == 'no-cache' @request.cache_control[:no_cache] = true end Locale.clear Locale.init(:driver => :cgi) Locale.set_request(@request.params['lang'], @request.cookies['lang'], @request.env['HTTP_ACCEPT_LANGUAGE'], @request.env['HTTP_ACCEPT_CHARSET']) @request.locale = Locale.current[0] if (action =~ /(.+)\.(\w+)$/) # strip extension, set format action = $1 @request.format = $2.to_sym end # Spider.reload_sources if Spider.conf.get('webserver.reload_sources') Spider.logger.info("Request: #{@request.http_method} #{@request.http_host} #{@request.path}") super(action, *arguments) end def execute(action='', *arguments) # FIXME: cache stripped action? action = $1 if (action =~ /(.+)\.(\w+)$/) # strip extension, set format super(action, *arguments) log_done #@response.headers['Date'] ||= Time.now.httpdate end def after(action='', *arguments) # FIXME: cache stripped action? action = $1 if (action =~ /(.+)\.(\w+)$/) # strip extension, set format @request.session.persist if @request.session && @request.session.respond_to?(:persist) super(action, *arguments) end def ensure(action='', *arguments) dispatch(:ensure, action, *arguments) $stdout = @previous_stdout if @uploaded_files @uploaded_files.each do |f| f.close end end end def log_done Spider::Request.current[:_http_logged_done] = true str = "Done: #{@response.status} #{Spider::HTTP.status_messages[@response.status]}" str += " (static)" if @request.misc[:is_static] str += " in #{(Time.now - Spider::Request.current[:_start])*1000}ms" if Spider::Request.current[:_start] if @request.respond_to?(:user) && @request.user str += " for user #{@request.user.class}(#{@request.user.primary_keys})" end if Spider.conf.get('log.memory') str += " - Memory usage: #{Spider::Memory.get_memory_usage}" end Spider.logger.info(str) end def get_route(path) path = path.clone path.slice!(0) if path.length > 0 && path[0].chr == "/" return Route.new(:path => path, :dest => Spider.home.controller, :action => path) end def try_rescue(exc) log_done unless Spider::Request.current[:_http_logged_done] self.done = true if exc.is_a?(Spider::Controller::NotFound) Spider.logger.error("Not found: #{exc.path}") elsif exc.is_a?(Spider::Controller::Forbidden) Spider.logger.warn("Forbidden: #{exc.message}") else super end end module HTTPRequest attr_accessor :http_method, :http_host, :domain, :port, :post, :get # Returns PATH_INFO reversing any proxy mappings if needed. def path Spider::ControllerMixins::HTTPMixin.reverse_proxy_mapping(self.env['PATH_INFO']) end def http_path 'http://'+self.env['HTTP_HOST']+path end def full_path Spider.logger.warn("Request#full_path is deprecated. Use Request#http_path instead") http_path end # Returns the REQUEST_URI reversing any proxy mappings if needed def uri Spider::ControllerMixins::HTTPMixin.reverse_proxy_mapping(self.env['REQUEST_URI']) end # Returns #uri prefixed with http:// and the HTTP_HOST def full_uri 'http://'+self.env['HTTP_HOST']+uri end def client_cert return @client_certificate if @client_cert unless self.env['SSL_CLIENT_CERT'].blank? @client_cert = OpenSSL::X509::Certificate.new(self.env['SSL_CLIENT_CERT']) end end def cache_control @cache_control ||= {} end def ssl=(bool) @ssl = bool end def ssl? @ssl end def post @post ||= {} end def get @get ||= {} end def post? self.http_method == :POST end def get? self.http_method == :GET end end end end