lib/hostfunc.rb in streamdal-0.0.1 vs lib/hostfunc.rb in streamdal-0.0.2

- old
+ new

@@ -1,6 +1,8 @@ -require "steps/sp_steps_kv_pb" +require 'steps/sp_steps_kv_pb' +require 'sp_wsm_pb' +require 'steps/sp_steps_httprequest_pb' module Streamdal class HostFunc ## @@ -12,11 +14,11 @@ ## # kv_exists is a host function that is used to check if a key exists in the KV store def kv_exists(caller, ptr, len) - data = caller.export("memory").to_memory.read(ptr, len) + data = caller.export('memory').to_memory.read(ptr, len) # Read request from memory and decode into HttpRequest req = Streamdal::Protos::KVStep.decode(data) exists = @kv.exists(req.key) @@ -33,70 +35,100 @@ end ## # http_request performs a http request on behalf of a wasm module since WASI cannot talk sockets def http_request(caller, ptr, len) - data = caller.export("memory").to_memory.read(ptr, len) + data = caller.export('memory').to_memory.read(ptr, len) # Read request from memory and decode into HttpRequest - req = Streamdal::Protos::HttpRequest.decode(data) + req = Streamdal::Protos::WASMRequest.decode(data) + begin + req_body = self._get_request_body_for_mode(req) + rescue => e + return self._http_request_response(caller, 400, e.to_s, {}) + end + # Attempt to make HTTP request # On error, return a mock 400 response with the error as the body begin - response = _make_http_request(req) + response = _make_http_request(req.step.http_request.request, req_body) rescue => e - wasm_resp = Streamdal::Protos::HttpResponse.new - wasm_resp.code = 400 - wasm_resp.body = "Unable to execute HTTP request: #{e}" - return wasm_resp + return self._http_request_response(caller, 400, "Unable to execute HTTP request: #{e}", {}) end - # Successful request, build the proto from the httparty response + self._http_request_response(caller, response.code, response.body, response.headers) + end + + private + + def _http_request_response(caller, code, body, headers) wasm_resp = Streamdal::Protos::HttpResponse.new - wasm_resp.code = response.code - wasm_resp.body = response.body + wasm_resp.code = code + wasm_resp.body = body wasm_resp.headers = Google::Protobuf::Map.new(:string, :string, {}) # Headers can have multiple values, but we just want a map[string]string here for simplicity # The client can pase by the delimiter ";" if needed. - response.headers.each do |k, values| - wasm_resp.headers[k] = values.kind_of?(Array) ? values.join("; ") : values + headers.each do |k, values| + wasm_resp.headers[k] = values.is_a?(Array) ? values.join('; ') : values end # Write the HttpResponse proto message to WASM memory # The .wasm module will read/decode this data internally write_to_memory(caller, wasm_resp) end - private + def _get_request_body_for_mode(req) + http_req = req.step.http_request.request + case http_req.body_mode + when :HTTP_REQUEST_BODY_MODE_INTER_STEP_RESULT + raise 'Inter step result is empty' if req.inter_step_result.nil? + + detective_res = req.inter_step_result.detective_result + + raise 'Detective result is empty' if detective_res.nil? + + # Wipe values to prevent PII from being leaked + detective_res.matches.each { |step_res| + step_res.value = '' + } + + req.inter_step_result.to_json + when :HTTP_REQUEST_BODY_MODE_STATIC + http_req.body + else + raise 'invalid http request body mode' + end + end + ## # Performs an http request - def _make_http_request(req) + def _make_http_request(req, body) if req.nil? - raise "req is required" + raise 'req is required' end options = { - headers: { "Content-Type": "application/json", }, + headers: { "Content-Type": 'application/json', }, } req.headers.each { |key, value| options.headers[key] = value } case req.to_h[:method] when :HTTP_REQUEST_METHOD_GET return HTTParty.get(req.url) when :HTTP_REQUEST_METHOD_POST - options.body = req.body + options.body = body return HTTParty.post(req.url, options) when :HTTP_REQUEST_METHOD_PUT - options.body = req.body + options.body = body return HTTParty.put(req.url, options) when :HTTP_REQUEST_METHOD_DELETE return HTTParty.delete(req.url) when :HTTP_REQUEST_METHOD_PATCH - options.body = req.body + options.body = body return HTTParty.patch(req.url, options) when :HTTP_REQUEST_METHOD_HEAD return HTTParty.head(req.url) when :HTTP_REQUEST_METHOD_OPTIONS return HTTParty.options(req.url) \ No newline at end of file