You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by aw...@apache.org on 2009/04/23 21:15:41 UTC

svn commit: r768018 - in /incubator/shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/oauth/ main/java/org/apache/shindig/gadgets/servlet/ test/java/org/apache/shindig/gadgets/servlet/

Author: awiner
Date: Thu Apr 23 19:15:41 2009
New Revision: 768018

URL: http://svn.apache.org/viewvc?rev=768018&view=rev
Log:
Get HttpRequestHandler fairly close to os:HttpRequest and osapi.http request and response format:
- url -> href
- auth block to top-level params
- add format="text/json/feed", for now get rid of "contentType"
- Change feed from a string blob to a JSON blob
- refresh -> refreshInterval
- in response, body -> content

Still need to change error responses to put content inside data block.

Added OAuthArguments.equals() to make the tests easier to write

Modified:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HttpRequestHandlerTest.java

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java?rev=768018&r1=768017&r2=768018&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/oauth/OAuthArguments.java Thu Apr 23 19:15:41 2009
@@ -18,6 +18,7 @@
  */
 package org.apache.shindig.gadgets.oauth;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.shindig.gadgets.AuthType;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
@@ -344,4 +345,39 @@
   public boolean programmaticConfig() {
     return Boolean.parseBoolean(requestOptions.get(PROGRAMMATIC_CONFIG_PARAM));
   }
+
+  @Override
+  public int hashCode() {
+    int result = (bypassSpecCache ? 1231 : 1237);
+    result = 31 * result + ((origClientState == null) ? 0 : origClientState.hashCode());
+    result = 31 * result + (proxiedContentRequest ? 1231 : 1237);
+    result = 31 * result + ((requestToken == null) ? 0 : requestToken.hashCode());
+    result = 31 * result + ((requestTokenSecret == null) ? 0 : requestTokenSecret.hashCode());
+    result = 31 * result + ((serviceName == null) ? 0 : serviceName.hashCode());
+    result = 31 * result + (signOwner ? 1231 : 1237);
+    result = 31 * result + (signViewer ? 1231 : 1237);
+    result = 31 * result + ((tokenName == null) ? 0 : tokenName.hashCode());
+    result = 31 * result + ((useToken == null) ? 0 : useToken.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    
+    if (!(obj instanceof OAuthArguments)) {
+      return false;
+    }
+
+    OAuthArguments other = (OAuthArguments) obj;
+    return (bypassSpecCache == other.bypassSpecCache
+        && StringUtils.equals(origClientState, other.origClientState)
+        && proxiedContentRequest == other.proxiedContentRequest
+        && StringUtils.equals(requestToken, other.requestToken)
+        && StringUtils.equals(requestTokenSecret, other.requestTokenSecret)
+        && StringUtils.equals(tokenName, other.tokenName)
+        && signViewer == other.signViewer
+        && useToken == other.useToken);
+  }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java?rev=768018&r1=768017&r2=768018&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpRequestHandler.java Thu Apr 23 19:15:41 2009
@@ -19,6 +19,7 @@
 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;
@@ -34,6 +35,8 @@
 import org.apache.shindig.protocol.Operation;
 import org.apache.shindig.protocol.ProtocolException;
 import org.apache.shindig.protocol.Service;
+import org.json.JSONException;
+import org.json.JSONObject;
 
 import java.util.Collection;
 import java.util.Map;
@@ -41,7 +44,6 @@
 
 import javax.servlet.http.HttpServletResponse;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.inject.Inject;
@@ -52,18 +54,16 @@
  * ...
  * method : http.<HTTP method name>
  * params : {
- *    url : <endpoint to fetch content from>,
+ *    href : <endpoint to fetch content from>,
  *    headers : { <header-name> : <header-value>, ...},
- *    contentType : <coerce content to specified mime type>
+ *    format : <"text", "json", "feed">
  *    body : <request body>
  *    gadget : <url of gadget spec for calling application>
- *    auth : {
- *          type : <None | OAuth | Signed>,
- *          signOwner : <boolean, default true>
- *          signViewer : <boolean, default true>
- *          ...<additional auth arguments. See OAuthArguments>
- *    },
- *    refresh : <Integer time in seconds to force as cache TTL. Default is to use response headers>
+ *    authz: : <none | oauth | signed>,
+ *    sign_owner: <boolean, default true>
+ *    sign_viewer: <boolean, default true>
+ *    ...<additional auth arguments. See OAuthArguments>
+ *    refreshInterval : <Integer time in seconds to force as cache TTL. Default is to use response headers>
  *    noCache : <Bypass container content cache. Default false>
  *    sanitize : <Force sanitize fetched content. Default false>
  *    summarize : <If contentType == "FEED" summarize the results. Default false>
@@ -75,14 +75,16 @@
  * data : {
  *    status : <HTTP status code.>
  *    headers : { <header name> : [<header val1>, <header val2>, ...], ...}
- *    body : <response body>
+ *    content : <response body>: string if 'text', JSON is 'feed' or 'json' format
  *    token : <If security token provides a renewed value.>
  *    metadata : { <metadata entry> : <metadata value>, ...}
  * }
  *
- * Its important to note that requests which generate HTTP error responses such as 500 are returned
+ * 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 "data", not plain content
  *
  * @see MakeRequestHandler
  */
@@ -107,21 +109,21 @@
   public HttpApiResponse get(BaseRequestItem request) {
     HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class);
     assertNoBody(httpReq, "GET");
-    return execute("GET", httpReq, request.getToken());
+    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.getToken());
+    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.getToken());
+    return execute("PUT", httpReq, request);
   }
 
   /** Execute an HTTP DELETE request */
