/*
* RequestCollector.java October 2002
*
* Copyright (C) 2002, 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.core;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.simpleframework.http.core.ContainerEvent.REQUEST_READY;
import static org.simpleframework.transport.TransportEvent.READ_WAIT;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import org.simpleframework.common.buffer.Allocator;
import org.simpleframework.http.message.Body;
import org.simpleframework.http.message.EntityConsumer;
import org.simpleframework.http.message.Header;
import org.simpleframework.transport.Channel;
import org.simpleframework.transport.ByteCursor;
import org.simpleframework.transport.trace.Trace;
/**
* The <code>RequestCollector</code> object is used to collect all of
* the data used to form a request entity. This will collect the data
* fragment by fragment from the underlying transport. When all of
* the data is consumed and the entity is created and then it is sent
* to the <code>Selector</code> object for processing. If the request
* has completed the next request can be collected from the
* underlying transport using a new collector object.
*
* @author Niall Gallagher
*/
class RequestCollector implements Collector {
/**
* This is used to consume the request entity from the channel.
*/
private final EntityConsumer entity;
/**
* This is the cursor used to read and reset the data.
*/
private final ByteCursor cursor;
/**
* This is the channel used to acquire the underlying data.
*/
private final Channel channel;
/**
* This is the trace used to listen for various collect events.
*/
private final Trace trace;
/**
* This represents the time the request collection began at.
*/
private final Timer timer;
/**
* The <code>RequestCollector</code> object used to collect the data
* from the underlying transport. In order to collect a body this
* must be given an <code>Allocator</code> which is used to create
* an internal buffer to store the consumed body.
*
* @param allocator this is the allocator used to buffer data
* @param tracker this is the tracker used to create sessions
* @param channel this is the channel used to read the data
*/
public RequestCollector(Allocator allocator, Channel channel) {
this.entity = new EntityConsumer(allocator, channel);
this.timer = new Timer(MILLISECONDS);
this.cursor = channel.getCursor();
this.trace = channel.getTrace();
this.channel = channel;
}
/**
* This is used to collect the data from a <code>Channel</code>
* which is used to compose the entity. If at any stage there
* are no ready bytes on the socket the controller provided can
* be used to queue the collector until such time as the socket
* is ready to read. Also, should the entity have completed reading
* all required content it is handed to the controller as ready,
* which processes the entity as a new client HTTP request.
*
* @param controller this is the controller used to queue this
*/
public void collect(Controller controller) throws IOException {
while(cursor.isReady()) {
if(entity.isFinished()) {
break;
} else {
timer.set();
entity.consume(cursor);
}
}
if(cursor.isOpen()) {
if(entity.isFinished()) {
trace.trace(REQUEST_READY);
controller.ready(this);
} else {
trace.trace(READ_WAIT);
controller.select(this);
}
}
}
/**
* This is the time in milliseconds when the request was first
* read from the underlying channel. The time represented here
* represents the time collection of this request began. This
* does not necessarily represent the time the bytes arrived on
* the receive buffers as some data may have been buffered.
*
* @return this represents the time the request was ready at
*/
public long getTime() {
return timer.get();
}
/**
* This provides the HTTP request header for the entity. This is
* always populated and provides the details sent by the client
* such as the target URI and the query if specified. Also this
* can be used to determine the method and protocol version used.
*
* @return the header provided by the HTTP request message
*/
public Header getHeader() {
return entity.getHeader();
}
/**
* This is used to acquire the body for this HTTP entity. This
* will return a body which can be used to read the content of
* the message, also if the request is multipart upload then all
* of the parts are provided as <code>Part</code> objects. Each
* part can then be read as an individual message.
*
* @return the body provided by the HTTP request message
*/
public Body getBody() {
return entity.getBody();
}
/**
* This provides the connected channel for the client. This is
* used to send and receive bytes to and from an transport layer.
* Each channel provided with an entity contains an attribute
* map which contains information about the connection.
*
* @return the connected channel for this HTTP entity
*/
public Channel getChannel() {
return channel;
}
/**
* This returns the socket channel that is used by the collector
* to read content from. This is a selectable socket, in that
* it can be registered with a Java NIO selector. This ensures
* that the system can be notified when the socket is ready.
*
* @return the socket channel used by this collector object
*/
public SocketChannel getSocket() {
return channel.getSocket();
}
}