Class: Carnivore::Source::HttpPaths

Inherits:
HttpSource show all
Includes:
Bogo::Memoization
Defined in:
lib/carnivore-http/http_paths.rb

Overview

Carnivore HTTP paths

Constant Summary collapse

DEFAULT_RESPONSE_TIMEOUT =

Default max wait time for message response

10
DEFAULT_RESPONSE_WAIT_STEP =

Default response wait time stepping

0.1

Constants inherited from HttpSource

Carnivore::Source::HttpSource::BODY_TO_FILE_SIZE

Instance Attribute Summary collapse

Attributes inherited from HttpSource

#args, #auth_allowed_origins, #auth_htpasswd, #retry_delivery

Instance Method Summary collapse

Methods inherited from HttpSource

#allowed_credentials?, #allowed_htpasswd?, #allowed_origin?, #authorized?, #auto_process?, #build_listener, #build_message, #confirm, #default_args, #perform_transmission, #retry_delivery_failure, #retry_directory, #retry_write_directory, #transmit, #write_for_retry

Methods included from Http::Utils::Params

#format_query_args, #format_query_type, included, #parse_query_string

Instance Attribute Details

#http_methodSymbol (readonly)

Returns http method.

Returns:

  • (Symbol)

    http method



21
22
23
# File 'lib/carnivore-http/http_paths.rb', line 21

def http_method
  @http_method
end

#http_pathString (readonly)

Returns end point path.

Returns:

  • (String)

    end point path



19
20
21
# File 'lib/carnivore-http/http_paths.rb', line 19

def http_path
  @http_path
end

Instance Method Details

#connectObject

Setup the HTTP listener source



51
52
53
# File 'lib/carnivore-http/http_paths.rb', line 51

def connect
  start_listener!
end

#halt_listenerObject

Kill listener on shutdown



24
25
26
27
28
29
30
31
# File 'lib/carnivore-http/http_paths.rb', line 24

def halt_listener
  listener = memoize("#{args[:bind]}-#{args[:port]}", :global){ nil }
  if(listener && listener.alive?)
    listener.terminate
  end
  unmemoize("#{args[:bind]}-#{args[:port]}", :global)
  unmemoize("#{args[:bind]}-#{args[:port]}-queues", :global)
end

#message_queueQueue

Returns:

  • (Queue)


68
69
70
# File 'lib/carnivore-http/http_paths.rb', line 68

def message_queue
  message_queues[queue_key]
end

#message_queuesQueue

Returns Message queue.

Returns:

  • (Queue)

    Message queue



56
57
58
59
60
# File 'lib/carnivore-http/http_paths.rb', line 56

def message_queues
  memoize("#{args[:bind]}-#{args[:port]}-queues", :global) do
    Smash.new
  end
end

#queue_keyString

Returns:

  • (String)


63
64
65
# File 'lib/carnivore-http/http_paths.rb', line 63

def queue_key
  "#{http_path}-#{http_method}"
end

#receive(*_) ⇒ Object

Returns:

  • (Object)


123
124
125
126
127
128
129
# File 'lib/carnivore-http/http_paths.rb', line 123

def receive(*_)
  val = nil
  until(val)
    val = Celluloid::Future.new{ message_queue[:queue].pop }.value
  end
  val
end

#setup(*_) ⇒ Object

Setup message queue for source



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/carnivore-http/http_paths.rb', line 34

def setup(*_)
  @http_path = args.fetch(:path, '/')
  @http_method = args.fetch(:method, 'get').to_s.downcase.to_sym
  super
  if(message_queues[queue_key])
    raise ArgumentError.new "Conflicting HTTP path source provided! path: #{http_path} method: #{http_method}"
  else
    message_queues[queue_key] = Smash.new(
      :queue => Queue.new
    )
  end
  message_queues[queue_key].merge!(
    Smash.new(:config => args.to_smash)
  )
end

#start_listener!Object

Start the HTTP(S) listener



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/carnivore-http/http_paths.rb', line 73

def start_listener!
  memoize("#{args[:bind]}-#{args[:port]}", :global) do
    build_listener do |con|
      con.each_request do |req|
        begin
          msg = build_message(con, req)
          # Start with static path lookup since it's the
          # cheapest, then fallback to iterative globbing
          msg_queue = nil
          unless(msg_queue = message_queues["#{req.path}-#{req.method.to_s.downcase}"])
            message_queues.each do |k,v|
              path_glob, http_method = k.split('-')
              if(req.method.to_s.downcase == http_method && File.fnmatch(path_glob, req.path))
                msg_queue = v
              end
            end
          end
          if(msg_queue)
            if(authorized?(msg))
              msg_queue[:queue] << msg
              if(msg_queue[:config][:auto_respond])
                code = msg_queue[:config].fetch(:response, :code, 'ok').to_sym
                response = msg_queue[:config].fetch(:response, :message, 'So long and thanks for all the fish!')
                req.respond(code, response)
              else
                wait_time = msg_queue[:config].fetch(:response_timeout, DEFAULT_RESPONSE_TIMEOUT).to_f
                wait_step = msg_queue[:config].fetch(:response_wait_step, DEFAULT_RESPONSE_WAIT_STEP).to_f
                while(!con.socket.closed? && wait_time > 0)
                  sleep(wait_step)
                  wait_time -= wait_step
                end
                if(con.response_state == :headers)
                  raise "Timeout has been exceeded waiting for response! (#{msg})"
                end
              end
            else
              req.respond(:unauthorized, 'You are not authorized to perform requested action!')
            end
          else
            req.respond(:not_found, 'Requested path not found!')
          end
        rescue => e
          req.respond(:bad_request, "Failed to process request -> #{e}")
        end
      end
    end
  end
end