@@ -129,7 +131,7 @@
   public HttpApiResponse delete(BaseRequestItem request) {
     HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class);
     assertNoBody(httpReq, "DELETE");
-    return execute("DELETE", httpReq, request.getToken());
+    return execute("DELETE", httpReq, request);
   }
 
   /** Execute an HTTP HEAD request */
@@ -137,7 +139,7 @@
   public HttpApiResponse head(BaseRequestItem request) {
     HttpApiRequest httpReq = request.getTypedRequest(HttpApiRequest.class);
     assertNoBody(httpReq, "HEAD");
-    return execute("HEAD", httpReq, request.getToken());
+    return execute("HEAD", httpReq, request);
   }
 
   private void assertNoBody(HttpApiRequest httpRequest, String method) {
@@ -151,20 +153,18 @@
    * Dispatch the request
    *
    * @param method HTTP method to execute
+   * @param requestItem TODO
    */
   private HttpApiResponse execute(String method, HttpApiRequest httpApiRequest,
-      SecurityToken token) {
-    if (httpApiRequest.url == null) {
-      throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Url parameter is missing");
+      BaseRequestItem requestItem) {
+    if (httpApiRequest.href == null) {
+      throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "href parameter is missing");
     }
 
     // Canonicalize the path
-    if (httpApiRequest.url.getPath() == null || httpApiRequest.url.getPath().length() == 0) {
-      httpApiRequest.url = new UriBuilder(httpApiRequest.url).setPath("/").toUri();
-    }
-
+    Uri href = normalizeUrl(httpApiRequest.href);
     try {
-      HttpRequest req = new HttpRequest(httpApiRequest.url);
+      HttpRequest req = new HttpRequest(href);
       req.setMethod(method);
       if (httpApiRequest.body != null) {
         req.setPostBody(httpApiRequest.body.getBytes());
@@ -178,49 +178,55 @@
       }
 
       // Extract the gadget URI from the request or the security token
-      Uri gadgetUri = getGadgetUri(token, httpApiRequest);
+      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 auth parsing
-      if (httpApiRequest.auth != null && httpApiRequest.auth.get("type") != null) {
-        req.setAuthType(AuthType.parse(httpApiRequest.auth.get("type")));
+      // Detect the authz parsing
+      if (httpApiRequest.authz != null) {
+        req.setAuthType(AuthType.parse(httpApiRequest.authz));
       }
 
       if (req.getAuthType() != AuthType.NONE) {
-        req.setSecurityToken(token);
-        req.setOAuthArguments(new OAuthArguments(req.getAuthType(), httpApiRequest.auth));
+        req.setSecurityToken(requestItem.getToken());
+        
+        Map<String, String> authSettings = getAuthSettings(requestItem);
+        OAuthArguments oauthArgs = new OAuthArguments(req.getAuthType(), authSettings);
+        oauthArgs.setSignOwner(httpApiRequest.signOwner);
+        oauthArgs.setSignViewer(httpApiRequest.signViewer);
+        
+        req.setOAuthArguments(oauthArgs);
       }
 
-      // Allow the rewriter to use an externally forced mime type. This is needed
+      // TODO: Allow the rewriter to use an externally forced mime type. This is needed
       // allows proper rewriting of <script src="x"/> where x is returned with
       // a content type like text/html which unfortunately happens all too often
-      req.setRewriteMimeType(httpApiRequest.contentType);
+      
       req.setIgnoreCache(httpApiRequest.noCache);
       req.setSanitizationRequested(httpApiRequest.sanitize);
 
       // If the proxy request specifies a refresh param then we want to force the min TTL for
       // the retrieved entry in the cache regardless of the headers on the content when it
       // is fetched from the original source.
-      if (httpApiRequest.refresh != null) {
-        req.setCacheTtl(httpApiRequest.refresh);
+      if (httpApiRequest.refreshInterval != null) {
+        req.setCacheTtl(httpApiRequest.refreshInterval);
       }
 
       HttpResponse results = requestPipeline.execute(req);
-      if (contentRewriterRegistry != null) {
-        results = contentRewriterRegistry.rewriteHttpResponse(req, results);
-      }
+      // TODO: os:HttpRequest and Preload do not use the content rewriter.
+      // Should we really do so here?
+      results = contentRewriterRegistry.rewriteHttpResponse(req, results);
 
       HttpApiResponse httpApiResponse = new HttpApiResponse(results,
-          tranformBody(httpApiRequest, results),
+          transformBody(httpApiRequest, results),
           httpApiRequest);
 
       // Renew the security token if we can
-      if (token != null) {
-        String updatedAuthToken = token.getUpdatedToken();
+      if (requestItem.getToken() != null) {
+        String updatedAuthToken = requestItem.getToken().getUpdatedToken();
         if (updatedAuthToken != null) {
           httpApiResponse.token = updatedAuthToken;
         }
@@ -235,34 +241,62 @@
     }
   }
 
+  /**
+   * Extract all unknown keys into a map for extra auth params.
+   */
+  private Map<String, String> getAuthSettings(BaseRequestItem requestItem) {
+    // Keys in a request item are always Strings
+    @SuppressWarnings("unchecked")
+    Set<String> allParameters = requestItem.getTypedRequest(Map.class).keySet();
+    
+    Map<String, String> authSettings = Maps.newHashMap();
+    for (String paramName : allParameters) {
+      if (!HttpApiRequest.KNOWN_PARAMETERS.contains(paramName)) {
+        authSettings.put(paramName, requestItem.getParameter(paramName));
+      }
+    }
+    
+    return authSettings;
+  }
+
+
   protected Uri normalizeUrl(Uri url) {
     if (url.getScheme() == null) {
       // Assume http
       url = new UriBuilder(url).setScheme("http").toUri();
-    } else if (!"http".equals(url.getScheme()) && !"https"
-        .equals(url.getScheme())) {
-      throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST,
-          "Only HTTP and HTTPS are supported");
     }
+
+    if (url.getPath() == null || url.getPath().length() == 0) {
+      url = new UriBuilder(url).setPath("/").toUri();
+    }
+
     return url;
   }
 
 
   /** Format a response as JSON, including additional JSON inserted by chained content fetchers. */
-  protected String tranformBody(HttpApiRequest request, HttpResponse results)
+  protected Object transformBody(HttpApiRequest request, HttpResponse results)
       throws GadgetException {
     String body = results.getResponseAsString();
-    if ("FEED".equals(request.contentType)) {
-      body = processFeed(request, body);
+    if ("feed".equalsIgnoreCase(request.format)) {
+      return processFeed(request, body);
+    } else if ("json".equalsIgnoreCase(request.format)) {
+      try {
+        return new JSONObject(body);
+      } catch (JSONException e) {
+        // TODO: include data block with invalid JSON
+        throw new ProtocolException(HttpServletResponse.SC_NOT_ACCEPTABLE, "Response not valid JSON", e);
+      }
     }
+    
     return body;
   }
 
   /** Processes a feed (RSS or Atom) using FeedProcessor. */
-  protected String processFeed(HttpApiRequest req, String responseBody)
+  protected Object processFeed(HttpApiRequest req, String responseBody)
       throws GadgetException {
-    return new FeedProcessor().process(req.url.toString(), responseBody, req.summarize,
-        req.entryCount).toString();
+    return new FeedProcessor().process(req.href.toString(), responseBody, req.summarize,
+        req.entryCount);
   }
 
   /** Extract the gadget URL from the request or the security token */
@@ -279,25 +313,39 @@
    * Simple type that represents an Http request to execute on the callers behalf
    */
   public static class HttpApiRequest {
+    static final Set<String> KNOWN_PARAMETERS = ImmutableSet.of(
+        "href", "headers", "body", "gadget", "authz", "sign_owner",
+        "sign_viewer", "format", "refreshInterval", "noCache", "sanitize",
+        "summarize", "entryCount");
 
     // Content to fetch / execute
-    Uri url;
+    Uri href;
 
     // TODO : Consider support Map<String, List<String>> to match response
     Map<String, String> headers = Maps.newHashMap();
 
+    /** POST body */
     String body;
 
     // Allowed to be null if it can be derived from the security token
+    // TODO: why is this useful?
     Uri gadget;
 
-    Map<String, String> auth = Maps.newHashMap();
-
-    // The content type to coerce the response into. "FEED" has special meaning
-    String contentType;
+    /** Authorization type ("none", "signed", "oauth") */
+    String authz = "none";
+    
+    /** Should the request be signed by owner? */
+    boolean signOwner = true;
+    
+    /** Should the request be signed by viewer? */
+    boolean signViewer = true;
+    
+    // The format type to coerce the response into. Supported values are
+    // "text", "json", and "feed".
+    String format;
 
     // Use Integer here to allow for null
-    Integer refresh;
+    Integer refreshInterval;
 
     // Bypass http caches
     boolean noCache;
@@ -309,12 +357,12 @@
     boolean summarize;
     int entryCount = 3;
 
-    public Uri getUrl() {
-      return url;
+    public Uri getHref() {
+      return href;
     }
 
-    public void setUrl(Uri url) {
-      this.url = url;
+    public void setHref(Uri url) {
+      this.href = url;
     }
 
     public Uri getGadget() {
@@ -341,12 +389,12 @@
       this.body = body;
     }
 
-    public Integer getRefresh() {
-      return refresh;
+    public Integer getRefreshInterval() {
+      return refreshInterval;
     }
 
-    public void setRefresh(Integer refresh) {
-      this.refresh = refresh;
+    public void setRefreshInterval(Integer refreshInterval) {
+      this.refreshInterval = refreshInterval;
     }
 
     public boolean isNoCache() {
@@ -365,22 +413,40 @@
       this.sanitize = sanitize;
     }
 
-    public String getContentType() {
-      return contentType;
+    public String getFormat() {
+      return format;
     }
 
-    public void setContentType(String contentType) {
-      this.contentType = contentType;
+    public void setFormat(String format) {
+      this.format = format;
     }
 
-    public Map<String, String> getAuth() {
-      return auth;
+    public String getAuthz() {
+      return authz;
     }
-
-    public void setAuth(Map<String, String> auth) {
-      this.auth = auth;
+    
+    public void setAuthz(String authz) {
+      this.authz = authz;
     }
 
+    public boolean isSignViewer() {
+      return signViewer;
+    }
+    
+    @JsonProperty("sign_viewer")
+    public void setSignViewer(boolean signViewer) {
+      this.signViewer = signViewer;
+    }
+    
+    public boolean isSignOwner() {
+      return signOwner;
+    }
+    
+    @JsonProperty("sign_owner")
+    public void setSignOwner(boolean signOwner) {
+      this.signOwner = signOwner;
+    }
+    
     public boolean isSummarize() {
       return summarize;
     }
@@ -404,12 +470,12 @@
   public static class HttpApiResponse {
     // Http status code
     int status;
-
+    
     // Returned headers
     Map<String, Collection<String>> headers;
 
-    // Body content
-    String body;
+    // Body content, either a String or a JSON-type structure
+    Object content;
 
     // Renewed security token if available
     String token;
@@ -424,7 +490,7 @@
     /**
      * Construct response based on HttpResponse from fetcher
      */
-    public HttpApiResponse(HttpResponse response, String body, HttpApiRequest httpApiRequest) {
+    public HttpApiResponse(HttpResponse response, Object content, HttpApiRequest httpApiRequest) {
       this.status = response.getHttpStatusCode();
       this.headers = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
 
@@ -434,11 +500,8 @@
       if (response.getHeaders().containsKey("location")) {
         this.headers.put("location", response.getHeaders("location"));
       }
-      // Override the content-type if specified
-      if (httpApiRequest.contentType != null) {
-        this.headers.put("Content-Type", ImmutableList.of(httpApiRequest.contentType));
-      }
-      this.body = body;
+      
+      this.content = content;
 
       this.metadata = response.getMetadata();
     }
@@ -459,12 +522,12 @@
       this.headers = headers;
     }
 
-    public String getBody() {
-      return body;
+    public Object getContent() {
+      return content;
     }
 
-    public void setBody(String body) {
-      this.body = body;
+    public void setContent(Object content) {
+      this.content = content;
     }
 
     public String getToken() {
@@ -477,8 +540,8 @@
 
     public Map<String, String> getMetadata() {
       // TODO - Review this once migration of JS occurs. Currently MakeRequestHandler suppresses
-      // this on output but that choice may not be the best one for compatability.
-      // Suppress metadata on output if its empty
+      // this on output but that choice may not be the best one for compatibility.
+      // Suppress metadata on output if it's empty
       if (metadata != null && metadata.isEmpty()) {
         return null;
       }

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HttpRequestHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HttpRequestHandlerTest.java?rev=768018&r1=768017&r2=768018&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HttpRequestHandlerTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/HttpRequestHandlerTest.java Thu Apr 23 19:15:41 2009
@@ -18,6 +18,7 @@
  */
 package org.apache.shindig.gadgets.servlet;
 
+import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.reportMatcher;
 
@@ -30,10 +31,11 @@
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.apache.shindig.gadgets.http.RequestPipeline;
+import org.apache.shindig.gadgets.oauth.OAuthArguments;
 import org.apache.shindig.gadgets.rewrite.CaptureRewriter;
-import org.apache.shindig.gadgets.rewrite.RequestRewriterRegistry;
 import org.apache.shindig.gadgets.rewrite.DefaultRequestRewriterRegistry;
 import org.apache.shindig.gadgets.rewrite.RequestRewriter;
+import org.apache.shindig.gadgets.rewrite.RequestRewriterRegistry;
 import org.apache.shindig.protocol.DefaultHandlerRegistry;
 import org.apache.shindig.protocol.HandlerExecutionListener;
 import org.apache.shindig.protocol.HandlerRegistry;
@@ -41,6 +43,7 @@
 import org.apache.shindig.protocol.RpcHandler;
 import org.apache.shindig.protocol.conversion.BeanJsonConverter;
 import org.apache.shindig.protocol.multipart.FormDataItem;
+import org.easymock.Capture;
 import org.easymock.IArgumentMatcher;
 import org.json.JSONArray;
 import org.json.JSONObject;
@@ -51,6 +54,8 @@
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -96,7 +101,7 @@
   @Test
   public void testSimpleGet() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent'"
+        + "href:'http://www.example.org/somecontent'"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("GET");
@@ -110,13 +115,13 @@
     verify();
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
-        "{ headers : {}, status : 200, body : 'CONTENT' }}");
+        "{ headers : {}, status : 200, content : 'CONTENT' }}");
   }
 
   @Test
   public void testFailGetWithBodyGet() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "body:'POSTBODY'"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
@@ -133,7 +138,7 @@
   @Test
   public void testSimplePost() throws Exception {
     JSONObject request = new JSONObject("{method:http.post, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "body:'POSTBODY'"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
@@ -149,13 +154,13 @@
     verify();
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
-        "{ headers : {}, status : 200, body : 'CONTENT' }}");
+        "{ headers : {}, status : 200, content : 'CONTENT' }}");
   }
 
   @Test
   public void testPostWithHeaders() throws Exception {
     JSONObject request = new JSONObject("{method:http.post, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "body:'POSTBODY',"
         + "headers:{goodheader:good, host : iamstripped, 'Content-Length':'1000'}"
         + "}}");
@@ -174,14 +179,14 @@
     verify();
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
-        "{ headers : {}, status : 200, body : 'CONTENT' }}");
+        "{ headers : {}, status : 200, content : 'CONTENT' }}");
   }
 
   @Test
   public void testFetchContentTypeFeed() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
-        + "contentType : FEED"
+        + "href:'http://www.example.org/somecontent',"
+        + "format : FEED"
         + "}}");
 
     String entryTitle = "Feed title";
@@ -212,7 +217,7 @@
         (HttpRequestHandler.HttpApiResponse)operation.execute(emptyFormItems, token, converter).get();
     verify();
 
-    JSONObject feed = new JSONObject(httpApiResponse.getBody());
+    JSONObject feed = (JSONObject) httpApiResponse.getContent();;
     JSONObject entry = feed.getJSONArray("Entry").getJSONObject(0);
 
     assertEquals(entryTitle, entry.getString("Title"));
@@ -225,8 +230,8 @@
   @Test
   public void testFetchFeedWithParameters() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
-        + "contentType : FEED,"
+        + "href:'http://www.example.org/somecontent',"
+        + "format : FEED,"
         + "summarize : true,"
         + "entryCount : 2"
         + "}}");
@@ -269,7 +274,7 @@
         (HttpRequestHandler.HttpApiResponse)operation.execute(emptyFormItems, token, converter).get();
     verify();
 
-    JSONObject feed = new JSONObject(httpApiResponse.getBody());
+    JSONObject feed = (JSONObject) httpApiResponse.getContent();
     JSONArray feeds = feed.getJSONArray("Entry");
 
     assertEquals("numEntries not parsed correctly.", 2, feeds.length());
@@ -283,14 +288,37 @@
   }
 
   @Test
+  public void testJsonGet() throws Exception {
+    JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
+        + "href:'http://www.example.org/somecontent', format:'json'"
+        + "}}");
+    HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
+    httpRequest.setMethod("GET");
+    builder.setResponseString("{key:1}");
+    expect(pipeline.execute(eqRequest(httpRequest))).andReturn(builder.create()).anyTimes();
+
+    replay();
+    RpcHandler operation = registry.getRpcHandler(request);
+
+    HttpRequestHandler.HttpApiResponse httpApiResponse =
+        (HttpRequestHandler.HttpApiResponse)operation.execute(emptyFormItems, token, converter).get();
+    verify();
+
+    JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
+        "{ headers : {}, status : 200, content : {key: 1}}}");
+  }
+
+  @Test
   public void testSignedGetRequest() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
-        + "auth : { type: signed }"
+        + "href:'http://www.example.org/somecontent',"
+        + "authz : 'signed' }"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("GET");
     httpRequest.setAuthType(AuthType.SIGNED);
+    httpRequest.setOAuthArguments(
+        new OAuthArguments(AuthType.SIGNED, ImmutableMap.<String, String>of()));
     expect(pipeline.execute(eqRequest(httpRequest))).andReturn(builder.create()).anyTimes();
     replay();
     RpcHandler operation = registry.getRpcHandler(request);
@@ -300,7 +328,7 @@
     verify();
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
-        "{ headers : {}, status : 200, body : 'CONTENT' }}");
+        "{ headers : {}, status : 200, content : 'CONTENT' }}");
 
     assertTrue(rewriter.responseWasRewritten());
   }
@@ -309,13 +337,15 @@
   public void testSignedPostAndUpdateSecurityToken() throws Exception {
     token.setUpdatedToken("updated");
     JSONObject request = new JSONObject("{method:http.post, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "body:'POSTBODY',"
-        + "auth : { type: signed }"
+        + "authz: 'signed' }"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("POST");
     httpRequest.setAuthType(AuthType.SIGNED);
+    httpRequest.setOAuthArguments(
+        new OAuthArguments(AuthType.SIGNED, ImmutableMap.<String, String>of()));
     httpRequest.setPostBody("POSTBODY".getBytes());
     expect(pipeline.execute(eqRequest(httpRequest))).andReturn(builder.create()).anyTimes();
     replay();
@@ -326,7 +356,7 @@
     verify();
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
-        "{ headers : {}, status : 200, body : 'CONTENT', token : updated }}");
+        "{ headers : {}, status : 200, content : 'CONTENT', token : updated }}");
 
     assertTrue(rewriter.responseWasRewritten());
   }
@@ -334,13 +364,15 @@
   @Test
   public void testOAuthRequest() throws Exception {
     JSONObject request = new JSONObject("{method:http.post, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "body:'POSTBODY',"
-        + "auth : { type: oauth }"
+        + "authz: 'oauth' }"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("POST");
     httpRequest.setAuthType(AuthType.OAUTH);
+    httpRequest.setOAuthArguments(
+        new OAuthArguments(AuthType.OAUTH, ImmutableMap.<String, String>of()));
     httpRequest.setPostBody("POSTBODY".getBytes());
     expect(pipeline.execute(eqRequest(httpRequest))).andReturn(builder.create()).anyTimes();
     replay();
@@ -351,11 +383,43 @@
   }
 
   @Test
+  public void testOAuthRequestWithParameters() throws Exception {
+    JSONObject request = new JSONObject("{method:http.post, id:req1, params : {"
+        + "href:'http://www.example.org/somecontent',"
+        + "body:'POSTBODY',"
+        + "sign_owner:'false',"
+        + "sign_viewer:'true',"
+        + "oauth_service_name:'oauthService',"
+        + "authz: 'oauth' }"
+        + "}}");
+    HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
+    httpRequest.setMethod("POST");
+    httpRequest.setAuthType(AuthType.OAUTH);
+    OAuthArguments oauthArgs =
+        new OAuthArguments(AuthType.OAUTH, ImmutableMap.<String, String>of());
+    oauthArgs.setSignOwner(false);
+    oauthArgs.setServiceName("oauthService");
+    httpRequest.setOAuthArguments(oauthArgs);
+    httpRequest.setPostBody("POSTBODY".getBytes());
+    
+    Capture<HttpRequest> requestCapture = new Capture<HttpRequest>();
+    expect(pipeline.execute(capture(requestCapture))).andReturn(builder.create());
+    replay();
+    RpcHandler operation = registry.getRpcHandler(request);
+
+    operation.execute(emptyFormItems, token, converter).get();
+    verify();
+    
+    assertEquals(httpRequest.getOAuthArguments(),
+        requestCapture.getValue().getOAuthArguments());
+  }
+
+  @Test
   public void testInvalidSigningTypeTreatedAsNone() throws Exception {
     JSONObject request = new JSONObject("{method:http.post, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "body:'POSTBODY',"
-        + "auth : { type: rubbish }"
+        + "authz : 'rubbish' }"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("POST");
@@ -372,8 +436,8 @@
   @Test
   public void testSignedGetRequestNoSecurityToken() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
-        + "auth : { type: signed }"
+        + "href:'http://www.example.org/somecontent',"
+        + "authz : 'signed'}"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("GET");
@@ -394,7 +458,7 @@
   @Test
   public void testBadHttpResponseIsPropagated() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent'"
+        + "href:'http://www.example.org/somecontent'"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("GET");
@@ -410,13 +474,13 @@
     verify();
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
-        "{ headers : {}, status : 500, body : 'I AM AN ERROR MESSAGE' }}");
+        "{ headers : {}, status : 500, content : 'I AM AN ERROR MESSAGE' }}");
   }
 
   @Test
   public void testMetadataCopied() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent'"
+        + "href:'http://www.example.org/somecontent'"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("GET");
@@ -431,13 +495,13 @@
     verify();
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
-        "{ headers : {}, status : 200, body : 'CONTENT', metadata : { foo : 'CONTENT' }}");
+        "{ headers : {}, status : 200, content : 'CONTENT', metadata : { foo : 'CONTENT' }}");
   }
 
   @Test
   public void testSetCookiesReturned() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("GET");
@@ -454,13 +518,13 @@
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
         "{ headers : { 'set-cookie' : ['foo=bar; Secure','name=value'] },"
-            + " status : 200, body : 'CONTENT' }");    
+            + " status : 200, content : 'CONTENT' }");    
   }
 
   @Test
   public void testLocationReturned() throws Exception {
     JSONObject request = new JSONObject("{method:http.get, id:req1, params : {"
-        + "url:'http://www.example.org/somecontent',"
+        + "href:'http://www.example.org/somecontent',"
         + "}}");
     HttpRequest httpRequest = new HttpRequest(Uri.parse("http://www.example.org/somecontent"));
     httpRequest.setMethod("GET");
@@ -476,7 +540,7 @@
 
     JsonAssert.assertJsonEquals(converter.convertToString(httpApiResponse),
         "{ headers : { 'location' : ['here'] },"
-            + " status : 200, body : 'CONTENT' }");
+            + " status : 200, content : 'CONTENT' }");
   }
 
   private static HttpRequest eqRequest(HttpRequest request) {
@@ -502,6 +566,7 @@
           match.getUri().equals(req.getUri()) &&
           match.getAuthType().equals(req.getAuthType()) &&
           match.getPostBodyAsString().equals(req.getPostBodyAsString()) &&
+          Objects.equal(match.getOAuthArguments(), req.getOAuthArguments()) &&
           match.getHeaders().equals(req.getHeaders()));
     }
   }