Chapter 5. Helper APIs

1.  The dns module consists of two main methods and a number of convenience methods. The two main methods are resolve(), which turns a domain name into a DNS record, and reverse(), which turns an IP address into a domain. All of the other methods in the dns module are more specialized forms of these methods.

 

2.  dns.resolve() takes three arguments:

a)  A string containing the domain to be resolved

b)  A string containing the types of records being requested

c)  A callback, the first argument of the callback is an error object. If an error occurs, the object will be non-null. The second argument is a list of the records returned by the query.

 

3.  There are convenience methods for all the types of records listed earlier. For example, rather than calling resolve('example.com', 'MX', callback), you can call resolveMx('example.com', callback) instead. The API also provides resolve4() and resolve6() methods, which resolve IPv4 and IPv6 address records, respectively.

 

4.  Because resolve() usually returns a list containing many IP addresses, there is also a convenience method called dns.lookup() that returns just one IP address from an A record query. The method takes a domain, an IP family (4 or 6), and a callback.

 

5.  crypto is used in lots of places for a variety of tasks. Node uses the OpenSSL library as the basis of its cryptography.

 

6.  To use hashes in Node, you should create a Hash object using the factory method crypto.createHash(). This returns a new Hash instance using a specified hashing algorithm. Most popular algorithms are available: md5, sha1, sha256, sha512, ripemd160.

 

7.  Once you have data in the hash, you can use it to create a digest by calling hash.update() with the hash data . You can keep updating a Hash with more data until you want to output it; the data you add to the hash is simply concatenated to the data passed in previous calls. To output the hash, call the hash.digest() method. This will output the digest of the data that was input into the hash with hash.update(). No more data can be added after you call hash.digest() and the Hash object is finalized and cannot be reused.

 

8.  The options for hash.digest() output are binary(default which is instance of SlowBuffer), hex, and base64.

 

9.  HMAC combines the hashing algorithms with a cryptographic key in order to stop a number of attacks on the integrity of the signature. This means that HMAC uses both a hashing algorithm and an encryption key. The creation of an hmac object requires a key as well as a hash algorithm. crypto.createHmac() returns an instance of hmac, which offers update() and digest() methods that work identically to the Hash methods.

 

10.  The key required to create an hmac object is a PEM-encoded key, passed as a string. It is easy to create a key on the command line using OpenSSL:

openssl genrsa -out key.pem 1024

 

11.  The public key cryptography functions are split into four classes: Cipher, Decipher, Sign, and Verify. Like all the other classes in crypto, they have factory methods. Cipher encrypts data, Decipher decrypts data, Sign creates a cryptographic signature for data, and Verify validates cryptographic signatures. Public key cryptography has matched sets of keys. One, the private key, is kept by the owner and is used to decrypt and sign data. The other, the public key, is made available to other parties. The public key can be used to encrypt data that only the private key owner can read, or to verify the signature of data signed with the private key.

 

12.  Node expects public keys in certificate format。 Extracting a public key certificate from a private key

$ openssl req -key key.pem -new -x509 -out cert.pem 

 

13.  The factory method to create a cipher takes an algorithm and the private key. Many modern cryptographic algorithms use block ciphers. This means that the output is always in standard-size “blocks”.  The Cipher API also uses the update() method to input data. If the amount of data in the cipher plus the amount of data passed to cipher.update() is enough to create one or more blocks, the encrypted data will be returned. If there isn’t enough to form a block, the input will be stored in the cipher. When cipher.final() is called, any remaining data in the cipher will be returned encrypted, but with enough padding to make sure the block size is reached.

 

14.  The Decipher class is almost the exact inverse of the Cipher class. You can pass encrypted data to a Decipher object using decipher.update(), and it will stream the data into blocks until it can output the unencrypted data. Decipher will buffer the data, thus you can pass it data you got off some other I/O transport, such as the disk or network, even though this might give you block sizes different from those used by the encryption algorithm.

 

