Package freenet.client.async

Source Code of freenet.client.async.ClientGetWorkerThread

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */

package freenet.client.async;

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;

import freenet.client.ClientMetadata;
import freenet.client.FetchException;
import freenet.client.FetchException.FetchExceptionMode;
import freenet.client.filter.ContentFilter;
import freenet.client.filter.FoundURICallback;
import freenet.client.filter.LinkFilterExceptionProvider;
import freenet.client.filter.TagReplacerCallback;
import freenet.client.filter.ContentFilter.FilterStatus;
import freenet.client.filter.UnsafeContentTypeException;
import freenet.crypt.HashResult;
import freenet.crypt.MultiHashInputStream;
import freenet.keys.FreenetURI;
import freenet.support.Logger;
import freenet.support.compress.CompressionOutputSizeException;
import freenet.support.io.Closer;
import freenet.support.io.FileUtil;

/**A thread which does postprocessing of decompressed data, in particular,
* writing it to its final destination. This thread also handles hashing and
* filtering. If these are not required, <code>null</code> may be passed through
* the relevant constructor arguments.*/
public class ClientGetWorkerThread extends Thread {

  private InputStream input;
  final private URI uri;
  final private HashResult[] hashes;
  final private boolean filterData;
  final private String charset;
  final private FoundURICallback prefetchHook;
  final private TagReplacerCallback tagReplacer;

  /** Link filter exception provider. */
  private final LinkFilterExceptionProvider linkFilterExceptionProvider;

  final private String mimeType;
  private OutputStream output;
  private boolean finished = false;
  private Throwable error = null;
  private ClientMetadata clientMetadata = null;

  private static volatile boolean logMINOR;

  static {
    Logger.registerClass(ClientGetWorkerThread.class);
  }

  private static int counter;
  private static synchronized int counter() {
    return counter++;
  }

   /**
   * @param input The stream to read the data from
   * @param output The final destination to which the data will be written
   * @param uri The URI of the fetched data. Needed for the ContentFilter. Optional.
   * @param mimeType MIME of the fetched data. The best guess is needed for the
   * ContentFilter. Optional.
   * @param hashes Hashes of the fetched data, to be compared against. Optional.
   * @param filterData If true, the ContentFilter will be invoked
   * @param charset Charset to be passed to the ContentFilter.
   * Only needed if filterData is true.
   * @param prefetchHook Only needed if filterData is true.
   * @param tagReplacer Used for web-pushing. Only needed if filterData is true.
   * @param linkFilterExceptionProvider Provider for link filter exceptions
   * @throws URISyntaxException
   */
  public ClientGetWorkerThread(InputStream input, OutputStream output, FreenetURI uri,
      String mimeType, HashResult[] hashes, boolean filterData, String charset,
      FoundURICallback prefetchHook, TagReplacerCallback tagReplacer, LinkFilterExceptionProvider linkFilterExceptionProvider) throws URISyntaxException {
    super("ClientGetWorkerThread-"+counter());
    this.input = input;
    if(uri != null) this.uri = uri.toURI("/");
    else this.uri = null;
    if(mimeType != null && mimeType.compareTo("application/xhtml+xml") == 0) mimeType = "text/html";
    this.mimeType = mimeType;
    this.hashes = hashes;
    this.output = output;
    this.filterData = filterData;
    this.charset = charset;
    this.prefetchHook = prefetchHook;
    this.tagReplacer = tagReplacer;
    this.linkFilterExceptionProvider = linkFilterExceptionProvider;
    if(logMINOR) Logger.minor(this, "Created worker thread for "+uri+" mime type "+mimeType+" filter data = "+filterData+" charset "+charset);
  }

  @Override
  public void run() {
    if(logMINOR) Logger.minor(this, "Starting worker thread for "+uri+" mime type "+mimeType+" filter data = "+filterData+" charset "+charset);
    try {
      //Validate the hash of the now decompressed data
      input = new BufferedInputStream(input);
      MultiHashInputStream hashStream = null;
      if(hashes != null) {
        hashStream = new MultiHashInputStream(input, HashResult.makeBitmask(hashes));
        input = hashStream;
      }
      //Filter the data, if we are supposed to
      if(filterData){
        if(logMINOR) Logger.minor(this, "Running content filter... Prefetch hook: "+prefetchHook+" tagReplacer: "+tagReplacer);
        if(mimeType == null || uri == null || input == null || output == null) throw new IOException("Insufficient arguements to worker thread");
        // Send XHTML as HTML because we can't use web-pushing on XHTML.
        FilterStatus filterStatus = ContentFilter.filter(input, output, mimeType, uri, prefetchHook, tagReplacer, charset, linkFilterExceptionProvider);

        String detectedMIMEType = filterStatus.mimeType.concat(filterStatus.charset == null ? "" : "; charset="+filterStatus.charset);
        synchronized(this) {
          clientMetadata = new ClientMetadata(detectedMIMEType);
        }
      }
      else {
        if(logMINOR) Logger.minor(this, "Ignoring content filter. The final result has not been written. Writing now.");
        FileUtil.copy(input, output, -1);
      }
      // Dump the rest.
      try {
        while(true) {
            // FileInputStream.skip() doesn't do what we want. Use read().
            // Note this is only necessary because we might have an AEADInputStream?
            // FIXME get rid - they should check the end anyway?
            byte[] buf = new byte[4096];
            int r = input.read(buf);
            if(r < 0) break;
        }
      } catch (EOFException e) {
        // Okay.
      }
      input.close();
      output.close();
      if(hashes != null) {
        HashResult[] results = hashStream.getResults();
        if(!HashResult.strictEquals(results, hashes)) {
          Logger.error(this, "Hashes failed verification (length read is "+hashStream.getReadBytes()+") "+" for "+uri);
          throw new FetchException(FetchExceptionMode.CONTENT_HASH_FAILED);
        }
      }

      onFinish();
    } catch(Throwable t) {
      if(!(t instanceof FetchException || t instanceof UnsafeContentTypeException || t instanceof CompressionOutputSizeException))
        Logger.error(this, "Exception caught while processing fetch: "+t,t);
      else if(logMINOR)
        Logger.minor(this, "Exception caught while processing fetch: "+t,t);
      setError(t);
    } finally {
      Closer.close(input);
      Closer.close(output);
    }
  }

  /**
   * @return a ClientMetadata created by the ContentFilter
   */
  public synchronized ClientMetadata getClientMetadata() {
    return clientMetadata;
  }

  /** Stores the exception and awakens blocked threads. */
  public synchronized void setError(Throwable t) {
    if(error != null) return;
    error = t;
    onFinish();
  }

  public synchronized void getError() throws Throwable {
    if(error != null) throw error;
  }
  /** Marks that all work has finished, and wakes blocked threads.*/
  public synchronized void onFinish() {
    finished = true;
    notifyAll();
  }

  /** Blocks until all threads have finished executing and cleaning up. This method
   * also passes an exception which occurred back to the parent thread.
   * @throws Throwable Any errors that arose during execution*/
  public synchronized void waitFinished() throws Throwable {
    while(!finished) {
      try {
        wait();
      } catch(InterruptedException e) {
        //Do nothing
      }
    }
    getError();
  }
}
TOP

Related Classes of freenet.client.async.ClientGetWorkerThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.