/**
  * Used to fake a browser visit to approve a token.
  *
  * @return a redirect URL, which may or may not include an oauth verifier
  */
 public String browserVisit(String url) throws Exception {
   ParsedUrl parsed = new ParsedUrl(url);
   String requestToken = parsed.getQueryParam("oauth_token");
   TokenState state = tokenState.get(requestToken);
   state.approveToken();
   // Not part of the OAuth spec, just a handy thing for testing.
   state.setUserData(parsed.getQueryParam("user_data"));
   if (state.callbackUrl != null) {
     UriBuilder callback = UriBuilder.parse(state.callbackUrl);
     callback.addQueryParameter(OAuthConstants.OAUTH_VERIFIER, state.verifier);
     return callback.toString();
   }
   return null;
 }
  // Loosely based off net.oauth.OAuthServlet, and even more loosely related
  // to the OAuth specification
  private MessageInfo parseMessage(HttpRequest request) {
    MessageInfo info = new MessageInfo();
    info.request = request;
    String method = request.getMethod();
    ParsedUrl parsed = new ParsedUrl(request.getUri().toString());

    List<OAuth.Parameter> params = Lists.newArrayList();
    params.addAll(parsed.getParsedQuery());

    if (!validParamLocations.contains(OAuthParamLocation.URI_QUERY)) {
      // Make sure nothing OAuth related ended up in the query string
      for (OAuth.Parameter p : params) {
        if (p.getKey().contains("oauth_")) {
          throw new RuntimeException("Found unexpected query param " + p.getKey());
        }
      }
    }

    // Parse authorization header
    if (validParamLocations.contains(OAuthParamLocation.AUTH_HEADER)) {
      String aznHeader = request.getHeader("Authorization");
      if (aznHeader != null) {
        info.aznHeader = aznHeader;
        for (OAuth.Parameter p : OAuthMessage.decodeAuthorization(aznHeader)) {
          if (!p.getKey().equalsIgnoreCase("realm")) {
            params.add(p);
          }
        }
      }
    }

    // Parse body
    info.body = request.getPostBodyAsString();
    try {
      info.rawBody = IOUtils.toByteArray(request.getPostBody());
    } catch (IOException e) {
      throw new RuntimeException("Can't read post body bytes", e);
    }
    if (OAuth.isFormEncoded(request.getHeader("Content-Type"))) {
      params.addAll(OAuth.decodeForm(request.getPostBodyAsString()));
      // If we're not configured to pass oauth parameters in the post body, double check
      // that they didn't end up there.
      if (!validParamLocations.contains(OAuthParamLocation.POST_BODY)) {
        if (info.body.contains("oauth_")) {
          throw new RuntimeException("Found unexpected post body data" + info.body);
        }
      }
    }

    // Return the lot
    info.message = new OAuthMessage(method, parsed.getLocation(), params);

    // Check for trusted parameters
    if (checkTrustedParams) {
      if (!"foo".equals(OAuthUtil.getParameter(info.message, "oauth_magic"))) {
        throw new RuntimeException("no oauth_trusted=foo parameter");
      }
      if (!"bar".equals(OAuthUtil.getParameter(info.message, "opensocial_magic"))) {
        throw new RuntimeException("no opensocial_trusted=foo parameter");
      }
      if (!"quux".equals(OAuthUtil.getParameter(info.message, "xoauth_magic"))) {
        throw new RuntimeException("no xoauth_magic=quux parameter");
      }
      trustedParamCount += 3;
    }

    return info;
  }