/*
 * ====================================================================
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.http.impl.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;

import org.apache.http.ConnectionClosedException;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpConnectionMetrics;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpInetConnection;
import org.apache.http.HttpMessage;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.config.MessageConstraints;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.entity.ContentLengthStrategy;
import org.apache.http.impl.HttpConnectionMetricsImpl;
import org.apache.http.impl.entity.LaxContentLengthStrategy;
import org.apache.http.impl.entity.StrictContentLengthStrategy;
import org.apache.http.impl.io.HttpTransportMetricsImpl;
import org.apache.http.impl.nio.codecs.ChunkDecoder;
import org.apache.http.impl.nio.codecs.ChunkEncoder;
import org.apache.http.impl.nio.codecs.IdentityDecoder;
import org.apache.http.impl.nio.codecs.IdentityEncoder;
import org.apache.http.impl.nio.codecs.LengthDelimitedDecoder;
import org.apache.http.impl.nio.codecs.LengthDelimitedEncoder;
import org.apache.http.impl.nio.reactor.SessionInputBufferImpl;
import org.apache.http.impl.nio.reactor.SessionOutputBufferImpl;
import org.apache.http.io.HttpTransportMetrics;
import org.apache.http.nio.ContentDecoder;
import org.apache.http.nio.ContentEncoder;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.reactor.EventMask;
import org.apache.http.nio.reactor.IOSession;
import org.apache.http.nio.reactor.SessionBufferStatus;
import org.apache.http.nio.reactor.SessionInputBuffer;
import org.apache.http.nio.reactor.SessionOutputBuffer;
import org.apache.http.nio.reactor.SocketAccessor;
import org.apache.http.nio.util.ByteBufferAllocator;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;
import org.apache.http.util.CharsetUtils;
import org.apache.http.util.NetUtils;

This class serves as a base for all NHttpConnection implementations and provides functionality common to both client and server HTTP connections.
Since:4.0
/** * This class serves as a base for all {@link NHttpConnection} implementations and provides * functionality common to both client and server HTTP connections. * * @since 4.0 */
@SuppressWarnings("deprecation") public class NHttpConnectionBase implements NHttpConnection, HttpInetConnection, SessionBufferStatus, SocketAccessor { protected final ContentLengthStrategy incomingContentStrategy; protected final ContentLengthStrategy outgoingContentStrategy; protected final SessionInputBufferImpl inbuf; protected final SessionOutputBufferImpl outbuf; private final int fragmentSizeHint; private final MessageConstraints constraints; protected final HttpTransportMetricsImpl inTransportMetrics; protected final HttpTransportMetricsImpl outTransportMetrics; protected final HttpConnectionMetricsImpl connMetrics; protected HttpContext context; protected IOSession session; protected SocketAddress remote; protected volatile ContentDecoder contentDecoder; protected volatile boolean hasBufferedInput; protected volatile ContentEncoder contentEncoder; protected volatile boolean hasBufferedOutput; protected volatile HttpRequest request; protected volatile HttpResponse response; protected volatile int status;
Creates a new instance of this class given the underlying I/O session.
Params:
  • session – the underlying I/O session.
  • allocator – byte buffer allocator.
  • params – HTTP parameters.
Deprecated:(4.3) use NHttpConnectionBase(IOSession, int, int, ByteBufferAllocator, CharsetDecoder, CharsetEncoder, ContentLengthStrategy, ContentLengthStrategy)
/** * Creates a new instance of this class given the underlying I/O session. * * @param session the underlying I/O session. * @param allocator byte buffer allocator. * @param params HTTP parameters. * * @deprecated (4.3) use * {@link NHttpConnectionBase#NHttpConnectionBase(IOSession, int, int, ByteBufferAllocator, * CharsetDecoder, CharsetEncoder, ContentLengthStrategy, ContentLengthStrategy)} */
@Deprecated public NHttpConnectionBase( final IOSession session, final ByteBufferAllocator allocator, final HttpParams params) { super(); Args.notNull(session, "I/O session"); Args.notNull(params, "HTTP params"); int bufferSize = params.getIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1); if (bufferSize <= 0) { bufferSize = 4096; } int lineBufferSize = bufferSize; if (lineBufferSize > 512) { lineBufferSize = 512; } CharsetDecoder decoder = null; CharsetEncoder encoder = null; Charset charset = CharsetUtils.lookup( (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET)); if (charset != null) { charset = Consts.ASCII; decoder = charset.newDecoder(); encoder = charset.newEncoder(); final CodingErrorAction malformedCharAction = (CodingErrorAction) params.getParameter( CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION); final CodingErrorAction unmappableCharAction = (CodingErrorAction) params.getParameter( CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION); decoder.onMalformedInput(malformedCharAction).onUnmappableCharacter(unmappableCharAction); encoder.onMalformedInput(malformedCharAction).onUnmappableCharacter(unmappableCharAction); } this.inbuf = new SessionInputBufferImpl(bufferSize, lineBufferSize, decoder, allocator); this.outbuf = new SessionOutputBufferImpl(bufferSize, lineBufferSize, encoder, allocator); this.fragmentSizeHint = bufferSize; this.constraints = MessageConstraints.DEFAULT; this.incomingContentStrategy = createIncomingContentStrategy(); this.outgoingContentStrategy = createOutgoingContentStrategy(); this.inTransportMetrics = createTransportMetrics(); this.outTransportMetrics = createTransportMetrics(); this.connMetrics = createConnectionMetrics( this.inTransportMetrics, this.outTransportMetrics); setSession(session); this.status = ACTIVE; }
Creates new instance NHttpConnectionBase given the underlying I/O session.
Params:
  • session – the underlying I/O session.
  • bufferSize – buffer size. Must be a positive number.
  • fragmentSizeHint – fragment size hint.
  • allocator – memory allocator. If null HeapByteBufferAllocator.INSTANCE will be used.
  • charDecoder – decoder to be used for decoding HTTP protocol elements. If null simple type cast will be used for byte to char conversion.
  • charEncoder – encoder to be used for encoding HTTP protocol elements. If null simple type cast will be used for char to byte conversion.
  • constraints – Message constraints. If null MessageConstraints.DEFAULT will be used.
  • incomingContentStrategy – incoming content length strategy. If null LaxContentLengthStrategy.INSTANCE will be used.
  • outgoingContentStrategy – outgoing content length strategy. If null StrictContentLengthStrategy.INSTANCE will be used.
