apache tomcat 8 websocket origin and client address - java

H.e.l.l.o community, i hope someone can help me ... i am using apache tomcat 8.0.0-RC5 and JSR-356 web socket API ...
I have 2 questions:
1) Is it possible to get the client ip on #OnOpen method ??
2) Is it possible to get the origin of the connection ???
I followed the websocket example which comes with the distribution of tomcat and i was not able to find the answers .... My java class is basically as follow
#ServerEndpoint(value = "/data.socket")
public class MyWebSocket {
#OnOpen
public void onOpen(Session session) {
// Here is where i need the origin and remote client address
}
#OnClose
public void onClose() {
// disconnection handling
}
#OnMessage
public void onMessage(String message) {
// message handling
}
#OnError
public void onError(Session session, Throwable throwable) {
// Error handling
}
}

Repeating the answer I already gave you on the Tomcat users mailing list...
Client IP. No. Generally this type of information is available at the handshake
which occurs before OnOpen but client IP is not one of the pieces of
information exposed. You might be better blocking these earlier e.g. with iptables or similar.
Origin. ServerEndpointConfig.Configurator.checkOrigin(String) You'll need a custom Configurator. Keep in mind that a malicious client can forge the origin header.

I know this question is old, but just in case someone else finds it in a web search:
Yes there is an easy workaround. A Servlet can receive and forward a WebSocket upgrade request. The trick is to get the client IP address and expose it as a parameter.
Here's your servlet code:
#WebServlet("/myExternalEntryPoint")
public class WebSocketServlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
var dispatcher = getServletContext().getRequestDispatcher("/myInternalEntryPoint");
var requestWrapper = new MyRequestWrapper(request);
dispatcher.forward(requestWrapper, response);
}
}
And here's MyRequestWrapper:
class MyRequestWrapper extends HttpServletRequestWrapper {
public RequestWrapper(HttpServletRequest request) {
super(request);
}
public Map<String, String[]> getParameterMap() {
return Collections.singletonMap("remoteAddr", new String[] {getRequest().getRemoteAddr()});
}
}
Now in your WebSocket implementation, you'll be able to get remoteAddr via javax.websocket.Session.getRequestParameterMap().
Naturally, if your original request has parameters that you care about, you'll need to create a map that includes those as well. Also I recommend you append a separate, secret parameter and check for it in your WebSocket code to prevent anyone from hitting the internal entry point directly.
I figured out this was possible because of this thoughtful comment in the Tomcat source code (WsFilter.java):
// No endpoint registered for the requested path. Let the
// application handle it (it might redirect or forward for example)

Related

Decoding 202 Accepted response with Spring SOAP

I'm trying to write a Spring-WS client for a preexisting service. The endpoint offers two very similar actions, they both consume my data and respond with a simple object status; I need to use both. The difference is, one of them responds with HTTP status code 200 and the other with 202. In the first case the status object is decoded correctly and returned from WebServiceTemplate.marshallSendAndReceive(), but in the second case it's not decoded at all, and the method returns null.
According to the HTTP spec, the HTTP status 202 is Accepted, and its purpose is to indicate that the request has been received but not yet acted upon. However, the response may still contain useful information, like current request status or a completion estimate. I want to get this response.
I tried to debug the exact process and noticed my program executing the following code in the org.springframework.ws.transport.http.AbstractHttpSenderConnection.hasResponse() method:
protected final boolean hasResponse() throws IOException {
int responseCode = getResponseCode();
if (HttpTransportConstants.STATUS_ACCEPTED == responseCode ||
HttpTransportConstants.STATUS_NO_CONTENT == responseCode) {
return false;
}
...
}
This code fragment seems responsible for never getting the status object from the 202 Accepted response. (Or a 204 No Content response, but that's obviously acceptable.)
Is there a way around this? It doesn't seem possible to override this method in a bean, it's marked final.
The closest thing to an answer I could find was the following SWS JIRA ticket. It's marked "Resolved" since August 2012, but it's really not, as the comment from 2015 says.
my workaround:
Implement a custom HttpResponseInterceptor to handle a HTTP202:
public class MyInterceptor implements HttpResponseInterceptor {
#Override
public void process(HttpResponse httpResponse, HttpContext arg1) throws HttpException, IOException {
if (202 == httpResponse.getStatusLine().getStatusCode()) {
httpResponse.setStatusLine(new BasicStatusLine(httpResponse.getStatusLine().getProtocolVersion(),200,httpResponse.getStatusLine().getReasonPhrase()));
}
}
}
Now, add the interceptor to my http client builder when creating the webServiceTemplate
public CloseableHttpClient httpClient() throws Exception {
return HttpClientBuilder.create().addInterceptorLast(new MyInterceptor()).setSSLSocketFactory(sslConnectionSocketFactory()).build();
}

Custom DNS Server with netty - coding the Answer section

I am trying to write a little custom DNS server using netty. The following code works but I have two questions:
Is there a better way than using DefaultDnsRawRecord to create the DNS answer section? There are some other DNS records like DefaultDnsOptEcsRecord or DefaultDnsPtrRecord but they seem to be used for other sections of the DNS response.
Should I use the netty internal method io.netty.util.internal.SocketUtils.addressByName() to create a InetAdress object for a fixed IP address without acutally querying the DNS which is required in my case? Is there a standard Java way for this?
Setting up the pipeline:
bootstrap.group(group)
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<NioDatagramChannel>() {
#Override
protected void initChannel(NioDatagramChannel nioDatagramChannel) throws Exception {
nioDatagramChannel.pipeline().addLast(new DatagramDnsQueryDecoder());
nioDatagramChannel.pipeline().addLast(new DatagramDnsResponseEncoder());
nioDatagramChannel.pipeline().addLast(new DnsHandler());
}
})
.option(ChannelOption.SO_BROADCAST, true)
.localAddress("127.0.0.1", 40053);
The DnsHandler:
public class DnsHandler extends SimpleChannelInboundHandler<DatagramDnsQuery> {
#Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery query) throws Exception {
DatagramDnsResponse response = new DatagramDnsResponse(query.recipient(), query.sender(), query.id());
DefaultDnsQuestion dnsQuestion = query.recordAt(DnsSection.QUESTION);
response.addRecord(DnsSection.QUESTION, dnsQuestion);
byte[] address = SocketUtils.addressByName("127.0.0.1").getAddress();
DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(dnsQuestion.name(),
DnsRecordType.A, 3600, Unpooled.wrappedBuffer(address));
response.addRecord(DnsSection.ANSWER, queryAnswer);
ctx.writeAndFlush(response);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
}
As netty itself not really has any specify encoder / decoder for server side the DefaultDnsRawRecord may be your best bet for now.
You should not use classes that resist in the .internal. packages as these are for internal usage by netty and may be changed or removed at any time (as these are not considered to be public api). Only use these if you really know what you are doing and not care about breakage.

Trying to retrieve HttpSession object

For some background, I'm using JBoss AS 7 with EJB. I'm sending a message to my server from the client using errai message bus when it initially connects to retrieve its session ID so that I can make requests from it later on and have the server respond to the specific client.
How do I go about doing this? Can I inject a HttpSession object server side somehow? I'm very new to this so please bear with me. If I'm too vague let me know and I'll try to elaborate more.
If you are sending a message to an ErraiBus service method, you will have the Message object available. You can retrieve the session from it and get that session's ID like this:
#Service
public class ClientHelloService implements MessageCallback {
#Override
public void callback(final Message message) {
HttpSession session = message.getResource(
HttpServletRequest.class, HttpServletRequest.class.getName()).getSession();
System.out.println("Client said hello. Session ID: " + session.getId());
}
}
If you are instead sending the message to an Errai RPC endpoint, you will not have such easy access to the message. In this case, you will have to use the RpcContext.getSession() method:
#Service
public class ClientHelloRpcServiceImpl implements ClientHelloRpcService {
#Override
public void hello() {
HttpSession session = RpcContext.getHttpSession();
System.out.println("Client said hello. Session ID: " + session.getId());
}
}
The way this works is simple but ugly: RpcContext class stores the Message object that contained the RPC request in a ThreadLocal, and it just retrieves the HttpSession from that.
// the following variable is in the Http servlet service() method arguments
// only shown here this way to demonstrate the process
javax.servlet.http.HttpServletRequest serviceRequest;
javax.servlet.http.HttpServletResponse serviceResp; // addCookie()
javax.servlet.http.HttpSession cecil;
javax.servlet.http.Cookie[] reqCk;
// "(boolean overload) "true" creates the session" or call the other overload version method with no argument
// to retrieve the session getSession() "the server container stores and creates sessions"
// false in that version is to avoid bothering for a session to cut down uneeded processing
cecil = serviceRequest.getSession();//creates a session if it does not have one
String httpSession_ID = cecil.getID();
if((reqCk = serviceRequest.getCookies()) == null){
// perhaps create a cookie here using "new class "
// cookiePiece = new javax.servlet.http.Cookie("COOKIENAME",....); ....YOU MUST LEARN THE COOKIE PARTS WRITING RULES FOR BROWSER COOKIES !!! ; ; ;
serviceResp.addCookie(cookiePiece); // now is on the servers array "reqCk"
}else{
// process the cookie here using javax.servlet.http.Cookie methods
}
Other ways of storing and retrieving data are session scoped JSP or JSF beans.