15.  crypto.createSign() is used to make a sign object. createSign() takes only the signing algorithm. sign.update() allows you to add data to the sign object. When you want to create the signature, call sign.sign() with a private key to sign the data.

 

16.  verify.update() adds data and when you have added all the data to be verified against the signature, verify.verify() validates the signature. It takes the cert (the public key), the signature, and the format of the signature.

 

17.  The process module is global and is always available as the variable process. process is an instance of EventEmitter, so it provides events based on systems calls to the Node process. The exit event provides a final hook before the Node process exits. Importantly, the event loop will not run after the exit event, so only code without callbacks will be executed.

 

18.Exceptions that hit the main event loop will kill your Node process. The uncaughtException event provides an extremely brute-force way of catching these exceptions:

 

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

setTimeout(function () {
  console.log('This will still run.');
}, 500);

// Intentionally cause an exception, but don't catch it.
nonexistentFunc();
console.log('This will not run.');

 

 

19.  After an event is emitted, one of the checks in the runtime handler is to see whether there is an array of listeners. If there is more than one listener, the runtime calls the listeners by looping through the array in order. All listeners on the same event are part of the same code path. So an uncaught exception in one callback will stop execution for all other callbacks on the same event. However, an uncaught exception in one instance of an event won’t affect other events.

 

20.  An operating system can generate a lot of POSIX system events, which can be found in the sigaction(2) manpage. Really common ones include SIGINT, the interrupt signal. Typically, a SIGINT is what happens when you press Ctrl-C in the terminal on a running process. Unless you handle the signal events via process, Node will just perform the default action; in the case of a SIGINT, the default is to immediately kill the process. 

 

21.  There are a number of properties of process that contain immutable (read-only) information about Node:

    a)  process.version

         Contains the version number of the instance of Node you are running.

    b)  process.installPrefix

         Contains the install path (/usr/local, etc.) used during installation.

    c)  process.platform

         Lists the platform on which Node is currently running. The output will specify the kernel.

    d)  process.uptime()

         Contains the number of seconds the process has been running.

 

22.  When the process runs, it does so with a particular user and group. You can get these and set them with process.getgid(), process.setgid(), process.getuid(), and process.setuid(). The set methods take either the numerical ID of the group or username or the group/username itself. However, if you pass the group or username, the methods do a blocking lookup to turn the group/username into an ID

 

23.  The process ID, or PID, of the running Node instance is also available as the process.pid property. You can set the title that Node displays to the system using the process.title property. Whatever is set in this property will be displayed in the ps command.

 

24.  process.execPath shows the execution path of the current Node binary. The current working directory (to which all files opened will be relative) is accessible with process.cwd(). The working directory is the directory you were in when Node was started. You can change it using process.chdir().

 

25.  You can get the memory usage of the current Node process using process.memoryUsage(). This returns an object specifying the size of the memory usage in a couple of ways: rss shows how much RAM is being used, and vsize shows the total memory used, including both RAM and swap. You’ll also get some V8 stats: heapTotal and heapUsed show how much memory V8 has allocated and how much it is actively using.

 

26.The process.stdin stream is always initialized in any Node process. But it starts out in a paused state, where Node can write to it but you can’t read from it. Before attempting to read from stdin, call its resume() method. Until then, Node will just fill the read buffer for the stream and then stop until you are ready to deal with it. This approach avoids data loss.

 

27.Writing stdin to stdout:

process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function (chunk) {
  process.stdout.write('data: ' + chunk);
});
process.stdin.on('end', function () {
  process.stdout.write('end');
});

 

Because stdin and stdout are both real streams, we could also easily do this with the stream pipe() method:

process.stdin.resume();
process.stdin.pipe(process.stdout);

 

 

28.  When you write to stderr, Node guarantees that the write will happen. However, unlike a regular stream, this is done as a blocking call. Typically, calls to Steam.write() return a Boolean value indicating whether Node was able to write to the kernel buffer. With process.stderr this will always be true, but it might take a while to return. Typically, it will be very fast, but the kernel buffer may sometimes be full and hold up your program. This means that it is generally inadvisable to write a lot to stderr in a production system, because it may block real work. One final thing to note is that process.stderr is always a UTF-8 stream. You are not able to change the encoding here.

 

