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

io.gatling.http.action.ws.fsm.WhenConnecting.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.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.cookie.CookieSupport

import io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS

object WhenConnecting {

  val WsConnectSuccessStatusCode = Some(Integer.toString(SWITCHING_PROTOCOLS.code))
}

trait WhenConnecting extends SslContextSupport { this: WsActor =>

  def gotoConnecting(session: Session, next: Either[Action, SendFrame], remainingTries: Int = httpProtocol.wsPart.maxReconnects.getOrElse(0)): State = {

    val listener = new WsListener(self, statsEngine, clock)

    // [fl]
    //
    // [fl]
    val userSslContexts = sslContexts(session)
    httpEngine.executeRequest(connectRequest, session.userId, httpProtocol.enginePart.shareConnections, listener, userSslContexts.map(_.sslContext).orNull, userSslContexts.flatMap(_.alplnSslContext).orNull)

    goto(Connecting) using ConnectingData(session, next, clock.nowMillis, remainingTries)
  }

  private def handleConnectFailure(session: Session, next: Either[Action, SendFrame], connectStart: Long, connectEnd: Long, code: Option[String], reason: String, remainingTries: Int): State = {
    // log connect failure
    val newSession = logResponse(session, connectActionName, connectStart, connectEnd, KO, code, Some(reason))
    val newRemainingTries = remainingTries - 1
    if (newRemainingTries > 0) {
      // try again
      logger.debug(s"Connect failed: $code:$reason, retrying ($newRemainingTries remaining tries)")
      gotoConnecting(newSession, next, newRemainingTries)

    } else {
      val nextAction = next match {
        case Left(n) =>
          // failed to connect
          logger.debug(s"Connect failed: $code:$reason, no remaining tries, going to Crashed state and performing next action")
          n
        case Right(sendFrame) =>
          // failed to reconnect, logging failure to send message
          logger.debug(s"Connect failed: $code:$reason, no remaining tries, going to Crashed state, failing pending Send and performing next action")
          statsEngine.logCrash(newSession, sendFrame.actionName, "Failed to reconnect")
          sendFrame.next
      }
      nextAction ! newSession.markAsFailed
      goto(Crashed) using CrashedData(Some(reason))
    }
  }

  when(Connecting) {
    case Event(WebSocketConnected(webSocket, cookies, connectEnd), ConnectingData(session, next, connectStart, _)) =>
      val sessionWithCookies = CookieSupport.storeCookies(session, connectRequest.getUri, cookies, connectEnd)
      val sessionWithGroupTimings = logResponse(sessionWithCookies, connectActionName, connectStart, connectEnd, OK, WhenConnecting.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(sendTextMessage) =>
                    logger.debug("Connected, performing checks, setting callback to send pending message after performing onConnected action")
                    s => self ! sendTextMessage.copyWithSession(s)
                }

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

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

          val timeoutId = scheduleTimeout(timeout)
          //[fl]
          //
          //[fl]
          goto(PerformingCheck) using PerformingCheckData(
            webSocket = webSocket,
            currentCheck = currentCheck,
            remainingChecks = remainingChecks,
            checkSequenceStart = connectEnd,
            checkSequenceTimeoutId = timeoutId,
            remainingCheckSequences = remainingCheckSequences,
            session = newSession,
            next = newNext
          )

        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")
                  s => self ! sendFrame.copyWithSession(s)
              }
              val newSession = OnConnectedChainEndAction.setOnConnectedChainEndCallback(sessionWithGroupTimings, onConnectedChainEndCallback)
              onConnectedAction ! newSession
              goto(Idle) using IdleData(newSession, webSocket)

            case _ =>
              // send next
              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")
                  self ! sendFrame.copyWithSession(sessionWithGroupTimings)
              }
              goto(Idle) using IdleData(sessionWithGroupTimings, webSocket)
          }
      }

    case Event(WebSocketClosed(code, reason, timestamp), ConnectingData(session, next, connectStart, remainingTries)) =>
      // unexpected close
      handleConnectFailure(session, next, connectStart, timestamp, Some(String.valueOf(code)), reason, remainingTries)

    case Event(WebSocketCrashed(t, timestamp), ConnectingData(session, next, connectStart, remainingTries)) =>
      // crash
      logger.debug("WebSocket crashed", t)
      handleConnectFailure(session, next, connectStart, timestamp, None, t.rootMessage, remainingTries)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy