From 9aa9e827c0162a7f002a6e10ae3e83e91ef2272d Mon Sep 17 00:00:00 2001 From: "steinkirch.eth, phd" Date: Sat, 15 Jul 2023 13:01:58 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=9B=20Commit=20progress=20before=20lun?= =?UTF-8?q?ch=20break?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ------- code/protocol_demos/grpc-demo/.gitignore | 1 + code/protocol_demos/grpc-demo/client.js | 35 ++++++++++++++ code/protocol_demos/grpc-demo/package.json | 16 +++++++ code/protocol_demos/grpc-demo/server.js | 38 +++++++++++++++ code/protocol_demos/grpc-demo/todo.proto | 21 +++++++++ code/protocol_demos/long-polling/index.js | 36 ++++++++++++++ code/protocol_demos/long-polling/package.json | 15 ++++++ code/protocol_demos/rabbitmq/consumer.js | 29 ++++++++++++ code/protocol_demos/rabbitmq/package.json | 16 +++++++ code/protocol_demos/rabbitmq/publisher.js | 22 +++++++++ code/protocol_demos/rabbitmq/scripts.md | 20 ++++++++ .../server-sent-events/index.js | 30 ++++++++++++ .../server-sent-events/package.json | 15 ++++++ code/protocol_demos/short-polling/index.js | 24 ++++++++++ .../protocol_demos/short-polling/package.json | 15 ++++++ communication/README.md | 47 ++++++++++++++++++- https/README.md | 0 proxy_and_lb/README.md | 0 19 files changed, 378 insertions(+), 19 deletions(-) create mode 100644 code/protocol_demos/grpc-demo/.gitignore create mode 100644 code/protocol_demos/grpc-demo/client.js create mode 100644 code/protocol_demos/grpc-demo/package.json create mode 100644 code/protocol_demos/grpc-demo/server.js create mode 100644 code/protocol_demos/grpc-demo/todo.proto create mode 100644 code/protocol_demos/long-polling/index.js create mode 100644 code/protocol_demos/long-polling/package.json create mode 100644 code/protocol_demos/rabbitmq/consumer.js create mode 100644 code/protocol_demos/rabbitmq/package.json create mode 100644 code/protocol_demos/rabbitmq/publisher.js create mode 100644 code/protocol_demos/rabbitmq/scripts.md create mode 100644 code/protocol_demos/server-sent-events/index.js create mode 100644 code/protocol_demos/server-sent-events/package.json create mode 100644 code/protocol_demos/short-polling/index.js create mode 100644 code/protocol_demos/short-polling/package.json delete mode 100644 https/README.md delete mode 100644 proxy_and_lb/README.md diff --git a/README.md b/README.md index 32f92f9..bfaa86a 100755 --- a/README.md +++ b/README.md @@ -54,23 +54,6 @@ * HTTP/3 * gRPC * WebRTC - - -
- - -* **[HTTP](https/)** - * https communication - * https over TCP with TLS 1.2 - * https over TCP with TLS 1.3 - * https over QUIC (HTTP/3) - * https over TFO with TLS 1.3 - * https over TCP with TLS 1.3 and ORTT - * https over QUICK with ORTT - -
- -* **[proxy and load balance](proxy_and_lb)** * proxy vs. reverse proxy * Layer 4 vs. Layer 7 load balancers diff --git a/code/protocol_demos/grpc-demo/.gitignore b/code/protocol_demos/grpc-demo/.gitignore new file mode 100644 index 0000000..30bc162 --- /dev/null +++ b/code/protocol_demos/grpc-demo/.gitignore @@ -0,0 +1 @@ +/node_modules \ No newline at end of file diff --git a/code/protocol_demos/grpc-demo/client.js b/code/protocol_demos/grpc-demo/client.js new file mode 100644 index 0000000..607c709 --- /dev/null +++ b/code/protocol_demos/grpc-demo/client.js @@ -0,0 +1,35 @@ +const grpc = require("grpc"); +const protoLoader = require("@grpc/proto-loader") +const packageDef = protoLoader.loadSync("todo.proto", {}); +const grpcObject = grpc.loadPackageDefinition(packageDef); +const todoPackage = grpcObject.todoPackage; + +const text = process.argv[2]; + +const client = new todoPackage.Todo("localhost:40000", +grpc.credentials.createInsecure()) +console.log(text) + +client.createTodo({ + "id": -1, + "text": text +}, (err, response) => { + + console.log("Recieved from server " + JSON.stringify(response)) + +}) +/* +client.readTodos(null, (err, response) => { + + console.log("read the todos from server " + JSON.stringify(response)) + if (!response.items) + response.items.forEach(a=>console.log(a.text)); +}) +*/ + +const call = client.readTodosStream(); +call.on("data", item => { + console.log("received item from server " + JSON.stringify(item)) +}) + +call.on("end", e => console.log("server done!")) \ No newline at end of file diff --git a/code/protocol_demos/grpc-demo/package.json b/code/protocol_demos/grpc-demo/package.json new file mode 100644 index 0000000..49d80cb --- /dev/null +++ b/code/protocol_demos/grpc-demo/package.json @@ -0,0 +1,16 @@ +{ + "name": "grpc-demo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@grpc/proto-loader": "^0.5.3", + "grpc": "^1.24.9" + } +} diff --git a/code/protocol_demos/grpc-demo/server.js b/code/protocol_demos/grpc-demo/server.js new file mode 100644 index 0000000..d6773a4 --- /dev/null +++ b/code/protocol_demos/grpc-demo/server.js @@ -0,0 +1,38 @@ +const grpc = require("grpc"); +const protoLoader = require("@grpc/proto-loader") +const packageDef = protoLoader.loadSync("todo.proto", {}); +const grpcObject = grpc.loadPackageDefinition(packageDef); +const todoPackage = grpcObject.todoPackage; + +const server = new grpc.Server(); +server.bind("0.0.0.0:40000", + grpc.ServerCredentials.createInsecure()); + +server.addService(todoPackage.Todo.service, + { + "createTodo": createTodo, + "readTodos" : readTodos, + "readTodosStream": readTodosStream + }); +server.start(); + +const todos = [] +function createTodo (call, callback) { + const todoItem = { + "id": todos.length + 1, + "text": call.request.text + } + todos.push(todoItem) + callback(null, todoItem); +} + +function readTodosStream(call, callback) { + + todos.forEach(t => call.write(t)); + call.end(); +} + + +function readTodos(call, callback) { + callback(null, {"items": todos}) +} diff --git a/code/protocol_demos/grpc-demo/todo.proto b/code/protocol_demos/grpc-demo/todo.proto new file mode 100644 index 0000000..e8299f2 --- /dev/null +++ b/code/protocol_demos/grpc-demo/todo.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package todoPackage; + +service Todo { + rpc createTodo(TodoItem) returns (TodoItem); + rpc readTodos(voidNoParam) returns (TodoItems); + rpc readTodosStream(voidNoParam) returns (stream TodoItem); + +} + +message voidNoParam {} + +message TodoItem { + int32 id = 1; + string text = 2; +} + +message TodoItems { + repeated TodoItem items = 1; +} \ No newline at end of file diff --git a/code/protocol_demos/long-polling/index.js b/code/protocol_demos/long-polling/index.js new file mode 100644 index 0000000..47c812b --- /dev/null +++ b/code/protocol_demos/long-polling/index.js @@ -0,0 +1,36 @@ +const app = require("express")(); +const jobs = {} + +app.post("/submit", (req, res) => { + const jobId = `job:${Date.now()}` + jobs[jobId] = 0; + updateJob(jobId,0); + res.end("\n\n" + jobId + "\n\n"); +}) + +app.get("/checkstatus", async (req, res) => { + console.log(jobs[req.query.jobId]) + //long polling, don't respond until done + while(await checkJobComplete(req.query.jobId) == false); + res.end("\n\nJobStatus: Complete " + jobs[req.query.jobId] + "%\n\n") + +} ) + +app.listen(8080, () => console.log("listening on 8080")); + +async function checkJobComplete(jobId) { + return new Promise( (resolve, reject) => { + if (jobs[jobId] < 100) + this.setTimeout(()=> resolve(false), 1000); + else + resolve(true); + }) + +} + +function updateJob(jobId, prg) { + jobs[jobId] = prg; + console.log(`updated ${jobId} to ${prg}`) + if (prg == 100) return; + this.setTimeout(()=> updateJob(jobId, prg + 10), 10000) +} \ No newline at end of file diff --git a/code/protocol_demos/long-polling/package.json b/code/protocol_demos/long-polling/package.json new file mode 100644 index 0000000..1f6087c --- /dev/null +++ b/code/protocol_demos/long-polling/package.json @@ -0,0 +1,15 @@ +{ + "name": "short-polling", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/code/protocol_demos/rabbitmq/consumer.js b/code/protocol_demos/rabbitmq/consumer.js new file mode 100644 index 0000000..eecec01 --- /dev/null +++ b/code/protocol_demos/rabbitmq/consumer.js @@ -0,0 +1,29 @@ +const amqp = require("amqplib"); + +connect(); +async function connect() { + + try { + const amqpServer = "amqp://localhost:5672" + const connection = await amqp.connect(amqpServer) + const channel = await connection.createChannel(); + await channel.assertQueue("jobs"); + + channel.consume("jobs", message => { + const input = JSON.parse(message.content.toString()); + console.log(`Recieved job with input ${input.number}`) + //"7" == 7 true + //"7" === 7 false + + if (input.number == 7 ) + channel.ack(message); + }) + + console.log("Waiting for messages...") + + } + catch (ex){ + console.error(ex) + } + +} \ No newline at end of file diff --git a/code/protocol_demos/rabbitmq/package.json b/code/protocol_demos/rabbitmq/package.json new file mode 100644 index 0000000..f18dfcd --- /dev/null +++ b/code/protocol_demos/rabbitmq/package.json @@ -0,0 +1,16 @@ +{ + "name": "rabbitmq", + "version": "1.0.0", + "description": "", + "main": "publisher.js", + "scripts": { + "publish": "node publisher.js", + "consume": "node consumer.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "amqplib": "^0.5.5" + } +} diff --git a/code/protocol_demos/rabbitmq/publisher.js b/code/protocol_demos/rabbitmq/publisher.js new file mode 100644 index 0000000..5e232d8 --- /dev/null +++ b/code/protocol_demos/rabbitmq/publisher.js @@ -0,0 +1,22 @@ +/* RabbitMQ */ +const amqp = require("amqplib"); + +const msg = {number: process.argv[2]} +connect(); +async function connect() { + + try { + const amqpServer = "amqp://localhost:5672" + const connection = await amqp.connect(amqpServer) + const channel = await connection.createChannel(); + await channel.assertQueue("jobs"); + await channel.sendToQueue("jobs", Buffer.from(JSON.stringify(msg))) + console.log(`Job sent successfully ${msg.number}`); + await channel.close(); + await connection.close(); + } + catch (ex){ + console.error(ex) + } + +} \ No newline at end of file diff --git a/code/protocol_demos/rabbitmq/scripts.md b/code/protocol_demos/rabbitmq/scripts.md new file mode 100644 index 0000000..e9904c6 --- /dev/null +++ b/code/protocol_demos/rabbitmq/scripts.md @@ -0,0 +1,20 @@ +## docker commands + +### Spin rabbitmq server docker +docker run --name rabbitmq -p 5672:5672 -d rabbitmq + +### Spin rabbitmq server HTTP server docker + +docker run --name rabbitmq -p 5672:5672 -p 15672:15672 -d rabbitmq:3-management + + + +HTTP +fetch("http://localhost:15672/api/vhosts”, {headers: {"Authorization" : `Basic ${btoa('guest:guest')}`}}).then(a=>a.json()).then(console.log) + + +fetch("http://localhost:15672/api/channels", {headers: {"Authorization" : `Basic ${btoa('guest:guest')}`}}).then(a=>a.json()).then(console.log) + + +fetch("http://localhost:15672/api/queues", {headers: {"Authorization" : `Basic ${btoa('guest:guest')}`}}).then(a=>a.json()).then(console.log) + diff --git a/code/protocol_demos/server-sent-events/index.js b/code/protocol_demos/server-sent-events/index.js new file mode 100644 index 0000000..c7af410 --- /dev/null +++ b/code/protocol_demos/server-sent-events/index.js @@ -0,0 +1,30 @@ +/* Client Code + +let sse = new EventSource("http://localhost:8080/stream"); +sse.onmessage = console.log + +*/ + +const app = require("express")(); + +app.get("/", (req, res) => res.send("hello!")); + +app.get("/stream", (req,res) => { + + res.setHeader("Content-Type", "text/event-stream"); + send(res); + +}) +const port = process.env.PORT || 8888; + +let i = 0; +function send (res) { + + res.write("data: " + `hello from server ---- [${i++}]\n\n`); + + + setTimeout(() => send(res), 1000); +} + +app.listen(port) +console.log(`Listening on ${port}`) \ No newline at end of file diff --git a/code/protocol_demos/server-sent-events/package.json b/code/protocol_demos/server-sent-events/package.json new file mode 100644 index 0000000..fad691d --- /dev/null +++ b/code/protocol_demos/server-sent-events/package.json @@ -0,0 +1,15 @@ +{ + "name": "server-sent-events", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/code/protocol_demos/short-polling/index.js b/code/protocol_demos/short-polling/index.js new file mode 100644 index 0000000..ef09907 --- /dev/null +++ b/code/protocol_demos/short-polling/index.js @@ -0,0 +1,24 @@ +const app = require("express")(); +const jobs = {} + +app.post("/submit", (req, res) => { + const jobId = `job:${Date.now()}` + jobs[jobId] = 0; + updateJob(jobId,0); + res.end("\n\n" + jobId + "\n\n"); +}) + +app.get("/checkstatus", (req, res) => { + console.log(jobs[req.query.jobId]) + res.end("\n\nJobStatus: " + jobs[req.query.jobId] + "%\n\n") + +} ) + +app.listen(8080, () => console.log("listening on 8080")); + +function updateJob(jobId, prg) { + jobs[jobId] = prg; + console.log(`updated ${jobId} to ${prg}`) + if (prg == 100) return; + this.setTimeout(()=> updateJob(jobId, prg + 10), 3000) +} \ No newline at end of file diff --git a/code/protocol_demos/short-polling/package.json b/code/protocol_demos/short-polling/package.json new file mode 100644 index 0000000..1f6087c --- /dev/null +++ b/code/protocol_demos/short-polling/package.json @@ -0,0 +1,15 @@ +{ + "name": "short-polling", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/communication/README.md b/communication/README.md index b5c96d2..2c5be34 100644 --- a/communication/README.md +++ b/communication/README.md @@ -162,9 +162,18 @@ curl -v --trace marinasouza.xyz
+* used when a request takes long time to process (e.g., upload a video) and very simple to build. +* however, it can be too chatting, use too much network bandwidth and backend resources. + +
#### basic idea - - when a request takes long time to process (e.g., upload a video) - - the backend want to sends notification + + 1. client sends a request + 2. server responds immediately with a handle + 3. server continues to process the request + 4. client uses that handle to check for status + 5. multiple short request response as polls +
@@ -173,6 +182,24 @@ curl -v --trace marinasouza.xyz ### Long Polling +
+ +* a poll requests where the server only responds when the job is ready (used when a request takes long time to process and it's not real time) +* used by Kafka + +
+ +#### basic idea + +
+ + 1. clients sends a request + 2. server responds immediately with a handle + 3. server continues to process the request + 4. client uses that handle to check for status + 5. server does not reply until has the response (and there are some timeouts) + +
--- @@ -180,6 +207,22 @@ curl -v --trace marinasouza.xyz ### Server Sent Events +
+ +* one request with a long response, but the client must be online and be able to handle the response. + +
+ +#### basic idea + + 1. a response has start and end + 2. client sends a request + 3. server sends logical events as part of response + 4. server never writes the end of the response + 5. it's still a request but an unending response + 6. client parses the streams data + 7. works with HTTP +
---- diff --git a/https/README.md b/https/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/proxy_and_lb/README.md b/proxy_and_lb/README.md deleted file mode 100644 index e69de29..0000000