001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.impl.nio; 029 030import java.io.IOException; 031import java.nio.channels.SelectionKey; 032import java.nio.charset.CharsetDecoder; 033import java.nio.charset.CharsetEncoder; 034 035import org.apache.http.HttpEntity; 036import org.apache.http.HttpEntityEnclosingRequest; 037import org.apache.http.HttpException; 038import org.apache.http.HttpRequest; 039import org.apache.http.HttpRequestFactory; 040import org.apache.http.HttpResponse; 041import org.apache.http.config.MessageConstraints; 042import org.apache.http.entity.ContentLengthStrategy; 043import org.apache.http.impl.entity.DisallowIdentityContentLengthStrategy; 044import org.apache.http.impl.entity.LaxContentLengthStrategy; 045import org.apache.http.impl.entity.StrictContentLengthStrategy; 046import org.apache.http.impl.nio.codecs.DefaultHttpRequestParser; 047import org.apache.http.impl.nio.codecs.DefaultHttpRequestParserFactory; 048import org.apache.http.impl.nio.codecs.DefaultHttpResponseWriter; 049import org.apache.http.impl.nio.codecs.DefaultHttpResponseWriterFactory; 050import org.apache.http.nio.NHttpMessageParser; 051import org.apache.http.nio.NHttpMessageParserFactory; 052import org.apache.http.nio.NHttpMessageWriter; 053import org.apache.http.nio.NHttpMessageWriterFactory; 054import org.apache.http.nio.NHttpServerEventHandler; 055import org.apache.http.nio.NHttpServerIOTarget; 056import org.apache.http.nio.NHttpServiceHandler; 057import org.apache.http.nio.reactor.EventMask; 058import org.apache.http.nio.reactor.IOSession; 059import org.apache.http.nio.reactor.SessionInputBuffer; 060import org.apache.http.nio.reactor.SessionOutputBuffer; 061import org.apache.http.nio.util.ByteBufferAllocator; 062import org.apache.http.params.HttpParamConfig; 063import org.apache.http.params.HttpParams; 064import org.apache.http.util.Args; 065 066/** 067 * Default implementation of the {@link org.apache.http.nio.NHttpServerConnection} 068 * interface. 069 * 070 * @since 4.0 071 */ 072@SuppressWarnings("deprecation") 073public class DefaultNHttpServerConnection 074 extends NHttpConnectionBase implements NHttpServerIOTarget { 075 076 protected final NHttpMessageParser<HttpRequest> requestParser; 077 protected final NHttpMessageWriter<HttpResponse> responseWriter; 078 079 /** 080 * Creates a new instance of this class given the underlying I/O session. 081 * 082 * @param session the underlying I/O session. 083 * @param requestFactory HTTP request factory. 084 * @param allocator byte buffer allocator. 085 * @param params HTTP parameters. 086 * 087 * @deprecated (4.3) use {@link DefaultNHttpServerConnection#DefaultNHttpServerConnection( 088 * IOSession, int, int, ByteBufferAllocator, CharsetDecoder, CharsetEncoder, 089 * MessageConstraints, ContentLengthStrategy, ContentLengthStrategy, 090 * NHttpMessageParserFactory, NHttpMessageWriterFactory)} 091 */ 092 @Deprecated 093 public DefaultNHttpServerConnection( 094 final IOSession session, 095 final HttpRequestFactory requestFactory, 096 final ByteBufferAllocator allocator, 097 final HttpParams params) { 098 super(session, allocator, params); 099 Args.notNull(requestFactory, "Request factory"); 100 this.requestParser = createRequestParser(this.inbuf, requestFactory, params); 101 this.responseWriter = createResponseWriter(this.outbuf, params); 102 } 103 104 /** 105 * Creates new instance DefaultNHttpServerConnection given the underlying I/O session. 106 * 107 * @param session the underlying I/O session. 108 * @param buffersize buffer size. Must be a positive number. 109 * @param fragmentSizeHint fragment size hint. 110 * @param allocator memory allocator. 111 * If {@code null} {@link org.apache.http.nio.util.HeapByteBufferAllocator#INSTANCE} 112 * will be used. 113 * @param chardecoder decoder to be used for decoding HTTP protocol elements. 114 * If {@code null} simple type cast will be used for byte to char conversion. 115 * @param charencoder encoder to be used for encoding HTTP protocol elements. 116 * If {@code null} simple type cast will be used for char to byte conversion. 117 * @param constraints Message constraints. If {@code null} 118 * {@link MessageConstraints#DEFAULT} will be used. 119 * @param incomingContentStrategy incoming content length strategy. If {@code null} 120 * {@link DisallowIdentityContentLengthStrategy#INSTANCE} will be used. 121 * @param outgoingContentStrategy outgoing content length strategy. If {@code null} 122 * {@link StrictContentLengthStrategy#INSTANCE} will be used. 123 * @param requestParserFactory request parser factory. If {@code null} 124 * {@link DefaultHttpRequestParserFactory#INSTANCE} will be used. 125 * @param responseWriterFactory response writer factory. If {@code null} 126 * {@link DefaultHttpResponseWriterFactory#INSTANCE} will be used. 127 * 128 * @since 4.3 129 */ 130 public DefaultNHttpServerConnection( 131 final IOSession session, 132 final int buffersize, 133 final int fragmentSizeHint, 134 final ByteBufferAllocator allocator, 135 final CharsetDecoder chardecoder, 136 final CharsetEncoder charencoder, 137 final MessageConstraints constraints, 138 final ContentLengthStrategy incomingContentStrategy, 139 final ContentLengthStrategy outgoingContentStrategy, 140 final NHttpMessageParserFactory<HttpRequest> requestParserFactory, 141 final NHttpMessageWriterFactory<HttpResponse> responseWriterFactory) { 142 super(session, buffersize, fragmentSizeHint, allocator, chardecoder, charencoder, 143 constraints, 144 incomingContentStrategy != null ? incomingContentStrategy : 145 DisallowIdentityContentLengthStrategy.INSTANCE, 146 outgoingContentStrategy != null ? outgoingContentStrategy : 147 StrictContentLengthStrategy.INSTANCE); 148 this.requestParser = (requestParserFactory != null ? requestParserFactory : 149 DefaultHttpRequestParserFactory.INSTANCE).create(this.inbuf, constraints); 150 this.responseWriter = (responseWriterFactory != null ? responseWriterFactory : 151 DefaultHttpResponseWriterFactory.INSTANCE).create(this.outbuf); 152 } 153 154 /** 155 * @since 4.3 156 */ 157 public DefaultNHttpServerConnection( 158 final IOSession session, 159 final int buffersize, 160 final CharsetDecoder chardecoder, 161 final CharsetEncoder charencoder, 162 final MessageConstraints constraints) { 163 this(session, buffersize, buffersize, null, chardecoder, charencoder, constraints, 164 null, null, null, null); 165 } 166 167 /** 168 * @since 4.3 169 */ 170 public DefaultNHttpServerConnection(final IOSession session, final int buffersize) { 171 this(session, buffersize, buffersize, null, null, null, null, null, null, null, null); 172 } 173 174 /** 175 * @deprecated (4.3) use constructor. 176 */ 177 @Override 178 @Deprecated 179 protected ContentLengthStrategy createIncomingContentStrategy() { 180 return new DisallowIdentityContentLengthStrategy(new LaxContentLengthStrategy(0)); 181 } 182 183 /** 184 * Creates an instance of {@link NHttpMessageParser} to be used 185 * by this connection for parsing incoming {@link HttpRequest} messages. 186 * <p> 187 * This method can be overridden in a super class in order to provide 188 * a different implementation of the {@link NHttpMessageParser} interface. 189 * 190 * @return HTTP response parser. 191 * 192 * @deprecated (4.3) use constructor. 193 */ 194 @Deprecated 195 protected NHttpMessageParser<HttpRequest> createRequestParser( 196 final SessionInputBuffer buffer, 197 final HttpRequestFactory requestFactory, 198 final HttpParams params) { 199 final MessageConstraints constraints = HttpParamConfig.getMessageConstraints(params); 200 return new DefaultHttpRequestParser(buffer, null, requestFactory, constraints); 201 } 202 203 /** 204 * Creates an instance of {@link NHttpMessageWriter} to be used 205 * by this connection for writing out outgoing {@link HttpResponse} 206 * messages. 207 * <p> 208 * This method can be overridden by a super class in order to provide 209 * a different implementation of the {@link NHttpMessageWriter} interface. 210 * 211 * @return HTTP response parser. 212 * 213 * @deprecated (4.3) use constructor. 214 */ 215 @Deprecated 216 protected NHttpMessageWriter<HttpResponse> createResponseWriter( 217 final SessionOutputBuffer buffer, 218 final HttpParams params) { 219 // override in derived class to specify a line formatter 220 return new DefaultHttpResponseWriter(buffer, null); 221 } 222 223 /** 224 * @since 4.2 225 */ 226 protected void onRequestReceived(final HttpRequest request) { 227 } 228 229 /** 230 * @since 4.2 231 */ 232 protected void onResponseSubmitted(final HttpResponse response) { 233 } 234 235 @Override 236 public void resetInput() { 237 this.request = null; 238 this.contentDecoder = null; 239 this.requestParser.reset(); 240 } 241 242 @Override 243 public void resetOutput() { 244 this.response = null; 245 this.contentEncoder = null; 246 this.responseWriter.reset(); 247 } 248 249 public void consumeInput(final NHttpServerEventHandler handler) { 250 if (this.status != ACTIVE) { 251 this.session.clearEvent(EventMask.READ); 252 return; 253 } 254 try { 255 if (this.request == null) { 256 int bytesRead; 257 do { 258 bytesRead = this.requestParser.fillBuffer(this.session.channel()); 259 if (bytesRead > 0) { 260 this.inTransportMetrics.incrementBytesTransferred(bytesRead); 261 } 262 this.request = this.requestParser.parse(); 263 } while (bytesRead > 0 && this.request == null); 264 if (this.request != null) { 265 if (this.request instanceof HttpEntityEnclosingRequest) { 266 // Receive incoming entity 267 final HttpEntity entity = prepareDecoder(this.request); 268 ((HttpEntityEnclosingRequest)this.request).setEntity(entity); 269 } 270 this.connMetrics.incrementRequestCount(); 271 this.hasBufferedInput = this.inbuf.hasData(); 272 onRequestReceived(this.request); 273 handler.requestReceived(this); 274 if (this.contentDecoder == null) { 275 // No request entity is expected 276 // Ready to receive a new request 277 resetInput(); 278 } 279 } 280 if (bytesRead == -1 && !this.inbuf.hasData()) { 281 handler.endOfInput(this); 282 } 283 } 284 if (this.contentDecoder != null && (this.session.getEventMask() & SelectionKey.OP_READ) > 0) { 285 handler.inputReady(this, this.contentDecoder); 286 if (this.contentDecoder.isCompleted()) { 287 // Request entity received 288 // Ready to receive a new request 289 resetInput(); 290 } 291 } 292 } catch (final HttpException ex) { 293 resetInput(); 294 handler.exception(this, ex); 295 } catch (final Exception ex) { 296 handler.exception(this, ex); 297 } finally { 298 // Finally set buffered input flag 299 this.hasBufferedInput = this.inbuf.hasData(); 300 } 301 } 302 303 public void produceOutput(final NHttpServerEventHandler handler) { 304 try { 305 if (this.status == ACTIVE) { 306 if (this.contentEncoder == null && !this.outbuf.hasData()) { 307 handler.responseReady(this); 308 } 309 if (this.contentEncoder != null) { 310 handler.outputReady(this, this.contentEncoder); 311 if (this.contentEncoder.isCompleted()) { 312 resetOutput(); 313 } 314 } 315 } 316 if (this.outbuf.hasData()) { 317 final int bytesWritten = this.outbuf.flush(this.session.channel()); 318 if (bytesWritten > 0) { 319 this.outTransportMetrics.incrementBytesTransferred(bytesWritten); 320 } 321 } 322 if (!this.outbuf.hasData()) { 323 if (this.status == CLOSING) { 324 this.session.close(); 325 this.status = CLOSED; 326 resetOutput(); 327 } 328 } 329 } catch (final Exception ex) { 330 handler.exception(this, ex); 331 } finally { 332 // Finally set the buffered output flag 333 this.hasBufferedOutput = this.outbuf.hasData(); 334 } 335 } 336 337 @Override 338 public void submitResponse(final HttpResponse response) throws IOException, HttpException { 339 Args.notNull(response, "HTTP response"); 340 assertNotClosed(); 341 if (this.response != null) { 342 throw new HttpException("Response already submitted"); 343 } 344 onResponseSubmitted(response); 345 this.responseWriter.write(response); 346 this.hasBufferedOutput = this.outbuf.hasData(); 347 348 if (response.getStatusLine().getStatusCode() >= 200) { 349 this.connMetrics.incrementResponseCount(); 350 if (response.getEntity() != null) { 351 this.response = response; 352 prepareEncoder(response); 353 } 354 } 355 356 this.session.setEvent(EventMask.WRITE); 357 } 358 359 @Override 360 public boolean isResponseSubmitted() { 361 return this.response != null; 362 } 363 364 @Override 365 public void consumeInput(final NHttpServiceHandler handler) { 366 consumeInput(new NHttpServerEventHandlerAdaptor(handler)); 367 } 368 369 @Override 370 public void produceOutput(final NHttpServiceHandler handler) { 371 produceOutput(new NHttpServerEventHandlerAdaptor(handler)); 372 } 373 374}