Deno 1.1.1 Static HTTP Server - node.js

I have been reading about Deno for a few hours and finally got an http static server running.
I would like to know what else is needed to add https to it.
I understand Leaf to Root arrangement of certificates but not in Deno.
Working code:
import {
gray,
green,
cyan,
bold,
yellow,
red,
} from 'https://deno.land/std#0.58.0/fmt/colors.ts';
import { Application, HttpError, send, Status } from 'https://deno.land/x/oak/mod.ts';
const app = new Application();
// Middleware 1 - Error handler
app.use(async (context, next) => {
try {
await next();
} catch (e) {
if (e instanceof HttpError) {
context.response.status = e.status as any;
// expose
// Determines if details about the error should be automatically exposed in a response.
// This is automatically set to true for 4XX errors, as they represent errors in the request...
if (e.expose) {
context.response.body = `
<!DOCTYPE html>
<html>
<body>
<h1>${e.status} - ${Status[e.status]}</h1>
<!-- <h1>${e.status} - ${e.message}</h1> -->
</body>
</html>`;
} else {
context.response.body = `
<!DOCTYPE html>
<html>
<body>
<h1>${e.status} - ${Status[e.status]}</h1>
<!-- <h1>${e.status} - ${e.message}</h1> -->
</body>
</html>`;
}
} else if (e instanceof Error) {
context.response.status = 500;
// ...while 5XX errors are set to false as they are internal server errors and
// exposing details could leak important server security information.
context.response.body = `
<!DOCTYPE html>
<html>
<body>
<h1>500 - Internal Server Error</h1>
</body>
</html>`;
console.log('Unhandled Error:', red(bold(e.message)));
console.log(e.stack);
}
}
});
// Middleware 2 - Logger
app.use(async (context, next) => {
await next();
const rt = context.response.headers.get('X-Response-Time');
console.log(`${green(context.request.method)} ${cyan(context.request.url.pathname)} - ${bold(String(rt),)}`, );
});
// Middleware 3 - Response Time
app.use(async (context, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
context.response.headers.set('X-Response-Time', `${ms}ms`);
});
// End point - Send static content
app.use(async (context) => {
await context.send({
root: `${Deno.cwd()}/var/www/example1.com/public`,
index: 'index.html',
});
});
// Welcome message
app.addEventListener('listen', ({ hostname, port }) => {
console.clear();
console.log(' ');
console.log(bold('Deno 1.1.1 - Static HTTP Server'));
console.log(gray(' #host: ') + yellow(`${hostname}`));
console.log(gray(' #port: ') + yellow(`${port}`));
console.log(gray(' Status: ') + green('online'));
console.log(gray(' #root: ') + cyan('/var/www/example1.com/public'));
console.log(gray(' #index: ') + cyan('index.html'));
console.log(' ');
});
await app.listen({ hostname: '127.0.0.1', port: 80 });
// run the server
// deno run --allow-net --allow-read httpServer.ts

Read the documentation on serveTLS and listenAndServeTLS.
If you have a valid certificate, you shouldn't have any issues. However, I had some difficulty overriding rejections of self-signed SSL certificates. Also, I ended up serving ssl on a port other than 443, due to permission errors I received while trying to run an HTTPS server as a local user.
Those are the issues I ran into and how I got around them. I hope it helps.

Related

Trouble configuring NextAuth and tRPC's Websockets when deploying

