public void testDecompressException() throws IOException {
    // build 5k array
    byte[] uncompressedData = new byte[5 * 1024];
    for (int i = 0; i < uncompressedData.length; i++) {
      uncompressedData[i] = 1;
    }

    byte[] compressedData = doCompress(uncompressedData);

    Bucket inBucket = new ArrayBucket(compressedData);
    NullBucket outBucket = new NullBucket();
    InputStream decompressorInput = null;
    OutputStream decompressorOutput = null;
    try {
      decompressorInput = inBucket.getInputStream();
      decompressorOutput = outBucket.getOutputStream();
      Compressor.COMPRESSOR_TYPE.GZIP.decompress(
          decompressorInput, decompressorOutput, 4096 + 10, 4096 + 20);
      decompressorInput.close();
      decompressorOutput.close();
    } catch (CompressionOutputSizeException e) {
      // expect this
      return;
    } finally {
      Closer.close(decompressorInput);
      Closer.close(decompressorOutput);
      inBucket.free();
      outBucket.free();
    }
    fail("did not throw expected CompressionOutputSizeException");
  }
Exemple #2
0
 @Override
 public void onGeneratedMetadata(
     Bucket metadata, BaseClientPutter state, ObjectContainer container) {
   Logger.error(
       this, "Bogus onGeneratedMetadata() on " + this + " from " + state, new Exception("error"));
   metadata.free();
 }
 public void freeData(ObjectContainer container, boolean persistForever) {
   if (data != null) {
     if (persistForever) container.activate(data, 1);
     data.free();
     if (persistForever) data.removeFrom(container);
     data = null;
   }
   if (persistForever) container.delete(this);
 }
Exemple #4
0
 @Override
 protected void freeData(ObjectContainer container) {
   Bucket data;
   synchronized (this) {
     data = returnBucket;
     returnBucket = null;
   }
   if (data != null) {
     if (persistenceType == PERSIST_FOREVER) container.activate(data, 5);
     data.free();
     if (persistenceType == PERSIST_FOREVER) data.removeFrom(container);
     if (persistenceType == PERSIST_FOREVER) container.store(this);
   }
 }
 @Override
 public void onSuccess(Object keyNum, ObjectContainer container, ClientContext context) {
   if (logMINOR) Logger.minor(this, "Succeeded (" + this + "): " + token);
   if (persistent) container.activate(parent, 1);
   if (parent.isCancelled()) {
     fail(new InsertException(InsertException.CANCELLED), container, context);
     return;
   }
   synchronized (this) {
     if (extraInserts > 0) {
       if (++completedInserts <= extraInserts) {
         if (logMINOR)
           Logger.minor(
               this,
               "Completed inserts "
                   + completedInserts
                   + " of extra inserts "
                   + extraInserts
                   + " on "
                   + this);
         if (persistent) container.store(this);
         return; // Let it repeat until we've done enough inserts. It hasn't been unregistered yet.
       }
     }
     if (finished) {
       // Normal with persistence.
       Logger.normal(this, "Block already completed: " + this);
       return;
     }
     finished = true;
   }
   if (persistent) {
     container.store(this);
     container.activate(sourceData, 1);
   }
   if (freeData) {
     sourceData.free();
     if (persistent) sourceData.removeFrom(container);
     sourceData = null;
     if (persistent) container.store(this);
   }
   parent.completedBlock(false, container, context);
   unregister(container, context, getPriorityClass(container));
   if (persistent) container.activate(cb, 1);
   if (logMINOR) Logger.minor(this, "Calling onSuccess for " + cb);
   cb.onSuccess(this, container, context);
   if (persistent) container.deactivate(cb, 1);
 }
