You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2010/05/25 20:17:41 UTC

svn commit: r948147 - in /shindig/trunk: ./ java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/ java/gadgets/src...

Author: johnh
Date: Tue May 25 18:17:40 2010
New Revision: 948147

URL: http://svn.apache.org/viewvc?rev=948147&view=rev
Log:
Introduces new interface HttpResponseRewriter, and for the moment - as a
proof-of-concept and for functional backward compatibility - implements only
BasicImageRewriter in terms of this interface.

HttpResponseRewriter's API parallels that of GadgetRewriter:
void rewrite(ContextObject in, MutableContent out);

Specifically:
void rewrite(HttpRequest in, HttpResponseBuilder out);

This change thus utilizes the HttpResponseBuilder-as-MutableContent CL submitted
a few weeks ago.

Additional notes:
* [Basic]ImageRewriter simply modifies an HttpResponse. Its implementation is
updated in this CL to reflect the same. 
* HttpResponseRegistry is introduced, with default binding to
BasicImageRewriter. The registry's rewriters are executed at the same place as
ImageRewriter previously was - in DefaultRequestPipeline, after fetch and before
caching.
* HttpResponseBuilder optimization implemented to return the "source"
HttpResponse object from which it was created when no changes were made to its
data. This avoids pointless object creation and data copying, maintaining
ImageRewriter's performance in such cases.

This CL is transitional, as its goals are to move to the following:
* Ability to perform image rewriting steps independently, in separate rewriting
passes (resizing, optimization, downsampling, format conversion, and so on).
* Situation in which all RequestRewriters are made into ResponseRewriters,
removing one Rewriter type.
* Ability to precisely define the caching characteristics of
[Response]Rewriters, of any type.


Added:
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultResponseRewriterRegistry.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriter.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriterRegistry.java
Modified:
    shindig/trunk/UPGRADING
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizer.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BaseOptimizer.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BasicImageRewriter.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizer.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizer.java
    shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizer.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpFetcherTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultInvalidationServiceTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultRequestPipelineTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/ImageRewriterTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizerTest.java
    shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizerTest.java

Modified: shindig/trunk/UPGRADING
URL: http://svn.apache.org/viewvc/shindig/trunk/UPGRADING?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/UPGRADING (original)
+++ shindig/trunk/UPGRADING Tue May 25 18:17:40 2010
@@ -53,6 +53,18 @@ encode tokens as well as decode them.
 The constructor RestfulCollection(List<T> entry, int startIndex, int totalResults) is removed.  
 Use the constructor with an items-per-page parameter instead.
 
+* RequestRewriter, ImageRewriter -> ResponseRewriter
+
+ResponseRewriter is replacing RequestRewriter and ImageRewriter. Its interface method is:
+void rewrite(HttpRequest req, HttpResponseBuilder builder);
+
+HttpResponseBuilder extends MutableContent. RequestRewriters may be migrated by
+mutating builder rather than the previously-passed MutableContent. There is no
+provision for reading "original" HttpResponse headers.
+
+ImageRewriters may be migrated to ResponseRewriters as well by mutating the builder,
+where previously a new HttpResponse was returned.
+
 
 == Java Guice Changes ==
 

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/DefaultRequestPipeline.java Tue May 25 18:17:40 2010
@@ -25,7 +25,8 @@ import com.google.inject.internal.Nullab
 import org.apache.shindig.common.util.Utf8UrlCoder;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.oauth.OAuthRequest;
-import org.apache.shindig.gadgets.rewrite.image.ImageRewriter;
+import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
+import org.apache.shindig.gadgets.rewrite.RewritingException;
 
 /**
  * A standard implementation of a request pipeline. Performs request caching and
@@ -36,7 +37,7 @@ public class DefaultRequestPipeline impl
   private final HttpFetcher httpFetcher;
   private final HttpCache httpCache;
   private final Provider<OAuthRequest> oauthRequestProvider;
-  private final ImageRewriter imageRewriter;
+  private final ResponseRewriterRegistry responseRewriterRegistry;
   private final InvalidationService invalidationService;
   private final HttpResponseMetadataHelper metadataHelper;
 
@@ -44,13 +45,13 @@ public class DefaultRequestPipeline impl
   public DefaultRequestPipeline(HttpFetcher httpFetcher,
                                 HttpCache httpCache,
                                 Provider<OAuthRequest> oauthRequestProvider,
-                                ImageRewriter imageRewriter,
+                                ResponseRewriterRegistry responseRewriterRegistry,
                                 InvalidationService invalidationService,
                                 @Nullable HttpResponseMetadataHelper metadataHelper) {
     this.httpFetcher = httpFetcher;
     this.httpCache = httpCache;
     this.oauthRequestProvider = oauthRequestProvider;
-    this.imageRewriter = imageRewriter;
+    this.responseRewriterRegistry = responseRewriterRegistry;
     this.invalidationService = invalidationService;
     this.metadataHelper = metadataHelper;
   }
@@ -106,8 +107,13 @@ public class DefaultRequestPipeline impl
     }
     
     if (!fetchedResponse.isError() && !request.getIgnoreCache() && request.getCacheTtl() != 0) {
-      fetchedResponse = imageRewriter.rewrite(request, fetchedResponse);
+      try {
+        fetchedResponse = responseRewriterRegistry.rewriteHttpResponse(request, fetchedResponse);
+      } catch (RewritingException e) {
+        throw new GadgetException(GadgetException.Code.INTERNAL_SERVER_ERROR, e, e.getHttpStatusCode());
+      }
     }
+    
     // Set response hash value in metadata (used for url versioning)
     fetchedResponse = HttpResponseMetadataHelper.updateHash(fetchedResponse, metadataHelper);
     if (!request.getIgnoreCache() ) {

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponseBuilder.java Tue May 25 18:17:40 2010
@@ -45,35 +45,49 @@ public class HttpResponseBuilder extends
   private int httpStatusCode = HttpResponse.SC_OK;
   private final Multimap<String, String> headers = HttpResponse.newHeaderMultimap();
   private final Map<String, String> metadata = Maps.newHashMap();
-
+  
+  // Stores the HttpResponse object, if any, from which this Builder is constructed.
+  // This allows us to avoid creating a new HttpResponse in create() when no changes
+  // have been made.
+  private HttpResponse responseObj;
+  private int responseObjNumChanges;
+  
+  public HttpResponseBuilder(GadgetHtmlParser parser, HttpResponse response) {
+    super(parser, response);
+    if (response != null) {
+      httpStatusCode = response.getHttpStatusCode();
+      headers.putAll(response.getHeaders());
+      metadata.putAll(response.getMetadata());
+    } else {
+      setResponse(null);
+    }
+    responseObj = response;
+    responseObjNumChanges = getNumChanges();
+  }
+  
   public HttpResponseBuilder() {
-    super(unsupportedParser(), (String)null);
-    this.setResponse(null);
+    this(unsupportedParser(), null);
   }
 
   public HttpResponseBuilder(HttpResponseBuilder builder) {
-    super(unsupportedParser(), builder.create());
-    httpStatusCode = builder.httpStatusCode;
-    headers.putAll(builder.headers);
-    metadata.putAll(builder.metadata);
+    this(unsupportedParser(), builder.create());
   }
 
   public HttpResponseBuilder(HttpResponse response) {
     this(unsupportedParser(), response);
   }
-  
-  public HttpResponseBuilder(GadgetHtmlParser parser, HttpResponse response) {
-    super(parser, response);
-    httpStatusCode = response.getHttpStatusCode();
-    headers.putAll(response.getHeaders());
-    metadata.putAll(response.getMetadata());
-  }
 
   /**
    * @return A new HttpResponse.
    */
   public HttpResponse create() {
-    return new HttpResponse(this);
+    if (getNumChanges() != responseObjNumChanges || responseObj == null) {
+      // Short-circuit the creation process: no need to create a
+      // new (immutable) HttpResponse object when no modifications occurred.
+      responseObj = new HttpResponse(this);
+      responseObjNumChanges = getNumChanges();
+    }
+    return responseObj;
   }
 
   /**
@@ -86,7 +100,6 @@ public class HttpResponseBuilder extends
   }
 
   public HttpResponseBuilder setEncoding(Charset charset) {
-
     Collection<String> values = headers.get("Content-Type");
     if (!values.isEmpty()) {
       String contentType = values.iterator().next();
@@ -101,6 +114,9 @@ public class HttpResponseBuilder extends
       newContentType += "charset=" + charset.name();
       values.clear();
       values.add(newContentType);
+      if (!(values.size() == 1 && !contentType.equals(newContentType))) {
+        incrementNumChanges();
+      }
     }
     return this;
   }
@@ -130,13 +146,23 @@ public class HttpResponseBuilder extends
   }
 
   public HttpResponseBuilder setHttpStatusCode(int httpStatusCode) {
-    this.httpStatusCode = httpStatusCode;
+    if (this.httpStatusCode != httpStatusCode) {
+      this.httpStatusCode = httpStatusCode;
+      incrementNumChanges();
+    }
+    return this;
+  }
+  
+  public HttpResponseBuilder clearAllHeaders() {
+    incrementNumChanges();
+    headers.clear();
     return this;
   }
 
   public HttpResponseBuilder addHeader(String name, String value) {
     if (name != null) {
       headers.put(name, value);
+      incrementNumChanges();
     }
     return this;
   }
@@ -144,6 +170,7 @@ public class HttpResponseBuilder extends
   public HttpResponseBuilder setHeader(String name, String value) {
     if (name != null) {
       headers.replaceValues(name, Lists.newArrayList(value));
+      incrementNumChanges();
     }
     return this;
   }
@@ -158,6 +185,7 @@ public class HttpResponseBuilder extends
   public HttpResponseBuilder addHeaders(Map<String, String> headers) {
     for (Map.Entry<String,String> entry : headers.entrySet()) {
       this.headers.put(entry.getKey(), entry.getValue());
+      incrementNumChanges();
     }
     return this;
   }
@@ -165,18 +193,24 @@ public class HttpResponseBuilder extends
   public HttpResponseBuilder addAllHeaders(Map<String, ? extends List<String>> headers) {
     for (Map.Entry<String,? extends List<String>> entry : headers.entrySet()) {
       this.headers.putAll(entry.getKey(), entry.getValue());
+      incrementNumChanges();
     }
     return this;
   }
 
   public Collection<String> removeHeader(String name) {
-    return headers.removeAll(name);
+    Collection<String> ret = headers.removeAll(name);
+    if (ret != null) {
+      incrementNumChanges();
+    }
+    return ret;
   }
 
   public HttpResponseBuilder setCacheTtl(int cacheTtl) {
     headers.removeAll("Pragma");
     headers.removeAll("Expires");
     headers.replaceValues("Cache-Control", ImmutableList.of("public,max-age=" + cacheTtl));
+    incrementNumChanges();
     return this;
   }
 
@@ -184,6 +218,7 @@ public class HttpResponseBuilder extends
     headers.removeAll("Cache-Control");
     headers.removeAll("Pragma");
     headers.put("Expires", DateUtil.formatRfc1123Date(expirationTime));
+    incrementNumChanges();
     return this;
   }
 
@@ -195,18 +230,25 @@ public class HttpResponseBuilder extends
     headers.replaceValues("Cache-Control", NO_CACHE_HEADER);
     headers.replaceValues("Pragma", NO_CACHE_HEADER);
     headers.removeAll("Expires");
+    incrementNumChanges();
     return this;
   }
 
   public HttpResponseBuilder setMetadata(String key, String value) {
     metadata.put(key, value);
+    incrementNumChanges();
     return this;
   }
 
   public HttpResponseBuilder setMetadata(Map<String, String> metadata) {
     this.metadata.putAll(metadata);
+    incrementNumChanges();
     return this;
   }
+  
+  public int getContentLength() {
+    return getResponse().length;
+  }
 
   Multimap<String, String> getHeaders() {
     return headers;

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultResponseRewriterRegistry.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultResponseRewriterRegistry.java?rev=948147&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultResponseRewriterRegistry.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/DefaultResponseRewriterRegistry.java Tue May 25 18:17:40 2010
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.gadgets.rewrite;
+
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
+import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
+
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+
+/**
+ * Basic registry -- just iterates over rewriters and invokes them sequentially.
+ */
+public class DefaultResponseRewriterRegistry implements ResponseRewriterRegistry {
+  protected final List<ResponseRewriter> rewriters;
+  protected final GadgetHtmlParser htmlParser;
+
+  @Inject
+  public DefaultResponseRewriterRegistry(
+      List<ResponseRewriter> rewriters,
+      GadgetHtmlParser htmlParser) {
+    if (rewriters == null) {
+      rewriters = Collections.emptyList();
+    }
+    this.rewriters = Lists.newLinkedList(rewriters);
+    this.htmlParser = htmlParser;
+  }
+
+  /** {@inheritDoc} */
+  public HttpResponse rewriteHttpResponse(HttpRequest req, HttpResponse resp)
+      throws RewritingException {
+    HttpResponseBuilder builder = new HttpResponseBuilder(htmlParser, resp);
+
+    for (ResponseRewriter rewriter : rewriters) {
+      rewriter.rewrite(req, builder);
+    }
+    
+    // Returns the original HttpResponse if no changes have been made.
+    return builder.create();
+  }
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriter.java?rev=948147&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriter.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriter.java Tue May 25 18:17:40 2010
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.gadgets.rewrite;
+
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
+
+public interface ResponseRewriter {
+  public void rewrite(HttpRequest request, HttpResponseBuilder response) throws RewritingException;
+}

Added: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriterRegistry.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriterRegistry.java?rev=948147&view=auto
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriterRegistry.java (added)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ResponseRewriterRegistry.java Tue May 25 18:17:40 2010
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.gadgets.rewrite;
+
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Performs rewriting operations by invoking one or more {@link ResponseRewriter}s.
+ */
+@ImplementedBy(DefaultResponseRewriterRegistry.class)
+public interface ResponseRewriterRegistry {
+
+  /**
+   * Rewrites an {@code HttpResponse} object with the given request as context,
+   * using the registered rewriters.
+   * @param req Request object for context.
+   * @param resp Original response object.
+   * @return Rewritten response object, or resp if not modified.
+   */
+  HttpResponse rewriteHttpResponse(HttpRequest req, HttpResponse resp)
+    throws RewritingException;
+}

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/RewriteModule.java Tue May 25 18:17:40 2010
@@ -29,6 +29,7 @@ import org.apache.shindig.gadgets.render
 import org.apache.shindig.gadgets.render.RenderingGadgetRewriter;
 import org.apache.shindig.gadgets.render.old.SanitizingGadgetRewriter;
 import org.apache.shindig.gadgets.render.old.SanitizingRequestRewriter;
+import org.apache.shindig.gadgets.rewrite.image.BasicImageRewriter;
 import org.apache.shindig.gadgets.rewrite.old.CssRequestRewriter;
 import org.apache.shindig.gadgets.rewrite.old.HTMLContentRewriter;
 import org.apache.shindig.gadgets.servlet.CajaContentRewriter;
@@ -76,4 +77,11 @@ public class RewriteModule extends Abstr
       SanitizingRequestRewriter sanitizedRewriter) {
     return ImmutableList.of(optimizingRewriter, cssRewriter, sanitizedRewriter);
   }
