Adding a filter to find out remote IP address of requester using JdkHttpServerFactory - rest

Is there any way to get the remote IP address while sticking to the JdkHttpServerFactory framework?
I have a very light RESTful server which uses JdkHttpServerFactory to create a server, essentially with one line of code:
JdkHttpServerFactory.createHttpServer(baseUri, config);
My dependencies are:
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.23.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jdk-http</artifactId>
<version>2.23.2</version>
</dependency>
It appears it is not trivial to add a filter to this framework. I have tried it by bringing in HttpServletRequest but this means I need to bring in the servlet framework, and this actually didn't work for me with JdkHttpServerFactory, I end up getting null as my request.
I found another suggestion, but that suggestion also meant switching over to using Grizly2, which agains means I have to abandon the simple/light JdkHttpServerFactory method.

The only way I can see to access the IP is through the HttpExchange. Unfortunately, this is not something that is exposed to us. You can either make a feature request to expose the IP or you can just modify it yourself and build your own artifact. In either case, the change I would suggest making is to just add a property in to the ContainerRequest, that you could pull out in your filter.
If you look here in the source code, you will see the ContainerRequest. I would just do something like
final ContainerRequest requestContext = new ContainerRequest(baseUri, requestUri,
exchange.getRequestMethod(), getSecurityContext(exchange.getPrincipal(), isSecure),
new MapPropertiesDelegate());
requestContext.setProperty(JdkServerProperties.REMOTE_IP_ADDR, exchange.getRemoteAddr());
Then in your ContainerRequestFilter, you can just do
#Override
public void filter(ContainerRequestContext requestContext) {
// ContainerRequest implements ContainerRequestContext
InetSockAddress remoteAddr = (InetSocketAddress) requestContext.getProperty(JdkServerProperties.REMOTE_IP_ADDR);
}
Yeah, so if I were to make a feature/pull request or just build my own artifact, this is probably the change I would make. Or if you don't want to expose the InetSocketAddress, you can just build the IP string and add that as the property value.

Related

Keycloak: Add custom extension io.undertow.servlet.ServletExtension

I wanted to add a custom servlet extension to Keycloak which would install a http handler that gets invoked on every request sent to Keycloak and sets up some logging MDC context that our custom SPI code can use for logging the incoming request traces correctly.
Following the docs here I created a custom extension class:
public class UndertowHandlerExtension implements ServletExtension {
#Override
public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
deploymentInfo.addInnerHandlerChainWrapper(TraceIdCapturingHandler::new);
}
}
And have defined my custom http handler TraceIdCapturingHandler in the same JAR file. I also added a file to META-INF/services/io.undertow.servlet.ServletExtension and set the fully qualified reference to the extension class. I also updated my deployments jboss-deployment-structure.xml and added the following 2 entries as dependencies:
<module name="io.undertow.servlet" />
<module name="javax.servlet.api" />
However, when my deployment is created the extension is not being invoked and my filter is not executing. Is there something I am missing in terms of how to configure Wildfly for Keycloak so that my extension and handler are installed and used correctly?
EDIT:
After doing a bit of digging I realized I was headed down the wrong path. Looked at this repository and I think I need a custom RealResourceProvider as shown here which in turn can install my filter by obtaining an instance of ResteasyProviderFactory and invoking getContainerRequestFilterRegistry().registerSingleton().
Will try this out and report back.
Please see the edit above for my question. I was able to implement a RealmResourceProviderFactory instance that initialized the filters I needed on startup in the init() method:
#Override
public void init(Config.Scope scope) {
log.info("Initializing");
initializeKeycloakFilters();
}
private void initializeKeycloakFilters() {
ResteasyProviderFactory providerFactory = ResteasyProviderFactory.getInstance();
TraceIdCapturingFilter filter = new TraceIdCapturingFilter();
providerFactory.getContainerRequestFilterRegistry().registerSingleton(filter);
}

Renaming an XML/SOAP tag using Apache CXF