Exemple #6
0
 /**
  * Must be called just after construction, but within a transaction.
  *
  * @throws IdentifierCollisionException If the identifier is already in use.
  */
 @Override
 void register(ObjectContainer container, boolean noTags) throws IdentifierCollisionException {
   if (client != null) assert (this.persistenceType == client.persistenceType);
   if (persistenceType != PERSIST_CONNECTION)
     try {
       client.register(this, container);
     } catch (IdentifierCollisionException e) {
       returnBucket.free();
       if (persistenceType == PERSIST_FOREVER) returnBucket.removeFrom(container);
       throw e;
     }
   if (persistenceType != PERSIST_CONNECTION && !noTags) {
     FCPMessage msg = persistentTagMessage(container);
     client.queueClientRequestMessage(msg, 0, container);
   }
 }
 /**
  * process the bucket containing the main index file
  *
  * @param bucket
  */
 private void processRequests(Bucket bucket) {
   try {
     InputStream is = bucket.getInputStream();
     parse(is);
     is.close();
     fetchStatus = FetchStatus.FETCHED;
     for (FindRequest req : waitingOnMainIndex) setdependencies(req);
     waitingOnMainIndex.clear();
   } catch (Exception e) {
     fetchStatus = FetchStatus.FAILED;
     for (FindRequest findRequest : waitingOnMainIndex) {
       findRequest.setError(new TaskAbortException("Failure parsing " + toString(), e));
     }
     Logger.error(this, indexuri, e);
   } finally {
     bucket.free();
   }
 }
 private void fail(
     InsertException e, boolean forceFatal, ObjectContainer container, ClientContext context) {
   synchronized (this) {
     if (finished) return;
     finished = true;
   }
   if (persistent) container.store(this);
   if (e.isFatal() || forceFatal) parent.fatallyFailedBlock(container, context);
   else parent.failedBlock(container, context);
   unregister(container, context, getPriorityClass(container));
   if (freeData) {
     if (persistent) container.activate(sourceData, 1);
     sourceData.free();
     if (persistent) sourceData.removeFrom(container);
     sourceData = null;
     if (persistent) container.store(this);
   }
   if (persistent) container.activate(cb, 1);
   cb.onFailure(e, this, container, context);
 }
 protected void onSuccess(
     Bucket data,
     boolean fromStore,
     Integer token,
     int blockNo,
     ClientKeyBlock block,
     ObjectContainer container,
     ClientContext context) {
   if (persistent) {
     container.activate(this, 1);
     container.activate(segment, 1);
     container.activate(parent, 1);
   }
   if (parent.isCancelled()) {
     data.free();
     if (persistent) data.removeFrom(container);
     onFailure(new FetchException(FetchException.CANCELLED), token, container, context);
     return;
   }
   segment.onSuccess(data, blockNo, block, container, context, this);
 }
 public void cancel(ObjectContainer container, ClientContext context) {
   synchronized (this) {
     if (finished) return;
     finished = true;
   }
   boolean wasActive = true;
   if (persistent) {
     container.store(this);
     wasActive = container.ext().isActive(cb);
     if (!wasActive) container.activate(cb, 1);
     container.activate(sourceData, 1);
   }
   if (freeData) {
     sourceData.free();
     if (persistent) sourceData.removeFrom(container);
     sourceData = null;
     if (persistent) container.store(this);
   }
   super.unregister(container, context, getPriorityClass(container));
   cb.onFailure(new InsertException(InsertException.CANCELLED), this, container, context);
   if (!wasActive) container.deactivate(cb, 1);
 }
  /**
   * You have to synchronize on this <code>WoTMessageListInserter</code> and then on the <code>
   * WoTMessageManager</code> when using this function.
   */
  private void insertMessageList(WoTOwnMessageList list)
      throws TransformerException, ParserConfigurationException, NoSuchMessageException,
          IOException, InsertException {
    Bucket tempB = mTBF.makeBucket(4096); /* TODO: set to a reasonable value */
    OutputStream os = null;

    try {
      os = tempB.getOutputStream();
      // This is what requires synchronization on the WoTMessageManager: While being marked as
      // "being inserted", message lists cannot be modified anymore,
      // so it must be guranteed that the "being inserted" mark does not change while we encode the
      // XML etc.
      mMessageManager.onMessageListInsertStarted(list);

      mXML.encode(mMessageManager, list, os);
      os.close();
      os = null;
      tempB.setReadOnly();

      /* We do not specifiy a ClientMetaData with mimetype because that would result in the insertion of an additional CHK */
      InsertBlock ib = new InsertBlock(tempB, null, list.getInsertURI());
      InsertContext ictx = mClient.getInsertContext(true);

      ClientPutter pu =
          mClient.insert(
              ib, false, null, false, ictx, this, RequestStarter.INTERACTIVE_PRIORITY_CLASS);
      addInsert(pu);
      tempB = null;

      if (logDEBUG)
        Logger.debug(this, "Started insert of WoTOwnMessageList at request URI " + list.getURI());
    } finally {
      if (tempB != null) tempB.free();
      Closer.close(os);
    }
  }
 @Override
 public void onGeneratedMetadata(
     Bucket metadata, BaseClientPutter state, ObjectContainer container) {
   metadata.free();
   throw new UnsupportedOperationException();
 }
 @Override
 public void free() {
   underlying.free();
 }
  private void handleZIPArchive(
      ArchiveStoreContext ctx,
      FreenetURI key,
      InputStream data,
      String element,
      ArchiveExtractCallback callback,
      MutableBoolean gotElement,
      boolean throwAtExit,
      ClientContext context)
      throws ArchiveFailureException, ArchiveRestartException {
    if (logMINOR) Logger.minor(this, "Handling a ZIP Archive");
    ZipInputStream zis = null;
    try {
      zis = new ZipInputStream(data);

      // MINOR: Assumes the first entry in the zip is a directory.
      ZipEntry entry;

      byte[] buf = new byte[32768];
      HashSet<String> names = new HashSet<String>();
      boolean gotMetadata = false;

      outerZIP:
      while (true) {
        entry = zis.getNextEntry();
        if (entry == null) break;
        if (entry.isDirectory()) continue;
        String name = stripLeadingSlashes(entry.getName());
        if (names.contains(name)) {
          Logger.error(this, "Duplicate key " + name + " in archive " + key);
          continue;
        }
        long size = entry.getSize();
        if (name.equals(".metadata")) gotMetadata = true;
        if (size > maxArchivedFileSize && !name.equals(element)) {
          addErrorElement(
              ctx,
              key,
              name,
              "File too big: "
                  + maxArchivedFileSize
                  + " greater than current archived file size limit "
                  + maxArchivedFileSize,
              true);
        } else {
          // Read the element
          long realLen = 0;
          Bucket output = tempBucketFactory.makeBucket(size);
          OutputStream out = output.getOutputStream();
          try {

            int readBytes;
            while ((readBytes = zis.read(buf)) > 0) {
              out.write(buf, 0, readBytes);
              readBytes += realLen;
              if (readBytes > maxArchivedFileSize) {
                addErrorElement(
                    ctx,
                    key,
                    name,
                    "File too big: "
                        + maxArchivedFileSize
                        + " greater than current archived file size limit "
                        + maxArchivedFileSize,
                    true);
                out.close();
                out = null;
                output.free();
                continue outerZIP;
              }
            }

          } finally {
            if (out != null) out.close();
          }
          if (size <= maxArchivedFileSize) {
            addStoreElement(ctx, key, name, output, gotElement, element, callback, context);
            names.add(name);
            trimStoredData();
          } else {
            // We are here because they asked for this file.
            callback.gotBucket(output, context);
            gotElement.value = true;
            addErrorElement(
                ctx,
                key,
                name,
                "File too big: "
                    + size
                    + " greater than current archived file size limit "
                    + maxArchivedFileSize,
                true);
          }
        }
      }

      // If no metadata, generate some
      if (!gotMetadata) {
        generateMetadata(ctx, key, names, gotElement, element, callback, context);
        trimStoredData();
      }
      if (throwAtExit) throw new ArchiveRestartException("Archive changed on re-fetch");

      if ((!gotElement.value) && element != null) callback.notInArchive(context);

    } catch (IOException e) {
      throw new ArchiveFailureException("Error reading archive: " + e.getMessage(), e);
    } finally {
      if (zis != null) {
        try {
          zis.close();
        } catch (IOException e) {
          Logger.error(this, "Failed to close stream: " + e, e);
        }
      }
    }
  }
  /** Handle an incoming connection. Blocking, obviously. */
  public static void handle(Socket sock, ToadletContainer container, PageMaker pageMaker) {
    try {
      InputStream is = new BufferedInputStream(sock.getInputStream(), 4096);

      LineReadingInputStream lis = new LineReadingInputStream(is);

      while (true) {

        String firstLine = lis.readLine(32768, 128, false); // ISO-8859-1 or US-ASCII, _not_ UTF-8
        if (firstLine == null) {
          sock.close();
          return;
        } else if (firstLine.equals("")) {
          continue;
        }

        boolean logMINOR = Logger.shouldLog(Logger.MINOR, ToadletContextImpl.class);

        if (logMINOR) Logger.minor(ToadletContextImpl.class, "first line: " + firstLine);

        String[] split = firstLine.split(" ");

        if (split.length != 3)
          throw new ParseException(
              "Could not parse request line (split.length=" + split.length + "): " + firstLine);

        if (!split[2].startsWith("HTTP/1."))
          throw new ParseException("Unrecognized protocol " + split[2]);

        URI uri;
        try {
          uri = URIPreEncoder.encodeURI(split[1]).normalize();
          if (logMINOR)
            Logger.minor(
                ToadletContextImpl.class,
                "URI: "
                    + uri
                    + " path "
                    + uri.getPath()
                    + " host "
                    + uri.getHost()
                    + " frag "
                    + uri.getFragment()
                    + " port "
                    + uri.getPort()
                    + " query "
                    + uri.getQuery()
                    + " scheme "
                    + uri.getScheme());
        } catch (URISyntaxException e) {
          sendURIParseError(sock.getOutputStream(), true, e);
          return;
        }

        String method = split[0];

        MultiValueTable<String, String> headers = new MultiValueTable<String, String>();

        while (true) {
          String line = lis.readLine(32768, 128, false); // ISO-8859 or US-ASCII, not UTF-8
          if (line == null) {
            sock.close();
            return;
          }
          // System.out.println("Length="+line.length()+": "+line);
          if (line.length() == 0) break;
          int index = line.indexOf(':');
          if (index < 0) {
            throw new ParseException("Missing ':' in request header field");
          }
          String before = line.substring(0, index).toLowerCase();
          String after = line.substring(index + 1);
          after = after.trim();
          headers.put(before, after);
        }

        boolean disconnect =
            shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers)
                || !container.enablePersistentConnections();

        boolean allowPost = container.allowPosts();
        BucketFactory bf = container.getBucketFactory();

        ToadletContextImpl ctx = new ToadletContextImpl(sock, headers, bf, pageMaker, container);
        ctx.shouldDisconnect = disconnect;

        /*
         * if we're handling a POST, copy the data into a bucket now,
         * before we go into the redirect loop
         */

        Bucket data;

        if (method.equals("POST")) {
          String slen = headers.get("content-length");
          if (slen == null) {
            sendError(
                sock.getOutputStream(),
                400,
                "Bad Request",
                l10n("noContentLengthInPOST"),
                true,
                null);
            return;
          }
          long len;
          try {
            len = Integer.parseInt(slen);
            if (len < 0) throw new NumberFormatException("content-length less than 0");
          } catch (NumberFormatException e) {
            sendError(
                sock.getOutputStream(),
                400,
                "Bad Request",
                l10n("cannotParseContentLengthWithError", "error", e.toString()),
                true,
                null);
            return;
          }
          if (allowPost && ((!container.publicGatewayMode()) || ctx.isAllowedFullAccess())) {

            data = bf.makeBucket(len);
            BucketTools.copyFrom(data, is, len);
          } else {
            FileUtil.skipFully(is, len);
            ctx.sendMethodNotAllowed("POST", true);
            ctx.close();
            return;
          }
        } else {
          // we're not doing to use it, but we have to keep
          // the compiler happy
          data = null;
        }

        // Handle it.
        try {
          boolean redirect = true;
          while (redirect) {
            // don't go around the loop unless set explicitly
            redirect = false;

            Toadlet t;
            try {
              t = container.findToadlet(uri);
            } catch (PermanentRedirectException e) {
              Toadlet.writePermanentRedirect(ctx, "Found elsewhere", e.newuri.toASCIIString());
              break;
            }

            if (t == null) {
              ctx.sendNoToadletError(ctx.shouldDisconnect);
              break;
            }

            HTTPRequestImpl req = new HTTPRequestImpl(uri, data, ctx, method);
            try {
              if (method.equals("GET")) {
                ctx.setActiveToadlet(t);
                t.handleGet(uri, req, ctx);
                ctx.close();
              } else if (method.equals("POST")) {
                ctx.setActiveToadlet(t);
                t.handlePost(uri, req, ctx);
              } else {
                ctx.sendMethodNotAllowed(method, ctx.shouldDisconnect);
                ctx.close();
              }
            } catch (RedirectException re) {
              uri = re.newuri;
              redirect = true;
            } finally {
              req.freeParts();
            }
          }
          if (ctx.shouldDisconnect) {
            sock.close();
            return;
          }
        } finally {
          if (data != null) data.free();
        }
      }

    } catch (ParseException e) {
      try {
        sendError(
            sock.getOutputStream(),
            400,
            "Bad Request",
            l10n("parseErrorWithError", "error", e.getMessage()),
            true,
            null);
      } catch (IOException e1) {
        // Ignore
      }
    } catch (TooLongException e) {
      try {
        sendError(
            sock.getOutputStream(), 400, "Bad Request", l10n("headersLineTooLong"), true, null);
      } catch (IOException e1) {
        // Ignore
      }
    } catch (IOException e) {
      // ignore and return
    } catch (ToadletContextClosedException e) {
      Logger.error(
          ToadletContextImpl.class, "ToadletContextClosedException while handling connection!");
    } catch (Throwable t) {
      Logger.error(ToadletContextImpl.class, "Caught error: " + t + " handling socket", t);
      try {
        sendError(sock.getOutputStream(), 500, "Internal Error", t.toString(), true, null);
      } catch (IOException e1) {
        // ignore and return
      }
    }
  }
Exemple #16
0
  public void onSuccess(FetchResult result, ClientGetter state, ObjectContainer container) {
    Logger.minor(this, "Succeeded: " + identifier);
    Bucket data = result.asBucket();
    if (persistenceType == PERSIST_FOREVER) {
      if (data != null) container.activate(data, 5);
      if (returnBucket != null) container.activate(returnBucket, 5);
      container.activate(client, 1);
      if (tempFile != null) container.activate(tempFile, 5);
      if (targetFile != null) container.activate(targetFile, 5);
    }
    if (returnBucket != data && !binaryBlob) {
      boolean failed = true;
      synchronized (this) {
        if (finished) {
          Logger.error(
              this,
              "Already finished but onSuccess() for " + this + " data = " + data,
              new Exception("debug"));
          data.free();
          if (persistenceType == PERSIST_FOREVER) data.removeFrom(container);
          return; // Already failed - bucket error maybe??
        }
        if (returnType == ClientGetMessage.RETURN_TYPE_DIRECT && returnBucket == null) {
          // Lost bucket for some reason e.g. bucket error (caused by IOException) on previous try??
          // Recover...
          returnBucket = data;
          failed = false;
        }
      }
      if (failed && persistenceType == PERSIST_FOREVER) {
        if (container.ext().getID(returnBucket) == container.ext().getID(data)) {
          Logger.error(
              this,
              "DB4O BUG DETECTED WITHOUT ARRAY HANDLING! EVIL HORRIBLE BUG! UID(returnBucket)="
                  + container.ext().getID(returnBucket)
                  + " for "
                  + returnBucket
                  + " active="
                  + container.ext().isActive(returnBucket)
                  + " stored = "
                  + container.ext().isStored(returnBucket)
                  + " but UID(data)="
                  + container.ext().getID(data)
                  + " for "
                  + data
                  + " active = "
                  + container.ext().isActive(data)
                  + " stored = "
                  + container.ext().isStored(data));
          // Succeed anyway, hope that the returned bucket is consistent...
          returnBucket = data;
          failed = false;
        }
      }
      if (failed) {
        Logger.error(
            this,
            "returnBucket = " + returnBucket + " but onSuccess() data = " + data,
            new Exception("debug"));
        // Caller guarantees that data == returnBucket
        onFailure(
            new FetchException(FetchException.INTERNAL_ERROR, "Data != returnBucket"),
            null,
            container);
        return;
      }
    }
    boolean dontFree = false;
    // FIXME I don't think this is a problem in this case...? (Disk write while locked..)
    AllDataMessage adm = null;
    synchronized (this) {
      if (succeeded) {
        Logger.error(this, "onSuccess called twice for " + this + " (" + identifier + ')');
        return; // We might be called twice; ignore it if so.
      }
      started = true;
      if (!binaryBlob) this.foundDataMimeType = result.getMimeType();
      else this.foundDataMimeType = BinaryBlob.MIME_TYPE;

      if (returnType == ClientGetMessage.RETURN_TYPE_DIRECT) {
        // Send all the data at once
        // FIXME there should be other options
        // FIXME: CompletionTime is set on finish() : we need to give it current time here
        // but it means we won't always return the same value to clients... Does it matter ?
        adm =
            new AllDataMessage(
                returnBucket,
                identifier,
                global,
                startupTime,
                System.currentTimeMillis(),
                this.foundDataMimeType);
        if (persistenceType == PERSIST_CONNECTION) adm.setFreeOnSent();
        dontFree = true;
        /*
         * } else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
        // Do nothing
         */
      } else if (returnType == ClientGetMessage.RETURN_TYPE_DISK) {
        // Write to temp file, then rename over filename
        if (!FileUtil.renameTo(tempFile, targetFile)) {
          postFetchProtocolErrorMessage =
              new ProtocolErrorMessage(
                  ProtocolErrorMessage.COULD_NOT_RENAME_FILE, false, null, identifier, global);
          // Don't delete temp file, user might want it.
        }
        returnBucket = new FileBucket(targetFile, false, true, false, false, false);
      }
      if (persistenceType == PERSIST_FOREVER && progressPending != null) {
        container.activate(progressPending, 1);
        progressPending.removeFrom(container);
      }
      progressPending = null;
      this.foundDataLength = returnBucket.size();
      this.succeeded = true;
      finished = true;
    }
    trySendDataFoundOrGetFailed(null, container);

    if (adm != null) trySendAllDataMessage(adm, null, container);
    if (!dontFree) {
      data.free();
    }
    if (persistenceType == PERSIST_FOREVER) {
      returnBucket.storeTo(container);
      container.store(this);
    }
    finish(container);
    if (client != null) client.notifySuccess(this, container);
  }
 @Override
 public void close() throws IOException {
   in.close();
   data.free();
 }
 public void dump() {
   copyBucket.free();
 }
  /** Handle an incoming connection. Blocking, obviously. */
  public static void handle(Socket sock, ToadletContainer container, PageMaker pageMaker) {
    try {
      InputStream is = new BufferedInputStream(sock.getInputStream(), 4096);

      LineReadingInputStream lis = new LineReadingInputStream(is);

      while (true) {

        String firstLine = lis.readLine(32768, 128, false); // ISO-8859-1 or US-ASCII, _not_ UTF-8
        if (firstLine == null) {
          sock.close();
          return;
        } else if (firstLine.equals("")) {
          continue;
        }

        boolean logMINOR = Logger.shouldLog(Logger.MINOR, ToadletContextImpl.class);

        if (logMINOR) Logger.minor(ToadletContextImpl.class, "first line: " + firstLine);

        String[] split = firstLine.split(" ");

        if (split.length != 3)
          throw new ParseException(
              "Could not parse request line (split.length=" + split.length + "): " + firstLine);

        if (!split[2].startsWith("HTTP/1."))
          throw new ParseException("Unrecognized protocol " + split[2]);

        URI uri;
        try {
          uri = URIPreEncoder.encodeURI(split[1]).normalize();
          if (logMINOR)
            Logger.minor(
                ToadletContextImpl.class,
                "URI: "
                    + uri
                    + " path "
                    + uri.getPath()
                    + " host "
                    + uri.getHost()
                    + " frag "
                    + uri.getFragment()
                    + " port "
                    + uri.getPort()
                    + " query "
                    + uri.getQuery()
                    + " scheme "
                    + uri.getScheme());
        } catch (URISyntaxException e) {
          sendURIParseError(sock.getOutputStream(), true, e);
          return;
        }

        String method = split[0];

        MultiValueTable<String, String> headers = new MultiValueTable<String, String>();

        while (true) {
          String line = lis.readLine(32768, 128, false); // ISO-8859 or US-ASCII, not UTF-8
          if (line == null) {
            sock.close();
            return;
          }
          // System.out.println("Length="+line.length()+": "+line);
          if (line.length() == 0) break;
          int index = line.indexOf(':');
          if (index < 0) {
            throw new ParseException("Missing ':' in request header field");
          }
          String before = line.substring(0, index).toLowerCase();
          String after = line.substring(index + 1);
          after = after.trim();
          headers.put(before, after);
        }

        boolean disconnect =
            shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers)
                || !container.enablePersistentConnections();

        boolean allowPost = container.allowPosts();
        BucketFactory bf = container.getBucketFactory();

        ToadletContextImpl ctx = new ToadletContextImpl(sock, headers, bf, pageMaker, container);
        ctx.shouldDisconnect = disconnect;

        /*
         * copy the data into a bucket now,
         * before we go into the redirect loop
         */

        Bucket data;

        boolean methodIsConfigurable = true;

        String slen = headers.get("content-length");

        if (METHODS_MUST_HAVE_DATA.contains(method)) {
          // <method> must have data
          methodIsConfigurable = false;
          if (slen == null) {
            ctx.shouldDisconnect = true;
            ctx.sendReplyHeaders(400, "Bad Request", null, null, -1);
            return;
          }
        } else if (METHODS_CANNOT_HAVE_DATA.contains(method)) {
          // <method> can not have data
          methodIsConfigurable = false;
          if (slen != null) {
            ctx.shouldDisconnect = true;
            ctx.sendReplyHeaders(400, "Bad Request", null, null, -1);
            return;
          }
        }

        if (slen != null) {
          long len;
          try {
            len = Integer.parseInt(slen);
            if (len < 0) throw new NumberFormatException("content-length less than 0");
          } catch (NumberFormatException e) {
            ctx.shouldDisconnect = true;
            ctx.sendReplyHeaders(400, "Bad Request", null, null, -1);
            return;
          }
          if (allowPost && ((!container.publicGatewayMode()) || ctx.isAllowedFullAccess())) {
            data = bf.makeBucket(len);
            BucketTools.copyFrom(data, is, len);
          } else {
            FileUtil.skipFully(is, len);
            if (method.equals("POST")) {
              ctx.sendMethodNotAllowed("POST", true);
            } else {
              sendError(
                  sock.getOutputStream(),
                  403,
                  "Forbidden",
                  "Content not allowed in this configuration",
                  true,
                  null);
            }
            ctx.close();
            return;
          }
        } else {
          // we're not doing to use it, but we have to keep
          // the compiler happy
          data = null;
        }

        if (!container.enableExtendedMethodHandling()) {
          if (!METHODS_RESTRICTED_MODE.contains(method)) {
            sendError(
                sock.getOutputStream(),
                403,
                "Forbidden",
                "Method not allowed in this configuration",
                true,
                null);
            return;
          }
        }

        // Handle it.
        try {
          boolean redirect = true;
          while (redirect) {
            // don't go around the loop unless set explicitly
            redirect = false;

            Toadlet t;
            try {
              t = container.findToadlet(uri);
            } catch (PermanentRedirectException e) {
              Toadlet.writePermanentRedirect(ctx, "Found elsewhere", e.newuri.toASCIIString());
              break;
            }

            if (t == null) {
              ctx.sendNoToadletError(ctx.shouldDisconnect);
              break;
            }

            // if the Toadlet does not support the method, we don't need to parse the data
            // also due this pre check a 'NoSuchMethodException' should never appear
            if (!(t.findSupportedMethods().contains(method))) {
              ctx.sendMethodNotAllowed(method, ctx.shouldDisconnect);
              break;
            }

            HTTPRequestImpl req = new HTTPRequestImpl(uri, data, ctx, method);
            try {
              String methodName = "handleMethod" + method;
              try {
                Class<? extends Toadlet> c = t.getClass();
                Method m = c.getMethod(methodName, HANDLE_PARAMETERS);
                if (methodIsConfigurable) {
                  AllowData anno = m.getAnnotation(AllowData.class);
                  if (anno == null) {
                    if (data != null) {
                      sendError(
                          sock.getOutputStream(),
                          400,
                          "Bad Request",
                          "Content not allowed",
                          true,
                          null);
                      ctx.close();
                      return;
                    }
                  } else if (anno.value()) {
                    if (data == null) {
                      sendError(
                          sock.getOutputStream(),
                          400,
                          "Bad Request",
                          "Missing Content",
                          true,
                          null);
                      ctx.close();
                      return;
                    }
                  }
                }
                ctx.setActiveToadlet(t);
                Object arglist[] = new Object[] {uri, req, ctx};
                m.invoke(t, arglist);
              } catch (InvocationTargetException ite) {
                throw ite.getCause();
              }
            } catch (RedirectException re) {
              uri = re.newuri;
              redirect = true;
            } finally {
              req.freeParts();
            }
          }
          if (ctx.shouldDisconnect) {
            sock.close();
            return;
          }
        } finally {
          if (data != null) data.free();
        }
      }

    } catch (ParseException e) {
      try {
        sendError(
            sock.getOutputStream(),
            400,
            "Bad Request",
            l10n("parseErrorWithError", "error", e.getMessage()),
            true,
            null);
      } catch (IOException e1) {
        // Ignore
      }
    } catch (TooLongException e) {
      try {
        sendError(
            sock.getOutputStream(), 400, "Bad Request", l10n("headersLineTooLong"), true, null);
      } catch (IOException e1) {
        // Ignore
      }
    } catch (IOException e) {
      // ignore and return
    } catch (ToadletContextClosedException e) {
      Logger.error(
          ToadletContextImpl.class, "ToadletContextClosedException while handling connection!");
    } catch (Throwable t) {
      Logger.error(ToadletContextImpl.class, "Caught error: " + t + " handling socket", t);
      try {
        sendError(sock.getOutputStream(), 500, "Internal Error", t.toString(), true, null);
      } catch (IOException e1) {
        // ignore and return
      }
    }
  }
 public void realFree() {
   bucket.free();
 }