New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@applitools/execution-grid-tunnel

Package Overview
Dependencies
Maintainers
53
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@applitools/execution-grid-tunnel - npm Package Compare versions

Comparing version 3.0.3 to 3.0.4

2

package.json
{
"name": "@applitools/execution-grid-tunnel",
"version": "3.0.3",
"version": "3.0.4",
"description": "",

@@ -5,0 +5,0 @@ "main": "scripts/run-execution-grid-tunnel.js",

@@ -241,3 +241,2 @@ 'use strict'

const {frpcPath} = await prepareEnvironment({

@@ -244,0 +243,0 @@ shouldInstallFrpc,

@@ -1,1 +0,140 @@

const{tunnelId:tunnelId,stringifyConfig:stringifyConfig,heartbeatTimeout:heartbeatTimeout=1e4,startTunnelTimeoutThreshold:startTunnelTimeoutThreshold=1e4,stopProcessTimeout:stopProcessTimeout=5e3,stringifyloggerOptions:stringifyloggerOptions}=process.env,loggerOptions=stringifyloggerOptions?JSON.parse(stringifyloggerOptions):{mode:"development",level:"info"},{createLogger:createLogger}=require("../../src/utils"),logger=createLogger({...loggerOptions,filename:tunnelId});logger.debug({action:"process-was-started",tunnelId:tunnelId,stringifyConfig:stringifyConfig,stringifyloggerOptions:stringifyloggerOptions,heartbeatTimeout:heartbeatTimeout,startTunnelTimeoutThreshold:startTunnelTimeoutThreshold,stopProcessTimeout:stopProcessTimeout});const{promises:fs}=require("fs"),nodeCleanup=require("node-cleanup"),{TunnelConnectionPool:TunnelConnectionPool}=require("./tunnel-connection-pool.js"),TUNNEL_STATUS=require("./tunnel-status.js"),tls=require("tls");let selfKillTimeoutId,initTimeoutId;const _startTunnel=async()=>{const e=JSON.parse(stringifyConfig),t=new TunnelConnectionPool({...e,logger:logger}),o=e=>{selfKillTimeoutId=setTimeout((async()=>{logger.error({action:"self-kill-timeout",error:`TunnelId ${tunnelId}: self-kill is called`,heartbeatTimeout:heartbeatTimeout}),console.log(`TunnelId ${tunnelId}: self-kill is called`);try{await t.end()}catch(e){console.log(e),t.destroy()}finally{process.exit(1)}}),e)};process.on("message",(({status:e})=>{"ok"===e&&(void 0!==selfKillTimeoutId&&clearTimeout(selfKillTimeoutId),o(heartbeatTimeout))})),nodeCleanup(((e,o)=>{logger.info({action:"tunnel-is-closing",exitCode:e,signal:o}),process.send({status:TUNNEL_STATUS.STOPPED}),selfKillTimeoutId&&clearTimeout(selfKillTimeoutId),initTimeoutId&&clearTimeout(initTimeoutId),t.end(Number(stopProcessTimeout)).catch((e=>{logger.warn({action:"tunnel-pool-timeout",error:e.message}),t.destroy()})).finally((()=>{logger.info({action:"tunnel-process-was-closed"}),setTimeout((()=>{logger.close(),process.exit()}),1e3)}))}));try{initTimeoutId=setTimeout((()=>{process.send({status:TUNNEL_STATUS.INIT_TIMEOUT_ERROR})}),startTunnelTimeoutThreshold),await t.start(),clearTimeout(initTimeoutId),initTimeoutId=void 0,o(parseInt(heartbeatTimeout)),process.send({status:TUNNEL_STATUS.RUNNING}),logger.info({action:"tunnel-started",tunnelId:tunnelId,success:!0})}catch(e){logger.error({action:"starting-tunnel-pool-failed",error:e.message}),initTimeoutId&&clearTimeout(initTimeoutId),process.send({status:TUNNEL_STATUS.INIT_ERROR})}};_startTunnel();
const {
tunnelId,
stringifyConfig,
heartbeatTimeout = 10000,
startTunnelTimeoutThreshold = 10000,
stopProcessTimeout = 5000,
stringifyloggerOptions
} = process.env
const loggerOptions = stringifyloggerOptions ? JSON.parse(stringifyloggerOptions): {mode: 'development', level: 'info'}
// TODO: fix logger
const {createLogger} = require('../../src/utils')
const logger = createLogger({...loggerOptions, filename: tunnelId})
logger.debug({
action: 'process-was-started',
tunnelId,
stringifyConfig,
stringifyloggerOptions,
heartbeatTimeout,
startTunnelTimeoutThreshold,
stopProcessTimeout,
})
const {promises: fs} = require('fs')
const nodeCleanup = require('node-cleanup')
const {TunnelConnectionPool, POOL_STATUS} = require("./tunnel-connection-pool.js")
const TUNNEL_STATUS = require('./tunnel-status.js')
const tls = require('tls')
let selfKillTimeoutId
let initTimeoutId
const _startTunnel = async () => {
const config = JSON.parse(stringifyConfig)
const pool = new TunnelConnectionPool({...config, logger})
pool.onSetStatus((status)=> {
if (status === POOL_STATUS.RECONNECT) process.send({status: TUNNEL_STATUS.RECONNECT})
})
// Closing frpc if doesn't get heart bit from parent
const startSelfKillTimeout = (timeout) => {
selfKillTimeoutId = setTimeout(async () => {
logger.error({
action: 'self-kill-timeout',
error: `TunnelId ${tunnelId}: self-kill is called`,
heartbeatTimeout
})
console.log(`TunnelId ${tunnelId}: self-kill is called`)
try{
await pool.end()
}catch (error){
console.log(error)
pool.destroy()
}finally{
process.exit(1)
}
}, timeout)
}
process.on('message', ({status}) => {
if (status !== 'ok') {
return
}
if (selfKillTimeoutId !== undefined) {
clearTimeout(selfKillTimeoutId)
}
startSelfKillTimeout(heartbeatTimeout)
})
// In some cases when frpc connection fails, it tries to reconnect forever.
// In that line we send error to frpc controller after startTunnelTimeoutThreshold
nodeCleanup((exitCode, signal) => {
logger.info({
action: 'tunnel-is-closing',
exitCode,
signal
})
process.send({status: TUNNEL_STATUS.STOPPED})
selfKillTimeoutId && clearTimeout(selfKillTimeoutId)
initTimeoutId && clearTimeout(initTimeoutId)
//TODO: should we check if proc.isRunning before run proc.stop
pool.end(Number(stopProcessTimeout)).catch(error => {
logger.warn({
action: 'tunnel-pool-timeout',
error: error.message
})
pool.destroy()
}).finally(() => {
logger.info({
action: 'tunnel-process-was-closed'
})
setTimeout(() => {
logger.close()
process.exit()
}, 1000)
})
})
try{
initTimeoutId = setTimeout(() => {
process.send({status: TUNNEL_STATUS.INIT_TIMEOUT_ERROR})
}, startTunnelTimeoutThreshold)
await pool.start()
clearTimeout(initTimeoutId)
initTimeoutId = undefined
startSelfKillTimeout(parseInt(heartbeatTimeout))
process.send({status: TUNNEL_STATUS.RUNNING })
logger.info({
action: 'tunnel-started',
tunnelId,
success: true
})
// set selfkill in case execution-grid-tunnel crashes before init
}catch(err){
logger.error({
action: 'starting-tunnel-pool-failed',
error: err.message
})
initTimeoutId && clearTimeout(initTimeoutId)
process.send({status: TUNNEL_STATUS.INIT_ERROR})
}
}
_startTunnel()

@@ -1,1 +0,202 @@

const{TunnelConnection:TunnelConnection,ErrorCircuitBreaker:ErrorCircuitBreaker}=require("../utils"),{v4:uuid}=require("uuid");class TunnelConnectionPool{constructor(e){this._isPoolShuttingDown=!1,this._haveConnectionSucceeded=!1,this._options=e,this._connectionMap=new Map,this._createConnectionMap=new Map,this._connectionStatusMap=new Map,this._availableConnectionMap=new Map,this._logger=this._options.logger,this._circuitBreaker=new ErrorCircuitBreaker({logger:this._options.logger})}async _createNewConnection(e){const{tunnelId:t,host:n,port:o,token:i,keepAliveMessage:c,connectedMessage:s,protocol:r,localProxyOptions:a,logger:l}=this._options,h=new(this._options.TunnelConnection||TunnelConnection)({keepAliveMessage:c,connectedMessage:s,logger:l,connectionId:e});return await h.connect({tunnelId:t,host:n,port:o,protocol:r,token:i,localProxyOptions:a}),h}async _handleConnectionLifecycle(){await this._circuitBreaker.waitForClosedState;const e=uuid();try{this._createConnectionMap.set(e,e);const t=await this._createNewConnection(e);this._createConnectionMap.delete(e),this._fireStatus("connected",e,t),t._remoteConnection?.on("error",(n=>{t._remoteConnection?.closed&&t._localConnection.closed?this._fireStatus("end",e):this._fireStatus("error",e,n)})),t._localConnection?.on("close",(()=>{this._fireStatus("end",e)}));t.waitForAttachingRequest().then((()=>this._fireStatus("occupied",e))),t.waitForRemoteConnectionClosing().then((()=>this._fireStatus("end",e)));return void this._circuitBreaker.resetErrorCounter()}catch(t){return this._createConnectionMap.delete(e),this._logger.warn({action:"handle-connection-lifecycle",error:t.message,id:e}),this._circuitBreaker.incErrorCounter(),this._fireStatus("error",e,t),t}}async _fireStatus(e,t,n=void 0){if(this._logger.debug(e,t),"connected"===e&&(this._connectionMap.set(t,n),this._connectionStatusMap.set(t,e),this._availableConnectionMap.set(t,t),!this._haveConnectionSucceeded)){this._haveConnectionSucceeded=!0;const e=this._options.preAllocation-this._connectionMap.size;for(let t=0;t<e;t++)this._handleConnectionLifecycle()}if("occupied"===e){if(!this._connectionStatusMap.has(t))return;this._connectionStatusMap.set(t,"occupied"),this._availableConnectionMap.delete(t),this._connectionStatusMap.delete(t)}"error"===e&&(this._availableConnectionMap.delete(t),this._connectionStatusMap.set(t,"error")),"end"===e&&(this._availableConnectionMap.delete(t),this._connectionStatusMap.delete(t),this._connectionMap.delete(t)),"occupied"!==e&&"end"!==e&&"error"!==e||this._haveConnectionSucceeded&&!this._isPoolShuttingDown&&this._availableConnectionMap.size+this._createConnectionMap.size<this._options.preAllocation&&this._connectionMap.size<this._options.maxConnections&&this._handleConnectionLifecycle(),this._logger.debug({action:"pool-statistics",available:this._availableConnectionMap.size,inCreation:this._createConnectionMap.size,total:this._connectionMap.size})}async start(){const e=await this._handleConnectionLifecycle();if(e)throw this._logger.error({action:"tunnel-pool-error",error:e.message}),e}async end(e=5e3){this._isPoolShuttingDown=!0;for(const e of this._connectionMap.values())e.end();return new Promise((async(t,n)=>{const o=setTimeout((()=>n(new Error("Timeout Error"))),e);for(;this._connectionMap.size>0;)await new Promise((e=>setTimeout(e,50)));clearTimeout(o),t()}))}destroy(){this._isPoolShuttingDown=!0;for(const e of this._connectionMap.values())e.destroy()}}module.exports={TunnelConnectionPool:TunnelConnectionPool};
const {TunnelConnection, ErrorCircuitBreaker} = require('../utils')
const {v4: uuid} = require('uuid')
const POOL_STATUS = {
OFF: 'off', // start is not called yet
INIT: 'init', //
INIT_ERROR: 'init_error',
RUNNING: 'running', // The process connected to the server
RECONNECT: 'reconnect', // The process tries to reconnect to server
ERROR: 'error',
STOPPING: 'stoping', // User asks to stop the tunnel
}
class TunnelConnectionPool{
constructor(options){
this._isPoolShuttingDown = false
this._haveConnectionSucceeded = false
this._options = options
this._connectionMap = new Map()
this._createConnectionMap = new Map()
this._connectionStatusMap = new Map()
this._availableConnectionMap = new Map()
this._logger = this._options.logger
this._onSetStatusCallbacks = []
this._poolStatus = POOL_STATUS.OFF
this._circuitBreaker = new ErrorCircuitBreaker({logger: this._options.logger})
this._circuitBreaker.onOpen(() => {
if (this._poolStatus === POOL_STATUS.RUNNING){
this.setStatus(POOL_STATUS.RECONNECT)
}
})
}
async _createNewConnection(connectionId){
const {tunnelId, host, port, token, keepAliveMessage, connectedMessage, protocol, localProxyOptions, logger} = this._options
const TunnelConnectionClass = this._options.TunnelConnection || TunnelConnection
const connection = new TunnelConnectionClass({keepAliveMessage, connectedMessage, logger, connectionId})
await connection.connect({tunnelId, host, port, protocol, token, localProxyOptions})
return connection
}
async _handleConnectionLifecycle(){
await this._circuitBreaker.waitForClosedState
const id = uuid()
try{
this._createConnectionMap.set(id, id)
const connection = await this._createNewConnection(id)
this._createConnectionMap.delete(id)
this._fireStatus('connected', id, connection)
connection._remoteConnection?.on('error', e => {
if (connection._remoteConnection?.closed && connection._localConnection.closed){
this._fireStatus('end', id)
}else {
this._fireStatus('error', id, e)
}
})
connection._localConnection?.on('close', () => {
this._fireStatus('end', id)
})
const occupiedPromise = connection.waitForAttachingRequest()
.then(()=> this._fireStatus('occupied', id))
const endPromise = connection.waitForRemoteConnectionClosing()
.then(() => this._fireStatus('end', id))
this._circuitBreaker.resetErrorCounter()
this.setStatus(POOL_STATUS.RUNNING)
return
}catch(e){
this._createConnectionMap.delete(id)
this._logger.warn({
action: 'handle-connection-lifecycle',
error: e.message,
id
})
this._circuitBreaker.incErrorCounter()
this._fireStatus('error', id, e)
return e
}
}
async _fireStatus(status, id, data = undefined){
//console.log(status, id)
this._logger.debug(status, id)
if(status === 'connected'){
this._connectionMap.set(id, data)
this._connectionStatusMap.set(id, status)
this._availableConnectionMap.set(id, id)
if (!this._haveConnectionSucceeded){
this._haveConnectionSucceeded = true
const count = this._options.preAllocation - this._connectionMap.size
for(let i=0; i< count; i++){
this._handleConnectionLifecycle()
}
}
}
if (status === 'occupied'){
if (!this._connectionStatusMap.has(id)) return
this._connectionStatusMap.set(id, 'occupied')
this._availableConnectionMap.delete(id)
this._connectionStatusMap.delete(id)
}
if (status === 'error'){
this._availableConnectionMap.delete(id)
this._connectionStatusMap.set(id, "error")
}
if (status === 'end'){
this._availableConnectionMap.delete(id)
this._connectionStatusMap.delete(id)
this._connectionMap.delete(id)
}
if(status === 'occupied' || status === 'end' || status === 'error'){
// console.log(this._haveConnectionSucceeded, this._shouldCloseConnection)
if (this._haveConnectionSucceeded && !this._isPoolShuttingDown){
if ((this._availableConnectionMap.size + this._createConnectionMap.size) < this._options.preAllocation &&
this._connectionMap.size < this._options.maxConnections){
this._handleConnectionLifecycle()
}
}
}
this._logger.debug({
action: 'pool-statistics',
available: this._availableConnectionMap.size,
inCreation: this._createConnectionMap.size,
total: this._connectionMap.size
})
}
async start(){
this.setStatus(POOL_STATUS.INIT)
const error = await this._handleConnectionLifecycle()
if (error){
this._logger.error({
action: 'tunnel-pool-error',
error: error.message
})
this.setStatus(POOL_STATUS.INIT_ERROR)
throw error
}
}
async end(timeout = 5000){
this._isPoolShuttingDown = true
for(const connection of this._connectionMap.values()){
connection.end()
}
return new Promise(async (resolve, reject) => {
const timeoutId = setTimeout(() => reject(new Error('Timeout Error')), timeout)
while(this._connectionMap.size > 0){
await new Promise(r => setTimeout(r,50))
}
clearTimeout(timeoutId)
resolve()
})
}
async onSetStatus(cb){
this._onSetStatusCallbacks.push(cb)
}
async setStatus(status){
if (this._poolStatus === status) return
this._poolStatus = status
for(const cb of this._onSetStatusCallbacks){
cb(status)
}
}
destroy(){
this._isPoolShuttingDown = true
this.setStatus(POOL_STATUS.STOPPING)
for(const connection of this._connectionMap.values()){
connection.destroy()
}
}
}
module.exports = {TunnelConnectionPool, POOL_STATUS}

@@ -10,2 +10,3 @@ class ErrorCircuitBreaker{

this.waitForClosedState = Promise.resolve()
this._onOpenCallbacks = []
}

@@ -38,2 +39,5 @@

this._failureCount = 0
for(const cb of this._onOpenCallbacks){
cb()
}
}

@@ -44,4 +48,8 @@

}
onOpen(cb){
this._onOpenCallbacks.push(cb)
}
}
module.exports = {ErrorCircuitBreaker}