Since:4.4
/** * Creates new instance NHttpConnectionBase given the underlying I/O session. * * @param session the underlying I/O session. * @param bufferSize buffer size. Must be a positive number. * @param fragmentSizeHint fragment size hint. * @param allocator memory allocator. * If {@code null} {@link org.apache.http.nio.util.HeapByteBufferAllocator#INSTANCE} * will be used. * @param charDecoder decoder to be used for decoding HTTP protocol elements. * If {@code null} simple type cast will be used for byte to char conversion. * @param charEncoder encoder to be used for encoding HTTP protocol elements. * If {@code null} simple type cast will be used for char to byte conversion. * @param constraints Message constraints. If {@code null} * {@link MessageConstraints#DEFAULT} will be used. * @param incomingContentStrategy incoming content length strategy. If {@code null} * {@link LaxContentLengthStrategy#INSTANCE} will be used. * @param outgoingContentStrategy outgoing content length strategy. If {@code null} * {@link StrictContentLengthStrategy#INSTANCE} will be used. * * @since 4.4 */
protected NHttpConnectionBase( final IOSession session, final int bufferSize, final int fragmentSizeHint, final ByteBufferAllocator allocator, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final MessageConstraints constraints, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy) { Args.notNull(session, "I/O session"); Args.positive(bufferSize, "Buffer size"); int lineBufferSize = bufferSize; if (lineBufferSize > 512) { lineBufferSize = 512; } this.inbuf = new SessionInputBufferImpl(bufferSize, lineBufferSize, charDecoder, allocator); this.outbuf = new SessionOutputBufferImpl(bufferSize, lineBufferSize, charEncoder, allocator); this.fragmentSizeHint = fragmentSizeHint >= 0 ? fragmentSizeHint : bufferSize; this.inTransportMetrics = new HttpTransportMetricsImpl(); this.outTransportMetrics = new HttpTransportMetricsImpl(); this.connMetrics = new HttpConnectionMetricsImpl(this.inTransportMetrics, this.outTransportMetrics); this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT; this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : LaxContentLengthStrategy.INSTANCE; this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : StrictContentLengthStrategy.INSTANCE; setSession(session); this.status = ACTIVE; }
Creates new instance NHttpConnectionBase given the underlying I/O session.
Params:
  • session – the underlying I/O session.
  • bufferSize – buffer size. Must be a positive number.
  • fragmentSizeHint – fragment size hint.
  • allocator – memory allocator. If null HeapByteBufferAllocator.INSTANCE will be used.
  • charDecoder – decoder to be used for decoding HTTP protocol elements. If null simple type cast will be used for byte to char conversion.
  • charEncoder – encoder to be used for encoding HTTP protocol elements. If null simple type cast will be used for char to byte conversion.
  • incomingContentStrategy – incoming content length strategy. If null LaxContentLengthStrategy.INSTANCE will be used.
  • outgoingContentStrategy – outgoing content length strategy. If null StrictContentLengthStrategy.INSTANCE will be used.
