Skip to content

Commit

Permalink
#186374352 : Support multipart outgoing (#53)
Browse files Browse the repository at this point in the history
Support Multipart outgoing body
  • Loading branch information
xinghengwang authored Nov 1, 2023
1 parent a399938 commit 866063d
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 32 deletions.
19 changes: 19 additions & 0 deletions lib/moesif_rack/moesif_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,23 @@ def format_replacement_body(replacement_body, original_body)
log_debug 'failed to convert replacement body ' + e.to_s
[replacement_body.to_json.to_s]
end

def parse_multipart(multipart_form_data, content_type)
log_debug("try to parse multiple part #{content_type}")

sanitized_multipart_form_data = multipart_form_data.gsub(/\r?\n/, "\r\n")

io = StringIO.new(sanitized_multipart_form_data)
tempfile = Rack::Multipart::Parser::TEMPFILE_FACTORY
bufsize = Rack::Multipart::Parser::BUFSIZE
query_parser = Rack::Utils.default_query_parser
result = Rack::Multipart::Parser.parse(io, sanitized_multipart_form_data.length, content_type, tempfile, bufsize,
query_parser)

log_debug('multipart parse result')
log_debug(result.inspect)

# this is a hash should be treated as JSON down the road.
result.params
end
end
25 changes: 3 additions & 22 deletions lib/moesif_rack/moesif_middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def initialize(app, options = {})
@app = app
raise 'application_id required for Moesif Middleware' unless options['application_id']

@api_client = MoesifApi::MoesifAPIClient.new(options['application_id'], 'moesif-rack/2.1.1')
@api_client = MoesifApi::MoesifAPIClient.new(options['application_id'], 'moesif-rack/2.2.1')
@api_controller = @api_client.api

@api_version = options['api_version']
Expand Down Expand Up @@ -59,7 +59,7 @@ def initialize(app, options = {})

@moesif_helpers.log_debug 'Start Capturing outgoing requests'
require_relative '../../moesif_capture_outgoing/httplog'
MoesifCaptureOutgoing.start_capture_outgoing(options, @app_config, @events_queue)
MoesifCaptureOutgoing.start_capture_outgoing(options, @app_config, @events_queue, @moesif_helpers)
end

def update_user(user_profile)
Expand Down Expand Up @@ -100,25 +100,6 @@ def @moesif_helpers.log_debug(message)
puts("#{Time.now} [Moesif Middleware] PID #{Process.pid} TID #{Thread.current.object_id} #{message}")
end

def parse_multipart(multipart_form_data, content_type)
@moesif_helpers.log_debug("try to parse multiple part #{content_type}")

sanitized_multipart_form_data = multipart_form_data.gsub(/\r?\n/, "\r\n")

io = StringIO.new(sanitized_multipart_form_data)
tempfile = Rack::Multipart::Parser::TEMPFILE_FACTORY
bufsize = Rack::Multipart::Parser::BUFSIZE
query_parser = Rack::Utils.default_query_parser
result = Rack::Multipart::Parser.parse(io, sanitized_multipart_form_data.length, content_type, tempfile, bufsize,
query_parser)

@moesif_helpers.log_debug('multipart parse result')
@moesif_helpers.log_debug(result.inspect)

# this is a hash shold be treated as JSON down the road.
result.params
end

def parse_body(body, headers)
begin
if body.instance_of?(Hash) || body.instance_of?(Array)
Expand All @@ -128,7 +109,7 @@ def parse_body(body, headers)
parsed_body = JSON.parse(body)
transfer_encoding = 'json'
elsif headers.key?('content-type') && (headers['content-type'].downcase.include? 'multipart/form-data')
parsed_body = parse_multipart(body, headers['content-type'])
parsed_body = @moesif_helpers.parse_multipart(body, headers['content-type'])
transfer_encoding = 'json'
elsif headers.key?('content-encoding') && (headers['content-encoding'].downcase.include? 'gzip')
uncompressed_string = decompress_body(body)
Expand Down
9 changes: 7 additions & 2 deletions moesif_capture_outgoing/httplog/adapters/net_http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ class HTTP
def request(request, body = nil, &block)
# Request Start Time
request_time = Time.now.utc.iso8601(3)

# URL
url = "https://#{@address}#{request.path}"

