HttpFileDownload.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
phex.download.handler |
![]() |
![]() |
Phex |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* PHEX - The pure-java Gnutella-servent.
* Copyright (C) 2001 - 2008 Phex Development Group
*
* 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
*
* --- SVN Information ---
* $Id: HttpFileDownload.java 4205 2008-06-18 09:49:16Z gregork $
*/
package phex.download.handler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.httpclient.ChunkedInputStream;
import phex.common.AltLocContainer;
import phex.common.AlternateLocation;
import phex.common.URN;
import phex.common.address.AddressUtils;
import phex.common.address.DestAddress;
import phex.common.address.IpAddress;
import phex.common.address.MalformedDestAddressException;
import phex.common.log.NLogger;
import phex.download.DataDownloadScope;
import phex.download.DownloadEngine;
import phex.download.FileNotAvailableException;
import phex.download.HostBusyException;
import phex.download.MemoryFile;
import phex.download.RangeUnavailableException;
import phex.download.ReconnectException;
import phex.download.RemotelyQueuedException;
import phex.download.ThexVerificationData;
import phex.download.WrongHTTPHeaderException;
import phex.download.swarming.SWDownloadCandidate;
import phex.download.swarming.SWDownloadConstants;
import phex.download.swarming.SWDownloadFile;
import phex.download.swarming.SWDownloadSegment;
import phex.download.swarming.SWDownloadSet;
import phex.download.swarming.SWDownloadWorker;
import phex.download.swarming.SWDownloadCandidate.CandidateStatus;
import phex.host.UnusableHostException;
import phex.http.GnutellaHeaderNames;
import phex.http.GnutellaRequest;
import phex.http.HTTPCodes;
import phex.http.HTTPHeader;
import phex.http.HTTPHeaderNames;
import phex.http.HTTPMessageException;
import phex.http.HTTPProcessor;
import phex.http.HTTPRangeSet;
import phex.http.HTTPRequest;
import phex.http.HTTPResponse;
import phex.http.HTTPRetryAfter;
import phex.http.XQueueParameters;
import phex.io.buffer.ByteBuffer;
import phex.net.connection.Connection;
import phex.net.repres.PresentationManager;
import phex.prefs.core.DownloadPrefs;
import phex.prefs.core.NetworkPrefs;
import phex.prefs.core.UploadPrefs;
import phex.security.PhexSecurityManager;
import phex.servent.Servent;
import phex.utils.IOUtil;
import phex.utils.LengthLimitedInputStream;
public class HttpFileDownload extends AbstractHttpDownload
{
private static final int BUFFER_LENGTH = 16 * 1024;
private InputStream inStream;
private ContentRange replyContentRange;
private long replyContentLength;
private boolean isDownloadSuccessful;
public HttpFileDownload( DownloadEngine engine )
{
super( engine );
}
/**
* Performs download pre process operations.
* @throws DownloadHandlerException in case no segment is found to allocate.
*/
public void preProcess() throws DownloadHandlerException
{
SWDownloadSet downloadSet = downloadEngine.getDownloadSet();
SWDownloadSegment segment = downloadSet.allocateSegment( );
if ( segment == null )
{// no more segments found...
NLogger.debug( SWDownloadWorker.class, "No segment to allocate found.");
downloadSet.getCandidate().addToCandidateLog(
"No segment to allocate found." );
downloadSet.getCandidate().setStatus( CandidateStatus.WAITING );
throw new DownloadHandlerException( "No segment found to allocate." );
}
}
public void processHandshake()
throws IOException, UnusableHostException, HTTPMessageException
{
isDownloadSuccessful = false;
Connection connection = downloadEngine.getConnection();
SWDownloadSet downloadSet = downloadEngine.getDownloadSet();
Servent servent = downloadSet.getServent();
PhexSecurityManager securityService = servent.getSecurityService();
SWDownloadCandidate candidate = downloadSet.getCandidate();
SWDownloadFile downloadFile = downloadSet.getDownloadFile();
SWDownloadSegment segment = downloadSet.getDownloadSegment();
long downloadOffset = segment.getTransferStartPosition();
OutputStreamWriter writer = new OutputStreamWriter(
connection.getOutputStream() );
// reset to default input stream
inStream = connection.getInputStream();
String requestUrl = candidate.getDownloadRequestUrl();
HTTPRequest request = new HTTPRequest( "GET", requestUrl, true );
request.addHeader( new HTTPHeader( HTTPHeaderNames.HOST,
candidate.getHostAddress().getFullHostName() ) );
long segmentEndOffset = segment.getEnd();
if ( segmentEndOffset == -1 )
{// create header with open end
request.addHeader( new HTTPHeader( HTTPHeaderNames.RANGE,
"bytes=" + downloadOffset + "-" ) );
}
else
{
request.addHeader( new HTTPHeader( HTTPHeaderNames.RANGE,
"bytes=" + downloadOffset + "-" + segmentEndOffset ) );
}
request.addHeader( new HTTPHeader( GnutellaHeaderNames.X_QUEUE,
"0.1" ) );
// request a HTTP keep alive connection, needed for queuing to work.
request.addHeader( new HTTPHeader( HTTPHeaderNames.CONNECTION,
"Keep-Alive" ) );
if ( candidate.isG2FeatureAdded() )
{
request.addHeader( new HTTPHeader( "X-Features",
"g2/1.0" ) );
}
buildAltLocRequestHeader( downloadFile, candidate, request,
servent.getLocalAddress(), servent.isFirewalled() );
DestAddress localAdress = servent.getLocalAddress();
IpAddress myIp = localAdress.getIpAddress();
if ( !servent.isFirewalled() && ( myIp == null || !myIp.isSiteLocalIP() ) )
{
request.addHeader( new HTTPHeader( GnutellaHeaderNames.X_NODE,
localAdress.getFullHostName() ) );
if ( NetworkPrefs.AllowChatConnection.get().booleanValue() )
{
request.addHeader( new HTTPHeader( "Chat", localAdress.getFullHostName() ) );
}
}
String httpRequestStr = request.buildHTTPRequestString();
NLogger.debug( DownloadEngine.class,
"HTTP Request to: " + candidate.getHostAddress() + "\n" + httpRequestStr );
candidate.addToCandidateLog( "HTTP Request:\n" + httpRequestStr );
// write request...
writer.write( httpRequestStr );
writer.flush();
HTTPResponse response = HTTPProcessor.parseHTTPResponse( connection );
if ( NLogger.isDebugEnabled( DownloadEngine.class ) )
{
NLogger.debug( DownloadEngine.class,
"HTTP Response from: " + candidate.getHostAddress() + "\n"
+ response.buildHTTPResponseString() );
}
if ( DownloadPrefs.CandidateLogBufferSize.get().intValue() > 0 )
{
candidate.addToCandidateLog( "HTTP Response:\n"
+ response.buildHTTPResponseString() );
}
HTTPHeader header = response.getHeader( HTTPHeaderNames.SERVER );
if ( header != null )
{
candidate.setVendor( header.getValue() );
}
header = response.getHeader( HTTPHeaderNames.TRANSFER_ENCODING );
if ( header != null )
{
if ( header.getValue().equals("chunked") )
{
inStream = new ChunkedInputStream( connection.getInputStream() );
}
}
replyContentRange = null;
header = response.getHeader( HTTPHeaderNames.CONTENT_RANGE );
if ( header != null )
{
replyContentRange = parseContentRange( header.getValue() );
// startPos of -1 indicates '*' (free to choose)
if ( replyContentRange.startPos != -1 &&
replyContentRange.startPos != downloadOffset )
{
throw new IOException( "Invalid 'CONTENT-RANGE' start offset." );
}
}
replyContentLength = -1;
header = response.getHeader( HTTPHeaderNames.CONTENT_LENGTH );
if ( header != null )
{
try
{
replyContentLength = header.longValue();
}
catch ( NumberFormatException exp )
{ //unknown
}
}
header = response.getHeader( GnutellaHeaderNames.X_THEX_URI );
handleThexUriHeader( downloadFile, candidate, header );
List<HTTPHeader> contentURNHeaders = new ArrayList<HTTPHeader>();
header = response.getHeader( GnutellaHeaderNames.X_GNUTELLA_CONTENT_URN );
if ( header != null )
{
contentURNHeaders.add( header );
}
// Shareaza 1.8.10.4 send also a bitprint urn in multiple X-Content-URN headers!
HTTPHeader[] headers = response.getHeaders( GnutellaHeaderNames.X_CONTENT_URN );
CollectionUtils.addAll( contentURNHeaders, headers );
handleContentUrnHeaders( downloadFile, contentURNHeaders );
// check Limewire chat support header.
header = response.getHeader( GnutellaHeaderNames.CHAT );
if ( header != null )
{
candidate.setChatSupported( true );
}
// read out REMOTE-IP header... to update my IP
header = response.getHeader( GnutellaHeaderNames.REMOTE_IP );
if ( header != null )
{
byte[] remoteIP = AddressUtils.parseIP( header.getValue() );
if ( remoteIP != null )
{
IpAddress ip = new IpAddress( remoteIP );
DestAddress address = PresentationManager.getInstance().createHostAddress(ip, -1);
servent.updateLocalAddress( address );
}
}
int httpCode = response.getStatusCode();
// read available ranges
header = response.getHeader( GnutellaHeaderNames.X_AVAILABLE_RANGES );
if ( header != null )
{
HTTPRangeSet availableRanges =
HTTPRangeSet.parseHTTPRangeSet( header.getValue(), false );
if ( availableRanges == null )
{// failed to parse... give more detailed error report
NLogger.error( DownloadEngine.class,
"Failed to parse X-Available-Ranges in "
+ candidate.getVendor() + " request: "
+ response.buildHTTPResponseString() );
}
candidate.setAvailableRangeSet( availableRanges );
}
else if ( httpCode >= 200 && httpCode < 300
&& downloadFile.getTotalDataSize() != SWDownloadConstants.UNKNOWN_FILE_SIZE)
{// OK header and no available range header.. we assume candidate
// shares the whole file
candidate.setAvailableRangeSet( new HTTPRangeSet(
0, downloadFile.getTotalDataSize() - 1) );
}
URN downloadFileURN = downloadFile.getFileURN();
// collect alternate locations...
List<AlternateLocation> altLocList = new ArrayList<AlternateLocation>();
headers = response.getHeaders( GnutellaHeaderNames.ALT_LOC );
List<AlternateLocation> altLocTmpList = AltLocContainer.parseUriResAltLocFromHeaders( headers,
securityService );
altLocList.addAll( altLocTmpList );
headers = response.getHeaders( GnutellaHeaderNames.X_ALT_LOC );
altLocTmpList = AltLocContainer.parseUriResAltLocFromHeaders( headers,
securityService );
altLocList.addAll( altLocTmpList );
headers = response.getHeaders( GnutellaHeaderNames.X_ALT );
altLocTmpList = AltLocContainer.parseCompactIpAltLocFromHeaders( headers,
downloadFileURN, securityService );
altLocList.addAll( altLocTmpList );
// TODO1 huh?? dont we pare X-NALT????
Iterator<AlternateLocation> iterator = altLocList.iterator();
while( iterator.hasNext() )
{
downloadFile.addDownloadCandidate( iterator.next() );
}
// collect push proxies.
// first the old headers..
headers = response.getHeaders( "X-Pushproxies" );
handlePushProxyHeaders( candidate, headers, securityService );
headers = response.getHeaders( "X-Push-Proxies" );
handlePushProxyHeaders( candidate, headers, securityService );
// now the standard header
headers = response.getHeaders( GnutellaHeaderNames.X_PUSH_PROXY );
handlePushProxyHeaders( candidate, headers, securityService );
updateKeepAliveSupport( response );
if ( httpCode >= 200 && httpCode < 300 )
{// code accepted
// check if we can accept the urn...
if ( contentURNHeaders.size() == 0
&& requestUrl.startsWith(GnutellaRequest.GNUTELLA_URI_RES_PREFIX) )
{// we requested a download via /uri-res resource urn.
// we expect that the result contains a x-gnutella-content-urn
// or Shareaza X-Content-URN header.
throw new IOException(
"Response to uri-res request without valid Content-URN header." );
}
// check if we need and can update our file and segment size.
if ( downloadFile.getTotalDataSize() == SWDownloadConstants.UNKNOWN_FILE_SIZE )
{
// we have a file with an unknown data size. For aditional check assert
// certain parameters
assert( segment.getTotalDataSize() == -1 );
// Now verify if we have the great chance to update our file data!
if ( replyContentRange != null &&
replyContentRange.totalLength != SWDownloadConstants.UNKNOWN_FILE_SIZE )
{
downloadFile.setFileSize( replyContentRange.totalLength );
// we learned the file size. To allow normal segment use
// interrupt the download!
stopDownload();
throw new ReconnectException();
}
}
// connection successfully finished
NLogger.debug( DownloadEngine.class,
"HTTP Handshake successfull.");
return;
}
// check error type
else if ( httpCode == 503 )
{// 503 -> host is busy (this can also be returned when remotly queued)
header = response.getHeader( GnutellaHeaderNames.X_QUEUE );
XQueueParameters xQueueParameters = null;
if ( header != null )
{
xQueueParameters = XQueueParameters.parseXQueueParameters( header.getValue() );
}
// check for persistent connection (gtk-gnutella uses queuing with 'Connection: close')
if ( xQueueParameters != null && isKeepAliveSupported )
{
throw new RemotelyQueuedException( xQueueParameters );
}
else
{
header = response.getHeader( HTTPHeaderNames.RETRY_AFTER );
if ( header != null )
{
int delta = HTTPRetryAfter.parseDeltaInSeconds( header );
if ( delta > 0 )
{
throw new HostBusyException( delta );
}
}
throw new HostBusyException();
}
}
else if ( httpCode == HTTPCodes.HTTP_401_UNAUTHORIZED
|| httpCode == HTTPCodes.HTTP_403_FORBIDDEN )
{
if ( "Network Disabled".equals( response.getStatusReason() ) )
{
if ( candidate.isG2FeatureAdded() )
{
// already tried G2 but no success.. better give up and dont hammer..
throw new UnusableHostException( "Request Forbidden" );
}
else
{
// we have not tried G2 but we could..
candidate.setG2FeatureAdded( true );
throw new HostBusyException( 60 * 5 );
}
}
else
{
throw new UnusableHostException( "Request Forbidden" );
}
}
else if ( httpCode == 408 )
{
// 408 -> Time out. Try later?
throw new HostBusyException();
}
else if ( httpCode == 404 || httpCode == 410 )
{// 404: File not found / 410: Host not sharing
throw new FileNotAvailableException();
}
else if ( httpCode == 416 )
{// 416: Requested Range Unavailable
header = response.getHeader( HTTPHeaderNames.RETRY_AFTER );
if ( header != null )
{
int delta = HTTPRetryAfter.parseDeltaInSeconds( header );
if ( delta > 0 )
{
throw new RangeUnavailableException( delta );
}
}
throw new RangeUnavailableException();
}
else if ( httpCode == 500 )
{
throw new UnusableHostException( "Internal Server Error" );
}
else
{
throw new IOException( "Unknown HTTP code: " + httpCode );
}
}
public void processDownload() throws IOException
{
SWDownloadSet downloadSet = downloadEngine.getDownloadSet();
SWDownloadCandidate candidate = downloadSet.getCandidate();
SWDownloadFile downloadFile = downloadSet.getDownloadFile();
SWDownloadSegment segment = downloadSet.getDownloadSegment();
String snapshotOfSegment;
NLogger.debug( DownloadEngine.class, "Download Engine starts download.");
LengthLimitedInputStream downloadStream = null;
try
{
segment.downloadStartNotify();
snapshotOfSegment = segment.toString();
// determine the length to download, we start with the MAX
// which would cause a download until the stream ends.
long downloadLengthLeft = Long.MAX_VALUE;
// maybe we know the file size
if ( replyContentRange != null && replyContentRange.totalLength != -1 )
{
downloadLengthLeft = replyContentRange.totalLength;
}
// maybe we know a reply content length
if ( replyContentLength != -1 )
{
downloadLengthLeft = Math.min( replyContentLength, downloadLengthLeft );
}
// maybe the segment has a smaler length (usually not the case)
long segmentDataSizeLeft = segment.getTransferDataSizeLeft();
if ( segmentDataSizeLeft != -1 )
{
downloadLengthLeft = Math.min( segmentDataSizeLeft, downloadLengthLeft );
}
downloadStream = new LengthLimitedInputStream(
inStream, downloadLengthLeft );
MemoryFile memoryFile = downloadFile.getMemoryFile();
long fileOffset = segment.getStart() + segment.getTransferredDataSize();
long lengthDownloaded = segment.getTransferredDataSize();
int len;
byte[] buffer = new byte[BUFFER_LENGTH];
while( (len = downloadStream.read( buffer, 0, BUFFER_LENGTH )) > 0 )
{
if ( NLogger.isDebugEnabled( DownloadEngine.class ) )
{
NLogger.debug( DownloadEngine.class, "Reading in " + len + "bytes." );
candidate.addToCandidateLog( "Reading in " + len + "bytes." );
}
synchronized (segment)
{
long tmpCheckLength = lengthDownloaded + len;
if ( tmpCheckLength < segment.getTransferredDataSize() )
{
NLogger.error( DownloadEngine.class,
"TransferredDataSize would be going down! " +
" ll " + downloadLengthLeft + " l " + len + " ld "
+ lengthDownloaded + " gtds "
+ segment.getTransferredDataSize()
+ " seg: " + segment
+ " originally: " + snapshotOfSegment);
throw new IOException( "TransferredDataSize would be going down!" );
}
else if (segment.getTransferDataSize() > -1
&& tmpCheckLength > segment.getTransferDataSize())
{
NLogger.error( DownloadEngine.class,
"TransferredDataSize would be larger then segment! " +
" ll " + downloadLengthLeft + " l " + len + " ld "
+ lengthDownloaded + " gtds "
+ segment.getTransferredDataSize()
+ " seg: " + segment
+ " originally: " + snapshotOfSegment);
throw new IOException( "TransferredDataSize would be larger then segment!" );
}
// this will request buffers between 1KB and 16KB of length.
// don't release or clear directByteBuffer this will be done
// by MemoryFile and DownloadDataWriter
ByteBuffer byteBuffer = ByteBuffer.allocate( len );
byteBuffer.put( buffer, 0, len );
byteBuffer.flip();
DataDownloadScope dataScope = new DataDownloadScope( fileOffset,
fileOffset + len - 1, byteBuffer );
if ( NLogger.isDebugEnabled( DownloadEngine.class ) )
{
NLogger.debug( DownloadEngine.class, "Buffering " + len + "bytes." );
candidate.addToCandidateLog( "Buffering in " + len + "bytes." );
}
memoryFile.bufferDataScope(dataScope);
if ( NLogger.isDebugEnabled( DownloadEngine.class ) )
{
NLogger.debug( DownloadEngine.class, "Buffered " + len + "bytes." );
candidate.addToCandidateLog( "Buffered " + len + "bytes." );
}
fileOffset += len;
lengthDownloaded += len;
segment.setTransferredDataSize( lengthDownloaded );
candidate.incTotalDownloadSize( len );
// get transfer size since it might have changed in the meantime.
segmentDataSizeLeft = segment.getTransferDataSizeLeft();
if ( segmentDataSizeLeft != -1 )
{
downloadLengthLeft = Math.min( segmentDataSizeLeft, downloadLengthLeft );
downloadStream.setLengthLimit(downloadLengthLeft);
}
}
}
isDownloadSuccessful = true;
// if we successful downloaded and we still dont know the total file size,
// we can assume that the file was completly downloaded.
if ( downloadFile.getTotalDataSize() == SWDownloadConstants.UNKNOWN_FILE_SIZE )
{
// we have a file with an unknown data size. For aditional check assert
// certain parameters
assert( segment.getTotalDataSize() == -1 );
downloadFile.setFileSize( segment.getTransferredDataSize() );
}
}
finally
{// dont close managed file since it might be used by parallel threads.
segment.downloadStopNotify();
boolean isAcceptingNextSegment = isAcceptingNextRequest();
candidate.addToCandidateLog( "Is accepting next segment: " + isAcceptingNextSegment );
// this is for keep alive support...
if ( isAcceptingNextSegment && downloadStream != null )
{
// only need to close and consume left overs if we plan to
// continue using this connection.
downloadStream.close();
}
else
{
stopDownload();
}
}
}
public void postProcess()
{
// segment download completed, release segment
SWDownloadSet downloadSet = downloadEngine.getDownloadSet();
SWDownloadSegment downloadSegment = downloadSet.getDownloadSegment();
SWDownloadCandidate candidate = downloadSet.getCandidate();
if ( downloadSegment == null )
{
candidate.addToCandidateLog( "No download segment available." );
NLogger.debug( HttpFileDownload.class, "No download segment available." );
}
else
{
candidate.addToCandidateLog(
"Completed a segment which started at " + downloadSegment.getStart()
+ " and was downloaded at a rate of " + downloadSegment.getLongTermTransferRate() );
NLogger.debug( HttpFileDownload.class,
"Completed a segment which started at " + downloadSegment.getStart()
+ " and was downloaded at a rate of " + downloadSegment.getLongTermTransferRate());
}
NLogger.debug( HttpFileDownload.class,
"Releasing DownloadSegment: " + downloadSet.toString()
+ " - " + this);
downloadSet.releaseDownloadSegment();
}
public void stopDownload()
{
IOUtil.closeQuietly( inStream );
SWDownloadSegment segment = downloadEngine.getDownloadSet().getDownloadSegment();
if ( segment != null )
{
segment.downloadStopNotify();
}
}
/**
* Indicates whether the connection is keept alive and the next request can
* be send.
* @return true if the next request can be send on this connection
*/
public boolean isAcceptingNextRequest()
{
return isDownloadSuccessful && isKeepAliveSupported && replyContentLength != -1;
}
private static void handleContentUrnHeaders(SWDownloadFile downloadFile, List<HTTPHeader> headerList)
throws IOException
{
URN downloadFileURN = downloadFile.getFileURN();
if ( downloadFileURN != null )
{
for ( HTTPHeader urnHeader : headerList )
{
String contentURNStr = urnHeader.getValue();
// check if I can understand urn.
if ( URN.isValidURN( contentURNStr ) )
{
URN contentURN = new URN( contentURNStr );
if ( contentURN.isBitprintNid() )
{
ThexVerificationData thexVerificationData = downloadFile.getThexVerificationData();
String expectedRootHash = thexVerificationData.getRootHash();
if ( expectedRootHash != null &&
!expectedRootHash.equals( contentURN.getTigerTreeRootNss() ) )
{
throw new IOException(
"Required TTR and content URN TTR do not match:" +
expectedRootHash + " " + contentURN.getTigerTreeRootNss() );
}
}
if ( !downloadFileURN.equals( contentURN ) )
{
throw new IOException(
"Required URN and content URN do not match." );
}
}
}
}
}
private static void handleThexUriHeader(SWDownloadFile downloadFile,
SWDownloadCandidate candidate, HTTPHeader header)
{
if ( header == null )
{
return;
}
String value = header.getValue();
int idx = value.indexOf( ";" );
if ( idx < 0 )
{
return;
}
String thexUri = value.substring( 0, idx );
String root = value.substring( idx + 1 );
ThexVerificationData thexVerificationData = downloadFile.getThexVerificationData();
String expectedRootHash = thexVerificationData.getRootHash();
if ( expectedRootHash == null )
{// learn the hash for this download
thexVerificationData.setRootHash( root );
}
candidate.setThexUriRoot( thexUri, root );
}
private static void buildAltLocRequestHeader( SWDownloadFile downloadFile,
SWDownloadCandidate candidate, HTTPRequest request,
DestAddress serventAddress, boolean isFirewalled )
{
URN downloadFileURN = downloadFile.getFileURN();
if ( downloadFileURN == null )
{
return;
}
// add good alt loc http header
AltLocContainer altLocContainer = new AltLocContainer(
downloadFileURN );
// downloadFile.getGoodAltLocContainer() always returns a alt-loc container
// when downloadFileURN != null
altLocContainer.addContainer( downloadFile.getGoodAltLocContainer() );
// create a temp copy of the container and add local alt location
// if partial file sharing is active and we are not covered by a firewall
if ( !isFirewalled && UploadPrefs.SharePartialFiles.get().booleanValue() )
{
// add the local peer to the alt loc on creation, but only if its
// not a site local address.
if ( !serventAddress.isSiteLocalAddress() )
{
AlternateLocation newAltLoc = new AlternateLocation( serventAddress,
downloadFileURN );
altLocContainer.addAlternateLocation( newAltLoc );
}
}
HTTPHeader header = altLocContainer.getAltLocHTTPHeaderForAddress(
GnutellaHeaderNames.X_ALT, candidate.getHostAddress(),
candidate.getSendAltLocsSet() );
if ( header != null )
{
request.addHeader( header );
}
// add bad alt loc http header
// downloadFile.getBadAltLocContainer() always returns a alt-loc container
// when downloadFileURN != null
altLocContainer = downloadFile.getBadAltLocContainer();
header = altLocContainer.getAltLocHTTPHeaderForAddress(
GnutellaHeaderNames.X_NALT, candidate.getHostAddress(),
candidate.getSendAltLocsSet() );
if ( header != null )
{
request.addHeader( header );
}
}
private static void handlePushProxyHeaders( SWDownloadCandidate candidate,
HTTPHeader[] headers, PhexSecurityManager securityService )
{
if ( headers == null || headers.length == 0 )
{
return;
}
List<DestAddress> proxyList = new ArrayList<DestAddress>();
StringTokenizer tokenizer;
for ( int i = 0; i < headers.length; i++ )
{
HTTPHeader header = headers[i];
tokenizer = new StringTokenizer( header.getValue(), ",");
while( tokenizer.hasMoreTokens() )
{
String addressStr = tokenizer.nextToken().trim();
DestAddress address;
try
{
// includes security validation.
address = AddressUtils.parseAndValidateAddress(
addressStr, false, securityService );
proxyList.add( address );
}
catch (MalformedDestAddressException exp)
{
NLogger.debug( DownloadEngine.class,
"Malformed alt-location URL: " + exp.getMessage() );
}
}
}
if ( proxyList.size() == 0 )
{
return;
}
DestAddress[] pushProxyAddresses = new DestAddress[ proxyList.size() ];
proxyList.toArray( pushProxyAddresses );
candidate.setPushProxyAddresses( pushProxyAddresses );
}
/**
* We only care for the start offset since this is the importent point to
* begin the download from. Wherever it ends we try to download as long as we
* stay connected or until we reach our goal.
*
* Possible Content-Range Headers ( maybe not complete / header is upper
* cased by Phex )
*
* Content-range:bytes abc-def/xyz
* Content-range:bytes abc-def/*
* Content-range:bytes *\/xyz
* Content-range: bytes=abc-def/xyz (wrong but older Phex version and old clients use this)
*
* @param contentRangeLine the content range value
* @throws WrongHTTPHeaderException if the content range line has wrong format.
* @return the content range start offset.
*/
private ContentRange parseContentRange( String contentRangeLine )
throws WrongHTTPHeaderException
{
try
{
ContentRange range = new ContentRange();
String line = contentRangeLine.toLowerCase();
// skip over bytes plus extra char
int idx = line.indexOf( "bytes" ) + 6;
String rangeStr = line.substring( idx ).trim();
int slashIdx = rangeStr.indexOf('/');
String leadingPart = rangeStr.substring(0, slashIdx);
String trailingPart = rangeStr.substring( slashIdx + 1 );
// ?????/*
if ( trailingPart.charAt(0) == '*' )
{
range.totalLength = -1;
}
else // ?????/789
{
long fileLength = Long.parseLong(trailingPart);
range.totalLength = fileLength;
}
// */???
if ( leadingPart.charAt( 0 ) == '*' )
{
// startPos of -1 indicates '*' (free to choose)
range.startPos = -1;
range.endPos = range.totalLength;
}
else
{
// 123-456/???
int dashIdx = rangeStr.indexOf( '-' );
String startOffsetStr = leadingPart.substring( 0, dashIdx );
long startOffset = Long.parseLong( startOffsetStr );
String endOffsetStr = leadingPart.substring( dashIdx+1 );
long endOffset = Long.parseLong( endOffsetStr );
range.startPos = startOffset;
range.endPos = endOffset;
}
return range;
}
catch ( NumberFormatException exp )
{
NLogger.warn( DownloadEngine.class, exp, exp);
throw new WrongHTTPHeaderException(
"Number error while parsing content range: " + contentRangeLine );
}
catch ( IndexOutOfBoundsException exp )
{
throw new WrongHTTPHeaderException(
"Error while parsing content range: " + contentRangeLine );
}
}
private class ContentRange
{
/**
* startPos of -1 indicates '*' (free to choose)
*/
long startPos;
long endPos;
long totalLength;
}
}
The table below shows all metrics for HttpFileDownload.java.




