All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gatling.http.action.sse.fsm.WhenPerformingCheck.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/*
 * Copyright 2011-2019 GatlingCorp (https://gatling.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.gatling.http.action.sse.fsm

import java.util.{ HashMap => JHashMap }

import io.gatling.commons.stats.{ KO, OK }
import io.gatling.commons.validation.{ Failure, Success }
import io.gatling.core.action.Action
import io.gatling.core.check.Check
import io.gatling.core.session.Session
import io.gatling.http.check.sse.{ SseCheck, SseMessageCheckSequence }

trait WhenPerformingCheck { this: SseActor =>

  when(PerformingCheck) {

    case Event(Timeout(timeoutId), PerformingCheckData(stream, currentCheck, _, checkSequenceStart, currentTimeoutId, _, session, next)) =>
      if (timeoutId == currentTimeoutId) {
        logger.debug(s"Check timeout $timeoutId")
        // check timeout
        // fail check, send next and goto Idle
        val errorMessage = s"Check ${currentCheck.name} timeout"
        val newSession = logResponse(session, currentCheck.name, checkSequenceStart, clock.nowMillis, KO, None, Some(errorMessage))
        val nextAction = next match {
          case Left(n) =>
            logger.debug("Check timeout, failing it and performing next action")
            n
          case Right(sendFrame) =>
            // logging crash
            logger.debug("Check timeout while trying to reconnect, failing pending send message and performing next action")
            statsEngine.logCrash(newSession, sendFrame.actionName, s"Couldn't reconnect: $errorMessage")
            sendFrame.next
        }
        nextAction ! newSession
        goto(Idle) using IdleData(newSession, stream)
      } else {
        logger.debug(s"Out-of-band timeout $timeoutId")
        // out-of-band timeoutId, ignore
        stay()
      }

    case Event(SseReceived(message, timestamp), data: PerformingCheckData) =>
      tryApplyingChecks(message, timestamp, data.currentCheck.matchConditions, data.currentCheck.checks, data)

    case Event(SseStreamClosed(_), PerformingCheckData(_, currentCheck, _, checkSequenceStart, _, _, session, next)) =>
      // unexpected close, fail check
      logger.debug("WebSocket remotely closed while waiting for checks")
      cancelTimeout()
      handleSseCheckCrash(currentCheck.name, session, next, checkSequenceStart, None, "Socket closed")

    case Event(SseStreamCrashed(t, _), PerformingCheckData(_, currentCheck, _, checkSequenceStart, _, _, session, next)) =>
      // crash, fail check
      logger.debug("WebSocket crashed while waiting for checks")
      cancelTimeout()
      handleSseCheckCrash(currentCheck.name, session, next, checkSequenceStart, None, t.getMessage)
  }

  private def handleSseCheckCrash(checkName: String, session: Session, next: Either[Action, SetCheck], checkSequenceStart: Long, code: Option[String], errorMessage: String): State = {
    val fullMessage = s"WebSocket crashed while waiting for check: $errorMessage"

    val newSession = logResponse(session, checkName, checkSequenceStart, clock.nowMillis, KO, code, Some(fullMessage))
    val nextAction = next match {
      case Left(n) =>
        // failed to connect
        logger.debug("WebSocket crashed, performing next action")
        n

      case Right(sendTextMessage) =>
        // failed to reconnect, logging crash
        logger.debug("WebSocket crashed while trying to reconnect, failing pending send message and performing next action")
        statsEngine.logCrash(newSession, sendTextMessage.actionName, s"Couldn't reconnect: $errorMessage")
        sendTextMessage.next
    }
    nextAction ! newSession
    goto(Crashed) using CrashedData(Some(errorMessage))
  }

  private def tryApplyingChecks(message: String, timestamp: Long, matchConditions: List[SseCheck], checks: List[SseCheck], data: PerformingCheckData): State = {

    import data._

    // cache is used for both matching and checking
    val preparedCache: JHashMap[Any, Any] = new JHashMap(2)

    // if matchConditions isEmpty, all messages are considered to be matching
    val messageMatches = matchConditions.forall(_.check(message, session, preparedCache).isInstanceOf[Success[_]])
    if (messageMatches) {
      logger.debug(s"Received matching message $message")
      cancelTimeout() // note, we might already have a Timeout in the mailbox, hence the currentTimeoutId check
      // matching message, apply checks
      val (sessionWithCheckUpdate, checkError) = Check.check(message, session, checks, preparedCache)

      checkError match {
        case Some(Failure(errorMessage)) =>
          logger.debug("Check failure")
          val newSession = logResponse(sessionWithCheckUpdate, currentCheck.name, checkSequenceStart, timestamp, KO, None, Some(errorMessage))

          val nextAction = next match {
            case Left(n) =>
              logger.debug("Check failed, performing next action")
              n
            case Right(sendMessage) =>
              // failed to reconnect, logging crash
              logger.debug("Check failed while trying to reconnect, failing pending send message and performing next action")
              statsEngine.logCrash(newSession, sendMessage.actionName, s"Couldn't reconnect: $errorMessage")
              sendMessage.next
          }

          nextAction ! newSession
          goto(Idle) using IdleData(newSession, stream)

        case _ =>
          logger.debug("Current check success")
          // check success
          val newSession = logResponse(sessionWithCheckUpdate, currentCheck.name, checkSequenceStart, timestamp, OK, None, None)
          remainingChecks match {
            case nextCheck :: nextRemainingChecks =>
              // perform next check
              logger.debug("Perform next check of current check sequence")
              //[fl]
              //
              //[fl]
              stay() using data.copy(currentCheck = nextCheck, remainingChecks = nextRemainingChecks, session = newSession)

            case _ =>
              remainingCheckSequences match {
                case SseMessageCheckSequence(timeout, newCurrentCheck :: newRemainingChecks) :: nextRemainingCheckSequences =>
                  logger.debug("Perform next check sequence")
                  // perform next CheckSequence
                  val timeoutId = scheduleTimeout(timeout)
                  //[fl]
                  //
                  //[fl]
                  stay() using data.copy(
                    currentCheck = newCurrentCheck,
                    remainingChecks = newRemainingChecks,
                    checkSequenceStart = timestamp,
                    checkSequenceTimeoutId = timeoutId,
                    remainingCheckSequences = nextRemainingCheckSequences,
                    session = newSession
                  )

                case _ =>
                  // all check sequences complete
                  logger.debug("Check sequences completed successfully")
                  next match {
                    case Left(nextAction)   => nextAction ! newSession
                    case Right(sendMessage) => self ! sendMessage.copyWithSession(newSession)
                  }
                  goto(Idle) using IdleData(newSession, stream)
              }
          }
      }
    } else {
      logger.debug(s"Received non-matching message $message")
      // server unmatched message, just log
      logUnmatchedServerMessage(session)
      stay()
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy