/* * Copyright (c) 2014-2017 by its authors. Some rights reserved. * See the project homepage at: https://monix.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package monix.nio.tcp import monix.eval.Task import monix.execution.Scheduler /** * A TCP client composed of an async reader([[monix.nio.tcp.AsyncSocketChannelObservable AsyncSocketChannelObservable]]) * and an async writer([[monix.nio.tcp.AsyncSocketChannelConsumer AsyncSocketChannelConsumer]]) pair * that both use the same underlying socket that is not released automatically. * * In order to release the connection use [[monix.nio.tcp.AsyncSocketChannelClient#close() close()]] * * @param host hostname * @param port TCP port number * @param bufferSize the size of the buffer used for reading * * @return an [[monix.nio.tcp.AsyncSocketChannelClient AsyncSocketChannelClient]] * * @define tcpObservableDesc Returns the underlying TCP client [[monix.nio.tcp.AsyncSocketChannelObservable reader]] * @define tcpConsumerDesc Returns the underlying TCP client [[monix.nio.tcp.AsyncSocketChannelConsumer writer]] * @define stopReadingDesc Indicates that this channel will not read more data * - end-of-stream indication * @define stopWritingDesc Indicates that this channel will not write more data * - end-of-stream indication * @define closeDesc Closes this channel */ final class AsyncSocketChannelClient( host: String, port: Int, bufferSize: Int)(implicit scheduler: Scheduler) { private var taskSocketChannel: Option[TaskSocketChannel] = None private def this(tsc: TaskSocketChannel, bufferSize: Int)(implicit scheduler: Scheduler) = { this("", 0, bufferSize) this.taskSocketChannel = Option(tsc) } private[this] val connectedSignal = scala.concurrent.Promise[Unit]() def init(): Unit = if (taskSocketChannel.isDefined) { connectedSignal.success(()) } else { taskSocketChannel = Some(TaskSocketChannel()) taskSocketChannel .get .connect(new java.net.InetSocketAddress(host, port)) .map(connectedSignal.success) .onErrorHandle { t => scheduler.reportFailure(t) connectedSignal.failure(t) } .runToFuture } private[this] lazy val asyncTcpClientObservable = new AsyncSocketChannelObservable(taskSocketChannel.get, bufferSize, closeWhenDone = false) /** $tcpObservableDesc */ def tcpObservable: Task[AsyncSocketChannelObservable] = Task.fromFuture { connectedSignal.future.map(_ => asyncTcpClientObservable) } private[this] lazy val asyncTcpClientConsumer = new AsyncSocketChannelConsumer(taskSocketChannel.get, closeWhenDone = false) /** $tcpConsumerDesc */ def tcpConsumer: Task[AsyncSocketChannelConsumer] = Task.fromFuture { connectedSignal.future.map(_ => asyncTcpClientConsumer) } /** $stopReadingDesc */ def stopReading() = taskSocketChannel.fold(Task.pure(()))(_.stopReading()) /** $stopWritingDesc */ def stopWriting() = taskSocketChannel.fold(Task.pure(()))(_.stopWriting()) /** $closeDesc */ def close(): Task[Unit] = taskSocketChannel.fold(Task.pure(()))(_.close()) } object AsyncSocketChannelClient { def apply( host: String, port: Int, bufferSize: Int)(implicit scheduler: Scheduler): AsyncSocketChannelClient = { val client = new AsyncSocketChannelClient(host, port, bufferSize) client.init() client } def apply( taskSocketChannel: TaskSocketChannel, bufferSize: Int)(implicit scheduler: Scheduler): AsyncSocketChannelClient = { val client = new AsyncSocketChannelClient(taskSocketChannel, bufferSize) client.init() client } }