/*
* Created on 2 Oct 2006
* Created by Paul Gardner
* Copyright (C) 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 63.529,40 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package com.aelitis.azureus.core.networkmanager.impl.http;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import org.gudy.azureus2.core3.util.Debug;
import com.aelitis.azureus.core.networkmanager.Transport;
import com.aelitis.azureus.core.peermanager.messaging.Message;
import com.aelitis.azureus.core.peermanager.messaging.MessageStreamDecoder;
public class
HTTPMessageDecoder
implements MessageStreamDecoder
{
private static final int MAX_HEADER = 1024;
private static final String NL = "\r\n";
private HTTPNetworkConnection http_connection;
private volatile boolean paused;
private volatile boolean paused_internally;
private volatile boolean destroyed;
private StringBuffer header_so_far = new StringBuffer();
private boolean header_ready;
private List messages = new ArrayList();
private int protocol_bytes_read;
public
HTTPMessageDecoder()
{
}
public
HTTPMessageDecoder(
String pre_read_header )
{
header_so_far.append( pre_read_header );
header_ready = true;
}
public void
setConnection(
HTTPNetworkConnection _http_connection )
{
http_connection = _http_connection;
if ( destroyed ){
http_connection.destroy();
}
}
public int
performStreamDecode(
Transport transport,
int max_bytes )
throws IOException
{
// before we start message processing we should have had the connection bound
if ( http_connection == null ){
Debug.out( "connection not yet assigned" );
throw( new IOException( "Internal error - connection not yet assigned" ));
}
// System.out.println( "performStreamDecode" );
protocol_bytes_read = 0;
if ( paused_internally ){
return( 0 );
}
if ( header_ready ){
header_ready = false;
int len = header_so_far.length();
http_connection.decodeHeader( this, header_so_far.toString());
header_so_far.setLength(0);
return( len );
}else{
int rem = max_bytes;
byte[] bytes = new byte[1];
ByteBuffer bb = ByteBuffer.wrap( bytes );
ByteBuffer[] bbs = { bb };
while( rem > 0 && !( paused || paused_internally )){
if ( transport.read( bbs,0, 1 ) == 0 ){
break;
}
rem--;
protocol_bytes_read++;
bb.flip();
char c = (char)(bytes[0]&0xff);
header_so_far.append( c );
if ( header_so_far.length() > MAX_HEADER ){
throw( new IOException( "HTTP header exceeded maximum of " + MAX_HEADER ));
}
if ( c == '\n' ){
String header_str = header_so_far.toString();
if ( header_str.endsWith( NL + NL )){
http_connection.decodeHeader( this, header_str );
header_so_far.setLength(0);
}
}
}
return( max_bytes - rem );
}
}
protected void
addMessage(
Message message )
{
synchronized( messages ){
messages.add( message );
}
http_connection.readWakeup();
}
public Message[]
removeDecodedMessages()
{
synchronized( messages ){
if ( messages.isEmpty()){
return null;
}
Message[] msgs = (Message[])messages.toArray( new Message[messages.size()] );
messages.clear();
return( msgs );
}
}
public int
getProtocolBytesDecoded()
{
return( protocol_bytes_read );
}
public int
getDataBytesDecoded()
{
return( 0 );
}
public int
getPercentDoneOfCurrentMessage()
{
return( 0 );
}
protected void
pauseInternally()
{
paused_internally = true;
}
public void
pauseDecoding()
{
paused = true;
}
public void
resumeDecoding()
{
if ( !destroyed ){
paused = false;
}
}
protected int
getQueueSize()
{
return( messages.size());
}
public ByteBuffer
destroy()
{
paused = true;
destroyed = true;
if ( http_connection != null ){
http_connection.destroy();
}
try{
for( int i=0; i<messages.size(); i++ ){
Message msg = (Message)messages.get( i );
msg.destroy();
}
}catch( IndexOutOfBoundsException e ){
// as access to messages_last_read isn't synchronized we can get this error if we destroy the
// decoder in parallel with messages being removed. We don't really want to synchornized access
// to this so we'll take the hit here
}
messages.clear();
return( null );
}
}