org.simpleframework.http.message.BoundaryConsumer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of simple-http Show documentation
Show all versions of simple-http Show documentation
Simple is a high performance asynchronous HTTP framework for Java
The newest version!
/*
* BoundaryConsumer.java February 2007
*
* Copyright (C) 2007, Niall Gallagher
*
* 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.common.buffer.Allocator;
import org.simpleframework.common.buffer.Buffer;
/**
* The BoundaryConsumer
is used to consume a boundary
* for a multipart message. This ensures that the boundary complies
* with the multipart specification in that it ends with a carriage
* return and line feed. This consumer implementation can be used
* multiple times as its internal buffer can be cleared and reset.
*
* @author Niall Gallagher
*/
class BoundaryConsumer extends ArrayConsumer {
/**
* This is the terminal token for a multipart boundary entity.
*/
private static final byte[] LAST = { '-', '-', '\r', '\n', };
/**
* This is the terminal token for a multipart boundary line.
*/
private static final byte[] LINE = { '\r', '\n' };
/**
* This represents the start of the boundary line for the part.
*/
private static final byte[] TOKEN = { '-', '-' };
/**
* This is used to allocate a buffer for for the boundary.
*/
private Allocator allocator;
/**
* This is used to consume the contents of the consumed buffer.
*/
private Buffer buffer;
/**
* This is the actual boundary value that is to be consumed.
*/
private byte[] boundary;
/**
* This counts the number of characters read from the start.
*/
private int seek;
/**
* Constructor for the BoundaryConsumer
object. This
* is used to create a boundary consumer for validating boundaries
* and consuming them from a provided source. This is used to help
* in reading multipart messages by removing boundaries from the
* stream.
*
* @param boundary this is the boundary value to be consumed
*/
public BoundaryConsumer(Allocator allocator, byte[] boundary) {
this.chunk = boundary.length + LAST.length + TOKEN.length;
this.allocator = allocator;
this.boundary = boundary;
}
/**
* This does not perform any processing after the boundary has
* been consumed. Because the boundary consumer is used only as a
* means to remove the boundary from the underlying stream there
* is no need to perform any processing of the value consumed.
*/
@Override
protected void process() throws IOException {
if(count < boundary.length + 4) {
throw new IOException("Invalid boundary processed");
}
}
/**
* This method is used to scan for the terminal token. It searches
* for the token and returns the number of bytes in the buffer
* after the terminal token. Returning the excess bytes allows the
* consumer to reset the bytes within the consumer object.
*
* @return this returns the number of excess bytes consumed
*/
@Override
protected int scan() throws IOException {
int size = boundary.length;
if(count >= 2 && seek < 2) {
if(scan(TOKEN)) {
append(TOKEN);
}
}
if(count >= 2 + size && seek < 2 + size) {
if(scan(boundary)) {
append(boundary);
}
}
if(count >= 4 + size && seek < 4 + size) {
if(array[size + 2] == TOKEN[0]) {
if(scan(TOKEN)) {
append(TOKEN);
}
} else if(array[size + 2] == LINE[0]) {
if(scan(LINE)) {
append(LINE);
}
done = true;
return count - seek;
}
}
if(count >= 6 + size && seek < 6 + size) {
if(scan(LINE)) {
append(LINE);
}
done = true;
return count - seek;
}
return 0;
}
/**
* This is used to append a token to the underlying buffer. Adding
* various tokens ensures that the whole message is reconstructed
* and can be forwarded to any connected service if used as a proxy.
*
* @param token this is the token that is to be appended
*/
private void append(byte[] token) throws IOException {
if(buffer == null) {
buffer = allocator.allocate(chunk);
}
buffer.append(token);
}
/**
* This is used to scan the specified token from the consumed bytes.
* If the data scanned does not match the token provided then this
* will throw an exception to signify a bad boundary. This will
* return true only when the whole boundary has been consumed.
*
* @param data this is the token to scan from the consumed bytes
*
* @return this returns true of the token has been read
*/
private boolean scan(byte[] data) throws IOException {
int size = data.length;
int pos = 0;
while(seek < count) {
if(array[seek++] != data[pos++]) {
throw new IOException("Invalid boundary");
}
if(pos == data.length) {
return true;
}
}
return pos == size;
}
/**
* This is used to determine whether the boundary has been read
* from the underlying stream. This is true only when the very
* last boundary has been read. This will be the boundary value
* that ends with the two -
characters.
*
* @return this returns true with the terminal boundary is read
*/
public boolean isEnd() {
return seek == chunk;
}
/**
* This is used to clear the state of the of boundary consumer
* such that it can be reused. This is required as the multipart
* body may contain many parts, all delimited with the same
* boundary. Clearing allows the next boundary to be consumed.
*/
public void clear() {
done = false;
count = seek = 0;
}
}