+  
+  @Provides
+  @Singleton
+  protected List<ResponseRewriter> provideResponseRewriters(
+      BasicImageRewriter imageRewriter) {
+    return ImmutableList.<ResponseRewriter>of(imageRewriter);
+  }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizer.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizer.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizer.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizer.java Tue May 25 18:17:40 2010
@@ -19,7 +19,7 @@ package org.apache.shindig.gadgets.rewri
 
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.Sanselan;
-import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 
 import javax.imageio.ImageIO;
 import javax.imageio.ImageWriter;
@@ -37,8 +37,8 @@ public class BMPOptimizer extends PNGOpt
     return Sanselan.getBufferedImage(is);
   }
 
-  public BMPOptimizer(OptimizerConfig config, HttpResponse original) {
-    super(config, original);
+  public BMPOptimizer(OptimizerConfig config, HttpResponseBuilder response) {
+    super(config, response);
     ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
     outputter = new ImageIOOutputter(writer, null);
   }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BaseOptimizer.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BaseOptimizer.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BaseOptimizer.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BaseOptimizer.java Tue May 25 18:17:40 2010
@@ -20,7 +20,6 @@ package org.apache.shindig.gadgets.rewri
 import org.apache.sanselan.ImageFormat;
 import org.apache.sanselan.ImageWriteException;
 import org.apache.sanselan.Sanselan;
-import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 
 import com.google.common.collect.ImmutableMap;
@@ -51,7 +50,7 @@ abstract class BaseOptimizer {
       "gif", ImageFormat.IMAGE_FORMAT_GIF,
       "jpeg", ImageFormat.IMAGE_FORMAT_JPEG);
 
-  final HttpResponse originalResponse;
+  final HttpResponseBuilder response;
   final OptimizerConfig config;
 
   protected ImageOutputter outputter;
@@ -60,10 +59,10 @@ abstract class BaseOptimizer {
   int reductionPct;
 
 
-  public BaseOptimizer(OptimizerConfig config, HttpResponse original) {
+  public BaseOptimizer(OptimizerConfig config, HttpResponseBuilder response) {
     this.config = config;
-    this.originalResponse = original;
-    this.minLength = original.getContentLength();
+    this.response = response;
+    this.minLength = response.getContentLength();
     this.outputter = getOutputter();
   }
 
@@ -92,14 +91,14 @@ abstract class BaseOptimizer {
     if (minLength > bytes.length) {
       minBytes = bytes;
       minLength = minBytes.length;
-      reductionPct = ((originalResponse.getContentLength() - minLength) * 100) /
-          originalResponse.getContentLength();
+      reductionPct = ((response.getContentLength() - minLength) * 100) /
+          response.getContentLength();
     }
   }
 
-  public HttpResponse rewrite(BufferedImage image) throws IOException {
+  public void rewrite(BufferedImage image) throws IOException {
     if (outputter == null) {
-      return originalResponse;
+      return;
     }
 
     long time = System.currentTimeMillis();
@@ -108,17 +107,16 @@ abstract class BaseOptimizer {
     if (minBytes != null && minBytes.length != 0) {
       StringBuilder rewriteMsg = new StringBuilder(24);
       rewriteMsg.append("c=").append(
-          ((minBytes.length * 100) / originalResponse.getContentLength()));
+          ((minBytes.length * 100) / response.getContentLength()));
       if (!getOutputContentType().equals(getOriginalContentType())) {
         rewriteMsg.append(";o=").append(getOriginalContentType());
       }
       rewriteMsg.append(";t=").append(time);
-      return new HttpResponseBuilder(originalResponse)
+      response.clearAllHeaders()
           .setHeader("Content-Type", getOutputContentType())
           .setHeader("X-Shindig-Rewrite", rewriteMsg.toString())
-          .setResponse(minBytes).create();
+          .setResponse(minBytes);
     }
-    return originalResponse;
   }
 
   /**

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BasicImageRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BasicImageRewriter.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BasicImageRewriter.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/BasicImageRewriter.java Tue May 25 18:17:40 2010
@@ -35,6 +35,7 @@ import org.apache.shindig.common.uri.Uri
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.HttpResponseBuilder;
+import org.apache.shindig.gadgets.rewrite.ResponseRewriter;
 import org.apache.shindig.gadgets.rewrite.image.BaseOptimizer.ImageIOOutputter;
 import org.apache.shindig.gadgets.rewrite.image.BaseOptimizer.ImageOutputter;
 import org.apache.shindig.gadgets.uri.UriCommon.Param;
@@ -45,7 +46,6 @@ import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -59,7 +59,7 @@ import javax.imageio.ImageWriter;
  * <p>Security Note: Uses the Sanselan library to parse image content and metadata to avoid security
  * issues in the ImageIO library. Uses ImageIO for output.
  */
-public class BasicImageRewriter implements ImageRewriter {
+public class BasicImageRewriter implements ResponseRewriter {
 
   static final String
       CONTENT_TYPE_AND_EXTENSION_MISMATCH =
@@ -99,21 +99,16 @@ public class BasicImageRewriter implemen
 
   private final OptimizerConfig config;
 
-  private final AtomicLong totalSourceImageSize = new AtomicLong();
-  private final AtomicLong totalRewrittenImageBytes = new AtomicLong();
-
   @Inject
   public BasicImageRewriter(OptimizerConfig config) {
     this.config = config;
   }
 
-  public HttpResponse rewrite(HttpRequest request, HttpResponse response) {
-    if (request == null || response == null)
-      return response;
+  public void rewrite(HttpRequest request, HttpResponseBuilder response) {
+    if (request == null || response == null) return;
     
     Uri uri = request.getUri();
-    if (null == uri)
-      return response;
+    if (null == uri) return;
     
     try {
       // Check resizing
@@ -124,28 +119,29 @@ public class BasicImageRewriter implemen
 
       // If the path or MIME type don't match, continue
       if (!isSupportedContent(response) && !isImage(uri)) {
-        return response;
+        return;
       }
       if (!isUsableParameter(requestedWidth) || !isUsableParameter(requestedHeight) ||
           !isUsableParameter(resizeQuality)) {
-        return response;
+        return;
       }
 
       // Content header checking is fast so this is fine to do for every response.
       ImageFormat imageFormat = Sanselan
-          .guessFormat(new ByteSourceInputStream(response.getResponse(), uri.getPath()));
+          .guessFormat(new ByteSourceInputStream(response.getContentBytes(), uri.getPath()));
 
       if (imageFormat == ImageFormat.IMAGE_FORMAT_UNKNOWN) {
-        return enforceUnreadableImageRestrictions(uri, response);
+        enforceUnreadableImageRestrictions(uri, response);
+        return;
       }
 
       // Don't handle very small images, but check after parsing format to
       // detect attacks.
       if (response.getContentLength() < config.getMinThresholdBytes()) {
-        return response;
+        return;
       }
 
-      ImageInfo imageInfo = Sanselan.getImageInfo(response.getResponse(), uri.getPath());
+      ImageInfo imageInfo = Sanselan.getImageInfo(response.getContentBytes(), uri.getPath());
       
       boolean noExpand = "1".equals(request.getParam(PARAM_NO_EXPAND));
       if (noExpand &&
@@ -157,20 +153,17 @@ public class BasicImageRewriter implemen
 
       boolean isOversizedImage = isImageTooLarge(imageInfo);
       if (isResizeRequested && isOversizedImage) {
-        HttpResponseBuilder rejectedResponseBuilder = new HttpResponseBuilder()
-            .setHttpStatusCode(HttpResponse.SC_FORBIDDEN)
-            .setResponseString(RESIZE_IMAGE_TOO_LARGE);
-        return rejectedResponseBuilder.create();
+        errorResponse(response, HttpResponse.SC_FORBIDDEN, RESIZE_IMAGE_TOO_LARGE);
+        return;
       }
 
       // Don't handle animations.
       // TODO: This doesn't work as current Sanselan doesn't return accurate image counts.
       // See animated GIF detection below.
       if (imageInfo.getNumberOfImages() > 1 || isOversizedImage) {
-        return response;
+        return;
       }
-      int originalContentSize = response.getContentLength();
-      totalSourceImageSize.addAndGet(originalContentSize);
+      
       BufferedImage image = readImage(imageFormat, response);
 
       if (isResizeRequested) {
@@ -220,11 +213,10 @@ public class BasicImageRewriter implemen
         if (isResizeRequired(requestedWidth, requestedHeight, imageInfo)
             && !isTargetImageTooLarge(requestedWidth, requestedHeight, imageInfo)) {
           image = resizeImage(image, requestedWidth, requestedHeight, widthDelta, heightDelta);
-          response = updateResponse(response, image);
+          updateResponse(response, image);
         }
       }
-      response = getOptimizer(response, imageFormat, image);
-      totalRewrittenImageBytes.addAndGet(response.getContentLength());
+      applyOptimizer(response, imageFormat, image);
     } catch (IOException ioe) {
       log.log(Level.WARNING, "IO Error rewriting image " + request.toString() + " - " + ioe.getMessage());
     } catch (RuntimeException re) {
@@ -234,7 +226,6 @@ public class BasicImageRewriter implemen
     } catch (ImageReadException ire) {
       log.log(Level.INFO, "Failed to read image. Skipping " + request.toString(), ire.getMessage());
     }
-    return response;
   }
 
   /**
@@ -246,16 +237,15 @@ public class BasicImageRewriter implemen
    * @param image the resized image that needs to be substituted for the original image from
    *        the response
    */
-  private HttpResponse updateResponse(HttpResponse response, BufferedImage image)
+  private void updateResponse(HttpResponseBuilder response, BufferedImage image)
       throws IOException {
     ImageWriter imageWriter = ImageIO.getImageWritersByFormatName(RESIZE_OUTPUT_FORMAT).next();
     ImageOutputter outputter = new ImageIOOutputter(imageWriter, null);
     byte[] imageBytes = outputter.toBytes(image);
-    HttpResponseBuilder newResponseBuilder = new HttpResponseBuilder(response)
+    response
         .setResponse(imageBytes)
         .setHeader(CONTENT_TYPE, CONTENT_TYPE_IMAGE_PNG)
         .setHeader(CONTENT_LENGTH, String.valueOf(imageBytes.length));
-    return newResponseBuilder.create();
   }
 
   private boolean isUsableParameter(Integer parameterValue) {
@@ -330,24 +320,22 @@ public class BasicImageRewriter implemen
     g2d.setComposite(AlphaComposite.SrcOver);
   }
 
-  private HttpResponse getOptimizer(HttpResponse response, ImageFormat imageFormat,
+  private void applyOptimizer(HttpResponseBuilder response, ImageFormat imageFormat,
       BufferedImage image) throws IOException {
-
     if (imageFormat == ImageFormat.IMAGE_FORMAT_GIF) {
       // Detecting the existence of the NETSCAPE2.0 extension by string comparison
       // is not exactly clean but is good enough to determine if a GIF is animated
       // Remove once Sanselan returns image count
-      if (!response.getResponseAsString().contains("NETSCAPE2.0")) {
-        response = new GIFOptimizer(config, response).rewrite(image);
+      if (!response.create().getResponseAsString().contains("NETSCAPE2.0")) {
+        new GIFOptimizer(config, response).rewrite(image);
       }
     } else if (imageFormat == ImageFormat.IMAGE_FORMAT_PNG) {
-      response = new PNGOptimizer(config, response).rewrite(image);
+      new PNGOptimizer(config, response).rewrite(image);
     } else if (imageFormat == ImageFormat.IMAGE_FORMAT_JPEG) {
-      response = new JPEGOptimizer(config, response).rewrite(image);
+      new JPEGOptimizer(config, response).rewrite(image);
     } else if (imageFormat == ImageFormat.IMAGE_FORMAT_BMP) {
-      response = new BMPOptimizer(config, response).rewrite(image);
+      new BMPOptimizer(config, response).rewrite(image);
     }
-    return response;
   }
 
   private boolean isImageTooLarge(ImageInfo imageInfo) {
@@ -368,7 +356,7 @@ public class BasicImageRewriter implemen
     return imageSizeBits > config.getMaxInMemoryBytes() * BITS_PER_BYTE;
   }
 
-  private boolean isSupportedContent(HttpResponse response) {
+  private boolean isSupportedContent(HttpResponseBuilder response) {
     return SUPPORTED_MIME_TYPES.contains(response.getHeader(CONTENT_TYPE));
   }
 
@@ -398,17 +386,16 @@ public class BasicImageRewriter implemen
    * MIME-type indicate that image content should be available but we failed to read it, then return
    * an error response.
    */
-  HttpResponse enforceUnreadableImageRestrictions(Uri uri, HttpResponse original) {
-    String contentType = original.getHeader(CONTENT_TYPE);
+  void enforceUnreadableImageRestrictions(Uri uri, HttpResponseBuilder response) {
+    String contentType = response.getHeader(CONTENT_TYPE);
     if (contentType != null) {
       contentType = contentType.toLowerCase();
       for (String expected : SUPPORTED_MIME_TYPES) {
         if (contentType.contains(expected)) {
           // MIME type says its a supported image but we can't read it. Reject.
-          return new HttpResponseBuilder(original)
-              .setHttpStatusCode(HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE)
-              .setResponseString(CONTENT_TYPE_AND_MIME_MISMATCH)
-              .create();
+          errorResponse(response, HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE,
+              CONTENT_TYPE_AND_MIME_MISMATCH);
+          return;
         }
       }
     }
@@ -417,26 +404,18 @@ public class BasicImageRewriter implemen
     for (String supportedExtension : SUPPORTED_FILE_EXTENSIONS) {
       if (path.endsWith(supportedExtension)) {
         // The file extension says its a supported image but we can't read it. Reject.
-        return new HttpResponseBuilder(original)
-            .setHttpStatusCode(HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE)
-            .setResponseString(CONTENT_TYPE_AND_EXTENSION_MISMATCH)
-            .create();
+        errorResponse(response, HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE,
+            CONTENT_TYPE_AND_EXTENSION_MISMATCH);
+        return;
       }
     }
-    return original;
   }
-
-  public long getOriginalImageBytes() {
-    // Thread-safe?
-    return totalSourceImageSize.get();
-  }
-
-  public long getRewrittenImageBytes() {
-    // Thread-safe?
-    return totalRewrittenImageBytes.get();
+  
+  private void errorResponse(HttpResponseBuilder response, int status, String msg) {
+    response.clearAllHeaders().setHttpStatusCode(status).setResponseString(msg);
   }
 
-  protected BufferedImage readImage(ImageFormat imageFormat, HttpResponse response)
+  protected BufferedImage readImage(ImageFormat imageFormat, HttpResponseBuilder response)
       throws ImageReadException, IOException{
     if (imageFormat == ImageFormat.IMAGE_FORMAT_GIF) {
       return readGif(response);
@@ -455,19 +434,19 @@ public class BasicImageRewriter implemen
   // implement additional security constraints or use their own more efficient
   // image reading mechanisms
 
-  protected BufferedImage readBmp(HttpResponse response) throws ImageReadException, IOException {
-    return BMPOptimizer.readBmp(response.getResponse());
+  protected BufferedImage readBmp(HttpResponseBuilder response) throws ImageReadException, IOException {
+    return BMPOptimizer.readBmp(response.getContentBytes());
   }
 
-  protected BufferedImage readPng(HttpResponse response) throws ImageReadException, IOException {
-    return PNGOptimizer.readPng(response.getResponse());
+  protected BufferedImage readPng(HttpResponseBuilder response) throws ImageReadException, IOException {
+    return PNGOptimizer.readPng(response.getContentBytes());
   }
 
-  protected BufferedImage readGif(HttpResponse response) throws ImageReadException, IOException {
-    return GIFOptimizer.readGif(response.getResponse());
+  protected BufferedImage readGif(HttpResponseBuilder response) throws ImageReadException, IOException {
+    return GIFOptimizer.readGif(response.getContentBytes());
   }
 
-  protected BufferedImage readJpeg(HttpResponse response) throws ImageReadException, IOException {
-    return JPEGOptimizer.readJpeg(response.getResponse());
+  protected BufferedImage readJpeg(HttpResponseBuilder response) throws ImageReadException, IOException {
+    return JPEGOptimizer.readJpeg(response.getContentBytes());
   }
 }

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizer.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizer.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizer.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizer.java Tue May 25 18:17:40 2010
@@ -19,7 +19,7 @@ package org.apache.shindig.gadgets.rewri
 
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.Sanselan;
-import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 
 import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
@@ -38,8 +38,8 @@ public class GIFOptimizer extends PNGOpt
 
   private boolean usePng;
 
-  public GIFOptimizer(OptimizerConfig config, HttpResponse original) {
-    super(config, original);
+  public GIFOptimizer(OptimizerConfig config, HttpResponseBuilder response) {
+    super(config, response);
   }
 
   @Override

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizer.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizer.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizer.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizer.java Tue May 25 18:17:40 2010
@@ -20,7 +20,7 @@ package org.apache.shindig.gadgets.rewri
 import org.apache.commons.io.IOUtils;
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.Sanselan;
-import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 
 import javax.imageio.ImageIO;
 import java.awt.color.ICC_Profile;
@@ -53,8 +53,8 @@ public class JPEGOptimizer extends BaseO
 
   private boolean usePng;
 
-  public JPEGOptimizer(OptimizerConfig config, HttpResponse original) {
-    super(config, original);
+  public JPEGOptimizer(OptimizerConfig config, HttpResponseBuilder response) {
+    super(config, response);
   }
 
   @Override
@@ -65,7 +65,7 @@ public class JPEGOptimizer extends BaseO
         config.getMinThresholdBytes());
 
     // Output the image as PNG
-    PNGOptimizer pngOptimizer = new PNGOptimizer(pngConfig, originalResponse);
+    PNGOptimizer pngOptimizer = new PNGOptimizer(pngConfig, response);
     pngOptimizer.rewriteImpl(image);
 
     int pngLength = Integer.MAX_VALUE;

Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizer.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizer.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizer.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizer.java Tue May 25 18:17:40 2010
@@ -19,7 +19,7 @@ package org.apache.shindig.gadgets.rewri
 
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.Sanselan;
-import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 
 import javax.imageio.ImageIO;
 import javax.imageio.ImageWriteParam;
@@ -41,8 +41,8 @@ class PNGOptimizer extends BaseOptimizer
 
   private boolean useJpeg;
 
-  public PNGOptimizer(OptimizerConfig config, HttpResponse original) {
-    super(config, original);
+  public PNGOptimizer(OptimizerConfig config, HttpResponseBuilder response) {
+    super(config, response);
   }
 
   @Override

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java Tue May 25 18:17:40 2010
@@ -225,7 +225,6 @@ public class AbstractHttpCacheTest {
   @Test
   public void getResponseNotCacheable() {
     HttpRequest request = new HttpRequest(DEFAULT_URI);
-    String key = cache.createKey(request);
     HttpResponse response = new HttpResponseBuilder().setStrictNoCache().create();
     cache.addResponse(request, response);
 

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpFetcherTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpFetcherTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpFetcherTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpFetcherTest.java Tue May 25 18:17:40 2010
@@ -57,7 +57,7 @@ public abstract class AbstractHttpFetche
     Uri uri = Uri.parse("http://a:b:c/");
     HttpRequest request = new HttpRequest(uri);
     try {
-      HttpResponse response = fetcher.fetch(request);
+      fetcher.fetch(request);
       fail("Expected GadgetException");
     } catch (GadgetException e) {
       assertEquals(400, e.getHttpStatusCode());
@@ -69,7 +69,7 @@ public abstract class AbstractHttpFetche
     Uri uri = Uri.parse("http://a:b/");
     HttpRequest request = new HttpRequest(uri);
     try {
-      HttpResponse response = fetcher.fetch(request);
+      fetcher.fetch(request);
       fail("Expected GadgetException");
     } catch (GadgetException e) {
       assertEquals(400, e.getHttpStatusCode());
@@ -81,7 +81,7 @@ public abstract class AbstractHttpFetche
     Uri uri = Uri.parse("host/data");
     HttpRequest request = new HttpRequest(uri);
     try {
-      HttpResponse response = fetcher.fetch(request);
+      fetcher.fetch(request);
       fail("Expected GadgetException");
     } catch (GadgetException e) {
       assertEquals(400, e.getHttpStatusCode());
@@ -93,7 +93,7 @@ public abstract class AbstractHttpFetche
     Uri uri = Uri.parse("//host/data");
     HttpRequest request = new HttpRequest(uri);
     try {
-      HttpResponse response = fetcher.fetch(request);
+      fetcher.fetch(request);
       fail("Expected GadgetException");
     } catch (GadgetException e) {
       assertEquals(400, e.getHttpStatusCode());

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultInvalidationServiceTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultInvalidationServiceTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultInvalidationServiceTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultInvalidationServiceTest.java Tue May 25 18:17:40 2010
@@ -24,7 +24,7 @@ import org.apache.shindig.common.testing
 import org.apache.shindig.common.uri.Uri;
 import org.apache.shindig.gadgets.AuthType;
 import org.apache.shindig.gadgets.oauth.OAuthArguments;
-import org.apache.shindig.gadgets.rewrite.image.NoOpImageRewriter;
+import org.apache.shindig.gadgets.rewrite.DefaultResponseRewriterRegistry;
 import org.easymock.EasyMock;
 import org.easymock.IMocksControl;
 import org.junit.Assert;
@@ -80,8 +80,9 @@ public class DefaultInvalidationServiceT
 
     fetcher = new DefaultRequestPipelineTest.FakeHttpFetcher();
     oauth = new DefaultRequestPipelineTest.FakeOAuthRequestProvider();
-    requestPipeline = new DefaultRequestPipeline(fetcher, cache, oauth, new NoOpImageRewriter(),
-        service, new HttpResponseMetadataHelper());
+    requestPipeline = new DefaultRequestPipeline(fetcher, cache, oauth,
+        new DefaultResponseRewriterRegistry(null, null), service,
+        new HttpResponseMetadataHelper());
   }
 
   @Test

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultRequestPipelineTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultRequestPipelineTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultRequestPipelineTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/DefaultRequestPipelineTest.java Tue May 25 18:17:40 2010
@@ -26,7 +26,7 @@ import org.apache.shindig.common.uri.Uri
 import org.apache.shindig.gadgets.AuthType;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.oauth.OAuthRequest;
-import org.apache.shindig.gadgets.rewrite.image.NoOpImageRewriter;
+import org.apache.shindig.gadgets.rewrite.DefaultResponseRewriterRegistry;
 import org.junit.Test;
 
 import java.util.Map;
@@ -45,7 +45,7 @@ public class DefaultRequestPipelineTest 
     }
   };
   private final RequestPipeline pipeline = new DefaultRequestPipeline(fetcher, cache, oauth,
-      new NoOpImageRewriter(), new NoOpInvalidationService(), helper);
+      new DefaultResponseRewriterRegistry(null, null), new NoOpInvalidationService(), helper);
 
   @Test
   public void authTypeNoneNotCached() throws Exception {
@@ -75,7 +75,7 @@ public class DefaultRequestPipelineTest 
     fetcher.response = new HttpResponse("response");
 
     RequestPipeline pipeline = new DefaultRequestPipeline(fetcher, cache, oauth,
-        new NoOpImageRewriter(), new NoOpInvalidationService(),
+        new DefaultResponseRewriterRegistry(null, null), new NoOpInvalidationService(),
         new HttpResponseMetadataHelper());
     HttpResponse response = pipeline.execute(request);
     assertEquals(1, response.getMetadata().size());

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseBuilderTest.java Tue May 25 18:17:40 2010
@@ -203,6 +203,33 @@ public class HttpResponseBuilderTest {
 
     // Insure that headers are stored in the order they are added
     assertEquals(Joiner.on(",").join(resp.getHeaders("Soup")), Joiner.on(",").join(soupList));
-
+  }
+  
+  @Test
+  public void noModsReturnsSameResponse() {
+    HttpResponseBuilder builder = new HttpResponseBuilder();
+    builder.setHttpStatusCode(HttpResponse.SC_BAD_GATEWAY);
+    builder.setResponseString("foo");
+    HttpResponse response = builder.create();
+    assertSame(response, builder.create());
+  }
+  
+  @Test
+  public void noModsReturnsSameResponseBuilderCtor() {
+    HttpResponseBuilder builder = new HttpResponseBuilder();
+    builder.setHttpStatusCode(HttpResponse.SC_OK);
+    HttpResponseBuilder nextBuilder = new HttpResponseBuilder(builder);
+    assertSame(builder.create(), nextBuilder.create());
+  }
+  
+  @Test
+  public void noModsReturnsSameResponseBaseCtor() {
+    HttpResponse response = new HttpResponse("foo");
+    HttpResponseBuilder builder = new HttpResponseBuilder(response);
+    assertSame(response, builder.create());
+    builder.setHttpStatusCode(HttpResponse.SC_BAD_GATEWAY);
+    HttpResponse newResponse = builder.create();
+    assertNotSame(response, newResponse);
+    assertSame(newResponse, builder.create());
   }
 }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizerTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/BMPOptimizerTest.java Tue May 25 18:17:40 2010
@@ -20,6 +20,7 @@ package org.apache.shindig.gadgets.rewri
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.Sanselan;
 import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -70,7 +71,9 @@ public class BMPOptimizerTest extends Ba
 
   protected HttpResponse rewrite(HttpResponse original)
        throws IOException, ImageReadException {
-     return new BMPOptimizer(new OptimizerConfig(), original).rewrite(
+    HttpResponseBuilder builder = new HttpResponseBuilder(original);
+    new BMPOptimizer(new OptimizerConfig(), builder).rewrite(
          Sanselan.getBufferedImage(original.getResponse()));
+    return builder.create();
    }
 }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizerTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/GIFOptimizerTest.java Tue May 25 18:17:40 2010
@@ -20,6 +20,7 @@ package org.apache.shindig.gadgets.rewri
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.Sanselan;
 import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -67,7 +68,9 @@ public class GIFOptimizerTest extends Ba
 
   protected HttpResponse rewrite(HttpResponse original)
       throws IOException, ImageReadException {
-    return new GIFOptimizer(new OptimizerConfig(), original).rewrite(
+    HttpResponseBuilder builder = new HttpResponseBuilder(original);
+    new GIFOptimizer(new OptimizerConfig(), builder).rewrite(
          Sanselan.getBufferedImage(original.getResponse()));
+    return builder.create();
   }
 }

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/ImageRewriterTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/ImageRewriterTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/ImageRewriterTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/ImageRewriterTest.java Tue May 25 18:17:40 2010
@@ -30,6 +30,7 @@ import org.apache.shindig.gadgets.uri.Ur
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.HttpResponseBuilder;
+import org.apache.shindig.gadgets.rewrite.ResponseRewriter;
 import org.easymock.IMocksControl;
 
 import org.junit.Assert;
@@ -70,8 +71,7 @@ public class ImageRewriterTest extends A
 
   private static final Uri IMAGE_URL = Uri.parse("http://www.example.com/image.gif");
 
-  private ImageRewriter rewriter;
-
+  private ResponseRewriter rewriter;
   private IMocksControl mockControl;
 
   @Before
@@ -81,12 +81,11 @@ public class ImageRewriterTest extends A
   }
 
   /** Makes a new {@link HttpResponse} with an image content. */
-  private HttpResponse getImageResponse(String contentType, byte[] imageBytes) {
+  private HttpResponseBuilder getImageResponse(String contentType, byte[] imageBytes) {
     return new HttpResponseBuilder()
         .setHeader(CONTENT_TYPE_HEADER, contentType)
         .setHttpStatusCode(HttpResponse.SC_OK)
-        .setResponse(imageBytes)
-        .create();
+        .setResponse(imageBytes);
   }
 
   /** Extracts an image by its resource name and converts it into a byte array. */
@@ -108,15 +107,15 @@ public class ImageRewriterTest extends A
       String targetContentType, String imageName, Integer width, Integer height, Integer quality,
       boolean noExpand)
       throws Exception {
-    HttpResponse originalResponse = getImageResponse(sourceContentType, getImageBytes(imageName));
+    HttpResponseBuilder response = getImageResponse(sourceContentType, getImageBytes(imageName));
     HttpRequest request = getMockRequest(width, height, quality, noExpand);
 
     mockControl.replay();
-    HttpResponse rewrittenResponse = rewriter.rewrite(request, originalResponse);
+    rewriter.rewrite(request, response);
     mockControl.verify();
 
-    assertEquals(targetContentType, rewrittenResponse.getHeader(CONTENT_TYPE_HEADER));
-    return ImageIO.read(rewrittenResponse.getResponse());
+    assertEquals(targetContentType, response.getHeader(CONTENT_TYPE_HEADER));
+    return ImageIO.read(response.getContentBytes());
   }
 
   private HttpRequest getMockRequest(Integer width, Integer height, Integer quality, boolean noExpand) {
@@ -132,56 +131,59 @@ public class ImageRewriterTest extends A
   @Test
   public void testRewriteValidImageWithValidMimeAndExtn() throws Exception {
     byte[] bytes = getImageBytes("org/apache/shindig/gadgets/rewrite/image/inefficient.png");
-    HttpResponse original = getImageResponse(CONTENT_TYPE_PNG, bytes);
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_PNG, bytes);
+    int originalContentLength = response.getContentLength();
 
-    HttpResponse rewritten = rewriter.rewrite(new HttpRequest(Uri.parse("some.png")), original);
-    assertEquals(HttpResponse.SC_OK, rewritten.getHttpStatusCode());
-    assertTrue(rewritten.getContentLength() < original.getContentLength());
+    rewriter.rewrite(new HttpRequest(Uri.parse("some.png")), response);
+    assertEquals(HttpResponse.SC_OK, response.getHttpStatusCode());
+    assertTrue(response.getContentLength() < originalContentLength);
   }
 
   @Test
   public void testRewriteValidImageWithInvalidMimeAndFileExtn() throws Exception {
     byte[] bytes = getImageBytes("org/apache/shindig/gadgets/rewrite/image/inefficient.png");
-    HttpResponse original = getImageResponse(CONTENT_TYPE_BOGUS, bytes);
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_BOGUS, bytes);
+    int originalContentLength = response.getContentLength();
 
-    HttpResponse rewritten = rewriter.rewrite(new HttpRequest(Uri.parse("some.junk")), original);
-    assertEquals(HttpResponse.SC_OK, rewritten.getHttpStatusCode());
-    assertEquals(rewritten.getContentLength(), original.getContentLength());
+    rewriter.rewrite(new HttpRequest(Uri.parse("some.junk")), response);
+    assertEquals(HttpResponse.SC_OK, response.getHttpStatusCode());
+    assertEquals(response.getContentLength(), originalContentLength);
   }
 
   @Test
   public void testRewriteInvalidImageContentWithValidMime() throws Exception {
-    HttpResponse original = getImageResponse(CONTENT_TYPE_PNG, "This is not a PNG".getBytes());
-    HttpResponse rewritten = rewriter.rewrite(new HttpRequest(Uri.parse("some.junk")), original);
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_PNG, "This is not a PNG".getBytes());
+    rewriter.rewrite(new HttpRequest(Uri.parse("some.junk")), response);
 
-    assertEquals(HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE, rewritten.getHttpStatusCode());
-    assertEquals(CONTENT_TYPE_AND_MIME_MISMATCH, rewritten.getResponseAsString());
+    assertEquals(HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE, response.getHttpStatusCode());
+    assertEquals(CONTENT_TYPE_AND_MIME_MISMATCH, response.create().getResponseAsString());
   }
 
   @Test
   public void testRewriteInvalidImageContentWithValidFileExtn() throws Exception {
-    HttpResponse original = getImageResponse(CONTENT_TYPE_BOGUS, "This is not an image".getBytes());
-    HttpResponse rewritten = rewriter.rewrite(new HttpRequest(Uri.parse("some.png")), original);
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_BOGUS, "This is not an image".getBytes());
+    rewriter.rewrite(new HttpRequest(Uri.parse("some.png")), response);
 
-    assertEquals(HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE, rewritten.getHttpStatusCode());
+    assertEquals(HttpResponse.SC_UNSUPPORTED_MEDIA_TYPE, response.getHttpStatusCode());
     assertEquals(CONTENT_TYPE_AND_EXTENSION_MISMATCH,
-        rewritten.getResponseAsString());
+        response.create().getResponseAsString());
   }
 
   @Test
   public void testNoRewriteAnimatedGIF() throws Exception {
-    HttpResponse original = getImageResponse(CONTENT_TYPE_GIF,
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_GIF,
         getImageBytes("org/apache/shindig/gadgets/rewrite/image/animated.gif"));
-    assertSame(rewriter.rewrite(new HttpRequest(Uri.parse("animated.gif")), original), original);
+    int changesBefore = response.getNumChanges();
+    rewriter.rewrite(new HttpRequest(Uri.parse("animated.gif")), response);
+    assertEquals(changesBefore, response.getNumChanges());
   }
 
   @Test
   public void testRewriteUnAnimatedGIF() throws Exception {
-    HttpResponse original = getImageResponse(CONTENT_TYPE_GIF,
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_GIF,
         getImageBytes("org/apache/shindig/gadgets/rewrite/image/large.gif"));
-    assertEquals(CONTENT_TYPE_PNG,
-        rewriter.rewrite(new HttpRequest(Uri.parse("large.gif")), original)
-            .getHeader(CONTENT_TYPE_HEADER));
+    rewriter.rewrite(new HttpRequest(Uri.parse("large.gif")), response);
+    assertEquals(CONTENT_TYPE_PNG, response.getHeader(CONTENT_TYPE_HEADER));
   }
 
   // Resizing image tests
@@ -273,24 +275,24 @@ public class ImageRewriterTest extends A
 
   @Test
   public void testResize_refuseHugeInputImages() throws Exception {
-    HttpResponse originalResponse = getImageResponse(CONTENT_TYPE_GIF, getImageBytes(HUGE_IMAGE));
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_GIF, getImageBytes(HUGE_IMAGE));
     HttpRequest request = getMockRequest(120, 60, null, false);
     mockControl.replay();
-    HttpResponse rewrittenResponse = rewriter.rewrite(request, originalResponse);
+    rewriter.rewrite(request, response);
     mockControl.verify();
-    assertEquals(HttpResponse.SC_FORBIDDEN, rewrittenResponse.getHttpStatusCode());
+    assertEquals(HttpResponse.SC_FORBIDDEN, response.getHttpStatusCode());
   }
 
   @Test
   public void testResize_acceptServeHugeImages() throws Exception {
     byte[] imageBytes = getImageBytes(HUGE_IMAGE);
-    HttpResponse originalResponse = getImageResponse(CONTENT_TYPE_GIF, imageBytes);
+    HttpResponseBuilder response = getImageResponse(CONTENT_TYPE_GIF, imageBytes);
     HttpRequest request = getMockRequest(null, null, null, false);
     mockControl.replay();
-    HttpResponse rewrittenResponse = rewriter.rewrite(request, originalResponse);
+    rewriter.rewrite(request, response);
     mockControl.verify();
-    assertEquals(HttpResponse.SC_OK, rewrittenResponse.getHttpStatusCode());
-    assertTrue(Arrays.equals(imageBytes, IOUtils.toByteArray(rewrittenResponse.getResponse())));
+    assertEquals(HttpResponse.SC_OK, response.getHttpStatusCode());
+    assertTrue(Arrays.equals(imageBytes, IOUtils.toByteArray(response.getContentBytes())));
   }
   
   @Test

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizerTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/JPEGOptimizerTest.java Tue May 25 18:17:40 2010
@@ -19,6 +19,7 @@ package org.apache.shindig.gadgets.rewri
 
 import org.apache.sanselan.ImageReadException;
 import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -100,7 +101,9 @@ public class JPEGOptimizerTest extends B
 
   HttpResponse rewrite(HttpResponse original)
       throws IOException, ImageReadException {
-    return new JPEGOptimizer(new OptimizerConfig(), original).rewrite(
+    HttpResponseBuilder builder = new HttpResponseBuilder(original);
+    new JPEGOptimizer(new OptimizerConfig(), builder).rewrite(
         JPEGOptimizer.readJpeg(original.getResponse()));
+    return builder.create();
   }
 }
\ No newline at end of file

Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizerTest.java?rev=948147&r1=948146&r2=948147&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizerTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/image/PNGOptimizerTest.java Tue May 25 18:17:40 2010
@@ -20,6 +20,7 @@ package org.apache.shindig.gadgets.rewri
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.Sanselan;
 import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -59,7 +60,9 @@ public class PNGOptimizerTest extends Ba
 
   HttpResponse rewrite(HttpResponse original)
       throws IOException, ImageReadException {
-    return new PNGOptimizer(new OptimizerConfig(), original).rewrite(
+    HttpResponseBuilder builder = new HttpResponseBuilder(original);
+    new PNGOptimizer(new OptimizerConfig(), builder).rewrite(
          Sanselan.getBufferedImage(original.getResponse()));
+    return builder.create();
   }
 }