Skip to content

Latest commit

 

History

History

rpc

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

rpc

schema-based RPC that supports authentication and more

concepts

Terms in italics should be understood in the context of this module.

protocol

A protocol is the description of the api between a client and a server. You can define multiple different protocols in your application for different functionalities, with equal number of client-server pairs each dedicated to one of the protocols.

client/server

All communications are initiated by client. Client calls, server executes. Client can only communicate with server through the api defined by the protocol, which means they both need to "understand" the same protocol.

transport

Client and server take care of invocation and execution, while transport takes care of message passing.

Transports are protocol-agnostic. It means different servers can share the same server transport. Similarly, different clients can share one client transport.

example

import { MuStruct, MuASCII, MuDate, MuOption, MuVoid, MuArray } from 'mudb/schema'
import { MuRPCHttpServerTransport } from 'mudb/rpc/http/server'
import { MuRPCServer } from 'mudb/rpc/server'
import { MuRPCHttpClientTransport } from 'mudb/rpc/http/client'
import { MuRPCClient } from 'mudb/rpc/client'
import http = require('http')

 const Info = new MuStruct({
    ip: new MuASCII(),
    datetime: new MuDate(),
    serverName: new MuOption(new MuASCII()),
})

// define api

const protocol = {
    // what is the api for
    name: 'analytics',

    // argument and return data types are defined by MuSchemas
    api: {
        // describe an endpoint that uploads traffic info
        // add(info:{ uid:number, ip:string, datetime:Date, serverName?:string })
        add: {
            arg: Info,
            // use void schema for return of POST-like endpoints
            ret: new MuVoid(),
        },

        // describe an endpoint that retrieves cumulated info of specified date
        // getByDate(date:Date) : info[]
        getByDate: {
            arg: new MuDate(),
            ret: new MuArray(Info, Infinity),
        },

        login: {
            arg: new MuASCII(),
            ret: new MuVoid(),
        },
    },
}

// server

const ROUTE = 'rpc'
const serverTransport = new MuRPCHttpServerTransport({
    route: ROUTE,
    byteLimit: 2 ** 20, // limit of request data size
    cookie: 'auth',     // cookie name
})

const store:any[] = []
const server = new MuRPCServer({
    protocol,
    transport: serverTransport,

    // checked on every request
    // an error response will be sent if this returns false
    authorize: async (conn) => true,

    // the remote procedures to be called
    handlers: {
        add: async (conn, info) => {
            store.push(info)
        },
        getByDate: async (conn, date) => {
            if (conn.auth === 'admin') {
                date.setHours(0, 0, 0, 0)
                const start = date.getTime()
                date.setHours(23, 59, 59, 999)
                const end = date.getTime()
                return store.filter((item) => {
                    const timestamp = item.datetime.getTime()
                    return timestamp >= start && timestamp <= end
                })
            }
            return []
        },
        login: async (conn, handle) => {
            conn.setAuth(handle)
        },
    }
})

const httpServer = http.createServer(async (req, res) => {
    // route requests
    await serverTransport.handler(req, res)
})
httpServer.listen(9966)

// client

const clientTransport = new MuRPCHttpClientTransport({
    url: `http://127.0.0.1:9966/${ROUTE}`,
    timeout: Infinity,
})
const client = new MuRPCClient(protocol, clientTransport)

await client.api.add({ ip: '72.92.73.181', datetime: new Date('2020-01-16T15:06:28'), serverName: undefined })
await client.api.add({ ip: '20.200.121.186', datetime: new Date('2020-03-21T22:39:35'), serverName: 'us1' })
await client.api.add({ ip: '109.79.143.230', datetime: new Date('2020-06-13T03:52:14'), serverName: 'jp1' })
await client.api.add({ ip: '200.28.109.236', datetime: new Date('2020-06-13T20:55:58'), serverName: undefined })

await client.api.login('user')
console.log(await client.api.getByDate(new Date('2020-06-13')))
await client.api.login('admin')
console.log(await client.api.getByDate(new Date('2020-06-13')))

API


MuRPCProtocol

