SignalR connection 400 error (Bad Request) - reactjs

I'm trying build a SignalR proof of concept where two applications are involved; one is a web single-page application and the other one is a server-side RESTful web api. The technology/framework being used is ReactJs, ASP.NET Web API 2 (.NET Framework 4.6, NOT .NET Framework Core) and SignalR.
The Web API
This is how I have SignalR wired-up in the server application. When the application starts, I map SignalR to the application pipeline...
public static void ConfigureSignarlR(IAppBuilder app)
{
app.MapSignalR<ChatConnection>("/signalr", new Microsoft.AspNet.SignalR.HubConfiguration
{
EnableDetailedErrors = true
});
}
The ChatConnection class is an implementation of PersistentConnection that does nothing special...
public class ChatConnection : PersistentConnection
{
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
return base.OnReceived(request, connectionId, data);
}
protected override Task OnConnected(IRequest request, string connectionId)
{
return base.OnConnected(request, connectionId);
}
public override Task ProcessRequest(HostContext context)
{
return base.ProcessRequest(context);
}
}
and then I have a very simple hub...
public class ChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
}
The Client App
For the client application I'm using the #aspnet/signalr-client npm package...this is how I create and start the connection...
initialize = () => {
const hubCon = new HubConnection("http://api.domain/signalr");
hubCon.start()
.then(() => console.log("Connection established..."))
.catch(err => console.log(err))
}
Things to be noticed
Both the API and the client app are hosted on the same local IIS server but with different host names (using host files)
When using the browser to navigate to http://api.domain/signalr/hubs, I get a 400 (Bad Request) response when the message following message Protocol error: Unknown transport.
When attempting to connect from the client app, I get the same error message
The ProcessRequest method is the only one that gets hit when debugging the ChatConnection class
Question(s)
What did I miss here? Or how can I get this PoC to work?
The question is quite broad because I seriously have no clue of what's going on here

After a bit of digging and reading through SignalR documentation I realized that I was doing everything wrong. Basically, SignalR implements two different connection patterns:
Hubs: a high-level API built on top of the Persistent connection API
Persistent connections
A client cannot communicate with a persistent connection endpoint using a Hub proxy (or at least not the way I was doing it). So, what I did was:
Kept the PersistentConnection but overrided the OnReceived method so it can broardcast to all clients
protected override async Task OnReceived(IRequest request, string connectionId, string data)
{
await Connection.BroadCast("message to broadcast");
}
Removed the "signalr\hubs" script reference because it's not needed
Registered the connection on start up (server-side)
app.MapSignalR<ChatConnection>("/chat");
Finnaly, on the client side, initialize the connection and register all necessary callbacks
this.connection = window.$.connection(process.env.REACT_APP_API_BASE_URI + "/chat");
this.connection.logging = true;
this.connection.received((data) => {
console.log("Received some data:")
console.log(data)
});
this.connection.start(() => {
console.log("Connection opened")
console.log("connectionId = " + this.connection.id)
});

Related

Only one usage of each socket address (protocol/network address/port) is normally permitted

