Websocket connection failing - reactjs

The WebSocket connection to 'ws://localhost:3000/ws/api/stocks_prices/AAPL/' failed: WebSocket is closed before the connection is established.
React frontend
views.js
useEffect(() => {
const socket = new WebSocket(
`ws://${window.location.host}/ws/api/stocks_prices/${symbol}/`
);
console.log("socket", socket);
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
setStockPrices((prevStockPrices) => ({
...prevStockPrices,
data: [data, ...prevStockPrices.data.slice(0, 4)],
}));
console.log("websocket data", data);
};
return () => {
socket.close();
console.log("WebSocket closed");
};
}, [symbol]);
Using Django for backend
routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/api/stocks_prices/(?P<symbol>\w+)/$', consumers.StockConsumer.as_asgi()),
]
consumer.py :
from channels.generic.websocket import AsyncWebsocketConsumer
import json
import yfinance as yf
class StockConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.stock_name = self.scope['url_route']['kwargs']['stock_name']
self.stock_group_name = 'stocks_prices/%s' % self.stock_name
# Join room group
await self.channel_layer.group_add(
self.stock_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.stock_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
stock_name = text_data_json['stock_name']
# Fetch real-time stock data
ticker = yf.Ticker(stock_name)
data = ticker.history(period='1d')
stock_price = {
'stock_name': stock_name,
'price': data['Close'].iloc[-1],
}
# Send real-time stock data to the client
await self.channel_layer.group_send(
self.stock_group_name,
{
'type': 'stock_price',
'price': stock_price
}
)
# Receive message from room group
async def stock_price(self, event):
stock_price = event['price']
# Send real-time stock data to the client
await self.send(text_data=json.dumps({
'stock_name': stock_price['stock_name'],
'price': stock_price['price']
}))
views.py :
#api_view(['GET'])
def get_stock(request, symbol):
ticker = yf.Ticker(symbol)
data = ticker.history(period='1d')
stocks_prices = {
'symbol': symbol,
'price': data['Close'].iloc[-1],
'volume': data['Volume'].iloc[-1],
'datetime': data.index[-1].isoformat(),
}
return Response(stocks_prices)
I am getting this in my console
WebSocket closed
View.js:127 socket WebSocket {url: 'ws://localhost:3000/ws/api/stocks_prices/AAPL/', readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, …}binaryType: "blob"bufferedAmount: 0extensions: ""onclose: nullonerror: nullonmessage: event => {…}onopen: nullprotocol: ""readyState: 0url: "ws://localhost:3000/ws/api/stocks_prices/AAPL/"[[Prototype]]: WebSocket
View.js:126
WebSocket connection to 'ws://localhost:3000/ws/api/stocks_prices/AAPL/' failed: WebSocket opening handshake timed out
I am trying to establish websocket connection and I have enabled CORS.

Related

SSE: Receive JSON from a Flask server through EventSource API

I have a Flask application which responds to a ReactJS application primarily with json. This is important because the Flask app is sending objects from our postgres database.
Recently I started building a "feed" for our website. How I imagine it working: React client issues a GET request to the Flask server for the "items" which will populate the feed. I want to use the EventSource API, using a SSE/stream . This should yield much more fluid page rendering from the user's perspective and allow for much more data to be sent to the client with noticeable loading time (right?).
The problem: EventSource API seems to require a content_type = 'text/event-stream'... which is not ideal for sending objects which much more naturally send as JSON. When I try to send JSON and change the content_type to 'application/json', the browser complains that EventSource was expecting 'text/event-stream'.
My question: is there anything inherent to EventSource which restricts it to 'text/event-stream' responses?
Also! CORS is involved in this process. But I don't think its in the way, because it works for the rest of my app. so this is communication between an app hosted at localhost:5000 and localhost:3000.
Some code:
Flask Server
item_feed.py
#bp.get('/items/stream')
def item_stream():
count = request.args.get("count", None)
def stream_messages_to_client(count):
i = 0
while i < int(count):
data = { "message": "This is a test object."}
# message = "data: This is a test streamable message.\n\n" # This will be received.
message = json.dumps(data) # This will not be received--and this is what I want.
i += 1
yield message
# content_type = 'text/event-stream' # This will be received.
content_type = 'application/json' # This will not be received.
response = Response(stream_with_context(
stream_messages_to_client(count)
), content_type=content_type)
return response
React JS Client
ItemFeed.js
export const ItemFeed = () => {
let [searchParams, setSearchParams] = useSearchParams();
const defaultMessages = [];
const [messages, setMessages] = useState(defaultMessages);
const [isConnectionOpen, setIsConnectionOpen] = useState(false);
const paramsString = searchParams.toString();
const url = process.env.REACT_APP_SERVER + `/items/stream?${paramsString}`;
const eventSource = new EventSource(url, { withCredentials: true })
const handleConnectionOpen = () => {
setIsConnectionOpen(true);
}
const handleStreamedData = (e) => {
// const { message } = JSON.parse(e.data); // This will not parse
const message = e.data; // This will parse, assuming 'data: {{ message }}\n\n'
(messages.length < 10)
? setMessages([...messages, message])
: eventSource.close();
}
const handleConnectionClose = () => {
setIsConnectionOpen(false);
}
eventSource.onopen = handleConnectionOpen;
eventSource.onmessage = handleStreamedData;
eventSource.onclose = handleConnectionClose;
useEffect(() => {
// do something
}, [messages]);
return (
<div className="row">
<p>this is a test</p>
{messages.map((message, index) => (
<div className="col-12" key={message}>{ message }</div>
))}
</div>
);
}

rsocketjs channel not established with spring-boot rsocket server

I have a simple rsocket backend with one message mapping defined :
#Controller
class MessageController {
#MessageMapping("test")
public Flux<String> test(Flux<String> stringFlux) {
return stringFlux.map(String::toUpperCase);
}
}
On the frontend side I try to connect to this channel from typescript + React + rsocketjs application.
Rsocket client :
class ChatServerClient {
private readonly host: String;
private readonly port: number;
private rsocket!: ReactiveSocket<any, any>;
private constructor(host: String = "localhost", port: number = 9090) {
this.host = host;
this.port = port
}
private async createClient() {
const client = new RSocketClient(
{
setup: {
dataMimeType: 'application/json',
keepAlive: 1000000, // avoid sending during test
lifetime: 100000,
metadataMimeType: MESSAGE_RSOCKET_COMPOSITE_METADATA.string,
},
transport: new RSocketWebsocketClient({
debug: true,
url: "ws://" + this.host + ":" + this.port,
wsCreator: (url) => {
return new WebSocket(url);
}
}, BufferEncoders),
errorHandler: (e) => {
console.log(e)
}
}
);
return await client.connect();
}
public static CreateAsync = async (host: String = "localhost", port: number = 9090) => {
const client = new ChatServerClient(host, port);
client.rsocket = await client.createClient()
return client;
};
test(pushMessage : Flowable<String>) {
const metadata = encodeCompositeMetadata(
[
[MESSAGE_RSOCKET_ROUTING.string, encodeRoute("test")]
]
)
const payloads = pushMessage.map(m => {return {
data : m,
metadata: metadata
}})
return this.rsocket.requestChannel(payloads)
}
}
export { ChatServerClient };
Simple component estabilishing connection :
interface ChatWindowProps { }
const ChatWindow: FC<ChatWindowProps> = (props: ChatWindowProps) => {
const [rsocket, setRsocket] = useState<ChatServerClient | null>(null)
useEffect(() => {
async function getClient() {
const client = await ChatServerClient.CreateAsync("localhost", 9090)
setRsocket(client)
}
getClient()
}, [setRsocket])
console.log("rerender")
const just : Flowable<String> = Flowable.just("a", "b")
const recivedStrings = rsocket?.test(just)
console.log("received strings " + recivedStrings)
recivedStrings?.subscribe({
onNext: m => console.log(m),
onSubscribe: s => {console.log("received strings on subscribe"); s.request(3)}
})
return (
<div className={styles.ChatWindow} data-testid="ChatWindow">
{ rsocket ? (
<div className="chat-container">
Connected
</div>
) : (
<div>Not Connected</div>
)}
</div>
)
};
export default ChatWindow;
I expect that on component rerender, when rsocket client is connected, and I establish the channel communication I would receive "A" and "B" in console but that is not the case, it seems that the channel is not created but client is connected to the server.
Other remarks :
this is just a simplification - other endpoints which use request-response style work so I do not think this is the problem with server setup
I can even see that the returned Flowable is subscribed to but nothing happens on the frontend side.
also on the server side I cannot see any frames which could indicate that any channel connection was established.
The question is - why is the channel not created and items are not emmited to the server and what is the correct way to use channel style communication from typescript rsocketjs client?

How to start WebRTC call with AWS Kinesis as Master

I'm trying to start a WebRTC call with AWS Kinesis, but the demo on The AWS Kinesis Javascript docs only shows how to join the call as a VIEWER not the MASTER.
I can't find a clear example anywhere online, and I've spent hours on it with my teammate.
I can see and hear myself, so I know I'm getting the hardware working correctly, but we can't see or hear each other. I know it's going to be something simple, but I just can't figure out where I'm going wrong with the connection.
const startKinesisCall = async () => {
const coachingSession = new AWS.KinesisVideo({
region,
accessKeyId,
secretAccessKey,
correctClockSkew: true
});
// Get Signaling Channel Endpoints
// Each signaling channel is assigned an HTTPS and WSS endpoint to connect to for
// data-plane operations. These can be discovered using the GetSignalingChannelEndpoint API.
const getSignalingChannelEndpointResponse = await coachingSession.getSignalingChannelEndpoint({
ChannelARN: channelARN,
SingleMasterChannelEndpointConfiguration: {
Protocols: ['WSS', 'HTTPS'],
Role: Role.VIEWER
}
}).promise();
const endpointsByProtocol = getSignalingChannelEndpointResponse?.ResourceEndpointList?.reduce((endpoints, endpoint) => {
endpoints[endpoint.Protocol] = endpoint?.ResourceEndpoint;
return endpoints;
}, {});
// Create KVS Signaling Client
// The HTTPS endpoint from the GetSignalingChannelEndpoint response is used with this client.
// This client is just used for getting ICE servers, not for actual signaling.
const kinesisVideoSignalingChannelsClient = new AWS.KinesisVideoSignalingChannels({
region,
accessKeyId,
secretAccessKey,
endpoint: endpointsByProtocol.HTTPS,
correctClockSkew: true,
});
// Get ICE server configuration
// For best performance, we collect STUN and TURN ICE server configurations.
// The KVS STUN endpoint is always stun:stun.kinesisvideo.${region}.amazonaws.com:443.
// To get TURN servers, the GetIceServerConfig API is used.
const getIceServerConfigResponse = await kinesisVideoSignalingChannelsClient
.getIceServerConfig({
ChannelARN: channelARN,
}).promise();
const iceServers = [{ urls: `stun:stun.kinesisvideo.${region}.amazonaws.com:443` }];
getIceServerConfigResponse.IceServerList.forEach(iceServer =>
iceServers.push({
urls: iceServer.Uris,
username: iceServer.Username,
credential: iceServer.Password,
}),
);
console.log('ICE SERVERS: ', iceServers);
// Create RTCPeerConnection
// The RTCPeerConnection is the primary interface for WebRTC communications in the Web.
const peerConnection = new RTCPeerConnection({ iceServers });
// Create WebRTC Signaling Client
// This is the actual client that is used to send messages over the signaling channel.
const signalingClient = new SignalingClient({
channelARN,
channelEndpoint: endpointsByProtocol.WSS,
role: Role.MASTER,
region,
clientId,
credentials: {
accessKeyId,
secretAccessKey,
},
systemClockOffset: coachingSession.config.systemClockOffset
});
// GET THE USER MEDIA DEVICES
const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).catch(e => {
console.log("COULD NOT FIND WEBCAM");
setShowErrorStartingVideoModal(true);
});
// *** AUDIO & VIDEO DEVICE COLLECTION ***
let audioInputDevices: MediaDeviceInfo[];
let audioOutputDevices: MediaDeviceInfo[];
let videoInputDevices: MediaDeviceInfo[];
try {
const mediaDevices = await navigator.mediaDevices.enumerateDevices();
audioInputDevices = mediaDevices.filter(device => device.kind === 'audioinput');
audioOutputDevices = mediaDevices.filter(device => device.kind === 'audiooutput');
videoInputDevices = mediaDevices.filter(device => device.kind === 'videoinput');
setMicrophoneList(audioInputDevices);
setSpeakerList(audioOutputDevices);
setCameraList(videoInputDevices);
} catch (e) {
console.log(e);
console.log("ERROR COLLECTING MEDIA DEVICE INFORMATION: MAKE SURE PERMISSIONS ARE ALLOWED AND TRY AGAIN");
};
// GRAB THE LOCAL PROVIDER AND PATIENT VIDEO TILES
const providerVideoTile: HTMLVideoElement = document.getElementById('provider-video-element') as HTMLVideoElement;
const patientVideoElement = document.getElementById('patient-video-element') as HTMLVideoElement;
// let dataChannel: RTCDataChannel
// Add Signaling Client Event Listeners
signalingClient.on('open', async () => {
if (!localStream || !peerConnection) return;
// Get a stream from the webcam, add it to the peer connection, and display it in the local view
try {
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
providerVideoTile.srcObject = localStream;
} catch (e) {
// Could not find webcam
console.log(e);
return;
};
// Create an SDP offer and send it to the master
const offer = await peerConnection.createOffer({
offerToReceiveAudio: true,
offerToReceiveVideo: true
});
console.log('CREATED OFFER: ', offer);
await peerConnection.setLocalDescription(offer);
if (peerConnection.localDescription) signalingClient.sendSdpOffer(peerConnection.localDescription, patient.patientID);
});
// When the SDP answer is received back from the master, add it to the peer connection.
signalingClient.on('sdpAnswer', async answer => {
console.log('RECEIVED ANSWER: ', answer);
if (!peerConnection) return;
await peerConnection.setRemoteDescription(answer).catch(e => console.log(e));
});
signalingClient.on('sdpOffer', async (offer, senderClientID) => {
console.log({ offer });
if (!peerConnection) return;
await peerConnection.setRemoteDescription(offer).catch(e => console.log(e));
console.log('REMOTE DESCRIPTION SET: ', peerConnection);
const answer = await peerConnection.createAnswer().catch(e => console.log(e));
console.log({ answer });
if (answer) signalingClient.sendSdpAnswer(answer, senderClientID);
// dataChannel = peerConnection.createDataChannel(`data-channel-of-${senderClientID}`);
// dataChannel.addEventListener("open", (event) => {
// console.log(event);
// dataChannel.send('******HI ALEC*******');
// });
});
// When an ICE candidate is received from the master, add it to the peer connection.
signalingClient.on('iceCandidate', async (candidate, senderClientID) => {
if (!peerConnection) return;
console.log('new iceCandidate received:', candidate);
await peerConnection.addIceCandidate(candidate).catch(e => console.log(e));
console.log("ICE CANDIDATE ADDED: ", candidate);
});
signalingClient.on('close', async () => {
if (!localStream) return;
// Handle client closures
console.log("ENDING THE CALL");
localStream.getTracks().forEach(track => track.stop());
peerConnection.close();
if ('srcObject' in providerVideoTile) providerVideoTile.srcObject = null;
});
signalingClient.on('error', error => {
// Handle client errors
console.log(error);
});
signalingClient.on('chat', (dataMessage: any) => {
const decodedMessage = UTF8Decoder.decode(new Uint8Array(dataMessage.data));
console.log("GOT TEST MESSAGE:", decodedMessage);
});
signalingClient.on('SeriesData', (dataMessage: any) => {
const seriesFromMobile = JSON.parse(UTF8Decoder.decode(new Uint8Array(dataMessage.data)));
console.log("SERIES FROM MOBILE:", seriesFromMobile);
kickOffSeriesCreation(seriesFromMobile);
});
signalingClient.on('EffortMarker', (dataMessage: any) => {
const effortMarker = UTF8Decoder.decode(new Uint8Array(dataMessage.data));
console.log("EFFORT MARKER:", effortMarker);
setEffortMarker(effortMarker);
});
signalingClient.on('CoachingMessage', async (dataMessage: any) => {
const coachingMessage = UTF8Decoder.decode(new Uint8Array(dataMessage.data));
console.log("COACHING MESSAGE FROM MOBILE:", coachingMessage);
if (coachingMessage === 'EndSeries') {
await handleForceEndEffort(signalingClient);
await handleEndSeries(signalingClient);
};
});
// Add Peer Connection Event Listeners
// Send any ICE candidates generated by the peer connection to the other peer
peerConnection.addEventListener('icecandidate', ({ candidate }) => {
if (candidate) {
console.log(candidate);
signalingClient.sendIceCandidate(candidate, patient.patientID);
} else {
// No more ICE candidates will be generated
console.log('NO MORE ICE CANDIDATES WILL BE GENERATED');
}
});
// As remote tracks are received, add them to the remote view
peerConnection.addEventListener('track', event => {
// if (patientVideoElement.srcObject) return;
setNoPatientConnected(false);
console.log({ event });
try {
peerConnection.addTrack(event.track, event.streams[0]);
if (event.track.kind === 'video') patientVideoElement.srcObject = event.streams[0];
} catch (e) {
console.log(e);
}
});
// Open Signaling Connection
signalingClient.open();
};
Try this this page, You can use master on one computer and viewer on other.
https://awslabs.github.io/amazon-kinesis-video-streams-webrtc-sdk-js/examples/index.html
For anyone else with the same issue, I managed to find the master example on this github repo and was able to get it working

