001/* 002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others. 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 * http://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 * Contributors: 017 * Kevin Leturc <kleturc@nuxeo.com> 018 */ 019package org.nuxeo.ecm.webengine.app; 020 021import java.io.IOException; 022import java.util.Collections; 023import java.util.Enumeration; 024import java.util.Hashtable; 025import java.util.LinkedList; 026import java.util.List; 027import java.util.NoSuchElementException; 028 029import javax.servlet.FilterChain; 030import javax.servlet.FilterConfig; 031import javax.servlet.ServletException; 032import javax.servlet.http.HttpServletRequest; 033import javax.servlet.http.HttpServletRequestWrapper; 034import javax.servlet.http.HttpServletResponse; 035import javax.ws.rs.core.HttpHeaders; 036 037import org.nuxeo.ecm.webengine.jaxrs.HttpFilter; 038 039/** 040 * This filter is used to fix the http headers sent to Nuxeo. Sometimes Nuxeo Drive send content type or mime type 041 * equals to 'pdf' instead of 'application/pdf', this filter is used to fix this value before giving it to appropriate 042 * webengine object. 043 * 044 * @since 9.2 045 */ 046public class HeaderFixFilter extends HttpFilter { 047 048 protected final String MIME_TYPE = "X-File-Type"; 049 050 @Override 051 protected void run(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 052 throws IOException, ServletException { 053 HttpServletRequest newRequest = request; 054 055 String ctype = request.getHeader(HttpHeaders.CONTENT_TYPE); 056 String mtype = request.getHeader(MIME_TYPE); 057 boolean patchCType = ctype == null || ctype.length() == 0 || !ctype.contains("/"); 058 boolean patchMMType = mtype != null && !mtype.contains("/"); 059 if (patchCType || patchMMType) { 060 newRequest = new DefaultContentTypeRequestWrapper(request, patchCType, patchMMType); 061 } 062 chain.doFilter(newRequest, response); 063 } 064 065 @Override 066 public void init(FilterConfig filterConfig) throws ServletException { 067 068 } 069 070 @Override 071 public void destroy() { 072 073 } 074 075 protected class DefaultContentTypeRequestWrapper extends HttpServletRequestWrapper { 076 077 protected final Hashtable<String, String[]> headers; 078 079 protected final String lCONTENT_TYPE = HttpHeaders.CONTENT_TYPE.toLowerCase(); 080 081 protected final String lFILE_TYPE = MIME_TYPE.toLowerCase(); 082 083 protected DefaultContentTypeRequestWrapper(HttpServletRequest request, boolean patchCType, boolean patchMType) { 084 super(request); 085 headers = patchHeaders(request, patchCType, patchMType); 086 } 087 088 protected Hashtable<String, String[]> patchHeaders(HttpServletRequest request, boolean patchCType, 089 boolean patchMType) { 090 Hashtable<String, String[]> headers = new Hashtable<>(); 091 // collect headers from request 092 Enumeration<String> eachNames = request.getHeaderNames(); 093 while (eachNames.hasMoreElements()) { 094 String name = eachNames.nextElement().toLowerCase(); 095 List<String> values = new LinkedList<>(); 096 Enumeration<String> eachValues = request.getHeaders(name); 097 while (eachValues.hasMoreElements()) { 098 values.add(eachValues.nextElement()); 099 } 100 headers.put(name, values.toArray(new String[values.size()])); 101 } 102 if (patchCType) { 103 // patch content type 104 String ctype = request.getContentType(); 105 if (ctype == null || ctype.isEmpty()) { 106 String[] ctypes = new String[] { "application/octet-stream" }; 107 headers.put(lCONTENT_TYPE, ctypes); 108 } else { 109 patchContentTypes(headers.get(lCONTENT_TYPE)); 110 } 111 } 112 if (patchMType) { 113 patchContentTypes(headers.get(lFILE_TYPE)); 114 } 115 return headers; 116 } 117 118 protected void patchContentTypes(String[] ctypes) { 119 for (int index = 0; index < ctypes.length; ++index) { 120 String value = ctypes[index]; 121 if (value.isEmpty()) { 122 ctypes[index] = "application/octet-stream"; 123 } else if (!value.contains("/")) { 124 ctypes[index] = "application/".concat(value); 125 } 126 } 127 } 128 129 @Override 130 public Enumeration<String> getHeaderNames() { 131 return headers.keys(); 132 } 133 134 @Override 135 public String getHeader(String name) { 136 String lname = name.toLowerCase(); 137 if (!headers.containsKey(lname)) { 138 return null; 139 } 140 return headers.get(lname)[0]; 141 } 142 143 @Override 144 public Enumeration<String> getHeaders(final String name) { 145 final String lname = name.toLowerCase(); 146 if (!headers.containsKey(lname)) { 147 return Collections.emptyEnumeration(); 148 } 149 return new Enumeration<String>() { 150 String[] values = headers.get(lname); 151 152 int index = 0; 153 154 @Override 155 public boolean hasMoreElements() { 156 return index < values.length; 157 } 158 159 @Override 160 public String nextElement() { 161 if (index >= values.length) { 162 throw new NoSuchElementException(index + " is higher than " + values.length); 163 } 164 return values[index++]; 165 } 166 }; 167 } 168 169 } 170 171}