29.  process.argv is an array containing the command-line arguments, starting with the node command itself. It’s simply a split of the command line based on whitespace. The first file argument is expanded. This means you can pass a relative file argument on the command line, and it will appear as its absolute pathname in argv. This is also true for special characters, such as using ~ to refer to the home directory. Only the first argument is expanded this way.

 

30.process.nextTick() creates a callback to be executed on the next “tick,” or iteration of the event loop. While it is implemented as a queue, it will supersede other events. The “tick” will always occur on the next pass of the event loop. Every time nextTick() is called, each callback is created in isolation, thus are not subject to the usual callback exception brittleness.

process.on('uncaughtException', function(e) {
  console.log(e);
});
process.nextTick(function() {
  console.log('tick');
});
process.nextTick(function() {
  iAmAMistake();
  console.log('tock');
});
process.nextTick(function() {
  console.log('tick tock');
});
console.log('End of 1st loop');

 

31.  All child processes contain properties for stdin, stdout, and stderr. There is also a pid property that contains the OS process ID of the child. Children emit the exit event when they exit. Other data events are available via the stream methods of child_process.stdin, child_process.stdout, and child_process.stderr.

 

32.  child_process.exec() creates a child process and returns the result as a callback when the process is complete. When you call exec(), you can pass a shell command for the new process to run:

child_process.exec('ls -l', function(e, stdout, stderr) {
  if(!e) {
    console.log(stdout);
    console.log(stderr);
  }
});

 

The callback function receives three arguments: an error object, the result of stdout, and the result of stderr. The error object returned will be null unless an error status code is returned from the child process or there was another exception. If the child_process returns with an error, its stdin and stdout properties will be unavailable and attempts to access them will throw an exception.

 

33.  child_process.exec() takes an optional second argument with an options object which contains the following properties:

    a)  encoding

    The encoding for passing characters on the I/O streams. 

    b)  timeout

    The number of milliseconds the process can run before Node kills it. 

    c)  killSignal

    The signal to use to terminate the process in case of a time or Buffer size overrun.

    d)  maxBuffer

    The maximum number of kilobytes that stdout or stderr each may grow to.

    e)  setsid

    Whether to create a new session inside Node for the process.

    f)  cwd

    The initial working directory for the process (where null uses Node’s current working directory).

    g)  env

    The process’s environment variables. All environment variables are also inherited from the parent.

And it has the following default value:

    

{encoding: 'utf8',
  timeout: 0,
  maxBuffer: 200 * 1024,
  killSignal: 'SIGTERM',
  setsid: false,
  cwd: null,
  env: null }

 

34.  The first argument of child_process.spawn() is still the command to start the process with, but unlike exec(), it is not a command string; it’s just the executable. The process’s arguments are passed in an array as the (optional) second argument to spawn(). Finally, spawn() also takes an options array as the final argument. The env, setsid, and cwd properties are all options for spawn(), as are uid and gid, which set the user ID and the group ID, respectively. You can also set custom file descriptors that will be given to the new child process. Unlike exec(), we don’t issue a callback to spawn() directly. That’s because we are expecting to use the Streams provided by the Child class to get and send data.

 

35.  The file descriptor is simply an integer. The file descriptors 0, 1, and 2 represent stdin, stdout, and stderr, respectively. It doesn’t represent only pure files; network and other sockets are also allocated file descriptors. Unix has interprocess communications (IPC) sockets that let processes talk to each other. We’ve been calling them stdin, stdout, and stderr. When passing the options object to spawn(), we can specify customFds to pass our own three file descriptors to the child instead of them creating a stdin, stdout, and stderr file descriptor. Once you wire up the Node process this way, the child process loses its child.stdin, child.stdout, and child.stderr file descriptor references. This is because once you pass the file descriptors to the process, they are duplicated and the kernel handles the data passing. It is still preferable in many cases, though, because routing through the kernel is much faster than using something like stream.pipe() with Node to connect the streams together:

