/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. All rights reserved.
*/
/**
* @file
* The file declares the Transmitter class.
*/
package httpclient4cj
/**
* The class is Transmitter
* @author guo_tingtingtekla,luoyukai4
* @since 0.29.3
*/
public class Transmitter {
/* var member client type is HttpClient */
private var client: HttpClient
/* var member connectionPool type is ConnectionPool */
private var connectionPool: ConnectionPool
/* var member call type is Call */
private var call: Call
/* var member canceled */
private var canceled = false
/** var member connection type is Option<Connection> */
public var connection: Option<Connection> = Option<Connection>.None
/* var member request type is Option<Request> */
private var request: Option<Request> = Option<Request>.None
/* var member address type is Address */
private var address: Address = Address(URL.parse(""), ArrayList<String>(), Option<TlsClientConfig>.None)
/* var member exchangeFinder type is Option<ExchangeFinder> */
private var exchangeFinder: Option<ExchangeFinder> = None
/* var member exchange type is Option<Exchange> */
private var exchange: Option<Exchange> = None
private var noExchanges: Bool = false
private var exchangeRequestDone: Bool = false
private var exchangeResponseDone: Bool = false
var ref: Bool = true
private let eventListener: EventListener
/**
* The Function is init constructor
*
* @param client of HttpClient
* @param call of Call
* @since 0.29.3
*/
public init(client: HttpClient, call: Call) {
this.client = client
match (client.getConnectionPool()) {
case Some(pool) => this.connectionPool = pool
case None =>
let pool = ConnectionPool()
this.connectionPool = pool
client.connectionPool = pool
}
let conn = ConnectionPool()
this.connectionPool = client.getConnectionPool() ?? conn
this.eventListener = client.getEventListener()
this.call = call
}
/**
* The Function is cancel
*
* @since 0.29.3
*/
public func cancel(): Unit {
let exchangeToCancel: Option<Exchange>
let connectionToCancel: Option<Connection>
synchronized(connectionPool.mutex) {
canceled = true
exchangeToCancel = exchange
connectionToCancel = match (exchangeFinder) {
case Some(find) => match (find.getConnectingConnection()) {
case Some(conn) => Option<Connection>.Some(conn)
case None => this.connection
}
case None => this.connection
}
}
match (exchangeToCancel) {
case Some(cancelEx) => cancelEx.cancel()
case None => match (connectionToCancel) {
case Some(cancelConn) => cancelConn.cancel()
case None => ()
}
}
}
/**
* The Function is isCanceled
*
* @since 0.29.3
*/
public func isCanceled(): Bool {
synchronized(connectionPool.mutex) {
return canceled
}
}
/**
* The Function is releaseConnectionNoEvents
*
* @return Type of Option<Socket>
* @since 0.29.3
*/
func releaseConnectionNoEvents(): Option<OKSocket> {
let conn = connection.getOrThrow()
let arr = conn.getTransmitters()
for (index in 0..arr.size) {
if (refEq(arr[index], this)) {
arr.remove(at: index)
break
}
}
let released = conn
this.connection = Option<Connection>.None
if (arr.isEmpty()) {
released.idleAtNanos = DateTime.now()
if (connectionPool.connectionBecameIdle(released)) {
return released.getSocket()
}
}
return Option<OKSocket>.None
}
/**
* The Function is acquireConnectionNoEvents
*
* @param connection of Connection
*
* @return Type of Unit
* @since 0.29.3
*/
func acquireConnectionNoEvents(connection: Option<Connection>): Unit {
synchronized(connectionPool.mutex) {
match (connection) {
case Some(conn) =>
this.connection = conn
conn.transmitters.add(this)
case None => throw HttpException("IllegalStateException")
}
}
}
/**
* The Function is prepareToConnect
*
* @param request of Request
*
* @return Type of Unit
* @since 0.29.3
*/
public func prepareToConnect(request: Request): Unit {
match (this.request) {
case Some(r) =>
if (Util.sameConnection(r.getUrl(), request.getUrl())) {
if (exchangeFinder.getOrThrow().hasRouteToTry()) {
return
}
}
if (hasExchange()) {
throw IllegalStateException("hasExchange")
}
if (hasExchangeFinder()) {
maybeReleaseConnection(true)
exchangeFinder = None
}
case None => ()
}
this.request = request
address = createAddress(request.getUrl())
this.exchangeFinder = Option<ExchangeFinder>.Some(
ExchangeFinder(this, connectionPool, address, call, eventListener))
}
private func createAddress(url: URL): Address {
if (url.scheme == "https") {
return Address(url, client.getProtocols(), client.getTlsConfig())
} else {
return Address(url, client.getProtocols())
}
}
/**
* The Function is newExchange
*
* @param chain of Chain
* @param doExtensiveHealthChecks of Bool
*
* @return Type of Exchange
* @since 0.29.3
*/
func newExchange(chain: Chain, doExtensiveHealthChecks: Bool): Exchange {
let finder: ExchangeFinder = exchangeFinder.getOrThrow()
let exchangeCodec: ExchangeCodec = finder.find(client, chain, doExtensiveHealthChecks)
let result: Exchange = Exchange(this, call, finder, exchangeCodec, eventListener)
synchronized(connectionPool.mutex) {
this.exchange = Some(result)
this.exchangeRequestDone = false
this.exchangeResponseDone = false
}
return result
}
public func noMoreExchanges(): Unit {
synchronized(connectionPool.mutex) {
noExchanges = true
}
maybeReleaseConnection(false)
}
private func maybeReleaseConnection(force: Bool): Unit {
var socket: Option<OKSocket> = None
var callEnd: Bool
synchronized(connectionPool.mutex) {
let hase = hasExchange()
if (force && hase) {
throw IllegalStateException("cannot release connection while it is in use")
}
if (hasConnection() && !hase && (force || noExchanges)) {
socket = releaseConnectionNoEvents()
} else {
socket = None
}
callEnd = noExchanges && !hase
}
Util.closeQuietly(socket)
if (callEnd) {
eventListener.callEnd(call)
}
}
public func hasExchangeFinder() {
return has(this.exchangeFinder)
}
public func hasConnection(): Bool {
synchronized(connectionPool.mutex) {
return match (this.connection) {
case Some(_) => true
case None => false
}
}
}
public func hasExchange(): Bool {
synchronized(connectionPool.mutex) {
return has(exchange)
}
}
func exchangeMessageDone(exchange: Exchange, requestDone: Bool, responseDone: Bool) {
var exchangeDone = false
synchronized(connectionPool.mutex) {
if (!refEq(this.exchange.getOrThrow(), exchange)) {
// from detached violently
return
}
var changed = false
if (requestDone) {
if (!exchangeRequestDone) {
changed = true
}
this.exchangeRequestDone = true
}
if (responseDone) {
if (!exchangeResponseDone) {
changed = true
}
this.exchangeResponseDone = true
}
if (exchangeRequestDone && exchangeResponseDone && changed) {
exchangeDone = true
this.exchange.getOrThrow().getConnection().successCount++
this.exchange = None
}
}
if (exchangeDone) {
maybeReleaseConnection(false)
}
}
public func exchangeDoneDueToException(): Unit {
synchronized(connectionPool.mutex) {
if (noExchanges) {
throw IllegalStateException("")
}
exchange = None
}
}
public func canRetry(): Bool {
let ex = exchangeFinder.getOrThrow()
let flag1 = ex.getHasStreamFailure()
let flag2 = ex.hasRouteToTry()
return flag1 && flag2
}
}