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.mock;
018
019import java.io.ByteArrayOutputStream;
020import java.io.IOException;
021import java.time.Instant;
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025import java.util.Set;
026import javax.servlet.http.Cookie;
027import org.apache.wicket.WicketRuntimeException;
028import org.apache.wicket.request.HttpHeaderCollection;
029import org.apache.wicket.request.http.WebResponse;
030import org.apache.wicket.util.lang.Args;
031
032/**
033 * Mocked {@link WebResponse}.
034 * 
035 * @author Matej Knopp
036 */
037public class MockWebResponse extends WebResponse
038{
039        /** response headers */
040        private final HttpHeaderCollection headers = new HttpHeaderCollection();
041
042        /** response cookies */
043        private final List<Cookie> cookies = new ArrayList<Cookie>();
044
045        /** url for redirection */
046        private String redirectUrl;
047
048        /** content type of response */
049        private String contentType;
050
051        /** content length of response in bytes */
052        private Long contentLength;
053
054        /** current characters in response body */
055        private StringBuilder textResponse;
056
057        /** current octets in response body */
058        private ByteArrayOutputStream binaryResponse;
059
060        /** http response status */
061        private Integer status;
062
063        /** response error message */
064        private String errorMessage;
065
066        /**
067         * Construct.
068         */
069        public MockWebResponse()
070        {
071        }
072
073        @Override
074        public void addCookie(Cookie cookie)
075        {
076                cookies.add(cookie);
077        }
078
079        @Override
080        public void clearCookie(Cookie cookie)
081        {
082                cookies.remove(cookie);
083        }
084
085        /**
086         * @return cookies set in this response
087         */
088        public List<Cookie> getCookies()
089        {
090                return Collections.unmodifiableList(cookies);
091        }
092
093        @Override
094        public void sendRedirect(String url)
095        {
096                redirectUrl = url;
097        }
098
099        /**
100         * @return redirect URL or <code>null</code> if {@link #sendRedirect(String)} was not called.
101         */
102        public String getRedirectUrl()
103        {
104                return redirectUrl;
105        }
106
107        /**
108         * @return <code>true</code> if redirect URL was set, <code>false</code> otherwise.
109         */
110        @Override
111        public boolean isRedirect()
112        {
113                return redirectUrl != null;
114        }
115
116        @Override
117        public void setContentLength(long length)
118        {
119                contentLength = length;
120                setHeader("Content-Length", String.valueOf(length).intern());
121        }
122
123        /**
124         * @return content length (set by {@link #setContentLength(long)})
125         */
126        public Long getContentLength()
127        {
128                return contentLength;
129        }
130
131        @Override
132        public void setContentType(String mimeType)
133        {
134                contentType = mimeType;
135        }
136
137        /**
138         * @return content mime type
139         */
140        public String getContentType()
141        {
142                return contentType;
143        }
144
145        @Override
146        public void setDateHeader(String name, Instant date)
147        {
148                Args.notNull(date, "date");
149                headers.setDateHeader(name, date);
150        }
151
152        /**
153         * @param name
154         * 
155         * @return date header with specified name
156         */
157        public Instant getDateHeader(String name)
158        {
159                final Instant time = headers.getDateHeader(name);
160
161                if (time == null)
162                {
163                        throw new WicketRuntimeException("Date header '" + name + "' is not set.");
164                }
165                return time;
166        }
167
168        @Override
169        public boolean isHeaderSupported()
170        {
171                return true;
172        }
173
174        @Override
175        public void setHeader(String name, String value)
176        {
177                internalSetContentType(name, value);
178                headers.setHeader(name, value);
179        }
180
181        @Override
182        public void addHeader(String name, String value)
183        {
184                internalSetContentType(name, value);
185                headers.addHeader(name, value);
186        }
187
188        /**
189         * set content type if it is specified
190         * 
191         * @param name
192         *            header name
193         * @param value
194         *            header value
195         */
196        private void internalSetContentType(String name, String value)
197        {
198                if ("Content-Type".equalsIgnoreCase(name))
199                {
200                        if (headers.containsHeader(name) == false)
201                        {
202                                setContentType(value);
203                        }
204                }
205        }
206
207        /**
208         * @param name
209         * 
210         * @return header string with specified name
211         */
212        public String getHeader(String name)
213        {
214                return headers.getHeader(name);
215        }
216
217        /**
218         * @param name
219         * 
220         * @return <code>true</code> if the header was set, <code>false</code> otherwise
221         */
222        public boolean hasHeader(String name)
223        {
224                return headers.containsHeader(name);
225        }
226
227        /**
228         * @return set of all header names
229         */
230        public Set<String> getHeaderNames()
231        {
232                return headers.getHeaderNames();
233        }
234
235        @Override
236        public void setStatus(int sc)
237        {
238                status = sc;
239        }
240
241        /**
242         * @return status code or <code>null</code> if status was not set
243         */
244        public Integer getStatus()
245        {
246                return status;
247        }
248
249        @Override
250        public String encodeURL(CharSequence url)
251        {
252                return url.toString();
253        }
254
255        @Override
256        public String encodeRedirectURL(CharSequence url)
257        {
258                return url.toString();
259        }
260
261        @Override
262        public void write(CharSequence sequence)
263        {
264                if (binaryResponse != null)
265                {
266                        throw new IllegalStateException("Binary response has already been initiated.");
267                }
268                if (textResponse == null)
269                {
270                        textResponse = new StringBuilder();
271                }
272                textResponse.append(sequence);
273        }
274
275        /**
276         * @return text response
277         */
278        public CharSequence getTextResponse()
279        {
280                return textResponse;
281        }
282
283        @Override
284        public void write(byte[] array)
285        {
286                if (textResponse != null)
287                {
288                        throw new IllegalStateException("Text response has already been initiated.");
289                }
290                if (binaryResponse == null)
291                {
292                        binaryResponse = new ByteArrayOutputStream();
293                }
294                try
295                {
296                        binaryResponse.write(array);
297                }
298                catch (IOException ignored)
299                {
300                }
301        }
302
303        @Override
304        public void write(byte[] array, int offset, int length)
305        {
306                if (textResponse != null)
307                {
308                        throw new IllegalStateException("Text response has already been initiated.");
309                }
310                if (binaryResponse == null)
311                {
312                        binaryResponse = new ByteArrayOutputStream();
313                }
314
315                binaryResponse.write(array, offset, length);
316        }
317
318        /**
319         * @return binary response
320         */
321        public byte[] getBinaryResponse()
322        {
323                if (binaryResponse == null)
324                {
325                        return null;
326                }
327                else
328                {
329                        byte[] bytes = binaryResponse.toByteArray();
330                        if (getContentLength() != null)
331                        {
332                                byte[] trimmed = new byte[getContentLength().intValue()];
333                                System.arraycopy(bytes, 0, trimmed, 0, getContentLength().intValue());
334                                return trimmed;
335                        }
336                        return bytes;
337                }
338        }
339
340        @Override
341        public void sendError(int sc, String msg)
342        {
343                status = sc;
344                errorMessage = msg;
345        }
346
347        /**
348         * @return error message
349         */
350        public String getErrorMessage()
351        {
352                return errorMessage;
353        }
354
355        @Override
356        public void flush()
357        {
358        }
359
360        @Override
361        public void reset()
362        {
363                super.reset();
364                if (binaryResponse != null)
365                {
366                        binaryResponse = new ByteArrayOutputStream();
367                }
368                contentLength = null;
369                contentType = null;
370                if (cookies != null)
371                {
372                        cookies.clear();
373                }
374                errorMessage = null;
375                if (headers != null)
376                {
377                        headers.clear();
378                }
379                redirectUrl = null;
380                status = null;
381                if (textResponse != null)
382                {
383                        textResponse.setLength(0);
384                }
385
386        }
387
388        @Override
389        public Object getContainerResponse()
390        {
391                return this;
392        }
393}