/*
* UpdateConsumer.java February 2007
*
* Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.simpleframework.http.message;
import java.io.IOException;
import org.simpleframework.transport.Cursor;
/**
* The <code>UpdateConsumer</code> object is used to create a consumer that is
* used to consume and process large bodies. Typically a large body will be one
* that is delivered as part of a multipart upload or as a large form POST. The
* task of the large consumer is to consume all the bytes for the body, and
* reset the cursor after the last byte that has been send with the body. This
* ensures that the next character read from the cursor is the first character
* of a HTTP header within the pipeline.
*
* @author Niall Gallagher
*/
public abstract class UpdateConsumer implements BodyConsumer {
/**
* This is an external array used to copy data between buffers.
*/
protected byte[] array;
/**
* This is used to determine whether the consumer has finished.
*/
protected boolean finished;
/**
* Constructor for the <code>UpdateConsumer</code> object. This is used to
* create a consumer with a one kilobyte buffer used to read the contents
* from the cursor and transfer it to the buffer.
*/
protected UpdateConsumer() {
this(2048);
}
/**
* Constructor for the <code>UpdateConsumer</code> object. This is used to
* create a consumer with a variable size buffer used to read the contents
* from the cursor and transfer it to the buffer.
*
* @param chunk
* this is the size of the buffer used to read bytes
*/
protected UpdateConsumer(int chunk) {
this.array = new byte[chunk];
}
/**
* This is used to determine whether the consumer has finished reading. The
* consumer is considered finished if it has read a terminal token or if it
* has exhausted the stream and can not read any more. Once finished the
* consumed bytes can be parsed.
*
* @return true if the consumer has finished reading its content
*/
@Override
public boolean isFinished() {
return this.finished;
}
/**
* This method is used to consume bytes from the provided cursor. Consuming
* of bytes from the cursor should be done in such a way that it does not
* block. So typically only the number of ready bytes in the
* <code>Cursor</code> object should be read. If there are no ready bytes
* then this method should return.
*
* @param cursor
* used to consume the bytes from the HTTP pipeline
*/
@Override
public void consume(Cursor cursor) throws IOException {
int ready = cursor.ready();
while (ready > 0) {
int size = Math.min(ready, this.array.length);
int count = cursor.read(this.array, 0, size);
if (count > 0) {
int reset = this.update(this.array, 0, count);
if (reset > 0) {
cursor.reset(reset);
}
}
if (this.finished) {
this.commit(cursor);
break;
}
ready = cursor.ready();
}
}
/**
* This method can be used to commit the consumer when all data has been
* consumed. It is often used to push back some data on to the cursor so
* that the next consumer can read valid tokens from the stream of bytes. If
* no commit is required then the default implementation of this will simply
* return quietly.
*
* @param cursor
* this is the cursor used by this consumer
*/
protected void commit(Cursor cursor) throws IOException {
if (!this.finished) throw new IOException("Consumer not finished");
}
/**
* This is used to process the bytes that have been read from the cursor.
* Depending on the delimiter used this knows when the end of the body has
* been encountered. If the end is encountered this method must return the
* number of bytes overflow, and set the state of the consumer to finished.
*
* @param array
* this is a chunk read from the cursor
* @param off
* this is the offset within the array the chunk starts
* @param count
* this is the number of bytes within the array
*
* @return this returns the number of bytes overflow that is read
*/
protected abstract int update(byte[] array, int off, int count)
throws IOException;
}