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: */
017:
018: package org.apache.coyote.http11.filters;
019:
020: import java.io.IOException;
021:
022: import org.apache.tomcat.util.buf.ByteChunk;
023: import org.apache.tomcat.util.buf.HexUtils;
024:
025: import org.apache.coyote.OutputBuffer;
026: import org.apache.coyote.Response;
027: import org.apache.coyote.http11.OutputFilter;
028:
029: /**
030: * Chunked output filter.
031: *
032: * @author Remy Maucherat
033: */
034: public class ChunkedOutputFilter implements OutputFilter {
035:
036: // -------------------------------------------------------------- Constants
037:
038: protected static final String ENCODING_NAME = "chunked";
039: protected static final ByteChunk ENCODING = new ByteChunk();
040:
041: /**
042: * End chunk.
043: */
044: protected static final ByteChunk END_CHUNK = new ByteChunk();
045:
046: // ----------------------------------------------------- Static Initializer
047:
048: static {
049: ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME
050: .length());
051: byte[] END_CHUNK_BYTES = { (byte) '0', (byte) '\r',
052: (byte) '\n', (byte) '\r', (byte) '\n' };
053: END_CHUNK.setBytes(END_CHUNK_BYTES, 0, END_CHUNK_BYTES.length);
054: }
055:
056: // ------------------------------------------------------------ Constructor
057:
058: /**
059: * Default constructor.
060: */
061: public ChunkedOutputFilter() {
062: chunkLength = new byte[10];
063: chunkLength[8] = (byte) '\r';
064: chunkLength[9] = (byte) '\n';
065: }
066:
067: // ----------------------------------------------------- Instance Variables
068:
069: /**
070: * Next buffer in the pipeline.
071: */
072: protected OutputBuffer buffer;
073:
074: /**
075: * Buffer used for chunk length conversion.
076: */
077: protected byte[] chunkLength = new byte[10];
078:
079: /**
080: * Chunk header.
081: */
082: protected ByteChunk chunkHeader = new ByteChunk();
083:
084: // ------------------------------------------------------------- Properties
085:
086: // --------------------------------------------------- OutputBuffer Methods
087:
088: /**
089: * Write some bytes.
090: *
091: * @return number of bytes written by the filter
092: */
093: public int doWrite(ByteChunk chunk, Response res)
094: throws IOException {
095:
096: int result = chunk.getLength();
097:
098: if (result <= 0) {
099: return 0;
100: }
101:
102: // Calculate chunk header
103: int pos = 7;
104: int current = result;
105: while (current > 0) {
106: int digit = current % 16;
107: current = current / 16;
108: chunkLength[pos--] = HexUtils.HEX[digit];
109: }
110: chunkHeader.setBytes(chunkLength, pos + 1, 9 - pos);
111: buffer.doWrite(chunkHeader, res);
112:
113: buffer.doWrite(chunk, res);
114:
115: chunkHeader.setBytes(chunkLength, 8, 2);
116: buffer.doWrite(chunkHeader, res);
117:
118: return result;
119:
120: }
121:
122: // --------------------------------------------------- OutputFilter Methods
123:
124: /**
125: * Some filters need additional parameters from the response. All the
126: * necessary reading can occur in that method, as this method is called
127: * after the response header processing is complete.
128: */
129: public void setResponse(Response response) {
130: }
131:
132: /**
133: * Set the next buffer in the filter pipeline.
134: */
135: public void setBuffer(OutputBuffer buffer) {
136: this .buffer = buffer;
137: }
138:
139: /**
140: * End the current request. It is acceptable to write extra bytes using
141: * buffer.doWrite during the execution of this method.
142: */
143: public long end() throws IOException {
144:
145: // Write end chunk
146: buffer.doWrite(END_CHUNK, null);
147:
148: return 0;
149:
150: }
151:
152: /**
153: * Make the filter ready to process the next request.
154: */
155: public void recycle() {
156: }
157:
158: /**
159: * Return the name of the associated encoding; Here, the value is
160: * "identity".
161: */
162: public ByteChunk getEncodingName() {
163: return ENCODING;
164: }
165:
166: }
|