@@ -1,1 +0,28 @@

const{Transform:Transform}=require("stream");class KeepaliveMessageFilter extends Transform{constructor(e){super(e),this._keepAliveMessage=e.keepAliveMessage,this._waitForChunkResolver=null,this._waitForChunkPromise=new Promise((e=>this._waitForChunkResolver=e))}_transform(e,s,r){const t=e;e.length===this._keepAliveMessage.length&&t.toString()===this._keepAliveMessage||(this._waitForChunkResolver&&(this._waitForChunkResolver(),this._waitForChunkResolver=null),this.push(e)),r()}async waitForChunk(){return this._waitForChunkPromise}}module.exports={KeepaliveMessageFilter:KeepaliveMessageFilter};
const {Transform} = require('stream')
class KeepaliveMessageFilter extends Transform {
constructor(options) {
super(options);
this._keepAliveMessage = options.keepAliveMessage
this._waitForChunkResolver = null
this._waitForChunkPromise = new Promise(resolve => this._waitForChunkResolver = resolve)
}
_transform(chunk, encoding, callback){
const transformedChunk = chunk
if (chunk.length !== this._keepAliveMessage.length || transformedChunk.toString() !== this._keepAliveMessage){
if(this._waitForChunkResolver){
this._waitForChunkResolver()
this._waitForChunkResolver = null
}
this.push(chunk);
}
callback();
}
async waitForChunk(){
return this._waitForChunkPromise
}
}
module.exports = {KeepaliveMessageFilter}

@@ -1,1 +0,110 @@

const tls=require("tls"),net=require("net"),{KeepaliveMessageFilter:KeepaliveMessageFilter}=require("./keepalive-filter-message"),ATTACHING_REQUEST="attaching-request",CLOSING_CONNECTION="closing-connection";class TunnelConnection{constructor({keepAliveMessage:e,connectedMessage:n,logger:o,connectionId:t}){this._logger=o,this._filter=new KeepaliveMessageFilter({keepAliveMessage:e}),this._connectedMessage=n,this._filter.on("error",(e=>o.warn({action:"filter-error",error:e.stack||e.message}))),this._connectionId=t}connect({tunnelId:e,host:n,port:o,token:t,protocol:c="tcp",localProxyOptions:i}){return new Promise(((r,s)=>{let l=!1;this._logger.debug({action:"connection-info",connectionId:this._connectionId,port:o,host:n,token:t,protocol:c});const a="tcp"==c?net.connect(o,n):tls.connect(o,n);this._remoteConnection=a,a.once("connect",(()=>{a.write(JSON.stringify({tunnelId:e,token:t})),a.once("data",(n=>{const o=n.toString();if(o!==this._connectedMessage){this._logger.warn({action:"connection-refused",connectionId:this._connectionId,error:o});const e=new Error(`Server Error: ${o}`);return s(e)}this._localConnection=net.connect(i),this._localConnection.on("error",(e=>{this._logger.warn({action:"local-connection-error",connectionId:this._connectionId,error:e.stack||e.message})})),this._localConnection.once("connect",(()=>{this._localConnection.on("end",(()=>{this._logger.debug({action:"local-connection-end",connectionId:this._connectionId,tunnelId:e}),a.end()})),a.pipe(this._filter).pipe(this._localConnection).pipe(a),l=!0,r()}))}))})),a.on("error",(e=>{this._logger.warn({action:"remote-connection-error",connectionId:this._connectionId,error:e.stack||e.message}),l||s(new Error(`Connection Closed: ${e.message||"Unexpected"}`)),a.end()}))}))}waitForAttachingRequest(){return this._filter.waitForChunk().then((()=>ATTACHING_REQUEST))}waitForRemoteConnectionClosing(){return new Promise((e=>{this._remoteConnection.once("end",(()=>{e(CLOSING_CONNECTION)}))}))}end(){this._localConnection?.closed||this._localConnection.end(),this._remoteConnection.closed||this._remoteConnection.end()}destroy(){this._localConnection?.destroyed||this._localConnection.destroy(),this._remoteConnection.destroyed||this._remoteConnection.destroy()}}module.exports={TunnelConnection:TunnelConnection,ATTACHING_REQUEST:ATTACHING_REQUEST,CLOSING_CONNECTION:CLOSING_CONNECTION};
const tls = require('tls')
const net = require('net')
const { KeepaliveMessageFilter } = require('./keepalive-filter-message');
const ATTACHING_REQUEST = 'attaching-request'
const CLOSING_CONNECTION = 'closing-connection'
class TunnelConnection {
constructor({keepAliveMessage, connectedMessage, logger, connectionId}){
this._logger = logger
this._filter = new KeepaliveMessageFilter({ keepAliveMessage })
this._connectedMessage = connectedMessage
this._filter.on('error', (error) => logger.warn({action: 'filter-error', error: error.stack || error.message}))
this._connectionId = connectionId
}
connect({tunnelId, host, port, token, protocol = 'tcp', localProxyOptions}){
return new Promise((resolve, reject) => {
let isResolved = false
this._logger.debug({action: 'connection-info', connectionId: this._connectionId, port, host, token, protocol})
const remoteConnection = protocol == 'tcp' ? net.connect(port,host): tls.connect(port, host)
this._remoteConnection = remoteConnection
remoteConnection.once('connect', () => {
remoteConnection.write(JSON.stringify({tunnelId, token}))
remoteConnection.once('data', (chunk) => {
const result = chunk.toString()
if (result !== this._connectedMessage){
this._logger.warn({action: 'connection-refused', connectionId: this._connectionId, error: result})
const error = new Error(`Server Error: ${result}`)
return reject(error)
}
this._localConnection = net.connect(localProxyOptions)
this._localConnection.on('error',
e => {
this._logger.warn({
action: 'local-connection-error',
connectionId: this._connectionId,
error: e.stack || e.message
})
})
this._localConnection.once('connect', () => {
this._localConnection.on('end', () => {
this._logger.debug({
action: 'local-connection-end',
connectionId: this._connectionId,
tunnelId
})
remoteConnection.end()
})
remoteConnection.pipe(this._filter).pipe(this._localConnection).pipe(remoteConnection)
isResolved = true
resolve()
})
})
})
remoteConnection.on('error', e => {
this._logger.warn({
action: 'remote-connection-error',
connectionId: this._connectionId,
error: e.stack || e.message
})
if (!isResolved) {
reject(new Error(`Connection Closed: ${e.message || 'Unexpected'}`))
}
remoteConnection.end()
})
})
}
waitForAttachingRequest(){
return this._filter.waitForChunk().then(() => ATTACHING_REQUEST)
}
waitForRemoteConnectionClosing(){
return new Promise(resolve => {
this._remoteConnection.once('end', () => {
resolve(CLOSING_CONNECTION)
})
})
}
end(){
if(!this._localConnection?.closed){
this._localConnection.end()
}
if(!this._remoteConnection.closed){
this._remoteConnection.end()
}
}
destroy(){
if(!this._localConnection?.destroyed){
this._localConnection.destroy()
}
if(!this._remoteConnection.destroyed){
this._remoteConnection.destroy()
}
}
}
module.exports = { TunnelConnection, ATTACHING_REQUEST, CLOSING_CONNECTION}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc