/*
* ConsumerFactory.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 static org.simpleframework.http.Protocol.BOUNDARY;
import static org.simpleframework.http.Protocol.CHUNKED;
import static org.simpleframework.http.Protocol.MULTIPART;
import org.simpleframework.http.ContentType;
import org.simpleframework.util.buffer.Allocator;
/**
* The <code>ConsumerFactory</code> object is used to create a factory for
* creating consumers. This allows the request to determine the type of content
* sent and allows consumption of the request body in a the manner specified by
* the HTTP header. This will allow multipart and chunked content to be consumed
* from the pipeline.
*
* @author Niall Gallagher
*/
class ConsumerFactory {
/**
* This is used to allocate the memory associated with the body.
*/
protected Allocator allocator;
/**
* This is the header associated with the request body consumed.
*/
protected Segment segment;
/**
* Constructor for the <code>ConsumerFactory</code> object. This will create
* a factory that makes use of the HTTP header in order to determine the
* type of the body that is to be consumed.
*
* @param allocator
* this is the allocator used to allocate memory
* @param segment
* this is the HTTP header used to determine type
*/
public ConsumerFactory(Allocator allocator, Segment segment) {
this.allocator = allocator;
this.segment = segment;
}
/**
* This method is used to create a body consumer to read the body from the
* pipeline. This will examine the HTTP header associated with the body to
* determine how to consume the data. This will provide an empty consumer if
* no specific delimiter was provided.
*
* @return this returns the consumer used to consume the body
*/
public BodyConsumer getInstance() {
long length = this.getContentLength();
if (length < 0) return this.getInstance(8192);
return this.getInstance(length);
}
/**
* This method is used to create a body consumer to read the body from the
* pipeline. This will examine the HTTP header associated with the body to
* determine how to consume the data. This will provide an empty consumer if
* no specific delimiter was provided.
*
* @param length
* this is the length of the body to be consumed
*
* @return this returns the consumer used to consume the body
*/
public BodyConsumer getInstance(long length) {
byte[] boundary = this.getBoundary(this.segment);
if (this.isPart(this.segment))
return new PartSeriesConsumer(this.allocator, boundary, length);
if (this.isChunked(this.segment))
return new ChunkedConsumer(this.allocator);
if (this.isFixed(this.segment))
return new FixedConsumer(this.allocator, length);
return new EmptyConsumer();
}
/**
* This is used to extract information from the HTTP header that can be used
* to determine the type of the body. This will look at the HTTP headers
* provided to find a specific token which enables it to determine how to
* consume the body.
*
* @param header
* this is the header associated with the body
*
* @return the boundary for a multipart upload body
*/
protected byte[] getBoundary(Segment header) {
ContentType type = header.getContentType();
if (type != null) {
String token = type.getParameter(BOUNDARY);
if (token != null) return token.getBytes();
}
return null;
}
/**
* This is used to extract information from the HTTP header that can be used
* to determine the type of the body. This will look at the HTTP headers
* provided to find a specific token which enables it to determine how to
* consume the body.
*
* @param segment
* this is the header associated with the body
*
* @return true if the content type is that of a multipart body
*/
protected boolean isPart(Segment segment) {
ContentType type = segment.getContentType();
if (type != null) {
String token = type.getPrimary();
if (token.equals(MULTIPART)) return true;
}
return false;
}
/**
* This is used to extract information from the HTTP header that can be used
* to determine the type of the body. This will look at the HTTP headers
* provided to find a specific token which enables it to determine how to
* consume the body.
*
* @param segment
* this is the header associated with the body
*
* @return true if the body is to be consumed as a chunked body
*/
protected boolean isChunked(Segment segment) {
String encoding = segment.getTransferEncoding();
if (encoding != null) {
if (encoding.equals(CHUNKED)) return true;
}
return false;
}
/**
* This is used to extract information from the HTTP header that can be used
* to determine the type of the body. This will look at the HTTP headers
* provided to find a specific token which enables it to determine how to
* consume the body.
*
* @param segment
* this is the header associated with the body
*
* @return true if there was a content length in the header
*/
protected boolean isFixed(Segment segment) {
long length = segment.getContentLength();
if (length > 0) return true;
return false;
}
/**
* This is a convenience method that can be used to determine the length of
* the message body. This will determine if there is a
* <code>Content-Length</code> header, if it does then the length can be
* determined, if not then this returns -1.
*
* @return the content length, or -1 if it cannot be determined
*/
protected long getContentLength() {
return this.segment.getContentLength();
}
}