001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.servlet;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.nio.charset.CharsetEncoderUtil;
020    import com.liferay.portal.kernel.util.ArrayUtil;
021    import com.liferay.portal.kernel.util.FileUtil;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.HttpUtil;
024    import com.liferay.portal.kernel.util.MimeTypesUtil;
025    import com.liferay.portal.kernel.util.PropsKeys;
026    import com.liferay.portal.kernel.util.PropsUtil;
027    import com.liferay.portal.kernel.util.RandomAccessInputStream;
028    import com.liferay.portal.kernel.util.ServerDetector;
029    import com.liferay.portal.kernel.util.StreamUtil;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.util.Validator;
034    
035    import java.io.ByteArrayInputStream;
036    import java.io.File;
037    import java.io.FileInputStream;
038    import java.io.IOException;
039    import java.io.InputStream;
040    import java.io.OutputStream;
041    
042    import java.net.SocketException;
043    
044    import java.nio.ByteBuffer;
045    import java.nio.CharBuffer;
046    import java.nio.channels.Channels;
047    import java.nio.channels.FileChannel;
048    
049    import java.util.ArrayList;
050    import java.util.Collections;
051    import java.util.List;
052    
053    import javax.servlet.ServletOutputStream;
054    import javax.servlet.http.HttpServletRequest;
055    import javax.servlet.http.HttpServletResponse;
056    
057    /**
058     * @author Brian Wing Shun Chan
059     * @author Shuyang Zhou
060     */
061    public class ServletResponseUtil {
062    
063            public static List<Range> getRanges(
064                            HttpServletRequest request, HttpServletResponse response,
065                            long length)
066                    throws IOException {
067    
068                    String rangeString = request.getHeader(HttpHeaders.RANGE);
069    
070                    if (Validator.isNull(rangeString)) {
071                            return Collections.emptyList();
072                    }
073    
074                    if (!rangeString.matches(_RANGE_REGEX)) {
075                            throw new IOException(
076                                    "Range header does not match regular expression " +
077                                            rangeString);
078                    }
079    
080                    List<Range> ranges = new ArrayList<>();
081    
082                    String[] rangeFields = StringUtil.split(rangeString.substring(6));
083    
084                    if (rangeFields.length > _MAX_RANGE_FIELDS) {
085                            StringBundler sb = new StringBundler(8);
086    
087                            sb.append("Request range ");
088                            sb.append(rangeString);
089                            sb.append(" with ");
090                            sb.append(rangeFields.length);
091                            sb.append(" range fields has exceeded maximum allowance as ");
092                            sb.append("specified by the property \"");
093                            sb.append(PropsKeys.WEB_SERVER_SERVLET_MAX_RANGE_FIELDS);
094                            sb.append("\"");
095    
096                            throw new IOException(sb.toString());
097                    }
098    
099                    for (String rangeField : rangeFields) {
100                            int index = rangeField.indexOf(StringPool.DASH);
101    
102                            long start = GetterUtil.getLong(rangeField.substring(0, index), -1);
103                            long end = GetterUtil.getLong(
104                                    rangeField.substring(index + 1, rangeField.length()), -1);
105    
106                            if (start == -1) {
107                                    start = length - end;
108                                    end = length - 1;
109                            }
110                            else if ((end == -1) || (end > (length - 1))) {
111                                    end = length - 1;
112                            }
113    
114                            if (start > end) {
115                                    throw new IOException(
116                                            "Range start " + start + " is greater than end " + end);
117                            }
118    
119                            Range range = new Range(start, end, length);
120    
121                            ranges.add(range);
122                    }
123    
124                    return ranges;
125            }
126    
127            public static boolean isClientAbortException(IOException ioe) {
128                    Class<?> clazz = ioe.getClass();
129    
130                    String className = clazz.getName();
131    
132                    if (className.equals(_CLIENT_ABORT_EXCEPTION)) {
133                            return true;
134                    }
135                    else {
136                            return false;
137                    }
138            }
139    
140            public static void sendFile(
141                            HttpServletRequest request, HttpServletResponse response,
142                            String fileName, byte[] bytes)
143                    throws IOException {
144    
145                    sendFile(request, response, fileName, bytes, null);
146            }
147    
148            public static void sendFile(
149                            HttpServletRequest request, HttpServletResponse response,
150                            String fileName, byte[] bytes, String contentType)
151                    throws IOException {
152    
153                    sendFile(request, response, fileName, bytes, contentType, null);
154            }
155    
156            public static void sendFile(
157                            HttpServletRequest request, HttpServletResponse response,
158                            String fileName, byte[] bytes, String contentType,
159                            String contentDispositionType)
160                    throws IOException {
161    
162                    setHeaders(
163                            request, response, fileName, contentType, contentDispositionType);
164    
165                    write(response, bytes);
166            }
167    
168            public static void sendFile(
169                            HttpServletRequest request, HttpServletResponse response,
170                            String fileName, InputStream inputStream)
171                    throws IOException {
172    
173                    sendFile(request, response, fileName, inputStream, null);
174            }
175    
176            public static void sendFile(
177                            HttpServletRequest request, HttpServletResponse response,
178                            String fileName, InputStream inputStream, long contentLength,
179                            String contentType)
180                    throws IOException {
181    
182                    sendFile(
183                            request, response, fileName, inputStream, contentLength,
184                            contentType, null);
185            }
186    
187            public static void sendFile(
188                            HttpServletRequest request, HttpServletResponse response,
189                            String fileName, InputStream inputStream, long contentLength,
190                            String contentType, String contentDispositionType)
191                    throws IOException {
192    
193                    setHeaders(
194                            request, response, fileName, contentType, contentDispositionType);
195    
196                    write(response, inputStream, contentLength);
197            }
198    
199            public static void sendFile(
200                            HttpServletRequest request, HttpServletResponse response,
201                            String fileName, InputStream inputStream, String contentType)
202                    throws IOException {
203    
204                    sendFile(request, response, fileName, inputStream, 0, contentType);
205            }
206    
207            /**
208             * @deprecated As of 6.1.0
209             */
210            @Deprecated
211            public static void sendFile(
212                            HttpServletResponse response, String fileName, byte[] bytes)
213                    throws IOException {
214    
215                    sendFile(null, response, fileName, bytes);
216            }
217    
218            /**
219             * @deprecated As of 6.1.0
220             */
221            @Deprecated
222            public static void sendFile(
223                            HttpServletResponse response, String fileName, byte[] bytes,
224                            String contentType)
225                    throws IOException {
226    
227                    sendFile(null, response, fileName, bytes, contentType);
228            }
229    
230            /**
231             * @deprecated As of 6.1.0
232             */
233            @Deprecated
234            public static void sendFile(
235                            HttpServletResponse response, String fileName,
236                            InputStream inputStream)
237                    throws IOException {
238    
239                    sendFile(null, response, fileName, inputStream);
240            }
241    
242            /**
243             * @deprecated As of 6.1.0
244             */
245            @Deprecated
246            public static void sendFile(
247                            HttpServletResponse response, String fileName,
248                            InputStream inputStream, int contentLength, String contentType)
249                    throws IOException {
250    
251                    sendFile(
252                            null, response, fileName, inputStream, contentLength, contentType);
253            }
254    
255            /**
256             * @deprecated As of 6.1.0
257             */
258            @Deprecated
259            public static void sendFile(
260                            HttpServletResponse response, String fileName,
261                            InputStream inputStream, String contentType)
262                    throws IOException {
263    
264                    sendFile(null, response, fileName, inputStream, contentType);
265            }
266    
267            public static void sendFileWithRangeHeader(
268                            HttpServletRequest request, HttpServletResponse response,
269                            String fileName, InputStream inputStream, long contentLength,
270                            String contentType)
271                    throws IOException {
272    
273                    if (_log.isDebugEnabled()) {
274                            _log.debug("Accepting ranges for the file " + fileName);
275                    }
276    
277                    response.setHeader(
278                            HttpHeaders.ACCEPT_RANGES, HttpHeaders.ACCEPT_RANGES_BYTES_VALUE);
279    
280                    List<Range> ranges = null;
281    
282                    try {
283                            ranges = getRanges(request, response, contentLength);
284                    }
285                    catch (IOException ioe) {
286                            if (_log.isErrorEnabled()) {
287                                    _log.error(ioe);
288                            }
289    
290                            response.setHeader(
291                                    HttpHeaders.CONTENT_RANGE, "bytes */" + contentLength);
292    
293                            response.sendError(
294                                    HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
295    
296                            return;
297                    }
298    
299                    if ((ranges == null) || ranges.isEmpty()) {
300                            sendFile(
301                                    request, response, fileName, inputStream, contentLength,
302                                    contentType);
303                    }
304                    else {
305                            if (_log.isDebugEnabled()) {
306                                    _log.debug(
307                                            "Request has range header " +
308                                                    request.getHeader(HttpHeaders.RANGE));
309                            }
310    
311                            write(
312                                    request, response, fileName, ranges, inputStream, contentLength,
313                                    contentType);
314                    }
315            }
316    
317            public static void write(
318                            HttpServletRequest request, HttpServletResponse response,
319                            String fileName, List<Range> ranges, InputStream inputStream,
320                            long fullLength, String contentType)
321                    throws IOException {
322    
323                    OutputStream outputStream = null;
324    
325                    try {
326                            outputStream = response.getOutputStream();
327    
328                            Range fullRange = new Range(0, fullLength - 1, fullLength);
329    
330                            Range firstRange = null;
331    
332                            if (!ranges.isEmpty()) {
333                                    firstRange = ranges.get(0);
334                            }
335    
336                            if ((firstRange == null) || firstRange.equals(fullRange)) {
337                                    if (_log.isDebugEnabled()) {
338                                            _log.debug("Writing full range");
339                                    }
340    
341                                    response.setContentType(contentType);
342    
343                                    setHeaders(
344                                            request, response, fileName, contentType, null, fullRange);
345    
346                                    copyRange(
347                                            inputStream, outputStream, fullRange.getStart(),
348                                            fullRange.getLength());
349                            }
350                            else if (ranges.size() == 1) {
351                                    if (_log.isDebugEnabled()) {
352                                            _log.debug("Attempting to write a single range");
353                                    }
354    
355                                    Range range = ranges.get(0);
356    
357                                    response.setContentType(contentType);
358    
359                                    setHeaders(
360                                            request, response, fileName, contentType, null, range);
361    
362                                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
363    
364                                    copyRange(
365                                            inputStream, outputStream, range.getStart(),
366                                            range.getLength());
367                            }
368                            else if (ranges.size() > 1 ) {
369                                    if (_log.isDebugEnabled()) {
370                                            _log.debug("Attempting to write multiple ranges");
371                                    }
372    
373                                    ServletOutputStream servletOutputStream =
374                                            (ServletOutputStream)outputStream;
375    
376                                    String boundary =
377                                            "liferay-multipart-boundary-" + System.currentTimeMillis();
378    
379                                    String multipartContentType =
380                                            "multipart/byteranges; boundary=" + boundary;
381    
382                                    response.setContentType(multipartContentType);
383    
384                                    setHeaders(
385                                            request, response, fileName, multipartContentType, null);
386    
387                                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
388    
389                                    for (int i = 0; i < ranges.size(); i++) {
390                                            Range range = ranges.get(i);
391    
392                                            servletOutputStream.println();
393                                            servletOutputStream.println(
394                                                    StringPool.DOUBLE_DASH + boundary);
395                                            servletOutputStream.println(
396                                                    HttpHeaders.CONTENT_TYPE + ": " + contentType);
397                                            servletOutputStream.println(
398                                                    HttpHeaders.CONTENT_RANGE + ": " +
399                                                            range.getContentRange());
400                                            servletOutputStream.println();
401    
402                                            inputStream = copyRange(
403                                                    inputStream, outputStream, range.getStart(),
404                                                    range.getLength());
405                                    }
406    
407                                    servletOutputStream.println();
408                                    servletOutputStream.println(
409                                            StringPool.DOUBLE_DASH + boundary + StringPool.DOUBLE_DASH);
410                            }
411                    }
412                    finally {
413                            try {
414                                    inputStream.close();
415                            }
416                            catch (IOException ioe) {
417                            }
418                    }
419            }
420    
421            public static void write(
422                            HttpServletResponse response,
423                            BufferCacheServletResponse bufferCacheServletResponse)
424                    throws IOException {
425    
426                    if (bufferCacheServletResponse.isByteMode()) {
427                            write(response, bufferCacheServletResponse.getByteBuffer());
428                    }
429                    else if (bufferCacheServletResponse.isCharMode()) {
430                            write(response, bufferCacheServletResponse.getCharBuffer());
431                    }
432            }
433    
434            public static void write(HttpServletResponse response, byte[] bytes)
435                    throws IOException {
436    
437                    write(response, bytes, 0, 0);
438            }
439    
440            public static void write(
441                            HttpServletResponse response, byte[] bytes, int offset,
442                            int contentLength)
443                    throws IOException {
444    
445                    try {
446    
447                            // LEP-3122
448    
449                            if (!response.isCommitted()) {
450    
451                                    // LEP-536
452    
453                                    if (contentLength == 0) {
454                                            contentLength = bytes.length;
455                                    }
456    
457                                    response.setContentLength(contentLength);
458    
459                                    response.flushBuffer();
460    
461                                    if (response instanceof BufferCacheServletResponse) {
462                                            BufferCacheServletResponse bufferCacheServletResponse =
463                                                    (BufferCacheServletResponse)response;
464    
465                                            bufferCacheServletResponse.setByteBuffer(
466                                                    ByteBuffer.wrap(bytes, offset, contentLength));
467                                    }
468                                    else {
469                                            ServletOutputStream servletOutputStream =
470                                                    response.getOutputStream();
471    
472                                            if ((contentLength == 0) && ServerDetector.isJetty()) {
473                                            }
474                                            else {
475                                                    servletOutputStream.write(bytes, offset, contentLength);
476                                            }
477                                    }
478                            }
479                    }
480                    catch (IOException ioe) {
481                            if ((ioe instanceof SocketException) ||
482                                    isClientAbortException(ioe)) {
483    
484                                    if (_log.isWarnEnabled()) {
485                                            _log.warn(ioe);
486                                    }
487                            }
488                            else {
489                                    throw ioe;
490                            }
491                    }
492            }
493    
494            public static void write(HttpServletResponse response, byte[][] bytesArray)
495                    throws IOException {
496    
497                    try {
498    
499                            // LEP-3122
500    
501                            if (!response.isCommitted()) {
502                                    long contentLength = 0;
503    
504                                    for (byte[] bytes : bytesArray) {
505                                            contentLength += bytes.length;
506                                    }
507    
508                                    setContentLength(response, contentLength);
509    
510                                    response.flushBuffer();
511    
512                                    ServletOutputStream servletOutputStream =
513                                            response.getOutputStream();
514    
515                                    for (byte[] bytes : bytesArray) {
516                                            servletOutputStream.write(bytes);
517                                    }
518                            }
519                    }
520                    catch (IOException ioe) {
521                            if ((ioe instanceof SocketException) ||
522                                    isClientAbortException(ioe)) {
523    
524                                    if (_log.isWarnEnabled()) {
525                                            _log.warn(ioe);
526                                    }
527                            }
528                            else {
529                                    throw ioe;
530                            }
531                    }
532            }
533    
534            public static void write(
535                            HttpServletResponse response, ByteBuffer byteBuffer)
536                    throws IOException {
537    
538                    if (response instanceof BufferCacheServletResponse) {
539                            BufferCacheServletResponse bufferCacheServletResponse =
540                                    (BufferCacheServletResponse)response;
541    
542                            bufferCacheServletResponse.setByteBuffer(byteBuffer);
543                    }
544                    else {
545                            write(
546                                    response, byteBuffer.array(),
547                                    byteBuffer.arrayOffset() + byteBuffer.position(),
548                                    byteBuffer.arrayOffset() + byteBuffer.limit());
549                    }
550            }
551    
552            public static void write(
553                            HttpServletResponse response, CharBuffer charBuffer)
554                    throws IOException {
555    
556                    if (response instanceof BufferCacheServletResponse) {
557                            BufferCacheServletResponse bufferCacheServletResponse =
558                                    (BufferCacheServletResponse)response;
559    
560                            bufferCacheServletResponse.setCharBuffer(charBuffer);
561                    }
562                    else {
563                            ByteBuffer byteBuffer = CharsetEncoderUtil.encode(
564                                    StringPool.UTF8, charBuffer);
565    
566                            write(response, byteBuffer);
567                    }
568            }
569    
570            public static void write(HttpServletResponse response, File file)
571                    throws IOException {
572    
573                    if (response instanceof BufferCacheServletResponse) {
574                            BufferCacheServletResponse bufferCacheServletResponse =
575                                    (BufferCacheServletResponse)response;
576    
577                            ByteBuffer byteBuffer = ByteBuffer.wrap(FileUtil.getBytes(file));
578    
579                            bufferCacheServletResponse.setByteBuffer(byteBuffer);
580                    }
581                    else {
582                            FileInputStream fileInputStream = new FileInputStream(file);
583    
584                            try (FileChannel fileChannel = fileInputStream.getChannel()) {
585                                    long contentLength = fileChannel.size();
586    
587                                    setContentLength(response, contentLength);
588    
589                                    response.flushBuffer();
590    
591                                    fileChannel.transferTo(
592                                            0, contentLength,
593                                            Channels.newChannel(response.getOutputStream()));
594                            }
595                    }
596            }
597    
598            public static void write(
599                            HttpServletResponse response, InputStream inputStream)
600                    throws IOException {
601    
602                    write(response, inputStream, 0);
603            }
604    
605            public static void write(
606                            HttpServletResponse response, InputStream inputStream,
607                            long contentLength)
608                    throws IOException {
609    
610                    if (response.isCommitted()) {
611                            StreamUtil.cleanUp(inputStream);
612    
613                            return;
614                    }
615    
616                    if (contentLength > 0) {
617                            response.setHeader(
618                                    HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
619                    }
620    
621                    response.flushBuffer();
622    
623                    StreamUtil.transfer(inputStream, response.getOutputStream());
624            }
625    
626            public static void write(HttpServletResponse response, String s)
627                    throws IOException {
628    
629                    if (response instanceof BufferCacheServletResponse) {
630                            BufferCacheServletResponse bufferCacheServletResponse =
631                                    (BufferCacheServletResponse)response;
632    
633                            bufferCacheServletResponse.setString(s);
634                    }
635                    else {
636                            ByteBuffer byteBuffer = CharsetEncoderUtil.encode(
637                                    StringPool.UTF8, s);
638    
639                            write(response, byteBuffer);
640                    }
641            }
642    
643            protected static InputStream copyRange(
644                            InputStream inputStream, OutputStream outputStream, long start,
645                            long length)
646                    throws IOException {
647    
648                    if (inputStream instanceof FileInputStream) {
649                            FileInputStream fileInputStream = (FileInputStream)inputStream;
650    
651                            FileChannel fileChannel = fileInputStream.getChannel();
652    
653                            fileChannel.transferTo(
654                                    start, length, Channels.newChannel(outputStream));
655    
656                            return fileInputStream;
657                    }
658                    else if (inputStream instanceof ByteArrayInputStream) {
659                            ByteArrayInputStream byteArrayInputStream =
660                                    (ByteArrayInputStream)inputStream;
661    
662                            byteArrayInputStream.reset();
663    
664                            byteArrayInputStream.skip(start);
665    
666                            StreamUtil.transfer(byteArrayInputStream, outputStream, length);
667    
668                            return byteArrayInputStream;
669                    }
670                    else if (inputStream instanceof RandomAccessInputStream) {
671                            RandomAccessInputStream randomAccessInputStream =
672                                    (RandomAccessInputStream)inputStream;
673    
674                            randomAccessInputStream.seek(start);
675    
676                            StreamUtil.transfer(
677                                    randomAccessInputStream, outputStream, StreamUtil.BUFFER_SIZE,
678                                    false, length);
679    
680                            return randomAccessInputStream;
681                    }
682    
683                    return copyRange(
684                            new RandomAccessInputStream(inputStream), outputStream, start,
685                            length);
686            }
687    
688            protected static void setContentLength(
689                    HttpServletResponse response, long contentLength) {
690    
691                    response.setHeader(
692                            HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
693            }
694    
695            protected static void setHeaders(
696                    HttpServletRequest request, HttpServletResponse response,
697                    String fileName, String contentType, String contentDispositionType) {
698    
699                    if (_log.isDebugEnabled()) {
700                            _log.debug("Sending file of type " + contentType);
701                    }
702    
703                    // LEP-2201
704    
705                    if (Validator.isNotNull(contentType)) {
706                            response.setContentType(contentType);
707                    }
708    
709                    if (!response.containsHeader(HttpHeaders.CACHE_CONTROL)) {
710                            response.setHeader(
711                                    HttpHeaders.CACHE_CONTROL,
712                                    HttpHeaders.CACHE_CONTROL_PRIVATE_VALUE);
713                    }
714    
715                    if (Validator.isNull(fileName)) {
716                            return;
717                    }
718    
719                    String contentDispositionFileName = "filename=\"" + fileName + "\"";
720    
721                    // If necessary for non-ASCII characters, encode based on RFC 2184.
722                    // However, not all browsers support RFC 2184. See LEP-3127.
723    
724                    boolean ascii = true;
725    
726                    for (int i = 0; i < fileName.length(); i++) {
727                            if (!Validator.isAscii(fileName.charAt(i))) {
728                                    ascii = false;
729    
730                                    break;
731                            }
732                    }
733    
734                    if (!ascii) {
735                            String encodedFileName = HttpUtil.encodeURL(fileName, true);
736    
737                            if (BrowserSnifferUtil.isIe(request)) {
738                                    contentDispositionFileName =
739                                            "filename=\"" + encodedFileName + "\"";
740                            }
741                            else {
742                                    contentDispositionFileName =
743                                            "filename*=UTF-8''" + encodedFileName;
744                            }
745                    }
746    
747                    if (Validator.isNull(contentDispositionType)) {
748                            String extension = GetterUtil.getString(
749                                    FileUtil.getExtension(fileName));
750    
751                            extension = StringUtil.toLowerCase(extension);
752    
753                            String[] mimeTypesContentDispositionInline = null;
754    
755                            try {
756                                    mimeTypesContentDispositionInline = PropsUtil.getArray(
757                                            PropsKeys.MIME_TYPES_CONTENT_DISPOSITION_INLINE);
758                            }
759                            catch (Exception e) {
760                                    mimeTypesContentDispositionInline = new String[0];
761                            }
762    
763                            if (ArrayUtil.contains(
764                                            mimeTypesContentDispositionInline, extension)) {
765    
766                                    contentDispositionType = HttpHeaders.CONTENT_DISPOSITION_INLINE;
767    
768                                    contentType = MimeTypesUtil.getContentType(fileName);
769    
770                                    response.setContentType(contentType);
771                            }
772                            else {
773                                    contentDispositionType =
774                                            HttpHeaders.CONTENT_DISPOSITION_ATTACHMENT;
775                            }
776                    }
777    
778                    StringBundler sb = new StringBundler(4);
779    
780                    sb.append(contentDispositionType);
781                    sb.append(StringPool.SEMICOLON);
782                    sb.append(StringPool.SPACE);
783                    sb.append(contentDispositionFileName);
784    
785                    if (_log.isDebugEnabled()) {
786                            _log.debug("Setting content disposition header " + sb.toString());
787                    }
788    
789                    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, sb.toString());
790            }
791    
792            protected static void setHeaders(
793                    HttpServletRequest request, HttpServletResponse response,
794                    String fileName, String contentType, String contentDispositionType,
795                    Range range) {
796    
797                    setHeaders(
798                            request, response, fileName, contentType, contentDispositionType);
799    
800                    if (range != null) {
801                            response.setHeader(
802                                    HttpHeaders.CONTENT_RANGE, range.getContentRange());
803    
804                            response.setHeader(
805                                    HttpHeaders.CONTENT_LENGTH, String.valueOf(range.getLength()));
806                    }
807            }
808    
809            private static final String _CLIENT_ABORT_EXCEPTION =
810                    "org.apache.catalina.connector.ClientAbortException";
811    
812            private static final int _MAX_RANGE_FIELDS = GetterUtil.getInteger(
813                    PropsUtil.get(PropsKeys.WEB_SERVER_SERVLET_MAX_RANGE_FIELDS));
814    
815            private static final String _RANGE_REGEX =
816                    "^bytes=\\d*-\\d*(,\\s?\\d*-\\d*)*$";
817    
818            private static final Log _log = LogFactoryUtil.getLog(
819                    ServletResponseUtil.class);
820    
821    }