View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.http.protocol.servlet;
22  
23  import com.google.common.io.ByteSource;
24  import org.waarp.common.logging.WaarpLogger;
25  import org.waarp.common.logging.WaarpLoggerFactory;
26  import org.waarp.common.utility.ParametersChecker;
27  import org.waarp.common.utility.WaarpStringUtils;
28  import org.waarp.common.utility.WaarpSystemUtil;
29  import org.waarp.http.protocol.HttpHelper;
30  import org.waarp.http.protocol.HttpResumableInfo;
31  import org.waarp.http.protocol.HttpResumableSession;
32  import org.waarp.http.protocol.HttpSessions;
33  
34  import javax.servlet.MultipartConfigElement;
35  import javax.servlet.ServletException;
36  import javax.servlet.annotation.MultipartConfig;
37  import javax.servlet.http.HttpServletRequest;
38  import javax.servlet.http.HttpServletResponse;
39  import javax.servlet.http.Part;
40  import java.io.IOException;
41  import java.io.InputStream;
42  import java.lang.reflect.InvocationTargetException;
43  import java.util.Collection;
44  import java.util.Enumeration;
45  import java.util.HashMap;
46  import java.util.Map;
47  
48  /**
49   * Upload Servlet: enables uploading file from Web Browser from final user
50   */
51  @MultipartConfig(fileSizeThreshold = 1024 * 1024)
52  public class UploadServlet extends AbstractServlet {
53    private static final long serialVersionUID = 2003L;
54    public static final String RESUMABLE_CHUNK_NUMBER = "resumableChunkNumber";
55    public static final String RESUMABLE_CHUNK_SIZE = "resumableChunkSize";
56    public static final String RESUMABLE_TOTAL_SIZE = "resumableTotalSize";
57    public static final String RESUMABLE_IDENTIFIER = "resumableIdentifier";
58    public static final String RESUMABLE_FILENAME = "resumableFilename";
59    public static final String RESUMABLE_RELATIVE_PATH = "resumableRelativePath";
60    public static final String FIELD_FILE = "file";
61    private static final WaarpLogger logger =
62        WaarpLoggerFactory.getLogger(UploadServlet.class);
63    private static final MultipartConfigElement MULTI_PART_CONFIG =
64        new MultipartConfigElement("/tmp");
65    private static final String __MULTIPART_CONFIG_ELEMENT =
66        "org.eclipse.jetty.multipartConfig";
67    protected static final String SHA_256 = "sha256";
68    protected static final String INVALID_BLOCK = "Invalid block.";
69  
70  
71    @Override
72    protected final void doPost(final HttpServletRequest request,
73                                final HttpServletResponse response)
74        throws ServletException {
75      // Check that we have a file upload request
76      final boolean isMultipart =
77          request.getHeader("Content-Type").contains("multipart/");
78      InputStream inputStream = null;
79      final HttpResumableInfo resumableInfo;
80      final HttpResumableSession session;
81      final Map<String, String> arguments = new HashMap<String, String>();
82      logger.debug("MULTIPART MODE? {} {}", isMultipart,
83                   request.getHeader("Content-Type"));
84      if (isMultipart) {
85        if (request.getAttribute(__MULTIPART_CONFIG_ELEMENT) == null) {
86          request.setAttribute(__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
87        }
88        try {
89          final Collection<Part> parts = request.getParts();
90          if (parts.isEmpty()) {
91            logger.warn("MULTIPART MODE BUT EMPTY");
92            inputStream = request.getInputStream();
93          } else {
94            for (final Part part : parts) {
95  
96              if (part.getName().equalsIgnoreCase(FIELD_FILE)) {
97                inputStream = part.getInputStream();
98              } else {
99                final InputStream finalInputStream = part.getInputStream();
100               final ByteSource byteSource = new ByteSource() {
101                 @Override
102                 public final InputStream openStream() {
103                   return finalInputStream;
104                 }
105               };
106 
107               final String valueText =
108                   byteSource.asCharSource(WaarpStringUtils.UTF8).read();
109               arguments.put(part.getName(), valueText);
110             }
111           }
112         }
113       } catch (final ServletException e) {
114         logger.warn("MULTIPART MODE BUT error {}", e.getMessage());
115         try {
116           inputStream = request.getInputStream();
117         } catch (final IOException e2) {
118           throw new ServletException(INVALID_BLOCK + ": " + e2.getMessage());
119         }
120       } catch (final IOException e) {
121         logger.warn("MULTIPART MODE BUT error {}", e.getMessage());
122         try {
123           inputStream = request.getInputStream();
124         } catch (final IOException e2) {
125           throw new ServletException(INVALID_BLOCK + ": " + e2.getMessage());
126         }
127       }
128     } else {
129       final Enumeration<String> names = request.getParameterNames();
130       while (names.hasMoreElements()) {
131         final String name = names.nextElement();
132         arguments.put(name, request.getParameter(name));
133       }
134       try {
135         inputStream = request.getInputStream();
136       } catch (final IOException e) {
137         throw new ServletException(INVALID_BLOCK + ": " + e.getMessage());
138       }
139     }
140     logger.warn("PARAMS: {}", arguments);
141     resumableInfo = getResumableInfo(arguments);
142     logger.debug("RECV: {}", resumableInfo);
143     try {
144       session = getResumableSession(arguments, resumableInfo);//NOSONAR
145     } catch (final ServletException e) {
146       logger.error(e.getMessage());
147       response.setStatus(400);
148       return;
149     }
150     logger.debug("SESSION: {}", session);
151     try {
152       if (!session.tryWrite(resumableInfo, inputStream)) {
153         throw new ServletException(INVALID_BLOCK);
154       }
155     } catch (final IOException e) {
156       throw new ServletException(INVALID_BLOCK + ": " + e.getMessage());
157     }
158 
159     String sha = arguments.get(SHA_256);
160     if (ParametersChecker.isNotEmpty(sha) &&
161         sha.equalsIgnoreCase("undefined")) {
162       sha = null;
163     }
164     try {
165       if (session.checkIfUploadFinished(sha)) {
166         // Check if all chunks uploaded, and change filename
167         logger.warn("ALL USER TRANSFER FINISHED: {}", session);
168         HttpSessions.getInstance().removeSession(session);
169         response.setStatus(200);
170         try {
171           response.getWriter().print("All finished.");
172         } catch (final IOException ignore) {
173           logger.debug(ignore);
174         }
175       } else {
176         logger.debug("PARTIAL UPLOAD: {}", session);
177         response.setStatus(201);
178         try {
179           response.getWriter().print("Upload");
180         } catch (final IOException ignore) {
181           logger.debug(ignore);
182         }
183       }
184     } catch (final IllegalArgumentException e) {
185       throw new ServletException(e);
186     }
187   }
188 
189   /**
190    * From the HttpServletRequest, build the HttpResumableInfo
191    *
192    * @param arguments Map of arguments
193    *
194    * @return the HttpResumableInfo
195    */
196   private HttpResumableInfo getResumableInfo(
197       final Map<String, String> arguments) {
198     final int resumableChunkNumber =
199         HttpHelper.toInt(arguments.get(RESUMABLE_CHUNK_NUMBER), -1);
200     final int resumableChunkSize =
201         HttpHelper.toInt(arguments.get(RESUMABLE_CHUNK_SIZE), -1);
202     final long resumableTotalSize =
203         HttpHelper.toLong(arguments.get(RESUMABLE_TOTAL_SIZE), -1);
204     final String resumableIdentifier = arguments.get(RESUMABLE_IDENTIFIER);
205     final String resumableFilename = arguments.get(RESUMABLE_FILENAME);
206     final String resumableRelativePath = arguments.get(RESUMABLE_RELATIVE_PATH);
207     return new HttpResumableInfo(resumableChunkNumber, resumableChunkSize,
208                                  resumableTotalSize, resumableIdentifier,
209                                  resumableFilename, resumableRelativePath);
210   }
211 
212   /**
213    * From the HttpServletRequest and the HttpResumableInfo, build the
214    * HttpResumableSession
215    *
216    * @param arguments Map of arguments
217    * @param resumableInfo
218    *
219    * @return the HttpResumableSession
220    *
221    * @throws ServletException
222    */
223   private HttpResumableSession getResumableSession(
224       final Map<String, String> arguments,
225       final HttpResumableInfo resumableInfo) throws ServletException {
226     final String rulename = arguments.get(RULENAME);
227     if (rulename == null) {
228       throw new ServletException(INVALID_REQUEST_PARAMS);
229     }
230     String comment = arguments.get(COMMENT);
231     if (comment == null) {
232       comment = "Web Upload " + resumableInfo.getIdentifier();
233     }
234     final HttpSessions sessions = HttpSessions.getInstance();
235 
236     try {
237       final HttpAuthent authent =
238           (HttpAuthent) WaarpSystemUtil.newInstance(authentClass);
239       authent.initializeAuthent(arguments);
240       final HttpResumableSession session =
241           sessions.getOrCreateResumableSession(resumableInfo, rulename, comment,
242                                                authent);
243       if (!session.valid(resumableInfo)) {
244         sessions.removeSession(resumableInfo);
245         throw new ServletException(INVALID_REQUEST_PARAMS);
246       }
247       return session;
248     } catch (final IllegalArgumentException e) {
249       throw new ServletException(
250           INVALID_REQUEST_PARAMS + ": " + e.getMessage());
251     } catch (final InvocationTargetException e) {
252       throw new ServletException(
253           INVALID_REQUEST_PARAMS + ": " + e.getMessage());
254     }
255   }
256 
257   @Override
258   protected final void doGet(final HttpServletRequest request,
259                              final HttpServletResponse response)
260       throws ServletException {
261     final Map<String, String> arguments = new HashMap<String, String>();
262     final Enumeration<String> names = request.getParameterNames();
263     while (names.hasMoreElements()) {
264       final String name = names.nextElement();
265       arguments.put(name, request.getParameter(name));
266     }
267     final HttpResumableInfo resumableInfo = getResumableInfo(arguments);
268     logger.debug("RECVGET: {}", resumableInfo);
269     final HttpResumableSession session;
270     try {
271       session = getResumableSession(arguments, resumableInfo);//NOSONAR
272     } catch (final ServletException e) {
273       logger.error(e.getMessage());
274       response.setStatus(400);
275       return;
276     }
277     logger.debug("SESSION: {}", session);
278     if (session.contains(resumableInfo)) {
279       logger.info("ALREADY: {}", session);
280       response.setStatus(200);
281       try {
282         response.getWriter().print("Uploaded."); //This Chunk has been Uploaded.
283       } catch (final IOException ignore) {
284         logger.debug(ignore);
285       }
286     } else {
287       logger.info("NOTDONE: {}", session);
288       response.setStatus(HttpServletResponse.SC_NOT_FOUND);
289     }
290   }
291 }