Module: MrMurano::Http

Includes:
Verbose
Included in:
AccountBase, HttpAuthed
Defined in:
lib/MrMurano/http.rb

Constant Summary

Constants included from Verbose

Verbose::TABULARIZE_DATA_FORMAT_ERROR

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Verbose

ask_yes_no, #ask_yes_no, #assert, assert, cmd_confirm_delete!, #cmd_confirm_delete!, debug, #debug, dump_file_json, dump_file_plain, dump_file_yaml, #dump_output_file, #error, error, #error_file_format!, fancy_ticks, #fancy_ticks, #load_file_json, #load_file_plain, #load_file_yaml, #load_input_file, outf, #outf, #outformat_engine, #pluralize?, pluralize?, #prepare_hash_csv, #read_hashf!, #tabularize, tabularize, verbose, #verbose, warning, #warning, #whirly_interject, whirly_interject, #whirly_linger, whirly_linger, #whirly_msg, whirly_msg, #whirly_pause, whirly_pause, #whirly_start, whirly_start, #whirly_stop, whirly_stop, #whirly_unpause, whirly_unpause

Class Method Details

.curldebug_after(_request, response) ⇒ Object

(lb): Ideally, we’d refactor and these wouldn’t be static class methods.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/MrMurano/http.rb', line 79

def self.curldebug_after(_request, response)
  return response unless $cfg['tool.curldebug'] && $cfg['tool.curlfancy']
  MrMurano::Http.curldebug_elapsed
  if response.nil?
    formatted = '<nil>'
  else
    begin
      formatted = "#{response.code} / #{response.body}"
    rescue StandardError
      formatted = response.to_s
    end
  end
  formatted = "Response: #{formatted}"
  MrMurano::Http.curldebug_log(formatted)
  response
end

.curldebug_elapsedObject



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/MrMurano/http.rb', line 96

def self.curldebug_elapsed
  http_recv_at = Time.now
  if !@http_sent_at.nil?
    formatted = "ElapsedT: #{http_recv_at - @http_sent_at}"
  else
    formatted = 'ElapsedT: Overlapping command clobbered time.'
    MrMurano::Verbose.warning(formatted) if $cfg['tool.debug']
  end
  MrMurano::Http.curldebug_log(formatted)
  @http_sent_at = nil
end

.curldebug_log(formatted, stamp_it: false) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/MrMurano/http.rb', line 108

def self.curldebug_log(formatted, stamp_it: false)
  if $cfg.curlfile_f.nil?
    MrMurano::Progress.instance.whirly_interject { puts formatted }
  else
    $cfg.curlfile_f << formatted + "\n\n"
    $cfg.curlfile_f.flush
  end
  return unless stamp_it
  @http_outstanding = [] unless defined? @http_outstanding
  @http_outstanding.push(formatted)
  @http_sent_at = Time.now
end

Instance Method Details

#add_headers(request) ⇒ Object



149
150
151
152
153
# File 'lib/MrMurano/http.rb', line 149

def add_headers(request)
  request.content_type = 'application/json'
  request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
  request
end

#curldebug(request) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/MrMurano/http.rb', line 46

