firecamp icon indicating copy to clipboard operation
firecamp copied to clipboard

[bug]: Socket.IO v4 sending EIO=3 instead of EIO=4 in Firecamp (v3.3.0-beta.3)

Open w20k opened this issue 10 months ago • 0 comments

Socket.IO Connection Issue in Firecamp

Describe the Bug

When attempting to connect to a Socket.IO server (v4) using Firecamp, the connection fails with an "Unsupported protocol version" error. Firecamp is attempting to use Engine.IO protocol version 3 (EIO=3) while the server is expecting version 4 (EIO=4), resulting in a protocol version mismatch.

To Reproduce

Steps to reproduce the behavior:

  1. Set up a Socket.IO v4 server on localhost:5000 (added dummy server .js below)
  2. Open Firecamp and create a new Socket.IO connection
  3. Enter http://localhost:5000 as the connection URL
  4. Attempt to connect
  5. Connection fails with error: "Unsupported protocol version"

Expected Behavior

Firecamp should either:

  • Protocol version should be either EIO=3 for v2 and v3, or EIO=4 for v4 (would be great to see that in connection url)
  • Default to the latest protocol version (EIO=4) for Socket.IO connections

Screenshots

Error from server logs:

Engine.IO connection error: {
  "code": 5,
  "message": "Unsupported protocol version",
  "context": {
    "protocol": 3
  }
}

Request headers showing EIO=3:

url: "/socket.io/?EIO=3&transport=websocket"

Desktop Information

  • OS: macOS 10.15.7
  • Application: Firecamp v3.3.0-beta.3
  • Electron: 29.1.0
  • Chrome: 122.0.6261.70

Note:

Maybe, you need to add one more configuration or change something somewhere, but as a User I thought it should be done via dropdown where you chose version? No?;

Couldn't see exactly what's happening in the code, because part of it is not accessible as it seems: io: { v2: SocketIOv2, v3: SocketIOv3, v4: SocketIOv4 },

Test server

Click me

Socket.IO Server Javascript

const http = require('node:http')
const { Server } = require('socket.io')

// Create HTTP server
const server = http.createServer((req, res) => {
  console.log(`HTTP Request: ${req.method} ${req.url}`)

  // Add CORS headers to all responses
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type')

  if (req.method === 'OPTIONS') {
    res.writeHead(204)
    res.end()
    return
  }

  res.writeHead(200, { 'Content-Type': 'text/plain' })
  res.end('Socket.IO test server running. This is a plain HTTP endpoint.')
})

// Create Socket.IO server with compatibility for v3 and v4
const io = new Server(server, {
  // Allow both v3 and v4 clients
  allowEIO3: true,

  // Ping configuration
  pingTimeout: 60000, // How long to wait after a ping before considering the connection closed
  pingInterval: 25000, // How often to ping the client

  // Connection timeout
  connectTimeout: 60000, // How long to wait for a connection to establish

  // Transport configuration
  transports: ['polling', 'websocket'],
  upgradeTimeout: 30000, // How long to wait for transport upgrade

  // CORS configuration
  cors: {
    origin: '*',
    methods: ['GET', 'POST', 'OPTIONS'],
    credentials: true,
  },

  // Additional options
  maxHttpBufferSize: 1e8, // 100 MB
  path: '/socket.io/', // Default path
  serveClient: true, // Serve client files
})

// Debug middleware for all Socket.IO connections
io.use((socket, next) => {
  console.log('Connection attempt:')
  console.log('  IP:', socket.handshake.address)
  console.log('  Transport:', socket.conn.transport.name)
  console.log('  Headers:', JSON.stringify(socket.handshake.headers))
  console.log('  Query:', JSON.stringify(socket.handshake.query))
  next()
})

// Socket.IO connection handler
io.on('connection', (socket) => {
  console.log(`Client connected: ${socket.id}`)
  console.log(`Transport: ${socket.conn.transport.name}`)

  // Send immediate welcome message
  socket.emit('welcome', {
    message: 'Connected to Socket.IO server',
    socketId: socket.id,
    timestamp: new Date().toISOString(),
  })

  // Set up a ping interval for this specific socket
  const pingInterval = setInterval(() => {
    console.log(`Pinging client ${socket.id}...`)

    // Custom ping implementation only - don't use socket.conn.ping()
    socket.emit('ping_from_server', { timestamp: new Date().toISOString() })

    // The built-in ping is handled automatically by Socket.IO
    // No need to call socket.conn.ping() manually
  }, 20000)

  // Handle custom ping response
  socket.on('pong_from_client', (data) => {
    console.log(`Received pong from ${socket.id}:`, data)
  })

  // Handle message event
  socket.on('message', (data) => {
    console.log(`Message from ${socket.id}:`, data)
    socket.emit('response', {
      echo: data,
      timestamp: new Date().toISOString(),
    })
  })

  // Handle any event
  socket.onAny((event, ...args) => {
    if (event !== 'message' && event !== 'ping_from_server' && event !== 'pong_from_client') {
      console.log(`Event "${event}" from ${socket.id}:`, args)
      socket.emit(`${event}_response`, {
        received: args,
        timestamp: new Date().toISOString(),
      })
    }
  })

  // Handle errors
  socket.on('error', (error) => {
    console.error(`Socket error for ${socket.id}:`, error)
  })

  // Handle disconnection
  socket.on('disconnect', (reason) => {
    console.log(`Client disconnected: ${socket.id}, reason: ${reason}`)
    clearInterval(pingInterval) // Clean up ping interval
  })
})

// Engine.IO errors
io.engine.on('connection_error', (err) => {
  console.error('Engine.IO connection error:', err)
})

// Start the server
const PORT = 5000
server.listen(PORT, '0.0.0.0', () => {
  console.log(`Socket.IO server running on port ${PORT}`)
  console.log(`Server time: ${new Date().toISOString()}`)
  console.log('Supports both Socket.IO v3 and v4 clients')
  console.log('Enhanced ping configuration enabled')
  console.log('Connect with Socket.IO client using:')
  console.log(`  - URL: http://localhost:${PORT}`)
})

// Handle server shutdown
process.on('SIGINT', () => {
  console.log('Shutting down server...')
  io.close()
  server.close()
  process.exit(0)
})

w20k avatar Mar 27 '25 09:03 w20k