1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.nio;
19
20import java.io.FileDescriptor;
21import java.io.IOException;
22import java.net.PlainServerSocketImpl;
23import java.net.ServerSocket;
24import java.net.Socket;
25import java.net.SocketAddress;
26import java.net.SocketImpl;
27import java.net.SocketTimeoutException;
28import java.nio.channels.ClosedChannelException;
29import java.nio.channels.IllegalBlockingModeException;
30import java.nio.channels.NotYetBoundException;
31import java.nio.channels.ServerSocketChannel;
32import java.nio.channels.SocketChannel;
33import java.nio.channels.spi.SelectorProvider;
34import libcore.io.ErrnoException;
35import libcore.io.IoUtils;
36import static libcore.io.OsConstants.*;
37
38/**
39 * The default ServerSocketChannel.
40 */
41final class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorChannel {
42
43    private final ServerSocketAdapter socket;
44    private final SocketImpl impl;
45
46    private boolean isBound = false;
47
48    private final Object acceptLock = new Object();
49
50    public ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
51        super(sp);
52        this.socket = new ServerSocketAdapter(this);
53        this.impl = socket.getImpl$();
54    }
55
56    @Override public ServerSocket socket() {
57        return socket;
58    }
59
60    @Override public SocketChannel accept() throws IOException {
61        if (!isOpen()) {
62            throw new ClosedChannelException();
63        }
64        if (!isBound) {
65            throw new NotYetBoundException();
66        }
67
68        // Create an empty socket channel. This will be populated by ServerSocketAdapter.accept.
69        SocketChannelImpl result = new SocketChannelImpl(provider(), false);
70        try {
71            begin();
72            synchronized (acceptLock) {
73                try {
74                    socket.implAccept(result);
75                } catch (SocketTimeoutException e) {
76                    if (shouldThrowSocketTimeoutExceptionFromAccept(e)) {
77                        throw e;
78                    }
79                    // Otherwise, this is a non-blocking socket and there's nothing ready, so we'll
80                    // fall through and return null.
81                }
82            }
83        } finally {
84            end(result.socket().isConnected());
85        }
86        return result.socket().isConnected() ? result : null;
87    }
88
89    private boolean shouldThrowSocketTimeoutExceptionFromAccept(SocketTimeoutException e) {
90        if (isBlocking()) {
91            return true;
92        }
93        Throwable cause = e.getCause();
94        if (cause instanceof ErrnoException) {
95            if (((ErrnoException) cause).errno == EAGAIN) {
96                return false;
97            }
98        }
99        return true;
100    }
101
102    @Override protected void implConfigureBlocking(boolean blocking) throws IOException {
103        IoUtils.setBlocking(impl.getFD$(), blocking);
104    }
105
106    synchronized protected void implCloseSelectableChannel() throws IOException {
107        if (!socket.isClosed()) {
108            socket.close();
109        }
110    }
111
112    public FileDescriptor getFD() {
113        return impl.getFD$();
114    }
115
116    private static class ServerSocketAdapter extends ServerSocket {
117        private final ServerSocketChannelImpl channelImpl;
118
119        ServerSocketAdapter(ServerSocketChannelImpl aChannelImpl) throws IOException {
120            this.channelImpl = aChannelImpl;
121        }
122
123        @Override public void bind(SocketAddress localAddress, int backlog) throws IOException {
124            super.bind(localAddress, backlog);
125            channelImpl.isBound = true;
126        }
127
128        @Override public Socket accept() throws IOException {
129            if (!channelImpl.isBound) {
130                throw new IllegalBlockingModeException();
131            }
132            SocketChannel sc = channelImpl.accept();
133            if (sc == null) {
134                throw new IllegalBlockingModeException();
135            }
136            return sc.socket();
137        }
138
139        public Socket implAccept(SocketChannelImpl clientSocketChannel) throws IOException {
140            Socket clientSocket = clientSocketChannel.socket();
141            boolean connectOK = false;
142            try {
143                synchronized (this) {
144                    super.implAccept(clientSocket);
145                    clientSocketChannel.setConnected();
146                    clientSocketChannel.setBound(true);
147                    clientSocketChannel.finishAccept();
148                }
149                connectOK = true;
150            } finally {
151                if (!connectOK) {
152                    clientSocket.close();
153                }
154            }
155            return clientSocket;
156        }
157
158        @Override public ServerSocketChannel getChannel() {
159            return channelImpl;
160        }
161
162        @Override public boolean isBound() {
163            return channelImpl.isBound;
164        }
165
166        @Override public void bind(SocketAddress localAddress) throws IOException {
167            super.bind(localAddress);
168            channelImpl.isBound = true;
169        }
170
171        @Override public void close() throws IOException {
172            synchronized (channelImpl) {
173                if (channelImpl.isOpen()) {
174                    channelImpl.close();
175                } else {
176                    super.close();
177                }
178            }
179        }
180    }
181}
182