/* * BoundaryConsumer.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.util.buffer.Allocator; import org.simpleframework.util.buffer.Buffer; /** * The <code>BoundaryConsumer</code> 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 <code>BoundaryConsumer</code> 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 + 6; 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 { this.buffer = this.allocator.allocate(this.chunk); this.buffer.append(TOKEN); this.buffer.append(this.boundary); if (this.seek == this.chunk) { this.buffer.append(TOKEN); } this.buffer.append(LINE); } /** * 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 = this.boundary.length; if (this.count >= (size + 4)) { if (this.array[size + 2] == LAST[0]) return this.boundary(LAST); return this.boundary(LINE); } return 0; } /** * This is used to scan the boundary with an optional terminal. If the * terminal token is scanned correctly then this will return the number of * bytes of overflow that has been consumed. * * @param terminal * this is the terminal token for the boundary * * @return this returns the number of bytes of overflow consumed */ private int boundary(byte[] terminal) throws IOException { int size = this.boundary.length + 2; if (this.count >= (size + terminal.length)) { this.scan(TOKEN); this.scan(this.boundary); this.scan(terminal); this.done = true; return this.count - this.seek; } return 0; } /** * 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 (this.seek < this.count) { if (this.array[this.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 * <code>-</code> characters. * * @return this returns true with the terminal boundary is read */ public boolean isEnd() { return this.seek == this.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() { this.done = false; this.count = this.seek = 0; } }