A protocol defines the api by argument and return data type of each endpoint, using MuSchemas. Note that each protocol MUST be given a different name.

type MuRPCProtocol = {
    name:string,
    api:{
        [method:string]:{
            arg:MuSchema<any>,
            ret:MuSchema<any>,
        },
    },
}

MuRPCHttpConnection

class MuRPCHttpConnection implements MuRPCConnection

Each call creates a MuRPCConnection instance, which is made available in every handler and the auth function. It carries useful info for handling the request.

props

request:http.IncomingMessage

the first parameter to the Node 'request' event listener

response:http.ServerResponse

the second parameter to the Node 'request' event listener

useCookie:boolean

is cookie authentication enabled

cookie:string

auth cookie name

auth:string

auth token

methods

setAuth(auth:string, options?:Partial<{
    domain:string,
    path:string,
    maxAge:number,
    sameSite:''|'none'|'lax'|'strict',
    httpOnly:boolean,
    secure:boolean,
}>) : void

Sets auth token in client cookies, with optional cookie attributes. setAuth('') will end the current session.


MuRPCHttpServerTransport

class MuRPCHttpServerTransport implements MuRPCServerTransport

new MuRPCHttpServerTransport(spec:{ route:string, byteLimit:number, cookie?:string })
  • byteLimit maximum byte size of request data
  • cookie the cookie name for auth token, cookie authentication is enabled if this is specified

methods

handler(req:http.IncomingMessage, res:http.ServerResponse) : Promise<boolean>

Routes requests. MUST be called in a 'request' event listener.


MuRPCHttpClientTransport

class MuRPCHttpClientTransport implements MuRPCClientTransport

new MuRPCHttpClientTransport(spec:{ url:string, timeout:number })
  • timeout how long it takes to automatically terminate a request, in ms

MuRPCServer

Authorizes and executes calls from the corresponding client.

class MuRPCServer<P extends MuRPCProtocol>

new MuRPCServer(spec:{
    protocol:P,
    transport:MuRPCServerTransport,
    authorize:(conn:MuRPCConnection) => Promise<boolean>,
    handlers:{
        [method in keyof P['api']]:(
            conn:MuRPCConnection,
            arg:P['api'][method]['arg']['identity'],
            ret:P['api'][method]['ret']['identity'],
        ) => Promise<P['api'][method]['ret']['identity']>
    },
    logger?:MuLogger,
})

MuRPCClient

Initiates invocations to procedures of the corresponding server.

class MuRPCClient<P extends MuRPCProtocol>

new MuRPCClient(protocol:P, transport:MuRPCClientTransport, logger?:MuLogger)

props

api:{
    [method in keyof P['api']]:(
        arg:P['api'][method]['arg']['identity'],
    ) => Promise<P['api'][method]['ret']['identity']>,
}

A table of stubs to be called to initiate invocations to remote procedures.

interfaces


MuRPCConnection

interface MuRPCConnection

A MuRPCConnection can be used to cumulate info for handling requests and set auth token, consumed by handlers and the auth function.

auth:string

the auth token received

setAuth(auth:string) : void

sets an auth token for client


MuRPCServerTransport

interface MuRPCServerTransport<P extends MuProtocol>

A MuRPCServerTransport is responsible sending messages to clients, consumed by MuRPCServer.

listen(
    schemas:MuRPCSchemas<P>,
    authorize:(conn:MuRPCConnection) => Promise<boolean>,
    recv:(
        conn:MuRPCConnection,
        arg:MuRPCSchemas<P>['argSchema']['identity'],
        resp:MuRPCSchemas<P>['responseSchema']['identity'],
    ) => Promise<void>,
) : void

MuRPCClientTransport

interface MuRPCClientTransport<P extends MuProtocol>

A MuRPCClientTransport is responsible for sending messages to the server, consumed by MuRPCClient.

send(
    schemas:MuRPCSchemas<P>,
    arg:MuRPCSchemas<P>['argSchema']['identity'],
) : Promise<MuRPCSchemas<P>['responseSchema']['identity']>

ideas

  • streaming transport
    • status
    • keep alive
    • auto reconnect