/**
  * Convert a ClientKeyBlock to a Bucket. If an error occurs, report it via onFailure and return
  * null.
  */
 protected Bucket extract(
     ClientKeyBlock block, Object token, ObjectContainer container, ClientContext context) {
   Bucket data;
   try {
     data =
         block.decode(
             context.getBucketFactory(persistent),
             (int) (Math.min(ctx.maxOutputLength, Integer.MAX_VALUE)),
             false);
   } catch (KeyDecodeException e1) {
     if (Logger.shouldLog(Logger.MINOR, this)) Logger.minor(this, "Decode failure: " + e1, e1);
     onFailure(
         new FetchException(FetchException.BLOCK_DECODE_ERROR, e1.getMessage()),
         token,
         container,
         context);
     return null;
   } catch (TooBigException e) {
     onFailure(
         new FetchException(FetchException.TOO_BIG, e.getMessage()), token, container, context);
     return null;
   } catch (IOException e) {
     Logger.error(this, "Could not capture data - disk full?: " + e, e);
     onFailure(new FetchException(FetchException.BUCKET_ERROR, e), token, container, context);
     return null;
   }
   if (Logger.shouldLog(Logger.MINOR, this))
     Logger.minor(
         this, data == null ? "Could not decode: null" : ("Decoded " + data.size() + " bytes"));
   return data;
 }
  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");
  }
 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);
 }
Beispiel #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);
 }
Beispiel #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);
   }
 }
Beispiel #7
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();
 }
 @Override
 public Bucket createShadow() {
   Bucket undershadow = underlying.createShadow();
   AEADCryptBucket ret = new AEADCryptBucket(undershadow, key);
   ret.setReadOnly();
   return ret;
 }
  private byte[] doCompress(byte[] uncompressedData) throws IOException {
    Bucket inBucket = new ArrayBucket(uncompressedData);
    BucketFactory factory = new ArrayBucketFactory();
    Bucket outBucket = null;

    outBucket = Compressor.COMPRESSOR_TYPE.GZIP.compress(inBucket, factory, 32768, 32768);

    InputStream in = null;
    in = outBucket.getInputStream();
    long size = outBucket.size();
    byte[] outBuf = new byte[(int) size];

    in.read(outBuf);

    return outBuf;
  }
 public void removeFrom(ObjectContainer container) {
   container.activate(data, 1);
   data.removeFrom(container);
   container.activate(targetURI, 5);
   targetURI.removeFrom(container);
   container.delete(this);
 }
 @Override
 public OutputStream getOutputStream() throws IOException {
   synchronized (this) {
     if (readOnly) throw new IOException("Read only");
   }
   OutputStream os = underlying.getOutputStream();
   return AEADOutputStream.createAES(os, key, NodeStarter.getGlobalSecureRandom());
 }
 public void realRemoveFrom(ObjectContainer container) {
   synchronized (this) {
     if (reallyRemoved) Logger.error(this, "Calling realRemoveFrom() twice on " + this);
     reallyRemoved = true;
   }
   bucket.removeFrom(container);
   container.delete(this);
 }
 public synchronized CacheFetchResult getShadowBucket(FreenetURI key, boolean noFilter) {
   Object[] downloads = downloadsByURI.getArray(key);
   if (downloads == null) return null;
   for (Object o : downloads) {
     DownloadRequestStatus download = (DownloadRequestStatus) o;
     Bucket data = download.getDataShadow();
     if (data == null) continue;
     if (data.size() == 0) continue;
     if (noFilter && download.filterData) continue;
     // FIXME it probably *is* worth the effort to allow this when it is overridden on the fetcher,
     // since the user changed the type???
     if (download.overriddenDataType) continue;
     return new CacheFetchResult(
         new ClientMetadata(download.getMIMEType()), new NoFreeBucket(data), download.filterData);
   }
   return null;
 }
 /**
  * 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);
 }
 public void objectOnActivate(ObjectContainer container) {
   //		StackTraceElement[] elements = Thread.currentThread().getStackTrace();
   //		if(elements != null && elements.length > 100) {
   //			System.err.println("Infinite recursion in progress...");
   //		}
   if (logMINOR) Logger.minor(this, "Activating " + super.toString() + " : " + bucket.getClass());
   if (bucket == this) {
     Logger.error(this, "objectOnActivate on DelayedFreeBucket: wrapping self!!!");
     return;
   }
   // Cascading activation of dependancies
   container.activate(bucket, 1);
 }
 private BlockItem getBlockItem(ObjectContainer container, ClientContext context) {
   try {
     synchronized (this) {
       if (finished) return null;
     }
     if (persistent) {
       if (sourceData == null) {
         Logger.error(
             this,
             "getBlockItem(): sourceData = null but active = " + container.ext().isActive(this));
         return null;
       }
     }
     boolean deactivateBucket = false;
     if (persistent) {
       container.activate(uri, 1);
       deactivateBucket = !container.ext().isActive(sourceData);
       if (deactivateBucket) container.activate(sourceData, 1);
     }
     Bucket data = sourceData.createShadow();
     FreenetURI u = uri;
     if (u.getKeyType().equals("CHK") && !persistent) u = FreenetURI.EMPTY_CHK_URI;
     else u = u.clone();
     if (data == null) {
       data = context.tempBucketFactory.makeBucket(sourceData.size());
       BucketTools.copy(sourceData, data);
     }
     if (persistent) {
       if (deactivateBucket) container.deactivate(sourceData, 1);
       container.deactivate(uri, 1);
     }
     return new BlockItem(
         this, data, isMetadata, compressionCodec, sourceLength, u, hashCode(), persistent);
   } catch (IOException e) {
     fail(new InsertException(InsertException.BUCKET_ERROR, e, null), container, context);
     return null;
   }
 }
  /**
   * 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);
    }
  }
 public void removeFrom(ObjectContainer container, ClientContext context) {
   if (logMINOR) Logger.minor(this, "removeFrom() on " + this);
   container.activate(uri, 5);
   uri.removeFrom(container);
   if (resultingURI != null) {
     container.activate(resultingURI, 5);
     resultingURI.removeFrom(container);
   }
   // cb, parent are responsible for removing themselves
   // ctx is passed in and unmodified - usually the ClientPutter removes it
   container.activate(errors, 5);
   errors.removeFrom(container);
   if (freeData && sourceData != null && container.ext().isStored(sourceData)) {
     Logger.error(this, "Data not removed!");
     container.activate(sourceData, 1);
     sourceData.removeFrom(container);
   }
   container.delete(this);
 }
  /** 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
      }
    }
  }
  /** 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
      }
    }
  }
 @Override
 public void close() throws IOException {
   in.close();
   data.free();
 }
 public static InputStream create(Bucket data) throws IOException {
   return new ReadBucketAndFreeInputStream(data.getInputStream(), data);
 }
 @Override
 public void onGeneratedMetadata(
     Bucket metadata, BaseClientPutter state, ObjectContainer container) {
   metadata.free();
   throw new UnsupportedOperationException();
 }
 @Override
 public long size() {
   return underlying.size() - OVERHEAD;
 }
 @Override
 public void removeFrom(ObjectContainer container) {
   underlying.removeFrom(container);
   container.delete(this);
 }
 @Override
 public void storeTo(ObjectContainer container) {
   underlying.storeTo(container);
   container.store(this);
 }
 @Override
 public void free() {
   underlying.free();
 }