#!/usr/bin/env ruby require 'unicorn-cuba-base' require 'base64' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) Application.new('httpimagestore', port: 3000, processor_count_factor: 2) do cli do description 'HTTP based image storage and thumbnailer' argument :config, cast: Pathname, description: 'configuration file path' version (Pathname.new(__FILE__).dirname + '..' + 'VERSION').read end settings do |settings| Controler.settings[:config_file] = settings.config end main do |settings| require 'httpimagestore/error_reporter' class HTTPImageStore < Controler extend Stats def_stats( :workers, :total_requests, :total_errors ) raindrops_stats = Raindrops::Middleware::Stats.new self.use Raindrops::Middleware, stats: raindrops_stats StatsReporter << HTTPImageStore.stats StatsReporter << raindrops_stats StatsReporter << Plugin::ResponseHelpers.stats self.define do HTTPImageStore.stats.incr_total_requests on error? do HTTPImageStore.stats.incr_total_errors run ErrorReporter end on 'stats' do run StatsReporter end on 'health_check' do log.debug "health_check" if client = env['app.configuration'].thumbnailer # 8x8 PNG data = Base64.decode64('iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAI0lEQVQI1z3KMQoAMAyAwEv//2c7pFQQHISqssXaQWby+NsFYkkV7w+CVgAAAAAASUVORK5CYII=') begin thumbnail = client.thumbnail(data, 'fit', 4, 4, 'jpeg') unless thumbnail.data.length > 300 and thumbnail.data.include? 'JFIF' write_plain 502, 'bad image data returned from thumbnailer' halt res.finish end rescue Errno::ECONNREFUSED => error write_error 502, error halt res.finish end end write_plain 200, 'HTTP Image Store OK' end env['app.configuration'].handlers.each do |handler| on eval(handler.http_method), *handler.uri_matchers.map{|m| instance_eval(&m.matcher)} do |*args| # map and decode matched URI componetns matches = {} Hash[handler.uri_matchers.select{|m| m.name}.map{|m| m.name}.zip(args)].each do |name, value| matches[name] = URI.decode(value).force_encoding('UTF-8') end # decode remaining URI components path = (env["PATH_INFO"][1..-1] || '').split('/').map do |part| URI.decode(part).force_encoding('UTF-8') end.join('/') # query string already doceded by Rack query_string = req.GET state = Configuration::RequestState.new(req.body.read, matches, path, query_string, memory_limit) handler.image_sources.each do |image_source| image_source.realize(state) unless image_source.respond_to? :excluded? and image_source.excluded?(state) end handler.stores.each do |store| store.realize(state) unless store.respond_to? :excluded? and store.excluded?(state) end handler.output.realize(state) instance_eval &state.output_callback end end on root do write_plain 200, 'HTTP Image Store' end end end class Configurator def initialize(app, configuration) @app = app @configuration = configuration end def call(env) env['app.configuration'] = @configuration @app.call(env) end end require 'httpimagestore/configuration' # connect Scope tree with Controler logger Configuration::Scope.logger = Controler.logger_for(Configuration::Scope) # load builin supported set require 'httpimagestore/configuration/path' require 'httpimagestore/configuration/handler' require 'httpimagestore/configuration/thumbnailer' require 'httpimagestore/configuration/file' require 'httpimagestore/configuration/output' require 'httpimagestore/configuration/s3' HTTPImageStore.use Configurator, Configuration.from_file(settings.config) HTTPImageStore end after_fork do |server, worker| HTTPImageStore.stats.incr_workers end end