def curldebug(request)
  return unless $cfg['tool.curldebug']
  formp = (request.content_type =~ %r{multipart/form-data})
  ccmd = []
  ccmd << %(curl -s)
  if request.key?('Authorization')
    ccmd << %(-H 'Authorization: #{request['Authorization']}')
  end
  ccmd << %(-H 'User-Agent: #{request['User-Agent']}')
  ccmd << %(-H 'Content-Type: #{request.content_type}') unless formp
  ccmd << %(-X #{request.method})
  ccmd << %('#{request.uri}')
  unless request.body.nil?
    if formp
      m = request.body.match(
        /form-data;\s+name="(?<name>[^"]+)";\s+filename="(?<filename>[^"]+)"/
      )
      ccmd << %(-F #{m[:name]}=@#{m[:filename]}) unless m.nil?
    else
      req_body = request.body
      unless $cfg['tool.show-password']
        req_body = req_body.gsub(
          /"password":(["'])(?:(?=(\\?))\2.)*?\1/, '"password":"<redacted>"'
        )
      end
      ccmd << %(-d '#{req_body}')
    end
  end

  MrMurano::Http.curldebug_log(ccmd.join(' '), stamp_it: true)
end

#delete(path = '', &block) ⇒ Object



262
263
264
265
# File 'lib/MrMurano/http.rb', line 262

def delete(path='', &block)
  uri = endpoint(path)
  workit(add_headers(Net::HTTP::Delete.new(uri)), &block)
end

#endpoint(path) ⇒ Object

Default endpoint unless Class overrides it.



33
34
35
# File 'lib/MrMurano/http.rb', line 33

def endpoint(path)
  URI($cfg['net.protocol'] + '://' + host + '/api:1/' + path.to_s)
end

#get(path = '', query = nil, &block) ⇒ Object



223
224
225
226
227
# File 'lib/MrMurano/http.rb', line 223

def get(path='', query=nil, &block)
  uri = endpoint(path)
  uri.query = URI.encode_www_form(query) unless query.nil?
  workit(add_headers(Net::HTTP::Get.new(uri)), &block)
end

#hostObject



24
25
26
# File 'lib/MrMurano/http.rb', line 24

def host
  $cfg['net.host'].to_s
end

#httpObject



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/MrMurano/http.rb', line 121

def http
  uri = URI($cfg['net.protocol'] + '://' + $cfg['net.host'])
  if !defined?(@http) || @http.nil?
    @http = Net::HTTP.new(uri.host, uri.port)
    # (lb): The HTTP timeouts default to 60 secs. But requests
    # sometimes timeout, so experimenting with longer timeouts.
    @http.open_timeout = 90 # default: 60
    @http.read_timeout = 90 # default: 60
    @http.use_ssl = true if $cfg['net.protocol'] == 'https'
    begin
      @http.start
    rescue SocketError => err
      # E.g., "error: Failed to open TCP connection to true:443
      #        (Hostname not known: true)."
      error %(Net socket error: #{err.message})
      exit 2
    rescue StandardError => err
      error %(Net request failed: #{err.message})
      exit 2
    end
  end
  @http
end

#http_resetObject



145
146
147
# File 'lib/MrMurano/http.rb', line 145

def http_reset
  @http = nil
end

#isJSON(data) ⇒ Object

rubocop:disable Style/MethodName “Use snake_case for method names.” 2017-08-20: isJSON and showHttpError are grandparented into lint exceptions.



157
158
159
160
161
# File 'lib/MrMurano/http.rb', line 157

def isJSON(data)
  [true, JSON.parse(data, json_opts)]
rescue StandardError
  [false, data]
end

#json_optsObject



37
38
39
40
41
42
43
44
# File 'lib/MrMurano/http.rb', line 37

def json_opts
  return @json_opts unless !defined?(@json_opts) || @json_opts.nil?
  @json_opts = {
    allow_nan: true,
    symbolize_names: true,
    create_additions: false,
  }
end

#patch(path = '', body = {}, &block) ⇒ Object



254
255
256
257
258
259
260
# File 'lib/MrMurano/http.rb', line 254

def patch(path='', body={}, &block)
  uri = endpoint(path)
  req = Net::HTTP::Patch.new(uri)
  add_headers(req)
  req.body = JSON.generate(body)
  workit(req, &block)
end

#post(path = '', body = {}, &block) ⇒ Object



229
230
231
232
233
234
235
# File 'lib/MrMurano/http.rb', line 229

def post(path='', body={}, &block)
  uri = endpoint(path)
  req = Net::HTTP::Post.new(uri)
  add_headers(req)
  req.body = JSON.generate(body)
  workit(req, &block)
end

#postf(path = '', form = {}, &block) ⇒ Object



237
238
239
240
241
242
243
244
# File 'lib/MrMurano/http.rb', line 237

def postf(path='', form={}, &block)
  uri = endpoint(path)
  req = Net::HTTP::Post.new(uri)
  add_headers(req)
  req.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
  req.form_data = form
  workit(req, &block)
end

#put(path = '', body = {}, &block) ⇒ Object



246
247
248
249
250
251
252
# File 'lib/MrMurano/http.rb', line 246

def put(path='', body={}, &block)
  uri = endpoint(path)
  req = Net::HTTP::Put.new(uri)
  add_headers(req)
  req.body = JSON.generate(body)
  workit(req, &block)
end

#showHttpError(request, response) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/MrMurano/http.rb', line 163

def showHttpError(request, response)
  if $cfg['tool.debug']
    puts "Sent #{request.method} #{request.uri}"
    request.each_capitalized { |k, v| puts "> #{k}: #{v}" }
    if request.body.nil?
    else
      puts ">> #{request.body[0..156]}"
    end
    puts "Got #{response.code} #{response.message}"
    response.each_capitalized { |k, v| puts "< #{k}: #{v}" }
  end
  isj, jsn = isJSON(response.body)
  resp = "Request Failed: #{response.code}: "
  if isj
    # 2017-07-02: Changing shovel operator << to +=
    # to support Ruby 3.0 frozen string literals.
    if $cfg['tool.fullerror']
      resp += JSON.pretty_generate(jsn)
    elsif jsn.is_a? Hash
      resp += "[#{jsn[:statusCode]}] " if jsn.key? :statusCode
      resp += jsn[:message].to_s if jsn.key? :message
    else
      resp += jsn.to_s
    end
  else
    resp += (jsn || 'nil')
  end
  error resp
end

#userObject



28
29
30
# File 'lib/MrMurano/http.rb', line 28

def user
  $cfg['user.name'].to_s
end

#workit(request) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/MrMurano/http.rb', line 193

def workit(request)
  curldebug(request)
  if block_given?
    response = yield request, http
    MrMurano::Http.curldebug_after(request, response)
  else
    response = http.request(request)
    MrMurano::Http.curldebug_after(request, response)
    case response
    when Net::HTTPSuccess
      workit_response(response)
    else
      # One problem with mixins is initialization...
      unless defined?(@suppress_error) && @suppress_error
        showHttpError(request, response)
      end
      nil
    end
  end
end

#workit_response(response) ⇒ Object



214
215
216
217
218
219
220
221
# File 'lib/MrMurano/http.rb', line 214

def workit_response(response)
  return {} if response.body.nil?
  begin
    JSON.parse(response.body, json_opts)
  rescue StandardError
    response.body
  end
end