This code is a very basic Restlet app:
public class FirstStepsApplication extends Application {
private static final String ROOT_URI = "/";
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach(ROOT_URI, RootServerResource.class);
return router;
}
}
How can I route not just root "/" into the RootServerResource but also all "/*" path? Is this a limitation of restlet or this can be done?
Try:
router.attachDefault(RootServerResource.class);
Related
Background
I'm currently working on a project that looks at various HTTP passive fingerprinting techniques for the purpose of security. Some aspects of the request I plan to fingerprint include the client hello, header order, HTTP2 frame settings, and HTTP2 pseudo header order. So far I have implemented a solution to retrieve the cipher suites, compression methods, and extensions from the client hello by extending Jetty's org.eclipse.jetty.util.ssl.SslContextFactory.Server class to wrap SSLEngine instances. I can then access the client hello data in a Zuul filter as shown below:
private static final String SSL_SESSION_ATTRIBUTE = "org.eclipse.jetty.servlet.request.ssl_session";
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
SSLSession sslSession = (SSLSession) request.getAttribute(SSL_SESSION_ATTRIBUTE);
ClientHello clientHello = (ClientHello) sslSession.getValue("client-hello");
return null;
}
More information about HTTP2 fingerprinting:
http://webcache.googleusercontent.com/search?q=cache:https://www.akamai.com/us/en/multimedia/documents/white-paper/passive-fingerprinting-of-http2-clients-white-paper.pdf
Issue
Although Spring, Netflix Zuul, and Jetty embedded server have thorough documentation I have been unable to find a way to achieve a similar sort of solution for retrieving HTTP2 frame setting.
You can extend HTTP2ServerConnectionFactory and override protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint) to return your custom implementation (that may delegate to the original implementation).
In this way, you have access to the low-level HTTP/2 frames (as objects, not in byte format) which may allow you to fingerprint the client.
A minimal code example to elaborate on #sbordet's answer.
You can extend HTTP2ServerConnectionFactory and override protected > ServerSessionListener newSessionListener(Connector connector, EndPoint > endPoint) to return your custom implementation (that may delegate to the original implementation).
MyServerSessionListener.java
public class MyServerSessionListener implements ServerSessionListener {
private final ServerSessionListener delegate;
public MyServerSessionListener(ServerSessionListener delegate) {
this.delegate = delegate;
}
...
#Override
public void onSettings(Session session, SettingsFrame settingsFrame) {
Map<Integer, Integer> settings = settingsFrame.getSettings();
RequestContext context = RequestContext.getCurrentContext();
context.set("http2-frame-settings", settings);
delegate.onSettings(session, settingsFrame);
}
}
MyHTTP2ConnectionFactory.java
public class MyHTTP2ServerConnectionFactory extends HTTP2ServerConnectionFactory {
public MyHTTP2ServerConnectionFactory(HttpConfiguration httpConfiguration) {
super(httpConfiguration);
}
public MyHTTP2ServerConnectionFactory(HttpConfiguration httpConfiguration, String... protocols) {
super(httpConfiguration, protocols);
}
#Override
protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint) {
ServerSessionListener delegate = super.newSessionListener(connector, endPoint);
return new MyServerSessionListener(delegate);
}
}
MyFilter.java
public class MyFilter extends ZuulFilter {
...
#Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
Map<Integer, Integer> http2FrameSettings = (Map<Integer, Integer>) context.get("http2-frame-settings");
return null;
}
}
Introduction
I would like to be able to have two different spring profiles, and depending on the profile to change to a hardcoded address for our feign builders.
Currently was have the following:
return builder.target(cls, "http://" + serviceName);
But I would actually like to do the following and over-ride the address:
return builder.target(cls, "http://our-server:8009/" + serviceName);
Why
Sometimes we don't want to run all the services within our development environment. Additionally, some of the services are only available through a zuul gateway sometimes.
So we run the same code in different situations and conditions.
Technical Details
We have the following code that we use for building our Feign Clients.
We had been using the #FeignClient annotation in the past, but lately we decided to start building our feignClients manually.
Example below:
#FeignClient(name = "ab-document-store", configuration = MultiPartSupportConfiguration.class, fallback = DocumentStoreFallback.class)
We call the feignRegistrar class with the following command:
return registerFeignClient(DocumentStoreClient.class, true);
#RequiredArgsConstructor
//#Component
#Slf4j
public class FeignRegistrar {
#Autowired
private Decoder decoder;
#Autowired
private Encoder encoder;
#Autowired
private Client client;
#Autowired
private Contract feignContract;
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Autowired
private List<RequestInterceptor> interceptors;
public <T> T register(Class<T> cls, String serviceName, boolean isDocumentStore) {
if(isDocumentStore){
encoder = new MultipartFormEncoder(new SpringEncoder(messageConverters));
}
//Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(encoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
for(RequestInterceptor interceptor : interceptors) {
builder.requestInterceptor(interceptor);
}
log.debug("Registering {} - as feign proxy ", serviceName);
return builder.target(cls, "http://" + serviceName);
}
public static class Slf4Logger extends Logger {
#Override
protected void log(String configKey, String format, Object... args) {
log.info("{} - {}", configKey, args);
}
}
}
Spring Cloud Property Over-ride
We have also been using property files such as application-ENV.property with entries such as the following:
ab-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
ab-document-store.ribbon.listOfServers: localhost:8025
Unfortunately, listOfServers is not enough for us. We would like to be able to assign a directory/path as well. Something like:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
Otherworkaround
I have thought about sneaking in a header into all requests such as X-SERVICE-NAME using a feign interceptor. Then we could point all services to an address (e.g. localhost:9001) , and forward/proxy those requests to localhost:9001/X-SERVICE-NAME.
However, I would prefer a much easier solution such as:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
But this doesn't work :(
Introduction
I found a solution for this using a proxy that detects a header.
So, I have a feign interceptor on the java-side that attaches a header x-service-name to every feign-request.
I also have a NodeJS proxy, that analyzes requests, finds x-service-name, and re-writes the requests to become: x-service-name/originalRequestPath.
This allows me to have all the microservices behind a zuul gateway but also access them using a eureka-over-ride.
Java-Feign-Interceptor
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(usedEncoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
NodeJS proxy
In the example, my zuul gateway ( or another proxy ) is on localhost:9001.
I'm listening on localhost:1200 .
let enableProxyForJava = process.env.ENABLE_PROXY_FOR_JAVA;
if (enableProxyForJava != undefined && enableProxyForJava.toLowerCase() === 'true') {
var httpProxyJava = require('http-proxy');
var proxyJava = httpProxyJava.createProxy();
gutil.log( gutil.colors.green('Enabling Proxy for Java. Set your Eureka overrides to localhost:1200.') );
require('http').createServer(function(req, res) {
console.log("req.headers['x-service-name'] = " + req.headers['x-service-name']);
console.log("Before req.url:"+ req.url);
if( req.headers['x-service-name'] != undefined){
let change = req.headers['x-service-name'] +req.url;
console.log("After req.url:"+ change);
req.url = change;
}
proxyJava.web(req, res, {
target: 'http://localhost:9001/'
});
}).listen(1200);
}
Property file inside Java Application that has feign clients
mbak-microservice1.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice1.ribbon.listOfServers: localhost:1200
mbak-microservice2.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice2.ribbon.listOfServers: localhost:1200
mbak-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-document-store.ribbon.listOfServers: localhost:1200
I'm having a bit of trouble making Swagger display API docs using Restlet. What Swagger shows is just these stuff:
And checking the api-docs it only shows this:
I wonder what is wrong with my code:
public class MyApplication extends SwaggerApplication {
private static final String ROOT_URI = "/";
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach(ROOT_URI, RootServerResource.class);
router.attach(ROOT_URI + "ping", PingServerResource.class);
router.attach(ROOT_URI + "ping/", PingServerResource.class);
// Some code omitted for simplicity
return router;
}
}
You could have a look at this article:
What can APISpark bring to your existing Web APIs (Part 2) -http://restlet.com/blog/2016/01/04/what-can-apispark-bring-to-your-existing-web-apis-part-2/
Both Swagger1 and 2 are supported by the Swagger extension of Restlet:
Swagger v1
public class ContactsApplication extends SwaggerApplication {
public Restlet createInboundRoot() {
Router router = new Router();
(...)
attachSwaggerSpecificationRestlet(router, "/docs");
return router;
}
}
Swagger v2
public class ContactsApplication extends Application {
public Restlet createInboundRoot() {
Router router = new Router();
(...)
Swagger2SpecificationRestlet swagger2SpecificationRestlet
= new Swagger2SpecificationRestlet(this);
swagger2SpecificationRestlet.setBasePath("http://myapp.org/");
swagger2SpecificationRestlet.attach(router, "/docs");
return router;
}
}
The solution is to add this code:
// Configuring Swagger 2 support
Swagger2SpecificationRestlet swagger2SpecificationRestlet
= new Swagger2SpecificationRestlet(this);
swagger2SpecificationRestlet.setBasePath("http://localhost:8080/api-docs");
swagger2SpecificationRestlet.attach(router);
And point the Swagger UI to /swagger.json
Swagger needs to find your API operations. I'm not sure about Restlet, in Jersey you annotate your REST resource classes with #Api and your methods with #ApiOperation. Read more here in the swagger docs.
How would one handle consuming an attachment from a client POST/PUT request on the server side and store that file in a local folder, all using Restlet ?
My thoughts are as follows:
Setup Server as follows:
new MailServerComponenet.start();
public MailServer(){
getServers().add(Protocol.HTTP, 8111);
getDefaultHost().attachDefault(new MailServer());
server.getContext().getParameters().set("tracing", "true");
}
#Put
public void store(Form form){
// *And here is where I am not sure*
}
Thanks for any insight and help in advance.
Here are the following steps you should follow to implement your Restlet application:
Create a component
Component component = new Component();
Create an application and attach it on the component
component.getServers().add(Protocol.HTTP, 8182);
Application application = new MyApplication();
component.getDefaultHost().attachDefault(application);
component.start();
Configure the application within the method createInboundRoot (create a router and attach server resource on it)
public class MyApplication extends Application {
#Override
public Restlet createInboundRoot() {
Router router = new Router();
router.attach("/test", MyServerResource.class);
return router;
}
}
Implement the server resources
public class MyServerResource extends ServerResource {
#Post
public Representation handlePost(Representation repr) {
(...)
}
}
Now the global frame is implemented, I would wonder how you would send the content to Restlet. Is it simple binary content within the request payload or multi-part content?
Binary content
#Post
public Representation handlePost(FileRepresentation fileRepr) {
fileRepr.write(new FileOutputStream(
new File("/tmp/myfile.txt")));
return null;
}
Multipart content. You can have a look at this answer in this case: File Upload with Description in Restlet
Hope it helps you,
Thierry
I faced with problem using jax ws client with Weblogic 10.3. I generate webservice stubs and test connection with webservice in simple java project. All works fine. But when I pack this project in jar file and add it to my main project which contains other jars and running on weblogic I get:
java.lang.NoSuchMethodError: org.home.client.AddressWS.getAddressByRequestAsync(ILjava/lang/String;)Ljavax/xml/ws/Response;
This exception was thrown when I tried to call webservice stub`s method.
public class MyServiceImpl implements MyService {
private AddressWS service;
private static final String ENDPOINT = "http://endpoint.address.ws.company.org/";
private static final String SERVICE_NAME = "AddressWSImplService";
#Override
public void setSOAPServiceURL(String serviceURL) {
URL url = createURL(serviceURL);
QName qName = new QName(ENDPOINT, SERVICE_NAME);
AddressWSImplService addressWSImplService= new AddressWSImplService(url, qName);
service = addressWSImplService.getAddressWSImplPort();
}
#Override
public String getAddressById(int id, String param) throws TimeoutException {
// NoSuchMethodError was thrown here
final Response<GetAddressById> response = service
.getAddressByIdAsync(id, param);
return (String) getValue(new Future<String>() {...});}
Any pointers will be helpfull.