I have built an app with tRPCv10 and NextAuth. As my app requires realtime updates, I have followed tRPC's docs on implementing subscriptions with websockets. tRPC docs on subscription tRPC example app.
From what I understand, to use websockets in tRPC, I need to create a standalone http server and run it alongside my Nextjs app. When I emit data through EventEmitter, the data is proxied through this http server and sent to all other subscribers. Thus, I have deployed my standalone http server on Railway with port 6957, and my Nextjs app on Vercel
Everything is working well when I am developing, through localhost. However, when I'm trying to deploy it, there is an error trying to connect to the websocket server and I'm receiving a NextAuth error when logging in too.
For example, my server name is "https://xxx-server.up.railway.app/" and my Nextjs app is "https://xxx-client.vercel.app/".
On the client side, I'm receiving an error: WebSocket connection to 'wss://xxx-server.up.railway.app:6957/' failed. When I hit the login button which runs the authorize function in NextAuth, the console returns the error: POST https://xxx-client.vercel.app/api/auth/calback/credentials? 401.
For reference, here are the file for _app.tsx and my websocket server:
// _app.tsx
const MyApp: AppType = ({
Component,
pageProps: { session, ...pageProps },
}) => {
return (
<SessionProvider session={session} refetchOnWindowFocus={false}>
<Component {...pageProps} />
</SessionProvider>
);
};
const getBaseUrl = () => {
if (typeof window !== "undefined") {
return "";
}
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
};
function getEndingLink() {
if (typeof window === "undefined") {
return httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
});
}
const client = createWSClient({
url: "wss://xxx-server.up.railway.app:6957"
});
return wsLink<AppRouter>({
client,
});
}
export default withTRPC<AppRouter>({
config({ ctx }) {
/**
* If you want to use SSR, you need to use the server's full URL
* #link https://trpc.io/docs/ssr
*/
const url = `${getBaseUrl()}/api/trpc`;
return {
url,
transformer: superjson,
links: [getEndingLink()],
/**
* #link https://react-query.tanstack.com/reference/QueryClient
*/
// queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
};
},
/**
* #link https://trpc.io/docs/ssr
*/
ssr: true,
})(MyApp);
// prodServer.ts
const port = parseInt(process.env.PORT || "3000", 10);
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = http.createServer((req, res) => {
const proto = req.headers["x-forwarded-proto"];
if (proto && proto === "http") {
// redirect to ssl
res.writeHead(303, {
location: `https://` + req.headers.host + (req.headers.url ?? ""),
});
res.end();
return;
}
const parsedUrl = parse(req.url!, true);
handle(req, res, parsedUrl);
});
const wss = new ws.Server({ server });
const handler = applyWSSHandler({ wss, router: appRouter, createContext });
process.on("SIGTERM", () => {
console.log("SIGTERM");
handler.broadcastReconnectNotification();
});
server.listen(port);
// tslint:disable-next-line:no-console
console.log(
`> Server listening at http://localhost:${port} as ${
dev ? "development" : process.env.NODE_ENV
}`
);
});

Deno: Server Sent Events

