Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for server request and client response #29

Closed
ScriptWorker opened this issue Mar 25, 2018 · 14 comments
Closed

Support for server request and client response #29

ScriptWorker opened this issue Mar 25, 2018 · 14 comments

Comments

@ScriptWorker
Copy link

We have a case when the server should make requests to the client. At this point this library do not have such functionality, while specification do not restrict regarding this.

Do you interested in merging such PR if we will implement it?

@mkozjak
Copy link
Member

mkozjak commented Mar 25, 2018

Hello! Can you just run both server and client instances on both sides? That would solve the case.

@ScriptWorker
Copy link
Author

Hello! Unfortunately no, as the server can't connect to the client. Basically we can't guarantee, that the client won't be under the firewall protection or even to have an external ip address.

@mkozjak
Copy link
Member

mkozjak commented Mar 25, 2018

@ScriptWorker: Ok, I agree and I would gladly merge the feature if no current use cases break!

@mkozjak
Copy link
Member

mkozjak commented Jun 27, 2018

@ScriptWorker Any progress here, maybe?

@mkozjak
Copy link
Member

mkozjak commented Jul 6, 2018

This will be available in version 5.x. Currently in development.

@oxygen
Copy link

oxygen commented Aug 1, 2018

@FMRb
Copy link

FMRb commented Oct 17, 2018

Any update on version 5.x? I would like the library to support this feature

@mkozjak
Copy link
Member

mkozjak commented Oct 17, 2018

@FMRb Unfortunately, no. Out of resources currently!

@davidmoshal
Copy link

you really ought to make it clear in the ReadMe that this is one-way, browser running procedures on a remote server only, because it's such a huge limitation of this library.

@mkozjak
Copy link
Member

mkozjak commented Apr 10, 2019 via email

@mkozjak
Copy link
Member

mkozjak commented Nov 18, 2019

@ScriptWorker @davidmoshal @FMRb: This is now available in version 6.0.0-beta.0.

https://github.com/elpheria/rpc-websockets/blob/v6.x/API.md

@mkozjak
Copy link
Member

mkozjak commented Jan 8, 2020

@FMRb: btw, any initial feedback on this feature, maybe? :)

@mkozjak
Copy link
Member

mkozjak commented Mar 16, 2020

Due to positive feedback this will hit master soon. It’s currently released as 6.x in beta due to insufficient migration docs, but that should be available soon. Closing this one.

@huanshiwushuang
Copy link

My solution is that the client calls the server method, registers a function A, and then the server returns promise.
Then when calling A, make Promise resolve

example:

const res = {}

res.websocket = {
    makeUpServer(wsServer) {
        // client 注册方法
        wsServer.register('_calleeRegister', ({ methodName, namespace = '/', password = '' }) => {
            const registerPassword = password

            return new Promise((calleeResolve) => {
                wsServer.register(
                    methodName,
                    function (params) {
                        return new Promise((callerResolve, callerReject) => {
                            const { password } = params

                            // 密码校验不通过
                            if (registerPassword && registerPassword != password) {
                                return callerReject({
                                    state: 'fail',
                                    code: 'le3t8gjp',
                                    msg: `call password error`
                                })
                            }

                            // 删除密码
                            delete params.password

                            // 本次调用 id
                            const _callerCall = `${Date.now().toString(36)}-${Math.random()}`

                            // 转发 调用数据 给注册方
                            calleeResolve({
                                _callerCall,
                                params
                            })

                            // 缓存本次
                            _calleeReturn.value[_callerCall] = {
                                ms: Date.now(),
                                // 转发 返回数据 给调用方
                                callerResolve
                            }
                        })
                    },
                    namespace
                )
            })
        })
        // client 注册方法的返回
        const _calleeReturnCache = {
            // 清理超时的 callerResolve
            timeout: 1000 * 10,
            // 多久清理一次
            detectInterval: 1000 * 20,
            // 超时的 callerResolve
            value: {
                // _callerCall: {
                //     callerResolve: function () {},
                //     ms: Date.now(),
                // }
            }
        }
        // 定时检测超时的 _calleeReturnCache
        setInterval(() => {
            Object.keys(_calleeReturnCache.value).forEach((v) => {
                if (Date.now() - _calleeReturnCache.value[v].ms >= _calleeReturnCache.timeout) {
                    delete _calleeReturnCache.value[v]
                }
            })
        }, _calleeReturnCache.detectInterval)

        wsServer.register('_calleeReturn', ({ _callerCall, returnValue }) => {
            if (!_calleeReturnCache.value[_callerCall]) {
                return {
                    state: 'fail',
                    code: 'le3wr8e1',
                    msg: `not found _calleeReturnCache by _callerCall ${_callerCall}`
                }
            }
            _calleeReturnCache.value[_callerCall].callerResolve(returnValue)
            delete _calleeReturnCache.value[_callerCall]
            return 1
        })
    },
    makeUpClient(wsClient) {
        Object.assign(wsClient, {
            async register(methodName, handler, password = '', namespace = '/') {
                // 注册函数
                const start = async () => {
                    // 通知 Server 注册一个方法
                    const res = await wsClient.call(`_calleeRegister`, {
                        methodName,
                        namespace,
                        password
                    })
                    const { _callerCall, params } = res
                    let returnValue = handler(params)

                    if (/promise/i.test(returnValue.toString())) {
                        returnValue = await returnValue
                    }

                    wsClient.notify('_calleeReturn', {
                        _callerCall,
                        returnValue: returnValue || {}
                    })
                }
                await start()

                while (true) {
                    await start()
                }
            }
        })
    }
}

module.exports = res

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants