Class: OpenC3::HttpClientInterface

Inherits:
Interface show all
Defined in:
lib/openc3/interfaces/http_client_interface.rb

Constant Summary

Constants included from Api

Api::DELAY_METRICS, Api::DURATION_METRICS, Api::SUBSCRIPTION_DELIMITER, Api::SUM_METRICS

Constants included from ApiShared

ApiShared::DEFAULT_TLM_POLLING_RATE

Constants included from Extract

Extract::SCANNING_REGULAR_EXPRESSION

Instance Attribute Summary

Attributes inherited from Interface

#auto_reconnect, #bytes_read, #bytes_written, #cmd_routers, #cmd_target_names, #config_params, #connect_on_startup, #disable_disconnect, #interfaces, #name, #num_clients, #options, #packet_log_writer_pairs, #protocol_info, #read_count, #read_protocols, #read_queue_size, #read_raw_data, #read_raw_data_time, #reconnect_delay, #routers, #scheduler, #secrets, #state, #stored_packet_log_writer_pairs, #stream_log_pair, #target_names, #tlm_target_names, #write_count, #write_protocols, #write_queue_size, #written_raw_data, #written_raw_data_time

Instance Method Summary collapse

Methods inherited from Interface

#_write, #add_protocol, #as_json, #copy_to, #interface_cmd, #protocol_cmd, #read, #read_allowed?, #read_interface_base, #set_option, #start_raw_logging, #stop_raw_logging, #write, #write_allowed?, #write_interface_base, #write_raw, #write_raw_allowed?

Methods included from Api

#_build_cmd_output_string, #_cmd_implementation, #_extract_target_command_names, #_extract_target_command_parameter_names, #_extract_target_packet_item_names, #_extract_target_packet_names, #_get_and_set_cmd, #_get_item, #_limits_group, #_set_tlm_process_args, #_tlm_process_args, #_validate_tlm_type, #build_cmd, #cmd, #cmd_no_checks, #cmd_no_hazardous_check, #cmd_no_range_check, #cmd_raw, #cmd_raw_no_checks, #cmd_raw_no_hazardous_check, #cmd_raw_no_range_check, #config_tool_names, #connect_interface, #connect_router, #delete_config, #disable_cmd, #disable_limits, #disable_limits_group, #disconnect_interface, #disconnect_router, #enable_cmd, #enable_limits, #enable_limits_group, #get_all_cmd_names, #get_all_cmds, #get_all_interface_info, #get_all_router_info, #get_all_settings, #get_all_tlm, #get_all_tlm_names, #get_cmd, #get_cmd_buffer, #get_cmd_cnt, #get_cmd_cnts, #get_cmd_hazardous, #get_cmd_time, #get_cmd_value, #get_interface, #get_interface_names, #get_item, #get_limits, #get_limits_events, #get_limits_groups, #get_limits_set, #get_limits_sets, #get_metrics, #get_out_of_limits, #get_overall_limits_state, #get_overrides, #get_packet_derived_items, #get_packets, #get_param, #get_router, #get_router_names, #get_setting, #get_settings, #get_target, #get_target_interfaces, #get_target_names, #get_tlm, #get_tlm_buffer, #get_tlm_cnt, #get_tlm_cnts, #get_tlm_packet, #get_tlm_values, #inject_tlm, #interface_cmd, #interface_protocol_cmd, #limits_enabled?, #list_configs, #list_settings, #load_config, #map_target_to_interface, #normalize_tlm, #offline_access_needed, #override_tlm, #router_cmd, #router_protocol_cmd, #save_config, #send_raw, #set_limits, #set_limits_set, #set_offline_access, #set_setting, #set_tlm, #start_raw_logging_interface, #start_raw_logging_router, #stash_all, #stash_delete, #stash_get, #stash_keys, #stash_set, #stop_raw_logging_interface, #stop_raw_logging_router, #subscribe_packets, #tlm, #tlm_formatted, #tlm_raw, #tlm_variable, #tlm_with_units

Constructor Details

#initialize(hostname, port = 80, protocol = 'http', write_timeout = 5, read_timeout = nil, connect_timeout = 5, include_request_in_response = false) ⇒ HttpClientInterface

Returns a new instance of HttpClientInterface.