Server Sent Events are a valuable tool to open a persistent connection to a web server, where the server has the ability to push new data to the client, when available.
Using this technology in Node.js is quite straightforward and can be implemented with the following code example:
#!/usr/bin/env node
'use strict';
const http = (options, listener) => require('http').createServer(listener).listen(options.port);
http({ port: 8080 }, (req, res) => {
switch (req.url) {
case '/server-sent-events': {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
});
const sendDate = () => res.write(`data: ${new Date()}\n\n`);
sendDate();
const interval = setInterval(sendDate, 1000);
req.on('close', () => clearInterval(interval));
} break;
default: {
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
});
res.end(`
<!DOCTYPE html>
<html>
<head>
<title>Server Send Events</title>
<meta charset="utf-8">
<script>
const sse = new EventSource('/server-sent-events');
sse.onerror = () => document.body.innerHTML = 'Connection Error';
sse.onmessage = ({ data }) => document.body.innerHTML = data;
</script>
</head>
<body></body>
</html>
`);
}
}
});
Unfortunately I am not able to achieve the same goal with Deno, as there is no simple write method on the request object, but I guess it has to be implemented somehow using the req.w buffer. Can you help me please finish off the following example code, so the Server Sent Events can be utilised with Deno as well?
#!/usr/bin/env deno run --allow-net
import { listenAndServe as http } from 'https://deno.land/std/http/server.ts';
http({ port: 8080 }, (req) => {
switch (req.url) {
case '/server-sent-events': {
// missing steps:
// * setup the server sent event headers
// * create the interval and send the date periodically
// * clear the interval when the connection gets closed
} break;
default: {
req.respond({
headers: new Headers({
'Content-Type': 'text/html; charset=utf-8',
}),
body: `
<!DOCTYPE html>
<html>
<head>
<title>Server Send Events</title>
<meta charset="utf-8">
<script>
const sse = new EventSource('/server-sent-events');
sse.onerror = () => document.body.innerHTML = 'Connection Error';
sse.onmessage = ({ data }) => document.body.innerHTML = data;
</script>
</head>
<body></body>
</html>
`,
});
}
}
});
Thank you very much for your support!
[Update 2021-11-04]:
I have made some progress doing some research across different sources (https://deno.land/std#0.76.0/http/server.ts, https://github.com/denoland/deno/issues/4817) and got a step closer to the solution. Using the updated example below at least the setup and usage of the Server Sent Events do work now. The remaining issue (besides cleaning up and refactoring of the code) remains the safe detection when the incoming request has been closed (see comments in the source code below):
#!/usr/bin/env deno run --allow-net
import { listenAndServe as http } from 'https://deno.land/std/http/server.ts';
http({ port: 8080 }, (req) => {
switch (req.url) {
case '/server-sent-events': {
// set up a quick´n´dirty write method without error checking
req.write = (data) => {
req.w.write(new TextEncoder().encode(data));
req.w.flush();
};
// setup the server sent event headers
let headers = '';
headers += 'HTTP/1.1 200 OK\r\n';
headers += 'Connection: keep-alive\r\n';
headers += 'Cache-Control: no-cache\r\n';
headers += 'Content-Type: text/event-stream\r\n';
headers += '\r\n';
req.write(headers);
// create the interval and send the date periodically
const sendDate = () => req.write(`data: ${new Date()}\n\n`);
sendDate();
const interval = setInterval(sendDate, 1000);
// final missing step:
// * clear the interval when the connection gets closed
// currently dropping the connection from the client will
// result in the error: Uncaught (in promise) BrokenPipe:
// Broken pipe (os error 32)
// this error also does not seem to be catchable in the
// req.write method above, so there needs to be another safe
// way to prevent this error from occurring.
} break;
default: {
req.respond({
headers: new Headers({
'Content-Type': 'text/html; charset=utf-8',
}),
body: `
<!DOCTYPE html>
<html>
<head>
<title>Server Send Events</title>
<meta charset="utf-8">
<script>
const sse = new EventSource('/server-sent-events');
sse.onerror = () => document.body.innerHTML = 'Connection Error';
sse.onmessage = ({ data }) => document.body.innerHTML = data;
</script>
</head>
<body></body>
</html>
`,
});
}
}
});
[Update 2021-04-16]
All issues have been resolved and are posted in my accepted answer below.
Deno's http library doesn't support SSE, but you can use Oak Framework, or implement it yourself.
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
const router = new Router();
router.get('/', ctx => {
ctx.response.body = `
<!DOCTYPE html>
<html>
<head>
<title>Server Send Events</title>
<meta charset="utf-8">
<script>
const sse = new EventSource('/server-sent-events');
sse.onerror = () => document.body.innerHTML = 'Connection Error';
sse.onmessage = ({ data }) => document.body.innerHTML = data;
</script>
</head>
<body></body>
</html>
`;
})
router.get("/server-sent-events", (ctx) => {
const target = ctx.sendEvents();
const sendDate = () => target.dispatchMessage(`${new Date()}`);
sendDate();
const interval = setInterval(sendDate, 1000);
});
app.use(router.routes());
await app.listen({ port: 8080 });
In the end I have found an answer to my question and so the full answer with plenty of comments follows, so you get a working version of server sent events in Deno. The solution below also solves the os error 32, which gets caused by not catching the connection writer flash method:
#!/usr/bin/env deno run --allow-net
// imports
import { ServerRequest, listenAndServe as http } from 'https://deno.land/std/http/server.ts';
// commodity
const encoder = new TextEncoder();
const print = console.log;
// start the web-server
// this one allows the endpoint `/server-sent-events`, which hosts a clock that
// will be refreshed every second (the efficiency of the clock solution could of
// course be optimised, as every client gets its own clock interval, but this
// this does not matter as this example wants to show how to setup and clean a
// task for every connecting client)
// all other requests will be answered with a simple html page that subscribes
// to the sse-based clock
http({ port: 8080 }, async (req) => {
// ip address of the client (formatted as `ip:port`, so we cut the `:port` part
// of it)
const ip = req.headers.get('host').split(':').slice(0, -1).join(':');
// determine the endpoint to access
switch (req.url) {
// host the server sent event based clock
case '/server-sent-events': {
// logging
print(`+ Client ${ip} connected`);
// prepare the disconnect promise. we will use this one later on to await
// the clients disconnect, so we can properly clean up. so the promise will
// be resolved manually by us when we detect a disconnect from the client
// on an attempt to send new data to him (unfortunately there seems to be
// no other way to detect when the client actually closed the connection)
let resolver;
const disconnect = new Promise((resolve) => resolver = resolve);
// write helper
req.write = async (data) => {
// send the current data to the client
req.w.write(encoder.encode(data));
// to actually send the data we need to flush the writer first. we need
// to try/catch this part, as not handling errors on flush will lead to
// the `Broken pipe (os error 32)` error
try {
await req.w.flush();
} catch(err) {
// throw any errors but the broken pipe, which gets thrown when the
// client has already disconnected and we try to send him new data
// later on
if (err.name !== 'BrokenPipe') {
throw err;
}
// close the connection from our side as well
req.conn.close();
// resolve our `disconnect` promise, so we can clean up
resolver();
}
};
// date writer (interval method which pushes the current date to the client)
const sendDate = async () => await req.write(`data: ${new Date()}\n\n`);
// prepare and send the headers
let headers = '';
headers += `HTTP/1.1 200 OK\r\n`;
headers += `Connection: keep-alive\r\n`;
headers += `Cache-Control: no-cache\r\n`;
headers += `Content-Type: text/event-stream\r\n`;
headers += `\r\n`;
await req.write(headers);
// send the date now for the first time and then every second
sendDate();
const interval = setInterval(sendDate, 1000);
// await until the clients disconnects to clean up. so we will be "stuck"
// here until a disconnect gets detected as we use a promise based approach
// to detect the disconnect
await disconnect;
clearInterval(interval);
// logging
print(`- Client ${ip} disconnected`);
} break;
// all other requests host a simple html page which subscribes to the clock
default: {
print(`* Serve website to ${ip}`);
req.respond({
headers: new Headers({
'Content-Type': 'text/html; charset=utf-8',
}),
body: `
<!DOCTYPE html>
<html>
<head>
<title>Server Sent Events</title>
<meta charset="utf-8">
<script>
const sse = new EventSource('/server-sent-events');
sse.onerror = () => document.body.innerHTML = 'Connection Error';
sse.onmessage = ({ data }) => document.body.innerHTML = data;
</script>
</head>
<body></body>
</html>
`,
});
}
}
});
This example seems more idiomatic.
import { serve } from "https://deno.land/std#0.116.0/http/server.ts";
const msg = new TextEncoder().encode("data: hello\r\n");
serve(async (_) => {
let timerId: number | undefined;
const body = new ReadableStream({
start(controller) {
timerId = setInterval(() => {
controller.enqueue(msg);
}, 1000);
},
cancel() {
if (typeof timerId === "number") {
clearInterval(timerId);
}
},
});
return new Response(body, {
headers: {
"Content-Type": "text/event-stream",
},
});
});
console.log("Listening on http://localhost:8000");

How can I call an AJAX funtion from server side in Express/Node JS?

Following is my POST function in Node js. I want to call a funtion in my client side HTML to display an error message on the page.
router.post('/',(req,res)=>{
const data = JSON.stringify({
institute_name: req.body.institute_name,
email : req.body.email,
password : req.body.password
})
const options = {
host:'localhost',
port:'8888',
path:'/registerInstitute',
method:'POST',
headers: {
'Content-Type':'application/json'
}
}
const req1 = http.request(options, (res1)=>
{
const status = res1.statusCode
if (status == 201)
{
//admin created
res.redirect('/?account_created=true')
}
else if ( status == 409)
{
//CALL AJAX FUNCTION TO DISPLAY ERROR MSG
}
})
req1.write(data)
req1.end()
})
The simple answer is NO. But there are workarounds to do so and I've demonstrated the one that will suite your scenario
Sockets
You can use web sockets to trigger event to notify client to run specific function
On service side you can do something like this:
var io = require('socket.io').listen(80); // initiate socket.io server
if (
io.sockets.on('connection', function (socket) {
if ( error == 403 ) {
socket.emit('runErrorFunction', { error: "error data" });
}
});
You can do something like this on client side:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost'); // connect to server
socket.on('runErrorFunction', function (data) { // listen to runErrorFunction event raised by the server
// Here goes your error showing logic
});
</script>
Here is the link for more info Socket.io

Uncaught gapi.auth2.ExternallyVisibleError: Invalid cookiePolicy, Uncaught Error: listen EADDRINUSE :::8070 in Electron

I trying to make google sign in out by OAuth2.0 in electron
I read this comment, so I made server.js too
Google Sign In API only works in a running web server.
these what I did to start a project and got errors
node server.js
npm start
Uncaught Error: listen EADDRINUSE :::8070 Uncaught uO {message:
"Invalid cookiePolicy", stack: "gapi.auth2.ExternallyVisibleError:
Invalid cookieP…pis.google.com/js/platform.js?onload=init:18:356)"}
Uncaught Error: listen EADDRINUSE :::8070
server.js
console.log("server start")
var http = require('http');
//create a server object:
var server =http.createServer(function (req, res) {
res.write('Hello World!'); //write a response to the client
res.end('test'); //end the response
}).listen(8070); //the server object listens on port 8080
server.on('listening',function(){
console.log('ok, server is running');
});
index.html
<script>
var gauth;
function checkLoginStatus() {
var loginBtn = document.querySelector('#loginBtn');
var nameTxt = document.querySelector('#name');
if (gauth.isSignedIn.get()) {
console.log("logined");
loginBtn.value = 'Logout'
var profile=gauth.currentUser.get().getBasicProfile();
nameTxt.innerHTML='Welcome <strong>'+profile.getName()+'</strong'
}
else {
console.log("loged out");
loginBtn.value = 'Login'
nameTxt.innerHTML='';
}
}
function init() {
console.log("init")
gapi.load('auth2', function () {
console.log("auth2");
gauth = gapi.auth2.init(
{client_id: '1067161273424-gud6khml7r3bssi35u2rj5lchepj25bo.apps.googleusercontent.com'}
).then(function () {
console.log("gapi done");
})
gauth.then(function () {
console.log("google auth success");
checkLoginStatus();
}, function () {
console.log("google auth faill");
})
});
}
</script>
<script>
// You can also require other files to run in this process
require('./renderer.js')
</script>
<span id="name"></span>
<input type="button" id="loginBtn" value="checking.." onclick="
if(this.value==='Login'){
gauth.signIn().then(function() {
console.log('gauth.signIn()');
checkLoginStatus();
});
}
else {
gauth.signOut().then(function() {
console.log('gauth.signOut()');
checkLoginStatus();
});
}
">
<script src="https://apis.google.com/js/platform.js?onload=init" async defer></script>

Nunjucks Async Rendering with Keystone View

In an Express-based app, I'm running an async function to fetch image source from a database and render them into a Nunjucks view engine
Nunjucks is rendering the following in the DOM
<img src="[object Promise]">
I have already enabled Nunjucks's async rendering with web: { async: true } and enabled the nunjucks async api with a callback like so
// controller.js (KeystoneJS app)
view.render('index', function (err, res) {
console.log('err at index render', err); // undefined
return res;
});
How can i get the resolved value of my async function?
As I understand it, Nunjucks doesn't support asynchronous render directly. You can use asynchronous filters to get it. Maybe I'm wrong.
Imho, use feature with Be Careful! mark is a not good idea.
// template.njk
Hello {{user_id | find | attr('name') }}!
// app.js
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
// Async filter
env.addFilter('find', function(a, cb) {
setTimeout(function () {
cb(null, {
name: 'Smith'
});
}, 10);
}, true)
// Sync filter
env.addFilter('attr', function(obj, attr) {
return obj && attr && obj[attr];
});
env.render('template.njk',
{user_id: 1}, // pass sync vars
function(err, res) {
if (err)
return;
console.log(res);
return res
}
);
I don't know about nunjucks, but you can implement async functionality regardless of the view engine being used. To show the idea, I tried to reproduce your situation. I created an HTML file named index.html with an img tag without any src attribute:
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Reproduce</title>
</head>
<body>
<img id='bg'></img>
<script src='./so.js'></script>
</body>
</html>
I have a <script> tag in my HTML which links to my so.js file shown below by which an image is requested by sending a HTTP request to NodeJS/Express server:
getImage();
function getImage(){
// Get an image by its name as URL parameter
fetch('/bg/background.jpg',{
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}).then(result=>{
return result.blob()
}).then(result=>{
console.log('result -> ', result)
document.querySelector('#bg').src=URL.createObjectURL(result)
document.querySelector('#bg').style.width='200px'
}).catch(err=>{
console.log('err -> ', err)
})
}
Here is my NodeJS/ExpressJS code inside a file name server.js:
express=require('express')
bodyParser=require('body-parser')
path=require('path')
fetch=require('node-fetch')
server=express()
//Body-parser middleware
server.use(bodyParser.json())
server.use(bodyParser.urlencoded({extended:false}))
server.use(bodyParser.raw())
//Set static path
server.use(express.static(path.join(__dirname,'public')))
server.get('/',(req,res)=>{
res.render('index.html')
})
// Pick a file on hard disk
// and send it as "Blob" to the browser
server.get('/bg/:name',(req,res)=>{
var options = {
root: __dirname + '/public/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
var fileName = req.params.name;
res.sendFile(fileName, options, function (err) {
if (err) {
next(err);
} else {
console.log('Sent:', fileName);
}
});
})
server.listen('8000',()=>{
console.log('Server listening on port 8000...')
})
As can be seen, I'm doing my async communication between browser and server by implementing fetch API and without even touching the view engine.
I just wanted to provide an alternative idea to use fetch API and do the async HTTP communications with it regardless of any view rendering engine that is being used.

Resources