[ 
https://issues.apache.org/jira/browse/AMBARI-4283?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13873805#comment-13873805
 ] 

Vitaly Brodetskyi commented on AMBARI-4283:
-------------------------------------------

[~mahadev].  Here are some thoughts about implementation of this functionality:
The main goal of this functionality(as i understand) to forward http requests 
of all types, that we got from API call. And then, return back result, that we 
got after url execution.

We can implement this functionality by API. But i think it's not correct. 
Because, to my mind, we can't map this functionality to our API architecture. 
In our API architecture, one of the main places, takes resources. But in our 
proxy, there are no resources, as i understand. At all, we need only service 
and provider, which will forward requests and responces. 

But if we need to implement it in API we can do the next. I'm not sure that 
names were choosen correctly(reason described before). 
We should create new resource type and resourcedefinition:
{code}
Resource.Type.Proxy
{code}

{code}
public class ProxyResourceDefinition extends BaseResourceDefinition {

  public ProxyResourceDefinition () {
    super(Resource.Type.Proxy);
  }
  
  @Override
  public String getPluralName() {
    return "urls";
  }

  @Override
  public String getSingularName() {
    return "url";
  }

  @Override
  public Set<SubResourceDefinition> getSubResourceDefinitions() {
    return Collections.emptySet();
  }

}
{code}

After resource type and resourcedefinition were created, we need to add them to 
ResourceInstanceFactoryImpl.getResourceDefinition(...)
It will look like:
{code}
case Proxy:
   resourceDefinition = new ProxyResourceDefinition();
   break;
{code}

Then we can create new service class, something like ProxyService. It will look 
like:
{code}
@Path("/proxy/")
public class ProxyService extends BaseService {

  @GET
  @Path("{url}")
  @Produces("text/plain")
  public Response proceedGetUrl(@Context HttpHeaders headers, @Context UriInfo 
ui,
      @PathParam("url") String url) {
    return handleRequest(headers, null, ui, Request.Type.GET, 
createProxyResource(url));
  }
  
  
  @POST
  @Path("{url}")
  @Produces("text/plain")
  public Response proceedPostURL(String body, @Context HttpHeaders headers, 
@Context UriInfo ui,
     @PathParam("url") String url) {
    return handleRequest(headers, body, ui, Request.Type.POST, 
createProxyResource(url));
  }
  
   
  @PUT
  @Path("{url}")
  @Produces("text/plain")
  public Response proceedPutURL(String body, @Context HttpHeaders headers, 
@Context UriInfo ui,
     @PathParam("url") String url) {
    return handleRequest(headers, body, ui, Request.Type.PUT, 
createProxyResource(url));
  }
   
   
  @DELETE
  @Path("{url}")
  @Produces("text/plain")
  public Response proceedDeleteURL(@Context HttpHeaders headers, @Context 
UriInfo ui,
     @PathParam("url") String url) {
    return handleRequest(headers, null, ui, Request.Type.DELETE, 
createProxyResource(url));
  }

  private ResourceInstance createProxyResource(String url) {
    return createResource(Resource.Type.Proxy,
        Collections.singletonMap(Resource.Type.Proxy, url));
  }
}
{code}

And now we can create provider class for our new resource. For example 
ProxyResourceProvider. Then we should add this provider to 
AbstractControllerResourceProvider.getResourceProvider(...)
{code}
case Proxy:
        return new ProxyResourceProvider(propertyIds, keyPropertyIds, 
managementController);
{code}

In ProxyPropertyProvider we should implement main functionality. Our main task 
is to execute URL request, which was sent for us by API, and return response 
for user. In our project we have URLStreamProvider class, which can help us to 
execute URL request. This class is used by Ganglia and Nagios to get some 
properties/info. Here are some code of the main method:
{code}
@Override
  public InputStream readFrom(String spec, String requestMethod, String params) 
throws IOException {
    if (LOG.isDebugEnabled()) {
      LOG.debug("readFrom spec:" + spec);
    }
    
    HttpURLConnection connection = spec.startsWith("https") ? 
        (HttpURLConnection)getSSLConnection(spec)
        : (HttpURLConnection)getConnection(spec);

    String appCookie = appCookieManager.getCachedAppCookie(spec);
    if (appCookie != null) {
      LOG.debug("Using cached app cookie for URL:" + spec);
      connection.setRequestProperty(COOKIE, appCookie);
    }
    connection.setConnectTimeout(connTimeout);
    connection.setReadTimeout(readTimeout);
    connection.setDoOutput(true);
    connection.setRequestMethod(requestMethod);
    
    if (params != null)
      connection.getOutputStream().write(params.getBytes());
    
    int statusCode = connection.getResponseCode();
    if (statusCode == HttpStatus.SC_UNAUTHORIZED ) {
      String wwwAuthHeader = connection.getHeaderField(WWW_AUTHENTICATE);
      if (LOG.isInfoEnabled()) {
        LOG.info("Received WWW-Authentication header:" + wwwAuthHeader + ", for 
URL:" + spec);
      }
      if (wwwAuthHeader != null && 
          wwwAuthHeader.trim().startsWith(NEGOTIATE)) {
        //connection.getInputStream().close();
        connection = spec.startsWith("https") ? 
            (HttpURLConnection)getSSLConnection(spec)
            : (HttpURLConnection)getConnection(spec);
        appCookie = appCookieManager.getAppCookie(spec, true);
        connection.setRequestProperty(COOKIE, appCookie);
        connection.setConnectTimeout(connTimeout);
        connection.setReadTimeout(readTimeout);
        connection.setDoOutput(true);
        
        return connection.getInputStream();
      } else {
        // no supported authentication type found
        // we would let the original response propogate
        LOG.error("Unsupported WWW-Authentication header:" + wwwAuthHeader+ ", 
for URL:" + spec);
        return connection.getInputStream();
      }
    } else {
      // not a 401 Unauthorized status code
      // we would let the original response propogate
      return connection.getInputStream();
    }
  }
{code} 

We can send any URL and any HTTP request type for this method, and it will 
return InputStream for us. I've tested it with simple GET 
request(http://dev01.hortonworks.com:50070/listPaths/user) and got correct 
answer:
{code}
<?xml version="1.0" encoding="UTF-8"?>
<listing time="2014-01-16T14:02:52+0000" recursive="no" path="/user" exclude="" 
filter=".*" version="2.2.0.2.0.6.0-101">
 <directory path="/user" modified="2014-01-16T10:41:16+0000" 
accesstime="1970-01-01T00:00:00+0000" permission="drwxr-xr-x" owner="hdfs" 
group="hdfs"/>
 <directory path="/user/ambari-qa" modified="2014-01-16T10:44:12+0000" 
accesstime="1970-01-01T00:00:00+0000" permission="drwxrwx---" owner="ambari-qa" 
group="hdfs"/>
</listing>
{code}

Then, UI can easy parse this response and show this info for user, in 
comfortable way.
**************************************************************************************************************************************************

Another way to implement this feature, it's Servlets. I think it will not take 
a lot of time to implement proxy on it . It can be configured for any url call 
(using filters). Servlet works fast enough. Servlet will work more faster then 
API(much less class/method calls and objects). In servlet, we can use 
URLStreamProvider class code to send http requests and get responses. And then, 
we will return response for user. About 'dispatcher forwarding', i think will 
not work correctly, because as i remember we can forward only in scope of 
current host.

What do you think about it?

> Pass Through API for API forwarding from the Ambari Server (Falcon/Jobs API).
> -----------------------------------------------------------------------------
>
>                 Key: AMBARI-4283
>                 URL: https://issues.apache.org/jira/browse/AMBARI-4283
>             Project: Ambari
>          Issue Type: Task
>          Components: agent
>    Affects Versions: 1.5.0
>            Reporter: Vitaly Brodetskyi
>            Assignee: Vitaly Brodetskyi
>             Fix For: 1.5.0
>
>
> Pass Through API for API forwarding from the Ambari Server (Falcon/Jobs API).



--
This message was sent by Atlassian JIRA
(v6.1.5#6160)

Reply via email to