001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.protocol.http.servlet; 018 019import java.io.IOException; 020import java.time.Instant; 021import javax.servlet.http.Cookie; 022import javax.servlet.http.HttpServletRequest; 023import javax.servlet.http.HttpServletResponse; 024import org.apache.wicket.WicketRuntimeException; 025import org.apache.wicket.request.Url; 026import org.apache.wicket.request.UrlRenderer; 027import org.apache.wicket.request.cycle.RequestCycle; 028import org.apache.wicket.request.http.WebResponse; 029import org.apache.wicket.util.lang.Args; 030 031/** 032 * WebResponse that wraps a {@link ServletWebResponse}. 033 * 034 * @author Matej Knopp 035 */ 036public class ServletWebResponse extends WebResponse 037{ 038 private final HttpServletResponse httpServletResponse; 039 private final ServletWebRequest webRequest; 040 041 private boolean redirect = false; 042 043 /** 044 * Construct. 045 * 046 * @param webRequest 047 * @param httpServletResponse 048 */ 049 public ServletWebResponse(ServletWebRequest webRequest, HttpServletResponse httpServletResponse) 050 { 051 Args.notNull(webRequest, "webRequest"); 052 Args.notNull(httpServletResponse, "httpServletResponse"); 053 054 this.httpServletResponse = httpServletResponse; 055 this.webRequest = webRequest; 056 } 057 058 @Override 059 public void addCookie(Cookie cookie) 060 { 061 httpServletResponse.addCookie(cookie); 062 } 063 064 @Override 065 public void clearCookie(Cookie cookie) 066 { 067 cookie.setMaxAge(0); 068 cookie.setValue(null); 069 addCookie(cookie); 070 } 071 072 @Override 073 public void setContentLength(long length) 074 { 075 httpServletResponse.addHeader("Content-Length", Long.toString(length)); 076 } 077 078 @Override 079 public void setContentType(String mimeType) 080 { 081 httpServletResponse.setContentType(mimeType); 082 } 083 084 @Override 085 public void setDateHeader(String name, Instant date) 086 { 087 Args.notNull(date, "date"); 088 httpServletResponse.setDateHeader(name, date.toEpochMilli()); 089 } 090 091 @Override 092 public boolean isHeaderSupported() 093 { 094 return !httpServletResponse.isCommitted(); 095 } 096 097 @Override 098 public void setHeader(String name, String value) 099 { 100 httpServletResponse.setHeader(name, value); 101 } 102 103 @Override 104 public void addHeader(String name, String value) 105 { 106 httpServletResponse.addHeader(name, value); 107 } 108 109 @Override 110 public void write(CharSequence sequence) 111 { 112 try 113 { 114 httpServletResponse.getWriter().append(sequence); 115 } 116 catch (IOException e) 117 { 118 throw new ResponseIOException(e); 119 } 120 } 121 122 @Override 123 public void write(byte[] array) 124 { 125 try 126 { 127 httpServletResponse.getOutputStream().write(array); 128 } 129 catch (IOException e) 130 { 131 throw new ResponseIOException(e); 132 } 133 } 134 135 @Override 136 public void write(byte[] array, int offset, int length) 137 { 138 try 139 { 140 httpServletResponse.getOutputStream().write(array, offset, length); 141 } 142 catch (IOException e) 143 { 144 throw new ResponseIOException(e); 145 } 146 } 147 148 149 @Override 150 public void setStatus(int sc) 151 { 152 httpServletResponse.setStatus(sc); 153 } 154 155 @Override 156 public void sendError(int sc, String msg) 157 { 158 try 159 { 160 if (msg == null) 161 { 162 httpServletResponse.sendError(sc); 163 } 164 else 165 { 166 httpServletResponse.sendError(sc, msg); 167 } 168 } 169 catch (IOException e) 170 { 171 throw new WicketRuntimeException(e); 172 } 173 } 174 175 @Override 176 public String encodeURL(CharSequence url) 177 { 178 Args.notNull(url, "url"); 179 180 UrlRenderer urlRenderer = getUrlRenderer(); 181 182 Url originalUrl = Url.parse(url); 183 184 /* 185 WICKET-4645 - always pass absolute url to the web container for encoding 186 because when REDIRECT_TO_BUFFER is in use Wicket may render PageB when 187 PageA is actually the requested one and the web container cannot resolve 188 the base url properly 189 */ 190 String fullUrl = urlRenderer.renderFullUrl(originalUrl); 191 String encodedFullUrl = httpServletResponse.encodeURL(fullUrl); 192 193 final String encodedUrl; 194 if (originalUrl.isFull()) 195 { 196 encodedUrl = encodedFullUrl; 197 } 198 else 199 { 200 if (fullUrl.equals(encodedFullUrl)) 201 { 202 // no encoding happened so just reuse the original url 203 encodedUrl = url.toString(); 204 } 205 else 206 { 207 // get the relative url with the jsessionid encoded in it 208 Url _encoded = Url.parse(encodedFullUrl); 209 encodedUrl = urlRenderer.renderRelativeUrl(_encoded); 210 } 211 } 212 return encodedUrl; 213 } 214 215 private UrlRenderer getUrlRenderer() 216 { 217 RequestCycle requestCycle = RequestCycle.get(); 218 if (requestCycle == null) 219 { 220 return new UrlRenderer(webRequest); 221 } 222 return requestCycle.getUrlRenderer(); 223 } 224 225 @Override 226 public String encodeRedirectURL(CharSequence url) 227 { 228 Args.notNull(url, "url"); 229 230 UrlRenderer urlRenderer = getUrlRenderer(); 231 232 Url originalUrl = Url.parse(url); 233 234 /* 235 * WICKET-4645 - always pass absolute url to the web container for encoding because when 236 * REDIRECT_TO_BUFFER is in use Wicket may render PageB when PageA is actually the requested 237 * one and the web container cannot resolve the base url properly 238 */ 239 String fullUrl = urlRenderer.renderFullUrl(originalUrl); 240 String encodedFullUrl = httpServletResponse.encodeRedirectURL(fullUrl); 241 242 final String encodedUrl; 243 if (originalUrl.isFull()) 244 { 245 encodedUrl = encodedFullUrl; 246 } 247 else 248 { 249 if (fullUrl.equals(encodedFullUrl)) 250 { 251 // no encoding happened so just reuse the original url 252 encodedUrl = url.toString(); 253 } 254 else 255 { 256 // get the relative url with the jsessionid encoded in it 257 Url _encoded = Url.parse(encodedFullUrl); 258 encodedUrl = urlRenderer.renderRelativeUrl(_encoded); 259 } 260 } 261 return encodedUrl; 262 } 263 264 @Override 265 public void sendRedirect(String url) 266 { 267 try 268 { 269 redirect = true; 270 url = encodeRedirectURL(url); 271 272 // wicket redirects should never be cached 273 disableCaching(); 274 275 if (webRequest.isAjax()) 276 { 277 setHeader("Ajax-Location", url); 278 setContentType("text/xml;charset=" + 279 webRequest.getContainerRequest().getCharacterEncoding()); 280 281 /* 282 * usually the Ajax-Location header is enough and we do not need to the redirect url 283 * into the response, but sometimes the response is processed via an iframe (eg 284 * using multipart ajax handling) and the headers are not available because XHR is 285 * not used and that is the only way javascript has access to response headers. 286 */ 287 httpServletResponse.getWriter().write( 288 "<ajax-response><redirect><![CDATA[" + url + "]]></redirect></ajax-response>"); 289 } 290 else 291 { 292 httpServletResponse.sendRedirect(url); 293 } 294 } 295 catch (IOException e) 296 { 297 throw new WicketRuntimeException(e); 298 } 299 } 300 301 @Override 302 public boolean isRedirect() 303 { 304 return redirect; 305 } 306 307 @Override 308 public void flush() 309 { 310 try 311 { 312 HttpServletRequest httpServletRequest = webRequest.getContainerRequest(); 313 if (httpServletRequest.isAsyncStarted() == false) 314 { 315 httpServletResponse.flushBuffer(); 316 } 317 } 318 catch (IOException e) 319 { 320 throw new ResponseIOException(e); 321 } 322 } 323 324 @Override 325 public void reset() 326 { 327 super.reset(); 328 httpServletResponse.reset(); 329 redirect = false; 330 } 331 332 @Override 333 public HttpServletResponse getContainerResponse() 334 { 335 return httpServletResponse; 336 } 337 338}