UBERF-8139: Check server version when connecting from client (#6608)

This commit is contained in:
Alexey Zinoviev 2024-09-18 07:01:27 +04:00 committed by GitHub
parent a1cee24473
commit c96318d415
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 81 additions and 8 deletions

1
.vscode/launch.json vendored
View File

@ -73,6 +73,7 @@
// "SERVER_PROVIDER":"uweb"
"SERVER_PROVIDER":"ws",
"MODEL_VERSION": "0.6.287",
// "VERSION": "0.6.289",
"ELASTIC_INDEX_NAME": "local_storage_index",
"UPLOAD_URL": "/files",

View File

@ -232,6 +232,10 @@ class Connection implements ClientConnection {
}
handleMsg (socketId: number, resp: Response<any>): void {
if (this.closed) {
return
}
if (resp.error !== undefined) {
if (resp.error?.code === UNAUTHORIZED.code || resp.terminate === true) {
Analytics.handleError(new PlatformError(resp.error))
@ -252,9 +256,24 @@ class Connection implements ClientConnection {
return
}
if (resp.result === 'hello') {
const helloResp = resp as HelloResponse
if (helloResp.binary) {
this.binaryMode = true
}
// We need to clear dial timer, since we recieve hello response.
clearTimeout(this.dialTimer)
this.dialTimer = null
const serverVersion = helloResp.serverVersion
console.log('Connected to server:', serverVersion)
if (this.opt?.onHello !== undefined && !this.opt.onHello(serverVersion)) {
this.closed = true
this.websocket?.close()
return
}
this.helloRecieved = true
if (this.upgrading) {
// We need to call upgrade since connection is upgraded
@ -262,9 +281,6 @@ class Connection implements ClientConnection {
}
this.upgrading = false
if ((resp as HelloResponse).binary) {
this.binaryMode = true
}
// Notify all waiting connection listeners
const handlers = this.onConnectHandlers.splice(0, this.onConnectHandlers.length)
for (const h of handlers) {

View File

@ -56,6 +56,7 @@ export enum ClientSocketReadyState {
}
export interface ClientFactoryOptions {
onHello?: (serverVersion?: string) => boolean
onUpgrade?: () => void
onUnauthorized?: () => void
onConnect?: (event: ClientConnectEvent, data: any) => void

View File

@ -67,6 +67,32 @@ export async function connect (title: string): Promise<Client | undefined> {
let version: Version | undefined
const clientFactory = await getResource(client.function.GetClient)
_client = await clientFactory(token, workspaceLoginInfo.endpoint, {
onHello: (serverVersion?: string) => {
const frontVersion = getMetadata(presentation.metadata.FrontVersion)
if (
serverVersion !== undefined &&
serverVersion !== '' &&
frontVersion !== undefined &&
frontVersion !== serverVersion
) {
const reloaded = localStorage.getItem(`versionUpgrade:s${serverVersion}:f${frontVersion}`)
if (reloaded === null) {
localStorage.setItem(`versionUpgrade:s${serverVersion}:f${frontVersion}`, 't')
location.reload()
return false
} else {
versionError.set(`Front version ${frontVersion} is not in sync with server version ${serverVersion}`)
setTimeout(() => {
location.reload()
}, 5000)
return false
}
}
return true
},
onUpgrade: () => {
location.reload()
},

View File

@ -153,6 +153,32 @@ export async function connect (title: string): Promise<Client | undefined> {
{},
async (ctx) =>
await clientFactory(token, endpoint, {
onHello: (serverVersion?: string) => {
const frontVersion = getMetadata(presentation.metadata.FrontVersion)
if (
serverVersion !== undefined &&
serverVersion !== '' &&
frontVersion !== undefined &&
frontVersion !== serverVersion
) {
const reloaded = localStorage.getItem(`versionUpgrade:s${serverVersion}:f${frontVersion}`)
if (reloaded === null) {
localStorage.setItem(`versionUpgrade:s${serverVersion}:f${frontVersion}`, 't')
location.reload()
return false
} else {
versionError.set(`Front version ${frontVersion} is not in sync with server version ${serverVersion}`)
setTimeout(() => {
location.reload()
}, 5000)
return false
}
}
return true
},
onUpgrade: () => {
location.reload()
},

View File

@ -15,7 +15,7 @@
"_phase:bundle": "rushx bundle",
"_phase:docker-build": "rushx docker:build",
"_phase:docker-staging": "rushx docker:staging",
"bundle": "mkdir -p bundle && esbuild src/__start.ts --sourcemap=inline --bundle --keep-names --platform=node --external:*.node --external:bufferutil --external:snappy --external:utf-8-validate --external:msgpackr-extract --define:process.env.MODEL_VERSION=$(node ../../common/scripts/show_version.js) --define:process.env.GIT_REVISION=$(../../common/scripts/git_version.sh) --outfile=bundle/bundle.js --log-level=error --sourcemap=external",
"bundle": "mkdir -p bundle && esbuild src/__start.ts --sourcemap=inline --bundle --keep-names --platform=node --external:*.node --external:bufferutil --external:snappy --external:utf-8-validate --external:msgpackr-extract --define:process.env.MODEL_VERSION=$(node ../../common/scripts/show_version.js) --define:process.env.VERSION=$(node ../../common/scripts/show_tag.js) --define:process.env.GIT_REVISION=$(../../common/scripts/git_version.sh) --outfile=bundle/bundle.js --log-level=error --sourcemap=external",
"docker:build": "../../common/scripts/docker_build.sh hardcoreeng/transactor",
"docker:tbuild": "docker build -t hardcoreeng/transactor . --platform=linux/amd64 && ../../common/scripts/docker_tag_push.sh hardcoreeng/transactor",
"docker:abuild": "docker build -t hardcoreeng/transactor . --platform=linux/arm64 && ../../common/scripts/docker_tag_push.sh hardcoreeng/transactor",

View File

@ -45,6 +45,7 @@ export interface HelloRequest extends Request<any[]> {
export interface HelloResponse extends Response<any> {
binary: boolean
reconnect?: boolean
serverVersion: string
}
function replacer (key: string, value: any): any {

View File

@ -15,7 +15,7 @@
//
import { UNAUTHORIZED } from '@hcengineering/platform'
import { RPCHandler } from '@hcengineering/rpc'
import { RPCHandler, type Response } from '@hcengineering/rpc'
import { generateToken } from '@hcengineering/server-token'
import WebSocket from 'ws'
import { start } from '../server'
@ -129,7 +129,7 @@ describe('server', () => {
conn.close(1000)
})
conn.on('message', (msg: string) => {
const resp = handler.readResponse(msg, false)
const resp: Response<any> = handler.readResponse(msg, false)
expect(resp.result === 'hello')
expect(resp.error?.code).toBe(UNAUTHORIZED.code)
conn.close(1000)
@ -240,7 +240,7 @@ describe('server', () => {
newConn.on('message', (msg: Buffer) => {
try {
console.log('resp:', msg.toString())
const parsedMsg = handler.readResponse(msg.toString(), false) // Hello
const parsedMsg: Response<any> = handler.readResponse(msg.toString(), false) // Hello
if (!helloReceived) {
expect(parsedMsg.result === 'hello')
helloReceived = true

View File

@ -91,6 +91,7 @@ class TSessionManager implements SessionManager {
timeMinutes = 0
modelVersion = process.env.MODEL_VERSION ?? ''
serverVersion = process.env.VERSION ?? ''
oldClientErrors: number = 0
clientErrors: number = 0
@ -914,7 +915,8 @@ class TSessionManager implements SessionManager {
id: -1,
result: 'hello',
binary: service.binaryMode,
reconnect
reconnect,
serverVersion: this.serverVersion
}
ws.send(requestCtx, helloResponse, false, false)
return