How to measure the cost of requests (cpm_usd) programmatically?

I want to keep track of the cpm_usd value of all requests that arrive on my GAE frontend instances. I see the cpm_usd headers in the logs of my application. But is there a way to access those numbers at runtime in order to graph them? I want to create a near real-time cost metric for every endpoint.
/rest/foo1: $0.000011
/rest/foo2: $0.000013
/rest/bar1: $0.000014
/rest/bar2: $0.000016
Is there a trusted tester program to do this? If not, is there another way to do this which does not involve parsing my log files? Or can I only get those numbers by parsing the logs in the background?
Updates
As described here, I tried adding a sitebricks request filter which intercepts the {add,set}Header() calls.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
#Override
public void setHeader(String name, String value) {
if ("X-AppEngine-Estimated-CPM-US-Dollars".equals(name)) {
// log request costs
}
super.setHeader(name, value);
}
#Override
public void addHeader(String name, String value) {
if ("X-AppEngine-Estimated-CPM-US-Dollars".equals(name)) {
// log request costs
}
super.addHeader(name, value);
}
});
}
I assume that the header either has a different name or GAE sets headers differently. In either case, I never see the cost header being caught.
The header is added by the application server that sits in front of your instance so you cannot access this header from your code.
You will have to parse the log files, and can consider using log2bq to do this.

Using JAX-WS: How can I set the user agent property

I've searched on this and found a few near misses. I've created a java client to consume a web service using JAX-WS. Is there a way when using JAX to set the HTTP_USER_AGENT value? I would like to have my web service log when specific clients (mine) access it so I wanted a customized value.
I've seen options where you set it in the system properties but this doesn't seem to work. The generated JAX classes don't seem to have a direct reference to the connection object so I don't see how I can manipulate those classes.
Any help would be great.
Thanks
ST
The solution to this kind of problem in JAX-WS is to implement a SoapMessage Handler (Interface: SOAPHandler< SOAPMessageContext >).
Within that handler you insert your HTTP header into maybe already existing headers, then you give control to the next handler in the handler chain.
The concept of this handler chain is kind of nice, you can have small classes for a very specific purpose (Security, Logging etc.).
In your client you configure the handler chain prior to sending any request:
// HandlerChain installieren
Binding binding = ((BindingProvider) port).getBinding();
List hchain = binding.getHandlerChain();
if (hchain == null) {
hchain = new ArrayList();
}
hchain.add(new HTTPUserAgentHandler());
binding.setHandlerChain(hchain);
And here is the code for the HTTPUserAgentHandler:
public class HTTPUserAgentHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean request = ((Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY)).booleanValue();
if (request) {
#SuppressWarnings("unchecked")
Map<String, List<String>> headers = (Map<String, List<String>>) context
.get(MessageContext.HTTP_REQUEST_HEADERS);
if (null == headers) {
headers = new HashMap<String, List<String>>();
}
headers.put("HTTP_USER_AGENT", Collections.singletonList("user_agent"));
context.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
#Override
public void close(MessageContext context) {}
#Override
public Set<QName> getHeaders() {
return null;
}
}
Let me question the idea of having HTTP header first.
A more correct (WS-centric) approach is to set SOAP Header, not HTTP header. Consider this: SOAP messages can be delivered not only by HTTP, but by JMS, SMTP or custom transports. By requiring to have user-agent HTTP Header, you unnecessary tie you code to only one transport, albeit currently prevailing.
This is the reason BTW why JAX-WS have no notion of HTTP headers except in handlers.
And (of course) StackOverlow knows how to create SOAP headers.
not sure if this is the best/most direct way to do it, but i think you could add a custom javax.xml.ws.handler.Handler to the handler chain in the dispatch javax.xml.ws.Binding. in the Handler, you should be able to set a custom map of extra http headers on the outgoing MessageContext using the MessageContext.HTTP_REQUEST_HEADERS property.

Categories