Skip to content
This repository has been archived by the owner on Nov 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #4 from udevbe/split_connection
Browse files Browse the repository at this point in the history
make connection setup generic so we can call it in a browser
  • Loading branch information
Zubnix authored Aug 23, 2020
2 parents a202bff + 18072c5 commit 19c5374
Show file tree
Hide file tree
Showing 6 changed files with 1,354 additions and 1,521 deletions.
44 changes: 22 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xtsb",
"version": "0.0.0-development",
"version": "0.0.1-alpha.1",
"description": "",
"keywords": [],
"main": "dist/xtsb.umd.js",
Expand Down Expand Up @@ -65,10 +65,10 @@
],
"coverageThreshold": {
"global": {
"branches": 50,
"functions": 75,
"lines": 75,
"statements": 75
"branches": 45,
"functions": 70,
"lines": 70,
"statements": 70
}
},
"collectCoverageFrom": [
Expand All @@ -85,40 +85,40 @@
]
},
"devDependencies": {
"@commitlint/cli": "^9.0.1",
"@commitlint/config-conventional": "^9.0.1",
"@types/jest": "^25.2.3",
"@commitlint/cli": "^9.1.2",
"@commitlint/config-conventional": "^9.1.2",
"@types/jest": "^26.0.10",
"@types/lodash.camelcase": "^4.3.6",
"@types/node": "^14.0.10",
"@types/node": "^14.6.0",
"@types/rollup-plugin-json": "^3.0.2",
"colors": "^1.3.2",
"commitizen": "^4.1.2",
"commitizen": "^4.1.5",
"coveralls": "^3.1.0",
"cross-env": "^7.0.2",
"cz-conventional-changelog": "^3.2.0",
"husky": "^4.2.5",
"jest": "^26.0.1",
"jest-config": "^26.0.1",
"lint-staged": "^10.2.6",
"jest": "^26.4.2",
"jest-config": "^26.4.2",
"lint-staged": "^10.2.11",
"lodash.camelcase": "^4.3.0",
"prettier": "^2.0.5",
"prompt": "^1.0.0",
"replace-in-file": "^6.0.0",
"replace-in-file": "^6.1.0",
"rimraf": "^3.0.2",
"rollup": "^2.10.8",
"rollup": "^2.26.5",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-sourcemaps": "^0.6.2",
"rollup-plugin-typescript2": "^0.27.1",
"semantic-release": "^17.0.8",
"rollup-plugin-typescript2": "^0.27.2",
"semantic-release": "^17.1.1",
"shelljs": "^0.8.3",
"ts-jest": "^26.0.0",
"ts-node": "^8.10.1",
"tslint": "^6.1.2",
"ts-jest": "^26.2.0",
"ts-node": "^9.0.0",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.15.0",
"tslint-config-standard": "^9.0.0",
"typedoc": "^0.17.7",
"typescript": "^3.0.3"
"typedoc": "^0.18.0",
"typescript": "^4.0.2"
}
}
64 changes: 64 additions & 0 deletions src/WebConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
connectGeneric,
SetupConnection,
XConnection,
XConnectionOptions,
XConnectionSocket
} from './connection'
import { Setup } from './xproto'

function isSetup(setup: any): setup is Setup {
// TODO we could check all Setup attributes but it would be a long list...
return setup.rootsLen !== undefined
&& setup.protocolMinorVersion !== undefined
&& setup.protocolMajorVersion !== undefined
&& setup.length !== undefined
}

function createXConnectionSocket(webSocket: WebSocket): XConnectionSocket {
webSocket.binaryType = 'arraybuffer'
const xConnectionSocket = {
close() {
webSocket.close()
},
write(data: Uint8Array) {
webSocket.send(data)
}
}
webSocket.onmessage = ev => xConnectionSocket?.write(ev.data)
webSocket.onerror = ev => {
xConnectionSocket.close()
console.error('XConnection is in error: ' + ev)
}
return xConnectionSocket
}

