001package com.hfg.webapp; 002 003 004import java.io.BufferedReader; 005import java.io.ByteArrayInputStream; 006import java.io.ByteArrayOutputStream; 007import java.io.File; 008import java.io.IOException; 009import java.io.InputStream; 010import java.io.OutputStream; 011import java.io.UnsupportedEncodingException; 012import java.net.SocketTimeoutException; 013import java.net.URLDecoder; 014import java.security.Principal; 015import java.util.Collection; 016import java.util.Collections; 017import java.util.Enumeration; 018import java.util.List; 019import java.util.Locale; 020import java.util.Map; 021import java.util.zip.GZIPInputStream; 022import java.util.zip.GZIPOutputStream; 023import javax.servlet.AsyncContext; 024import javax.servlet.DispatcherType; 025import javax.servlet.RequestDispatcher; 026import javax.servlet.ServletContext; 027import javax.servlet.ServletException; 028import javax.servlet.ServletInputStream; 029import javax.servlet.ServletRequest; 030import javax.servlet.ServletResponse; 031import javax.servlet.http.Cookie; 032import javax.servlet.http.HttpServletRequest; 033import javax.servlet.http.HttpServletResponse; 034import javax.servlet.http.HttpSession; 035import javax.servlet.http.Part; 036 037import com.hfg.util.StringUtil; 038import com.hfg.util.collection.OrderedMap; 039import com.hfg.util.io.RuntimeIOException; 040import com.hfg.util.mime.MimeEntity; 041import com.hfg.util.mime.MimeType; 042import com.hfg.util.mime.MultipartMimeParser; 043 044//------------------------------------------------------------------------------ 045/** 046 Wrapper for the standard HttpServletRequest that adds support for multi-part mime uploads. 047 <div> 048 @author J. Alex Taylor, hairyfatguy.com 049 </div> 050 */ 051//------------------------------------------------------------------------------ 052// com.hfg XML/HTML Coding Library 053// 054// This library is free software; you can redistribute it and/or 055// modify it under the terms of the GNU Lesser General Public 056// License as published by the Free Software Foundation; either 057// version 2.1 of the License, or (at your option) any later version. 058// 059// This library is distributed in the hope that it will be useful, 060// but WITHOUT ANY WARRANTY; without even the implied warranty of 061// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 062// Lesser General Public License for more details. 063// 064// You should have received a copy of the GNU Lesser General Public 065// License along with this library; if not, write to the Free Software 066// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 067// 068// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 069// jataylor@hairyfatguy.com 070//------------------------------------------------------------------------------ 071 072public class HfgHttpServletRequest implements HttpServletRequest 073{ 074 private HttpServletRequest mRequest; 075 private File mTmpDir; 076 private File mTmpDirRoot = sDefaultTmpDirRoot; 077 078 private Map<String, String[]> mParameterMap; 079 private CachedInputStream mInputStream; 080 private byte[] mCompressedInput; 081 082 private static File sDefaultTmpDirRoot = new File("/tmp"); 083 084 //########################################################################### 085 // CONSTRUCTORS 086 //########################################################################### 087 088 //--------------------------------------------------------------------------- 089 public HfgHttpServletRequest(HttpServletRequest inRequest) 090 throws IOException 091 { 092 mRequest = inRequest; 093 094 init(); 095 } 096 097 //--------------------------------------------------------------------------- 098 private void init() 099 throws IOException 100 { 101 mParameterMap = new OrderedMap<>(50); 102 103 // GET params? 104 if (StringUtil.isSet(mRequest.getQueryString())) 105 { 106 extractParamsFromQueryString(mRequest.getQueryString()); 107 } 108 109 // POST params? 110 if (mRequest.getMethod().equalsIgnoreCase("POST")) 111 { 112 if (null == mRequest.getContentType() 113 || mRequest.getContentType().startsWith(MimeType.APPLICATION_WWW_FORM_URLENCODED.toString())) 114 { 115 for (String key : mRequest.getParameterMap().keySet()) 116 { 117 setParameter(key, mRequest.getParameterValues(key)); 118 } 119 } 120 else if (mRequest.getContentType().startsWith(MimeType.MULTIPART_FORM_DATA.toString())) 121 { 122 try 123 { 124 // Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryqLdKEwR9tfg86rj1 125 if (MultipartMimeParser.isMultipartMimeRequest(mRequest)) 126 { 127 MultipartMimeParser multipartMimeParser = new MultipartMimeParser().setFileCacheDir(getTmpDir()); 128 List<MimeEntity> mimeEntities = multipartMimeParser.parse(mRequest); 129 for (MimeEntity mimeEntity : mimeEntities) 130 { 131 if (mimeEntity.getCachedContentFile() != null) 132 { 133 addParameter(mimeEntity.getContentDisposition().getName(), mimeEntity.getCachedContentFile().getPath()); 134 } 135 else 136 { 137 addParameter(mimeEntity.getContentDisposition().getName(), mimeEntity.getContent()); 138 } 139 } 140 } 141 } 142 catch (SocketTimeoutException e) 143 { 144 throw e; // This isn't our fault 145 } 146 catch (IOException e) 147 { 148 throw new RuntimeIOException("Problem extracting multi-part mime data!", e); 149 } 150 } 151 } 152 } 153 154 //########################################################################### 155 // PUBLIC METHODS 156 //########################################################################### 157 158 //--------------------------------------------------------------------------- 159 public static void setDefaultTmpDirRoot(File inDir) 160 { 161 sDefaultTmpDirRoot = inDir; 162 } 163 164 //--------------------------------------------------------------------------- 165 public static File getDefaultTmpDirRoot() 166 { 167 return sDefaultTmpDirRoot; 168 } 169 170 //--------------------------------------------------------------------------- 171 public void setTmpDirRoot(File inDir) 172 { 173 mTmpDirRoot = inDir; 174 } 175 176 //--------------------------------------------------------------------------- 177 public File getTmpDirRoot() 178 { 179 return mTmpDirRoot; 180 } 181 182 //--------------------------------------------------------------------------- 183 public String[] removeParameter(String inName) 184 { 185 String[] result = null; 186 if (mParameterMap != null) 187 { 188 result = mParameterMap.remove(inName); 189 } 190 191 return result; 192 } 193 194 //--------------------------------------------------------------------------- 195 public File getFileParameter(String inName) 196 { 197 File file = null; 198 199 String filename = getParameter(inName); 200 if (StringUtil.isSet(filename)) 201 { 202 file = new File(filename); 203 } 204 205 return file; 206 } 207 208 //--------------------------------------------------------------------------- 209 public HfgHttpServletRequest setParameter(String inName, String[] inValues) 210 { 211 if (null == mParameterMap) 212 { 213 mParameterMap = new OrderedMap<>(); 214 } 215 216 mParameterMap.put(inName, inValues); 217 218 return this; 219 } 220 221 //--------------------------------------------------------------------------- 222 public HfgHttpServletRequest addParameter(String inName, String inValue) 223 { 224 String[] existingValues = getParameterValues(inName); 225 String[] newValues = new String[existingValues != null ? existingValues.length + 1 : 1]; 226 if (existingValues != null) 227 { 228 System.arraycopy(existingValues, 0, newValues, 0, existingValues.length); 229 } 230 231 newValues[newValues.length - 1] = inValue; 232 233 setParameter(inName, newValues); 234 235 return this; 236 } 237 238 //--------------------------------------------------------------------------- 239 public void resetCachedInputStream() 240 { 241 mInputStream = null; 242 } 243 244 //--------------------------------------------------------------------------- 245 public InputStream getCachedInputStream() 246 throws IOException 247 { 248 if (null == mInputStream) 249 { 250 if (null == mCompressedInput) 251 { 252 // Read the whole input stream 253 InputStream inStream = mRequest.getInputStream(); 254 255 ByteArrayOutputStream compressedByteStream = new ByteArrayOutputStream(); 256 OutputStream outStream = new GZIPOutputStream(compressedByteStream); 257 258 int read = 0; 259 byte[] buffer = new byte[1024]; 260 while ((read = inStream.read(buffer)) != -1) 261 { 262 outStream.write(buffer, 0, read); 263 } 264 outStream.close(); 265 mCompressedInput = compressedByteStream.toByteArray(); 266 } 267 268 mInputStream = new CachedInputStream(new GZIPInputStream(new ByteArrayInputStream(mCompressedInput))); 269 } 270 271 return mInputStream; 272 } 273 274 //--------------------------------------------------------------------------- 275 @Override 276 public String getAuthType() 277 { 278 return mRequest.getAuthType(); 279 } 280 281 @Override 282 public Cookie[] getCookies() 283 { 284 return mRequest.getCookies(); 285 } 286 287 @Override 288 public long getDateHeader(String s) 289 { 290 return mRequest.getDateHeader(s); 291 } 292 293 @Override 294 public String getHeader(String s) 295 { 296 return mRequest.getHeader(s); 297 } 298 299 @Override 300 public Enumeration<String> getHeaders(String s) 301 { 302 return mRequest.getHeaders(s); 303 } 304 305 @Override 306 public Enumeration<String> getHeaderNames() 307 { 308 return mRequest.getHeaderNames(); 309 } 310 311 @Override 312 public int getIntHeader(String s) 313 { 314 return mRequest.getIntHeader(s); 315 } 316 317 @Override 318 public String getMethod() 319 { 320 return mRequest.getMethod(); 321 } 322 323 @Override 324 public String getPathInfo() 325 { 326 return mRequest.getPathInfo(); 327 } 328 329 @Override 330 public String getPathTranslated() 331 { 332 return mRequest.getPathTranslated(); 333 } 334 335 @Override 336 public String getContextPath() 337 { 338 return mRequest.getContextPath(); 339 } 340 341 @Override 342 public String getQueryString() 343 { 344 return mRequest.getQueryString(); 345 } 346 347 @Override 348 public String getRemoteUser() 349 { 350 return mRequest.getRemoteUser(); 351 } 352 353 @Override 354 public boolean isUserInRole(String s) 355 { 356 return mRequest.isUserInRole(s); 357 } 358 359 @Override 360 public Principal getUserPrincipal() 361 { 362 return mRequest.getUserPrincipal(); 363 } 364 365 @Override 366 public String getRequestedSessionId() 367 { 368 return mRequest.getRequestedSessionId(); 369 } 370 371 @Override 372 public String getRequestURI() 373 { 374 return mRequest.getRequestURI(); 375 } 376 377 @Override 378 public StringBuffer getRequestURL() 379 { 380 return mRequest.getRequestURL(); 381 } 382 383 @Override 384 public String getServletPath() 385 { 386 return mRequest.getServletPath(); 387 } 388 389 @Override 390 public HttpSession getSession(boolean b) 391 { 392 return mRequest.getSession(b); 393 } 394 395 @Override 396 public HttpSession getSession() 397 { 398 return mRequest.getSession(); 399 } 400 401 @Override 402 public boolean isRequestedSessionIdValid() 403 { 404 return mRequest.isRequestedSessionIdValid(); 405 } 406 407 @Override 408 public boolean isRequestedSessionIdFromCookie() 409 { 410 return mRequest.isRequestedSessionIdFromCookie(); 411 } 412 413 @Override 414 public boolean isRequestedSessionIdFromURL() 415 { 416 return mRequest.isRequestedSessionIdFromURL(); 417 } 418 419 @Override 420 @SuppressWarnings("deprecation") 421 public boolean isRequestedSessionIdFromUrl() 422 { 423 return mRequest.isRequestedSessionIdFromUrl(); 424 } 425 426 @Override 427 public boolean authenticate(HttpServletResponse httpServletResponse) 428 throws IOException, ServletException 429 { 430 return mRequest.authenticate(httpServletResponse); 431 } 432 433 @Override 434 public void login(String s, String s1) 435 throws ServletException 436 { 437 mRequest.login(s, s1); 438 } 439 440 @Override 441 public void logout() 442 throws ServletException 443 { 444 mRequest.logout(); 445 } 446 447 @Override 448 public Collection<Part> getParts() 449 throws IOException, ServletException 450 { 451 return mRequest.getParts(); 452 } 453 454 @Override 455 public Part getPart(String s) 456 throws IOException, ServletException 457 { 458 return mRequest.getPart(s); 459 } 460 461 @Override 462 public Object getAttribute(String s) 463 { 464 return mRequest.getAttribute(s); 465 } 466 467 @Override 468 public Enumeration<String> getAttributeNames() 469 { 470 return mRequest.getAttributeNames(); 471 } 472 473 @Override 474 public String getCharacterEncoding() 475 { 476 return mRequest.getCharacterEncoding(); 477 } 478 479 @Override 480 public void setCharacterEncoding(String s) 481 throws UnsupportedEncodingException 482 { 483 mRequest.setCharacterEncoding(s); 484 } 485 486 @Override 487 public int getContentLength() 488 { 489 return mRequest.getContentLength(); 490 } 491 492 @Override 493 public String getContentType() 494 { 495 return mRequest.getContentType(); 496 } 497 498 @Override 499 public ServletInputStream getInputStream() 500 throws IOException 501 { 502 return (mInputStream != null ? mInputStream : mRequest.getInputStream()); 503 } 504 505 @Override 506 public String getParameter(String s) 507 { 508 String[] values = mParameterMap.get(s); 509 return (values != null ? values[0] : null); 510 } 511 512 @Override 513 public Enumeration<String> getParameterNames() 514 { 515 return mRequest.getParameterNames(); 516 } 517 518 @Override 519 public String[] getParameterValues(String s) 520 { 521 return mParameterMap.get(s); 522 } 523 524 @Override 525 public Map<String, String[]> getParameterMap() 526 { 527 return mParameterMap != null ? Collections.unmodifiableMap(mParameterMap) : null; 528 } 529 530 @Override 531 public String getProtocol() 532 { 533 return mRequest.getProtocol(); 534 } 535 536 @Override 537 public String getScheme() 538 { 539 return mRequest.getScheme(); 540 } 541 542 @Override 543 public String getServerName() 544 { 545 return mRequest.getServerName(); 546 } 547 548 @Override 549 public int getServerPort() 550 { 551 return mRequest.getServerPort(); 552 } 553 554 @Override 555 public BufferedReader getReader() 556 throws IOException 557 { 558 return mRequest.getReader(); 559 } 560 561 @Override 562 public String getRemoteAddr() 563 { 564 return mRequest.getRemoteAddr(); 565 } 566 567 @Override 568 public String getRemoteHost() 569 { 570 return mRequest.getRemoteHost(); 571 } 572 573 @Override 574 public void setAttribute(String s, Object o) 575 { 576 mRequest.setAttribute(s, o); 577 } 578 579 @Override 580 public void removeAttribute(String s) 581 { 582 mRequest.removeAttribute(s); 583 } 584 585 @Override 586 public Locale getLocale() 587 { 588 return mRequest.getLocale(); 589 } 590 591 @Override 592 public Enumeration<Locale> getLocales() 593 { 594 return mRequest.getLocales(); 595 } 596 597 @Override 598 public boolean isSecure() 599 { 600 return mRequest.isSecure(); 601 } 602 603 @Override 604 public RequestDispatcher getRequestDispatcher(String s) 605 { 606 return mRequest.getRequestDispatcher(s); 607 } 608 609 @Override 610 @SuppressWarnings("deprecation") 611 public String getRealPath(String s) 612 { 613 return mRequest.getRealPath(s); 614 } 615 616 @Override 617 public int getRemotePort() 618 { 619 return mRequest.getRemotePort(); 620 } 621 622 @Override 623 public String getLocalName() 624 { 625 return mRequest.getLocalName(); 626 } 627 628 @Override 629 public String getLocalAddr() 630 { 631 return mRequest.getLocalAddr(); 632 } 633 634 @Override 635 public int getLocalPort() 636 { 637 return mRequest.getLocalPort(); 638 } 639 640 @Override 641 public ServletContext getServletContext() 642 { 643 return mRequest.getServletContext(); 644 } 645 646 @Override 647 public AsyncContext startAsync() 648 throws IllegalStateException 649 { 650 return mRequest.startAsync(); 651 } 652 653 @Override 654 public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) 655 throws IllegalStateException 656 { 657 return mRequest.startAsync(servletRequest, servletResponse); 658 } 659 660 @Override 661 public boolean isAsyncStarted() 662 { 663 return mRequest.isAsyncStarted(); 664 } 665 666 @Override 667 public boolean isAsyncSupported() 668 { 669 return mRequest.isAsyncSupported(); 670 } 671 672 @Override 673 public AsyncContext getAsyncContext() 674 { 675 return mRequest.getAsyncContext(); 676 } 677 678 @Override 679 public DispatcherType getDispatcherType() 680 { 681 return mRequest.getDispatcherType(); 682 } 683 684 //########################################################################### 685 // PRIVATE METHODS 686 //########################################################################### 687 688 //--------------------------------------------------------------------------- 689 private File getTmpDir() 690 { 691 if (null == mTmpDir) 692 { 693 mTmpDir = new File(getTmpDirRoot(), "tmp_request_" + System.currentTimeMillis()); 694 } 695 696 return mTmpDir; 697 } 698 699 //--------------------------------------------------------------------------- 700 private void extractParamsFromQueryString(String inQueryString) 701 { 702 String fields[] = inQueryString.split("&"); 703 704 try 705 { 706 for (String field : fields) 707 { 708 String pieces[] = field.split("="); 709 addParameter(URLDecoder.decode(pieces[0], "UTF-8"), (pieces.length > 1 ? URLDecoder.decode(pieces[1], "UTF-8") : null)); 710 } 711 } 712 catch (Exception e) 713 { 714 throw new RuntimeException("Problem during extraction of GET parameters!", e); 715 } 716 } 717 718 //########################################################################### 719 // INNER CLASS 720 //########################################################################### 721 722 private class CachedInputStream extends ServletInputStream 723 { 724 private InputStream mInnerStream; 725 726 //------------------------------------------------------------------------ 727 public CachedInputStream(InputStream inStream) 728 { 729 mInnerStream = inStream; 730 } 731 732 //------------------------------------------------------------------------ 733 @Override 734 public int read() 735 throws IOException 736 { 737 return mInnerStream.read(); 738 } 739 } 740 741}