Since:4.3
/** * Creates new instance NHttpConnectionBase given the underlying I/O session. * * @param session the underlying I/O session. * @param bufferSize buffer size. Must be a positive number. * @param fragmentSizeHint fragment size hint. * @param allocator memory allocator. * If {@code null} {@link org.apache.http.nio.util.HeapByteBufferAllocator#INSTANCE} * will be used. * @param charDecoder decoder to be used for decoding HTTP protocol elements. * If {@code null} simple type cast will be used for byte to char conversion. * @param charEncoder encoder to be used for encoding HTTP protocol elements. * If {@code null} simple type cast will be used for char to byte conversion. * @param incomingContentStrategy incoming content length strategy. If {@code null} * {@link LaxContentLengthStrategy#INSTANCE} will be used. * @param outgoingContentStrategy outgoing content length strategy. If {@code null} * {@link StrictContentLengthStrategy#INSTANCE} will be used. * * @since 4.3 */
protected NHttpConnectionBase( final IOSession session, final int bufferSize, final int fragmentSizeHint, final ByteBufferAllocator allocator, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy) { this(session, bufferSize, fragmentSizeHint, allocator, charDecoder, charEncoder, null, incomingContentStrategy, outgoingContentStrategy); } private void setSession(final IOSession session) { this.session = session; this.context = new SessionHttpContext(this.session); this.session.setBufferStatus(this); this.remote = this.session.getRemoteAddress(); }
Binds the connection to a different IOSession. This may be necessary when the underlying I/O session gets upgraded with SSL/TLS encryption.
Since:4.2
/** * Binds the connection to a different {@link IOSession}. This may be necessary * when the underlying I/O session gets upgraded with SSL/TLS encryption. * * @since 4.2 */
protected void bind(final IOSession session) { Args.notNull(session, "I/O session"); setSession(session); }
Since:4.2
Deprecated:(4.3) use constructor.
/** * @since 4.2 * * @deprecated (4.3) use constructor. */
@Deprecated protected ContentLengthStrategy createIncomingContentStrategy() { return new LaxContentLengthStrategy(); }
Since:4.2
Deprecated:(4.3) use constructor.
/** * @since 4.2 * * @deprecated (4.3) use constructor. */
@Deprecated protected ContentLengthStrategy createOutgoingContentStrategy() { return new StrictContentLengthStrategy(); }
Since:4.1
Deprecated:(4.3) no longer used.
/** * @since 4.1 * * @deprecated (4.3) no longer used. */
@Deprecated protected HttpTransportMetricsImpl createTransportMetrics() { return new HttpTransportMetricsImpl(); }
Since:4.1
Deprecated:(4.3) use decorator to add additional metrics.
/** * @since 4.1 * * @deprecated (4.3) use decorator to add additional metrics. */
@Deprecated protected HttpConnectionMetricsImpl createConnectionMetrics( final HttpTransportMetrics inTransportMetric, final HttpTransportMetrics outTransportMetric) { return new HttpConnectionMetricsImpl(inTransportMetric, outTransportMetric); } @Override public int getStatus() { return this.status; } @Override public HttpContext getContext() { return this.context; } @Override public HttpRequest getHttpRequest() { return this.request; } @Override public HttpResponse getHttpResponse() { return this.response; } @Override public void requestInput() { this.session.setEvent(EventMask.READ); } @Override public void requestOutput() { this.session.setEvent(EventMask.WRITE); } @Override public void suspendInput() { this.session.clearEvent(EventMask.READ); } @Override public void suspendOutput() { synchronized (this.session) { if (!this.outbuf.hasData()) { this.session.clearEvent(EventMask.WRITE); } } }
Initializes a specific ContentDecoder implementation based on the properties of the given HttpMessage and generates an instance of HttpEntity matching the properties of the content decoder.
Params:
  • message – the HTTP message.
