Introduction
This article comes from an attempt to be acquainted on NodeJS[1] and how a server built with JavaScript and NodeJS can call PHP scripts.
I am aware that there are many frameworks to speed up the development of applications, such as, to name one, Express.js and also a NodeJS extension for managing PHP based site (like WordPress sites), but it is precisely this abundance of products that is an indication of the easiness with which we can develop a pure NodeJS HTTP server.
NodeJS
There are two main features of NodeJS: modularity and the non-blocking asynchronously programming[2]. Modularity is based on require(JavaScript_File)
function that reads and executes the contents of JavaScript_File
, returning an object; NodeJS has many native modules and, of course, one can add their own modules.
The non-blocking characteristic is based on that NodeJS objects have, normally, the method addListener
(or his synonym on
) to manage events; in the fragment below, the event data
is fired when the request
object (that is the request made to the web server) has data; the same object reacts at the event end
of data:
var inData = '';
request.on('data', function(chunk) {
inData += chunk.toString();
});
request.on('end', function() {
...
Create and Start the WEB Server
We can create and start a web server with few instructions:
var port = 1337;
var server = require('http').createServer().listen(port);
console.log('Server running at 127.0.0.1:' + port);
server.on('request', function(req, resp) {
...
In these lines of the above code, I highlight the two main features of NodeJS:
-
var server = require('http').createServer().listen(port);
This statement arose because the createServer
method on module http
returns a server
whose method listen
returns the object server
itself, i.e., this is a compact writing of:
var server = require('http').createServer();
server.listen(port);
-
server.on('request', function(request, response) {...
This is the function that activates when a request arrives from the browser.
The script containing the web server is executed in the window console:
set path=C:\Sviluppo\php7;\%path% C:\Users\User>node
C:\www\condorinformatique\Nodejs\tryNodeJS.js
(The first line simply makes the PHP interpreter accessible.).
Managing the Requests
As previously mentioned, the server
instance of HTTP object has the on
method that allows to intercept an event, in particular, the request
event that indicates the reception of a request from the browser, request that is managed by a function with two objects: request
and response
.
When the event end
of request
object occurs, there are three steps to meet the request:
- to discover the type of the requested resource (HTML file, image, JavaScript, style sheet, PHP script, etc.)
- to recover the data arrived, if any
- to send the answer
Which is the Resource?
The module url
is used to obtain an object that contains the URI components as properties:
var Url = require('url').parse(request.url,true)
Those necessary for the demo in question are pathname
and query
, i.e., the file requested and the possible parameters (the path
property contains both):
query: { Authors: '' },
pathname: '/getData.php',
path: '/getData.php?Authors',
...
Which are the Data?
The query
property of Url
object contains the possible parameters present in the URI, but if the request
method is POST
, as when submitting a form, the data are retrieved by the event data
of the request
object.
...
var Regexp = /name=\"(.+)\"\r\n\r\n(.*)\r\n/gm; // for extract form data
...
var inData = ''; // for POST data recovery (possibly)
request.on('data', function(chunk) {
inData += chunk.toString();
});
...
var parms = Url['query']; // data from GET or URI
// extract fields
match = Regexp.exec(inData); // data from form (POST method)
while (match != null) { // matched text: match[0];
// capturing group n: match[n]
parms[match[1]] = match[2];
match = Regexp.exec(inData);
}
...
In the above fragment, after having acquired the possible POST data, an array of input data is created both from URI both from POST ed data (if any). The first ones are contained in the property query of the url object:
var parms = Url['query']; .
For the second ones, in case of submission of a form, the data have a structure similar to this on the right and these can be extracted using the regular expression[3]:
/name=\" (.+)\"\r\n\r\n (.*)\r\n/gm
where the bold parts identify the name and the value of the fields on form:
parms[match[1]] = match[2]; . |
-----------------------------7e217f121030a
Content-Disposition: form-data; name="fg_Store"
fg_frm
-----------------------------7e217f121030a
Content-Disposition: form-data; name="Author_Group"
Social Aphorisms
-----------------------------7e217f121030a
Content-Disposition: form-data; name="Author"
Claudie Baudry
-----------------------------7e217f121030a--
|
Sending the Answer
The answer is sent when the event end
of the request
object is fired:
request.on('end', function() {
Url = require('url').parse(request.url,true)
if (pathName == "/") pathName = "\\index.html";
fs.access(__basedir + pathName,(err) => {
if (err == null) {
var type = path.extname(pathName).substring(1).toLowerCase();
var cType = "text/html; charset=utf-8"
if (typeof types[type] != "undefined") cType = types[type];
if (type == "php") {
...
} else {
fs.readFile(__basedir + pathName, (err,data) => {
if (err) throw err;
response.writeHead(200, {'Content-Type': cType});
response.end(data,'binary');
});
}
} else {
response.writeHead(404, {'Content-Type': 'text/html; charset=utf-8'});
response.end("<img src='images/condor.gif'>" + err.message);
console.log(err.message);
}
}
);
});
...
The above function deals on a default request, if the pathname
is root, i.e., /
; if the resource called is not present generates an error message.
For the resources found, it is necessary to determine its Content-type
in order to send an appropriate header and finally send the resource itself (for PHP scripts, see the paragraph below).
Dealing with PHP Resources
If the resource requested is a PHP script, the program calls a PHP interpreter with switch -r
followed by PHP instructions; this can be simply as follows:
var spawnPHP = spawn('php',["-r","include 'phpscript'"]);
if the script has no parameters.
In case of this demo, before the instruction that includes the PHP script, the form fields must be inserted in the PHP associative array $_REQUEST
so after the -r
switch, there is something similar to:
$_REQUEST['Author'] = 'Nicolas de Condorcet';
$_REQUEST['Author_Group'] = 'Social Aphorisms';
$_REQUEST['fg_Store'] = 'fg_frm';
$_REQUEST['fg_Button'] = 'Author';
$_REQUEST['R'] = '0.7828608712500384';
include 'C:\www\condorinformatique\nodejs/getData.php';
The script
fragment below shows how to start the PHP interpreter and how to retrieve the answer on the stdout
when the data
event succeeded:
var requests = "include '" + __basedir + pathName + "';";
for(q in parms) requests = "\$_REQUEST['" + q +"'] = '" +
parms[q] + "';" + requests;
var spawnPHP = spawn('php',["-r",requests]);
spawnPHP.stdout.on('data',function (data) {
response.writeHead(200, {'Content-Type': cType});
response.end(data);
console.log(getTime() + " Sent resource: " + pathName)
});
Using the Demo
Decompress the demo file in a folder of your choice. You must change in the first line set path=C:\Sviluppo\php7;\%path%
with the path to your PHP interpreter; at this point, you are ready to start typing in the browser http://127.0.0.1:1337/
.
The demo uses some of my programs that you can download with the documentation from my site:
- Crossing field in PDO table, there is also a CodeProject article
- JavaScript Form Generator, there is also a CodeProject article
Conclusion
This article is a first approach for how to create a web server and it does not cover all the possibilities, in particular, how to manage the upload of files (which could be the topic of a next article).
Note
- ^ For an introduction on NodeJS, see the CodeProject article All About Core NodeJS — Part 1.
How Node.Js Single Thread mechanism Work ? Understanding Event Loop in NodeJs - ^ The effect of the non-blocking asynchronously is that the sequence of resources required by the browser may be different from those provided: as we can see from the console login, a JavaScript script formgen.js is requested before the CSS file styles.css bat it is served after because the size of the first is larger than the second.
C:\www\condorinformatique\nodejs>node tryNodeJS.js
16:28:45 Server running at 127.0.0.1:1337
16:29:07 Request resource: \index.html
16:29:07 Sent resource: \index.html
16:29:07 Request resource: /js/formgen.js
16:29:07 Request resource: /styles.css
16:29:07 Sent resource: /js/formgen.js
16:29:07 Sent resource: /styles.css
16:29:07 Request resource: /images/condor.gif
16:29:07 Sent resource: /images/condor.gif
16:29:07 Request resource: /getData.php
16:29:07 Request resource: /CONDOR.WAV
16:29:08 Sent resource: /CONDOR.WAV
16:29:08 Request resource: /favicon.ico
16:29:08 Sent resource: /favicon.ico
16:29:08 Sent resource: /getData.php
- ^A great site to try and increase knowledge is: Online regex tester and debugger.
History
- 8th January, 2019: Initial post