Module: NiceHttpManageRequest

Included in:
NiceHttp
Defined in:
lib/nice_http/manage_request.rb

Instance Method Summary collapse

Instance Method Details

#manage_request(*arguments) ⇒ Object

private method to manage Request input: 3 args: path, data, headers 1 arg: Hash containg at least keys :path and :data In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data. output: path, data, headers



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
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
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/nice_http/manage_request.rb', line 12

def manage_request(*arguments)
  require "json"

  @prev_request = Hash.new() if @prev_request.nil?
  @defaults_request = self.class.requests if @defaults_request.nil? and self.class.requests.is_a?(Hash)
  @request = Hash.new() if @request.nil?
  @defaults_request = Hash.new() unless @defaults_request.is_a?(Hash)

  begin
    content_type_included = false
    path = ""
    data = ""

    @response = Hash.new()
    headers_t = @headers.dup()
    headers_t.merge!(@defaults_request[:headers]) if @defaults_request.key?(:headers)

    cookies_to_set_str = ""
    if arguments.size == 3
      path = arguments[0]
    elsif arguments.size == 1 and arguments[0].kind_of?(Hash)
      path = arguments[0][:path]
    elsif arguments.size == 1 and arguments[0].kind_of?(String)
      path = arguments[0].to_s()
    end
    path = (@prepath + path).gsub("//", "/") unless path.nil? or path.start_with?("http:") or path.start_with?("https:")
    @cookies.each { |cookie_path, cookies_hash|
      cookie_path = "" if cookie_path == "/"
      path_to_check = path
      if path == "/" or path[-1] != "/"
        path_to_check += "/"
      end
      if path_to_check.scan(/^#{cookie_path}\//).size > 0
        cookies_hash.each { |key, value|
          cookies_to_set_str += "#{key}=#{value}; "
        }
      end
    }
    headers_t["Cookie"] = cookies_to_set_str

    method_s = caller[0].to_s().scan(/:in `(.*)'/).join

    if arguments.size == 3
      data = arguments[1]
      if arguments[2].kind_of?(Hash)
        headers_t.merge!(arguments[2])
      end
    elsif arguments.size == 1 and arguments[0].kind_of?(Hash)
      if arguments[0][:data].nil?
        if arguments[0].keys.include?(:data)
          data = ""
        elsif arguments[0].keys.include?(:data_examples) and
              arguments[0][:data_examples].kind_of?(Array)
          data = arguments[0][:data_examples][0] #the first example by default
        else
          data = ""
        end
      else
        data = arguments[0][:data]
      end
      if arguments[0].include?(:headers)
        headers_t.merge!(arguments[0][:headers])
      end

      if headers_t["Content-Type"].to_s() == "" and headers_t["content-type"].to_s() == "" and
         headers_t[:"content-type"].to_s() == "" and headers_t[:"Content-Type"].to_s() == ""
        content_type_included = false
      elsif headers_t["content-type"].to_s() != ""
        content_type_included = true
        headers_t["Content-Type"] = headers_t["content-type"]
      elsif headers_t[:"content-type"].to_s() != ""
        content_type_included = true
        headers_t["Content-Type"] = headers_t[:"content-type"]
        headers_t.delete(:"content-type")
      elsif headers_t[:"Content-Type"].to_s() != ""
        content_type_included = true
        headers_t["Content-Type"] = headers_t[:"Content-Type"]
        headers_t.delete(:"Content-Type")
      elsif headers_t["Content-Type"].to_s() != ""
        content_type_included = true
      end
      if !content_type_included and (data.kind_of?(Hash) or data.kind_of?(Array))
        headers_t["Content-Type"] = "application/json"
        content_type_included = true
      end
      # to be backwards compatible since before was :values
      if arguments[0].include?(:values) and !arguments[0].include?(:values_for)
        arguments[0][:values_for] = arguments[0][:values]
      end

      if @values_for.size > 0
        if arguments[0][:values_for].nil?
          arguments[0][:values_for] = @values_for.dup
        else
          arguments[0][:values_for] = @values_for.merge(arguments[0][:values_for])
        end
      end

      if content_type_included and (!headers_t["Content-Type"][/text\/xml/].nil? or
                                    !headers_t["Content-Type"]["application/soap+xml"].nil? or
                                    !headers_t["Content-Type"][/application\/jxml/].nil?)
        if arguments[0].include?(:values_for)
          arguments[0][:values_for].each { |key, value|
            #todo: implement set_nested 
            data = NiceHttpUtils.set_value_xml_tag(key.to_s(), data, value.to_s(), true)
          }
        end
      elsif content_type_included and !headers_t["Content-Type"][/application\/json/].nil? and data.to_s() != ""
        require "json"
        if data.kind_of?(String)
          if arguments[0].include?(:values_for)
            arguments[0][:values_for].each { |key, value|
            #todo: implement set_nested
              data.gsub!(/"(#{key})":\s*"([^"]*)"/, '"\1": "' + value + '"')  # "key":"value"
              data.gsub!(/(#{key}):\s*"([^"]*)"/, '\1: "' + value + '"')  # key:"value"
              data.gsub!(/(#{key}):\s*'([^']*)'/, '\1: \'' + value + "'")  # key:'value'
              data.gsub!(/"(#{key})":\s*(\w+)/, '"\1": ' + value)  # "key":456
              data.gsub!(/(#{key}):\s*(\w+)/, '\1: ' + value)  # key:456
            }
          end
        elsif data.kind_of?(Hash)
          data.merge!(@defaults_request[:data]) if @defaults_request.key?(:data)
          #lambdas on data only supports on root of the hash
          data.each do |k, v|
            if v.is_a?(Proc)
              data[k] = v.call 
            end
          end
          if arguments[0].include?(:values_for)
            data = data.set_values(arguments[0][:values_for])
          end
          data = data.to_json()
        elsif data.kind_of?(Array)
          #todo: implement set_nested 
          data_arr = Array.new()
          data.each_with_index { |row, indx|
            if arguments[0].include?(:values_for) and (row.is_a?(Array) or row.is_a?(Hash))
              if arguments[0][:values_for].is_a?(Array)
                data_n = row.set_values(arguments[0][:values_for][indx])
              elsif arguments[0][:values_for].is_a?(Hash)
                data_n = row.set_values(arguments[0][:values_for])
              else
                @logger.fatal("Wrong format on request application/json, be sure is a Hash, Array of Hashes or JSON string. values_for needs to be an array or a hash.")
                return :error, :error, :error
              end
            else
              data_n = row
            end
            data_arr.push(data_n)
          }
          data = data_arr.to_json()
        else
          @logger.fatal("Wrong format on request application/json, be sure is a Hash, Array of Hashes or JSON string")
          return :error, :error, :error
        end
      elsif content_type_included and arguments[0].include?(:values_for)
        if arguments[0][:values_for].kind_of?(Hash) and arguments[0][:values_for].keys.size > 0
          if !headers_t.nil? and headers_t.kind_of?(Hash) and headers_t["Content-Type"] != "application/x-www-form-urlencoded" and headers_t["content-type"] != "application/x-www-form-urlencoded"
            @logger.warn(":values_for key given without a valid content-type or data for request. No values modified on the request")
          end
        end
      end
    elsif arguments.size == 1 and arguments[0].kind_of?(String)
      #path=arguments[0].to_s()
      data = ""
    else
      @logger.fatal("Invalid number of arguments or wrong arguments in #{method_s}")
      return :error, :error, :error
    end
    if headers_t.keys.include?("Content-Type") and !headers_t["Content-Type"]["multipart/form-data"].nil? and headers_t["Content-Type"] != ["multipart/form-data"] #only for the case raw multipart request
      encoding = "UTF-8"
      data_s = ""
    else
      encoding = data.to_s().scan(/encoding='(.*)'/i).join
      if encoding.to_s() == ""
        encoding = data.to_s().scan(/charset='(.*)'/i).join
      end
      if encoding.to_s() == "" and headers_t.include?("Content-Type")
        encoding = headers_t["Content-Type"].scan(/charset='?(.*)'?/i).join
        if encoding.to_s() == ""
          encoding = headers_t["Content-Type"].scan(/encoding='?(.*)'?/i).join
        end
      end

      begin
        data_s = JSON.pretty_generate(JSON.parse(data))
      rescue
        data_s = data
      end
      data_s = data_s.to_s().gsub("<", "&lt;")
    end
    if headers_t.keys.include?("Accept-Encoding")
      headers_t["Accept-Encoding"].gsub!("gzip", "") #removed so the response is in plain text
    end

    if data.to_s() != "" and encoding.to_s().upcase != "UTF-8" and encoding != ""
      data = data.to_s().encode(encoding, "UTF-8")
    end
    @request[:path] = path
    @request[:data] = data
    @request[:headers] = headers_t
    @request[:method] = method_s.upcase
    if arguments.size == 1 and arguments[0].kind_of?(Hash) and arguments[0].key?(:name)
      @request[:name] = arguments[0][:name]
    end
    self.class.request = @request
    headers_t.each do |k, v|
      # for lambdas
      if v.is_a?(Proc)
        headers_t[k] = v.call 
      end
    end
    @request[:headers] = headers_t
    self.class.request = @request

    if @debug or @prev_request[:path] != path or @prev_request[:headers] != headers_t or @prev_request[:data] != data
      show_headers_data = true
    else
      show_headers_data = false
    end

    @prev_request[:path] = path
    @prev_request[:data] = data
    @prev_request[:headers] = headers_t
    @prev_request[:method] = method_s.upcase
    if arguments.size == 1 and arguments[0].kind_of?(Hash) and arguments[0].key?(:name)
      @prev_request[:name] = arguments[0][:name]
    end

    headers_ts = ""

    if @log_headers == :none
      @logger.info "No header values since option log_headers is set to :none"
      headers_t.each { |key, val| headers_ts += key.to_s + ":" + "''" + ", " }
    elsif @log_headers == :partial
      @logger.info "Just the last 10 characters on header values since option log_headers is set to :partial"
      headers_t.each { |key, val| 
        if val.to_s.size>10
          headers_ts += key.to_s + ": ..." + (val.to_s[-10..-1] || val.to_s) + ", " 
        else
          headers_ts += key.to_s + ":" + (val.to_s[-10..-1] || val.to_s) + ", " 
        end
      }
    else
      headers_t.each { |key, val| headers_ts += key.to_s + ":" + val.to_s() + ", " }
    end

    message = "\n\n#{"- " * 25}\n"
    if arguments.size == 1 and arguments[0].kind_of?(Hash) and arguments[0].key?(:name)
      message += "#{method_s.upcase} Request: #{arguments[0][:name]}"
    else
      message += "#{method_s.upcase} Request"
    end
    message += "\n path: " + path.to_s() + "\n"
    if show_headers_data
      message += " headers: {" + headers_ts.to_s() + "}\n"
      message += " data: " + data_s.to_s() + "\n"
      message = @message_server + "\n" + message
    else
      message += " Same#{" headers" if headers_t != {}}#{" and" if headers_t != {} and data.to_s != ""}#{" data" if data.to_s != ""} as in the previous request."
    end
    if path.to_s().scan(/^https?:\/\//).size > 0 and path.to_s().scan(/^https?:\/\/#{@host}/).size == 0
      # the path is for another server than the current
      # todo: identify if it is better to log the request, or if it is done later
    else
      self.class.last_request = message
      @logger.info(message)
    end

    return path, data, headers_t
  rescue Exception => stack
    @logger.fatal(stack)
    @logger.fatal("manage_request Error on method #{method_s} . path:#{path.to_s()}. data:#{data.to_s()}. headers:#{headers_t.to_s()}")
    return :error
  end
end