text: import Foundation
  
  /// The socket which encapsulates the logic to handle connection to proxies.
  open class ProxySocket: NSObject, SocketProtocol, RawTCPSocketDelegate {
      /// Received `ConnectSession`.
      public var session: ConnectSession?
  
      public var observer: Observer<ProxySocketEvent>?
  
      private var _cancelled = false
      var isCancelled: Bool {
          return _cancelled
      }
  
      open override var description: String {
          if let session = session {
              return "<\(typeName) host:\(session.host) port: \(session.port))>"
          } else {
              return "<\(typeName)>"
          }
      }
  
      /**
       Init a `ProxySocket` with a raw TCP socket.
  
       - parameter socket: The raw TCP socket.
       */
      public init(socket: RawTCPSocketProtocol, observe: Bool = true) {
          self.socket = socket
  
          super.init()
  
          self.socket.delegate = self
  
          if observe {
              observer = ObserverFactory.currentFactory?.getObserverForProxySocket(self)
          }
      }
  
      /**
       Begin reading and processing data from the socket.
       */
      open func openSocket() {
          guard !isCancelled else {
              return
          }
  
          observer?.signal(.socketOpened(self))
      }
  
      /**
       Response to the `AdapterSocket` on the other side of the `Tunnel` which has succefully connected to the remote server.
  
       - parameter adapter: The `AdapterSocket`.
       */
      open func respondTo(adapter: AdapterSocket) {
          guard !isCancelled else {
              return
          }
  
          observer?.signal(.askedToResponseTo(adapter, on: self))
      }
  
      /**
       Read data from the socket.
       - warning: This should only be called after the last read is finished, i.e., `delegate?.didReadData()` is called.
       */
      open func readData() {
          guard !isCancelled else {
              return
          }
  
          socket.readData()
      }
  
      /**
       Send data to remote.
  
       - parameter data: Data to send.
       - warning: This should only be called after the last write is finished, i.e., `delegate?.didWriteData()` is called.
       */
      open func write(data: Data) {
          guard !isCancelled else {
              return
          }
  
          socket.write(data: data)
      }
  
      /**
       Disconnect the socket elegantly.
       */
      open func disconnect(becauseOf error: Error? = nil) {
          guard !isCancelled else {
              return
          }
  
          _status = .disconnecting
          _cancelled = true
          session?.disconnected(becauseOf: error, by: .proxy)
          socket.disconnect()
          observer?.signal(.disconnectCalled(self))
      }
  
      /**
       Disconnect the socket immediately.
       */
      open func forceDisconnect(becauseOf error: Error? = nil) {
          guard !isCancelled else {
              return
          }
  
          _status = .disconnecting
          _cancelled = true
          session?.disconnected(becauseOf: error, by: .proxy)
          socket.forceDisconnect()
          observer?.signal(.forceDisconnectCalled(self))
      }
  
      // MARK: SocketProtocol Implementation
  
      /// The underlying TCP socket transmitting data.
      public var socket: RawTCPSocketProtocol!
  
      /// The delegate instance.
      weak public var delegate: SocketDelegate?
  
      var _status: SocketStatus = .established
      /// The current connection status of the socket.
      public var status: SocketStatus {
          return _status
      }
  
      // MARK: RawTCPSocketDelegate Protocol Implementation
      /**
       The socket did disconnect.
  
       - parameter socket: The socket which did disconnect.
       */
      open func didDisconnectWith(socket: RawTCPSocketProtocol) {
          _status = .closed
          observer?.signal(.disconnected(self))
          delegate?.didDisconnectWith(socket: self)
      }
  
      /**
       The socket did read some data.
  
       - parameter data:    The data read from the socket.
       - parameter withTag: The tag given when calling the `readData` method.
       - parameter from:    The socket where the data is read from.
       */
      open func didRead(data: Data, from: RawTCPSocketProtocol) {
          observer?.signal(.readData(data, on: self))
      }
  
      /**
       The socket did send some data.
  
       - parameter data:    The data which have been sent to remote (acknowledged). Note this may not be available since the data may be released to save memory.
       - parameter from:    The socket where the data is sent out.
       */
      open func didWrite(data: Data?, by: RawTCPSocketProtocol) {
          observer?.signal(.wroteData(data, on: self))
      }
  
      /**
       The socket did connect to remote.
  
       - note: This never happens for `ProxySocket`.
  
       - parameter socket: The connected socket.
       */
      open func didConnectWith(socket: RawTCPSocketProtocol) {
  
      }
  
  }
  