SocketWrapper.java

package com.renomad.minum.web;

import com.renomad.minum.logging.ILogger;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.charset.Charset;

/**
 * This wraps Sockets to make them more particular to our use case
 */
final class SocketWrapper implements ISocketWrapper {

    private final Socket socket;
    private final String hostName;
    private final InputStream inputStream;
    private final OutputStream writer;
    private final ILogger logger;
    private final IServer server;

    /**
     * Constructor
     * @param socket a socket we intend to wrap with methods applicable to our use cases
     * @param logger not much more to say on this param
     * @param timeoutMillis we'll configure the socket to timeout after this many milliseconds.
     */
    SocketWrapper(Socket socket, IServer server, ILogger logger, int timeoutMillis, String hostName) throws IOException {
        this.socket = socket;
        this.hostName = hostName;
        logger.logTrace(() -> String.format("Setting timeout of %d milliseconds on socket %s", timeoutMillis, socket));
        this.socket.setSoTimeout(timeoutMillis);
        this.inputStream = socket.getInputStream();
        writer = socket.getOutputStream();
        this.logger = logger;
        this.server = server;
    }

    @Override
    public void send(String msg) throws IOException {
        writer.write(msg.getBytes(Charset.defaultCharset()));
    }

    @Override
    public void send(byte[] bodyContents) throws IOException {
        writer.write(bodyContents);
    }

    @Override
    public void send(byte[] bodyContents, int off, int len) throws IOException {
        writer.write(bodyContents, off, len);
    }

    @Override
    public void send(int b) throws IOException {
        writer.write(b);
    }

    @Override
    public void sendHttpLine(String msg) throws IOException {
        logger.logTrace(() -> String.format("%s sending: \"%s\"", this, msg));
        send(msg + WebEngine.HTTP_CRLF);
    }

    @Override
    public int getLocalPort() {
        return socket.getLocalPort();
    }

    @Override
    public SocketAddress getRemoteAddrWithPort() {
        return socket.getRemoteSocketAddress();
    }

    @Override
    public String getRemoteAddr() {
        return socket.getInetAddress().getHostAddress();
    }

    @Override
    public HttpServerType getServerType() {
        return server.getServerType();
    }

    @Override
    public void close() throws IOException {
        logger.logTrace(() -> "close called on " + this);
        socket.close();
        if (server != null) server.removeMyRecord(this);
    }

    @Override
    public InputStream getInputStream() {
        return this.inputStream;
    }

    /**
     * Note that since we are indicating just the remote address
     * as the unique value, in cases like tests where we are operating as
     * sometimes server or client, you might see the server as the remote.
     */
    @Override
    public String toString() {
        return "(SocketWrapper for remote address: " + this.getRemoteAddrWithPort().toString() + ")";
    }

    @Override
    public String getHostName() {
        return hostName;
    }
}