I'm having trouble connecting to a websocket in Java. My code worked previously when I ran a server endpoint on localhost port 8080, but when I'm trying to connect to a websocket I found online, i'm getting
javax.websocket.DeploymentException: The HTTP response from the server [HTTP/1.1 200 OK] did not permit the HTTP upgrade to WebSocket
I tested the ws endpoint by using Simple Websocket client chrome extension and I'm getting a response. So the server is definitely working properly.
My client code:
package clientendpoints;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.DeploymentException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
//import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Logger;
#ClientEndpoint
public class MyClientEndpoint {
private Logger logger = Logger.getLogger(this.getClass().getName());
Session session = null;
public MyClientEndpoint(String endpointURI) {
//start connection
javax.websocket.WebSocketContainer container = javax.websocket.ContainerProvider.getWebSocketContainer();
try {
container.connectToServer(this, new URI(endpointURI));
} catch (Exception e) {
e.printStackTrace();
}
}
#OnOpen
public void onOpen(Session session) {
logger.info("Client onOpen..." + session.getId());
this.session = session;
}
#OnMessage
public void onMessage(String message, Session session) {
logger.info("MyClientEndpoint log: " + message);
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
logger.info(String.format("Session %s close because of %s", session.getId(), closeReason));
}
And I call it with
MyClientEndpoint client = new MyClientEndpoint(MY_SECRET_ENDPOINT);
Related
my fornt end is website I am using jquery frame to request a websocket request at web page loads and my server is a springboot application.
but the springboot application logs sad "org.springframework.web.servlet.PageNotFound : No mapping for GET"
This is my code to implement the websocket request
//刷新页面时创建链接
$(function(){
//创建webSocket链接
var ws = new WebSocket(serverWebSocketURL + "/empPoint");
ws.onopen = function(evt){
send("Hello world!!!");
}
ws.onmessage = function(evt){
}
ws.onerror = function(evt){
}
ws.onclose = function(evt){
//关闭链路时打开
}
});
This is my code from springboot application server
package com.njustjjy.tpk2.endpoint;
import com.njustjjy.tpk2.config.GetHttpSessionConfig;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
#Log4j2
#ServerEndpoint(value = "/empPoint",configurator = GetHttpSessionConfig.class)
#Component
public class EmpEndpoint {
//生命session对象
private Session session;
//获取session
private HttpSession httpSession;
#OnOpen
public void onOpen(Session session, EndpointConfig endpointConfig){
//将局部的session赋值到全局作用域
this.session = session;
//获取HttpSession
this.httpSession = (HttpSession) endpointConfig.getUserProperties().get(HttpSession.class.getName());
}
#OnMessage
public void onMessage(String message, Session session){
System.out.print(message);
}
#OnError
public void onError(Throwable err){
log.error("发生错误:" + err);
}
}
This is JavaConfig in the springboot
package com.njustjjy.tpk2.config;
import com.njustjjy.tpk2.endpoint.EmpEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class WebScoketConfig {
#Bean
public EmpEndpoint businessEndpoint(){
return new EmpEndpoint();
}
}
I am try some blogs but they are no any way,but I think this problem about tomcat support for websocket protocol. Do I have any master to help me?
I am using core java for my rest service. Based on my requirement I cannot use spring boot for rest endpoint.
I achieved implementing http endpoint from core java.
But I cannot enable HTTPS via core java application.
I have .p12 file with me. Is there any way to enable HTTPS for Core java application ?
Below is my code
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpServer;
public class Sample {
public static void main(String[] args) {
try {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/endpoint", new CustomHttpHandler());
server.setExecutor(null);
server.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
CustomHttpHandler
import java.io.IOException;
import java.io.OutputStream;
import com.sun.net.httpserver.*;
public class CustomHttpHandler implements HttpHandler {
#Override
public void handle(HttpExchange res) throws IOException {
String response = "Response from server";
res.sendResponseHeaders(200, response.length());
OutputStream outputStream = res.getResponseBody();
outputStream.write(response.getBytes());
outputStream.close();
}
}
Here's what I'm trying to do:
Set up a Tomcat 7 server programmatically - that works with a simple servlet. I'm defining Tomcat's endpoints from within the code only, there are no settings nor routers on the file system (this is a constraint).
Have the said Tomcat server include a websocket server endpoint using Javax Websockets API (1.1) - that doesn't work.
My app/server entry point:
package com.myapp;
import java.io.File;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.deploy.FilterDef;
import org.apache.catalina.deploy.FilterMap;
import org.apache.catalina.startup.Tomcat;
public class App
{
public static void main(String[] args) throws LifecycleException
{
int port = 8080;
Tomcat webServer = new Tomcat();
webServer.setPort(port);
webServer.setHostname("localhost");
String appBase = ".";
webServer.getHost().setAppBase(appBase);
File docBase = new File(System.getProperty("java.io.tmpdir"));
Context context = webServer.addContext("", docBase.getAbsolutePath());
// both MyServlet and MyFilter exist and work.
Class servletClass = MyServlet.class;
Tomcat.addServlet(context, servletClass.getSimpleName(), servletClass.getName());
context.addServletMapping("/my-servlet/*", servletClass.getSimpleName());
Class filterClass = MyFilter.class;
FilterDef myFilterDef = new FilterDef();
myFilterDef.setFilterClass(filterClass.getName());
myFilterDef.setFilterName(filterClass.getSimpleName());
context.addFilterDef(myFilterDef);
FilterMap myFilterMap = new FilterMap();
myFilterMap.setFilterName(filterClass.getSimpleName());
myFilterMap.addURLPattern("/my-servlet/*");
context.addFilterMap(myFilterMap);
webServer.start();
webServer.getServer().await();
}
}
My websocket server endpoint class:
package com.myapp;
import java.nio.ByteBuffer;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/web-socket/")
public class WebSocket
{
#OnOpen
public void onOpen()
{
System.out.println("Open Connection ...");
}
#OnMessage
public static void onTextMessage(Session session, String msg) {
System.out.println("On Message for Web Socket");
}
#OnMessage
public void onBinaryMessage(Session session, ByteBuffer msg){
System.out.println("On Message for Web Socket");
}
#OnMessage
public void onPongMessage(Session session, PongMessage pMsg) {
System.out.println("On Message for Web Socket");
}
#OnClose
public void onClose(Session session) {
System.out.println("Connection Close for Web Socket");
}
#OnError
public void onError(Throwable e)
{
e.printStackTrace();
}
}
I'm using Maven, and I'm including the WebSocket API with <scope>provided</scope>, as I've seen in other questions that this was the root of other problems.
When I run the server, I can hit the my-servlet endpoint successfully (as a web page, of course), but when I try to create a WS object, simply within the browser's dev console using var webSocket = new WebSocket("ws://localhost:8080/web-socket"), it says that it can't hit the endpoint:
WebSocket connection to 'ws://localhost:8080/web-socket' failed: Error during WebSocket handshake: Unexpected response code: 404
My question: What am I missing? The error message (from Chrome) suggests that the endpoint isn't registered (hence the 404). How do I register the websocket ServerEndpoint? Thanks!
EDIT:
Here's my implementation of MyServlet class:
package com.myapp;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet
{
#Override
protected void doGet(
HttpServletRequest req,
HttpServletResponse resp) throws IOException
{
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().write("Works...");
resp.getWriter().flush();
resp.getWriter().close();
}
}
And here's my implementation of MyFilter:
package com.myapp;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class MyFilter implements Filter
{
#Override
public void init(FilterConfig filterConfig)
{
// ...
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.addHeader("myHeader", "myHeaderValue");
chain.doFilter(request, httpResponse);
}
#Override
public void destroy()
{
// ...
}
}
Try this to manually add web socket endpoint:
String serverContainerClass = ServerContainer.class.getName();
//should be "javax.websocket.server.ServerContainer", if not, some external package could have hogged the implementation
final ServerContainer serverContainer = (ServerContainer) context.getServletContext().getAttribute(serverContainerClass);
try
{
serverContainer.addEndpoint(WebSocket.class);
}
catch (DeploymentException e)
{
TraceWriter.Error(this, "Failed to initialize websocket", e);
}
You should put this code after you add servlet to context
I am working on an authentication filter for my REST service.
Can some one please explain why this name binding not works.
When I make a post request, I can receive the String "Tokenized", but log does not print "Inside the filter".
import java.io.IOException;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.FormParam;
import javax.ws.rs.NameBinding;
import javax.ws.rs.Produces;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Target;
import javax.annotation.Priority;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.ext.Provider;
import org.apache.log4j.Logger;
#Path("/authentication")
public class AuthenticationHandler {
final static Logger log = Logger.getLogger(AuthenticationHandler.class);
#NameBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Secured {
}
#Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
log.info("Inside the filter");
// Get the HTTP Authorization header from the request
String authorizationHeader
= requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the token from the HTTP Authorization header
String token = authorizationHeader.substring("Bearer".length()).trim();
try {
// Validate the token
validateToken(token);
} catch (Exception e) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
}
private void validateToken(String token) throws Exception {
// Check if it was issued by the server and if it's not expired
// Throw an Exception if the token is invalid
}
}
#POST
#Secured
#Path("/request_token")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response authenticateUser(#FormParam("username") String username,
#FormParam("password") String password) {
try {
// Authenticate the user using the credentials provided
authenticate(username, password);
// Issue a token for the user
String token = issueToken(username);
// Return the token on the response
return Response.ok(token).build();
} catch (Exception e) {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
private boolean authenticate(String username, String password) throws Exception {
return true;
}
private String issueToken(String username) {
return "Tokenized";
}
}
Thanks for the tip peeskillet.
It is now working fine after creating separate classes for Name Bind and Filter. I'm posting the solution below if anyone required.
My problem now is, is there a way to keep this two classes in a separate package, because I tried by putting it on my Util package and it didn't work properly.
peeskillet : Thanks for the tip again.
AuthenticationFilter.java
import com.binosaurs.sf.backend.handler.Secured;
import com.binosaurs.sf.backend.util.*;
import java.io.IOException;
import javax.annotation.Priority;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.apache.log4j.Logger;
#Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
// Get Log4j Logger
final static Logger log = Logger.getLogger(AuthenticationFilter.class);
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
log.info("Inside the filter");
// Get the HTTP Authorization header from the request
String authorizationHeader
= requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the token from the HTTP Authorization header
String token = authorizationHeader.substring("Bearer".length()).trim();
try {
// Validate the token
validateToken(token);
} catch (Exception e) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
}
private void validateToken(String token) throws Exception {
// Check if it was issued by the server and if it's not expired
// Throw an Exception if the token is invalid
}
}
Secured.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.binosaurs.sf.backend.handler;
import com.binosaurs.sf.backend.util.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import javax.ws.rs.NameBinding;
#NameBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Secured {
}
AuthenticationHandler.java
package com.binosaurs.sf.backend.handler;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.FormParam;
import javax.ws.rs.Produces;
import org.apache.log4j.Logger;
#Path("/authentication")
public class AuthenticationHandler {
final static Logger log = Logger.getLogger(AuthenticationHandler.class);
#POST
#Secured
#Path("/request_token")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response authenticateUser(#FormParam("username") String username,
#FormParam("password") String password) {
try {
// Authenticate the user using the credentials provided
authenticate(username, password);
// Issue a token for the user
String token = issueToken(username);
// Return the token on the response
return Response.ok(token).build();
} catch (Exception e) {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
private boolean authenticate(String username, String password) throws Exception {
return true;
}
private String issueToken(String username) {
return "Tokenized";
}
}
I am trying to set timeout for WS call. I extended WebServiceGatewaySupport and was trying to set to Sender timeout like this:
public Object marshalSendAndReceive(Object requestPayload) {
WebServiceTemplate wsTemplate = this.getWebServiceTemplate();
for (WebServiceMessageSender sender : wsTemplate.getMessageSenders()) {
try {
HttpComponentsMessageSender httpSender = (HttpComponentsMessageSender) sender;
httpSender.setReadTimeout(3000);
httpSender.setConnectionTimeout(2000);
} catch (ClassCastException | NumberFormatException cex) {
logger.warn("Cannot set WS timeout: " + cex.getMessage());
}
}
return wsTemplate.marshalSendAndReceive(requestPayload);
}
(credit to question #6733744)
However I get: Cannot set WS timeout: org.springframework.ws.transport.http.HttpsUrlConnectionMessageSender cannot be cast to org.springframework.ws.transport.http.HttpComponentsMessageSender
Can timeout be set to HttpsUrlConnectionMessageSender somehow? Or is there any other way to set timeout to https ws call in spring-boot?
Thank you.
I had the same issue, and managed to make it work using HttpComponentsMessageSender. Here is my code:
HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender();
HttpClient httpClient = HttpClientFactory.getHttpsClient(sslUtils, timeout);
messageSender.setHttpClient(httpClient);
webServiceTemplate.setMessageSender(messageSender);
I also created a new factory class HttpClientFactory that sets the SSL and timeout:
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
public class HttpClientFactory {
private static CloseableHttpClient client;
private HttpClientFactory() {
}
public static HttpClient getHttpsClient(SslUtils sslUtils, int timeout) throws Exception {
if (client != null) {
return client;
}
SSLContext sslcontext = getSSLContext(sslUtils);
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.addInterceptorFirst(new ContentLengthHeaderRemover());
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.build();
return httpClientBuilder.setSSLSocketFactory(factory)
.setDefaultRequestConfig(config)
.build();
}
private static class ContentLengthHeaderRemover implements HttpRequestInterceptor {
#Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
request.removeHeaders(HTTP.CONTENT_LEN);
}
}
public static void releaseInstance() {
client = null;
}
private static SSLContext getSSLContext(SslUtils sslUtils) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(sslUtils.getKeystore().getInputStream(), sslUtils.getKeyPwd().toCharArray());
sslUtils.getKeystore().getInputStream().close();
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(sslUtils.getTrustStore().getInputStream(), sslUtils.getTrustPwd().toCharArray());
sslUtils.getTrustStore().getInputStream().close();
SSLContextBuilder sslContextBuilder = SSLContexts.custom();
try {
sslContextBuilder = SSLContexts.custom().loadKeyMaterial(ks, ssl.getKeyPwd().toCharArray());
} catch (UnrecoverableKeyException e) {
e.printStack();
}
sslContextBuilder.loadTrustMaterial(ts, new TrustSelfSignedStrategy());
return sslContextBuilder.build();
}
}
For information the SslUtils is just a bean class that holds the keystore and truststore informations' :
public class SslUtils {
private Resource keystore;
private String keyPwd;
private Resource trustStore;
private String trustPwd;
// Getters and Setters
}
This works for me and let me use both SSL and timeout at the same. I hope this will help others.
You're getting a cast exception because HttpsUrlConnectionMessageSender and HttpComponentsMessageSender are not compatible types. They both share the same superclass (WebServiceMessageSender), but you can't cast one to the other. I think you can just use a HttpComponentsMessageSender in your WebServiceTemplate configuration. See How to set timeout in Spring WebServiceTemplate and Spring webServiceTemplate connection timeout property