View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http.impl.io;
29  
30  import java.io.IOException;
31  import java.io.OutputStream;
32  import java.net.Socket;
33  import java.nio.charset.CharsetDecoder;
34  import java.nio.charset.CharsetEncoder;
35  
36  import org.apache.hc.core5.http.ClassicHttpRequest;
37  import org.apache.hc.core5.http.ClassicHttpResponse;
38  import org.apache.hc.core5.http.ContentLengthStrategy;
39  import org.apache.hc.core5.http.HttpEntity;
40  import org.apache.hc.core5.http.HttpException;
41  import org.apache.hc.core5.http.HttpStatus;
42  import org.apache.hc.core5.http.HttpVersion;
43  import org.apache.hc.core5.http.ProtocolVersion;
44  import org.apache.hc.core5.http.UnsupportedHttpVersionException;
45  import org.apache.hc.core5.http.config.Http1Config;
46  import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
47  import org.apache.hc.core5.http.io.HttpMessageParser;
48  import org.apache.hc.core5.http.io.HttpMessageParserFactory;
49  import org.apache.hc.core5.http.io.HttpMessageWriter;
50  import org.apache.hc.core5.http.io.HttpMessageWriterFactory;
51  import org.apache.hc.core5.http.io.HttpServerConnection;
52  import org.apache.hc.core5.util.Args;
53  
54  /**
55   * Default implementation of {@link HttpServerConnection}.
56   *
57   * @since 4.3
58   */
59  public class DefaultBHttpServerConnection extends BHttpConnectionBase implements HttpServerConnection {
60  
61      private final String scheme;
62      private final ContentLengthStrategy incomingContentStrategy;
63      private final ContentLengthStrategy outgoingContentStrategy;
64      private final HttpMessageParser<ClassicHttpRequest> requestParser;
65      private final HttpMessageWriter<ClassicHttpResponse> responseWriter;
66  
67      /**
68       * Creates new instance of DefaultBHttpServerConnection.
69       *
70       * @param scheme protocol scheme
71       * @param http1Config Message http1Config. If {@code null}
72       *   {@link Http1Config#DEFAULT} will be used.
73       * @param charDecoder decoder to be used for decoding HTTP protocol elements.
74       *   If {@code null} simple type cast will be used for byte to char conversion.
75       * @param charEncoder encoder to be used for encoding HTTP protocol elements.
76       *   If {@code null} simple type cast will be used for char to byte conversion.
77       * @param incomingContentStrategy incoming content length strategy. If {@code null}
78       *   {@link DefaultContentLengthStrategy#INSTANCE} will be used.
79       * @param outgoingContentStrategy outgoing content length strategy. If {@code null}
80       *   {@link DefaultContentLengthStrategy#INSTANCE} will be used.
81       * @param requestParserFactory request parser factory. If {@code null}
82       *   {@link DefaultHttpRequestParserFactory#INSTANCE} will be used.
83       * @param responseWriterFactory response writer factory. If {@code null}
84       *   {@link DefaultHttpResponseWriterFactory#INSTANCE} will be used.
85       */
86      public DefaultBHttpServerConnection(
87              final String scheme,
88              final Http1Config http1Config,
89              final CharsetDecoder charDecoder,
90              final CharsetEncoder charEncoder,
91              final ContentLengthStrategy incomingContentStrategy,
92              final ContentLengthStrategy outgoingContentStrategy,
93              final HttpMessageParserFactory<ClassicHttpRequest> requestParserFactory,
94              final HttpMessageWriterFactory<ClassicHttpResponse> responseWriterFactory) {
95          super(http1Config, charDecoder, charEncoder);
96          this.scheme = scheme;
97          this.requestParser = (requestParserFactory != null ? requestParserFactory :
98              DefaultHttpRequestParserFactory.INSTANCE).create(http1Config);
99          this.responseWriter = (responseWriterFactory != null ? responseWriterFactory :
100             DefaultHttpResponseWriterFactory.INSTANCE).create();
101         this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy :
102                 DefaultContentLengthStrategy.INSTANCE;
103         this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy :
104                 DefaultContentLengthStrategy.INSTANCE;
105     }
106 
107     public DefaultBHttpServerConnection(
108             final String scheme,
109             final Http1Config http1Config,
110             final CharsetDecoder charDecoder,
111             final CharsetEncoder charEncoder) {
112         this(scheme, http1Config, charDecoder, charEncoder, null, null, null, null);
113     }
114 
115     public DefaultBHttpServerConnection(
116             final String scheme,
117             final Http1Config http1Config) {
118         this(scheme, http1Config, null, null);
119     }
120 
121     protected void onRequestReceived(final ClassicHttpRequest request) {
122     }
123 
124     protected void onResponseSubmitted(final ClassicHttpResponse response) {
125     }
126 
127     @Override
128     public void bind(final Socket socket) throws IOException {
129         super.bind(socket);
130     }
131 
132     @Override
133     public ClassicHttpRequest receiveRequestHeader() throws HttpException, IOException {
134         final SocketHolder socketHolder = ensureOpen();
135         final ClassicHttpRequest request = this.requestParser.parse(this.inBuffer, socketHolder.getInputStream());
136         if (request == null) {
137             return null;
138         }
139         final ProtocolVersion transportVersion = request.getVersion();
140         if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
141             throw new UnsupportedHttpVersionException(transportVersion);
142         }
143         request.setScheme(this.scheme);
144         this.version = transportVersion;
145         onRequestReceived(request);
146         incrementRequestCount();
147         return request;
148     }
149 
150     @Override
151     public void receiveRequestEntity(final ClassicHttpRequest request)
152             throws HttpException, IOException {
153         Args.notNull(request, "HTTP request");
154         final SocketHolder socketHolder = ensureOpen();
155 
156         final long len = this.incomingContentStrategy.determineLength(request);
157         if (len == ContentLengthStrategy.UNDEFINED) {
158             return;
159         }
160         request.setEntity(createIncomingEntity(request, this.inBuffer, socketHolder.getInputStream(), len));
161     }
162 
163     @Override
164     public void sendResponseHeader(final ClassicHttpResponse response)
165             throws HttpException, IOException {
166         Args.notNull(response, "HTTP response");
167         final SocketHolder socketHolder = ensureOpen();
168         this.responseWriter.write(response, this.outbuffer, socketHolder.getOutputStream());
169         onResponseSubmitted(response);
170         if (response.getCode() >= HttpStatus.SC_SUCCESS) {
171             incrementResponseCount();
172         }
173     }
174 
175     @Override
176     public void sendResponseEntity(final ClassicHttpResponse response)
177             throws HttpException, IOException {
178         Args.notNull(response, "HTTP response");
179         final SocketHolder socketHolder = ensureOpen();
180         final HttpEntity entity = response.getEntity();
181         if (entity == null) {
182             return;
183         }
184         final long len = this.outgoingContentStrategy.determineLength(response);
185         try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
186             entity.writeTo(outStream);
187         }
188     }
189 }