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

commonMain.network.handler.NetworkHandler.kt Maven / Gradle / Ivy

There is a newer version: 2.16.0
Show newest version
/*
 * Copyright 2019-2022 Mamoe Technologies and contributors.
 *
 * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
 * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
 *
 * https://github.com/mamoe/mirai/blob/dev/LICENSE
 */

package net.mamoe.mirai.internal.network.handler

import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.takeWhile
import net.mamoe.mirai.Bot
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.components.BotInitProcessor
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler
import net.mamoe.mirai.internal.network.handler.state.StateObserver
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
import net.mamoe.mirai.utils.MiraiLogger

/**
 * Coroutine-based network framework. Usually wrapped with [SelectorNetworkHandler] to enable retrying.
 *
 * Implementation is typically subclass of [NetworkHandlerSupport].
 *
 * Instances are often created by [NetworkHandlerFactory].
 *
 * @see NetworkHandlerSupport
 * @see NetworkHandlerFactory
 */
internal interface NetworkHandler : CoroutineScope {
    val context: NetworkHandlerContext

    /**
     * Current state of this handler. This is volatile.
     */
    val state: State

    fun getLastFailure(): Throwable?

    /**
     * The channel that is sent with a [State] when changed.
     */
    val stateChannel: ReceiveChannel

    /**
     * State of this handler.
     *
     * ## States transition overview
     *
     * There are 5 [State]s, each of which encapsulates the state of the network connection.
     *
     * Initial state is [State.INITIALIZED], at which no packets can be send before [resumeConnection], which transmits state into [State.CONNECTING].
     * On [State.CONNECTING], [NetworkHandler] establishes a connection with the server while [SsoProcessor] takes responsibility in the single-sign-on process.
     *
     * Successful logon turns state to [State.LOADING], an **open state**, which does nothing by default. Jobs can be *attached* by [StateObserver]s.
     * For example, attaching a [BotInitProcessor] to handle account-relevant jobs to the [Bot].
     *
     * Failure during [State.CONNECTING] and [State.LOADING] switches state to [State.CLOSED], on which [NetworkHandler] is considered **permanently dead**.
     * The state after finishing of [State.LOADING] is [State.OK]. This state lasts for the majority of time.
     *
     * When connection is lost (e.g. due to Internet unavailability), it does NOT return to [State.CONNECTING] but to [State.CLOSED]. No reconnection is allowed.
     * Retrial may only be performed in [SelectorNetworkHandler].
     *
     * @see state
     */
    enum class State {
        /**
         * Just created and no connection has been made.
         *
         * At this state [resumeConnection] turns state into [CONNECTING] and
         * establishes a connection to the server and do authentication, for which [sendAndExpect] suspends.
         */
        INITIALIZED,

        /**
         * Connection to server, including the process of authentication.
         *
         * At this state [resumeConnection] does nothing. [sendAndExpect] suspends for the result of connection started in [INITIALIZED].
         */
        CONNECTING,

        /**
         * Loading essential data from server and local cache. Data include contact list.
         *
         * At this state [resumeConnection] waits for the jobs. [sendAndExpect] works normally.
         */
        LOADING,

        /**
         * Everything is working.
         *
         * At this state [resumeConnection] does nothing. [sendAndExpect] works normally.
         */
        OK,

        /**
         * The terminal state. Both [resumeConnection] and [sendAndExpect] throw a [IllegalStateException].
         *
         * **Important nodes**: iff [NetworkHandler] is [SelectorNetworkHandler], it might return to a normal state e.g. [INITIALIZED] if new instance of [NetworkHandler] is created.
         * However callers usually do not need to pay extra attention on this behavior. Everything will just work fine if you consider [CLOSED] as a final, non-recoverable state.
         *
         * At this state [resumeConnection] throws the exception caught from underlying socket implementation (i.e netty).
         * [sendAndExpect] throws [IllegalStateException].
         */
        CLOSED,
    }

    /**
     * Suspends the coroutine until [sendAndExpect] can be executed without suspension or state is [State.CLOSED].
     *
     * In other words, if this functions returns, it indicates that [state] is [State.LOADING] or [State.OK]
     *
     * May throw exception that had caused current state to fail.
     * @see State
     */
    @Throws(Exception::class)
    suspend fun resumeConnection()


    suspend fun 

sendAndExpect( packet: OutgoingPacketWithRespType

, timeout: Long = 5000, attempts: Int = 2 ): P /** * Sends [packet], suspends and expects to receive a response from the server. * * Coroutine suspension may happen if connection is not yet available however, * [IllegalStateException] is thrown if [NetworkHandler] is already in [State.CLOSED] since closure is final. * * @throws TimeoutCancellationException if timeout has been reached. * @throws CancellationException if the [NetworkHandler] is closed, with the last cause for closure. * @throws IllegalArgumentException if [timeout] or [attempts] are invalid. * * @param attempts ranges `1..INFINITY` */ suspend fun

sendAndExpect(packet: OutgoingPacket, timeout: Long = 5000, attempts: Int = 2): P /** * Sends [packet] and does not expect any response. * * Response is still being processed but not passed as a return value of this function, so it does not suspends this function (due to awaiting for the response). * However, coroutine is still suspended if connection is not yet available, * and [IllegalStateException] is thrown if [NetworkHandler] is already in [State.CLOSED] since closure is final. * legalStateException] is thrown if [NetworkHandler] is already in [State.CLOSED] since closure is final. * * @throws CancellationException if the [NetworkHandler] is closed, with the last cause for closure. */ suspend fun sendWithoutExpect(packet: OutgoingPacket) /** * Closes this handler gracefully (i.e. asynchronously). You should provide [cause] as possible as you can to give debugging information. * * After invocation of [close], [state] will always be [State.CLOSED]. */ fun close(cause: Throwable?) /////////////////////////////////////////////////////////////////////////// // compatibility /////////////////////////////////////////////////////////////////////////// } internal val NetworkHandler.logger: MiraiLogger get() = context.logger /** * Suspend coroutine to wait for the state [suspendUntil]. */ internal suspend fun NetworkHandler.awaitState(suspendUntil: NetworkHandler.State) { if (this.state == suspendUntil) return stateChannel.consumeAsFlow().takeWhile { it != suspendUntil }.collect() } internal suspend fun NetworkHandler.awaitStateChange() { stateChannel.consumeAsFlow().first() }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy