All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.shindig.gadgets.servlet.HttpRequestHandler Maven / Gradle / Ivy

Go to download

Renders gadgets, provides the gadget metadata service, and serves all javascript required by the OpenSocial specification.

The newest version!
/*
 * 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 org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.JsonProperty;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.uri.UriBuilder;
import org.apache.shindig.gadgets.AuthType;
import org.apache.shindig.gadgets.FeedProcessor;
import org.apache.shindig.gadgets.GadgetException;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.RequestPipeline;
import org.apache.shindig.gadgets.oauth.OAuthArguments;
import org.apache.shindig.gadgets.oauth2.OAuth2Arguments;
import org.apache.shindig.gadgets.rewrite.ResponseRewriterList.RewriteFlow;
import org.apache.shindig.gadgets.rewrite.ResponseRewriterRegistry;
import org.apache.shindig.gadgets.rewrite.RewriterRegistry;
import org.apache.shindig.gadgets.rewrite.RewritingException;
import org.apache.shindig.protocol.BaseRequestItem;
import org.apache.shindig.protocol.Operation;
import org.apache.shindig.protocol.ProtocolException;
import org.apache.shindig.protocol.Service;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.servlet.http.HttpServletResponse;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Provider;

/**
 * An alternate implementation of the Http proxy service using the standard API dispatcher for REST
 * / JSON-RPC calls. The basic form of the request is as follows
 * ...
 * method : http.
 * params : {
 *    href : ,
 *    headers : {  : [, ...]},
 *    format : <"text", "json", "feed">
 *    body : 
 *    gadget : 
 *    authz: : ,
 *    sign_owner: 
 *    sign_viewer: 
 *    ...
 *    refreshInterval : 
 *    noCache : 
 *    sanitize : 
 *    summarize : 
 *    entryCount : 
 * }
 *
 * A successful response response will have the form
 *
 * data : {
 *    status : 
 *    headers : { 
: [
,
, ...], ...} * content : : string if 'text', JSON is 'feed' or 'json' format * token : * metadata : { : , ...} * } * * It's important to note that requests which generate HTTP error responses such as 500 are returned * in the above format. The RPC itself succeeded in these cases. If an RPC error occurred the client * should introspect the error message for information as to the cause. * * TODO: send errors using "result", not plain content * * @see MakeRequestHandler */ @Service(name = "http") public class HttpRequestHandler { static final Set BAD_HEADERS = ImmutableSet.of("HOST", "ACCEPT-ENCODING"); private final RequestPipeline requestPipeline; private final ResponseRewriterRegistry contentRewriterRegistry; private final Provider feedProcessorProvider; @Inject public HttpRequestHandler(RequestPipeline requestPipeline, @RewriterRegistry(rewriteFlow = RewriteFlow.DEFAULT) ResponseRewriterRegistry contentRewriterRegistry, Provider feedProcessorProvider) { this.requestPipeline = requestPipeline; this.contentRewriterRegistry = contentRewriterRegistry; this.feedProcessorProvider = feedProcessorProvider; } /** Execute an HTTP GET request */ @Operation(httpMethods = {"POST","GET"}, path = "/get") public HttpApiResponse get(BaseRequestItem request) { HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class); assertNoBody(httpReq, "GET"); return execute("GET", httpReq, request); } /** Execute an HTTP POST request */ @Operation(httpMethods = "POST", path = "/post") public HttpApiResponse post(BaseRequestItem request) { HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class); return execute("POST", httpReq, request); } /** Execute an HTTP PUT request */ @Operation(httpMethods = "POST", path = "/put") public HttpApiResponse put(BaseRequestItem request) { HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class); return execute("PUT", httpReq, request); } /** Execute an HTTP DELETE request */ @Operation(httpMethods = "POST", path = "/delete") public HttpApiResponse delete(BaseRequestItem request) { HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class); assertNoBody(httpReq, "DELETE"); return execute("DELETE", httpReq, request); } /** Execute an HTTP HEAD request */ @Operation(httpMethods = {"POST","GET"}, path = "/head") public HttpApiResponse head(BaseRequestItem request) { HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class); assertNoBody(httpReq, "HEAD"); return execute("HEAD", httpReq, request); } private void assertNoBody(HttpApiRequest httpRequest, String method) { if (httpRequest.body != null) { throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Request body not supported for " + method); } } /** * Dispatch the request * * @param method HTTP method to execute * @param requestItem TODO */ private HttpApiResponse execute(String method, HttpApiRequest httpApiRequest, BaseRequestItem requestItem) { if (httpApiRequest.href == null) { throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "href parameter is missing"); } // Canonicalize the path Uri href = normalizeUrl(httpApiRequest.href); try { HttpRequest req = new HttpRequest(href); req.setMethod(method); if (httpApiRequest.body != null) { req.setPostBody(httpApiRequest.body.getBytes()); } // Copy over allowed headers for (Map.Entry> header : httpApiRequest.headers.entrySet()) { if (!BAD_HEADERS.contains(header.getKey().trim().toUpperCase())) { for (String value : header.getValue()) { req.addHeader(header.getKey(), value); } } } // Extract the gadget URI from the request or the security token Uri gadgetUri = getGadgetUri(requestItem.getToken(), httpApiRequest); if (gadgetUri == null) { throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Gadget URI not specified in request"); } req.setGadget(gadgetUri); // Detect the authz parsing if (httpApiRequest.authz != null) { req.setAuthType(AuthType.parse(httpApiRequest.authz)); } final AuthType authType = req.getAuthType(); if (authType != AuthType.NONE) { if (authType == AuthType.OAUTH2) { req.setSecurityToken(requestItem.getToken()); Map authSettings = getAuthSettings(requestItem); OAuth2Arguments oauth2Args = new OAuth2Arguments(req.getAuthType(), authSettings); req.setOAuth2Arguments(oauth2Args); } else { req.setSecurityToken(requestItem.getToken()); Map authSettings = getAuthSettings(requestItem); OAuthArguments oauthArgs = new OAuthArguments(req.getAuthType(), authSettings); oauthArgs.setSignOwner(httpApiRequest.signOwner); oauthArgs.setSignViewer(httpApiRequest.signViewer); req.setOAuthArguments(oauthArgs); } } // TODO: Allow the rewriter to use an externally forced mime type. This is needed // allows proper rewriting of