async function auth(webSocket: WebSocket, options?: XConnectionOptions) {
return new Promise<Setup>((resolve, reject) => {
webSocket.onmessage = ev => {
const message = JSON.parse(ev.data)
if (message?.reply === 'connectAck' && isSetup(message?.replyArgs)) {
resolve(message.replyArgs)
} else {
reject('Expected connectAck, got: ' + ev)
}
}
webSocket.onerror = ev => {
reject('WebSocket is in error: ' + ev)
}
webSocket.send(JSON.stringify({
request: 'connect',
requestArgs: [options]
}))
})
}

export async function connect(webSocket: WebSocket, options?: XConnectionOptions): Promise<XConnection> {
const connectionSetup: SetupConnection = async () => {
const setup = await auth(webSocket, options)
const xConnectionSocket = createXConnectionSocket(webSocket)
return { setup, xConnectionSocket }
}

return connectGeneric(connectionSetup)
}
22 changes: 19 additions & 3 deletions src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ const unmarshallGetInputFocusReply: Unmarshaller<GetInputFocusReply> = (buffer,
return {
value: {
revertTo,
focus,
focus
},
offset,
offset
}
}

Expand Down Expand Up @@ -138,7 +138,7 @@ export class XConnection {
// tslint:disable-next-line:no-floating-promises
this.sendRequest<GetInputFocusReply>([new ArrayBuffer(4)], 43, unmarshallGetInputFocusReply)
return voidRequestPromise
},
}
}
}

Expand Down Expand Up @@ -191,3 +191,19 @@ function createRequestBuffer(requestParts: ArrayBuffer[]): Uint8Array {
{ buffer: new Uint8Array(requestSize), offset: 0 }
).buffer
}

export interface SetupConnection {
(): Promise<{ setup: Setup, xConnectionSocket: XConnectionSocket }>
}

export async function connectGeneric(setupConnection: SetupConnection, options?: XConnectionOptions): Promise<XConnection> {
const display = options?.display ?? process.env.DISPLAY ?? ':0'

const displayMatch = display.match(/^(?:[^:]*?\/)?(.*):(\d+)(?:.(\d+))?$/)
if (!displayMatch) {
throw new Error('Cannot parse display')
}
const displayNum = displayMatch[2] ?? '0'
const { setup, xConnectionSocket } = await setupConnection()
return new XConnection(xConnectionSocket, displayNum, setup)
}
98 changes: 51 additions & 47 deletions src/nodeConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { homedir, hostname } from 'os'
import * as path from 'path'
import * as util from 'util'
import { authenticate } from './auth'
import { XConnection, XConnectionOptions, XConnectionSocket } from './connection'
import { connectGeneric, XConnection, XConnectionOptions, XConnectionSocket } from './connection'

const fsReadFile = util.promisify(fs.readFile)

Expand All @@ -31,7 +31,7 @@ const connectionTypeToName: ConnectionTypeToName = {
1: 'DECnet',
2: 'Chaos',
5: 'ServerInterpreted',
6: 'Internet6',
6: 'Internet6'
}

