View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.http.server;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStreamWriter;
24  import java.io.Writer;
25  import java.net.InetSocketAddress;
26  import java.net.URI;
27  import java.net.URISyntaxException;
28  import java.net.URLEncoder;
29  import java.nio.charset.Charset;
30  import java.security.Principal;
31  import java.util.Arrays;
32  import java.util.Enumeration;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  import javax.servlet.http.HttpServletRequest;
37  
38  import org.springframework.http.HttpHeaders;
39  import org.springframework.http.HttpMethod;
40  import org.springframework.http.MediaType;
41  import org.springframework.util.Assert;
42  import org.springframework.util.LinkedCaseInsensitiveMap;
43  import org.springframework.util.StringUtils;
44  
45  /**
46   * {@link ServerHttpRequest} implementation that is based on a {@link HttpServletRequest}.
47   *
48   * @author Arjen Poutsma
49   * @author Rossen Stoyanchev
50   * @since 3.0
51   */
52  public class ServletServerHttpRequest implements ServerHttpRequest {
53  
54  	protected static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
55  
56  	protected static final String FORM_CHARSET = "UTF-8";
57  
58  	private static final String METHOD_POST = "POST";
59  
60  
61  	private final HttpServletRequest servletRequest;
62  
63  	private HttpHeaders headers;
64  
65  	private ServerHttpAsyncRequestControl asyncRequestControl;
66  
67  
68  	/**
69  	 * Construct a new instance of the ServletServerHttpRequest based on the given {@link HttpServletRequest}.
70  	 * @param servletRequest the servlet request
71  	 */
72  	public ServletServerHttpRequest(HttpServletRequest servletRequest) {
73  		Assert.notNull(servletRequest, "HttpServletRequest must not be null");
74  		this.servletRequest = servletRequest;
75  	}
76  
77  
78  	/**
79  	 * Returns the {@code HttpServletRequest} this object is based on.
80  	 */
81  	public HttpServletRequest getServletRequest() {
82  		return this.servletRequest;
83  	}
84  
85  	@Override
86  	public HttpMethod getMethod() {
87  		return HttpMethod.valueOf(this.servletRequest.getMethod());
88  	}
89  
90  	@Override
91  	public URI getURI() {
92  		try {
93  			return new URI(this.servletRequest.getScheme(), null, this.servletRequest.getServerName(),
94  					this.servletRequest.getServerPort(), this.servletRequest.getRequestURI(),
95  					this.servletRequest.getQueryString(), null);
96  		}
97  		catch (URISyntaxException ex) {
98  			throw new IllegalStateException("Could not get HttpServletRequest URI: " + ex.getMessage(), ex);
99  		}
100 	}
101 
102 	@Override
103 	public HttpHeaders getHeaders() {
104 		if (this.headers == null) {
105 			this.headers = new HttpHeaders();
106 			for (Enumeration<?> headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
107 				String headerName = (String) headerNames.nextElement();
108 				for (Enumeration<?> headerValues = this.servletRequest.getHeaders(headerName);
109 						headerValues.hasMoreElements();) {
110 					String headerValue = (String) headerValues.nextElement();
111 					this.headers.add(headerName, headerValue);
112 				}
113 			}
114 			// HttpServletRequest exposes some headers as properties: we should include those if not already present
115 			MediaType contentType = this.headers.getContentType();
116 			if (contentType == null) {
117 				String requestContentType = this.servletRequest.getContentType();
118 				if (StringUtils.hasLength(requestContentType)) {
119 					contentType = MediaType.parseMediaType(requestContentType);
120 					this.headers.setContentType(contentType);
121 				}
122 			}
123 			if (contentType != null && contentType.getCharSet() == null) {
124 				String requestEncoding = this.servletRequest.getCharacterEncoding();
125 				if (StringUtils.hasLength(requestEncoding)) {
126 					Charset charSet = Charset.forName(requestEncoding);
127 					Map<String, String> params = new LinkedCaseInsensitiveMap<String>();
128 					params.putAll(contentType.getParameters());
129 					params.put("charset", charSet.toString());
130 					MediaType newContentType = new MediaType(contentType.getType(), contentType.getSubtype(), params);
131 					this.headers.setContentType(newContentType);
132 				}
133 			}
134 			if (this.headers.getContentLength() == -1) {
135 				int requestContentLength = this.servletRequest.getContentLength();
136 				if (requestContentLength != -1) {
137 					this.headers.setContentLength(requestContentLength);
138 				}
139 			}
140 		}
141 		return this.headers;
142 	}
143 
144 	@Override
145 	public Principal getPrincipal() {
146 		return this.servletRequest.getUserPrincipal();
147 	}
148 
149 	@Override
150 	public InetSocketAddress getLocalAddress() {
151 		return new InetSocketAddress(this.servletRequest.getLocalName(), this.servletRequest.getLocalPort());
152 	}
153 
154 	@Override
155 	public InetSocketAddress getRemoteAddress() {
156 		return new InetSocketAddress(this.servletRequest.getRemoteHost(), this.servletRequest.getRemotePort());
157 	}
158 
159 	@Override
160 	public InputStream getBody() throws IOException {
161 		if (isFormPost(this.servletRequest)) {
162 			return getBodyFromServletRequestParameters(this.servletRequest);
163 		}
164 		else {
165 			return this.servletRequest.getInputStream();
166 		}
167 	}
168 
169 	private boolean isFormPost(HttpServletRequest request) {
170 		return (request.getContentType() != null && request.getContentType().contains(FORM_CONTENT_TYPE) &&
171 				METHOD_POST.equalsIgnoreCase(request.getMethod()));
172 	}
173 
174 	/**
175 	 * Use {@link javax.servlet.ServletRequest#getParameterMap()} to reconstruct the
176 	 * body of a form 'POST' providing a predictable outcome as opposed to reading
177 	 * from the body, which can fail if any other code has used ServletRequest
178 	 * to access a parameter thus causing the input stream to be "consumed".
179 	 */
180 	private InputStream getBodyFromServletRequestParameters(HttpServletRequest request) throws IOException {
181 		ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
182 		Writer writer = new OutputStreamWriter(bos, FORM_CHARSET);
183 
184 		Map<String, String[]> form = request.getParameterMap();
185 		for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext();) {
186 			String name = nameIterator.next();
187 			List<String> values = Arrays.asList(form.get(name));
188 			for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
189 				String value = valueIterator.next();
190 				writer.write(URLEncoder.encode(name, FORM_CHARSET));
191 				if (value != null) {
192 					writer.write('=');
193 					writer.write(URLEncoder.encode(value, FORM_CHARSET));
194 					if (valueIterator.hasNext()) {
195 						writer.write('&');
196 					}
197 				}
198 			}
199 			if (nameIterator.hasNext()) {
200 				writer.append('&');
201 			}
202 		}
203 		writer.flush();
204 
205 		return new ByteArrayInputStream(bos.toByteArray());
206 	}
207 
208 	@Override
209 	public ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response) {
210 		if (this.asyncRequestControl == null) {
211 			Assert.isInstanceOf(ServletServerHttpResponse.class, response);
212 			ServletServerHttpResponse servletServerResponse = (ServletServerHttpResponse) response;
213 			this.asyncRequestControl = new ServletServerHttpAsyncRequestControl(this, servletServerResponse);
214 		}
215 		return this.asyncRequestControl;
216 	}
217 
218 }