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.H1Config;
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 h1Config Message h1Config. If {@code null}
72       *   {@link H1Config#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 H1Config h1Config,
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(h1Config, charDecoder, charEncoder);
96          this.scheme = scheme;
97          this.requestParser = (requestParserFactory != null ? requestParserFactory :
98              DefaultHttpRequestParserFactory.INSTANCE).create(h1Config);
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 H1Config h1Config,
110             final CharsetDecoder charDecoder,
111             final CharsetEncoder charEncoder) {
112         this(scheme, h1Config, charDecoder, charEncoder, null, null, null, null);
113     }
114 
115     public DefaultBHttpServerConnection(
116             final String scheme,
117             final H1Config h1Config) {
118         this(scheme, h1Config, 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         final ProtocolVersion transportVersion = request.getVersion();
137         if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
138             throw new UnsupportedHttpVersionException(transportVersion);
139         }
140         request.setScheme(this.scheme);
141         this.version = transportVersion;
142         onRequestReceived(request);
143         incrementRequestCount();
144         return request;
145     }
146 
147     @Override
148     public void receiveRequestEntity(final ClassicHttpRequest request)
149             throws HttpException, IOException {
150         Args.notNull(request, "HTTP request");
151         final SocketHolder socketHolder = ensureOpen();
152 
153         final long len = this.incomingContentStrategy.determineLength(request);
154         if (len == ContentLengthStrategy.UNDEFINED) {
155             return;
156         }
157         final HttpEntity entity = createIncomingEntity(request, this.inBuffer, socketHolder.getInputStream(), len);
158         request.setEntity(entity);
159     }
160 
161     @Override
162     public void sendResponseHeader(final ClassicHttpResponse response)
163             throws HttpException, IOException {
164         Args.notNull(response, "HTTP response");
165         final SocketHolder socketHolder = ensureOpen();
166         this.responseWriter.write(response, this.outbuffer, socketHolder.getOutputStream());
167         onResponseSubmitted(response);
168         if (response.getCode() >= HttpStatus.SC_SUCCESS) {
169             incrementResponseCount();
170         }
171     }
172 
173     @Override
174     public void sendResponseEntity(final ClassicHttpResponse response)
175             throws HttpException, IOException {
176         Args.notNull(response, "HTTP response");
177         final SocketHolder socketHolder = ensureOpen();
178         final HttpEntity entity = response.getEntity();
179         if (entity == null) {
180             return;
181         }
182         final long len = this.outgoingContentStrategy.determineLength(response);
183         try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
184             entity.writeTo(outStream);
185         }
186     }
187 }