/* * 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 ); } }