The last few weeks we have been experiencing this error message while using the Azure Search SDK (1.1.1 - 1.1.2) and performing searches.
We consume the Search SDK from internal APIs (deployed as Azure Web Apps) that scale up-down based on traffic (so there could be more than 1 instance of the APIs doing the searches).
Our API queries 5 different indexes and maintains an in-memory copy of the SearchIndexClient object that corresponds to each index, a very simple implementation would look like:
public class AzureSearchService
{
private readonly SearchServiceClient _serviceClient;
private Dictionary<string, SearchIndexClient> _clientDictionary;
public AzureSearchService()
{
_serviceClient = new SearchServiceClient("myservicename", new SearchCredentials("myservicekey"));
_clientDictionary = new Dictionary<string, SearchIndexClient>();
}
public SearchIndexClient GetClient(string indexName)
{
try
{
if (!_clientDictionary.ContainsKey(indexName))
{
_clientDictionary.Add(indexName, _serviceClient.Indexes.GetClient(indexName));
}
return _clientDictionary[indexName];
}
catch
{
return null;
}
}
public async Task<SearchResults> SearchIndex(SearchIndexClient client, string text)
{
var parameters = new SearchParameters();
parameters.Top = 10;
parameters.IncludeTotalResultCount = true;
var response = await client.Documents.SearchWithHttpMessagesAsync(text, parameters, null, null);
return response.Body;
}
}
And the API would invoke the service by:
public class SearchController : ApiController
{
private readonly AzureSearchService service;
public SearchController()
{
service = new AzureSearchService();
}
public async Task<HttpResponseMessage> Post(string indexName, [FromBody] string text)
{
var indexClient = service.GetClient(indexName);
var results = await service.SearchIndex(indexClient, text);
return Request.CreateResponse(HttpStatusCode.OK, results, Configuration.Formatters.JsonFormatter);
}
}
We are using SearchWithHttpMessagesAsync due to a requirement to receive custom HTTP headers instead of the SearchAsync method.
This way we avoid opening/closing the client under traffic bursts. Before using this memory cache (and wrapping each client on a using clause) we would get port exhaustion alerts on Azure App Services.
Is this a good pattern? Could we be receiving this error because of the multiple instances running in parallel?
In case it is needed, the stack trace shows:
System.Net.Http.HttpRequestException: Only one usage of each socket address (protocol/network address/port) is normally permitted service.ip.address.hidden:443
[SocketException:Only one usage of each socket address (protocol/network address/port)is normally permitted service.ip.address.hidden:443]
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)
[WebException:Unable to connect to the remote server]
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
EDIT: We are also receiving this error A connection attempt failed because the connected party did not properly respond after a period of time:
System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond service.ip.address.hidden:443
[SocketException:A connection attempt failed because the connected party did not properly respond after a period of time,or established connection failed because connected host has failed to respond service.ip.address.hidden:443]
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)
[WebException:Unable to connect to the remote server]
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
As implemented in the code in your question, the cache will not prevent port exhaustion. This is because you're instantiating it as a field of the ApiController, which is created once per request. If you want to avoid port exhaustion, the cache must be shared across all requests. To make it concurrency-safe, you should use something like ConcurrentDictionary instead of Dictionary.
The "connection attempt failed" error is likely unrelated.

(Android Studio) Connecting an app to Google Endpoints Module

I'm having trouble following the second step here.
I really don't understand how this sample does anything other than return a simple toast message. How does it utilize the API to display that message?
class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private static MyApi myApiService = null;
private Context context;
#Override
protected String doInBackground(Pair<Context, String>... params) {
if(myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
}
I'm afraid my this sample is too complex for my limited knowledge. How exactly do I "talk" to the Google Endpoints Module when running an app? Specifically, What is EndpointsAsyncTask();?
Are there any resources listing all the methods available to me? Is there a simpler example of an app communicating with a Google Cloud Endpoint?
The service methods available to you are defined by the backend source in section 1.
In the example you posted, this line: myApiService.sayHi(name).execute()
is an actual invocation call to the backend that you defined by annotating #ApiMethod("sayHi") on the method in the MyEndpoint.java class of your backend module.
The reason your Android app defines an EndpointsAsyncTask is because slow operations such as calls that hit the network need to happen off of the UI thread to avoid locking the UI. The demo simply puts the returned value into a Toast but you could modify onPostExecute() to do whatever you'd like with the result.
For more info on Google Endpoints check out:
https://cloud.google.com/appengine/docs/java/endpoints/
And for info about using an Android AsyncTask look here:
http://developer.android.com/reference/android/os/AsyncTask.html

Is Tyrus nessary to implement Java WebSocket authentication?

Although there's very similar post, it is unanswered.
My JavaFX app with WebSocket will
send user id、password to server
keep the session to let user do something like personal data management.
Learning from
Oracle WebSocket、
Tyrus 8.14 Client HTTP Authentication
I have:
#ClientEndPoint
public class loginEndPoint {
final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
public static void main(String [] args) {
AuthConfig authConfig = AuthConfig.Builder.create().disableBasicAuth().build();
Credentials credentials = new Credentials("ws_user", "password");
client.getProperties().put(ClientProperties.AUTH_CONFIG, authConfig);
client.getProperties().put(ClientProperties.CREDENTIALS, credentials);
client.connectToServer(new Endpoint() {
#Override
public void onOpen(Session session, EndpointConfig config) {
try {
session.addMessageHandler((MessageHandler.Whole<String>) (String message) -> {
System.out.println("Received message: "+message);
messageLatch.countDown();
});
//let user do some data management
} catch (IOException e) {
System.out.println("Connect Fail.");
}
}
}, cec, new URI("ws://localhost/myApp/login"));
}
}
Is these code right to do the authentication? And where could I do the server side authentication on #ServerEndPoint?
#ServerEndpoint
public class loginServerEndPoint {
}
Thanks for help.
No, it is not necessary to use Tyrus as a server implementation.
On the server-side you should secure WebSocket in exactly the same way as you secure servlet or jsp in your servlet container, which can be slightly different from container to container (mapping users to roles).
Look at authentication example
Note that this example shows up how to make authenticated WebSocket request handshake with BASIC auth scheme, but your client code disables it explicitly, so probably you want to use DIGEST scheme.

Enforcing Basic Authentication with RestEasy & TJWS

We use Resteasy to communicate between multiple backend servers & we want to lock this down so not just anyone can attach a client or browser to the restlet server.
We're using Resteasy 3.04 and as our backend services are numerous but very light-weight an embeddded TJWS webserver.
Example Server code:
public class RestEasySSLBasicAuthenticationServer {
static TJWSEmbeddedJaxrsServer webServer;
static class BasicAthenticationSecurityDomain implements SecurityDomain {
#Override
public Principal authenticate(String aUsername, String aPassword) throws SecurityException {
System.out.println("User:" + aUsername + " Password" + aPassword);
if (aPassword.equals("password") == false) {
throw new SecurityException("Access denied to user " + aUsername);
}
return null;
}
#Override
public boolean isUserInRoll(Principal aUsername, String aRole) {
// No role based checks so return true
return true;
}
}
public static void main(String[] args) throws Exception {
// Create embedded TJWS web server
webServer = new TJWSEmbeddedJaxrsServer();
// Set up SSL connections on server
webServer.setSSLPort(8081);
webServer.setSSLKeyStoreFile("K:\\source\\RestEasyTest\\server_localhost.jks");
webServer.setSSLKeyStorePass("krypton");
webServer.setSSLKeyStoreType("JKS");
// Add basic HTTP authentication to the server
webServer.setSecurityDomain( new BasicAthenticationSecurityDomain() );
// Add the restlet resource
webServer.getDeployment().getActualResourceClasses().add(PlayerResource.class);
// Start the web server
webServer.start();
// Run until user presses a key
System.out.print("Web server started. Press a key to stop...");
System.in.read();
// Stop the web server
webServer.stop();
}
}
Example client code:
public class RestEasySSLBasicAuthenticationClient {
public static void main(String[] args) throws Exception {
// Set up the keystore
System.setProperty("javax.net.ssl.keyStore", "K:\\source\\RestEasyTest\\client_localhost.jks");
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
System.setProperty("javax.net.ssl.keyStorePassword", "krypton");
// Create a new Restlet client
Client restletClient = ClientBuilder.newClient();
// *** Even WITHOUT these credentitials we can access the restlet
// restletClient.register(new BasicAuthentication("username", "password"));
// Set up the restlet request target.
WebTarget request = restletClient.target("https://localhost:8081/player/{id}");
request = request.resolveTemplate("id", Long.valueOf(1));
// Build the restlet request
Invocation invocation = request.request("application/xml").buildGet();
// Call the restlet and get returned object
Player result = invocation.invoke( Player.class );
System.out.println(result.toString());
}
}
Using the test client and a registered authentication filter works and as expected I can a 401 access error if I get the password incorrect.
However if no authentication is registered at the client then the server never calls the SecurityDomain check and access is allowed.
How do I enforce a login at the server?
You can ensure all users are authenticated by enabling security on the embedded TJWS web server.
webServer.getDeployment().setSecurityEnabled(true);

NoClassDefFoundError: javax.naming.directory.InitialDirContext is a restricted class. Using CCS (GCM) in Google App Engine

Im trying to implement google's Cloud Connection Server with Google App Engine following this tutorial -
Implementing an XMPP-based App Server. I copied latest smack jars from http://www.igniterealtime.org/projects/smack/ (smack.jar and smackx.jar), put them in WEB-INF/lib and added them to the classpath (im using eclipse).
In the code sample in the first link i posted, the XMPPConnection is initiated in a 'main' method. Since this is not really suitable to GAE i created a ServletContextListener and added it to web.xml.
public class GCMContextListener implements ServletContextListener {
private static final String GCM_SENDER_ID = "*GCM_SENDER_ID*";
private static final String API_KEY = "*API_KEY*";
private SmackCcsClient ccsClient;
public GCMContextListener() {
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
final String userName = GCM_SENDER_ID + "#gcm.googleapis.com";
final String password = API_KEY;
ccsClient = new SmackCcsClient();
try {
ccsClient.connect(userName, password);
} catch (XMPPException e) {
e.printStackTrace();
}
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
try {
ccsClient.disconnect();
} catch (XMPPException e) {
e.printStackTrace();
}
}
}
web.xml
<web-app>
<listener>
<listener-class>com.myserver.bootstrap.GCMContextListener</listener-class>
</listener>
</web-app>
Now, when i start the GAE server i get the following exception :
java.lang.NoClassDefFoundError: javax.naming.directory.InitialDirContext is a restricted class. Please see the Google App Engine developer's guide for more details.
i searched the "Google App Engine developer's guide for more details" but couldnt find anything about this. can you please help me ?
Google App Engine restricts access to certain JRE classes. In fact they published a whitelist that shows you which classes are useable. It seems to me that the Smack library might require some reference to a directory context (maybe to create the XMPP messages?) and that is why your servlet causes this exception. The javax.naming.directory is not in the whitelist.
I'm currently working on setting up a GCM Server as well. It seems to me that you need to read through the example and see what that main method is doing. What I see is a connection to the GCM server:
try {
ccsClient.connect(userName, password);
} catch (XMPPException e) {
e.printStackTrace();
}
Then a downstream message being sent to a device:
// Send a sample hello downstream message to a device.
String toRegId = "RegistrationIdOfTheTargetDevice";
String messageId = ccsClient.getRandomMessageId();
Map<String, String> payload = new HashMap<String, String>();
payload.put("Hello", "World");
payload.put("CCS", "Dummy Message");
payload.put("EmbeddedMessageId", messageId);
String collapseKey = "sample";
Long timeToLive = 10000L;
Boolean delayWhileIdle = true;
ccsClient.send(createJsonMessage(toRegId, messageId, payload, collapseKey,
timeToLive, delayWhileIdle));
}
These operations would be completed at some point during your application's lifecycle, so your servlet should support them by providing the methods the example is implementing, such as the connect method that appears in the first piece of code that I pasted here. It's implementation is in the example at line 235 if I'm not mistaken.
As the documentation says, the 3rd party application server, which is what you're trying to implement using GAE, should be:
Able to communicate with your client.
Able to fire off properly formatted requests to the GCM server.
Able to handle requests and resend them as needed, using exponential back-off.
Able to store the API key and client registration IDs. The API key is included in the header of POST requests that send messages.
Able to store the API key and client registration IDs.
Able to generate message IDs to uniquely identify each message it sends.

Resources