deepstreamIO / deepstream.io

deepstream.io server
https://deepstreamio.github.io
MIT License
7.14k stars 381 forks source link

TypeError: Cannot read property 'add' of undefined #1073

Closed davidbehler closed 3 years ago

davidbehler commented 4 years ago

Might be related to deepstreamIO/deepstream.io-client-js/issues/528, but not sure.

What I did:

/usr/local/bin/dist/src/connection-endpoint/base/connection-endpoint.js:151
                this.clientVersions[msg.sdkType].add(msg.sdkVersion);
                                                 ^
TypeError: Cannot read property 'add' of undefined
    at WSBinaryConnectionEndpoint.processConnectionMessage (/usr/local/bin/dist/src/connection-endpoint/base/connection-endpoint.js:151:50)
    at WebSocket.<anonymous> (/usr/local/bin/dist/src/services/http/node/node-http.js:109:35)
    at WebSocket.emit (events.js:223:5)
    at Receiver.receiverOnMessage (/usr/local/bin/node_modules/ws/lib/websocket.js:800:20)
    at Receiver.emit (events.js:223:5)
    at Receiver.dataMessage (/usr/local/bin/node_modules/ws/lib/receiver.js:427:14)
    at Receiver.getData (/usr/local/bin/node_modules/ws/lib/receiver.js:366:17)
    at Receiver.startLoop (/usr/local/bin/node_modules/ws/lib/receiver.js:142:22)
    at Receiver._write (/usr/local/bin/node_modules/ws/lib/receiver.js:77:10)
    at doWrite (_stream_writable.js:435:12)

My config.yml:

# General
# Each server within a cluster needs a unique name. Set to UUID to have deepstream autogenerate a unique id
serverName: UUID
# Show the deepstream logo on startup
showLogo: true
# Plugin startup timeout – deepstream init will fail if any plugins fail to emit a 'done' event within this timeout
dependencyInitializationTimeout: 5000
# Directory where all plugins reside
libDir: /etc/deepstream/lib
# Exit the process a fatal error occurs, like losing a cache connection
exitOnFatalError: false

# This disables specific feature in DS, which is a more performant way
# than disabling via permissions and is also how telemetry figures out
# what features are enabled
enabledFeatures:
  record: true
  event: true
  rpc: true
  presence: true

telemetry:
  type: deepstreamIO
  options:
    # Disable telemetry entirely
    enabled: false
    # Prints whatever will be sent to the telemetry endpoint,
    # without actually sending it
    debug: false
    # An anonymous uuid that allows us to know its one unique
    # deployment. Please don't generate these randomly if using
    # node, it really skews up analytics. 
    # deploymentId: <uuid goes here>

rpc:
  # Timeout for client RPC acknowledgement
  ackTimeout: 1000
  # Timeout for actual RPC provider response
  responseTimeout: 10000

record:
  # Maximum time permitted to fetch from cache
  cacheRetrievalTimeout: 30000
  # Maximum time permitted to fetch from storage
  storageRetrievalTimeout: 30000
  # A list of prefixes that, when a record starts with one of the prefixes the
  # records data won't be stored in the db
  # storageExclusionPrefixes:
  #   - no-storage/
  #   - temporary-data/
  # A list of prefixes that, when a record is updated via setData and it matches one of the prefixes
  # it will be permissioned and written directly to the cache and storage layers
  # storageHotPathPrefixes:
  #   - analytics/
  #   - metrics/

  # Invalid configuration: data should NOT have additional properties
listen:
  # Try finding a provider randomly rather than by the order they subscribed to.
  shuffleProviders: true
  # The amount of time to wait for a provider to acknowledge or reject a listen request
  responseTimeout: 500
  # The amount of time before trying to reattempt finding matches for subscriptions. This
  # is not a cheap operation so it's recommended to raise keep this at minutes rather then
  # second intervals if you are experiencing heavy loads
  rematchInterval: 60000
  # The amount of time a server will refuse to retry finding a subscriber after a previously
  # failed attempt. This is used to avoid servers constantly trying to find a match without a
  # cooldown period
  matchCooldown: 10000

