How to open TCP connection with TLS in scala using akka

前端 未结 2 1098
梦毁少年i
梦毁少年i 2021-01-13 05:19

I want to write a Scala client that talks a proprietary protocol over a tcp connection with TLS.

Basically, I want to rewrite the following code from Node.js in Sca

2条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-13 05:52

    I really liked Jeremias Werner's answer as it got me where I needed to be. However, I would like to offer the code below (heavily influenced by his answer) as a "one cut and paste" solution that hits an actual TLS server using as little code as I had time to produce.

    import javax.net.ssl.SSLContext
    
    import akka.NotUsed
    import akka.actor.ActorSystem
    import akka.stream.TLSProtocol.NegotiateNewSession
    import akka.stream.scaladsl.{BidiFlow, Flow, Sink, Source, TLS, Tcp}
    import akka.stream.{ActorMaterializer, OverflowStrategy, TLSProtocol, TLSRole}
    import akka.util.ByteString
    
    object TlsClient {
    
      // Flow needed for TLS as well as mapping the TLS engine's flow to ByteStrings
      def tlsClientLayer = {
    
        // Default SSL context supporting most protocols and ciphers. Embellish this as you need
        // by constructing your own SSLContext and NegotiateNewSession instances.
        val tls = TLS(SSLContext.getDefault, NegotiateNewSession.withDefaults, TLSRole.client)
    
        // Maps the TLS stream to a ByteString
        val tlsSupport = BidiFlow.fromFlows(
          Flow[ByteString].map(TLSProtocol.SendBytes),
          Flow[TLSProtocol.SslTlsInbound].collect {
            case TLSProtocol.SessionBytes(_, sb) => ByteString.fromByteBuffer(sb.asByteBuffer)
          })
    
        tlsSupport.atop(tls)
      }
    
      // Very simple logger
      def logging: BidiFlow[ByteString, ByteString, ByteString, ByteString, NotUsed] = {
    
        // function that takes a string, prints it with some fixed prefix in front and returns the string again
        def logger(prefix: String) = (chunk: ByteString) => {
          println(prefix + chunk.utf8String)
          chunk
        }
    
        val inputLogger = logger("> ")
        val outputLogger = logger("< ")
    
        // create BidiFlow with a separate logger function for each of both streams
        BidiFlow.fromFunctions(outputLogger, inputLogger)
      }
    
      def main(args: Array[String]): Unit = {
        implicit val system: ActorSystem = ActorSystem("sip-client")
        implicit val materializer: ActorMaterializer = ActorMaterializer()
    
        val source = Source.actorRef(1000, OverflowStrategy.fail)
        val connection = Tcp().outgoingConnection("www.google.com", 443)
        val tlsFlow = tlsClientLayer.join(connection)
        val srcActor = tlsFlow.join(logging).to(Sink.ignore).runWith(source)
    
        // I show HTTP here but send/receive your protocol over this actor
        // Should respond with a 302 (Found) and a small explanatory HTML message
        srcActor ! ByteString("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
      }
    }
    

提交回复
热议问题