void ProcessEFTResponse(EFTResponse response) { try { switch (response) { case null: Log(LogLevel.Error, tr => tr.Set($"Unable to handle null param {nameof(response)}")); break; case SetDialogResponse r: if (currentRequest is SetDialogRequest) { gotResponse = true; hideDialogEvent.Set(); } break; case EFTReceiptResponse r: SendReceiptAcknowledgement(); Log(LogLevel.Info, tr => tr.Set($"IsPrePrint={((EFTReceiptResponse)response).IsPrePrint}")); if (r.IsPrePrint == false) { FireClientResponseEvent(nameof(OnReceipt), OnReceipt, new EFTEventArgs <EFTReceiptResponse>(r)); } break; case EFTDisplayResponse r: //DialogUIHandler.HandleDisplayResponse(r); FireClientResponseEvent(nameof(OnDisplay), OnDisplay, new EFTEventArgs <EFTDisplayResponse>(r)); break; case EFTLogonResponse r: FireClientResponseEvent(nameof(OnLogon), OnLogon, new EFTEventArgs <EFTLogonResponse>(r)); break; case EFTCloudLogonResponse r: FireClientResponseEvent(nameof(OnCloudLogon), OnCloudLogon, new EFTEventArgs <EFTCloudLogonResponse>(r)); break; case EFTCloudPairResponse r: FireClientResponseEvent(nameof(OnCloudPair), OnCloudPair, new EFTEventArgs <EFTCloudPairResponse>(r)); break; case EFTTransactionResponse r: FireClientResponseEvent(nameof(OnTransaction), OnTransaction, new EFTEventArgs <EFTTransactionResponse>(r)); break; case EFTGetLastTransactionResponse r: FireClientResponseEvent(nameof(OnGetLastTransaction), OnGetLastTransaction, new EFTEventArgs <EFTGetLastTransactionResponse>(r)); break; case EFTReprintReceiptResponse r: FireClientResponseEvent(nameof(OnDuplicateReceipt), OnDuplicateReceipt, new EFTEventArgs <EFTReprintReceiptResponse>(r)); break; case EFTControlPanelResponse r: FireClientResponseEvent(nameof(OnDisplayControlPanel), OnDisplayControlPanel, new EFTEventArgs <EFTControlPanelResponse>(r)); break; case EFTSettlementResponse r: FireClientResponseEvent(nameof(OnSettlement), OnSettlement, new EFTEventArgs <EFTSettlementResponse>(r)); break; case EFTStatusResponse r: FireClientResponseEvent(nameof(OnStatus), OnStatus, new EFTEventArgs <EFTStatusResponse>(r)); break; case EFTQueryCardResponse r: FireClientResponseEvent(nameof(OnQueryCard), OnQueryCard, new EFTEventArgs <EFTQueryCardResponse>(r)); break; case EFTChequeAuthResponse r: FireClientResponseEvent(nameof(OnChequeAuth), OnChequeAuth, new EFTEventArgs <EFTChequeAuthResponse>(r)); break; case EFTGetPasswordResponse r: FireClientResponseEvent(nameof(OnGetPassword), OnGetPassword, new EFTEventArgs <EFTGetPasswordResponse>(r)); break; case EFTSlaveResponse r: FireClientResponseEvent(nameof(OnSlave), OnSlave, new EFTEventArgs <EFTSlaveResponse>(r)); break; case EFTConfigureMerchantResponse r: FireClientResponseEvent(nameof(OnConfigMerchant), OnConfigMerchant, new EFTEventArgs <EFTConfigureMerchantResponse>(r)); break; case EFTClientListResponse r: FireClientResponseEvent(nameof(OnClientList), OnClientList, new EFTEventArgs <EFTClientListResponse>(r)); break; default: Log(LogLevel.Error, tr => tr.Set($"Unknown response type", response)); break; } } catch (Exception Ex) { Log(LogLevel.Error, tr => tr.Set($"Unhandled error in {nameof(ProcessEFTResponse)}", Ex)); } }
bool ReceiveEFTResponse(byte[] data) { // Clear the receive buffer if 5 seconds has lapsed since the last message var tc = System.Environment.TickCount; if (tc - recvTickCount > 5000) { Log(LogLevel.Debug, tr => tr.Set($"Data is being cleared from the buffer due to a timeout. Content {recvBuf.ToString()}")); recvBuf = ""; } recvTickCount = System.Environment.TickCount; // Append receive data to our buffer recvBuf += System.Text.Encoding.ASCII.GetString(data, 0, data.Length); // Keep parsing until no more characters try { int index = 0; while (index < recvBuf.Length) { // Look for start character if (recvBuf[index] == (byte)'#') { // Check that we have enough bytes to validate length, if not wait for more if (recvBuf.Length < index + 5) { recvBuf = recvBuf.Substring(index); break; } // We have enough bytes to check for length index++; // Try to get the length of the new message. If it's not a valid length // we might have some corrupt data, keep checking for a valid message if (!int.TryParse(recvBuf.Substring(index, 4), out int length) || length <= 5) { continue; } // We have a valid length index += 4; // If our buffer doesn't contain enough data, wait for more if (recvBuf.Length < index + length - 5) { recvBuf = recvBuf.Substring(index - 5); continue; } // We have a valid response var response = recvBuf.Substring(index, length - 5); FireOnTcpReceive(response); // Process the response EFTResponse eftResponse = null; try { eftResponse = _parser.StringToEFTResponse(response); ProcessEFTResponse(eftResponse); if (eftResponse.GetType() == _currentStartTxnRequest?.GetPairedResponseType()) { dialogUIHandler.HandleCloseDisplay(); } } catch (ArgumentException argumentException) { Log(LogLevel.Error, tr => tr.Set("Error parsing response string", argumentException)); } index += length - 5; } // Clear our buffer if we are all done if (index == recvBuf.Length) { recvBuf = ""; } } } catch (Exception ex) { // Fail gracefully. FireOnSocketFailEvent(EFTClientIPErrorType.Client_ParseError, ex.Message); Log(LogLevel.Error, tr => tr.Set($"Exception (ReceiveEFTResponse): {ex.Message}", ex)); return(false); } return(true); }
/// <summary> /// Retrieves the next EFT response from the client /// </summary> /// <param name="cancellationToken">The token to monitor for cancellation requests</param> /// <returns> An EFTResponse if one could be read, otherwise null </returns> /// <exception cref="ConnectionException">The socket is closed.</exception> /// <exception cref="TaskCanceledException">The task was cancelled by cancellationToken</exception> public async Task <EFTResponse> ReadResponseAsync(System.Threading.CancellationToken cancellationToken) { // Clear the receive buffer if we have been waiting for more than // 5 seconds for the remaining parts of the message to arrive var tc = System.Environment.TickCount; if (_recvBufWaiting && tc - _recvTickCount > 5000 && _recvBuf.Length > 0) { Log(LogLevel.Debug, tr => tr.Set($"Data is being cleared from the buffer due to a timeout. Content {_recvBuf.ToString()}")); _recvBufWaiting = false; _recvBuf.Clear(); } // Only try to read more data if our receive buffer is empty Log(LogLevel.Debug, tr => tr.Set($"ReadResponseAsync() _recvBuf.Length={_recvBuf.Length} _recvBufWaiting={_recvBufWaiting}")); if (_recvBuf.Length == 0 || _recvBufWaiting == true) { var bytesRead = 0; try { bytesRead = await _clientStream.ReadResponseAsync(_buffer, cancellationToken); _recvTickCount = System.Environment.TickCount; } catch (TaskCanceledException e) { Log(LogLevel.Debug, tr => tr.Set($"Socket read cancelled", e)); throw e; } catch (Exception e) { Log(LogLevel.Error, tr => tr.Set($"An error occured sending the request", e)); Disconnect(); throw new ConnectionException(e.Message, e.InnerException); } // Check for socket closed if (bytesRead <= 0) { Log(LogLevel.Error, tr => tr.Set("Recv 0 bytes. Socket closed")); Disconnect(); throw new ConnectionException("Socket closed."); } var msgString = DirectEncoding.DIRECT.GetString(_buffer, 0, bytesRead); Log(LogLevel.Info, tr => tr.Set($"Rx {msgString}")); // Append receive data to our buffer _recvBuf.Append(msgString); } // Keep parsing until no more characters try { bool isXMLFormatted = false; int index = 0; while (index < _recvBuf.Length) { // If the current char isn't #/& then keep cycling through the message until we find one if (_recvBuf[index] != (byte)'#' && !(isXMLFormatted = _recvBuf[index] == (byte)'&')) { index++; continue; } // We have a valid start char, check for length. index++; int lengthSize = isXMLFormatted ? 6 : 4; // Check that we have enough bytes to validate length, if not wait for more if (_recvBuf.Length < index + lengthSize) { Log(LogLevel.Debug, tr => tr.Set($"Unable to validate message header. Waiting for more data. Length:{_recvBuf.Length} Required:{index + lengthSize + 1}")); _recvBufWaiting = true; break; } // Try to get the length of the new message. If it's not a valid length // we might have some corrupt data, keep checking for a valid message var lengthStr = _recvBuf.Substring(index, lengthSize); if (!int.TryParse(lengthStr, out int length) || length <= (lengthSize + 1)) { Log(LogLevel.Error, tr => tr.Set($"Invalid length. Content:{lengthStr}")); continue; } // We have a valid length index += lengthSize; // If the defined message length is > our current buffer size, wait for more data if (_recvBuf.Length < index + length - (lengthSize + 1)) { Log(LogLevel.Debug, tr => tr.Set($"Buffer is less than the indicate length. Waiting for more data. Length:{_recvBuf.Length < index} Required:{length - (lengthSize + 1)}")); _recvBuf = _recvBuf.Remove(0, index - (lengthSize + 1)); _recvBufWaiting = true; break; } // We have a valid response _recvBufWaiting = false; var response = _recvBuf.Substring(index, length - (lengthSize + 1)); index += (length - (lengthSize + 1)); _recvBuf.Remove(0, index); // Process the response EFTResponse eftResponse = null; try { //if (response.Equals("3M") || response.Equals("3C")) // FOR PREPRINT TIMEOUT //if (response.Substring(0,2).Equals("3R")) // FOR PRINT TIMEOUT // System.Threading.Thread.Sleep(61000); eftResponse = (isXMLFormatted) ? _parser.XMLStringToEFTResponse(response) : _parser.StringToEFTResponse(response); // If we have an EFTResponse we need to return it. if (eftResponse != null) { // Print requests need a response if (eftResponse is EFTReceiptResponse) { await WriteRequestAsync(new EFTReceiptRequest()); } //DialogUIHandler needs to be notified of display messages else if (eftResponse is EFTDisplayResponse && DialogUIHandlerAsync != null) { await DialogUIHandlerAsync.HandleDisplayResponseAsync(eftResponse as EFTDisplayResponse); } else if (eftResponse.GetType() == _currentStartTxnRequest?.GetPairedResponseType() && DialogUIHandlerAsync != null) { await DialogUIHandlerAsync.HandleCloseDisplayAsync(); } return(eftResponse); } } catch (Exception e) { Log(LogLevel.Error, tr => tr.Set("Error parsing response string", e)); } } // Clear our buffer if we are all done (this shouldn't happen) if (index == _recvBuf.Length) { _recvBufWaiting = false; _recvBuf.Clear(); } } catch (Exception ex) { Log(LogLevel.Error, tr => tr.Set($"Exception (ReceiveEFTResponse): {ex.Message}", ex)); throw ex; } return(null); }