package com.limegroup.gnutella.uploader; import java.io.File; import java.net.InetAddress; import java.util.Collections; import java.util.List; import org.apache.http.HttpResponse; import org.limewire.collection.Range; import org.limewire.core.api.transfer.SourceInfo; import com.limegroup.gnutella.PushEndpoint; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.Uploader; import com.limegroup.gnutella.http.AltLocTracker; import com.limegroup.gnutella.library.FileDesc; import com.limegroup.gnutella.library.IncompleteFileDesc; /** * Maintains state for an HTTP upload. */ public class HTTPUploader extends AbstractUploader implements Uploader { /** * The URN specified in the X-Gnutella-Content-URN header, if any. */ private URN requestedURN; private boolean supportsQueueing = false; private AltLocTracker altLocTracker; private long uploadBegin; private long uploadEnd; private boolean containedRangeRequest; private long startTime = -1; private boolean visible; private String method; private volatile String friendId; private HttpResponse lastResponse; private PushEndpoint pushEndpoint; public HTTPUploader(String fileName, HTTPUploadSession session) { super(fileName, session); } @Override public void reinitialize() { super.reinitialize(); requestedURN = null; uploadBegin = 0; uploadEnd = 0; containedRangeRequest = false; method = null; } @Override public void setFileDesc(FileDesc fd) { super.setFileDesc(fd); setUploadBegin(0); setUploadEnd(getFileSize()); } public void setFile(File file) { setFileSize(file.length()); setUploadBegin(0); setUploadEnd(getFileSize()); } public void setFriendId(String id) { this.friendId = id; } public InetAddress getConnectedHost() { return getSession().getConnectedHost(); } public void stop() { // for testing: if the uploader was not initialized from a real // connection it does not have an IO session if (getSession().getIOSession() != null) { getSession().getIOSession().shutdown(); } } /** * Returns the index of the first byte of the file to upload. */ public long getUploadBegin() { return this.uploadBegin; } /** * Returns the exclusive index of the last byte to upload. */ public long getUploadEnd() { return this.uploadEnd; } public boolean containedRangeRequest() { return containedRangeRequest; } /** * Validates the byte range to upload. Shrinks the range to a valid range if * it was explicitly requested. * * @return true, if the upload end and upload begin are set to valid values */ public boolean validateRange() { long first = getUploadBegin(); long last = getUploadEnd(); if (first >= last) { return false; } if (getFileDesc() instanceof IncompleteFileDesc) { // If we are allowing, see if we have the range. IncompleteFileDesc ifd = (IncompleteFileDesc) getFileDesc(); // If the request contained a 'Range:' header, then we can // shrink the request to what we have available. if (containedRangeRequest()) { Range request = ifd.getAvailableSubRange( first, last - 1); if (request == null) { return false; } setUploadBegin(request.getLow()); setUploadEnd(request.getHigh() + 1); } else { if (!ifd.isRangeSatisfiable(first, last - 1)) { return false; } } } else if (first < 0 || last > getFileSize()) { return false; } return true; } /** * Returns the content URN that the client asked for. */ public URN getRequestedURN() { return requestedURN; } public void setRequestedURN(URN requestedURN) { this.requestedURN = requestedURN; } public boolean supportsQueueing() { return supportsQueueing && isValidQueueingAgent(); } /** * Blocks certain vendors from being queued, because of buggy downloading * implementations on their side. */ private boolean isValidQueueingAgent() { if (getUserAgent() == null) return true; return !getUserAgent().startsWith("Morpheus 3.0.2"); } public void setSupportsQueueing(boolean supportsQueueing) { this.supportsQueueing = supportsQueueing; } public AltLocTracker getAltLocTracker() { if (altLocTracker == null) { altLocTracker = new AltLocTracker(getFileDesc().getSHA1Urn()); } return altLocTracker; } public void setUploadBegin(long uploadBegin) { this.uploadBegin = uploadBegin; } public void setUploadEnd(long uploadEnd) { this.uploadEnd = uploadEnd; } public void setContainedRangeRequest(boolean containedRangeRequest) { this.containedRangeRequest = containedRangeRequest; } /** * Returns the time when the upload of content was started. */ public long getStartTime() { return startTime; } public void setStartTime(long startTime) { this.startTime = startTime; } public boolean isPartial() { return getUploadEnd() - getUploadBegin() < getFileSize(); } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public void setLastResponse(HttpResponse lastResponse) { this.lastResponse = lastResponse; } public HttpResponse getLastResponse() { return lastResponse; } @Override public boolean isBrowseHostEnabled() { return super.isBrowseHostEnabled() && (getGnutellaPort() != -1 || pushEndpoint != null); } public void setPushEndpoint(PushEndpoint pushEndpoint) { this.pushEndpoint = pushEndpoint; } public PushEndpoint getPushEndpoint() { return pushEndpoint; } @Override public File getFile() { FileDesc fileDesc = getFileDesc(); return fileDesc != null ? fileDesc.getFile() : null; } @Override public URN getUrn() { return getFileDesc().getSHA1Urn(); } @Override public int getNumUploadConnections() { return 1; } @Override public String getPresenceId() { return friendId; } @Override public float getSeedRatio() { //not applicable return -1; } @Override public List<SourceInfo> getTransferDetails() { return Collections.emptyList(); } }