Throws:
Returns:HTTP entity.
/** * Initializes a specific {@link ContentDecoder} implementation based on the * properties of the given {@link HttpMessage} and generates an instance of * {@link HttpEntity} matching the properties of the content decoder. * * @param message the HTTP message. * @return HTTP entity. * @throws HttpException in case of an HTTP protocol violation. */
protected HttpEntity prepareDecoder(final HttpMessage message) throws HttpException { final BasicHttpEntity entity = new BasicHttpEntity(); final long len = this.incomingContentStrategy.determineLength(message); this.contentDecoder = createContentDecoder( len, this.session.channel(), this.inbuf, this.inTransportMetrics); if (len == ContentLengthStrategy.CHUNKED) { entity.setChunked(true); entity.setContentLength(-1); } else if (len == ContentLengthStrategy.IDENTITY) { entity.setChunked(false); entity.setContentLength(-1); } else { entity.setChunked(false); entity.setContentLength(len); } final Header contentTypeHeader = message.getFirstHeader(HTTP.CONTENT_TYPE); if (contentTypeHeader != null) { entity.setContentType(contentTypeHeader); } final Header contentEncodingHeader = message.getFirstHeader(HTTP.CONTENT_ENCODING); if (contentEncodingHeader != null) { entity.setContentEncoding(contentEncodingHeader); } return entity; }
Factory method for ContentDecoder instances.
Params:
Returns:content decoder.
Since:4.1
/** * Factory method for {@link ContentDecoder} instances. * * @param len content length, if known, {@link ContentLengthStrategy#CHUNKED} or * {@link ContentLengthStrategy#IDENTITY}, if unknown. * @param channel the session channel. * @param buffer the session buffer. * @param metrics transport metrics. * * @return content decoder. * * @since 4.1 */
protected ContentDecoder createContentDecoder( final long len, final ReadableByteChannel channel, final SessionInputBuffer buffer, final HttpTransportMetricsImpl metrics) { if (len == ContentLengthStrategy.CHUNKED) { return new ChunkDecoder(channel, buffer, this.constraints, metrics); } else if (len == ContentLengthStrategy.IDENTITY) { return new IdentityDecoder(channel, buffer, metrics); } else { return new LengthDelimitedDecoder(channel, buffer, metrics, len); } }
Initializes a specific ContentEncoder implementation based on the properties of the given HttpMessage.
Params:
  • message – the HTTP message.
