Archive for July, 2020
iTunes Support?
It’s funny to use the word Apple and Support. It seems congress found that out today too. Not really surprised but there’s no way I could post a Apple’s simple spelling error on the web page. You would think there would be a way, so they could correct their errors when they make them but alas not. I tried the web page, which gave me no potential solutions and no chat opportunity to report it or fix it. I imagine they think their staff too professional to make a mistake like this.
It’s simply tedious problem. They search for Endeavour correctly in the iTunes Store but when you purchase the Endeavour Season 7, it downloads Endeavor (American spelling). One has to wonder what’s underneath the hood of iTunes now. Is it a relational database where their should be only one spelling of a title or a document database where there may be more than one spelling? Who knows, here’s what it looks like in my iTunes screen:
If anybody knows how to report this to correct it without calling Apple Support, let me know. I already know how to fudge (finesse) it but would prefer not to do so. Thanks to anybody who can fix it! BTW, I’m on hold for Apple Support to report it. 😉
Express.js & MySQL
Sometimes, you just half to chuckle. A couple folks felt that I didn’t give enough information in my post showing how to configure a small Node.js application that could access a MySQL database. Specifically, they wanted me to explain the following:
- Configure your Express.js and MySQL development in a single Node.js application.
- How to convert the list of
RowDataPacket
objects as elements of data, which is really just simple JavaScript knowledge. - How to bind variables into the query.
Like the other blog post, this one assumes you’ve performed a global install of Node.js on a Linux server. If you’re unfamiliar with how to perform a global Node.js installation, I cover how to do it in this earlier blog post.
Before you write the Node.js applicaiton, you need to setup a db
developer directory. A global install of Node.js means you need to create a node_modules
symbolic link to the /usr/local/lib/node_modules
directory in the db
directory (in Linux). You can use the following Linux command from within the db
directory to create the appropriate symbolic link:
ln -s /usr/local/lib/node_modules `pwd`/node_modules |
or, assuming you have a /home/some_user/db directory
ln -s /usr/local/lib/node_modules /home/some_user/node_modules |
After creating the node_modules
symbolic link, you need to run the following two npm
commands. Please note that second command holds the secret-sauce for generating a package.json
file that supports Express.js and the MySQL driver:
npm init --y sudo npm install --save express mysql |
Then, you need to replace the package.json
file with the contents of the package-lock.json
file from your last npm
command.
Here’s a small sample program that uses Express.js, converts the RowDataPackets
collection, and binds local variables into the query:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // Require libraries. const express = require('express') const mysql = require('mysql') // Create a mysql connection. const connection = mysql.createConnection({ host: 'localhost', user: 'student', password: 'student', database: 'studentdb' }) // Declare two local variables. const start_date = '2001-01-01' const end_date = '2003-12-31' // Connect and display results in the console log. connection.connect((err) => { if (err) throw err else { console.log('Connected to MySQL Server!\n') connection.query("SELECT i.item_title " + ", date_format(i.release_date,'%d-%M-%Y') AS release_date " + "FROM item i JOIN common_lookup cl " + "ON i.item_type = cl.common_lookup_id " + "WHERE cl.common_lookup_type = 'BLU-RAY' " + "AND i.release_date BETWEEN ? AND ? " + "ORDER BY i.release_date" ,[start_date, end_date], function (err, result) { if (err) throw err else { // Prints the index value in the RowDataPacket. for(let element in result) { console.log(result[element].item_title + ', ' + result[element].release_date) } console.log('') console.log('Press Ctrl-C to terminate ...') } }) } }) |
Line 28 shows two question marks. They act as placeholders for binding variables. Then, on line 30 you see a collection of the start_date
and end_date
local variables, which is the second argument to the query()
function.
Rather than define individual variables, you can pass them as a collection directly. For example, you replace lines 14 and 15 with this single line:
14 | const dates = ['2001-01-01','2003-12-31'] |
Then, you can pass dates
as the second argument to the query()
function, like this:
30 | ,dates, function (err, result) { |
Lines 35 and 36 show you how to convert a collection of RowDataPacket
objects into elements of data. The for
loop assigns the index value to the element
variable, which lets you address a single RowDataPacket
object instance. The dot (“.
“) notation lets you use the name in a name-value pair to reference its value.
It displays the following:
Connected to MySQL Server! Star Wars II, 16-May-2002 Harry Potter and the Chamber of Secrets, 28-May-2002 Harry Potter and the Sorcerer's Stone, 28-May-2002 Die Another Day, 03-June-2003 Press Ctrl-C to terminate ... |
Naturally, I’ll get around to writing something up that shows how to leverage MySQL data into a dynamic form with Handlebars at some point in the near future.
As always, I hope this helps those looking for a solution.
Node.js & MySQL
These are my notes for creating a small Node.js application that queries a MySQL database. The post will show you how to:
- Configure your Node.js development directory.
- Build a small application to test a MySQL connection.
- Build a small application that connects to the MySQL database and queries data.
This blog post assumes you’ve performed a global install of Node.js on a Linux server. If you’re unfamiliar with how to perform a global Node.js installation, I cover how to do it in this earlier blog post.
Before you write the Node.js applicaiton, you need to setup a db
developer directory. Then, create a node_modules
symbolic link to the /usr/local/lib/node_modules
directory in the db
directory. You can use the following command from the db
directory:
ln -s /usr/local/lib/node_modules `pwd`/node_modules |
After creating the node_modules
symbolic link, you need to run the following two npm
commands:
npm init --y npm install --save mysql |
The first command sets up a generic package.json
file, and the second adds the mysql
package and supporting packages to the package.json
file. These two steps configure the Node.js side of these examples.
They both require that you create the student
user with a native password, like so:
CREATE USER 'student'@'localhost' IDENTIFIED WITH 'mysql_native_password' BY 'student'; GRANT ALL PRIVILEGES ON studentdb.* TO 'student'@'localhost'; FLUSH PRIVILEGES; |
The following example shows you how to check a connection to the MySQL database:
const mysql = require('mysql') const connection = mysql.createConnection({ host: 'localhost', user: 'student', password: 'student', database: 'studentdb' }) connection.connect((err) => { if (err) throw err else console.log('Connected to MySQL Server!\n') console.log('User configured wiht mysql_native_password.\n'); console.log('Press Ctrl-C to terminate ...') }) |
You extend the previous example by adding a query component and returning the query result
value to the console’s log (leveraging the w3school’s Node.js and MySQL tutorial example):
const mysql = require('mysql') const connection = mysql.createConnection({ host: 'localhost', user: 'student', password: 'student', database: 'studentdb' }) connection.connect((err) => { if (err) throw err else { console.log('Connected to MySQL Server!\n') connection.query('SELECT DISTINCT item_title FROM item', function (err, result) { if (err) throw err else console.log(result) console.log('Press Ctrl-C to terminate ...') }) } }) |
It should display the following:
Connected to MySQL Server! [ RowDataPacket { item_title: 'The Hunt for Red October' }, RowDataPacket { item_title: 'Star Wars I' }, RowDataPacket { item_title: 'Star Wars II' }, RowDataPacket { item_title: 'Star Wars III' }, RowDataPacket { item_title: 'The Chronicles of Narnia' }, RowDataPacket { item_title: 'RoboCop' }, RowDataPacket { item_title: 'Pirates of the Caribbean' }, RowDataPacket { item_title: 'MarioKart' }, RowDataPacket { item_title: 'Splinter Cell' }, RowDataPacket { item_title: 'Need for Speed' }, RowDataPacket { item_title: 'The DaVinci Code' }, RowDataPacket { item_title: 'Cars' }, RowDataPacket { item_title: 'Beau Geste' }, RowDataPacket { item_title: 'I Remember Mama' }, RowDataPacket { item_title: 'Tora! Tora! Tora!' }, RowDataPacket { item_title: 'A Man for All Seasons' }, RowDataPacket { item_title: 'Hook' }, RowDataPacket { item_title: 'Around the World in 80 Days' }, RowDataPacket { item_title: 'Harry Potter and the Sorcerer\'s Stone' }, RowDataPacket { item_title: 'Camelot' }, RowDataPacket { item_title: 'Casino Royale' }, RowDataPacket { item_title: 'Die Another Day' }, RowDataPacket { item_title: 'Golden Eye' }, RowDataPacket { item_title: 'Tomorrow Never Dies' }, RowDataPacket { item_title: 'The World Is Not Enough' }, RowDataPacket { item_title: 'Brave Heart' }, RowDataPacket { item_title: 'Christmas Carol' }, RowDataPacket { item_title: 'Scrooge' }, RowDataPacket { item_title: 'Clear and Present Danger' }, RowDataPacket { item_title: 'Harry Potter and the Chamber of Secrets' }, RowDataPacket { item_title: 'Harry Potter and the Prisoner of Azkaban' }, RowDataPacket { item_title: 'Harry Potter and the Goblet of Fire' }, RowDataPacket { item_title: 'Harry Potter and the Order of the Phoenix' }, RowDataPacket { item_title: 'Harry Potter and the Half Blood Prince' }, RowDataPacket { item_title: 'Harry Potter and the Deathly Hallows, Part 1' }, RowDataPacket { item_title: 'Harry Potter and the Deathly Hallows, Part 2' }, RowDataPacket { item_title: 'Tron' }, RowDataPacket { item_title: 'The Avengers' }, RowDataPacket { item_title: 'Thor: The Dark World' } ] Press Ctrl-C to terminate ... |
As always, I hope this helps those looking to learn
Node.js Routing
I liked the example used to explore basic routing in Chapter 2 of the Web Development with Node & Express book. I embellished the Node.js router example and static pages just a bit. The routing example requires you create a public
subdirectory where you deploy the code and put the about.html
, home.html
, and 404.html
files in the public
subdirectory. Then, you must put a logo.png
file in a tmp
subdirectory inside of the public
directory. The book assumes you know how to build these pages, which seems reasonable but just in case, here are the files I used to test the book’s example.
The about.html
page:
<html> <head> <title>About Page</title> </head> <body> <p>A small sample Node.js routing example.</p> <p><img width="25" src="/img/logo.png" /> <sup><i>MEAN Stack Development</i></sup></p> </body> </html> |
The home.html
page:
<html> <head> <title>Home Page</title> </head> <body> <p style="font-size:110%">Star Trek: The Original Series - Season 1</p> <p><img width="300" src="/img/StarTrekOriginal1.png" /></p> <p><img width="25" src="/img/logo.png" /> <sup><i>MEAN Stack Development</i></sup></p> </body> </html> |
The 404.html
page:
<html> <head> <title>404 Error Message</title> </head> <body> <p>A 404 Error Message Page.</p> <p><img width="25" src="/img/logo.png" /> <sup><i>MEAN Stack Development</i></sup></p> </body> </html> |
The routing example sets the path to lowercase, which is important when you type the URL to verify physical files. For example, you need to use the routing startrekoriginal1.png
string value in the URL. It will fail if you use the mixed case actual file name of the StarTrekOriginal1.png
file. That’s because the routing maps the lowercase string to the physical file.
While the /public
directory is the home directory of the virtual map as a rule, you can’t use it in the URL (as explained in next Chapter 3). Also, the router uses /img
as a virtual directory which maps to the physical /tmp
subdirectory. When you want to validate a physical image file you need to know these two rules. They explain why the following URL lets you verify a physical image file found in the /public/tmp
directory.
localhost:3000/img/StarTrekOriginal1.png |
you will trigger the 404 Error page. Here’s the Node.js routing code:
/* Construct a web server. */ const http = require('http') const fs = require('fs') const port = process.env.PORT || 3000 /* Function uses the fs package to read files. */ function serveStaticFile(res, path, contentType, responseCode = 200) { fs.readFile(__dirname + path, (err, data) => { if (err) { res.writeHead(500, { 'Content-Type': 'text/plain' }) return res.end('500 - Internal Error') } res.writeHead(responseCode, { 'Content-Type': contentType }) res.end(data) }) } /* Create the Node.js server. */ const server = http.createServer((req, res) => { // Normalize URL by removing query string, optional // trailing slash, and making it lowercase. const path= req.url.replace(/\/?(?:\?.*)?$/,'').toLowerCase() switch(path) { case '': serveStaticFile(res, '/public/home.html', 'text/html' ) break case '/about': serveStaticFile(res, '/public/about.html', 'text/html' ) break case '/img/startrekoriginal1.png': serveStaticFile(res, '/public/tmp/StarTrekOriginal1.png', 'image/png' ) break case '/img/logo.png': serveStaticFile(res, '/public/tmp/logo.png', 'image/png' ) break default: serveStaticFile(res, '/public/404.html', 'text/html', 404 ) break } }) server.listen(port, () => console.log(`server started on port ${port}; ` + 'press Ctrl-C to terminate...')) |
Assuming you name the Node.js routing example helloworld3.js
, you would start the router with the following command:
node helloworld3.js |
It should start the router. Enter the following URL:
http://localhost:3000 |
You should see a page rendered like the following:
As always, I hope this helps those trying to use this technology.
Node.js Fedora Install
I want to add the MEAN (MongoDB, Express.js, Angular.js, and Node.js) stack to my backend server development course. This post documents the installation and configuration of components on Fedora 30.
The first step requires installing the Node package. The Node package also contains the Node package manager (npm
). You install the Node packages as the root
user or as a sudoer user with the following command.
yum install -y npm |
It should produce the following installation log:
Last metadata expiration check: 1:10:42 ago on Wed 08 Jul 2020 06:57:52 PM MDT. Dependencies resolved. ================================================================================================================================ Package Architecture Version Repository Size ================================================================================================================================ Installing: npm x86_64 1:6.13.4-1.10.19.0.1.fc30 updates 3.8 M Installing dependencies: nodejs x86_64 1:10.19.0-1.fc30 updates 88 k nodejs-libs x86_64 1:10.19.0-1.fc30 updates 9.1 M Installing weak dependencies: nodejs-full-i18n x86_64 1:10.19.0-1.fc30 updates 7.3 M Transaction Summary ================================================================================================================================ Install 4 Packages Total download size: 20 M Installed size: 91 M Downloading Packages: (1/4): nodejs-10.19.0-1.fc30.x86_64.rpm 173 kB/s | 88 kB 00:00 (2/4): nodejs-full-i18n-10.19.0-1.fc30.x86_64.rpm 2.8 MB/s | 7.3 MB 00:02 (3/4): nodejs-libs-10.19.0-1.fc30.x86_64.rpm 2.7 MB/s | 9.1 MB 00:03 (4/4): npm-6.13.4-1.10.19.0.1.fc30.x86_64.rpm 1.3 MB/s | 3.8 MB 00:02 -------------------------------------------------------------------------------------------------------------------------------- Total 4.9 MB/s | 20 MB 00:04 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Running scriptlet: npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 1/1 Preparing : 1/1 Installing : nodejs-libs-1:10.19.0-1.fc30.x86_64 1/4 Installing : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 2/4 Installing : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 3/4 Installing : nodejs-1:10.19.0-1.fc30.x86_64 4/4 Running scriptlet: nodejs-1:10.19.0-1.fc30.x86_64 4/4 Verifying : nodejs-1:10.19.0-1.fc30.x86_64 1/4 Verifying : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 2/4 Verifying : nodejs-libs-1:10.19.0-1.fc30.x86_64 3/4 Verifying : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 4/4 Installed: nodejs-1:10.19.0-1.fc30.x86_64 nodejs-full-i18n-1:10.19.0-1.fc30.x86_64 nodejs-libs-1:10.19.0-1.fc30.x86_64 npm-1:6.13.4-1.10.19.0.1.fc30.x86_64 Complete! |
After installing the Node package, you should use the Node package manager (npm
) to install the Node Monitor nodemon
. nodemon
is a popular utility that automatically lets you restart Node programs when you make changes to the source code.
While npm
is installed as part of the Node package, you must use npm
to install the Node Monitor. The following command installs the nodemon
globally on your Fedora system. The -g
flag lets you install it globally, which is important when you manage package.json
files.
npm install -g nodemon |
You install nodemon globally but most of your web app or project files will be installed locally. Node is a different paradigm than building an Apache or IIS web application because Node provides a framework for you to build a web server.
Here’s a quick Hello World! example that I borrowed a JavaScript helloworld.js
file from an excellent Web Development with Node & Express: Leveraging the JavaScript Stack by Ethan Brown. For those who haven’t worked with JavaScript in years, semicolons are optional now.
/* Construct a web server. */ const http = require('http') const port = process.env.PORT || 3000 const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello world!') }) server.listen(port, () => console.log(`server started on port ${port}); ` + 'press Ctrl-C to terminate...')) |
I put this in /var/www/html/node
directory, which is owned by the superuser, root
. You need to start the server before accessing it from a browser. You can start the program with the following syntax as a privileged user:
node /var/www/html/node/helloworld.js |
Then, you can use the localhost to access it with the following URL:
http://localhost:3000 |
It will display the following:
Next, you need to use the Node Package Manager (npm) to install the Express.js packages. You do that with the following syntax:
npm install -g express express-generator |
It should produce a console out put like the following:
npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) /usr/local/bin/express -> /usr/local/lib/node_modules/express-generator/bin/express-cli.js + express@4.17.1 + express-generator@4.16.1 added 60 packages from 42 contributors in 4.798s |
After you install all the packages, you can inspect them with the following command. The packages are found in the /usr/local/lib/node_modules/express
directory. The listing is generated from the package.json
file on Fedora and Ubuntu Linux.
npm list -g |
It should display something like this:
/usr/local/lib ├─┬ express@4.17.1 │ ├─┬ accepts@1.3.7 │ │ ├─┬ mime-types@2.1.27 │ │ │ └── mime-db@1.44.0 │ │ └── negotiator@0.6.2 │ ├── array-flatten@1.1.1 │ ├─┬ body-parser@1.19.0 │ │ ├── bytes@3.1.0 │ │ ├── content-type@1.0.4 deduped │ │ ├── debug@2.6.9 deduped │ │ ├── depd@1.1.2 deduped │ │ ├─┬ http-errors@1.7.2 │ │ │ ├── depd@1.1.2 deduped │ │ │ ├── inherits@2.0.3 │ │ │ ├── setprototypeof@1.1.1 deduped │ │ │ ├── statuses@1.5.0 deduped │ │ │ └── toidentifier@1.0.0 │ │ ├─┬ iconv-lite@0.4.24 │ │ │ └── safer-buffer@2.1.2 │ │ ├── on-finished@2.3.0 deduped │ │ ├── qs@6.7.0 deduped │ │ ├─┬ raw-body@2.4.0 │ │ │ ├── bytes@3.1.0 deduped │ │ │ ├── http-errors@1.7.2 deduped │ │ │ ├── iconv-lite@0.4.24 deduped │ │ │ └── unpipe@1.0.0 deduped │ │ └── type-is@1.6.18 deduped │ ├─┬ content-disposition@0.5.3 │ │ └── safe-buffer@5.1.2 deduped │ ├── content-type@1.0.4 │ ├── cookie@0.4.0 │ ├── cookie-signature@1.0.6 │ ├─┬ debug@2.6.9 │ │ └── ms@2.0.0 │ ├── depd@1.1.2 │ ├── encodeurl@1.0.2 │ ├── escape-html@1.0.3 │ ├── etag@1.8.1 │ ├─┬ finalhandler@1.1.2 │ │ ├── debug@2.6.9 deduped │ │ ├── encodeurl@1.0.2 deduped │ │ ├── escape-html@1.0.3 deduped │ │ ├── on-finished@2.3.0 deduped │ │ ├── parseurl@1.3.3 deduped │ │ ├── statuses@1.5.0 deduped │ │ └── unpipe@1.0.0 │ ├── fresh@0.5.2 │ ├── merge-descriptors@1.0.1 │ ├── methods@1.1.2 │ ├─┬ on-finished@2.3.0 │ │ └── ee-first@1.1.1 │ ├── parseurl@1.3.3 │ ├── path-to-regexp@0.1.7 │ ├─┬ proxy-addr@2.0.6 │ │ ├── forwarded@0.1.2 │ │ └── ipaddr.js@1.9.1 │ ├── qs@6.7.0 │ ├── range-parser@1.2.1 │ ├── safe-buffer@5.1.2 │ ├─┬ send@0.17.1 │ │ ├── debug@2.6.9 deduped │ │ ├── depd@1.1.2 deduped │ │ ├── destroy@1.0.4 │ │ ├── encodeurl@1.0.2 deduped │ │ ├── escape-html@1.0.3 deduped │ │ ├── etag@1.8.1 deduped │ │ ├── fresh@0.5.2 deduped │ │ ├── http-errors@1.7.2 deduped │ │ ├── mime@1.6.0 │ │ ├── ms@2.1.1 │ │ ├── on-finished@2.3.0 deduped │ │ ├── range-parser@1.2.1 deduped │ │ └── statuses@1.5.0 deduped │ ├─┬ serve-static@1.14.1 │ │ ├── encodeurl@1.0.2 deduped │ │ ├── escape-html@1.0.3 deduped │ │ ├── parseurl@1.3.3 deduped │ │ └── send@0.17.1 deduped │ ├── setprototypeof@1.1.1 │ ├── statuses@1.5.0 │ ├─┬ type-is@1.6.18 │ │ ├── media-typer@0.3.0 │ │ └── mime-types@2.1.27 deduped │ ├── utils-merge@1.0.1 │ └── vary@1.1.2 └─┬ express-generator@4.16.1 ├── commander@2.15.1 ├── ejs@2.6.1 ├─┬ minimatch@3.0.4 │ └─┬ brace-expansion@1.1.11 │ ├── balanced-match@1.0.0 │ └── concat-map@0.0.1 ├─┬ mkdirp@0.5.1 │ └── minimist@0.0.8 └── sorted-object@2.0.1 |
You can also create a secure node site (HTTPS) with the following additional steps. They include creating a self-signed secure public and private key. This creates the public key:
openssl genrsa -out key.pem |
The openssl
command will generate a private key key.pem
file. It generates something like the following text message to console:
Generating RSA private key, 2048 bit long modulus (2 primes) ...........+++++ .............................................+++++ e is 65537 (0x010001) |
Next, you need to generate a self-signed certificate. You do this in two steps.
- Create a Distinguished Name (
DN
) file. Thecsr.pem
file is theDN
file. You need it to create a self-signed certificate:openssl req -new -key key.pem -out csr.pem
It will prompt you for values, like the following:
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]: State or Province Name (full name) []:MiddleEarth Locality Name (eg, city) [Default City]:Rivendell Organization Name (eg, company) [Default Company Ltd]:Fellowship Organizational Unit Name (eg, section) []:Self Common Name (eg, your name or your server's hostname) []:Localhost Email Address []:bilbo@loth.org Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []:friend An optional company name []:Bilbo
- Use the DN file to create your secure certificate. The following
openssl
command creates the certificate file by using your private keykey.pem
andDN
csr.pem
files.openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
It should generate a Secure certificate cert.pem file and return something like the following to the console.
Signature ok subject=C = XX, ST = MiddleEarth, L = Rivendell, O = Fellowship, OU = Self, CN = Localhost, emailAddress = bilbo@loth.org Getting Private key
You can put these private key (key.pem
) and certificate (cert.pem
) files in an ssl
subdirectory of the directory where you put the JavaScript program. The following creates a secure server page with the following code.
/* Construct a secure web server. */ const https = require('https') const fs = require('fs') const port = process.env.PORT || 3000 const options = { key: fs.readFileSync('ssl/key.pem'), cert: fs.readFileSync('ssl/cert.pem') } const server = https.createServer((options, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello world!') }) server.listen(port, () => console.log(`server started on port ${port}); ` + 'press Ctrl-C to terminate...')) |
If you try launch your browser using the localhost
instead of a DNS or file resolved network name on the designated port, it will raise the following security error:
This site can't be reached https's server IP address could not be found. DNS_PROBE_FINISHED_NXDOMAIN |
An alternate approach to writing a secure server includes using Express.js library. The syntax changes somewhat and you include two new libraries, as shown below:
/* Construct a secure web server. */ const https = require('https') const express = require('express') const fs = require('fs') const app = express() const port = process.env.PORT || 3001 const options = { key: fs.readFileSync('ssl/key.pem'), cert: fs.readFileSync('ssl/cert.pem') } https.createServer(options, app).listen(port, () => { console.log(`Express started in ${app.get('env')} mode ` + `on port + ${port}.`) }) |
This will fail with the following error message if you’re running it with a global installation unless you set the $NODE_PATH
environment variable correctly. Without setting the variable you may get the following error message:
internal/modules/cjs/loader.js:638 throw err; ^ Error: Cannot find module 'express' at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15) at Function.Module._load (internal/modules/cjs/loader.js:562:25) at Module.require (internal/modules/cjs/loader.js:692:17) at require (internal/modules/cjs/helpers.js:25:18) at Object.<anonymous> (/var/www/html/node/helloworldsecure.js:3:17) at Module._compile (internal/modules/cjs/loader.js:778:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10) at Module.load (internal/modules/cjs/loader.js:653:32) at tryModuleLoad (internal/modules/cjs/loader.js:593:12) at Function.Module._load (internal/modules/cjs/loader.js:585:3) |
Sometimes they’ll advise you to do an individual user installation of Express.js to get past this error but that’s not necessary. You just need to set the $NODE_PATH
environment variable as follows:
export NODE_PATH=/usr/local/lib/node_modules |
This will enable the JavaScript to work without error and without a specific user installation. Assuming you name either of these programs as helloworldsecure.js, you run them with the following command:
node helloworldsecure.js |
You can terminate the program with a Ctrl+c
or if use the kill -15 pid
command if you started it as a background process. You can find the process ID (pid
) with the jobs
command.
As always, I hope this is helpful to those starting out with this cool technology stack.
Recursive bash function
While teaching a class on the Linux Command-Line (CLI), the book gave an example of generating a list of random US telephone numbers into a file. The book uses the RANDOM
function to generate segments of the telephone number, and then the grep
command to identify malformed telephone numbers.
My students wanted me to explain why the numbers were malformed. I had to explain that the RANDOM
function returns a random number between 1 and 99,999. The RANDOM
function may return a 1 to 5 digit random number, which means you may get a 1-digit or 2-digit number when you request a 3-digit random number or a 1- to 3-digit number when you request a 4-digit random number.
The author’s example is:
for i in {1..10}; do echo "(${RANDOM:0:3}) ${RANDOM:0:3}-${RANDOM:0:4}" >> list.txt done |
They asked if there was a way to write a shell script that guaranteed random but well-formed US telephone numbers. I said yes, however, you need to write a recursive bash shell function and assign the result to a global variable set in the shell script.
They seemed doubtful, so I wrote it for them. Here’s the script if you’re interested in learning more about bash shell scripting. While I implemented it with an bash array, that’s optional.
#!/usr/bin/bash # ============================================================ # Name: telephone.sh # Author: Michael McLaughlin # Date: 05-May-2020 # ------------------------------------------------------------ # Purpose: Demonstrate how to generate random telehpone # numbers. The RANDOM function returns a random # number between 1 and 99999; and while you can # easily shave off a extra digit guarnteeing a # value above 100 is impossible without logic. # ============================================================ targetLength() { # Declare variable in function-level scope. randomString='' # Check the number of parameters to process. if [[ ${#} = 2 ]]; then # Assign value to function-level and local variables. randomString=${1} formatLength=${2} # Get the length of the telephone number as integer. length=`echo -n ${randomString} | wc -c` # Calculate any shortfall. short=$((${formatLength}-${length})) # Check if the telephone number is too short. if [[ ${short} > 0 ]]; then randomString=`echo "${randomString}${RANDOM:0:${short}}"` fi fi # Check if the combination of random numbers equals the target length # and assign the value to the global variable, or repeat processing # by making a recursive function call. if [[ `echo -n ${randomString} | wc -c` = ${formatLength} ]]; then result=${randomString} else targetLength ${randomString} ${formatLength} fi } # Declare global variable to support targetLength(). result='' # Declare an array of strings. declare -A telephone_parts # Generate one hundred random telephone numbers. for i in {1..100}; do # Create random three digit area code. targetLength ${RANDOM:0:3} 3 telephone_parts[1]=${result} # Create random three digit prefix code. targetLength ${RANDOM:0:3} 3 telephone_parts[2]=${result} # Create random four digit number code. targetLength ${RANDOM:0:4} 4 telephone_parts[3]=${result} # Print the telephone numbers. echo "[${i}] (${telephone_parts[1]}) ${telephone_parts[2]}-${telephone_parts[3]}" done |
For reference, a recursive function call isn’t required here. It could be done more effectively with the following while
loop:
targetLength() { # Declare variable in function-level scope. randomString='' short=1 # Check the number of parameters to process. if [[ ${#} = 2 ]]; then # Assign value to function-level and local variables. randomString=${1} formatLength=${2} # Check if the telephone number is too short. while [[ ${short} > 0 ]]; do # Get the length of the telephone number as integer. length=`echo -n ${randomString} | wc -c` # Calculate any shortfall. short=$((${formatLength}-${length})) # Assign new value to randomString. randomString=`echo "${randomString}${RANDOM:0:${short}}"` done # Assign randomString to global result variable. result=${randomString} fi } |
As always, I hope this helps those you want to learn or solve a problem.