require 'travis/api/app' require 'useragent' class Travis::Api::App class Middleware class UserAgentTracker < Middleware WEB_BROWSERS = [ "Internet Explorer", "Webkit", "Chrome", "Safari", "Android", "Firefox", "Camino", "Iceweasel", "Seamonkey", "Android", "Opera", "Mozilla" ] before(agent: /^$/) do ::Metriks.meter("api.user_agent.missing").mark halt(400, "error" => "missing User-Agent header") if Travis::Features.feature_active?(:require_user_agent) end before(agent: /^.+$/) do agent = UserAgent.parse(request.user_agent) case agent.browser when *WEB_BROWSERS then mark_browser when "curl", "Wget" then mark(:console, agent.browser) when "travis-api-wrapper" then mark(:script, :node_js, agent.browser) when "TravisPy" then mark(:script, :python, agent.browser) when "Ruby", "PHP", "Perl", "Python" then mark(:script, agent.browser, :vanilla) when "Faraday" then mark(:script, :ruby, :vanilla) when "Travis" then mark_travis(agent) else mark_unknown end end def mark_browser # allows a JavaScript Client to set X-User-Agent, for instance to "travis-web" in travis-web x_agent = UserAgent.parse(env['HTTP_X_USER_AGENT'] || 'unknown').browser mark(:browser, x_agent) end def mark_travis(agent) os, *rest = agent.application.comment ruby, rubygems, command = "unknown", "unknown", nil rest.each do |comment| case comment when /^Ruby (\d\.\d.\d)/ then ruby = $1 when /^RubyGems (.+)$/ then rubygems = $1 when /^command (.+)$/ then command = $1 end end # "Ubuntu 12.04 like Linux" => "linux.ubuntu.12.04" if os =~ /^(.+) (\S+) like (\S+)$/ os = "#{$3}.#{$1}.#{$2[/\d+\.\d+/]}" end if command mark(:cli, version: agent.version, ruby: ruby, rubygems: rubygems, command: command, os: os) else # only track ruby version and library version for non-cli usage mark(:script, :ruby, :travis, version: agent.version, ruby: ruby) end end def mark_unknown logger.warn "[user-agent-tracker] Unknown User-Agent: %p" % request.user_agent mark(:unknown) end def track_key(string) string.to_s.downcase.gsub(/[^a-z0-9\-\.]+/, '_') end def mark(*keys) key = "api.user_agent" keys.each do |subkey| if subkey.is_a? Hash subkey.each_pair { |k, v| ::Metriks.meter("#{key}.#{track_key(k)}.#{track_key(v)}").mark } else ::Metriks.meter(key << "." << track_key(subkey)).mark end end end end end end