1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.io.nio;
20
21import java.io.IOException;
22import java.net.InetSocketAddress;
23import java.net.Socket;
24import java.net.SocketException;
25import java.nio.ByteBuffer;
26import java.nio.channels.ByteChannel;
27import java.nio.channels.GatheringByteChannel;
28import java.nio.channels.SelectableChannel;
29import java.nio.channels.SocketChannel;
30
31import org.eclipse.jetty.io.Buffer;
32import org.eclipse.jetty.io.EndPoint;
33import org.eclipse.jetty.util.StringUtil;
34import org.eclipse.jetty.util.log.Log;
35import org.eclipse.jetty.util.log.Logger;
36
37/**
38 * Channel End Point.
39 * <p>Holds the channel and socket for an NIO endpoint.
40 *
41 */
42public class ChannelEndPoint implements EndPoint
43{
44    private static final Logger LOG = Log.getLogger(ChannelEndPoint.class);
45
46    protected final ByteChannel _channel;
47    protected final ByteBuffer[] _gather2=new ByteBuffer[2];
48    protected final Socket _socket;
49    protected final InetSocketAddress _local;
50    protected final InetSocketAddress _remote;
51    protected volatile int _maxIdleTime;
52    private volatile boolean _ishut;
53    private volatile boolean _oshut;
54
55    public ChannelEndPoint(ByteChannel channel) throws IOException
56    {
57        super();
58        this._channel = channel;
59        _socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
60        if (_socket!=null)
61        {
62            _local=(InetSocketAddress)_socket.getLocalSocketAddress();
63            _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
64            _maxIdleTime=_socket.getSoTimeout();
65        }
66        else
67        {
68            _local=_remote=null;
69        }
70    }
71
72    protected ChannelEndPoint(ByteChannel channel, int maxIdleTime) throws IOException
73    {
74        this._channel = channel;
75        _maxIdleTime=maxIdleTime;
76        _socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
77        if (_socket!=null)
78        {
79            _local=(InetSocketAddress)_socket.getLocalSocketAddress();
80            _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
81            _socket.setSoTimeout(_maxIdleTime);
82        }
83        else
84        {
85            _local=_remote=null;
86        }
87    }
88
89    public boolean isBlocking()
90    {
91        return  !(_channel instanceof SelectableChannel) || ((SelectableChannel)_channel).isBlocking();
92    }
93
94    public boolean blockReadable(long millisecs) throws IOException
95    {
96        return true;
97    }
98
99    public boolean blockWritable(long millisecs) throws IOException
100    {
101        return true;
102    }
103
104    /*
105     * @see org.eclipse.io.EndPoint#isOpen()
106     */
107    public boolean isOpen()
108    {
109        return _channel.isOpen();
110    }
111
112    /** Shutdown the channel Input.
113     * Cannot be overridden. To override, see {@link #shutdownInput()}
114     * @throws IOException
115     */
116    protected final void shutdownChannelInput() throws IOException
117    {
118        LOG.debug("ishut {}", this);
119        _ishut = true;
120        if (_channel.isOpen())
121        {
122            if (_socket != null)
123            {
124                try
125                {
126                    if (!_socket.isInputShutdown())
127                    {
128                        _socket.shutdownInput();
129                    }
130                }
131                catch (SocketException e)
132                {
133                    LOG.debug(e.toString());
134                    LOG.ignore(e);
135                }
136                finally
137                {
138                    if (_oshut)
139                    {
140                        close();
141                    }
142                }
143            }
144        }
145    }
146
147    /* (non-Javadoc)
148     * @see org.eclipse.io.EndPoint#close()
149     */
150    public void shutdownInput() throws IOException
151    {
152        shutdownChannelInput();
153    }
154
155    protected final void shutdownChannelOutput() throws IOException
156    {
157        LOG.debug("oshut {}",this);
158        _oshut = true;
159        if (_channel.isOpen())
160        {
161            if (_socket != null)
162            {
163                try
164                {
165                    if (!_socket.isOutputShutdown())
166                    {
167                        _socket.shutdownOutput();
168                    }
169                }
170                catch (SocketException e)
171                {
172                    LOG.debug(e.toString());
173                    LOG.ignore(e);
174                }
175                finally
176                {
177                    if (_ishut)
178                    {
179                        close();
180                    }
181                }
182            }
183        }
184    }
185
186    /* (non-Javadoc)
187     * @see org.eclipse.io.EndPoint#close()
188     */
189    public void shutdownOutput() throws IOException
190    {
191        shutdownChannelOutput();
192    }
193
194    public boolean isOutputShutdown()
195    {
196        return _oshut || !_channel.isOpen() || _socket != null && _socket.isOutputShutdown();
197    }
198
199    public boolean isInputShutdown()
200    {
201        return _ishut || !_channel.isOpen() || _socket != null && _socket.isInputShutdown();
202    }
203
204    /* (non-Javadoc)
205     * @see org.eclipse.io.EndPoint#close()
206     */
207    public void close() throws IOException
208    {
209        LOG.debug("close {}",this);
210        _channel.close();
211    }
212
213    /* (non-Javadoc)
214     * @see org.eclipse.io.EndPoint#fill(org.eclipse.io.Buffer)
215     */
216    public int fill(Buffer buffer) throws IOException
217    {
218        if (_ishut)
219            return -1;
220        Buffer buf = buffer.buffer();
221        int len=0;
222        if (buf instanceof NIOBuffer)
223        {
224            final NIOBuffer nbuf = (NIOBuffer)buf;
225            final ByteBuffer bbuf=nbuf.getByteBuffer();
226
227            //noinspection SynchronizationOnLocalVariableOrMethodParameter
228            try
229            {
230                synchronized(bbuf)
231                {
232                    try
233                    {
234                        bbuf.position(buffer.putIndex());
235                        len=_channel.read(bbuf);
236                    }
237                    finally
238                    {
239                        buffer.setPutIndex(bbuf.position());
240                        bbuf.position(0);
241                    }
242                }
243
244                if (len<0 && isOpen())
245                {
246                    if (!isInputShutdown())
247                        shutdownInput();
248                    if (isOutputShutdown())
249                        _channel.close();
250                }
251            }
252            catch (IOException x)
253            {
254                LOG.debug("Exception while filling", x);
255                try
256                {
257                    if (_channel.isOpen())
258                        _channel.close();
259                }
260                catch (Exception xx)
261                {
262                    LOG.ignore(xx);
263                }
264
265                if (len>0)
266                    throw x;
267                len=-1;
268            }
269        }
270        else
271        {
272            throw new IOException("Not Implemented");
273        }
274
275        return len;
276    }
277
278    /* (non-Javadoc)
279     * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer)
280     */
281    public int flush(Buffer buffer) throws IOException
282    {
283        Buffer buf = buffer.buffer();
284        int len=0;
285        if (buf instanceof NIOBuffer)
286        {
287            final NIOBuffer nbuf = (NIOBuffer)buf;
288            final ByteBuffer bbuf=nbuf.getByteBuffer().asReadOnlyBuffer();
289            try
290            {
291                bbuf.position(buffer.getIndex());
292                bbuf.limit(buffer.putIndex());
293                len=_channel.write(bbuf);
294            }
295            finally
296            {
297                if (len>0)
298                    buffer.skip(len);
299            }
300        }
301        else if (buf instanceof RandomAccessFileBuffer)
302        {
303            len = ((RandomAccessFileBuffer)buf).writeTo(_channel,buffer.getIndex(),buffer.length());
304            if (len>0)
305                buffer.skip(len);
306        }
307        else if (buffer.array()!=null)
308        {
309            ByteBuffer b = ByteBuffer.wrap(buffer.array(), buffer.getIndex(), buffer.length());
310            len=_channel.write(b);
311            if (len>0)
312                buffer.skip(len);
313        }
314        else
315        {
316            throw new IOException("Not Implemented");
317        }
318        return len;
319    }
320
321    /* (non-Javadoc)
322     * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
323     */
324    public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
325    {
326        int length=0;
327
328        Buffer buf0 = header==null?null:header.buffer();
329        Buffer buf1 = buffer==null?null:buffer.buffer();
330
331        if (_channel instanceof GatheringByteChannel &&
332            header!=null && header.length()!=0 && buf0 instanceof NIOBuffer &&
333            buffer!=null && buffer.length()!=0 && buf1 instanceof NIOBuffer)
334        {
335            length = gatheringFlush(header,((NIOBuffer)buf0).getByteBuffer(),buffer,((NIOBuffer)buf1).getByteBuffer());
336        }
337        else
338        {
339            // flush header
340            if (header!=null && header.length()>0)
341                length=flush(header);
342
343            // flush buffer
344            if ((header==null || header.length()==0) &&
345                 buffer!=null && buffer.length()>0)
346                length+=flush(buffer);
347
348            // flush trailer
349            if ((header==null || header.length()==0) &&
350                (buffer==null || buffer.length()==0) &&
351                 trailer!=null && trailer.length()>0)
352                length+=flush(trailer);
353        }
354
355        return length;
356    }
357
358    protected int gatheringFlush(Buffer header, ByteBuffer bbuf0, Buffer buffer, ByteBuffer bbuf1) throws IOException
359    {
360        int length;
361
362        synchronized(this)
363        {
364            // Adjust position indexs of buf0 and buf1
365            bbuf0=bbuf0.asReadOnlyBuffer();
366            bbuf0.position(header.getIndex());
367            bbuf0.limit(header.putIndex());
368            bbuf1=bbuf1.asReadOnlyBuffer();
369            bbuf1.position(buffer.getIndex());
370            bbuf1.limit(buffer.putIndex());
371
372            _gather2[0]=bbuf0;
373            _gather2[1]=bbuf1;
374
375            // do the gathering write.
376            length=(int)((GatheringByteChannel)_channel).write(_gather2);
377
378            int hl=header.length();
379            if (length>hl)
380            {
381                header.clear();
382                buffer.skip(length-hl);
383            }
384            else if (length>0)
385            {
386                header.skip(length);
387            }
388        }
389        return length;
390    }
391
392    /* ------------------------------------------------------------ */
393    /**
394     * @return Returns the channel.
395     */
396    public ByteChannel getChannel()
397    {
398        return _channel;
399    }
400
401
402    /* ------------------------------------------------------------ */
403    /*
404     * @see org.eclipse.io.EndPoint#getLocalAddr()
405     */
406    public String getLocalAddr()
407    {
408        if (_socket==null)
409            return null;
410       if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
411           return StringUtil.ALL_INTERFACES;
412        return _local.getAddress().getHostAddress();
413    }
414
415    /* ------------------------------------------------------------ */
416    /*
417     * @see org.eclipse.io.EndPoint#getLocalHost()
418     */
419    public String getLocalHost()
420    {
421        if (_socket==null)
422            return null;
423       if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
424           return StringUtil.ALL_INTERFACES;
425        return _local.getAddress().getCanonicalHostName();
426    }
427
428    /* ------------------------------------------------------------ */
429    /*
430     * @see org.eclipse.io.EndPoint#getLocalPort()
431     */
432    public int getLocalPort()
433    {
434        if (_socket==null)
435            return 0;
436        if (_local==null)
437            return -1;
438        return _local.getPort();
439    }
440
441    /* ------------------------------------------------------------ */
442    /*
443     * @see org.eclipse.io.EndPoint#getRemoteAddr()
444     */
445    public String getRemoteAddr()
446    {
447        if (_socket==null)
448            return null;
449        if (_remote==null)
450            return null;
451        return _remote.getAddress().getHostAddress();
452    }
453
454    /* ------------------------------------------------------------ */
455    /*
456     * @see org.eclipse.io.EndPoint#getRemoteHost()
457     */
458    public String getRemoteHost()
459    {
460        if (_socket==null)
461            return null;
462        if (_remote==null)
463            return null;
464        return _remote.getAddress().getCanonicalHostName();
465    }
466
467    /* ------------------------------------------------------------ */
468    /*
469     * @see org.eclipse.io.EndPoint#getRemotePort()
470     */
471    public int getRemotePort()
472    {
473        if (_socket==null)
474            return 0;
475        return _remote==null?-1:_remote.getPort();
476    }
477
478    /* ------------------------------------------------------------ */
479    /*
480     * @see org.eclipse.io.EndPoint#getConnection()
481     */
482    public Object getTransport()
483    {
484        return _channel;
485    }
486
487    /* ------------------------------------------------------------ */
488    public void flush()
489        throws IOException
490    {
491    }
492
493    /* ------------------------------------------------------------ */
494    public int getMaxIdleTime()
495    {
496        return _maxIdleTime;
497    }
498
499    /* ------------------------------------------------------------ */
500    /**
501     * @see org.eclipse.jetty.io.bio.StreamEndPoint#setMaxIdleTime(int)
502     */
503    public void setMaxIdleTime(int timeMs) throws IOException
504    {
505        if (_socket!=null && timeMs!=_maxIdleTime)
506            _socket.setSoTimeout(timeMs>0?timeMs:0);
507        _maxIdleTime=timeMs;
508    }
509}
510