// Copyright 2014 The Bazel Authors. All rights reserved. // // 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 com.google.devtools.build.lib.unix; import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; /** *

An implementation of ServerSocket for local (AF_UNIX) sockets. * *

This class intentionally doesn't extend java.net.ServerSocket although it * has some similarity to it. The java.net class hierarchy is a terrible mess * and is inextricably coupled to the Internet Protocol. * *

This code is not intended to be portable to non-UNIX platforms. */ public class LocalServerSocket extends LocalSocket { // Socket timeout in milliseconds. No timeout by default. private long soTimeoutMillis = 0; /** * Constructs an unbound local server socket. */ public LocalServerSocket() throws IOException { super(); } /** * Constructs a server socket, binds it to the specified address, and * listens for incoming connections with the specified backlog. * * @throws IOException if any of the socket/bind/listen operations failed. */ public LocalServerSocket(LocalSocketAddress address, int backlog) throws IOException { this(); bind(address); listen(backlog); } /** * Constructs a server socket, binds it to the specified address, and begin * listening for incoming connections using the default backlog. * * @throws IOException if any of the socket/bind/listen operations failed. */ public LocalServerSocket(LocalSocketAddress address) throws IOException { this(address, 50); } /** * Specifies the timeout in milliseconds for accept(). Setting it to * zero means an indefinite timeout. */ public void setSoTimeout(long timeoutMillis) { soTimeoutMillis = timeoutMillis; } /** * Returns the current timeout in milliseconds. */ public long getSoTimeout() { return soTimeoutMillis; } /** * Binds the specified address to this socket. The socket must be unbound. * This causes the filesystem entry to appear. * * @throws IOException if the bind failed. */ public synchronized void bind(LocalSocketAddress address) throws IOException { if (address == null) { throw new NullPointerException("address"); } checkNotClosed(); if (state != State.NEW) { throw new SocketException("socket is already bound to an address"); } bind(fd, address.getName().toString()); // JNI this.address = address; this.state = State.BOUND; } /** * Listen for incoming connections on a socket using the specfied backlog. * The socket must be bound but not already listening. * * @throws IOException if the listen failed. */ public synchronized void listen(int backlog) throws IOException { if (backlog < 1) { throw new IllegalArgumentException("backlog=" + backlog); } checkNotClosed(); if (address == null) { throw new SocketException("socket has no address bound"); } if (state == State.LISTENING) { throw new SocketException("socket is already listening"); } listen(fd, backlog); // JNI this.state = State.LISTENING; } /** * Blocks until a connection is made to this socket and accepts it, returning * a new socket connected to the client. * * @return the new socket connected to the client. * @throws IOException if an error occurs when waiting for a connection. * @throws SocketTimeoutException if a timeout was previously set with * setSoTimeout and the timeout has been reached. * @throws InterruptedIOException if the thread is interrupted when the * method is blocked. */ public synchronized Socket accept() throws IOException, SocketTimeoutException, InterruptedIOException { if (state != State.LISTENING) { throw new SocketException("socket is not in listening state"); } // Throws a SocketTimeoutException if timeout. if (soTimeoutMillis != 0) { poll(fd, soTimeoutMillis); // JNI } FileDescriptor clientFd = new FileDescriptor(); accept(fd, clientFd); // JNI final LocalSocketImpl impl = new LocalSocketImpl(clientFd); return new Socket(impl) { @Override public boolean isConnected() { return true; } @Override public synchronized void close() throws IOException { if (isClosed()) { return; } else { super.close(); // Workaround for the fact that super.created==false because we // created the impl ourselves. As a result, super.close() doesn't // call impl.close(). *Sigh*, java.net is horrendous. // (Perhaps we should dispense with Socket/SocketImpl altogether?) impl.close(); } } }; } @Override public String toString() { return "LocalServerSocket(" + address + ")"; } }