How do i integrate pubnub in mobile devices

i am trying to integarte pubnub(replacing socket.io withh pubnub).here below i have shown code.it is working fine for pc.but in mobile devices its not working .i am not getting error as well.any body can tel what i done wrong.
till now i have tried to replace socket with my pubnub connection
import PubNub from 'pubnub';
import { PubNubProvider, usePubNub } from 'pubnub-react';
const pubnub = new PubNub({
publishKey: 'xxxxxxxxxxxx',
subscribeKey: 'xxxxxxxxxxx'
});
//leave Table when close window
const closingCode = () => {
sendMsg("leaveTable");
return null;
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
this.receiveMsg = []
}
window.onbeforeunload = closingCode;
// Read res from service via Socket IO
// socket.on("message", receiveMsg);
socket.on("message", text => {
let params = text.split("|"); //.map(p => Base64.Decode(p)); // we are not using b64 now
let message = params.shift(); // message, eg. playerSitOut, clearTable
this.receiveMsg.push(message);
this.props.updateMessage({ message, params });
});
pubnub.addListener({ message: function (m) {
const channelName = m.channel; // Channel on which the message was published
const channelGroup = m.subscription; // Channel group or wildcard subscription match (if exists)
const pubTT = m.timetoken; // Publish timetoken
const msg = m.message; // Message payload
const publisher = m.publisher; // Message publisher/ } });
//pubnub.subscribe();
}

Can't send POST request from react method

I am creating a FB like app with Django and react. In the main page there's a feed where users can create posts and post them to the feed.
I am trying to implement the delete post functionality but I am running into some difficulties.
The logic is:
The user clicks on the delete button on a post and the Browser sends and XMLHttpeRequest to the server to delete that post.
Here's the react component:
class Feed extends React.Component {
constructor(props){
super(props);
this.state = {
profile_pic: profile_pic_url,
user: username,
posts: posts_from_server,
}
}
handleClick() {
const text = document.querySelector('#new_post_text').value;
if (text.length > 1) {
const data = {author: username, text: text}
// send that post to the server to save it
const csrftoken = Cookies.get('csrftoken');
const request = new XMLHttpRequest();
request.open('POST', '/create_new_post', true);
request.setRequestHeader('X-CSRFToken', csrftoken);
request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
request.onload = () => {
const response = JSON.parse(request.responseText)
this.setState({
posts : [{author: response.author, author_picture: profile_pic_url, text: response.text, date: response.date}, ...this.state.posts]
})
document.querySelector("#new_post_text").value = '';
console.log(response)
}
request.send(JSON.stringify(data))
}
}
deletePost(post_id, author) {
const post = document.getElementById(post_id)
post.style.animationPlayState = 'running';
setTimeout(() =>{
this.setState({
posts: this.state.posts.filter(post => post.id != post_id)
})
}, 1000)
// delete the post from the server
const data = {'post_author': author, 'id': post_id}
const csrftoken = Cookies.get('csrftoken');
const request = new XMLHttpRequest();
request.open('POST', '/delete_post', true);
request.setRequestHeader('X-CSRFToken', csrftoken);
request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
request.onload = () => {
response = JSON.parse(request.responseText);
console.log(response)
}
request.send(JSON.stringify(data))
}
render() {
return (
<div>
<Post_generator
current_user={this.state.user}
picture={this.state.profile_pic}
onClick={() => this.handleClick()} />
{this.state.posts.map(post => <Post
onClick={() => this.deletePost(post.id, post.author)}
key={post.id}
post_id={post.id}
current_user={this.state.user}
user={post.author}
profile_pic={post.author_picture}
text={post.text}
date={post.date}
/>)}
</div>
)
}
}
this is urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='login'),
path('login', views.login_view, name='login'),
path('index', views.index, name='index'),
path('register', views.register_view, name='register'),
path('logout', views.logout_view, name='logout'),
path('profile', views.profile, name='profile'),
path('change_profile_pic', views.change_profile_pic, name='upload_profile_pic'),
path('create_new_post', views.create_new_post, name='create_new_post'),
path('<str:friend>', views.friends_profile, name='friends_profile'),
path('delete_post', views.delete_post_function, name='delete_post'),
]
and these are the views in views.py handling the two requests:
#login_required
#require_http_methods(['POST'])
def create_new_post(request):
data = json.loads(request.body.decode("utf-8"))
print(data)
new_post = Post(author=request.user, text=data['text'])
new_post.save()
data['date']= new_post.date
return JsonResponse(data)
# delete post
#login_required
#require_http_methods(['POST'])
def delete_post_function(request):
print('sdfgdfsghdsfkgjsdklfg')
response = {"response": "data arrived to server"}
return JsonResponse(response)
The thing that is giving me trouble is the deletePost() method...
When it is executed I get a 405 error in the console.
The funny thing is that the XMLHTTPRequest in the handleClick method works just fine
Ok I figured it out...if the delete_post path in urls.py is not at the end of the urlspatterns list the code works...

Resources