httpServer:
  type: default
  options:
    # url path for http health-checks, GET requests to this path will return 200 if deepstream is alive
    healthCheckPath: /health-check
    # -- CORS --
    # if disabled, only requests with an 'Origin' header matching one specified under 'origins'
    # below will be permitted and the 'Access-Control-Allow-Credentials' response header will be
    # enabled
    allowAllOrigins: true
    # a list of allowed origins
    origins:
      - 'https://example.com'
    # Options required to create an ssl app
    ssl:
      key: fileLoad(/etc/letsencrypt/live/example.com/privkey.pem)
      cert: fileLoad(/etc/letsencrypt/live/example.com/cert.pem)
    #   ca: ...

  # type: uws
  # options:
  #   # url path for http health-checks, GET requests to this path will return 200 if deepstream is alive
  #   healthCheckPath: /health-check
  #   # Headers to copy over from websocket
  #   headers:
  #     - user-agent
  #   # Options required to create an ssl app
  #   # ssl:
  #   #   key: file(ssl/key.pem)
  #   #   cert: file(ssl/cert.pem)
  #   ##  dhParams: ...
  #   ##  passphrase: ...

# Connection Endpoint Configuration
# to disable, replace configuration with null eg. `http: null`
connectionEndpoints:
  - type: ws-binary
    options:
      # url path websocket connections connect to
      urlPath: /deepstream
      # the amount of milliseconds between each ping/heartbeat message
      heartbeatInterval: 30000
      # the amount of milliseconds that writes to sockets are buffered
      outgoingBufferTimeout: 10
      # the maximum amount of bytes to buffer before flushing, stops the client from large enough packages
      # to block its responsiveness
      maxBufferByteSize: 100000

      # Security
      # amount of time a connection can remain open while not being logged in
      unauthenticatedClientTimeout: 180000
      # invalid login attempts before the connection is cut
      maxAuthAttempts: 3
      # maximum allowed size of an individual message in bytes
      maxMessageSize: 1048576

  - type: ws-text
    options:
      # url path websocket connections connect to
      urlPath: /deepstream-v3
      # the amount of milliseconds between each ping/heartbeat message
      heartbeatInterval: 30000
      # the amount of milliseconds that writes to sockets are buffered
      outgoingBufferTimeout: 10
      # the maximum amount of bytes to buffer before flushing, stops the client from large enough packages
      # to block its responsiveness
      maxBufferByteSize: 100000

      # Security
      # amount of time a connection can remain open while not being logged in
      unauthenticatedClientTimeout: 180000
      # invalid login attempts before the connection is cut
      maxAuthAttempts: 3
      # maximum allowed size of an individual message in bytes
      maxMessageSize: 1048576

  - type: ws-json
    options:
      # url path websocket connections connect to
      urlPath: /deepstream-json
      # the amount of milliseconds between each ping/heartbeat message
      heartbeatInterval: 30000
      # the amount of milliseconds that writes to sockets are buffered
      outgoingBufferTimeout: 10
      # the maximum amount of bytes to buffer before flushing, stops the client from large enough packages
      # to block its responsiveness
      maxBufferByteSize: 100000

      # Security
      # amount of time a connection can remain open while not being logged in
      unauthenticatedClientTimeout: 180000
      # invalid login attempts before the connection is cut
      maxAuthAttempts: 3
      # maximum allowed size of an individual message in bytes
      maxMessageSize: 1048576

  - type: http
    options:
      # allow 'authData' parameter in POST requests, if disabled only token and OPEN auth is
      # possible
      allowAuthData: true
      # enable the authentication endpoint for requesting tokens/userData.
      # note: a custom authentication handler is required for token generation
      enableAuthEndpoint: false
      # path for authentication requests
      authPath: /api/auth
      # path for POST requests
      postPath: /api
      # path for GET requests
      getPath: /api
      # maximum allowed size of an individual message in bytes
      maxMessageSize: 1024

  # - type: mqtt
  #   options:
  #       # port for the mqtt server
  #       port: 1883
  #       # host for the mqtt server
  #       host: 0.0.0.0
  #       # timeout for idle devices
  #       idleTimeout: 60000

