Package org.apache.shindig.gadgets.servlet

Source Code of org.apache.shindig.gadgets.servlet.HtmlAccelServlet

/*
* 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.servlet;

import com.google.inject.Inject;
import com.google.inject.name.Named;

import org.apache.commons.io.IOUtils;
import org.apache.shindig.gadgets.DefaultGadgetSpecFactory;
import org.apache.shindig.gadgets.GadgetContext;
import org.apache.shindig.gadgets.GadgetException;
import org.apache.shindig.gadgets.GadgetException.Code;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.RequestPipeline;

import java.io.IOException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

/**
* Html pages acceleration servlet.
* It uses the rewriter pipeline to improve speed of loading html.
* Internally it create a fake gadget spec and uses the Gadget rendering servlet.
* Only html content is being rewritten, other content is passed as is.
* (It uses the rawxml tag to pass spec, so require DefaultGadgetSpecFactory)
*/
public class HtmlAccelServlet extends GadgetRenderingServlet {

  public static final String URL_PARAM_NAME = "url";
  /**
   * Use ACCEL_GADGET parameter to check for AccelServlet during rendering.
   */
  public static final String ACCEL_GADGET_PARAM_NAME = "accelGadget";
  /**
   * Use the next value with '==' operation when checking for ACCEL_GADGET,
   * That will prevent spoofing it using url params.
   */
  public static final String ACCEL_GADGET_PARAM_VALUE = "true";
  public static final String CONTENT_TYPE = "Content-Type";
  public static final String HTML_CONTENT = "text/html";
  public static final String CONTENT_ENCODING = "Content-Encoding";
  public static final String CONTENT_LENGTH = "Content-Length";

  /** Fake spec to wrap the html data */
  private static final String FAKE_SPEC_TPL =
      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
      + "<Module>\n"
      + "  <ModulePrefs title=\"Apache Shindig Accelerator\"\n"
      + "               author=\"Apache Shindig\"\n"
      + "               author_email=\"no-reply@gmail.com\">\n"
      + "    <Require feature=\"core.none\"/>\n"
      + "    <Optional feature=\"content-rewrite\">\n"
      + "      <Param name=\"include-urls\">.*</Param>\n"
      + "    </Optional>\n"
      + "  </ModulePrefs>\n"
      + "  <Content type=\"html\">\n"
      + "    <![CDATA[%s]]>\n"
      + "  </Content>\n"
      + "</Module>\n";

  private RequestPipeline requestPipeline;
  private Map<String, String> addedServletParams = null;
 
  @Inject
  public void setRequestPipeline(RequestPipeline pipeline) {
    this.requestPipeline = pipeline;
  }
 
  @Inject(optional = true)
  public void setAddedServletParams(
      @Named("shindig.accelerate.added-params") Map<String, String> params) {
    this.addedServletParams = params;
  }
 
  public static boolean isAccel(GadgetContext context) {
    return context.getParameter(HtmlAccelServlet.ACCEL_GADGET_PARAM_NAME) ==
      HtmlAccelServlet.ACCEL_GADGET_PARAM_VALUE;
  }

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws IOException {

    // Get data to accelerate:
    HttpResponse data;
    HttpGadgetContext context = new HttpGadgetContext(req);
    try {
      data = fetch(context);
    } catch (GadgetException e) {
      resp.sendError(e.getHttpStatusCode(), e.getMessage());
      return;
    }
   
    // No such page:
    if (data == null) {
      resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error fetching data");
      return;
    }

    // If not html, just return data
    if (!isHtmlContent(data)) {
      respondVerbatim(data, resp);
      return;
    }
   
    // For html, use the gadgetServlet for rewrite
    String content = data.getResponseAsString();

    // Create fake spec wrapper for the html:
    // (Note that the content can be big so don't use the limited String.Format)
    final String spec = createFakeSpec(content);
   
    // wrap the request with the added params
    HttpServletRequestWrapper reqWrapper = new HttpServletRequestWrapper(req) {
      @Override
      public String getParameter(String name) {
        // Mark this as an accelerate page spec
        // (The code that check for that field have to use the defined constant,
        //  hence the use of == )
        if (name == ACCEL_GADGET_PARAM_NAME) {
          return ACCEL_GADGET_PARAM_VALUE;
        }
        // Pass the spec using rawxml (require DefaultGadgetSpecFactory):
        if (name == DefaultGadgetSpecFactory.RAW_GADGETSPEC_XML_PARAM_NAME) {
          return spec;
        }
        // Allow overriding extra params (i.e. container)
        if (addedServletParams != null && addedServletParams.containsKey(name)) {
          return addedServletParams.get(name);
        }
        return super.getParameter(name);
      }
    };
   
    // Call the gadget renderer to rewrite content:
    super.doGet(reqWrapper,resp);
   
    // Pass original return code:
    if (data.getHttpStatusCode() == HttpResponse.SC_INTERNAL_SERVER_ERROR) {
      // Convert external "internal error" to gateway error
      resp.setStatus(HttpServletResponse.SC_BAD_GATEWAY);
    } else if (data.getHttpStatusCode() >= 400) {
      resp.setStatus(data.getHttpStatusCode());
    }
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    doGet(req,resp);
  }
 
  protected boolean isHtmlContent(HttpResponse data) {
    return data.getHeader(CONTENT_TYPE) != null &&
           data.getHeader(CONTENT_TYPE).contains(HTML_CONTENT);
  }

  private HttpResponse fetch(HttpGadgetContext context) throws GadgetException {

    if (context.getUrl() == null) {
      throw new GadgetException(Code.INVALID_PARAMETER, "Missing url paramater",
          HttpResponse.SC_BAD_REQUEST);
    }

    HttpRequest request = new HttpRequest(context.getUrl())
        .setIgnoreCache(context.getIgnoreCache())
        .setContainer(context.getContainer());

    HttpResponse results = requestPipeline.execute(request);
    return results;
  }
 
  private void respondVerbatim(HttpResponse results, HttpServletResponse response)
      throws IOException {
   
    for (Map.Entry<String, String> entry : results.getHeaders().entries()) {
      // Encoding such as gzip was already stripped from data
      if (!CONTENT_ENCODING.equals(entry.getKey()) &&
          !CONTENT_LENGTH.equals(entry.getKey())) {
        response.setHeader(entry.getKey(), entry.getValue());
      }
    }
    response.setStatus(results.getHttpStatusCode());
    if (results.getHttpStatusCode() == HttpResponse.SC_INTERNAL_SERVER_ERROR) {
      // Convert external "internal error" to gateway error
      response.setStatus(HttpServletResponse.SC_BAD_GATEWAY);
    }
    IOUtils.copy(results.getResponse(), response.getOutputStream());
  }

  private String createFakeSpec(String content) {
    // need to rescape CDATA: ( each "]]>" and "<![CDATA[" should be "]]><![CDATA[")
    // TODO: just xml escape this and remove the CDATA section
    String data = content.replace("]]>", "<![CDATA[")
      .replace("<![CDATA[", "]]><![CDATA[");
    return String.format(FAKE_SPEC_TPL, data);  
  }
}
TOP

Related Classes of org.apache.shindig.gadgets.servlet.HtmlAccelServlet

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