I need your help about Websocket with iOS.
I am trying to connect to websocket server using Node.js from iOS client using Socket.IO-Client-Swift, but it seems Node.js websocket server does not recognize access from iOS client.
/etc/nginx/conf.d/rsp.arakaki.app.conf
(Configuration file for nginx)
Websocket server is running on port 3000, so requests to /socket.io/ will be proxied to upstream websocket (server localhost:3000;).
upstream php-fpm {
server localhost:9000;
}
upstream websocket {
server localhost:3000;
}
server {
server_name rsp.arakaki.app;
listen 80;
return 301 https://$host$request_uri;
}
server {
server_name rsp.arakaki.app;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/rsp.arakaki.app/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rsp.arakaki.app/privkey.pem;
root /var/www/rsp.arakaki.app/webroot;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location /socket.io/ {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_pass php-fpm;
fastcgi_index index.php;
fastcgi_intercept_errors on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
/var/www/rsp.arakaki.app/websocket/src/server.js
(Source file for websocket server)
I suppose that websocket server logs some messages when websocket client successfully connected to the server. But I can only see the messages when I use Web client. I cannot see any message when I use iOS client.
const fs = require("fs");
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const io = require("socket.io")(server);
io.on("connection", (socket) => {
console.log("a user connected");
socket.on("disconnecting", (reason) => {
console.log("user disconnecting", { reason });
});
socket.on("disconnect", (reason) => {
console.log("user disconnected", { reason });
});
});
server.listen(3000, () => {
console.log("server running...");
});
MainViewController.swift
(iOS websocket client)
Websocket server logs nothing with this code.
I think it is because of iOS client since I succeeded to connect from web client.
I have no idea.
import UIKit
import SocketIO
class MainViewController: UIViewController {
var manager: SocketManager!
var socket: SocketIOClient!
override func viewDidLoad() {
super.viewDidLoad()
manager = SocketManager(socketURL: URL(string: "https://rsp.arakaki.app/")!, config: [.log(true), .forceWebsockets(true)])
socket = manager?.defaultSocket
socket.on(clientEvent: .connect) { (data, ack) in
print("socket connected")
self.socket.emit("join", ["hoge"])
}
socket.on(clientEvent: .error, callback: { (data, ack) in
print("socket error")
})
socket.connect(timeoutAfter: 3, withHandler: {
print("socket timeout")
})
}
}
websocket.php
(Web websocket client)
This script works well. Websocket server logs as connected.
<script src="https://rsp.arakaki.app/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>
Environment
CentOS 7
Nginx (1.18.0)
Node.js (14.15.3)
socket.io (3.0.4)
express (4.17.1)
Swift 5
Socket.IO-Client-Swift (15.2.0)
Xcode (12.3)
Thank you for your interest. Any comments are welcome. Please help me.
Socket.IO-Client-Swift could connect to server after I downgraded socket.io library for nodejs server to version 2.0.4. Thank you.
Your "Socket.IO-Client-Swift" version is 15.2, it is not compatible with socket.io server's 3.0 version.
"The client now supports socket.io 3 servers." is written in "Upgrading from v15 to v16" guide: https://nuclearace.github.io/Socket.IO-Client-Swift/15to16.html
You must use v16 minimum for server 3.0 version, but already not released. Probably it is coming soon.
So using the socket.io server's 2 version it is good solution for as now.
You can look version tags for ios client at the below link:
https://github.com/socketio/socket.io-client-swift/tags
Related
I have 2 servers, one with dockerized nginx and one with 3 dockerized web apis allowing traffic through different ports (say 441, 442, 443) which has swagger UI along with it respectively.
with limited knowledge on nginx, I am trying to reverse proxy to all the swagger UI endpoints using the nginx container. This is how my nginx conf looks like, but it doesnt work as expected, it would be great if someone can advice where I am going wrong.
I am able to hit the service with the exact match location context /FileService which return index.html. But index.html has the script call where nginx fails to serve these static contents.
index.html
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
nginx.conf
server {
listen 443 ssl http2;
server_name www.webby.com;
access_log /var/log/nginx/access.log;
ssl_certificate /etc/ssl/yyyy.crt;
ssl_certificate_key /etc/ssl/xxxx.key;
ssl_protocols TLSv1.2;
if ($http_referer = 'https://$host/FileService') {
rewrite ^/(\w+) /swagger/fileservice/$1;
}
if ($http_referer = 'https://$host/PreProcess') {
rewrite ^/(\w+) /swagger/preprocess/$1;
}
location = /FileService {
proxy_pass 'http://appy.com:441/swagger/index.html';
}
location = /PreProcess {
proxy_pass 'http://appy.com:442/swagger/index.html';
}
# curl http://appy.com:441/swagger/swagger-ui-bundle.js is giving the js on this container
location ~* /swagger/fileservice(.*) {
proxy_pass 'http://appy.com:441/swagger/$1';
}
location ~* /swagger/preprocess(.*) {
proxy_pass 'http://appy.com:442/swagger/$1';
}
}
accesslog on the nginx looks like
anyways I struggled my way to implement this. Not sure if its the right approach (because I read on the internet that if block inside location context is evil), but works for my case. Feel free to correct my answer
server {
listen 443 ssl http2;
server_name www.webby.com;
access_log /var/log/nginx/access.log;
ssl_certificate /etc/ssl/yyyy.crt;
ssl_certificate_key /etc/ssl/xxxx.key;
ssl_protocols TLSv1.2;
location = /FileService {
proxy_pass 'http://appy.com:441/swagger/index.html';
}
location = /PreProcess {
proxy_pass 'http://appy.com:442/swagger/index.html';
}
location ~ ^/swagger/(.*)$ {
if ($http_referer = 'https://$host/FileService') {
proxy_pass 'http://appy.com:441/swagger/$1';
}
if ($http_referer = 'https://$host/PreProcess') {
proxy_pass 'http://appy.com:442/swagger/$1';
}
}
location ~ ^/swagger(.*)$ {
if ($http_referer = 'https://$host/FileService') {
proxy_pass 'http://appy.com:441/swagger/swagger$1';
}
if ($http_referer = 'https://$host/PreProcess') {
proxy_pass 'hhttp://appy.com:442/swagger/swagger$1';
}
}
}
I have 2 API containers (docker) running on port 10000 and 10003. I want to reverse proxy both of them so the API can be called from a single port which is port 80. I am trying to use NGINX to do that and this is my nginx configuration file:
worker_processes 1;
events { worker_connections 1024; }
http {
server {
listen 80;
server_name container1;
location / {
proxy_pass http://10.10.10.50:10003;
}
}
server {
listen 80;
server_name container2;
location / {
proxy_pass http://10.10.10.50:10000;
}
}
}
I found that it is only working on the container 1 and if there is a request for container 2, it will generate 404 not found warning because the request go to the container 1 instead of container 2.
Finally, I found a solution using NGINX. All I need to do is to create a new NGINX container then reconfigure the url of my 2 API container. The configuration file that I wrote looks like this:
worker_processes auto;
events { worker_connections 1024; }
http {
upstream container1 {
server 10.10.10.50:10003;
}
upstream container2 {
server 10.10.10.50:10000;
}
server {
listen 80;
location /container1/ {
proxy_pass http://container1/;
}
location /container2/ {
proxy_pass http://container2/;
}
}
}
Now, I can make requests for both API containers by using port 80 as it will be re-routed from the port into the designated port (reverse-proxy).
app.somename.local is not working, but localhost is proxies to app:3000. I want to access to app via typing server_name into browser
worker_processes 1;
events {
worker_connections 1024;
}
http {
upstream app {
server app:3000;
}
server {
server_name app.somename.local;
location / {
proxy_pass http://app/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
}
}
}
app.somename.local
Your shooting at wrong target. You need to get that server name resolved.
Do this.
127.0.0.1 app.somename.local >> /etc/hosts
And it should work.
I'm trying to setup my production server to use faye messages using nodejs and HTTPS, but no luck.
What I have until now is:
A faye + nodejs server setup file:
var https = require('https');
var faye = require('faye');
var fs = require('fs');
var options = {
key: fs.readFileSync('/etc/httpd/ssl/example.com.key'),
cert: fs.readFileSync('/etc/httpd/ssl/example.com.crt'),
ca: fs.readFileSync('/etc/httpd/ssl/ca_bundle.crt')
};
var server = https.createServer(options);
var bayeux = new faye.NodeAdapter({mount: '/faye', timeout: 60});
bayeux.attach(server);
server.listen(8000);
A rails helper to send messages:
def broadcast(channel, &block)
message = {:channel => channel, :data => capture(&block)}
uri = URI.parse(Rails.configuration.faye_url)
Net::HTTPS.post(uri, message.to_json)
end
A javascript function to open a listener:
function openListener(channel, callback){
var faye_client = new Faye.Client("<%= Rails.configuration.faye_url %>");
faye_client.subscribe(channel , callback);
return faye_client;
}
My faye url config in production.rb:
config.faye_url = "https://example.com:8000/faye"
And finally, a call in my page javascript:
fayeClient = openListener("my_channel" , function(data) {
//do something...
});
Everything was working when testing over http on development machine. But in production don't.
If I point browser to https://example.com:8000/faye.js I got the correct javascript file.
What could be happen?
The problem was with Apache server.
I had switch to nginx and now it´s working.
However, I need to make some configurations:
Faye + node.js setup file:
var http = require('http'),
faye = require('faye');
var server = http.createServer(),
bayeux = new faye.NodeAdapter({mount: '/faye', timeout: 60});
bayeux.attach(server);
server.listen(8000);
Rails helper:
def broadcast(channel, &block)
message = {:channel => channel, :data => capture(&block)}
uri = URI.parse(Rails.configuration.faye_url)
Net::HTTP.post_form(uri, :message => message.to_json)
end
Faye url:
https://example.com/faye
And finally, nginx config
server {
# Listen on 80 and 443
listen 80;
listen 443 ssl;
server_name example.com;
passenger_enabled on;
root /home/rails/myapp/public;
ssl_certificate /home/rails/ssl/myapp.crt;
ssl_certificate_key /home/rails/ssl/myapp.key;
# Redirect all non-SSL traffic to SSL.
if ($ssl_protocol = "") {
rewrite ^ https://$host$request_uri? permanent;
}
location /faye {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Short words: nginx convert https requests in /faye address, to http in port 8000.
Use default http in server side, and https in client side.
Using the websocket-rails gem, I'm able to successfully get a websocket connection straight through puma in development, however, when deployed to production and attempting to access the websocket through nginx (passing off to puma) I have a couple of errors: one in the nginx error log:
[info] 14340#0: *7 upstream timed out (110: Connection
timed out) while proxying upgraded connection, client: 123.45.67.89, server:
foo.com, request: "GET /websocket HTTP/1.1", upstream:
"http://unix:///opt/oneconnect/shared/tmp/sockets/puma.sock:/websocket", host:
"foo.com"
... and one on the javascript console:
WebSocket connection to 'ws://foo.com/websocket' failed: Error during WebSocket handshake: Unexpected response code: 301
I found that nginx (the version I'm using is 1.4.6) is capable of websocket use but requires special configuration, which I've already attemped (getting the errors above). Here's my nginx.conf:
upstream oneconnect {
server unix:///opt/oneconnect/shared/tmp/sockets/puma.sock;
}
server {
listen 80;
listen 443 ssl;
#ssl on;
ssl_certificate /etc/ssl/foo.com.crt;
ssl_certificate_key /etc/ssl/foo.com.key;
root /opt/oneconnect/current/public;
try_files $uri #oneconnect;
access_log /opt/oneconnect/current/log/nginx.access.log;
error_log /opt/oneconnect/current/log/nginx.error.log info;
server_name foo.com;
location ~ ^/(assets)/ {
root /opt/oneconnect/current/public;
gzip_static on;
expires max;
add_header Cache-Control public;
}
location /websocket/ {
proxy_pass http://oneconnect;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location #oneconnect {
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://oneconnect;
}
}
I'm assuming that I'm missing something simple, but I'm stumped at this point and have Googled until my eyes started bleeding. If anyone could help it would be much appreciated, or maybe just point me to how to debug these connections (it seems hard to get debug info from a ws connection). Thanks for your time.
Assuming u have already initializer for eventmachine
config/initializers/eventmachine.rb
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
nginx site conf:
upstream puma_project_production {
server unix:/var/www/project/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 504 /500.html;
error_page 503 #503;
server_name localhost project.local;
root /var/www/project/current/public;
try_files $uri/index.html $uri #puma_project_production;
location #puma_project_production {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://puma_project_production;
# limit_req zone=one;
access_log /var/www/project/shared/log/nginx.access.log;
error_log /var/www/project/shared/log/nginx.error.log;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location = /50x.html {
root html;
}
location = /404.html {
root html;
}
location #503 {
error_page 405 = /system/maintenance.html;
if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html break;
}
rewrite ^(.*)$ /503.html break;
}
if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
return 405;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
location /websocket {
proxy_pass http://puma_project_production;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location ~ \.(php|rb)$ {
return 405;
}
}