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

io.gatling.http.action.ws.fsm.WsConnectingState.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/*
 * Copyright 2011-2023 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.ws.fsm

import io.gatling.commons.stats.{ KO, OK }
import io.gatling.commons.util.Throwables._
import io.gatling.core.action.Action
import io.gatling.core.session.Session
import io.gatling.http.action.ws.{ OnConnectedChainEndAction, WsListener }
import io.gatling.http.cache.SslContextSupport
import io.gatling.http.check.ws.WsFrameCheckSequence
import io.gatling.http.client.WebSocket
import io.gatling.http.cookie.CookieSupport

import com.typesafe.scalalogging.StrictLogging
import io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS
import io.netty.handler.codec.http.cookie.Cookie

object WsConnectingState extends StrictLogging {
  private val WsConnectSuccessStatusCode = Some(Integer.toString(SWITCHING_PROTOCOLS.code))

  def gotoConnecting(fsm: WsFsm, session: Session, next: Either[Action, SendFrame], remainingReconnects: Int): NextWsState = {
    import fsm._

    val listener = new WsListener(fsm, clock)

    // [e]
    //
    // [e]
    logger.debug(s"Connecting to ${connectRequest.getUri}")
    val userSslContexts = SslContextSupport.sslContexts(session)
    NextWsState(
      state = new WsConnectingState(fsm, session, next, clock.nowMillis, remainingReconnects),
      () =>
        httpEngine.executeRequest(
          connectRequest,
          session.userId,
          httpProtocol.enginePart.shareConnections,
          session.eventLoop,
          listener,
          userSslContexts
        )
    )
  }
}

final case class WsConnectingState(fsm: WsFsm, session: Session, next: Either[Action, SendFrame], connectStart: Long, remainingReconnects: Int)
    extends WsState(fsm)
    with StrictLogging {
  import fsm._

  override def onWebSocketConnected(webSocket: WebSocket, cookies: List[Cookie], connectEnd: Long): NextWsState = {
    val sessionWithCookies = CookieSupport.storeCookies(session, connectRequest.getUri, cookies, connectEnd)
    val sessionWithGroupTimings =
      logResponse(sessionWithCookies, connectRequest.getName, connectStart, connectEnd, OK, WsConnectingState.WsConnectSuccessStatusCode, None)

    connectCheckSequence match {
      case WsFrameCheckSequence(timeout, currentCheck :: remainingChecks) :: remainingCheckSequences =>
        // wait for some checks before proceeding

        // select nextAction
        val (newSession, newNext) =
          onConnected match {
            case Some(onConnectedAction) =>
              // once check complete, perform connect sequence
              // we store the after next action in the session
              // other solution would be to store in FSM state but we'd need one more message passing to contact the actor from ConnectSequenceEndAction

              val onConnectedChainEndCallback: Session => Unit = next match {
                case Left(nextAction) =>
                  logger.debug("Connected, performing checks, setting callback to perform next action after performing onConnected action")
                  nextAction ! _

                case Right(sendFrame) =>
                  logger.debug("Connected, performing checks, setting callback to send pending message after performing onConnected action")
                  sendFrameNextAction(_, sendFrame)
              }

              (OnConnectedChainEndAction.setOnConnectedChainEndCallback(sessionWithGroupTimings, onConnectedChainEndCallback), Left(onConnectedAction))

            case _ =>
              // once check complete -> send next
              logger.debug("Connected, performing checks before proceeding (no onConnected action)")
              (sessionWithGroupTimings, next)
          }

        fsm.scheduleTimeout(timeout)
        // [e]
        //
        // [e]

        NextWsState(
          WsPerformingCheckState(
            fsm,
            webSocket = webSocket,
            currentCheck = currentCheck,
            remainingChecks = remainingChecks,
            checkSequenceStart = connectEnd,
            remainingCheckSequences = remainingCheckSequences,
            remainingReconnects = remainingReconnects,
            session = newSession,
            next = newNext,
            actionName = fsm.connectRequest.getName,
            requestMessage = None
          )
        )

      case _ => // same as Nil as WsFrameCheckSequence#checks can't be Nil, but compiler complains that match may not be exhaustive
        onConnected match {
          case Some(onConnectedAction) =>
            // connect sequence -> store actual next
            val onConnectedChainEndCallback: Session => Unit = next match {
              case Left(nextAction) =>
                logger.debug("Connected, no checks, performing onConnected action before performing next action")
                nextAction ! _

              case Right(sendFrame) =>
                logger.debug("Reconnected, no checks, performing onConnected action before sending pending message")
                sendFrameNextAction(_, sendFrame)
            }
            val newSession = OnConnectedChainEndAction.setOnConnectedChainEndCallback(sessionWithGroupTimings, onConnectedChainEndCallback)

            NextWsState(
              new WsIdleState(fsm, newSession, webSocket, remainingReconnects),
              () => onConnectedAction ! newSession
            )

          case _ =>
            val afterStateUpdate =
              next match {
                case Left(nextAction) =>
                  logger.debug("Connected, no checks, performing next action")
                  () => nextAction ! sessionWithGroupTimings

                case Right(sendFrame) =>
                  logger.debug("Reconnected, no checks, sending pending message")
                  () => sendFrameNextAction(sessionWithGroupTimings, sendFrame)
              }

            NextWsState(
              new WsIdleState(fsm, sessionWithGroupTimings, webSocket, remainingReconnects),
              afterStateUpdate
            )
        }
    }
  }

  override def onWebSocketCrashed(t: Throwable, timestamp: Long): NextWsState = {
    // crash
    logger.debug(s"WebSocket crashed by the server while in Connecting state", t)
    val failedSession = session.markAsFailed
    logResponse(failedSession, connectRequest.getName, connectStart, timestamp, KO, None, Some(t.rootMessage))

    val n = next match {
      case Left(nextAction) => nextAction
      case Right(sendFrame) => sendFrame.next
    }
    logger.debug("Connect failed, performing next action")

    NextWsState(new WsCrashedState(fsm, Some(t.rootMessage), remainingReconnects), () => n ! failedSession)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy