jetty/jetty.project

Jetty12 migration help

johmn123-wq opened this issue · 5 comments

Jetty version(s)
migration from Jetty 11.0.x to Jetty 12.0.x

Jetty Environment
Applicable for jetty-12 ee10

Java version/vendor (use: java -version)
Java17

OS type/version

Description
Here is the code for 11.0.x

public class ApacheServerHandler extends AbstractHandler{

   private ApacheServerHandler(){
       client = HttpClients.createDeafult();
   }

@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{

  String newHost = request.getHeader('sb_host');
 if(newHost.endsWith("/")){
     newHost  = StringUtils.substring(newHost, 0, newHost.length() - 1);  
  }

HttpUriRequest = null;
String method = request.getMethod();
String queryString = getQueryString(baseRequest);
String path = baseRequest.getPathInfo();
Enumeration<String>headers = request.getHeaderNames();

String newUrl = String.format("http://%s%s", newHost, path, queryString);

if("GET".equalsIgnoreCase(method){
  uriRequest = new HttpGet(newUrl);
}
else if("POST".equalsIgnoreCase(method){
  uriRequest = new HttpPost(newUrl);
}
else if("PUT".equalsIgnoreCase(method){
  uriRequest = new HttpPut(newUrl);
}
else{
  baseRequest.setHandled(false);
}

if(uriRequest instanceof HttpEntityEncloseingRequest){
  HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) uriRequest;
  entityRequest.setEntity(new InputStreamEntity(request.getInputStream()));
}

while(headers.hasMoreElements()){
    String headerName = headers.nextElement();
    if(headerName.equalIgnoreCase('Content-length'))
       continue;
    uriRequest.addHeader(headerName, baseRequest.getHeader(headerName));

    try(ClosableHttpResponse resp = client.execute(uriRequest)){
         for(Header header: resp.getAllHeaders){
                response.addHeader(header.getName(), header.getValue());
         } 
         String body = EntityUtils.toString(resp.getEntity());
         reponse.getOutputStream().write(body.getBytes())
   }

  baseRequest.setHandled(true);

}

private String getQueryString(Request request){
   
      String queryString = baseRequest.getQueryString();
      if(queryString == null){
              return "";
       }
      return queryString;
 }

}

Here is the migrated code to jetty12 where getOutStream, setHandled, response.add() etc functions are not supported.

public class ApacheServerHandler extends Handler.Abstract{

   private ApacheServerHandler(){
       client = HttpClients.createDeafult();
   }

@Override
public void handle(Request request, Response response, Callback callback) throws IOException, ServletException{

  String newHost = request.getHeaders().get('sb_host');
  if(newHost.endsWith("/")){
     newHost  = StringUtils.substring(newHost, 0, newHost.length() - 1);  
   }
 
 HttpUriRequest = null;
 String method = request.getMethod();
 String queryString = request.getHttpURI().getQuery();
 String path = request.getHttpURI().getPath();
 Enumeration<String>headers = request.getHeaders().getFieldNames();

 String newUrl = String.format("http://%s%s", newHost, path, queryString);

 if("GET".equalsIgnoreCase(method){
   uriRequest = new HttpGet(newUrl);
 }
 else if("POST".equalsIgnoreCase(method){
   uriRequest = new HttpPost(newUrl);
 }
 else if("PUT".equalsIgnoreCase(method){
   uriRequest = new HttpPut(newUrl);
 }
 else{
   baseRequest.**setHandled**(false);
 } 

 if(uriRequest instanceof HttpEntityEncloseingRequest){
  HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) uriRequest;
  entityRequest.setEntity(new InputStreamEntity(Request.asInputStream(requrest), ContentType.create(request.getHeaders().getHtHeader.**CONTENT_TYPE**.asString()))));
 }

while(headers.hasMoreElements()){
    String headerName = headers.nextElement();
    if(headerName.equalIgnoreCase('Content-length'))
       continue;
    uriRequest.addHeader(headerName, baseRequest.getHeader(headerName));

    try(ClosableHttpResponse resp = client.execute(uriRequest)){
         for(Header header: resp.getAllHeaders){
                response.**add**(header.**getName**(), header.**getValue**());
         } catch(ParseException e){
                throw new IOException(e);
     } 

         String body = EntityUtils.toString(resp.getEntity());
         reponse.getOutputStream().write(body.getBytes())
   }

   baseRequest.**setHandled**(true);
   return false;

 }


 }

@johmn123-wq thanks, we are updating the documentation.

I'll point you to the PR with the updated documentation, rather than duplicating the answers here.

@johmn123-wq please see #12160.

Let us know if there is something missing, and we will expand.

Specific to your example, you can replace:

if("GET".equalsIgnoreCase(method)

with

if (HttpMethod.GET.is(method)

Also consider using HttpHeader.CONTENT_LENGTH.is(headerName).

You do not need to call request.setHandled(false), replace that with return false.

At the end of the handle() method, remove the call to request.setHandled(true) and just return true.

The last missing piece is that you must complete the Callback when you return true from the handle() method -- your code currently does not.

IIUC, your code also seems wrong as it calls client.execute() for every header? Maybe there are just too many braces around.

In any case, your code looks like a proxy, so consider using Jetty's ProxyHandler, so you don't have to maintain this code.

If you really want to do it on your own, when you receive the response from the external server you contacted with the client, you now have:

reponse.getOutputStream().write(body.getBytes())

change it to be:

response.write(true, ByteBuffer.wrap(body.getBytes()), callback)

where the last parameter callback is the Callback object passed to handle().
In this way, when the write() is complete, also the request/response processing will be completed.

@sbordet
yes there was mismatch of braces, Here is the modified code as per your suggestion.

public class ApacheServerHandler extends Handler.Abstract{

private ApacheServerHandler(){
client = HttpClients.createDeafult();
}

@OverRide
public void handle(Request request, Response response, Callback callback) throws IOException, ServletException{

String newHost = request.getHeaders().get('sb_host');
if(newHost.endsWith("/")){
newHost = StringUtils.substring(newHost, 0, newHost.length() - 1);
}

HttpUriRequest = null;
String method = request.getMethod();
String queryString = request.getHttpURI().getQuery();
String path = request.getHttpURI().getPath();
Enumerationheaders = request.getHeaders().getFieldNames();

String newUrl = String.format("http://%s%s", newHost, path, queryString);

if(HttpMethod.GET.matches(method)){
uriRequest = new HttpGet(newUrl);
}
else if(HttpMethod.POST.matches(method)){
uriRequest = new HttpPost(newUrl);
}
else if(HttpMethod.PUT.matches(method)){
uriRequest = new HttpPut(newUrl);
}
else{
return true;
}

if(uriRequest instanceof HttpEntityEncloseingRequest){
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) uriRequest;
entityRequest.setEntity(Content.Source.asInputStream(request),
ContentType.create(request.getHeaders().get(HttpHeaders.CONTENT_TYPE)));
}

while(headers.hasMoreElements()){
String headerName = headers.nextElement();
if(headerName.equalIgnoreCase('Content-length'))
continue;
uriRequest.addHeader(headerName, baseRequest.getHeader(headerName));
}

try(ClosableHttpResponse resp = client.execute(uriRequest)){
     for(Header header: resp.getAllHeaders){
            response.**add**(header.**getName**(), header.**getValue**());
     } catch(ParseException e){
            throw new IOException(e);
 } 

     String body = EntityUtils.toString(resp.getEntity());
     reponse.write(true, ByteBuffer.wrap(body.getBytes()), callback);

     return false;

}

Can you please provide the alternative of this block of code 'response.add(header.getName(), header.getValue())' adding each of these headers to a new HTTP response.

HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) uriRequest;
entityRequest.setEntity(Content.Source.asInputStream(request),
ContentType.create(request.getHeaders().get(HttpHeaders.CONTENT_TYPE))); having an issue with method setEntity in interface org.apache.hc.core5.http.httpEntityContainer cannot be applied to given types;
required : org.apache.hc.core5.http.httpEntity
found: java.io.inputStream, org.apache.hc.core5.http.ContetntType
reason: actual and formal aregument lists differ in length.

Need you assistance here.

Can you please provide the alternative of this block of code 'response.add(header.getName(), header.getValue())' adding each of these headers to a new HTTP response.

I already pointed you to use HttpFields and its APIs, see its javadocs.