interface Cookie {
Expand Down Expand Up @@ -73,7 +73,7 @@ function parseXauth(buf: Uint8Array): Cookie[] {
'address',
'display',
'authName',
'authData',
'authData'
]

while (offset < buf.length) {
Expand All @@ -82,7 +82,7 @@ function parseXauth(buf: Uint8Array): Cookie[] {
throw new Error('Unknown address type')
}
const cookie: Partial<Cookie> = {
type: type as keyof ConnectionTypeToName,
type: type as keyof ConnectionTypeToName
}

offset += 2
Expand Down Expand Up @@ -156,7 +156,7 @@ export async function getAuthenticationCookie(
if (!data) {
return {
authName: '',
authData: '',
authData: ''
}
}
const auth = parseXauth(data)
Expand All @@ -173,62 +173,66 @@ export async function getAuthenticationCookie(
// If no cookie is found, proceed without authentication
return {
authName: '',
authData: '',
authData: ''
}
}

export async function connect(options?: XConnectionOptions): Promise<XConnection> {
const display = options?.display ?? process.env.DISPLAY ?? ':0'
const xAuthority = options?.xAuthority
const connectionSetup = async () => {
const display = options?.display ?? process.env.DISPLAY ?? ':0'
const xAuthority = options?.xAuthority

const displayMatch = display.match(/^(?:[^:]*?\/)?(.*):(\d+)(?:.(\d+))?$/)
if (!displayMatch) {
throw new Error('Cannot parse display')
}
const displayMatch = display.match(/^(?:[^:]*?\/)?(.*):(\d+)(?:.(\d+))?$/)
if (!displayMatch) {
throw new Error('Cannot parse display')
}

const host = displayMatch[1]
const displayNum = displayMatch[2] ?? '0'
const screenNum = displayMatch[3] ?? '0'

let socketPath: string | undefined
// try local socket on non-windows platforms
if (['cygwin', 'win32', 'win64'].indexOf(process.platform) < 0) {
// FIXME check if mac is ok?
// @ts-ignore
if (process.platform === 'darwin' || process.platform === 'mac') {
// socket path on OSX is /tmp/launch-(some id)/org.x:0
if (display[0] === '/') {
socketPath = display
const host = displayMatch[1]
const displayNum = displayMatch[2] ?? '0'
const screenNum = displayMatch[3] ?? '0'

let socketPath: string | undefined
// try local socket on non-windows platforms
if (['cygwin', 'win32', 'win64'].indexOf(process.platform) < 0) {
// FIXME check if mac is ok?
// @ts-ignore
if (process.platform === 'darwin' || process.platform === 'mac') {
// socket path on OSX is /tmp/launch-(some id)/org.x:0
if (display[0] === '/') {
socketPath = display
}
} else if (!host) {
socketPath = '/tmp/.X11-unix/X' + displayNum
}
} else if (!host) {
socketPath = '/tmp/.X11-unix/X' + displayNum
}
}

const socket = await connectSocket(host, displayNum, socketPath)
const socket = await connectSocket(host, displayNum, socketPath)
const xConnectionSocket: XConnectionSocket = {
write(data: Uint8Array) {
socket.write(data)
},

const xConnectionSocket: XConnectionSocket = {
write(data: Uint8Array) {
socket.write(data)
},
close() {
socket.end()
}
}
socket.on('data', (data) => xConnectionSocket.onData?.(data))

close() {
socket.end()
},
}
socket.on('data', (data) => xConnectionSocket.onData?.(data))
let authHost = socket.remoteAddress
let socketFamily = socket.remoteFamily as 'IPv4' | 'IPv6' | undefined

if (!authHost || authHost === '127.0.0.1' || authHost === '::1') {
authHost = hostname()
socketFamily = undefined
}

let authHost = socket.remoteAddress
let socketFamily = socket.remoteFamily as 'IPv4' | 'IPv6' | undefined
// tslint:disable-next-line:no-floating-promises
const cookie = await getAuthenticationCookie(displayNum, authHost, socketFamily, xAuthority)
const setup = await authenticate(xConnectionSocket, displayNum, authHost, socketFamily, cookie)

if (!authHost || authHost === '127.0.0.1' || authHost === '::1') {
authHost = hostname()
socketFamily = undefined
return { setup, xConnectionSocket }
}

// tslint:disable-next-line:no-floating-promises
const cookie = await getAuthenticationCookie(displayNum, authHost, socketFamily, xAuthority)
const setup = await authenticate(xConnectionSocket, displayNum, authHost, socketFamily, cookie)

return new XConnection(xConnectionSocket, displayNum, setup)
return connectGeneric(connectionSetup)
}
11 changes: 8 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es5",
"module":"es2015",
"lib": ["es2015", "es2016", "es2017", "dom"],
"target": "ES6",
"module": "ES6",
"lib": [
"es2015",
"es2016",
"es2017",
"dom"
],
"strict": true,
"sourceMap": true,
"declaration": true,
Expand Down
Loading

0 comments on commit 19c5374

Please sign in to comment.