# Logger Configuration
logger:
  # use the default logger, this does not currently support meta objects
  type: default
  options:
    colors: true
    # Log messages with this level and above. Valid levels are DEBUG, INFO, WARN, ERROR, OFF
    logLevel: INFO

  # log using json, this supports meta objects
  # name: pino
  # options:
  # # value of logLevel (line 4) will always overwrite this value
  #   logLevel: INFO

  # name: winston
  # options:
  #   transports:
  #     # specify a list of transports (console, file, time)
  #     -
  #       type: console
  #       options:
  #         level: verbose
  #         colorize: true
  #     -
  #       type: file
  #       level: debug
  #       options:
  #         filename: 'logs.json'
  #     -
  #       type: time
  #       level: warn
  #       options:
  #         filename: time-rotated-logfile
  #         datePattern: .yyyy-MM-dd-HH-mm

# cache:
#   name: redis
#   options:
#     host: ${REDIS_HOST}
#     port: ${REDIS_PORT}

# storage:
#   name: mongodb
#   options:
#     connectionString: ${MONGO_CONNECTION_STRING}
#     db: default

# Authentication
auth:
  - type: none

  # # reading users and passwords from the storage layer
  # - type: storage
  #   options:
  #     # the table users are stored in storage
  #     table: Users
  #     # the split character used for tables (defaults to /)
  #     tableSplitChar: string
  #     # automatically create users if they don't exist in the database
  #     createUser: true
  #     # the name of a HMAC digest algorithm
  #     hash: 'md5'
  #     # the number of times the algorithm should be applied
  #     iterations: 100
  #     # the length of the resulting key
  #     # keyLength: 32

  # - type: file
  #   options:
  #     # Path to the user file. Can be json, js or yml
  #     users: fileLoad(users.yml)
  #     # the name of a HMAC digest algorithm
  #     hash: 'md5'
  #     # the number of times the algorithm should be applied
  #     iterations: 100
  #     # the length of the resulting key
  #     keyLength: 32

  # # getting permissions from a http webhook
  # - type: http
  #   options:
  #     # a post request will be send to this url on every incoming connection
  #     endpointUrl: https://someurl.com/validateLogin
  #     # any of these will be treated as access granted
  #     permittedStatusCodes: [ 200 ]
  #     # if the webhook didn't respond after this amount of milliseconds, the connection will be rejected
  #     requestTimeout: 2000
  #     # promote the following items from the login auth data into headers
  #     promoteToHeader:
  #       - token
  #     # the codes which the auth handler should retry. This is useful for when the API you depend on is
  #     # flaky or going through a not so blue/green deployment
  #     retryStatusCodes: [ 404, 504 ]
  #     # the maximum amount of retries before returning a false login
  #     retryAttempts: 3
  #     # the time in milliseconds between retries
  #     retryInterval: 5000

# Permissioning
permission:
  type: config
  options:
    # Permissions file
    permissions: fileLoad(permissions.yml)
    # Amount of times nested cross-references will be loaded. Avoids endless loops
    maxRuleIterations: 3
    # PermissionResults are cached to increase performance. Lower number means more loading
    cacheEvacuationInterval: 60000

monitoring:
  type: none

  # # Allows monitoring stats to be requested via HTTP, useful for polling agents
  # # such as LogStash
  # type: http
  # options:
  #   url: /monitoring
  #   allowOpenPermissions: false
  #   headerKey: deepstream-password2
  #   headerValue: deepstream-secret

  # # Logs monitoring stats, useful for kibana where you can visualize meta data
  # type: log
  # options:
  #   logInterval: 30000
  #   monitoringKey: DEEPSTREAM_MONITORING

# clusterNode:
#   type: default
#   options:
#     host: localhost
#     port: 6379

# Custom Plugins
# plugins:
#   custom:
#     path: '...'

#   heap-snapshot:
#     name: 'heap-snapshot'
#     options:
#       interval: 60000
#       outputDir: file(../heap-snapshots)

#   aws:
#     name: aws
#     options:
#       accessKeyId: ${AWS_ACCESS_KEY}
#       secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
#       services:
#         - type: s3-sync
#           options:
#             syncInterval: 60000
#             syncDir: file(../heap-snapshots)
#             bucketName: ${SYNC_BUCKET_NAME}
#             bucketRegion: ${AWS_DEFAULT_REGION}

Any idea what might be causing this and what can be done about it?

yasserf commented 4 years ago

This seems to be that the client cdn isn’t updated, and also that your not running the latest version.

https://github.com/deepstreamIO/deepstream.io/commit/347621d0bcc1302d8b9133c2cf8a148db2b0b9e2

5.1.1 was released shortly after 5.1 and has that fix.

davidbehler commented 4 years ago

Like I said in the other issue, I'm trying to use deepstream in the browser and the cdn is the only way to access the .js file. The release does only contain TypeScript files and I don't know how to compile them myself.

So I'll have to wait for the cdn to be updated?

And how can the client crash the server?

yasserf commented 4 years ago

The comment I made contained the commit with the issue, mentioned the fact it was fixed in 5.1.1

Please note that 5.1.1 is the server and not the client.

The client cdn is actually correct, since it’s sending down the new messages in the protocol.

The reason it’s crashing is because there are some new fields in the protocol and a guard wasn’t in place to support both cases.

2.0 was years ago, but we never bundled js files into the git release. Normally people would instead use npm and then reference ‘node_modules/@deepstream/client/dist/Client.min.js’ or something similar. Depends greatly on your client side development environment (script tags, bower, npm, etc).

Please confirm your on 5.1.1 on the server.

davidbehler commented 4 years ago

I used 5.10 because it's the top release on the list of releases: grafik

I didn't even check the release below as I expected the first entry in the list to be the latest release.

I'll give 5.11 a try and get back to you.

davidbehler commented 4 years ago

I've updated the server to 5.11 while still using the client from the cdn and now I'm getting a different error:

INCOMING_CONNECTION | from undefined (95.222.240.129)
AUTH_SUCCESSFUL | peer-1685-103236

/usr/local/bin/dist/src/services/permission/valve/rule-application.js:109
        this.params.recordHandler.removeRecordRequest(this.params.name);
                                  ^
TypeError: Cannot read property 'removeRecordRequest' of undefined
    at RuleApplication.destroy (/usr/local/bin/dist/src/services/permission/valve/rule-application.js:109:35)
    at RuleApplication.run (/usr/local/bin/dist/src/services/permission/valve/rule-application.js:66:18)
    at new RuleApplication (/usr/local/bin/dist/src/services/permission/valve/rule-application.js:33:14)
    at ConfigPermission.canPerformAction (/usr/local/bin/dist/src/services/permission/valve/config-permission.js:86:9)
    at MessageProcessor.process (/usr/local/bin/dist/src/utils/message-processor.js:53:46)
    at WSBinarySocketWrapper.unauthenticatedSocketWrapper.onMessage (/usr/local/bin/dist/src/connection-endpoint/base/connection-endpoint.js:238:18)
    at WebSocket.<anonymous> (/usr/local/bin/dist/src/services/http/node/node-http.js:109:35)
    at WebSocket.emit (events.js:223:5)
    at Receiver.receiverOnMessage (/usr/local/bin/node_modules/ws/lib/websocket.js:800:20)
    at Receiver.emit (events.js:223:5)

This happens right after I call client.event.subscribe(). Here's what my code looks like:

var client = new DeepstreamClient('example.com:6020');
client.on('connectionStateChanged', StateChangedCallbackFunction);
client.on('error', errorCallbackFunction);
client.login({username: 'some-user'});
client.event.subscribe('some-event', eventCallbackFunction);