if (not request.body_stream.nil?) && MoesifCaptureOutgoing.should_capture_body
req_body_from_stream = request.body_stream.read
request.body_stream.rewind
end

# Response
@response = orig_request(request, body, &block)

# Response Time
response_time = Time.now.utc.iso8601(3)

# Log Event to Moesif
MoesifCaptureOutgoing.call(url, request, request_time, @response, response_time) if started?
body_from_req_call = body
MoesifCaptureOutgoing.call(url, request, request_time, @response, response_time, body_from_req_call, req_body_from_stream) if started?

@response
end
Expand Down
32 changes: 25 additions & 7 deletions moesif_capture_outgoing/httplog/http_log.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

module MoesifCaptureOutgoing
class << self
def start_capture_outgoing(options, app_config_manager, events_queue)
def start_capture_outgoing(options, app_config_manager, events_queue, moesif_helpers)
@moesif_options = options
raise 'application_id required for Moesif Middleware' unless @moesif_options['application_id']

Expand All @@ -28,10 +28,15 @@ def start_capture_outgoing(options, app_config_manager, events_queue)
@events_queue = events_queue
@sampling_percentage = 100
@last_updated_time = Time.now.utc
@moesif_helpers = moesif_helpers
end

def call(url, request, request_time, response, response_time)
send_moesif_event(url, request, request_time, response, response_time)
def should_capture_body
@moesif_options.nil? ? false : @moesif_options['capture_outgoing_requests'] && @log_body_outgoing
end

def call(url, request, request_time, response, response_time, body_from_req_call, req_body_from_stream)
send_moesif_event(url, request, request_time, response, response_time, body_from_req_call, req_body_from_stream)
end

def get_response_body(response)
Expand All @@ -44,18 +49,30 @@ def transform_response_code(response_code_name)
Rack::Utils::HTTP_STATUS_CODES.detect { |_k, v| v.to_s.casecmp(response_code_name.to_s).zero? }.first
end

def send_moesif_event(url, request, request_time, response, response_time)
def send_moesif_event(url, request, request_time, response, response_time, body_from_req_call, req_body_from_stream)
if url.downcase.include? 'moesif'
puts 'Skip sending as it is moesif Event' if @debug
else
response.code = transform_response_code(response.code) if response.code.is_a?(Symbol)

# Request headers
req_headers = request.each_header.collect.to_h
req_content_type = req_headers['content-type'].nil? ? req_headers['Content-Type'] : req_headers['content-type']

# Request Body
req_body_string = request.body.nil? || request.body.empty? ? nil : request.body
req_body_string = request.body.nil? || request.body.empty? ? body_from_req_call : request.body
req_body_transfer_encoding = nil
req_body = nil

if @log_body_outgoing && (req_body_string && req_body_string.length != 0)
if @log_body_outgoing && (not req_content_type.nil?) && (req_content_type.downcase.include? 'multipart/form-data')
@moesif_helpers.log_debug 'outgoing request is multipart, parsing req_body_from_stream'
begin
req_body = @moesif_helpers.parse_multipart(req_body_from_stream, req_content_type)
rescue StandardError => e
@moesif_helpers.log_debug 'outgoing request is multipart, but failed to process req_body_from_stream: ' + req_body_from_stream.to_s + e.to_s
req_body = nil
end
elsif @log_body_outgoing && (req_body_string && req_body_string.length != 0)
begin
req_body = JSON.parse(req_body_string)
rescue StandardError
Expand Down Expand Up @@ -83,8 +100,9 @@ def send_moesif_event(url, request, request_time, response, response_time)
event_req.time = request_time
event_req.uri = url
event_req.verb = request.method.to_s.upcase
event_req.headers = request.each_header.collect.to_h
event_req.headers = req_headers
event_req.api_version = nil

event_req.body = req_body
event_req.transfer_encoding = req_body_transfer_encoding

Expand Down
2 changes: 1 addition & 1 deletion moesif_rack.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'moesif_rack'
s.version = '2.1.1'
s.version = '2.2.1'
s.summary = 'moesif_rack'
s.description = 'Rack/Rails middleware to log API calls to Moesif API analytics and monitoring'
s.authors = ['Moesif, Inc', 'Xing Wang']
Expand Down

0 comments on commit 866063d

Please sign in to comment.