Throws:
/** * Initializes a specific {@link ContentEncoder} implementation based on the * properties of the given {@link HttpMessage}. * * @param message the HTTP message. * @throws HttpException in case of an HTTP protocol violation. */
protected void prepareEncoder(final HttpMessage message) throws HttpException { final long len = this.outgoingContentStrategy.determineLength(message); this.contentEncoder = createContentEncoder( len, this.session.channel(), this.outbuf, this.outTransportMetrics); }
Factory method for ContentEncoder instances.
Params:
Returns:content encoder.
Since:4.1
/** * Factory method for {@link ContentEncoder} instances. * * @param len content length, if known, {@link ContentLengthStrategy#CHUNKED} or * {@link ContentLengthStrategy#IDENTITY}, if unknown. * @param channel the session channel. * @param buffer the session buffer. * @param metrics transport metrics. * * @return content encoder. * * @since 4.1 */
protected ContentEncoder createContentEncoder( final long len, final WritableByteChannel channel, final SessionOutputBuffer buffer, final HttpTransportMetricsImpl metrics) { if (len == ContentLengthStrategy.CHUNKED) { return new ChunkEncoder(channel, buffer, metrics, this.fragmentSizeHint); } else if (len == ContentLengthStrategy.IDENTITY) { return new IdentityEncoder(channel, buffer, metrics, this.fragmentSizeHint); } else { return new LengthDelimitedEncoder(channel, buffer, metrics, len, this.fragmentSizeHint); } } @Override public boolean hasBufferedInput() { return this.hasBufferedInput; } @Override public boolean hasBufferedOutput() { return this.hasBufferedOutput; }
Assets if the connection is still open.
Throws:
  • ConnectionClosedException – in case the connection has already been closed.
/** * Assets if the connection is still open. * * @throws ConnectionClosedException in case the connection has already * been closed. */
protected void assertNotClosed() throws ConnectionClosedException { if (this.status != ACTIVE) { throw new ConnectionClosedException(); } } @Override public void close() throws IOException { if (this.status != ACTIVE) { return; } this.status = CLOSING; if (this.outbuf.hasData()) { this.session.setEvent(EventMask.WRITE); } else { this.session.close(); this.status = CLOSED; } } @Override public boolean isOpen() { return this.status == ACTIVE && !this.session.isClosed(); } @Override public boolean isStale() { return this.session.isClosed(); } @Override public InetAddress getLocalAddress() { final SocketAddress address = this.session.getLocalAddress(); return address instanceof InetSocketAddress ? ((InetSocketAddress) address).getAddress() : null; } @Override public int getLocalPort() { final SocketAddress address = this.session.getLocalAddress(); return address instanceof InetSocketAddress ? ((InetSocketAddress) address).getPort() : -1; } @Override public InetAddress getRemoteAddress() { final SocketAddress address = this.session.getRemoteAddress(); return address instanceof InetSocketAddress ? ((InetSocketAddress) address).getAddress() : null; } @Override public int getRemotePort() { final SocketAddress address = this.session.getRemoteAddress(); return address instanceof InetSocketAddress ? ((InetSocketAddress) address).getPort() : -1; } @Override public void setSocketTimeout(final int timeout) { this.session.setSocketTimeout(timeout); } @Override public int getSocketTimeout() { return this.session.getSocketTimeout(); } @Override public void shutdown() throws IOException { this.status = CLOSED; this.session.shutdown(); } @Override public HttpConnectionMetrics getMetrics() { return this.connMetrics; } @Override public String toString() { final SocketAddress remoteAddress = this.session.getRemoteAddress(); final SocketAddress localAddress = this.session.getLocalAddress(); if (remoteAddress != null && localAddress != null) { final StringBuilder buffer = new StringBuilder(); NetUtils.formatAddress(buffer, localAddress); buffer.append("<->"); NetUtils.formatAddress(buffer, remoteAddress); return buffer.toString(); } return "[Not bound]"; } @Override public Socket getSocket() { return this.session instanceof SocketAccessor ? ((SocketAccessor) this.session).getSocket() : null; } }