1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.catalina.connector;
18
19 import java.io.IOException;
20 import java.io.PrintWriter;
21 import java.security.AccessController;
22 import java.security.PrivilegedAction;
23 import java.security.PrivilegedActionException;
24 import java.security.PrivilegedExceptionAction;
25 import java.util.Collection;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.function.Supplier;
29
30 import javax.servlet.ServletOutputStream;
31 import javax.servlet.http.Cookie;
32 import javax.servlet.http.HttpServletResponse;
33
34 import org.apache.catalina.Globals;
35 import org.apache.catalina.security.SecurityUtil;
36 import org.apache.tomcat.util.res.StringManager;
37
38 /**
39  * Facade class that wraps a Coyote response object.
40  * All methods are delegated to the wrapped response.
41  *
42  * @author Remy Maucherat
43  */

44 @SuppressWarnings("deprecation")
45 public class ResponseFacade implements HttpServletResponse {
46
47     // ----------------------------------------------------------- DoPrivileged
48
49     private final class SetContentTypePrivilegedAction
50             implements PrivilegedAction<Void> {
51
52         private final String contentType;
53
54         public SetContentTypePrivilegedAction(String contentType){
55             this.contentType = contentType;
56         }
57
58         @Override
59         public Void run() {
60             response.setContentType(contentType);
61             return null;
62         }
63     }
64
65     private final class DateHeaderPrivilegedAction
66             implements PrivilegedAction<Void> {
67
68         private final String name;
69         private final long value;
70         private final boolean add;
71
72         DateHeaderPrivilegedAction(String name, long value, boolean add) {
73             this.name = name;
74             this.value = value;
75             this.add = add;
76         }
77
78         @Override
79         public Void run() {
80             if(add) {
81                 response.addDateHeader(name, value);
82             } else {
83                 response.setDateHeader(name, value);
84             }
85             return null;
86         }
87     }
88
89     private static class FlushBufferPrivilegedAction implements PrivilegedExceptionAction<Void> {
90
91         private final Response response;
92
93         public FlushBufferPrivilegedAction(Response response) {
94             this.response = response;
95         }
96
97         @Override
98         public Void run() throws IOException {
99             response.setAppCommitted(true);
100             response.flushBuffer();
101             return null;
102         }
103     }
104
105
106     // ----------------------------------------------------------- Constructors
107
108     /**
109      * Construct a wrapper for the specified response.
110      *
111      * @param response The response to be wrapped
112      */

113     public ResponseFacade(Response response) {
114
115          this.response = response;
116     }
117
118
119     // ----------------------------------------------- Class/Instance Variables
120
121
122     /**
123      * The string manager for this package.
124      */

125     protected static final StringManager sm = StringManager.getManager(ResponseFacade.class);
126
127
128     /**
129      * The wrapped response.
130      */

131     protected Response response = null;
132
133
134     // --------------------------------------------------------- Public Methods
135
136
137     /**
138      * Clear facade.
139      */

140     public void clear() {
141         response = null;
142     }
143
144
145     /**
146      * Prevent cloning the facade.
147      */

148     @Override
149     protected Object clone()
150         throws CloneNotSupportedException {
151         throw new CloneNotSupportedException();
152     }
153
154
155     public void finish() {
156
157         if (response == null) {
158             throw new IllegalStateException(
159                             sm.getString("responseFacade.nullResponse"));
160         }
161
162         response.setSuspended(true);
163     }
164
165
166     public boolean isFinished() {
167
168         if (response == null) {
169             throw new IllegalStateException(
170                             sm.getString("responseFacade.nullResponse"));
171         }
172
173         return response.isSuspended();
174     }
175
176
177     public long getContentWritten() {
178
179         if (response == null) {
180             throw new IllegalStateException(
181                             sm.getString("responseFacade.nullResponse"));
182         }
183
184         return response.getContentWritten();
185     }
186
187     // ------------------------------------------------ ServletResponse Methods
188
189
190     @Override
191     public String getCharacterEncoding() {
192
193         if (response == null) {
194             throw new IllegalStateException(
195                             sm.getString("responseFacade.nullResponse"));
196         }
197
198         return response.getCharacterEncoding();
199     }
200
201
202     @Override
203     public ServletOutputStream getOutputStream()
204         throws IOException {
205
206         //        if (isFinished())
207         //            throw new IllegalStateException
208         //                (/*sm.getString("responseFacade.finished")*/);
209
210         ServletOutputStream sos = response.getOutputStream();
211         if (isFinished()) {
212             response.setSuspended(true);
213         }
214         return sos;
215
216     }
217
218
219     @Override
220     public PrintWriter getWriter()
221         throws IOException {
222
223         //        if (isFinished())
224         //            throw new IllegalStateException
225         //                (/*sm.getString("responseFacade.finished")*/);
226
227         PrintWriter writer = response.getWriter();
228         if (isFinished()) {
229             response.setSuspended(true);
230         }
231         return writer;
232
233     }
234
235
236     @Override
237     public void setContentLength(int len) {
238         if (isCommitted()) {
239             return;
240         }
241         response.setContentLength(len);
242     }
243
244
245     @Override
246     public void setContentLengthLong(long length) {
247         if (isCommitted()) {
248             return;
249         }
250         response.setContentLengthLong(length);
251     }
252
253
254     @Override
255     public void setContentType(String type) {
256
257         if (isCommitted()) {
258             return;
259         }
260
261         if (SecurityUtil.isPackageProtectionEnabled()){
262             AccessController.doPrivileged(new SetContentTypePrivilegedAction(type));
263         } else {
264             response.setContentType(type);
265         }
266     }
267
268
269     @Override
270     public void setBufferSize(int size) {
271
272         if (isCommitted()) {
273             throw new IllegalStateException
274                 (sm.getString("coyoteResponse.setBufferSize.ise"));
275         }
276
277         response.setBufferSize(size);
278
279     }
280
281
282     @Override
283     public int getBufferSize() {
284
285         if (response == null) {
286             throw new IllegalStateException(
287                             sm.getString("responseFacade.nullResponse"));
288         }
289
290         return response.getBufferSize();
291     }
292
293
294     @Override
295     public void flushBuffer() throws IOException {
296
297         if (isFinished()) {
298             return;
299         }
300
301         if (SecurityUtil.isPackageProtectionEnabled()) {
302             try{
303                 AccessController.doPrivileged(new FlushBufferPrivilegedAction(response));
304             } catch(PrivilegedActionException e) {
305                 Exception ex = e.getException();
306                 if (ex instanceof IOException) {
307                     throw (IOException)ex;
308                 }
309             }
310         } else {
311             response.setAppCommitted(true);
312             response.flushBuffer();
313         }
314     }
315
316
317     @Override
318     public void resetBuffer() {
319
320         if (isCommitted()) {
321             throw new IllegalStateException
322                 (sm.getString("coyoteResponse.resetBuffer.ise"));
323         }
324
325         response.resetBuffer();
326
327     }
328
329
330     @Override
331     public boolean isCommitted() {
332
333         if (response == null) {
334             throw new IllegalStateException(
335                             sm.getString("responseFacade.nullResponse"));
336         }
337
338         return response.isAppCommitted();
339     }
340
341
342     @Override
343     public void reset() {
344
345         if (isCommitted()) {
346             throw new IllegalStateException
347                 (sm.getString("coyoteResponse.reset.ise"));
348         }
349
350         response.reset();
351
352     }
353
354
355     @Override
356     public void setLocale(Locale loc) {
357
358         if (isCommitted()) {
359             return;
360         }
361
362         response.setLocale(loc);
363     }
364
365
366     @Override
367     public Locale getLocale() {
368
369         if (response == null) {
370             throw new IllegalStateException(
371                             sm.getString("responseFacade.nullResponse"));
372         }
373
374         return response.getLocale();
375     }
376
377
378     @Override
379     public void addCookie(Cookie cookie) {
380
381         if (isCommitted()) {
382             return;
383         }
384
385         response.addCookie(cookie);
386
387     }
388
389
390     @Override
391     public boolean containsHeader(String name) {
392
393         if (response == null) {
394             throw new IllegalStateException(
395                             sm.getString("responseFacade.nullResponse"));
396         }
397
398         return response.containsHeader(name);
399     }
400
401
402     @Override
403     public String encodeURL(String url) {
404
405         if (response == null) {
406             throw new IllegalStateException(
407                             sm.getString("responseFacade.nullResponse"));
408         }
409
410         return response.encodeURL(url);
411     }
412
413
414     @Override
415     public String encodeRedirectURL(String url) {
416
417         if (response == null) {
418             throw new IllegalStateException(
419                             sm.getString("responseFacade.nullResponse"));
420         }
421
422         return response.encodeRedirectURL(url);
423     }
424
425
426     @Override
427     public String encodeUrl(String url) {
428
429         if (response == null) {
430             throw new IllegalStateException(
431                             sm.getString("responseFacade.nullResponse"));
432         }
433
434         return response.encodeURL(url);
435     }
436
437
438     @Override
439     public String encodeRedirectUrl(String url) {
440
441         if (response == null) {
442             throw new IllegalStateException(
443                             sm.getString("responseFacade.nullResponse"));
444         }
445
446         return response.encodeRedirectURL(url);
447     }
448
449
450     @Override
451     public void sendError(int sc, String msg)
452         throws IOException {
453
454         if (isCommitted()) {
455             throw new IllegalStateException
456                 (sm.getString("coyoteResponse.sendError.ise"));
457         }
458
459         response.setAppCommitted(true);
460
461         response.sendError(sc, msg);
462
463     }
464
465
466     @Override
467     public void sendError(int sc)
468         throws IOException {
469
470         if (isCommitted()) {
471             throw new IllegalStateException
472                 (sm.getString("coyoteResponse.sendError.ise"));
473         }
474
475         response.setAppCommitted(true);
476
477         response.sendError(sc);
478
479     }
480
481
482     @Override
483     public void sendRedirect(String location)
484         throws IOException {
485
486         if (isCommitted()) {
487             throw new IllegalStateException
488                 (sm.getString("coyoteResponse.sendRedirect.ise"));
489         }
490
491         response.setAppCommitted(true);
492
493         response.sendRedirect(location);
494
495     }
496
497
498     @Override
499     public void setDateHeader(String name, long date) {
500
501         if (isCommitted()) {
502             return;
503         }
504
505         if(Globals.IS_SECURITY_ENABLED) {
506             AccessController.doPrivileged(new DateHeaderPrivilegedAction
507                                              (name, date, false));
508         } else {
509             response.setDateHeader(name, date);
510         }
511
512     }
513
514
515     @Override
516     public void addDateHeader(String name, long date) {
517
518         if (isCommitted()) {
519             return;
520         }
521
522         if(Globals.IS_SECURITY_ENABLED) {
523             AccessController.doPrivileged(new DateHeaderPrivilegedAction
524                                              (name, date, true));
525         } else {
526             response.addDateHeader(name, date);
527         }
528
529     }
530
531
532     @Override
533     public void setHeader(String name, String value) {
534
535         if (isCommitted()) {
536             return;
537         }
538
539         response.setHeader(name, value);
540
541     }
542
543
544     @Override
545     public void addHeader(String name, String value) {
546
547         if (isCommitted()) {
548             return;
549         }
550
551         response.addHeader(name, value);
552
553     }
554
555
556     @Override
557     public void setIntHeader(String name, int value) {
558
559         if (isCommitted()) {
560             return;
561         }
562
563         response.setIntHeader(name, value);
564
565     }
566
567
568     @Override
569     public void addIntHeader(String name, int value) {
570
571         if (isCommitted()) {
572             return;
573         }
574
575         response.addIntHeader(name, value);
576
577     }
578
579
580     @Override
581     public void setStatus(int sc) {
582
583         if (isCommitted()) {
584             return;
585         }
586
587         response.setStatus(sc);
588
589     }
590
591
592     @Override
593     public void setStatus(int sc, String sm) {
594
595         if (isCommitted()) {
596             return;
597         }
598
599         response.setStatus(sc, sm);
600     }
601
602
603     @Override
604     public String getContentType() {
605
606         if (response == null) {
607             throw new IllegalStateException(
608                             sm.getString("responseFacade.nullResponse"));
609         }
610
611         return response.getContentType();
612     }
613
614
615     @Override
616     public void setCharacterEncoding(String arg0) {
617
618         if (response == null) {
619             throw new IllegalStateException(
620                             sm.getString("responseFacade.nullResponse"));
621         }
622
623         response.setCharacterEncoding(arg0);
624     }
625
626     @Override
627     public int getStatus() {
628         return response.getStatus();
629     }
630
631     @Override
632     public String getHeader(String name) {
633         return response.getHeader(name);
634     }
635
636     @Override
637     public Collection<String> getHeaderNames() {
638         return response.getHeaderNames();
639     }
640
641     @Override
642     public Collection<String> getHeaders(String name) {
643         return response.getHeaders(name);
644     }
645
646
647     @Override
648     public void setTrailerFields(Supplier<Map<String, String>> supplier) {
649         response.setTrailerFields(supplier);
650     }
651
652
653     @Override
654     public Supplier<Map<String, String>> getTrailerFields() {
655         return response.getTrailerFields();
656     }
657 }
658