/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
package stdx.net.tls
import stdx.crypto.x509.*
import std.net.StreamingSocket
import stdx.net.tls.common.TlsException
abstract class TlsSocketState <: ToString {
// this is abstract class instead of interface to fit AtomicReference constraints
public func close(): Unit // CJ demand it to be public
}
// TLS socket is just created and ready for handshake
class SocketReady <: TlsSocketState {
SocketReady(
let socket: StreamingSocket,
let config: HandshakeConfig
) {}
public func toString(): String {
return "${socket}, ready for handshake"
}
public override func close(): Unit {
socket.close()
}
}
// socket is currently in handshake sequence
class SocketInHandshake <: TlsSocketState {
SocketInHandshake(let socket: StreamingSocket) {}
public func toString(): String {
return "${socket}, handshake"
}
public override func close(): Unit {
socket.close()
}
}
// this states represents TLS socket that is passed handshake and hasn't been closed yet
class SocketConnected <: TlsSocketState {
private let peerCertificateHolder = Lazy<?Array<X509Certificate>>()
SocketConnected(
let stream: TlsRawSocket,
let socket: StreamingSocket,
let myCertificate: ?Array<X509Certificate>,
let isClient: Bool,
let bridge: Bridge
) {
peerCertificateHolder.configure {
stream.getPeerCertificate()
}
}
func setPeerCerts(v: ?Array<X509Certificate>): Unit {
peerCertificateHolder.set(v)
}
public func toString(): String {
return "${stream}, connected"
}
/**
* Does graceful shutdown. Closes the socket first and then waiting for
* a chance to terminate the native stream.
* If invoked within a withSession() invocation, then the native resources
* will be deleted after leaving the invocation.
*/
public override func close(): Unit {
try {
stream.close()
} finally {
// if we remove bridge before closing a stream then we may potentially loose
// sessions that can be negotiated just before the shutdown
// so it's important to remove it AFTER closing the stream
Bridge.remove(bridge)
}
}
/**
* Executes block providing the native pointer with guarantee that it will be
* never deleted during this invocation.
*
* Reentrant and concurrent-safe.
*
* Does safely bypass all exceptions inside the block.
*
* @throws exception if close() has been invoked before
*/
func withStream<R>(block: (CPointer<Ssl>) -> R): R {
stream.otherNonIO<R> {ssl, _ => block(ssl)}
}
prop peerCertificate: ?Array<X509Certificate> {
get() {
peerCertificateHolder.value
}
}
prop certificate: ?Array<X509Certificate> {
get() {
myCertificate
}
}
}
// the TLS socket is already closed
class SocketClosed <: TlsSocketState {
private SocketClosed(let selfClosed: Bool // whether it has been closed but user or by TLS socket itself
) {}
private static let instance: SocketClosed = SocketClosed(false)
private static let selfClosedInstance: SocketClosed = SocketClosed(true)
static func getInstance(selfClosed: Bool): SocketClosed {
match (selfClosed) {
case true => selfClosedInstance
case false => instance
}
}
public override func toString(): String {
"closed"
}
static func alreadyClosedException(): Exception {
TlsException("TLS socket is already closed.")
}
static func throwAlreadyClosed(): Nothing {
throw alreadyClosedException()
}
public override func close(): Unit {
}
}