Parameters:

  • hostname (String)

    HTTP/HTTPS server to connect to

  • port (Integer) (defaults to: 80)

    HTTP/HTTPS port

  • protocol (String) (defaults to: 'http')

    http or https



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/openc3/interfaces/http_client_interface.rb', line 32

def initialize(hostname, port = 80, protocol = 'http', write_timeout = 5, read_timeout = nil,
               connect_timeout = 5, include_request_in_response = false)
  super()
  @hostname = hostname
  @port = Integer(port)
  @protocol = protocol
  if (@port == 80 and @protocol == 'http') or (@port == 443 and @protocol == 'https')
    @url = "#{@protocol}://#{@hostname}"
  else
    @url = "#{@protocol}://#{@hostname}:#{@port}"
  end
  @write_timeout = ConfigParser.handle_nil(write_timeout)
  @write_timeout = Float(@write_timeout) if @write_timeout
  @read_timeout = ConfigParser.handle_nil(read_timeout)
  @read_timeout = Float(@read_timeout) if @read_timeout
  @connect_timeout = ConfigParser.handle_nil(connect_timeout)
  @connect_timeout = Float(@connect_timeout) if @connect_timeout
  @include_request_in_response = ConfigParser.handle_true_false(include_request_in_response)
  @response_queue = Queue.new
end

Instance Method Details

#connectObject

Connects the interface to its target(s)



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/openc3/interfaces/http_client_interface.rb', line 58

def connect
  # Per https://github.com/lostisland/faraday/blob/main/lib/faraday/options/env.rb
  # :timeout       - time limit for the entire request (Integer in seconds)
  # :open_timeout  - time limit for just the connection phase (e.g. handshake) (Integer in seconds)
  # :read_timeout  - time limit for the first response byte received from the server (Integer in seconds)
  # :write_timeout - time limit for the client to send the request to the server (Integer in seconds)
  request = {}
  request['open_timeout'] = @connect_timeout if @connect_timeout
  request['read_timeout'] = @read_timeout if @read_timeout
  request['write_timeout'] = @write_timeout if @write_timeout
  @http = Faraday.new(request: request) do |f|
    f.response :follow_redirects # use Faraday::FollowRedirects::Middleware
    f.adapter :net_http # adds the adapter to the connection, defaults to `Faraday.default_adapter`
  end
  super()
end

#connected?Boolean

Whether the interface is connected to its target(s) But in this case it is basically whether connect was called

Returns:

  • (Boolean)


77
78
79
80
81
82
83
# File 'lib/openc3/interfaces/http_client_interface.rb', line 77

def connected?
  if @http
    return true
  else
    return false
  end
end

#connection_stringObject



53
54
55
# File 'lib/openc3/interfaces/http_client_interface.rb', line 53

def connection_string
  return @url
end

#convert_data_to_packet(data, extra = nil) ⇒ Packet

Called to convert the read data into a OpenC3 Packet object

Parameters:

  • data (String)

    Raw packet data

  • extra (Hash) (defaults to: nil)

    Contains the following keys: HTTP_HEADERS - Hash of response headers HTTP_STATUS - Integer response status code HTTP_REQUEST - [data, extra]

    where data is the request data and extra contains:
      HTTP_REQUEST_TARGET_NAME - String request target name
      HTTP_URI - String request URI based on HTTP_PATH
      HTTP_PATH - String request path
      HTTP_METHOD - String request method
      HTTP_PACKET - String response packet name
      HTTP_ERROR_PACKET - Optional string error packet name
      HTTP_QUERIES - Optional hash of request queries
      HTTP_HEADERS - Optional hash of request headers
    

Returns:

  • (Packet)

    OpenC3 Packet with buffer filled with data



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/openc3/interfaces/http_client_interface.rb', line 205

