lib/vaas/vaas_main.rb in vaas-1.0.1 vs lib/vaas/vaas_main.rb in vaas-2.0.0

- old
+ new

@@ -12,128 +12,177 @@ require_relative 'vaas_errors' module VAAS class VaasMain - attr_accessor :session_id, :websocket, :url - - def initialize(url="wss://gateway-vaas.gdatasecurity.de") + def initialize(url = "wss://gateway-vaas.gdatasecurity.de", timeout = 600) @url = url + @timeout = timeout + @requests = {} end def connect(token) # connect to endpoint - endpoint = Async::HTTP::Endpoint.parse(url, alpn_protocols: Async::HTTP::Protocol::HTTP1.names) - self.websocket = Async::WebSocket::Client.connect(endpoint) + endpoint = Async::HTTP::Endpoint.parse(@url, alpn_protocols: Async::HTTP::Protocol::HTTP1.names) + @websocket = Async::WebSocket::Client.connect(endpoint) - # send authentication request - auth_request = JSON.generate({:kind => "AuthRequest", :token => token}) - websocket.write(auth_request) + read_messages + keep_alive - # receive authentication message - while message = websocket.read - message = JSON.parse(message) - if message['success'] == true - self.session_id = message['session_id'] - break - else - raise VaasAuthenticationError + # send authentication request + auth_task = Async do |task| + task.with_timeout(@timeout) do + @auth_notification = Async::Notification.new + auth_request = JSON.generate({:kind => "AuthRequest", :token => token}) + @websocket.write(auth_request) + @websocket.flush + @auth_notification.wait + rescue Async::TimeoutError + close + raise VaasTimeoutError end end - end - def get_authenticated_websocket - raise VaasInvalidStateError unless websocket - raise VaasInvalidStateError, "connect() was not awaited" unless session_id - raise VaasConnectionClosedError if websocket.closed? - websocket + message = auth_task.wait + raise VaasAuthenticationError if message['success'] == false + @session_id = message['session_id'] end - def close - websocket&.close + def keep_alive + @keep_alive_task = Async do + until @websocket.closed? + sleep 10 + @websocket.send_ping + @websocket.flush + end + end end - def __for_sha256(sha256, guid) - # send verdict request with sha256 - websocket = get_authenticated_websocket - guid = guid || SecureRandom.uuid.to_s - verdict_request = JSON.generate({:kind => "VerdictRequest", - :session_id => session_id, - :sha256 => sha256, - :guid => guid}) - websocket.write(verdict_request) - - # receive verdict message - while message = websocket.read - message = JSON.parse(message) - if message['kind'] == "VerdictResponse" - return message + def read_messages + @read_task = Async do |task| + task.with_timeout(@timeout) do + while message = @websocket.read + message = JSON.parse(message) + if message['kind'] == "AuthResponse" + @auth_notification.signal(message) + elsif message['kind'] == "VerdictResponse" + @requests[message['guid']].signal(message) + end + end + rescue Async::TimeoutError + close + raise VaasTimeoutError end end end - def for_sha256(sha256, guid = nil) - response = __for_sha256(sha256, guid) - VaasVerdict.new(response) + def get_authenticated_websocket + raise VaasInvalidStateError unless @websocket + raise VaasInvalidStateError, "connect() was not awaited" unless @session_id + raise VaasConnectionClosedError if @websocket.closed? + @websocket end - def for_file(path, guid = nil) - # get sha256 of file and send verdict request - sha256 = Digest::SHA256.file(path).hexdigest - response = __for_sha256(sha256, guid) + def close + @keep_alive_task&.stop + @read_task&.stop + @websocket&.close + end - # upload file if verdict is unknown - if response['verdict'] == 'Unknown' - upload(response, path) - else - return VaasVerdict.new(response) + def for_sha256(sha256) + Async do |task| + task.with_timeout(@timeout) do + verdict_notification = Async::Notification.new + guid = SecureRandom.uuid.to_s + websocket = get_authenticated_websocket + verdict_request = JSON.generate({:kind => "VerdictRequest", + :session_id => @session_id, + :sha256 => sha256, + :guid => guid}) + @requests[guid] = verdict_notification + websocket.write(verdict_request) + websocket.flush + VaasVerdict.new(verdict_notification.wait) + rescue Async::TimeoutError + close + raise VaasTimeoutError + end end + end - # receive verdict message of uploaded file - while message = websocket.read - message = JSON.parse(message) - if message['kind'] == "VerdictResponse" && message['verdict'] != "Unknown" - return VaasVerdict.new(message) + def for_url(url) + Async do |task| + task.with_timeout(@timeout) do + verdict_notification = Async::Notification.new + guid = SecureRandom.uuid.to_s + websocket = get_authenticated_websocket + verdict_request = JSON.generate({:kind => "VerdictRequestForUrl", + :session_id => @session_id, + :url => url, + :guid => guid}) + @requests[guid] = verdict_notification + websocket.write(verdict_request) + websocket.flush + VaasVerdict.new(verdict_notification.wait) + rescue Async::TimeoutError + close + raise VaasTimeoutError end end end - def for_url(url, guid = nil) - #send verdict request with url - websocket = get_authenticated_websocket - guid = guid || SecureRandom.uuid.to_s - url = URI(url).to_s - verdict_request = JSON.generate({:kind => "VerdictRequestForUrl", - :session_id => session_id, - :guid => guid, - :url => url}) - websocket.write(verdict_request) + def for_file(path) + Async do |task| + task.with_timeout(@timeout) do + sha256 = Digest::SHA256.file(path).hexdigest + verdict_notification = Async::Notification.new + guid = SecureRandom.uuid.to_s + websocket = get_authenticated_websocket + verdict_request = JSON.generate({:kind => "VerdictRequest", + :session_id => @session_id, + :sha256 => sha256, + :guid => guid}) + @requests[guid] = verdict_notification + websocket.write(verdict_request) + websocket.flush + message = verdict_notification.wait - # receive verdict message - while message = websocket.read - message = JSON.parse(message) - if message['kind'] == "VerdictResponse" - return VaasVerdict.new(message) + if message['verdict'] == "Unknown" + upload_notification = Async::Notification.new + @requests[guid] = upload_notification + upload(message, path) + VaasVerdict.new(upload_notification.wait) + else + VaasVerdict.new(message) + end + rescue Async::TimeoutError + close + raise VaasTimeoutError end end end - def upload (message, path) - token = message['upload_token'] - url = message['url'] + def upload(message, path) + Async do |task| + task.with_timeout(@timeout) do + token = message['upload_token'] + url = message['url'] - Async do - client = Async::HTTP::Internet.new + client = Async::HTTP::Internet.new - header = [['authorization', token]] - body = Protocol::HTTP::Body::File.open(File.join(path)) + header = [['authorization', token]] + body = Protocol::HTTP::Body::File.open(File.join(path)) - client.put(url, header, body).read + response = client.put(url, header, body) + response.read - rescue => e - raise VaasUploadError, e - ensure - client&.close + raise VaasUploadError, "Upload failed with code: #{response.status}" if response.status != 200 + rescue Async::TimeoutError + close + raise VaasTimeoutError, "Upload timed out" + ensure + client&.close + end end end end end