This is code that worked fine with the v4 version.

slachtar commented 4 years ago

Should'nt you wait for login success to subscribe to events?

On Tue, Jun 2, 2020 at 7:38 AM David Behler notifications@github.com wrote:

I've updated the server to 5.11 while still using the client from the cdn and now I'm getting a different error:

INCOMING_CONNECTION | from undefined (95.222.240.129) AUTH_SUCCESSFUL | peer-1685-103236

/usr/local/bin/dist/src/services/permission/valve/rule-application.js:109 this.params.recordHandler.removeRecordRequest(this.params.name); ^ TypeError: Cannot read property 'removeRecordRequest' of undefined at RuleApplication.destroy (/usr/local/bin/dist/src/services/permission/valve/rule-application.js:109:35) at RuleApplication.run (/usr/local/bin/dist/src/services/permission/valve/rule-application.js:66:18) at new RuleApplication (/usr/local/bin/dist/src/services/permission/valve/rule-application.js:33:14) at ConfigPermission.canPerformAction (/usr/local/bin/dist/src/services/permission/valve/config-permission.js:86:9) at MessageProcessor.process (/usr/local/bin/dist/src/utils/message-processor.js:53:46) at WSBinarySocketWrapper.unauthenticatedSocketWrapper.onMessage (/usr/local/bin/dist/src/connection-endpoint/base/connection-endpoint.js:238:18) at WebSocket. (/usr/local/bin/dist/src/services/http/node/node-http.js:109:35) at WebSocket.emit (events.js:223:5) at Receiver.receiverOnMessage (/usr/local/bin/node_modules/ws/lib/websocket.js:800:20) at Receiver.emit (events.js:223:5)

This happens right after I call client.event.subscribe(). Here's my full code:

var client = new DeepstreamClient('example.com:6020'); client.on('connectionStateChanged', callbackFunction); client.on('error', errorCallbackFunction); client.login({username: 'some-user'}); client.event.subscribe('some-event', eventCallback);

This is code that worked fine with the v4 version.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/deepstreamIO/deepstream.io/issues/1073#issuecomment-637311059, or unsubscribe https://github.com/notifications/unsubscribe-auth/AATKH2V7MGRJKFK6XWPYLQTRUSM7ZANCNFSM4NN6BHTA .

-- Slah Lachtar Tel. (+216) 98 221 575

davidbehler commented 4 years ago

You are correct and that's actually what the code does but my above example skipped that, because I did not think it was revelant. Foolish me 🙈

The subscribe method is actually called in the login callback:

client.login({username: 'some-user'}, function() {
  client.event.subscribe('some-event', eventCallbackFunction);
});
davidbehler commented 4 years ago

Any news on this?

yasserf commented 4 years ago

hey @davidbehler

Sorry for the delay.

Are you using custom permissions? Looks like it's a cross referencing bug

davidbehler commented 4 years ago

What do you mean by "custom permissions"?

yasserf commented 4 years ago

as in are you using the default valve permissions or did u modify it?

davidbehler commented 4 years ago

I've posted my config.yml in my initial post. Does that help?

yasserf commented 4 years ago

unfortunately not no. The config in permissions: fileLoad(permissions.yml) is what matters. If changed

davidbehler commented 4 years ago

I have not touched the permissions.yml, but here it is:

presence:
  "*":
    allow: true
record:
  "*":
    create: true
    write: true
    read: true
    delete: true
    listen: true
    notify: true
event:
  "*":
    publish: true
    subscribe: true
    listen: true
rpc:
  "*":
    provide: true
    request: true
yasserf commented 4 years ago

Hmm. Honestly I have no idea what can be causing this. Subscribing to an event is something I used to do on a daily basis, there are a bunch of tests and it feels like something that would have been raised by other users.

If you create a small reproducible repo I can took a look into it. The code you pasted doesn't replicate the issue for me. Thanks!

jaime-ez commented 3 years ago

Closing this for now. Please reopen if problemm persist and share some reproducible code to test the issue.