I've got a SOAP web-service server using Apache CXF as implementation. Due to some external technical constraint I'd like to be able to rename some XML tags naming an operation parameter (which are deprecated) in the inbound SOAP request. I was reading about using Interceptors for this, but the documentation on how to setup/configure them is not very clear.
My code to publish an endpoint is the following:
Endpoint endpoint = Endpoint.create(
"http://schemas.xmlsoap.org/soap/", new MyServer());
endpoint.publish("ws/endpoint");
Ideally I'd like to add a filter only to a given endpoint (I have several of them).
Apache's documentations about interceptors are quite clear (IMO), anyway, there is a helloworld project (based on spring boot, cxf and maven) in my github profile which you can take a look for setting up interceptors (in fact it's a baisc autentication interceptor).
For setting up an interceptor (e.g InInterceptor), your class should extend AbstractPhaseInterceptor<Message> and override handleMessage(Message message) method, then in the constructor you should declare the phase in which the interceptor is going to be applied. Finally you have to instantiate it and apply in on an Endpoint.
As you said:
rename some XML tags naming an operation parameter (which are
deprecated) in the inbound SOAP request
I think the name of the operation parameter (in WSDL file) is something different from the argument of your web method. Suppose that there is method in your endpoint named addPerson:
#WebMethod
String addPerson(Person person) {
/*method logic*/
}
and Person class:
class Person {
private String firstName;
private String lastName;
private Date birthDate;
//getters and setters
}
in order to map lastName property to a different name, you have to annotate it with
#XmlElement(name = "sureName")
private String lastName;
after applying this anotation, sureName (in wsdl file) is going to be mapped to lastName.
In addition, there is #WebParam annotation which can be used for changing the name of web method arguments:
#WebMethod
String sayHello( #WebParam(name = "sureName") String lastName);
Hope it helps.

Questions about combining Hystrix with Feign

I am trying to use the new HystrixFeign support in Feign. Here is what my code looks like
route66Client =
HystrixFeign.builder()
.logger(new Slf4jLogger())
.encoder(new GsonEncoder())
.target(Route66Client.class, "http://route66/");
The Route66Client is defined as
public interface Route66Client {
#RequestLine("POST /route")
ApiResponse getRoute(
RouteRequest request);
}
When i try to run the code. I get UnknownHostException. As it is not able to resolve route66 for its hostname. Anyone knows what i could be missing ?
Further i had this working with regular Feign ( not HystrixFeign ). All i did was to annotate my Route66Client class with #FeignClient("...") and then injecting Route66Client in the calling class ( So no Feign.builder() was used )
But i couldn't find some #HystrixFeignClient annotation. So i went ahead and started using the HystrixFeign.builder(). But then when i did that the serviceName->Address resolution stopped working.
If you want the benefits of eureka, don't use the builder directly. Put #EnableFeignClients on an #Configuration class (usually your application). Then label Route66Client with #FeignClient("route66") and inject Route66Client. This is only available in Brixton's 2nd Milestone. See the documentation.

Servlet: forward request to Servlet by its name when using annotations instead of web.xml?

I have an entry servlet (called DispatcherServlet) which redirects all incoming GET and POST requests at /* to other servlets depending on a configuration parameter. In order to dispatch the request to other servlets, I use their name instead of a path, because my DispatcherServlet would end up in an endless loop while listening to /*.
The "old" way was to give servlets a name in the web.xml descriptor:
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>mypackage.MyServlet</servlet-class>
</servlet>
Afterwards, one is able to address the servlet by its name, for example, when using a RequestDispatcher. The code in my DispatcherServlet looks like this:
RequestDispatcher dispatcher = getServletContext().getNamedDispatcher("MyServlet");
dispatcher.forward(request, response);
This works perfectly. But, I would like to use the "new" Servlet 3 annotations without the web.xml, hence I do this:
#WebServlet(name="MyServlet")
public class MyServlet extends HttpServlet {
...
But now, getServletContext().getNamedDispatcher("MyServlet") within my DispatcherServlet returns null instead of the appropriate dispatcher for MyServlet, although I expect that #WebServlet(name="MyServlet") behaves the same like <servlet-name>MyServlet</servlet-name>
I have tested this on Tomcat 7.
Is this because MyServlet has not been loaded yet by the time when DispatcherServlet is called and hence the name of MyServlet is not known? If so, what sense does the #WebServlet(name="...") annotation make ;)
Please don't answer with something like "why don't you use filters?" etc. This is more about the background of annotations vs. web.xml
Thanks in advance!
(ps. please change the title if you find a more suitable one ;))
If you do not specify the urlMapping with the name, you will need do add the full class name. Like
RequestDispatcher dispatcher = getServletContext().
getNamedDispatcher("mypackage.MyServlet");
It works for me if the servlet is annotated as:
#WebServlet(urlPatterns="/Servlet", name="Servlet")
or better:
#WebServlet(urlPatterns="/Servlet", displayName="Servlet", name="Servlet")
Also, you can try to get your servlet name with:
getServletName()
By just comment with #WebServlet is not enough to load a Servlet into the Web container; you must include at least one URL pattern, and if you don't want to expose the Servlet to keep it for internal forward only, set the URL to start with '/WEB-INF/'.
And keep in mind that the 'name' property is buggy, so not use it.
#WebServlet(urlPatterns = "/WEB-INF/thisServlet")
public class FooServlet extends HttpServlet {
...
}
and to forward the request, call something like this:
servletContext.getNamedDispatcher(FooServlet.class.getName()).forward(request, response);

GWT Maven build with maven profiles

I'm attempting to use capabilities provided by maven profiles to build customized builds for different server environments. What I'm attempting to do is combine maven resource filtering
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
with it's profile mechanism
<profiles>
<profile>
<id>mock</id>
<properties>
<application-url>http://mock-server-url</application-url>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
to convert this value in a file named server.cfg
${application-url}
to something I can use here:
public interface ServerResource extends ClientBundle {
public static final ServerResource INSTANCE = GWT.create(ServerResource.class);
#Source("server.cfg")
public TextResource server();
}
I can see that the value's been replaced in WEB-INF/classes but it doesn't appear that GWT used the file with the replacement to create the application javascript. How can I do this?
Using GWT compiler permutations to apply this kind of configuration is in my opinion a very bad idea. One of the most common complaints about GWT is the time it takes to compile, and by doing this you're just adding to the problem.
Configuration should usually be read from configuration files (surprise!), like shown here.
Anyway, what you're trying to do seems to me impossible. You cannot tell the client-side code to which server it should connect. This would violate the same-origin policy! The app can only communicate with the server it came from.
To have different apps running in different URLs, you would need to deploy several GWT apps with different names (even if they are basically the same). Then, you would just have to type the correct URL for each app (version) in the browser, and it will "look" at the right app. So you could have URLs like this:
http://myserver.com/app1
http://myserver.com/app2
In order to make a request to a different app running in the same server as the GWT application, you can do something like this:
String serviceUrl = "/app2/someService"; // or some other String sourced from a config file, using a GWT ClientResource for example
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET,
serviceUrl);
try {
// send request from app1 to app2
rb.sendRequest(null, new RequestCallback() {
#Override
public void onResponseReceived(Request request,
Response response) {
log.info("Response: " + response.getStatusText());
// if response is 200 it's ok, you can read the outputStream to see what's in there
}
#Override
public void onError(Request request, Throwable exception) {
log.warning("Request Error", exception);
// do something more
}
});
} catch (RequestException e) {
log.warning("Request Exception", e);
// getting here means trouble with the connection or service!
}
I solved what I was trying to accomplish without the use of maven profiles or the GWT ClientBundle (which I never did get to work in the way I had intended when I wrote the question).
Here were the main issues I hoped to solve using maven profiles and the workaround I employed to solve the issue at hand:
Use Mock MVP Models in Hosted Mode
// inside the initialization for my model locator
boolean hostedMode = GWT.getPermutationStrongName().equals("HostedMode");
if (hostedMode) {
// instantiate mock models
} else {
// instantiate real models to call REST web services
}
Provide real models with correct RESTful server URL
I was able to accomplish this because my GWT app and the RESTful web service url follow a set naming convention. I basically strip the trailing '/' from the URL and append '_services"
String createServicesBaseUrl() {
StringBuffer baseUrl = new StringBuffer(GWT.getHostPageBaseURL());
int length = baseUrl.length();
baseUrl.replace(length-1, length, "_services");
return baseUrl.toString();
}
Enable testing of as much of the MVP Presenter (Activities & Places) as I could
I was already injecting the model locator into my Activity classes, so replacing that with a mock model locator for use by JUnit was straightforward. I did the same for my views and abstracted away some of the other code which didn't seem to work outside of the browser (like the GWT PlaceController).
All in all my build is much the same, but I learned how to gain a lot of flexibility in testing, configuring the server instance my GWT application connects with, and which model my application uses (dependent on hosted vs server mode).