def convert_data_to_packet(data, extra = nil)
  packet = Packet.new(nil, nil, :BIG_ENDIAN, nil, data.to_s)
  packet.accessor = HttpAccessor.new(packet)
  # Grab the request extra set in the write_interface method
  request_extra = extra['HTTP_REQUEST'][1] if extra and extra['HTTP_REQUEST']
  if request_extra
    # Identify the response target
    request_target_name = request_extra['HTTP_REQUEST_TARGET_NAME']
    if request_target_name
      request_target_name = request_target_name.to_s.upcase
      response_packet_name = request_extra['HTTP_PACKET']
      error_packet_name = request_extra['HTTP_ERROR_PACKET']
      # HTTP_STATUS was set in the base extra
      status = extra['HTTP_STATUS'].to_i
      if status >= 300 and error_packet_name
        # Handle error special case response packet
        packet.target_name = request_target_name
        packet.packet_name = error_packet_name.to_s.upcase
      else
        if response_packet_name
          packet.target_name = request_target_name
          packet.packet_name = response_packet_name.to_s.upcase
        end
      end
    end
    unless @include_request_in_response
      extra.delete("HTTP_REQUEST")
    end
  end
  packet.extra = extra
  return packet
end

#convert_packet_to_data(packet) ⇒ Object

Called to convert a packet into a data buffer. Write protocols then potentially modify the data in their write_data methods. Finally write_interface is called to send the data to the target.

Parameters:

  • packet (Packet)

    Packet to extract data from

Returns:

  • data, extra



104
105
106
107
108
109
110
111
112
# File 'lib/openc3/interfaces/http_client_interface.rb', line 104

def convert_packet_to_data(packet)
  extra = packet.extra
  extra ||= {}
  data = packet.buffer(true) # Copy buffer so logged command isn't modified
  extra['HTTP_URI'] = URI("#{@url}#{packet.read('HTTP_PATH')}").to_s
  # Store the target name for use in identifying the response
  extra['HTTP_REQUEST_TARGET_NAME'] = packet.target_name
  return data, extra
end

#disconnectObject

Disconnects the interface from its target(s)



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/openc3/interfaces/http_client_interface.rb', line 86

def disconnect
  @http.close if @http
  @http = nil
  # Clear the response queue
  while @response_queue.length > 0
    @response_queue.pop
  end
  super()
  # Push nil to cause read_interface to return nil
  @response_queue.push(nil)
end

#read_interfaceObject

Returns the response data and extra from the interface which was queued up by the write_interface method. Read protocols can then potentially modify the data in their read_data methods. Then convert_data_to_packet is called to convert the data into a Packet object. Finally the read protocols read_packet methods are called.



179
180
181
182
183
184
185
186
# File 'lib/openc3/interfaces/http_client_interface.rb', line 179

def read_interface
  # This blocks until a response is available
  data, extra = @response_queue.pop
  return nil if data.nil?

  read_interface_base(data, extra)
  return data, extra
end

#write_interface(data, extra = nil) ⇒ Object

Calls the appropriate HTTP method using Faraday



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/openc3/interfaces/http_client_interface.rb', line 115

def write_interface(data, extra = nil)
  extra ||= {}
  queries = extra['HTTP_QUERIES']
  queries ||= {}
  headers = extra['HTTP_HEADERS']
  headers ||= {}
  uri = extra['HTTP_URI']
  method = extra['HTTP_METHOD']

  resp = nil
  case method
  when 'get'
    resp = @http.get(uri) do |req|
      req.params = queries
      req.headers = headers
    end
  when 'put'
    resp = @http.put(uri) do |req|
      req.params = queries
      req.headers = headers
      req.body = data
    end
  when 'delete'
    resp = @http.delete(uri) do |req|
      req.params = queries
      req.headers = headers
    end
  when 'post'
    resp = @http.post(uri) do |req|
      req.params = queries
      req.headers = headers
      req.body = data
    end
  else
    raise "Unsupported HTTP Method: #{method}"
  end

  # Normalize Response into simple hash
  response_data = nil
  response_extra = {}
  if resp
    # We store the request data and extra under HTTP_REQUEST
    # This can optionally be returned in the response
    # but is also used to identify the response target
    response_extra['HTTP_REQUEST'] = [data, extra]
    if resp.headers and resp.headers.length > 0
      response_extra['HTTP_HEADERS'] = resp.headers
    end
    response_extra['HTTP_STATUS'] = resp.status
    response_data = resp.body
    response_data ||= '' # Ensure an empty string if we got a response but no data
  end

  @response_queue.push([response_data, response_extra])

  write_interface_base(data, extra)
  return data, extra
end