var cp = require('child_process');
var child = cp.spawn('cat', [], {customFds:[0, 1, 2]});
child.stdout.on('data', function(d) {
  console.log('data out');
});

 

36.  stdin, stdout, and stderr aren’t the only file descriptors worth connecting to child processes. A very common use case is connecting network sockets to a number of children, which allows for multicore utilization. We can pass the stdin, stdout, and stderr of a master process, we can create other sockets and pass those in to child processes. However, because we are passing file descriptors instead of messages, the kernel will deal with the routing. This means that although the master Node process is still required, it isn’t bearing the load for all the traffic.

 

37.  assert is a core library that provides the basis for testing code. It allows you to make claims about objects and function calls and send out messages when the assertions are violated.

 

38.  The ok() method is a shorthand for testing whether something is truthy, by comparing the value with true using ==.

 

39.  The deepEqual() and notDeepEqual() methods provide a way of deeply comparing object values.

 

40.  throws() and doesNotThrow() check whether a particular block of code does or doesn’t throw an exception. To pass blocks of code to throws() and doesNotThrow(), wrap them in functions that take no arguments. The exception being tested for is optional. If one isn’t passed, throws() will just check whether any exception happened, and doesNotThrow() will ensure that an exception hasn’t been thrown. If a specific error is passed, throws() will check that the specified exception and only that exception was thrown. If any other exceptions are thrown or the exception isn’t thrown, the test will not pass. For doesNotThrow(), when an error is specified, it will continue without error if any exception other than the one specified in the argument is thrown. If an exception matching the specified error is thrown, it will cause the test to fail. There are four ways to specify the type of error to look for or avoid:

    a)  Comparison function

    The function should take the exception error as its single argument. In the function, compare the exception actually thrown to the one you expect to find out whether there is a match. Return true if there is a match and false otherwise.

    b)  Regular expression

    The library will compare the regex to the error message to find a match using the regex.test() method in JavaScript.

    c)  String

   The library will directly compare the string to the error message.

    d)  Object constructor

    The library will perform a typeof test on the exception. If this test throws an error with the typeof constructor, then the exception matches.

 

41.  The vm module allows you to run arbitrary chunks of code and get a result back. This can be useful to act as a kind of faux sandbox. However, the code is still running in the same Node process, so you should be cautious. 

 

42.  vm is similar to eval(), but it doesn’t have the ability to interact with the local scope in the way that eval() does.

 

43.  There are two ways to run code with vm. Running the code “inline” is similar to using eval(). The second way is to precompile the code into a vm.Script object.

 

44.  The vm subsystem actually maintains its own local context that persists from one invocation of vm to another. It’s also possible to pass a preexisting context to vm using vm.runInNewContext(), which takes a context object as a second argument. The scope of that object becomes the context for the code we run with vm.

>var vm = require('vm');

> var fs = require('fs');

> var code = fs.readFileSync('example.js');

> code.toString();

'console.log(output);\n'

> var script = vm.createScript(code);

> script.runInNewContext({output:"Kick Ass"});

ReferenceError: console is not defined

    at undefined:1:1

    at [object Context]:1:8

    at Interface.<anonymous> (repl.js:171:22)

    at Interface.emit (events.js:64:17)

    at Interface._onLine (readline.js:153:10)

    at Interface._line (readline.js:408:8)

    at Interface._ttyWrite (readline.js:585:14)

    at ReadStream.<anonymous> (readline.js:73:12)

    at ReadStream.emit (events.js:81:20)

    at ReadStream._emitKey (tty_posix.js:307:10)

> script.runInNewContext({"console":console,"output":"Kick Ass"});

Kick Ass

你可能感兴趣的:(nodejs,VM,assert,process,child_process)