001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.multipart.support;
018
019import java.io.ByteArrayInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.nio.charset.Charset;
023
024import javax.servlet.http.HttpServletRequest;
025import javax.servlet.http.Part;
026
027import org.springframework.http.HttpHeaders;
028import org.springframework.http.MediaType;
029import org.springframework.http.server.ServerHttpRequest;
030import org.springframework.http.server.ServletServerHttpRequest;
031import org.springframework.lang.Nullable;
032import org.springframework.web.multipart.MultipartException;
033import org.springframework.web.multipart.MultipartFile;
034import org.springframework.web.multipart.MultipartHttpServletRequest;
035import org.springframework.web.multipart.MultipartResolver;
036
037/**
038 * {@link ServerHttpRequest} implementation that accesses one part of a multipart
039 * request. If using {@link MultipartResolver} configuration the part is accessed
040 * through a {@link MultipartFile}. Or if using Servlet 3.0 multipart processing
041 * the part is accessed through {@code ServletRequest.getPart}.
042 *
043 * @author Rossen Stoyanchev
044 * @author Juergen Hoeller
045 * @since 3.1
046 */
047public class RequestPartServletServerHttpRequest extends ServletServerHttpRequest {
048
049        private final MultipartHttpServletRequest multipartRequest;
050
051        private final String requestPartName;
052
053        private final HttpHeaders multipartHeaders;
054
055
056        /**
057         * Create a new {@code RequestPartServletServerHttpRequest} instance.
058         * @param request the current servlet request
059         * @param requestPartName the name of the part to adapt to the {@link ServerHttpRequest} contract
060         * @throws MissingServletRequestPartException if the request part cannot be found
061         * @throws MultipartException if MultipartHttpServletRequest cannot be initialized
062         */
063        public RequestPartServletServerHttpRequest(HttpServletRequest request, String requestPartName)
064                        throws MissingServletRequestPartException {
065
066                super(request);
067
068                this.multipartRequest = MultipartResolutionDelegate.asMultipartHttpServletRequest(request);
069                this.requestPartName = requestPartName;
070
071                HttpHeaders multipartHeaders = this.multipartRequest.getMultipartHeaders(requestPartName);
072                if (multipartHeaders == null) {
073                        throw new MissingServletRequestPartException(requestPartName);
074                }
075                this.multipartHeaders = multipartHeaders;
076        }
077
078
079        @Override
080        public HttpHeaders getHeaders() {
081                return this.multipartHeaders;
082        }
083
084        @Override
085        public InputStream getBody() throws IOException {
086                // Prefer Servlet Part resolution to cover file as well as parameter streams
087                boolean servletParts = (this.multipartRequest instanceof StandardMultipartHttpServletRequest);
088                if (servletParts) {
089                        Part part = retrieveServletPart();
090                        if (part != null) {
091                                return part.getInputStream();
092                        }
093                }
094
095                // Spring-style distinction between MultipartFile and String parameters
096                MultipartFile file = this.multipartRequest.getFile(this.requestPartName);
097                if (file != null) {
098                        return file.getInputStream();
099                }
100                String paramValue = this.multipartRequest.getParameter(this.requestPartName);
101                if (paramValue != null) {
102                        return new ByteArrayInputStream(paramValue.getBytes(determineCharset()));
103                }
104
105                // Fallback: Servlet Part resolution even if not indicated
106                if (!servletParts) {
107                        Part part = retrieveServletPart();
108                        if (part != null) {
109                                return part.getInputStream();
110                        }
111                }
112
113                throw new IllegalStateException("No body available for request part '" + this.requestPartName + "'");
114        }
115
116        @Nullable
117        private Part retrieveServletPart() {
118                try {
119                        return this.multipartRequest.getPart(this.requestPartName);
120                }
121                catch (Exception ex) {
122                        throw new MultipartException("Failed to retrieve request part '" + this.requestPartName + "'", ex);
123                }
124        }
125
126        private Charset determineCharset() {
127                MediaType contentType = getHeaders().getContentType();
128                if (contentType != null) {
129                        Charset charset = contentType.getCharset();
130                        if (charset != null) {
131                                return charset;
132                        }
133                }
134                String encoding = this.multipartRequest.getCharacterEncoding();
135                return (encoding != null ? Charset.forName(encoding) : FORM_CHARSET);
136        }
137
138}