From 885aecb444662005977efb4c5b950768ed38c8d4 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 16 May 2012 15:29:04 -0700 Subject: [PATCH] first stab --- .gitignore | 2 + DEVNOTES.txt | 3 + README.md | 36 + bin/nodedoc.py | 315 +++++ deps/README.md | 2 + deps/appdirs.py | 346 +++++ deps/appdirs.pyc | Bin 0 -> 13440 bytes deps/markdown2.py | 2299 +++++++++++++++++++++++++++++++ deps/markdown2.pyc | Bin 0 -> 66561 bytes doc/api/_toc.markdown | 37 + doc/api/addons.markdown | 624 +++++++++ doc/api/all.markdown | 36 + doc/api/appendix_1.markdown | 44 + doc/api/assert.markdown | 84 ++ doc/api/buffer.markdown | 645 +++++++++ doc/api/child_process.markdown | 407 ++++++ doc/api/cluster.markdown | 439 ++++++ doc/api/crypto.markdown | 363 +++++ doc/api/debugger.markdown | 138 ++ doc/api/dgram.markdown | 209 +++ doc/api/dns.markdown | 148 ++ doc/api/documentation.markdown | 68 + doc/api/domain.markdown | 261 ++++ doc/api/events.markdown | 95 ++ doc/api/fs.markdown | 667 +++++++++ doc/api/globals.markdown | 150 ++ doc/api/http.markdown | 886 ++++++++++++ doc/api/https.markdown | 173 +++ doc/api/index.markdown | 1 + doc/api/modules.markdown | 461 +++++++ doc/api/net.markdown | 467 +++++++ doc/api/os.markdown | 134 ++ doc/api/path.markdown | 156 +++ doc/api/process.markdown | 410 ++++++ doc/api/querystring.markdown | 49 + doc/api/readline.markdown | 283 ++++ doc/api/repl.markdown | 188 +++ doc/api/stdio.markdown | 61 + doc/api/stream.markdown | 184 +++ doc/api/string_decoder.markdown | 24 + doc/api/synopsis.markdown | 23 + doc/api/timers.markdown | 31 + doc/api/tls.markdown | 491 +++++++ doc/api/tty.markdown | 75 + doc/api/url.markdown | 99 ++ doc/api/util.markdown | 190 +++ doc/api/vm.markdown | 220 +++ doc/api/zlib.markdown | 276 ++++ package.json | 14 + 49 files changed, 12314 insertions(+) create mode 100644 .gitignore create mode 100644 DEVNOTES.txt create mode 100644 README.md create mode 100755 bin/nodedoc.py create mode 100644 deps/README.md create mode 100644 deps/appdirs.py create mode 100644 deps/appdirs.pyc create mode 100755 deps/markdown2.py create mode 100644 deps/markdown2.pyc create mode 100644 doc/api/_toc.markdown create mode 100644 doc/api/addons.markdown create mode 100644 doc/api/all.markdown create mode 100644 doc/api/appendix_1.markdown create mode 100644 doc/api/assert.markdown create mode 100644 doc/api/buffer.markdown create mode 100644 doc/api/child_process.markdown create mode 100644 doc/api/cluster.markdown create mode 100644 doc/api/crypto.markdown create mode 100644 doc/api/debugger.markdown create mode 100644 doc/api/dgram.markdown create mode 100644 doc/api/dns.markdown create mode 100644 doc/api/documentation.markdown create mode 100644 doc/api/domain.markdown create mode 100644 doc/api/events.markdown create mode 100644 doc/api/fs.markdown create mode 100644 doc/api/globals.markdown create mode 100644 doc/api/http.markdown create mode 100644 doc/api/https.markdown create mode 100644 doc/api/index.markdown create mode 100644 doc/api/modules.markdown create mode 100644 doc/api/net.markdown create mode 100644 doc/api/os.markdown create mode 100644 doc/api/path.markdown create mode 100644 doc/api/process.markdown create mode 100644 doc/api/querystring.markdown create mode 100644 doc/api/readline.markdown create mode 100644 doc/api/repl.markdown create mode 100644 doc/api/stdio.markdown create mode 100644 doc/api/stream.markdown create mode 100644 doc/api/string_decoder.markdown create mode 100644 doc/api/synopsis.markdown create mode 100644 doc/api/timers.markdown create mode 100644 doc/api/tls.markdown create mode 100644 doc/api/tty.markdown create mode 100644 doc/api/url.markdown create mode 100644 doc/api/util.markdown create mode 100644 doc/api/vm.markdown create mode 100644 doc/api/zlib.markdown create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bc2ef5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/tmp +/node_modules diff --git a/DEVNOTES.txt b/DEVNOTES.txt new file mode 100644 index 0000000..5363dc5 --- /dev/null +++ b/DEVNOTES.txt @@ -0,0 +1,3 @@ +# dev notes + + less '+10G' configure # how to call `less` to jump to particular line diff --git a/README.md b/README.md new file mode 100644 index 0000000..dcdee64 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +A fledgling `perldoc` for node.js. + +# Installation + +1. Get [node](http://nodejs.org). + +2. `npm install -g nodedoc` + +You should now have "nodedoc" on your PATH: + + $ nodedoc --version + nodedoc 1.0.0 + +# Status + +This really is a quick hack. There are a number of limitations in the current +Markdown -> HTML -> ANSI escape-colored text. Among them: + +- nested lists aren't handled properly +- `
    ` aren't handled properly + +The current version of the node.js docs is a snapshot of the + master. + + +# Examples + +This will render and color the fs.markdown core docs and page through them +(using your `PAGER` environment setting, if any): + + $ nodedoc fs + +List all nodedoc sections: + + $ nodedoc -l + diff --git a/bin/nodedoc.py b/bin/nodedoc.py new file mode 100755 index 0000000..ec56bb9 --- /dev/null +++ b/bin/nodedoc.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""nodedoc -- fledgling perldoc for node.js + +Usage: + nodedoc SECTION + +See for more info. +""" + +__version_info__ = (1, 0, 0) +__version__ = '.'.join(map(str, __version_info__)) + +import re +import sys +import textwrap +import os +from os.path import dirname, abspath, join, exists +import logging +import codecs +import optparse +from glob import glob + +TOP = dirname(dirname(abspath(__file__))) +sys.path.insert(0, join(TOP, "deps")) +import markdown2 +import appdirs + + + +#---- globals + +log = logging.getLogger("nodedoc") +CACHE_DIR = appdirs.user_cache_dir("nodedoc", "trentm") + + + +#---- exceptions + +class Error(Exception): + pass + + + +#---- stylers + +def red(text): + return '\033[31m' + text + '\033[39m' +def green(text): + return '\033[32m' + text + '\033[39m' +def cyan(text): + return '\033[36m' + text + '\033[39m' +def grey(text): + return '\033[90m' + text + '\033[39m' + +def bold(text): + return '\033[1m' + text + '\033[22m' +def italic(text): + return '\033[3m' + text + '\033[23m' +def inverse(text): + return '\033[7m' + text + '\033[27m' + + + +#---- re.sub transformers + +def indent(match): + INDENT = ' ' + text = match.group(1) + after = INDENT + INDENT.join(text.splitlines(True)) + #print "XXX hit: after=%r" % after + return after + +def code(match): + """green. Special case grey for "Stability: ..." pre-blocks.""" + text = match.group(1) + styler = green + if text.startswith("Stability:"): + styler = grey + lines = [ + styler(line) + for line in text.splitlines(False) + ] + return '\n'.join(lines) + +def wrap(match, width=80): + """XXX TODO: somehow make the ANSI escapes zero-length for width + calculation.""" + text = match.group(1) + text = '\n'.join(textwrap.wrap(text, width=width)) + return text + +def h1(match): + """bold red""" + text = match.group(1) + return bold(red('# ' + text)) + return '\n'.join(lines) + +def h2(match): + """bold red, extra leading space""" + text = match.group(1) + text = '\n' + bold(red('## ' + text)) + return text + +def h3(match): + """bold red""" + text = match.group(1) + text = '\n' + bold(red('### ' + text)) + return text + +def a(match): + """blue""" + text = match.group(1) + lines = [ + '\033[34m' + line + '\033[39m' + for line in text.splitlines(False) + ] + return '\n'.join(lines) + +def em(match): + """cyan""" + text = match.group(1) + lines = [cyan(line) for line in text.splitlines(False)] + return cyan('*') + '\n'.join(lines) + cyan('*') + +def strong(match): + """bold cyan""" + text = match.group(1) + lines = [bold(cyan(line)) for line in text.splitlines(False)] + return bold(cyan('**')) + '\n'.join(lines) + bold(cyan('**')) + +def li(match): + """bullet and indent and reflow""" + text = match.group(1) + text = '\n'.join(textwrap.wrap(text, width=78)) + INDENT = ' ' + text = INDENT + INDENT.join(text.splitlines(True)) + text = '-' + text[1:] + return text + +def noop(match): + return match.group(1) + + + +#---- main nodedoc functionality + +def generate_html_path(markdown_path, html_path): + if not exists(dirname(html_path)): + os.makedirs(dirname(html_path)) + html = markdown2.markdown_path(markdown_path) + codecs.open(html_path, 'w', 'utf-8').write(html) + +def generate_nodedoc_path(html_path, nodedoc_path): + if not exists(dirname(nodedoc_path)): + os.makedirs(dirname(nodedoc_path)) + + content = codecs.open(html_path, 'r', 'utf-8').read() + + # html comments: drop + content = re.compile('\n?\n', re.S).sub('', content) + + # code: + content = re.compile('(.*?)', re.S).sub(code, content) + + # pre: indent + content = re.compile('
    (.*?)
    ', re.S).sub(indent, content) + + # li: bullet + # XXX how to know in ol? AFAICT only one
      in node.js docs, so ignoring for now. + # XXX does this mess up multi-para li? + content = re.compile('
    1. (?:

      )?(.*?)(?:

      )?
    2. ', re.S).sub(li, content) + # ol, ul: ignore + content = re.compile('\n?
        (.*?)
      \n', re.S).sub(noop, content) + content = re.compile('\n?
        (.*?)
      \n', re.S).sub(noop, content) + + # p: wrap content at 80 columns + content = re.compile('

      (.*?)

      ', re.S).sub(wrap, content) + + # a: drop attrs (until/unless have a way to follow those links) + content = re.compile(']*>(.*?)', re.S).sub(a, content) + + content = re.compile('(.*?)', re.S).sub(em, content) + content = re.compile('(.*?)', re.S).sub(strong, content) + + # hN: highlight, but how to highlight different levels? + content = re.compile('

      (.*?)

      ', re.S).sub(h1, content) + content = re.compile('

      (.*?)

      ', re.S).sub(h2, content) + content = re.compile('

      (.*?)

      ', re.S).sub(h3, content) + + #TODO:XXX special case two adjacent h2's, e.g.: + # + #

      fs.utimes(path, atime, mtime, [callback])

      + # + #

      fs.utimesSync(path, atime, mtime)

      + # + #

      Change file timestamps of the file referenced by the supplied path.

      + + codecs.open(nodedoc_path, 'w', 'utf-8').write(content) + + +def nodedoc(section): + markdown_path = join(TOP, "doc", "api", section + ".markdown") + if not exists(markdown_path): + raise Error("no such section: '%s'" % section) + + html_path = join(CACHE_DIR, section + ".html") + if not exists(html_path) or mtime(html_path) < mtime(markdown_path): + generate_html_path(markdown_path, html_path) + + nodedoc_path = join(CACHE_DIR, section + ".nodedoc") + if not exists(nodedoc_path) or mtime(nodedoc_path) < mtime(html_path): + generate_nodedoc_path(html_path, nodedoc_path) + + pager = os.environ.get("PAGER", "less") + cmd = '%s "%s"' % (pager, nodedoc_path) + return os.system(cmd) + +def nodedoc_sections(): + markdown_paths = glob(join(TOP, "doc", "api", "*.markdown")) + for p in markdown_paths: + yield os.path.splitext(os.path.basename(p))[0] + + + +#---- other internal support stuff + +class _LowerLevelNameFormatter(logging.Formatter): + def format(self, record): + record.lowerlevelname = record.levelname.lower() + return logging.Formatter.format(self, record) + +def _setup_logging(): + hdlr = logging.StreamHandler(sys.stdout) + fmt = "%(name)s: %(lowerlevelname)s: %(message)s" + fmtr = _LowerLevelNameFormatter(fmt=fmt) + hdlr.setFormatter(fmtr) + logging.root.addHandler(hdlr) + +class _NoReflowFormatter(optparse.IndentedHelpFormatter): + """An optparse formatter that does NOT reflow the description.""" + def format_description(self, description): + return description or "" + +def mtime(path): + return os.stat(path).st_mtime + + + +#---- mainline + +def main(argv=sys.argv): + _setup_logging() + log.setLevel(logging.INFO) + + # Parse options. + parser = optparse.OptionParser(prog="nodedoc", usage='', + version="%prog " + __version__, description=__doc__, + formatter=_NoReflowFormatter()) + parser.add_option("-v", "--verbose", dest="log_level", + action="store_const", const=logging.DEBUG, + help="more verbose output") + parser.add_option("-q", "--quiet", dest="log_level", + action="store_const", const=logging.WARNING, + help="quieter output (just warnings and errors)") + parser.add_option("-l", "--list", action="store_true", + help="list all nodedoc sections") + parser.set_defaults(log_level=logging.INFO) + opts, sections = parser.parse_args() + log.setLevel(opts.log_level) + + if opts.list: + print '\n'.join(nodedoc_sections()) + elif len(sections) == 0: + parser.print_help() + elif len(sections) > 1: + log.error("too many arguments: %s", ' '.join(sections)) + else: + return nodedoc(sections[0]) + + +## {{{ http://code.activestate.com/recipes/577258/ (r4) +if __name__ == "__main__": + try: + retval = main(sys.argv) + except KeyboardInterrupt: + sys.exit(1) + except SystemExit: + raise + except: + import traceback, logging + if not log.handlers and not logging.root.handlers: + logging.basicConfig() + skip_it = False + exc_info = sys.exc_info() + if hasattr(exc_info[0], "__name__"): + exc_class, exc, tb = exc_info + if isinstance(exc, IOError) and exc.args[0] == 32: + # Skip 'IOError: [Errno 32] Broken pipe': often a cancelling of `less`. + skip_it = True + if not skip_it: + tb_path, tb_lineno, tb_func = traceback.extract_tb(tb)[-1][:3] + log.error("%s (%s:%s in %s)", exc_info[1], tb_path, + tb_lineno, tb_func) + else: # string exception + log.error(exc_info[0]) + if not skip_it: + if log.isEnabledFor(logging.DEBUG): + print() + traceback.print_exception(*exc_info) + sys.exit(1) + else: + sys.exit(retval) +## end of http://code.activestate.com/recipes/577258/ }}} diff --git a/deps/README.md b/deps/README.md new file mode 100644 index 0000000..3d3f256 --- /dev/null +++ b/deps/README.md @@ -0,0 +1,2 @@ +markdown2.py # from https://github.com/trentm/python-markdown2 +appdirs.py # from https://github.com/ActiveState/appdirs diff --git a/deps/appdirs.py b/deps/appdirs.py new file mode 100644 index 0000000..94c592f --- /dev/null +++ b/deps/appdirs.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python +# Copyright (c) 2005-2010 ActiveState Software Inc. + +"""Utilities for determining application-specific dirs. + +See for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 2, 0) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +class AppDirsError(Exception): + pass + + + +def user_data_dir(appname, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + "appauthor" (only required and used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ + Unix: ~/.config/ # or in $XDG_CONFIG_HOME if defined + Win XP (not roaming): C:\Documents and Settings\\Application Data\\ + Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ + Win 7 (not roaming): C:\Users\\AppData\Local\\ + Win 7 (roaming): C:\Users\\AppData\Roaming\\ + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. We don't + use $XDG_DATA_HOME as that data dir is mostly used at the time of + installation, instead of the application adding data during runtime. + Also, in practice, Linux apps tend to store their data in + "~/.config/" instead of "~/.local/share/". + """ + if sys.platform.startswith("win"): + if appauthor is None: + raise AppDirsError("must specify 'appauthor' on Windows") + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.join(_get_win_folder(const), appauthor, appname) + elif sys.platform == 'darwin': + path = os.path.join( + os.path.expanduser('~/Library/Application Support/'), + appname) + else: + path = os.path.join( + os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")), + appname.lower()) + if version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname, appauthor=None, version=None): + """Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + "appauthor" (only required and used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + + Typical user data directories are: + Mac OS X: /Library/Application Support/ + Unix: /etc/xdg/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if sys.platform.startswith("win"): + if appauthor is None: + raise AppDirsError("must specify 'appauthor' on Windows") + path = os.path.join(_get_win_folder("CSIDL_COMMON_APPDATA"), + appauthor, appname) + elif sys.platform == 'darwin': + path = os.path.join( + os.path.expanduser('/Library/Application Support'), + appname) + else: + # XDG default for $XDG_CONFIG_DIRS[0]. Perhaps should actually + # *use* that envvar, if defined. + path = "/etc/xdg/"+appname.lower() + if version: + path = os.path.join(path, version) + return path + + +def user_cache_dir(appname, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + "appauthor" (only required and used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: C:\Users\\AppData\Local\\\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if sys.platform.startswith("win"): + if appauthor is None: + raise AppDirsError("must specify 'appauthor' on Windows") + path = os.path.join(_get_win_folder("CSIDL_LOCAL_APPDATA"), + appauthor, appname) + if opinion: + path = os.path.join(path, "Cache") + elif sys.platform == 'darwin': + path = os.path.join( + os.path.expanduser('~/Library/Caches'), + appname) + else: + path = os.path.join( + os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')), + appname.lower()) + if version: + path = os.path.join(path, version) + return path + +def user_log_dir(appname, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + "appauthor" (only required and used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Logs/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if sys.platform == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif sys.platform == "win32": + path = user_data_dir(appname, appauthor, version); version=False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version); version=False + if opinion: + path = os.path.join(path, "log") + if version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname, appauthor, version=None, roaming=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version) + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +if sys.platform == "win32": + try: + import win32com.shell + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + import ctypes + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", "site_data_dir", "user_cache_dir", + "user_log_dir") + + print("-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/deps/appdirs.pyc b/deps/appdirs.pyc new file mode 100644 index 0000000000000000000000000000000000000000..938de06b9047167e685c281e198822b3ab8d6b4e GIT binary patch literal 13440 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 literal 0 HcmV?d00001 diff --git a/deps/markdown2.py b/deps/markdown2.py new file mode 100755 index 0000000..fc141cb --- /dev/null +++ b/deps/markdown2.py @@ -0,0 +1,2299 @@ +#!/usr/bin/env python +# Copyright (c) 2012 Trent Mick. +# Copyright (c) 2007-2008 ActiveState Corp. +# License: MIT (http://www.opensource.org/licenses/mit-license.php) + +from __future__ import generators + +r"""A fast and complete Python implementation of Markdown. + +[from http://daringfireball.net/projects/markdown/] +> Markdown is a text-to-HTML filter; it translates an easy-to-read / +> easy-to-write structured text format into HTML. Markdown's text +> format is most similar to that of plain text email, and supports +> features such as headers, *emphasis*, code blocks, blockquotes, and +> links. +> +> Markdown's syntax is designed not as a generic markup language, but +> specifically to serve as a front-end to (X)HTML. You can use span-level +> HTML tags anywhere in a Markdown document, and you can use block level +> HTML tags (like
      and as well). + +Module usage: + + >>> import markdown2 + >>> markdown2.markdown("*boo!*") # or use `html = markdown_path(PATH)` + u'

      boo!

      \n' + + >>> markdowner = Markdown() + >>> markdowner.convert("*boo!*") + u'

      boo!

      \n' + >>> markdowner.convert("**boom!**") + u'

      boom!

      \n' + +This implementation of Markdown implements the full "core" syntax plus a +number of extras (e.g., code syntax coloring, footnotes) as described on +. +""" + +cmdln_desc = """A fast and complete Python implementation of Markdown, a +text-to-HTML conversion tool for web writers. + +Supported extra syntax options (see -x|--extras option below and +see for details): + +* code-friendly: Disable _ and __ for em and strong. +* cuddled-lists: Allow lists to be cuddled to the preceding paragraph. +* fenced-code-blocks: Allows a code block to not have to be indented + by fencing it with '```' on a line before and after. Based on + with support for + syntax highlighting. +* footnotes: Support footnotes as in use on daringfireball.net and + implemented in other Markdown processors (tho not in Markdown.pl v1.0.1). +* header-ids: Adds "id" attributes to headers. The id value is a slug of + the header text. +* html-classes: Takes a dict mapping html tag names (lowercase) to a + string to use for a "class" tag attribute. Currently only supports + "pre" and "code" tags. Add an issue if you require this for other tags. +* markdown-in-html: Allow the use of `markdown="1"` in a block HTML tag to + have markdown processing be done on its contents. Similar to + but with + some limitations. +* metadata: Extract metadata from a leading '---'-fenced block. + See for details. +* pyshell: Treats unindented Python interactive shell sessions as + blocks. +* link-patterns: Auto-link given regex patterns in text (e.g. bug number + references, revision number references). +* smarty-pants: Replaces ' and " with curly quotation marks or curly + apostrophes. Replaces --, ---, ..., and . . . with en dashes, em dashes, + and ellipses. +* toc: The returned HTML string gets a new "toc_html" attribute which is + a Table of Contents for the document. (experimental) +* xml: Passes one-liner processing instructions and namespaced XML tags. +* wiki-tables: Google Code Wiki-style tables. See + . +""" + +# Dev Notes: +# - Python's regex syntax doesn't have '\z', so I'm using '\Z'. I'm +# not yet sure if there implications with this. Compare 'pydoc sre' +# and 'perldoc perlre'. + +__version_info__ = (1, 4, 3) +__version__ = '.'.join(map(str, __version_info__)) +__author__ = "Trent Mick" + +import os +import sys +from pprint import pprint +import re +import logging +try: + from hashlib import md5 +except ImportError: + from md5 import md5 +import optparse +from random import random, randint +import codecs + + +#---- Python version compat + +try: + from urllib.parse import quote # python3 +except ImportError: + from urllib import quote # python2 + +if sys.version_info[:2] < (2,4): + from sets import Set as set + def reversed(sequence): + for i in sequence[::-1]: + yield i + +# Use `bytes` for byte strings and `unicode` for unicode strings (str in Py3). +if sys.version_info[0] <= 2: + py3 = False + try: + bytes + except NameError: + bytes = str + base_string_type = basestring +elif sys.version_info[0] >= 3: + py3 = True + unicode = str + base_string_type = str + + + +#---- globals + +DEBUG = False +log = logging.getLogger("markdown") + +DEFAULT_TAB_WIDTH = 4 + + +SECRET_SALT = bytes(randint(0, 1000000)) +def _hash_text(s): + return 'md5-' + md5(SECRET_SALT + s.encode("utf-8")).hexdigest() + +# Table of hash values for escaped characters: +g_escape_table = dict([(ch, _hash_text(ch)) + for ch in '\\`*_{}[]()>#+-.!']) + + + +#---- exceptions + +class MarkdownError(Exception): + pass + + + +#---- public api + +def markdown_path(path, encoding="utf-8", + html4tags=False, tab_width=DEFAULT_TAB_WIDTH, + safe_mode=None, extras=None, link_patterns=None, + use_file_vars=False): + fp = codecs.open(path, 'r', encoding) + text = fp.read() + fp.close() + return Markdown(html4tags=html4tags, tab_width=tab_width, + safe_mode=safe_mode, extras=extras, + link_patterns=link_patterns, + use_file_vars=use_file_vars).convert(text) + +def markdown(text, html4tags=False, tab_width=DEFAULT_TAB_WIDTH, + safe_mode=None, extras=None, link_patterns=None, + use_file_vars=False): + return Markdown(html4tags=html4tags, tab_width=tab_width, + safe_mode=safe_mode, extras=extras, + link_patterns=link_patterns, + use_file_vars=use_file_vars).convert(text) + +class Markdown(object): + # The dict of "extras" to enable in processing -- a mapping of + # extra name to argument for the extra. Most extras do not have an + # argument, in which case the value is None. + # + # This can be set via (a) subclassing and (b) the constructor + # "extras" argument. + extras = None + + urls = None + titles = None + html_blocks = None + html_spans = None + html_removed_text = "[HTML_REMOVED]" # for compat with markdown.py + + # Used to track when we're inside an ordered or unordered list + # (see _ProcessListItems() for details): + list_level = 0 + + _ws_only_line_re = re.compile(r"^[ \t]+$", re.M) + + def __init__(self, html4tags=False, tab_width=4, safe_mode=None, + extras=None, link_patterns=None, use_file_vars=False): + if html4tags: + self.empty_element_suffix = ">" + else: + self.empty_element_suffix = " />" + self.tab_width = tab_width + + # For compatibility with earlier markdown2.py and with + # markdown.py's safe_mode being a boolean, + # safe_mode == True -> "replace" + if safe_mode is True: + self.safe_mode = "replace" + else: + self.safe_mode = safe_mode + + # Massaging and building the "extras" info. + if self.extras is None: + self.extras = {} + elif not isinstance(self.extras, dict): + self.extras = dict([(e, None) for e in self.extras]) + if extras: + if not isinstance(extras, dict): + extras = dict([(e, None) for e in extras]) + self.extras.update(extras) + assert isinstance(self.extras, dict) + if "toc" in self.extras and not "header-ids" in self.extras: + self.extras["header-ids"] = None # "toc" implies "header-ids" + self._instance_extras = self.extras.copy() + + self.link_patterns = link_patterns + self.use_file_vars = use_file_vars + self._outdent_re = re.compile(r'^(\t|[ ]{1,%d})' % tab_width, re.M) + + self._escape_table = g_escape_table.copy() + if "smarty-pants" in self.extras: + self._escape_table['"'] = _hash_text('"') + self._escape_table["'"] = _hash_text("'") + + def reset(self): + self.urls = {} + self.titles = {} + self.html_blocks = {} + self.html_spans = {} + self.list_level = 0 + self.extras = self._instance_extras.copy() + if "footnotes" in self.extras: + self.footnotes = {} + self.footnote_ids = [] + if "header-ids" in self.extras: + self._count_from_header_id = {} # no `defaultdict` in Python 2.4 + if "metadata" in self.extras: + self.metadata = {} + + def convert(self, text): + """Convert the given text.""" + # Main function. The order in which other subs are called here is + # essential. Link and image substitutions need to happen before + # _EscapeSpecialChars(), so that any *'s or _'s in the + # and tags get encoded. + + # Clear the global hashes. If we don't clear these, you get conflicts + # from other articles when generating a page which contains more than + # one article (e.g. an index page that shows the N most recent + # articles): + self.reset() + + if not isinstance(text, unicode): + #TODO: perhaps shouldn't presume UTF-8 for string input? + text = unicode(text, 'utf-8') + + if self.use_file_vars: + # Look for emacs-style file variable hints. + emacs_vars = self._get_emacs_vars(text) + if "markdown-extras" in emacs_vars: + splitter = re.compile("[ ,]+") + for e in splitter.split(emacs_vars["markdown-extras"]): + if '=' in e: + ename, earg = e.split('=', 1) + try: + earg = int(earg) + except ValueError: + pass + else: + ename, earg = e, None + self.extras[ename] = earg + + # Standardize line endings: + text = re.sub("\r\n|\r", "\n", text) + + # Make sure $text ends with a couple of newlines: + text += "\n\n" + + # Convert all tabs to spaces. + text = self._detab(text) + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ \t]*\n+/ . + text = self._ws_only_line_re.sub("", text) + + # strip metadata from head and extract + if "metadata" in self.extras: + text = self._extract_metadata(text) + + if self.safe_mode: + text = self._hash_html_spans(text) + + # Turn block-level HTML blocks into hash entries + text = self._hash_html_blocks(text, raw=True) + + # Strip link definitions, store in hashes. + if "footnotes" in self.extras: + # Must do footnotes first because an unlucky footnote defn + # looks like a link defn: + # [^4]: this "looks like a link defn" + text = self._strip_footnote_definitions(text) + text = self._strip_link_definitions(text) + + text = self._run_block_gamut(text) + + if "footnotes" in self.extras: + text = self._add_footnotes(text) + + text = self.postprocess(text) + + text = self._unescape_special_chars(text) + + if self.safe_mode: + text = self._unhash_html_spans(text) + + text += "\n" + + rv = UnicodeWithAttrs(text) + if "toc" in self.extras: + rv._toc = self._toc + if "metadata" in self.extras: + rv.metadata = self.metadata + return rv + + def postprocess(self, text): + """A hook for subclasses to do some postprocessing of the html, if + desired. This is called before unescaping of special chars and + unhashing of raw HTML spans. + """ + return text + + # Is metadata if the content starts with '---'-fenced `key: value` + # pairs. E.g. (indented for presentation): + # --- + # foo: bar + # another-var: blah blah + # --- + _metadata_pat = re.compile("""^---[ \t]*\n((?:[ \t]*[^ \t:]+[ \t]*:[^\n]*\n)+)---[ \t]*\n""") + + def _extract_metadata(self, text): + # fast test + if not text.startswith("---"): + return text + match = self._metadata_pat.match(text) + if not match: + return text + + tail = text[len(match.group(0)):] + metadata_str = match.group(1).strip() + for line in metadata_str.split('\n'): + key, value = line.split(':', 1) + self.metadata[key.strip()] = value.strip() + + return tail + + + _emacs_oneliner_vars_pat = re.compile(r"-\*-\s*([^\r\n]*?)\s*-\*-", re.UNICODE) + # This regular expression is intended to match blocks like this: + # PREFIX Local Variables: SUFFIX + # PREFIX mode: Tcl SUFFIX + # PREFIX End: SUFFIX + # Some notes: + # - "[ \t]" is used instead of "\s" to specifically exclude newlines + # - "(\r\n|\n|\r)" is used instead of "$" because the sre engine does + # not like anything other than Unix-style line terminators. + _emacs_local_vars_pat = re.compile(r"""^ + (?P(?:[^\r\n|\n|\r])*?) + [\ \t]*Local\ Variables:[\ \t]* + (?P.*?)(?:\r\n|\n|\r) + (?P.*?\1End:) + """, re.IGNORECASE | re.MULTILINE | re.DOTALL | re.VERBOSE) + + def _get_emacs_vars(self, text): + """Return a dictionary of emacs-style local variables. + + Parsing is done loosely according to this spec (and according to + some in-practice deviations from this): + http://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html#Specifying-File-Variables + """ + emacs_vars = {} + SIZE = pow(2, 13) # 8kB + + # Search near the start for a '-*-'-style one-liner of variables. + head = text[:SIZE] + if "-*-" in head: + match = self._emacs_oneliner_vars_pat.search(head) + if match: + emacs_vars_str = match.group(1) + assert '\n' not in emacs_vars_str + emacs_var_strs = [s.strip() for s in emacs_vars_str.split(';') + if s.strip()] + if len(emacs_var_strs) == 1 and ':' not in emacs_var_strs[0]: + # While not in the spec, this form is allowed by emacs: + # -*- Tcl -*- + # where the implied "variable" is "mode". This form + # is only allowed if there are no other variables. + emacs_vars["mode"] = emacs_var_strs[0].strip() + else: + for emacs_var_str in emacs_var_strs: + try: + variable, value = emacs_var_str.strip().split(':', 1) + except ValueError: + log.debug("emacs variables error: malformed -*- " + "line: %r", emacs_var_str) + continue + # Lowercase the variable name because Emacs allows "Mode" + # or "mode" or "MoDe", etc. + emacs_vars[variable.lower()] = value.strip() + + tail = text[-SIZE:] + if "Local Variables" in tail: + match = self._emacs_local_vars_pat.search(tail) + if match: + prefix = match.group("prefix") + suffix = match.group("suffix") + lines = match.group("content").splitlines(0) + #print "prefix=%r, suffix=%r, content=%r, lines: %s"\ + # % (prefix, suffix, match.group("content"), lines) + + # Validate the Local Variables block: proper prefix and suffix + # usage. + for i, line in enumerate(lines): + if not line.startswith(prefix): + log.debug("emacs variables error: line '%s' " + "does not use proper prefix '%s'" + % (line, prefix)) + return {} + # Don't validate suffix on last line. Emacs doesn't care, + # neither should we. + if i != len(lines)-1 and not line.endswith(suffix): + log.debug("emacs variables error: line '%s' " + "does not use proper suffix '%s'" + % (line, suffix)) + return {} + + # Parse out one emacs var per line. + continued_for = None + for line in lines[:-1]: # no var on the last line ("PREFIX End:") + if prefix: line = line[len(prefix):] # strip prefix + if suffix: line = line[:-len(suffix)] # strip suffix + line = line.strip() + if continued_for: + variable = continued_for + if line.endswith('\\'): + line = line[:-1].rstrip() + else: + continued_for = None + emacs_vars[variable] += ' ' + line + else: + try: + variable, value = line.split(':', 1) + except ValueError: + log.debug("local variables error: missing colon " + "in local variables entry: '%s'" % line) + continue + # Do NOT lowercase the variable name, because Emacs only + # allows "mode" (and not "Mode", "MoDe", etc.) in this block. + value = value.strip() + if value.endswith('\\'): + value = value[:-1].rstrip() + continued_for = variable + else: + continued_for = None + emacs_vars[variable] = value + + # Unquote values. + for var, val in list(emacs_vars.items()): + if len(val) > 1 and (val.startswith('"') and val.endswith('"') + or val.startswith('"') and val.endswith('"')): + emacs_vars[var] = val[1:-1] + + return emacs_vars + + # Cribbed from a post by Bart Lateur: + # + _detab_re = re.compile(r'(.*?)\t', re.M) + def _detab_sub(self, match): + g1 = match.group(1) + return g1 + (' ' * (self.tab_width - len(g1) % self.tab_width)) + def _detab(self, text): + r"""Remove (leading?) tabs from a file. + + >>> m = Markdown() + >>> m._detab("\tfoo") + ' foo' + >>> m._detab(" \tfoo") + ' foo' + >>> m._detab("\t foo") + ' foo' + >>> m._detab(" foo") + ' foo' + >>> m._detab(" foo\n\tbar\tblam") + ' foo\n bar blam' + """ + if '\t' not in text: + return text + return self._detab_re.subn(self._detab_sub, text)[0] + + # I broke out the html5 tags here and add them to _block_tags_a and + # _block_tags_b. This way html5 tags are easy to keep track of. + _html5tags = '|article|aside|header|hgroup|footer|nav|section|figure|figcaption' + + _block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del' + _block_tags_a += _html5tags + + _strict_tag_block_re = re.compile(r""" + ( # save in \1 + ^ # start of line (with re.M) + <(%s) # start tag = \2 + \b # word break + (.*\n)*? # any number of lines, minimally matching + # the matching end tag + [ \t]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + ) + """ % _block_tags_a, + re.X | re.M) + + _block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math' + _block_tags_b += _html5tags + + _liberal_tag_block_re = re.compile(r""" + ( # save in \1 + ^ # start of line (with re.M) + <(%s) # start tag = \2 + \b # word break + (.*\n)*? # any number of lines, minimally matching + .* # the matching end tag + [ \t]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + ) + """ % _block_tags_b, + re.X | re.M) + + _html_markdown_attr_re = re.compile( + r'''\s+markdown=("1"|'1')''') + def _hash_html_block_sub(self, match, raw=False): + html = match.group(1) + if raw and self.safe_mode: + html = self._sanitize_html(html) + elif 'markdown-in-html' in self.extras and 'markdown=' in html: + first_line = html.split('\n', 1)[0] + m = self._html_markdown_attr_re.search(first_line) + if m: + lines = html.split('\n') + middle = '\n'.join(lines[1:-1]) + last_line = lines[-1] + first_line = first_line[:m.start()] + first_line[m.end():] + f_key = _hash_text(first_line) + self.html_blocks[f_key] = first_line + l_key = _hash_text(last_line) + self.html_blocks[l_key] = last_line + return ''.join(["\n\n", f_key, + "\n\n", middle, "\n\n", + l_key, "\n\n"]) + key = _hash_text(html) + self.html_blocks[key] = html + return "\n\n" + key + "\n\n" + + def _hash_html_blocks(self, text, raw=False): + """Hashify HTML blocks + + We only want to do this for block-level HTML tags, such as headers, + lists, and tables. That's because we still want to wrap

      s around + "paragraphs" that are wrapped in non-block-level tags, such as anchors, + phrase emphasis, and spans. The list of tags we're looking for is + hard-coded. + + @param raw {boolean} indicates if these are raw HTML blocks in + the original source. It makes a difference in "safe" mode. + """ + if '<' not in text: + return text + + # Pass `raw` value into our calls to self._hash_html_block_sub. + hash_html_block_sub = _curry(self._hash_html_block_sub, raw=raw) + + # First, look for nested blocks, e.g.: + #

      + #
      + # tags for inner block must be indented. + #
      + #
      + # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
      ` and stop at the first `
      `. + text = self._strict_tag_block_re.sub(hash_html_block_sub, text) + + # Now match more liberally, simply from `\n` to `\n` + text = self._liberal_tag_block_re.sub(hash_html_block_sub, text) + + # Special case just for
      . It was easier to make a special + # case than to make the other regex more complicated. + if "", start_idx) + 3 + except ValueError: + break + + # Start position for next comment block search. + start = end_idx + + # Validate whitespace before comment. + if start_idx: + # - Up to `tab_width - 1` spaces before start_idx. + for i in range(self.tab_width - 1): + if text[start_idx - 1] != ' ': + break + start_idx -= 1 + if start_idx == 0: + break + # - Must be preceded by 2 newlines or hit the start of + # the document. + if start_idx == 0: + pass + elif start_idx == 1 and text[0] == '\n': + start_idx = 0 # to match minute detail of Markdown.pl regex + elif text[start_idx-2:start_idx] == '\n\n': + pass + else: + break + + # Validate whitespace after comment. + # - Any number of spaces and tabs. + while end_idx < len(text): + if text[end_idx] not in ' \t': + break + end_idx += 1 + # - Must be following by 2 newlines or hit end of text. + if text[end_idx:end_idx+2] not in ('', '\n', '\n\n'): + continue + + # Escape and hash (must match `_hash_html_block_sub`). + html = text[start_idx:end_idx] + if raw and self.safe_mode: + html = self._sanitize_html(html) + key = _hash_text(html) + self.html_blocks[key] = html + text = text[:start_idx] + "\n\n" + key + "\n\n" + text[end_idx:] + + if "xml" in self.extras: + # Treat XML processing instructions and namespaced one-liner + # tags as if they were block HTML tags. E.g., if standalone + # (i.e. are their own paragraph), the following do not get + # wrapped in a

      tag: + # + # + # + _xml_oneliner_re = _xml_oneliner_re_from_tab_width(self.tab_width) + text = _xml_oneliner_re.sub(hash_html_block_sub, text) + + return text + + def _strip_link_definitions(self, text): + # Strips link definitions from text, stores the URLs and titles in + # hash references. + less_than_tab = self.tab_width - 1 + + # Link defs are in the form: + # [id]: url "optional title" + _link_def_re = re.compile(r""" + ^[ ]{0,%d}\[(.+)\]: # id = \1 + [ \t]* + \n? # maybe *one* newline + [ \t]* + ? # url = \2 + [ \t]* + (?: + \n? # maybe one newline + [ \t]* + (?<=\s) # lookbehind for whitespace + ['"(] + ([^\n]*) # title = \3 + ['")] + [ \t]* + )? # title is optional + (?:\n+|\Z) + """ % less_than_tab, re.X | re.M | re.U) + return _link_def_re.sub(self._extract_link_def_sub, text) + + def _extract_link_def_sub(self, match): + id, url, title = match.groups() + key = id.lower() # Link IDs are case-insensitive + self.urls[key] = self._encode_amps_and_angles(url) + if title: + self.titles[key] = title + return "" + + def _extract_footnote_def_sub(self, match): + id, text = match.groups() + text = _dedent(text, skip_first_line=not text.startswith('\n')).strip() + normed_id = re.sub(r'\W', '-', id) + # Ensure footnote text ends with a couple newlines (for some + # block gamut matches). + self.footnotes[normed_id] = text + "\n\n" + return "" + + def _strip_footnote_definitions(self, text): + """A footnote definition looks like this: + + [^note-id]: Text of the note. + + May include one or more indented paragraphs. + + Where, + - The 'note-id' can be pretty much anything, though typically it + is the number of the footnote. + - The first paragraph may start on the next line, like so: + + [^note-id]: + Text of the note. + """ + less_than_tab = self.tab_width - 1 + footnote_def_re = re.compile(r''' + ^[ ]{0,%d}\[\^(.+)\]: # id = \1 + [ \t]* + ( # footnote text = \2 + # First line need not start with the spaces. + (?:\s*.*\n+) + (?: + (?:[ ]{%d} | \t) # Subsequent lines must be indented. + .*\n+ + )* + ) + # Lookahead for non-space at line-start, or end of doc. + (?:(?=^[ ]{0,%d}\S)|\Z) + ''' % (less_than_tab, self.tab_width, self.tab_width), + re.X | re.M) + return footnote_def_re.sub(self._extract_footnote_def_sub, text) + + + _hr_data = [ + ('*', re.compile(r"^[ ]{0,3}\*(.*?)$", re.M)), + ('-', re.compile(r"^[ ]{0,3}\-(.*?)$", re.M)), + ('_', re.compile(r"^[ ]{0,3}\_(.*?)$", re.M)), + ] + + def _run_block_gamut(self, text): + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + + if "fenced-code-blocks" in self.extras: + text = self._do_fenced_code_blocks(text) + + text = self._do_headers(text) + + # Do Horizontal Rules: + # On the number of spaces in horizontal rules: The spec is fuzzy: "If + # you wish, you may use spaces between the hyphens or asterisks." + # Markdown.pl 1.0.1's hr regexes limit the number of spaces between the + # hr chars to one or two. We'll reproduce that limit here. + hr = "\n tags around block-level tags. + text = self._hash_html_blocks(text) + + text = self._form_paragraphs(text) + + return text + + def _pyshell_block_sub(self, match): + lines = match.group(0).splitlines(0) + _dedentlines(lines) + indent = ' ' * self.tab_width + s = ('\n' # separate from possible cuddled paragraph + + indent + ('\n'+indent).join(lines) + + '\n\n') + return s + + def _prepare_pyshell_blocks(self, text): + """Ensure that Python interactive shell sessions are put in + code blocks -- even if not properly indented. + """ + if ">>>" not in text: + return text + + less_than_tab = self.tab_width - 1 + _pyshell_block_re = re.compile(r""" + ^([ ]{0,%d})>>>[ ].*\n # first line + ^(\1.*\S+.*\n)* # any number of subsequent lines + ^\n # ends with a blank line + """ % less_than_tab, re.M | re.X) + + return _pyshell_block_re.sub(self._pyshell_block_sub, text) + + def _wiki_table_sub(self, match): + ttext = match.group(0).strip() + #print 'wiki table: %r' % match.group(0) + rows = [] + for line in ttext.splitlines(0): + line = line.strip()[2:-2].strip() + row = [c.strip() for c in re.split(r'(?', '

      '] + for row in rows: + hrow = [''] + for cell in row: + hrow.append('') + hrow.append('') + hlines.append(''.join(hrow)) + hlines += ['', '
      ') + hrow.append(self._run_span_gamut(cell)) + hrow.append('
      '] + return '\n'.join(hlines) + '\n' + + def _do_wiki_tables(self, text): + # Optimization. + if "||" not in text: + return text + + less_than_tab = self.tab_width - 1 + wiki_table_re = re.compile(r''' + (?:(?<=\n\n)|\A\n?) # leading blank line + ^([ ]{0,%d})\|\|.+?\|\|[ ]*\n # first line + (^\1\|\|.+?\|\|\n)* # any number of subsequent lines + ''' % less_than_tab, re.M | re.X) + return wiki_table_re.sub(self._wiki_table_sub, text) + + def _run_span_gamut(self, text): + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + + text = self._do_code_spans(text) + + text = self._escape_special_chars(text) + + # Process anchor and image tags. + text = self._do_links(text) + + # Make links out of things like `` + # Must come after _do_links(), because you can use < and > + # delimiters in inline links like [this](). + text = self._do_auto_links(text) + + if "link-patterns" in self.extras: + text = self._do_link_patterns(text) + + text = self._encode_amps_and_angles(text) + + text = self._do_italics_and_bold(text) + + if "smarty-pants" in self.extras: + text = self._do_smart_punctuation(text) + + # Do hard breaks: + text = re.sub(r" {2,}\n", " + | + # auto-link (e.g., ) + <\w+[^>]*> + | + # comment + | + <\?.*?\?> # processing instruction + ) + """, re.X) + + def _escape_special_chars(self, text): + # Python markdown note: the HTML tokenization here differs from + # that in Markdown.pl, hence the behaviour for subtle cases can + # differ (I believe the tokenizer here does a better job because + # it isn't susceptible to unmatched '<' and '>' in HTML tags). + # Note, however, that '>' is not allowed in an auto-link URL + # here. + escaped = [] + is_html_markup = False + for token in self._sorta_html_tokenize_re.split(text): + if is_html_markup: + # Within tags/HTML-comments/auto-links, encode * and _ + # so they don't conflict with their use in Markdown for + # italics and strong. We're replacing each such + # character with its corresponding MD5 checksum value; + # this is likely overkill, but it should prevent us from + # colliding with the escape values by accident. + escaped.append(token.replace('*', self._escape_table['*']) + .replace('_', self._escape_table['_'])) + else: + escaped.append(self._encode_backslash_escapes(token)) + is_html_markup = not is_html_markup + return ''.join(escaped) + + def _hash_html_spans(self, text): + # Used for safe_mode. + + def _is_auto_link(s): + if ':' in s and self._auto_link_re.match(s): + return True + elif '@' in s and self._auto_email_link_re.match(s): + return True + return False + + tokens = [] + is_html_markup = False + for token in self._sorta_html_tokenize_re.split(text): + if is_html_markup and not _is_auto_link(token): + sanitized = self._sanitize_html(token) + key = _hash_text(sanitized) + self.html_spans[key] = sanitized + tokens.append(key) + else: + tokens.append(token) + is_html_markup = not is_html_markup + return ''.join(tokens) + + def _unhash_html_spans(self, text): + for key, sanitized in list(self.html_spans.items()): + text = text.replace(key, sanitized) + return text + + def _sanitize_html(self, s): + if self.safe_mode == "replace": + return self.html_removed_text + elif self.safe_mode == "escape": + replacements = [ + ('&', '&'), + ('<', '<'), + ('>', '>'), + ] + for before, after in replacements: + s = s.replace(before, after) + return s + else: + raise MarkdownError("invalid value for 'safe_mode': %r (must be " + "'escape' or 'replace')" % self.safe_mode) + + _tail_of_inline_link_re = re.compile(r''' + # Match tail of: [text](/url/) or [text](/url/ "title") + \( # literal paren + [ \t]* + (?P # \1 + <.*?> + | + .*? + ) + [ \t]* + ( # \2 + (['"]) # quote char = \3 + (?P.*?) + \3 # matching quote + )? # title is optional + \) + ''', re.X | re.S) + _tail_of_reference_link_re = re.compile(r''' + # Match tail of: [text][id] + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + \[ + (?P<id>.*?) + \] + ''', re.X | re.S) + + def _do_links(self, text): + """Turn Markdown link shortcuts into XHTML <a> and <img> tags. + + This is a combination of Markdown.pl's _DoAnchors() and + _DoImages(). They are done together because that simplified the + approach. It was necessary to use a different approach than + Markdown.pl because of the lack of atomic matching support in + Python's regex engine used in $g_nested_brackets. + """ + MAX_LINK_TEXT_SENTINEL = 3000 # markdown2 issue 24 + + # `anchor_allowed_pos` is used to support img links inside + # anchors, but not anchors inside anchors. An anchor's start + # pos must be `>= anchor_allowed_pos`. + anchor_allowed_pos = 0 + + curr_pos = 0 + while True: # Handle the next link. + # The next '[' is the start of: + # - an inline anchor: [text](url "title") + # - a reference anchor: [text][id] + # - an inline img: ![text](url "title") + # - a reference img: ![text][id] + # - a footnote ref: [^id] + # (Only if 'footnotes' extra enabled) + # - a footnote defn: [^id]: ... + # (Only if 'footnotes' extra enabled) These have already + # been stripped in _strip_footnote_definitions() so no + # need to watch for them. + # - a link definition: [id]: url "title" + # These have already been stripped in + # _strip_link_definitions() so no need to watch for them. + # - not markup: [...anything else... + try: + start_idx = text.index('[', curr_pos) + except ValueError: + break + text_length = len(text) + + # Find the matching closing ']'. + # Markdown.pl allows *matching* brackets in link text so we + # will here too. Markdown.pl *doesn't* currently allow + # matching brackets in img alt text -- we'll differ in that + # regard. + bracket_depth = 0 + for p in range(start_idx+1, min(start_idx+MAX_LINK_TEXT_SENTINEL, + text_length)): + ch = text[p] + if ch == ']': + bracket_depth -= 1 + if bracket_depth < 0: + break + elif ch == '[': + bracket_depth += 1 + else: + # Closing bracket not found within sentinel length. + # This isn't markup. + curr_pos = start_idx + 1 + continue + link_text = text[start_idx+1:p] + + # Possibly a footnote ref? + if "footnotes" in self.extras and link_text.startswith("^"): + normed_id = re.sub(r'\W', '-', link_text[1:]) + if normed_id in self.footnotes: + self.footnote_ids.append(normed_id) + result = '<sup class="footnote-ref" id="fnref-%s">' \ + '<a href="#fn-%s">%s</a></sup>' \ + % (normed_id, normed_id, len(self.footnote_ids)) + text = text[:start_idx] + result + text[p+1:] + else: + # This id isn't defined, leave the markup alone. + curr_pos = p+1 + continue + + # Now determine what this is by the remainder. + p += 1 + if p == text_length: + return text + + # Inline anchor or img? + if text[p] == '(': # attempt at perf improvement + match = self._tail_of_inline_link_re.match(text, p) + if match: + # Handle an inline anchor or img. + is_img = start_idx > 0 and text[start_idx-1] == "!" + if is_img: + start_idx -= 1 + + url, title = match.group("url"), match.group("title") + if url and url[0] == '<': + url = url[1:-1] # '<url>' -> 'url' + # We've got to encode these to avoid conflicting + # with italics/bold. + url = url.replace('*', self._escape_table['*']) \ + .replace('_', self._escape_table['_']) + if title: + title_str = ' title="%s"' % ( + _xml_escape_attr(title) + .replace('*', self._escape_table['*']) + .replace('_', self._escape_table['_'])) + else: + title_str = '' + if is_img: + result = '<img src="%s" alt="%s"%s%s' \ + % (url.replace('"', '"'), + _xml_escape_attr(link_text), + title_str, self.empty_element_suffix) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + curr_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + elif start_idx >= anchor_allowed_pos: + result_head = '<a href="%s"%s>' % (url, title_str) + result = '%s%s</a>' % (result_head, link_text) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + # <img> allowed from curr_pos on, <a> from + # anchor_allowed_pos on. + curr_pos = start_idx + len(result_head) + anchor_allowed_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + else: + # Anchor not allowed here. + curr_pos = start_idx + 1 + continue + + # Reference anchor or img? + else: + match = self._tail_of_reference_link_re.match(text, p) + if match: + # Handle a reference-style anchor or img. + is_img = start_idx > 0 and text[start_idx-1] == "!" + if is_img: + start_idx -= 1 + link_id = match.group("id").lower() + if not link_id: + link_id = link_text.lower() # for links like [this][] + if link_id in self.urls: + url = self.urls[link_id] + # We've got to encode these to avoid conflicting + # with italics/bold. + url = url.replace('*', self._escape_table['*']) \ + .replace('_', self._escape_table['_']) + title = self.titles.get(link_id) + if title: + before = title + title = _xml_escape_attr(title) \ + .replace('*', self._escape_table['*']) \ + .replace('_', self._escape_table['_']) + title_str = ' title="%s"' % title + else: + title_str = '' + if is_img: + result = '<img src="%s" alt="%s"%s%s' \ + % (url.replace('"', '"'), + link_text.replace('"', '"'), + title_str, self.empty_element_suffix) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + curr_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + elif start_idx >= anchor_allowed_pos: + result = '<a href="%s"%s>%s</a>' \ + % (url, title_str, link_text) + result_head = '<a href="%s"%s>' % (url, title_str) + result = '%s%s</a>' % (result_head, link_text) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + # <img> allowed from curr_pos on, <a> from + # anchor_allowed_pos on. + curr_pos = start_idx + len(result_head) + anchor_allowed_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + else: + # Anchor not allowed here. + curr_pos = start_idx + 1 + else: + # This id isn't defined, leave the markup alone. + curr_pos = match.end() + continue + + # Otherwise, it isn't markup. + curr_pos = start_idx + 1 + + return text + + def header_id_from_text(self, text, prefix, n): + """Generate a header id attribute value from the given header + HTML content. + + This is only called if the "header-ids" extra is enabled. + Subclasses may override this for different header ids. + + @param text {str} The text of the header tag + @param prefix {str} The requested prefix for header ids. This is the + value of the "header-ids" extra key, if any. Otherwise, None. + @param n {int} The <hN> tag number, i.e. `1` for an <h1> tag. + @returns {str} The value for the header tag's "id" attribute. Return + None to not have an id attribute and to exclude this header from + the TOC (if the "toc" extra is specified). + """ + header_id = _slugify(text) + if prefix and isinstance(prefix, base_string_type): + header_id = prefix + '-' + header_id + if header_id in self._count_from_header_id: + self._count_from_header_id[header_id] += 1 + header_id += '-%s' % self._count_from_header_id[header_id] + else: + self._count_from_header_id[header_id] = 1 + return header_id + + _toc = None + def _toc_add_entry(self, level, id, name): + if self._toc is None: + self._toc = [] + self._toc.append((level, id, name)) + + _setext_h_re = re.compile(r'^(.+)[ \t]*\n(=+|-+)[ \t]*\n+', re.M) + def _setext_h_sub(self, match): + n = {"=": 1, "-": 2}[match.group(2)[0]] + demote_headers = self.extras.get("demote-headers") + if demote_headers: + n = min(n + demote_headers, 6) + header_id_attr = "" + if "header-ids" in self.extras: + header_id = self.header_id_from_text(match.group(1), + self.extras["header-ids"], n) + if header_id: + header_id_attr = ' id="%s"' % header_id + html = self._run_span_gamut(match.group(1)) + if "toc" in self.extras and header_id: + self._toc_add_entry(n, header_id, html) + return "<h%d%s>%s</h%d>\n\n" % (n, header_id_attr, html, n) + + _atx_h_re = re.compile(r''' + ^(\#{1,6}) # \1 = string of #'s + [ \t]* + (.+?) # \2 = Header text + [ \t]* + (?<!\\) # ensure not an escaped trailing '#' + \#* # optional closing #'s (not counted) + \n+ + ''', re.X | re.M) + def _atx_h_sub(self, match): + n = len(match.group(1)) + demote_headers = self.extras.get("demote-headers") + if demote_headers: + n = min(n + demote_headers, 6) + header_id_attr = "" + if "header-ids" in self.extras: + header_id = self.header_id_from_text(match.group(2), + self.extras["header-ids"], n) + if header_id: + header_id_attr = ' id="%s"' % header_id + html = self._run_span_gamut(match.group(2)) + if "toc" in self.extras and header_id: + self._toc_add_entry(n, header_id, html) + return "<h%d%s>%s</h%d>\n\n" % (n, header_id_attr, html, n) + + def _do_headers(self, text): + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + text = self._setext_h_re.sub(self._setext_h_sub, text) + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + text = self._atx_h_re.sub(self._atx_h_sub, text) + + return text + + + _marker_ul_chars = '*+-' + _marker_any = r'(?:[%s]|\d+\.)' % _marker_ul_chars + _marker_ul = '(?:[%s])' % _marker_ul_chars + _marker_ol = r'(?:\d+\.)' + + def _list_sub(self, match): + lst = match.group(1) + lst_type = match.group(3) in self._marker_ul_chars and "ul" or "ol" + result = self._process_list_items(lst) + if self.list_level: + return "<%s>\n%s</%s>\n" % (lst_type, result, lst_type) + else: + return "<%s>\n%s</%s>\n\n" % (lst_type, result, lst_type) + + def _do_lists(self, text): + # Form HTML ordered (numbered) and unordered (bulleted) lists. + + # Iterate over each *non-overlapping* list match. + pos = 0 + while True: + # Find the *first* hit for either list style (ul or ol). We + # match ul and ol separately to avoid adjacent lists of different + # types running into each other (see issue #16). + hits = [] + for marker_pat in (self._marker_ul, self._marker_ol): + less_than_tab = self.tab_width - 1 + whole_list = r''' + ( # \1 = whole list + ( # \2 + [ ]{0,%d} + (%s) # \3 = first list item marker + [ \t]+ + (?!\ *\3\ ) # '- - - ...' isn't a list. See 'not_quite_a_list' test case. + ) + (?:.+?) + ( # \4 + \Z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ \t]* + %s[ \t]+ + ) + ) + ) + ''' % (less_than_tab, marker_pat, marker_pat) + if self.list_level: # sub-list + list_re = re.compile("^"+whole_list, re.X | re.M | re.S) + else: + list_re = re.compile(r"(?:(?<=\n\n)|\A\n?)"+whole_list, + re.X | re.M | re.S) + match = list_re.search(text, pos) + if match: + hits.append((match.start(), match)) + if not hits: + break + hits.sort() + match = hits[0][1] + start, end = match.span() + text = text[:start] + self._list_sub(match) + text[end:] + pos = end + + return text + + _list_item_re = re.compile(r''' + (\n)? # leading line = \1 + (^[ \t]*) # leading whitespace = \2 + (?P<marker>%s) [ \t]+ # list marker = \3 + ((?:.+?) # list item text = \4 + (\n{1,2})) # eols = \5 + (?= \n* (\Z | \2 (?P<next_marker>%s) [ \t]+)) + ''' % (_marker_any, _marker_any), + re.M | re.X | re.S) + + _last_li_endswith_two_eols = False + def _list_item_sub(self, match): + item = match.group(4) + leading_line = match.group(1) + leading_space = match.group(2) + if leading_line or "\n\n" in item or self._last_li_endswith_two_eols: + item = self._run_block_gamut(self._outdent(item)) + else: + # Recursion for sub-lists: + item = self._do_lists(self._outdent(item)) + if item.endswith('\n'): + item = item[:-1] + item = self._run_span_gamut(item) + self._last_li_endswith_two_eols = (len(match.group(5)) == 2) + return "<li>%s</li>\n" % item + + def _process_list_items(self, list_str): + # Process the contents of a single ordered or unordered list, + # splitting it into individual list items. + + # The $g_list_level global keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + self.list_level += 1 + self._last_li_endswith_two_eols = False + list_str = list_str.rstrip('\n') + '\n' + list_str = self._list_item_re.sub(self._list_item_sub, list_str) + self.list_level -= 1 + return list_str + + def _get_pygments_lexer(self, lexer_name): + try: + from pygments import lexers, util + except ImportError: + return None + try: + return lexers.get_lexer_by_name(lexer_name) + except util.ClassNotFound: + return None + + def _color_with_pygments(self, codeblock, lexer, **formatter_opts): + import pygments + import pygments.formatters + + class HtmlCodeFormatter(pygments.formatters.HtmlFormatter): + def _wrap_code(self, inner): + """A function for use in a Pygments Formatter which + wraps in <code> tags. + """ + yield 0, "<code>" + for tup in inner: + yield tup + yield 0, "</code>" + + def wrap(self, source, outfile): + """Return the source with a code, pre, and div.""" + return self._wrap_div(self._wrap_pre(self._wrap_code(source))) + + formatter_opts.setdefault("cssclass", "codehilite") + formatter = HtmlCodeFormatter(**formatter_opts) + return pygments.highlight(codeblock, lexer, formatter) + + def _code_block_sub(self, match, is_fenced_code_block=False): + lexer_name = None + if is_fenced_code_block: + lexer_name = match.group(1) + if lexer_name: + formatter_opts = self.extras['fenced-code-blocks'] or {} + codeblock = match.group(2) + codeblock = codeblock[:-1] # drop one trailing newline + else: + codeblock = match.group(1) + codeblock = self._outdent(codeblock) + codeblock = self._detab(codeblock) + codeblock = codeblock.lstrip('\n') # trim leading newlines + codeblock = codeblock.rstrip() # trim trailing whitespace + + # Note: "code-color" extra is DEPRECATED. + if "code-color" in self.extras and codeblock.startswith(":::"): + lexer_name, rest = codeblock.split('\n', 1) + lexer_name = lexer_name[3:].strip() + codeblock = rest.lstrip("\n") # Remove lexer declaration line. + formatter_opts = self.extras['code-color'] or {} + + if lexer_name: + lexer = self._get_pygments_lexer(lexer_name) + if lexer: + colored = self._color_with_pygments(codeblock, lexer, + **formatter_opts) + return "\n\n%s\n\n" % colored + + codeblock = self._encode_code(codeblock) + pre_class_str = self._html_class_str_from_tag("pre") + code_class_str = self._html_class_str_from_tag("code") + return "\n\n<pre%s><code%s>%s\n</code></pre>\n\n" % ( + pre_class_str, code_class_str, codeblock) + + def _html_class_str_from_tag(self, tag): + """Get the appropriate ' class="..."' string (note the leading + space), if any, for the given tag. + """ + if "html-classes" not in self.extras: + return "" + try: + html_classes_from_tag = self.extras["html-classes"] + except TypeError: + return "" + else: + if tag in html_classes_from_tag: + return ' class="%s"' % html_classes_from_tag[tag] + return "" + + def _do_code_blocks(self, text): + """Process Markdown `<pre><code>` blocks.""" + code_block_re = re.compile(r''' + (?:\n\n|\A\n?) + ( # $1 = the code block -- one or more lines, starting with a space/tab + (?: + (?:[ ]{%d} | \t) # Lines must start with a tab or a tab-width of spaces + .*\n+ + )+ + ) + ((?=^[ ]{0,%d}\S)|\Z) # Lookahead for non-space at line-start, or end of doc + ''' % (self.tab_width, self.tab_width), + re.M | re.X) + return code_block_re.sub(self._code_block_sub, text) + + _fenced_code_block_re = re.compile(r''' + (?:\n\n|\A\n?) + ^```([\w+-]+)?[ \t]*\n # opening fence, $1 = optional lang + (.*?) # $2 = code block content + ^```[ \t]*\n # closing fence + ''', re.M | re.X | re.S) + + def _fenced_code_block_sub(self, match): + return self._code_block_sub(match, is_fenced_code_block=True); + + def _do_fenced_code_blocks(self, text): + """Process ```-fenced unindented code blocks ('fenced-code-blocks' extra).""" + return self._fenced_code_block_re.sub(self._fenced_code_block_sub, text) + + # Rules for a code span: + # - backslash escapes are not interpreted in a code span + # - to include one or or a run of more backticks the delimiters must + # be a longer run of backticks + # - cannot start or end a code span with a backtick; pad with a + # space and that space will be removed in the emitted HTML + # See `test/tm-cases/escapes.text` for a number of edge-case + # examples. + _code_span_re = re.compile(r''' + (?<!\\) + (`+) # \1 = Opening run of ` + (?!`) # See Note A test/tm-cases/escapes.text + (.+?) # \2 = The code block + (?<!`) + \1 # Matching closer + (?!`) + ''', re.X | re.S) + + def _code_span_sub(self, match): + c = match.group(2).strip(" \t") + c = self._encode_code(c) + return "<code>%s</code>" % c + + def _do_code_spans(self, text): + # * Backtick quotes are used for <code></code> spans. + # + # * You can use multiple backticks as the delimiters if you want to + # include literal backticks in the code span. So, this input: + # + # Just type ``foo `bar` baz`` at the prompt. + # + # Will translate to: + # + # <p>Just type <code>foo `bar` baz</code> at the prompt.</p> + # + # There's no arbitrary limit to the number of backticks you + # can use as delimters. If you need three consecutive backticks + # in your code, use four for delimiters, etc. + # + # * You can use spaces to get literal backticks at the edges: + # + # ... type `` `bar` `` ... + # + # Turns to: + # + # ... type <code>`bar`</code> ... + return self._code_span_re.sub(self._code_span_sub, text) + + def _encode_code(self, text): + """Encode/escape certain characters inside Markdown code runs. + The point is that in code, these characters are literals, + and lose their special Markdown meanings. + """ + replacements = [ + # Encode all ampersands; HTML entities are not + # entities within a Markdown code span. + ('&', '&'), + # Do the angle bracket song and dance: + ('<', '<'), + ('>', '>'), + ] + for before, after in replacements: + text = text.replace(before, after) + hashed = _hash_text(text) + self._escape_table[text] = hashed + return hashed + + _strong_re = re.compile(r"(\*\*|__)(?=\S)(.+?[*_]*)(?<=\S)\1", re.S) + _em_re = re.compile(r"(\*|_)(?=\S)(.+?)(?<=\S)\1", re.S) + _code_friendly_strong_re = re.compile(r"\*\*(?=\S)(.+?[*_]*)(?<=\S)\*\*", re.S) + _code_friendly_em_re = re.compile(r"\*(?=\S)(.+?)(?<=\S)\*", re.S) + def _do_italics_and_bold(self, text): + # <strong> must go first: + if "code-friendly" in self.extras: + text = self._code_friendly_strong_re.sub(r"<strong>\1</strong>", text) + text = self._code_friendly_em_re.sub(r"<em>\1</em>", text) + else: + text = self._strong_re.sub(r"<strong>\2</strong>", text) + text = self._em_re.sub(r"<em>\2</em>", text) + return text + + # "smarty-pants" extra: Very liberal in interpreting a single prime as an + # apostrophe; e.g. ignores the fact that "round", "bout", "twer", and + # "twixt" can be written without an initial apostrophe. This is fine because + # using scare quotes (single quotation marks) is rare. + _apostrophe_year_re = re.compile(r"'(\d\d)(?=(\s|,|;|\.|\?|!|$))") + _contractions = ["tis", "twas", "twer", "neath", "o", "n", + "round", "bout", "twixt", "nuff", "fraid", "sup"] + def _do_smart_contractions(self, text): + text = self._apostrophe_year_re.sub(r"’\1", text) + for c in self._contractions: + text = text.replace("'%s" % c, "’%s" % c) + text = text.replace("'%s" % c.capitalize(), + "’%s" % c.capitalize()) + return text + + # Substitute double-quotes before single-quotes. + _opening_single_quote_re = re.compile(r"(?<!\S)'(?=\S)") + _opening_double_quote_re = re.compile(r'(?<!\S)"(?=\S)') + _closing_single_quote_re = re.compile(r"(?<=\S)'") + _closing_double_quote_re = re.compile(r'(?<=\S)"(?=(\s|,|;|\.|\?|!|$))') + def _do_smart_punctuation(self, text): + """Fancifies 'single quotes', "double quotes", and apostrophes. + Converts --, ---, and ... into en dashes, em dashes, and ellipses. + + Inspiration is: <http://daringfireball.net/projects/smartypants/> + See "test/tm-cases/smarty_pants.text" for a full discussion of the + support here and + <http://code.google.com/p/python-markdown2/issues/detail?id=42> for a + discussion of some diversion from the original SmartyPants. + """ + if "'" in text: # guard for perf + text = self._do_smart_contractions(text) + text = self._opening_single_quote_re.sub("‘", text) + text = self._closing_single_quote_re.sub("’", text) + + if '"' in text: # guard for perf + text = self._opening_double_quote_re.sub("“", text) + text = self._closing_double_quote_re.sub("”", text) + + text = text.replace("---", "—") + text = text.replace("--", "–") + text = text.replace("...", "…") + text = text.replace(" . . . ", "…") + text = text.replace(". . .", "…") + return text + + _block_quote_re = re.compile(r''' + ( # Wrap whole match in \1 + ( + ^[ \t]*>[ \t]? # '>' at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + ''', re.M | re.X) + _bq_one_level_re = re.compile('^[ \t]*>[ \t]?', re.M); + + _html_pre_block_re = re.compile(r'(\s*<pre>.+?</pre>)', re.S) + def _dedent_two_spaces_sub(self, match): + return re.sub(r'(?m)^ ', '', match.group(1)) + + def _block_quote_sub(self, match): + bq = match.group(1) + bq = self._bq_one_level_re.sub('', bq) # trim one level of quoting + bq = self._ws_only_line_re.sub('', bq) # trim whitespace-only lines + bq = self._run_block_gamut(bq) # recurse + + bq = re.sub('(?m)^', ' ', bq) + # These leading spaces screw with <pre> content, so we need to fix that: + bq = self._html_pre_block_re.sub(self._dedent_two_spaces_sub, bq) + + return "<blockquote>\n%s\n</blockquote>\n\n" % bq + + def _do_block_quotes(self, text): + if '>' not in text: + return text + return self._block_quote_re.sub(self._block_quote_sub, text) + + def _form_paragraphs(self, text): + # Strip leading and trailing lines: + text = text.strip('\n') + + # Wrap <p> tags. + grafs = [] + for i, graf in enumerate(re.split(r"\n{2,}", text)): + if graf in self.html_blocks: + # Unhashify HTML blocks + grafs.append(self.html_blocks[graf]) + else: + cuddled_list = None + if "cuddled-lists" in self.extras: + # Need to put back trailing '\n' for `_list_item_re` + # match at the end of the paragraph. + li = self._list_item_re.search(graf + '\n') + # Two of the same list marker in this paragraph: a likely + # candidate for a list cuddled to preceding paragraph + # text (issue 33). Note the `[-1]` is a quick way to + # consider numeric bullets (e.g. "1." and "2.") to be + # equal. + if (li and len(li.group(2)) <= 3 and li.group("next_marker") + and li.group("marker")[-1] == li.group("next_marker")[-1]): + start = li.start() + cuddled_list = self._do_lists(graf[start:]).rstrip("\n") + assert cuddled_list.startswith("<ul>") or cuddled_list.startswith("<ol>") + graf = graf[:start] + + # Wrap <p> tags. + graf = self._run_span_gamut(graf) + grafs.append("<p>" + graf.lstrip(" \t") + "</p>") + + if cuddled_list: + grafs.append(cuddled_list) + + return "\n\n".join(grafs) + + def _add_footnotes(self, text): + if self.footnotes: + footer = [ + '<div class="footnotes">', + '<hr' + self.empty_element_suffix, + '<ol>', + ] + for i, id in enumerate(self.footnote_ids): + if i != 0: + footer.append('') + footer.append('<li id="fn-%s">' % id) + footer.append(self._run_block_gamut(self.footnotes[id])) + backlink = ('<a href="#fnref-%s" ' + 'class="footnoteBackLink" ' + 'title="Jump back to footnote %d in the text.">' + '↩</a>' % (id, i+1)) + if footer[-1].endswith("</p>"): + footer[-1] = footer[-1][:-len("</p>")] \ + + ' ' + backlink + "</p>" + else: + footer.append("\n<p>%s</p>" % backlink) + footer.append('</li>') + footer.append('</ol>') + footer.append('</div>') + return text + '\n\n' + '\n'.join(footer) + else: + return text + + # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + # http://bumppo.net/projects/amputator/ + _ampersand_re = re.compile(r'&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)') + _naked_lt_re = re.compile(r'<(?![a-z/?\$!])', re.I) + _naked_gt_re = re.compile(r'''(?<![a-z0-9?!/'"-])>''', re.I) + + def _encode_amps_and_angles(self, text): + # Smart processing for ampersands and angle brackets that need + # to be encoded. + text = self._ampersand_re.sub('&', text) + + # Encode naked <'s + text = self._naked_lt_re.sub('<', text) + + # Encode naked >'s + # Note: Other markdown implementations (e.g. Markdown.pl, PHP + # Markdown) don't do this. + text = self._naked_gt_re.sub('>', text) + return text + + def _encode_backslash_escapes(self, text): + for ch, escape in list(self._escape_table.items()): + text = text.replace("\\"+ch, escape) + return text + + _auto_link_re = re.compile(r'<((https?|ftp):[^\'">\s]+)>', re.I) + def _auto_link_sub(self, match): + g1 = match.group(1) + return '<a href="%s">%s</a>' % (g1, g1) + + _auto_email_link_re = re.compile(r""" + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-\w]+(\.[-\w]+)*\.[a-z]+ + ) + > + """, re.I | re.X | re.U) + def _auto_email_link_sub(self, match): + return self._encode_email_address( + self._unescape_special_chars(match.group(1))) + + def _do_auto_links(self, text): + text = self._auto_link_re.sub(self._auto_link_sub, text) + text = self._auto_email_link_re.sub(self._auto_email_link_sub, text) + return text + + def _encode_email_address(self, addr): + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + # <a href="mailto:foo@e + # xample.com">foo + # @example.com</a> + # + # Based on a filter by Matthew Wickline, posted to the BBEdit-Talk + # mailing list: <http://tinyurl.com/yu7ue> + chars = [_xml_encode_email_char_at_random(ch) + for ch in "mailto:" + addr] + # Strip the mailto: from the visible part. + addr = '<a href="%s">%s</a>' \ + % (''.join(chars), ''.join(chars[7:])) + return addr + + def _do_link_patterns(self, text): + """Caveat emptor: there isn't much guarding against link + patterns being formed inside other standard Markdown links, e.g. + inside a [link def][like this]. + + Dev Notes: *Could* consider prefixing regexes with a negative + lookbehind assertion to attempt to guard against this. + """ + link_from_hash = {} + for regex, repl in self.link_patterns: + replacements = [] + for match in regex.finditer(text): + if hasattr(repl, "__call__"): + href = repl(match) + else: + href = match.expand(repl) + replacements.append((match.span(), href)) + for (start, end), href in reversed(replacements): + escaped_href = ( + href.replace('"', '"') # b/c of attr quote + # To avoid markdown <em> and <strong>: + .replace('*', self._escape_table['*']) + .replace('_', self._escape_table['_'])) + link = '<a href="%s">%s</a>' % (escaped_href, text[start:end]) + hash = _hash_text(link) + link_from_hash[hash] = link + text = text[:start] + hash + text[end:] + for hash, link in list(link_from_hash.items()): + text = text.replace(hash, link) + return text + + def _unescape_special_chars(self, text): + # Swap back in all the special characters we've hidden. + for ch, hash in list(self._escape_table.items()): + text = text.replace(hash, ch) + return text + + def _outdent(self, text): + # Remove one level of line-leading tabs or spaces + return self._outdent_re.sub('', text) + + +class MarkdownWithExtras(Markdown): + """A markdowner class that enables most extras: + + - footnotes + - code-color (only has effect if 'pygments' Python module on path) + + These are not included: + - pyshell (specific to Python-related documenting) + - code-friendly (because it *disables* part of the syntax) + - link-patterns (because you need to specify some actual + link-patterns anyway) + """ + extras = ["footnotes", "code-color"] + + +#---- internal support functions + +class UnicodeWithAttrs(unicode): + """A subclass of unicode used for the return value of conversion to + possibly attach some attributes. E.g. the "toc_html" attribute when + the "toc" extra is used. + """ + metadata = None + _toc = None + def toc_html(self): + """Return the HTML for the current TOC. + + This expects the `_toc` attribute to have been set on this instance. + """ + if self._toc is None: + return None + + def indent(): + return ' ' * (len(h_stack) - 1) + lines = [] + h_stack = [0] # stack of header-level numbers + for level, id, name in self._toc: + if level > h_stack[-1]: + lines.append("%s<ul>" % indent()) + h_stack.append(level) + elif level == h_stack[-1]: + lines[-1] += "</li>" + else: + while level < h_stack[-1]: + h_stack.pop() + if not lines[-1].endswith("</li>"): + lines[-1] += "</li>" + lines.append("%s</ul></li>" % indent()) + lines.append('%s<li><a href="#%s">%s</a>' % ( + indent(), id, name)) + while len(h_stack) > 1: + h_stack.pop() + if not lines[-1].endswith("</li>"): + lines[-1] += "</li>" + lines.append("%s</ul>" % indent()) + return '\n'.join(lines) + '\n' + toc_html = property(toc_html) + +## {{{ http://code.activestate.com/recipes/577257/ (r1) +_slugify_strip_re = re.compile(r'[^\w\s-]') +_slugify_hyphenate_re = re.compile(r'[-\s]+') +def _slugify(value): + """ + Normalizes string, converts to lowercase, removes non-alpha characters, + and converts spaces to hyphens. + + From Django's "django/template/defaultfilters.py". + """ + import unicodedata + value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode() + value = _slugify_strip_re.sub('', value).strip().lower() + return _slugify_hyphenate_re.sub('-', value) +## end of http://code.activestate.com/recipes/577257/ }}} + + +# From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549 +def _curry(*args, **kwargs): + function, args = args[0], args[1:] + def result(*rest, **kwrest): + combined = kwargs.copy() + combined.update(kwrest) + return function(*args + rest, **combined) + return result + +# Recipe: regex_from_encoded_pattern (1.0) +def _regex_from_encoded_pattern(s): + """'foo' -> re.compile(re.escape('foo')) + '/foo/' -> re.compile('foo') + '/foo/i' -> re.compile('foo', re.I) + """ + if s.startswith('/') and s.rfind('/') != 0: + # Parse it: /PATTERN/FLAGS + idx = s.rfind('/') + pattern, flags_str = s[1:idx], s[idx+1:] + flag_from_char = { + "i": re.IGNORECASE, + "l": re.LOCALE, + "s": re.DOTALL, + "m": re.MULTILINE, + "u": re.UNICODE, + } + flags = 0 + for char in flags_str: + try: + flags |= flag_from_char[char] + except KeyError: + raise ValueError("unsupported regex flag: '%s' in '%s' " + "(must be one of '%s')" + % (char, s, ''.join(list(flag_from_char.keys())))) + return re.compile(s[1:idx], flags) + else: # not an encoded regex + return re.compile(re.escape(s)) + +# Recipe: dedent (0.1.2) +def _dedentlines(lines, tabsize=8, skip_first_line=False): + """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines + + "lines" is a list of lines to dedent. + "tabsize" is the tab width to use for indent width calculations. + "skip_first_line" is a boolean indicating if the first line should + be skipped for calculating the indent width and for dedenting. + This is sometimes useful for docstrings and similar. + + Same as dedent() except operates on a sequence of lines. Note: the + lines list is modified **in-place**. + """ + DEBUG = False + if DEBUG: + print("dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\ + % (tabsize, skip_first_line)) + indents = [] + margin = None + for i, line in enumerate(lines): + if i == 0 and skip_first_line: continue + indent = 0 + for ch in line: + if ch == ' ': + indent += 1 + elif ch == '\t': + indent += tabsize - (indent % tabsize) + elif ch in '\r\n': + continue # skip all-whitespace lines + else: + break + else: + continue # skip all-whitespace lines + if DEBUG: print("dedent: indent=%d: %r" % (indent, line)) + if margin is None: + margin = indent + else: + margin = min(margin, indent) + if DEBUG: print("dedent: margin=%r" % margin) + + if margin is not None and margin > 0: + for i, line in enumerate(lines): + if i == 0 and skip_first_line: continue + removed = 0 + for j, ch in enumerate(line): + if ch == ' ': + removed += 1 + elif ch == '\t': + removed += tabsize - (removed % tabsize) + elif ch in '\r\n': + if DEBUG: print("dedent: %r: EOL -> strip up to EOL" % line) + lines[i] = lines[i][j:] + break + else: + raise ValueError("unexpected non-whitespace char %r in " + "line %r while removing %d-space margin" + % (ch, line, margin)) + if DEBUG: + print("dedent: %r: %r -> removed %d/%d"\ + % (line, ch, removed, margin)) + if removed == margin: + lines[i] = lines[i][j+1:] + break + elif removed > margin: + lines[i] = ' '*(removed-margin) + lines[i][j+1:] + break + else: + if removed: + lines[i] = lines[i][removed:] + return lines + +def _dedent(text, tabsize=8, skip_first_line=False): + """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text + + "text" is the text to dedent. + "tabsize" is the tab width to use for indent width calculations. + "skip_first_line" is a boolean indicating if the first line should + be skipped for calculating the indent width and for dedenting. + This is sometimes useful for docstrings and similar. + + textwrap.dedent(s), but don't expand tabs to spaces + """ + lines = text.splitlines(1) + _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line) + return ''.join(lines) + + +class _memoized(object): + """Decorator that caches a function's return value each time it is called. + If called later with the same arguments, the cached value is returned, and + not re-evaluated. + + http://wiki.python.org/moin/PythonDecoratorLibrary + """ + def __init__(self, func): + self.func = func + self.cache = {} + def __call__(self, *args): + try: + return self.cache[args] + except KeyError: + self.cache[args] = value = self.func(*args) + return value + except TypeError: + # uncachable -- for instance, passing a list as an argument. + # Better to not cache than to blow up entirely. + return self.func(*args) + def __repr__(self): + """Return the function's docstring.""" + return self.func.__doc__ + + +def _xml_oneliner_re_from_tab_width(tab_width): + """Standalone XML processing instruction regex.""" + return re.compile(r""" + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,%d} + (?: + <\?\w+\b\s+.*?\?> # XML processing instruction + | + <\w+:\w+\b\s+.*?/> # namespaced single tag + ) + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + """ % (tab_width - 1), re.X) +_xml_oneliner_re_from_tab_width = _memoized(_xml_oneliner_re_from_tab_width) + +def _hr_tag_re_from_tab_width(tab_width): + return re.compile(r""" + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in \1 + [ ]{0,%d} + <(hr) # start tag = \2 + \b # word break + ([^<>])*? # + /?> # the matching end tag + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + """ % (tab_width - 1), re.X) +_hr_tag_re_from_tab_width = _memoized(_hr_tag_re_from_tab_width) + + +def _xml_escape_attr(attr, skip_single_quote=True): + """Escape the given string for use in an HTML/XML tag attribute. + + By default this doesn't bother with escaping `'` to `'`, presuming that + the tag attribute is surrounded by double quotes. + """ + escaped = (attr + .replace('&', '&') + .replace('"', '"') + .replace('<', '<') + .replace('>', '>')) + if not skip_single_quote: + escaped = escaped.replace("'", "'") + return escaped + + +def _xml_encode_email_char_at_random(ch): + r = random() + # Roughly 10% raw, 45% hex, 45% dec. + # '@' *must* be encoded. I [John Gruber] insist. + # Issue 26: '_' must be encoded. + if r > 0.9 and ch not in "@_": + return ch + elif r < 0.45: + # The [1:] is to drop leading '0': 0x63 -> x63 + return '&#%s;' % hex(ord(ch))[1:] + else: + return '&#%s;' % ord(ch) + + + +#---- mainline + +class _NoReflowFormatter(optparse.IndentedHelpFormatter): + """An optparse formatter that does NOT reflow the description.""" + def format_description(self, description): + return description or "" + +def _test(): + import doctest + doctest.testmod() + +def main(argv=None): + if argv is None: + argv = sys.argv + if not logging.root.handlers: + logging.basicConfig() + + usage = "usage: %prog [PATHS...]" + version = "%prog "+__version__ + parser = optparse.OptionParser(prog="markdown2", usage=usage, + version=version, description=cmdln_desc, + formatter=_NoReflowFormatter()) + parser.add_option("-v", "--verbose", dest="log_level", + action="store_const", const=logging.DEBUG, + help="more verbose output") + parser.add_option("--encoding", + help="specify encoding of text content") + parser.add_option("--html4tags", action="store_true", default=False, + help="use HTML 4 style for empty element tags") + parser.add_option("-s", "--safe", metavar="MODE", dest="safe_mode", + help="sanitize literal HTML: 'escape' escapes " + "HTML meta chars, 'replace' replaces with an " + "[HTML_REMOVED] note") + parser.add_option("-x", "--extras", action="append", + help="Turn on specific extra features (not part of " + "the core Markdown spec). See above.") + parser.add_option("--use-file-vars", + help="Look for and use Emacs-style 'markdown-extras' " + "file var to turn on extras. See " + "<https://github.com/trentm/python-markdown2/wiki/Extras>") + parser.add_option("--link-patterns-file", + help="path to a link pattern file") + parser.add_option("--self-test", action="store_true", + help="run internal self-tests (some doctests)") + parser.add_option("--compare", action="store_true", + help="run against Markdown.pl as well (for testing)") + parser.set_defaults(log_level=logging.INFO, compare=False, + encoding="utf-8", safe_mode=None, use_file_vars=False) + opts, paths = parser.parse_args() + log.setLevel(opts.log_level) + + if opts.self_test: + return _test() + + if opts.extras: + extras = {} + for s in opts.extras: + splitter = re.compile("[,;: ]+") + for e in splitter.split(s): + if '=' in e: + ename, earg = e.split('=', 1) + try: + earg = int(earg) + except ValueError: + pass + else: + ename, earg = e, None + extras[ename] = earg + else: + extras = None + + if opts.link_patterns_file: + link_patterns = [] + f = open(opts.link_patterns_file) + try: + for i, line in enumerate(f.readlines()): + if not line.strip(): continue + if line.lstrip().startswith("#"): continue + try: + pat, href = line.rstrip().rsplit(None, 1) + except ValueError: + raise MarkdownError("%s:%d: invalid link pattern line: %r" + % (opts.link_patterns_file, i+1, line)) + link_patterns.append( + (_regex_from_encoded_pattern(pat), href)) + finally: + f.close() + else: + link_patterns = None + + from os.path import join, dirname, abspath, exists + markdown_pl = join(dirname(dirname(abspath(__file__))), "test", + "Markdown.pl") + if not paths: + paths = ['-'] + for path in paths: + if path == '-': + text = sys.stdin.read() + else: + fp = codecs.open(path, 'r', opts.encoding) + text = fp.read() + fp.close() + if opts.compare: + from subprocess import Popen, PIPE + print("==== Markdown.pl ====") + p = Popen('perl %s' % markdown_pl, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True) + p.stdin.write(text.encode('utf-8')) + p.stdin.close() + perl_html = p.stdout.read().decode('utf-8') + if py3: + sys.stdout.write(perl_html) + else: + sys.stdout.write(perl_html.encode( + sys.stdout.encoding or "utf-8", 'xmlcharrefreplace')) + print("==== markdown2.py ====") + html = markdown(text, + html4tags=opts.html4tags, + safe_mode=opts.safe_mode, + extras=extras, link_patterns=link_patterns, + use_file_vars=opts.use_file_vars) + if py3: + sys.stdout.write(html) + else: + sys.stdout.write(html.encode( + sys.stdout.encoding or "utf-8", 'xmlcharrefreplace')) + if extras and "toc" in extras: + log.debug("toc_html: " + + html.toc_html.encode(sys.stdout.encoding or "utf-8", 'xmlcharrefreplace')) + if opts.compare: + test_dir = join(dirname(dirname(abspath(__file__))), "test") + if exists(join(test_dir, "test_markdown2.py")): + sys.path.insert(0, test_dir) + from test_markdown2 import norm_html_from_html + norm_html = norm_html_from_html(html) + norm_perl_html = norm_html_from_html(perl_html) + else: + norm_html = html + norm_perl_html = perl_html + print("==== match? %r ====" % (norm_perl_html == norm_html)) + + +if __name__ == "__main__": + sys.exit( main(sys.argv) ) diff --git a/deps/markdown2.pyc b/deps/markdown2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86dbd7f501f42f868d98aebbd1377d82efb800f6 GIT binary patch literal 66561 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 literal 0 HcmV?d00001 diff --git a/doc/api/_toc.markdown b/doc/api/_toc.markdown new file mode 100644 index 0000000..0e90fe6 --- /dev/null +++ b/doc/api/_toc.markdown @@ -0,0 +1,37 @@ +* [About these Docs](documentation.html) +* [Synopsis](synopsis.html) +* [Globals](globals.html) +* [STDIO](stdio.html) +* [Timers](timers.html) +* [Modules](modules.html) +* [C/C++ Addons](addons.html) +* [Process](process.html) +* [Utilities](util.html) +* [Events](events.html) +* [Domain](domain.html) +* [Buffer](buffer.html) +* [Stream](stream.html) +* [Crypto](crypto.html) +* [TLS/SSL](tls.html) +* [String Decoder](string_decoder.html) +* [File System](fs.html) +* [Path](path.html) +* [Net](net.html) +* [UDP/Datagram](dgram.html) +* [DNS](dns.html) +* [HTTP](http.html) +* [HTTPS](https.html) +* [URL](url.html) +* [Query Strings](querystring.html) +* [Readline](readline.html) +* [REPL](repl.html) +* [VM](vm.html) +* [Child Processes](child_process.html) +* [Assertion Testing](assert.html) +* [TTY](tty.html) +* [ZLIB](zlib.html) +* [OS](os.html) +* [Debugger](debugger.html) +* [Cluster](cluster.html) +* Appendixes + * [Appendix 1: Recommended Third-party Modules](appendix_1.html) diff --git a/doc/api/addons.markdown b/doc/api/addons.markdown new file mode 100644 index 0000000..df42815 --- /dev/null +++ b/doc/api/addons.markdown @@ -0,0 +1,624 @@ +# Addons + +Addons are dynamically linked shared objects. They can provide glue to C and +C++ libraries. The API (at the moment) is rather complex, involving +knowledge of several libraries: + + - V8 JavaScript, a C++ library. Used for interfacing with JavaScript: + creating objects, calling functions, etc. Documented mostly in the + `v8.h` header file (`deps/v8/include/v8.h` in the Node source tree), + which is also available [online](http://izs.me/v8-docs/main.html). + + - [libuv](https://github.com/joyent/libuv), C event loop library. Anytime one + needs to wait for a file descriptor to become readable, wait for a timer, or + wait for a signal to received one will need to interface with libuv. That is, + if you perform any I/O, libuv will need to be used. + + - Internal Node libraries. Most importantly is the `node::ObjectWrap` + class which you will likely want to derive from. + + - Others. Look in `deps/` for what else is available. + +Node statically compiles all its dependencies into the executable. When +compiling your module, you don't need to worry about linking to any of these +libraries. + + +## Hello world + +To get started let's make a small Addon which is the C++ equivalent of +the following Javascript code: + + exports.hello = function() { return 'world'; }; + +First we create a file `hello.cc`: + + #include <node.h> + #include <v8.h> + + using namespace v8; + + Handle<Value> Method(const Arguments& args) { + HandleScope scope; + return scope.Close(String::New("world")); + } + + void init(Handle<Object> target) { + target->Set(String::NewSymbol("hello"), + FunctionTemplate::New(Method)->GetFunction()); + } + NODE_MODULE(hello, init) + +Note that all Node addons must export an initialization function: + + void Initialize (Handle<Object> target); + NODE_MODULE(module_name, Initialize) + +There is no semi-colon after `NODE_MODULE` as it's not a function (see `node.h`). + +The `module_name` needs to match the filename of the final binary (minus the +.node suffix). + +The source code needs to be built into `hello.node`, the binary Addon. To +do this we create a file called `binding.gyp` which describes the configuration +to build your module in a JSON-like format. This file gets compiled by +[node-gyp](https://github.com/TooTallNate/node-gyp). + + { + "targets": [ + { + "target_name": "hello", + "sources": [ "hello.cc" ] + } + ] + } + +The next step is to generate the appropriate project build files for the +current platform. Use `node-gyp configure` for that. + +Now you will have either a `Makefile` (on Unix platforms) or a `vcxproj` file +(on Windows) in the `build/` directory. Next invoke the `node-gyp build` +command. + +Now you have your compiled `.node` bindings file! The compiled bindings end up +in `build/Release/`. + +You can now use the binary addon in a Node project `hello.js` by pointing `require` to +the recently built `hello.node` module: + + var addon = require('./build/Release/hello'); + + console.log(addon.hello()); // 'world' + +Please see patterns below for further information or +<https://github.com/arturadib/node-qt> for an example in production. + + +## Addon patterns + +Below are some addon patterns to help you get started. Consult the online +[v8 reference](http://izs.me/v8-docs/main.html) for help with the various v8 +calls, and v8's [Embedder's Guide](http://code.google.com/apis/v8/embed.html) +for an explanation of several concepts used such as handles, scopes, +function templates, etc. + +In order to use these examples you need to compile them using `node-gyp`. +Create the following `binding.gyp` file: + + { + "targets": [ + { + "target_name": "addon", + "sources": [ "addon.cc" ] + } + ] + } + +In cases where there is more than one `.cc` file, simply add the file name to the +`sources` array, e.g.: + + "sources": ["addon.cc", "myexample.cc"] + +Now that you have your `binding.gyp` ready, you can configure and build the +addon: + + $ node-gyp configure build + + +### Function arguments + +The following pattern illustrates how to read arguments from JavaScript +function calls and return a result. This is the main and only needed source +`addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + + using namespace v8; + + Handle<Value> Add(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 2) { + ThrowException(Exception::TypeError(String::New("Wrong number of arguments"))); + return scope.Close(Undefined()); + } + + if (!args[0]->IsNumber() || !args[1]->IsNumber()) { + ThrowException(Exception::TypeError(String::New("Wrong arguments"))); + return scope.Close(Undefined()); + } + + Local<Number> num = Number::New(args[0]->NumberValue() + + args[1]->NumberValue()); + return scope.Close(num); + } + + void Init(Handle<Object> target) { + target->Set(String::NewSymbol("add"), + FunctionTemplate::New(Add)->GetFunction()); + } + + NODE_MODULE(addon, Init) + +You can test it with the following JavaScript snippet: + + var addon = require('./build/Release/addon'); + + console.log( 'This should be eight:', addon.add(3,5) ); + + +### Callbacks + +You can pass JavaScript functions to a C++ function and execute them from +there. Here's `addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + + using namespace v8; + + Handle<Value> RunCallback(const Arguments& args) { + HandleScope scope; + + Local<Function> cb = Local<Function>::Cast(args[0]); + const unsigned argc = 1; + Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) }; + cb->Call(Context::GetCurrent()->Global(), argc, argv); + + return scope.Close(Undefined()); + } + + void Init(Handle<Object> target) { + target->Set(String::NewSymbol("runCallback"), + FunctionTemplate::New(RunCallback)->GetFunction()); + } + + NODE_MODULE(addon, Init) + +To test it run the following JavaScript snippet: + + var addon = require('./build/Release/addon'); + + addon.runCallback(function(msg){ + console.log(msg); // 'hello world' + }); + + +### Object factory + +You can create and return new objects from within a C++ function with this +`addon.cc` pattern, which returns an object with property `msg` that echoes +the string passed to `createObject()`: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + + using namespace v8; + + Handle<Value> CreateObject(const Arguments& args) { + HandleScope scope; + + Local<Object> obj = Object::New(); + obj->Set(String::NewSymbol("msg"), args[0]->ToString()); + + return scope.Close(obj); + } + + void Init(Handle<Object> target) { + target->Set(String::NewSymbol("createObject"), + FunctionTemplate::New(CreateObject)->GetFunction()); + } + + NODE_MODULE(addon, Init) + +To test it in JavaScript: + + var addon = require('./build/Release/addon'); + + var obj1 = addon.createObject('hello'); + var obj2 = addon.createObject('world'); + console.log(obj1.msg+' '+obj2.msg); // 'hello world' + + +### Function factory + +This pattern illustrates how to create and return a JavaScript function that +wraps a C++ function: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + + using namespace v8; + + Handle<Value> MyFunction(const Arguments& args) { + HandleScope scope; + return scope.Close(String::New("hello world")); + } + + Handle<Value> CreateFunction(const Arguments& args) { + HandleScope scope; + + Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction); + Local<Function> fn = tpl->GetFunction(); + fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous + + return scope.Close(fn); + } + + void Init(Handle<Object> target) { + target->Set(String::NewSymbol("createFunction"), + FunctionTemplate::New(CreateFunction)->GetFunction()); + } + + NODE_MODULE(addon, Init) + + +To test: + + var addon = require('./build/Release/addon'); + + var fn = addon.createFunction(); + console.log(fn()); // 'hello world' + + +### Wrapping C++ objects + +Here we will create a wrapper for a C++ object/class `MyObject` that can be +instantiated in JavaScript through the `new` operator. First prepare the main +module `addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + #include "myobject.h" + + using namespace v8; + + void InitAll(Handle<Object> target) { + MyObject::Init(target); + } + + NODE_MODULE(addon, InitAll) + +Then in `myobject.h` make your wrapper inherit from `node::ObjectWrap`: + + #ifndef MYOBJECT_H + #define MYOBJECT_H + + #include <node.h> + + class MyObject : public node::ObjectWrap { + public: + static void Init(v8::Handle<v8::Object> target); + + private: + MyObject(); + ~MyObject(); + + static v8::Handle<v8::Value> New(const v8::Arguments& args); + static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args); + double counter_; + }; + + #endif + +And in `myobject.cc` implement the various methods that you want to expose. +Here we expose the method `plusOne` by adding it to the constructor's +prototype: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + #include "myobject.h" + + using namespace v8; + + MyObject::MyObject() {}; + MyObject::~MyObject() {}; + + void MyObject::Init(Handle<Object> target) { + // Prepare constructor template + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("MyObject")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + // Prototype + tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"), + FunctionTemplate::New(PlusOne)->GetFunction()); + + Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction()); + target->Set(String::NewSymbol("MyObject"), constructor); + } + + Handle<Value> MyObject::New(const Arguments& args) { + HandleScope scope; + + MyObject* obj = new MyObject(); + obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); + obj->Wrap(args.This()); + + return args.This(); + } + + Handle<Value> MyObject::PlusOne(const Arguments& args) { + HandleScope scope; + + MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This()); + obj->counter_ += 1; + + return scope.Close(Number::New(obj->counter_)); + } + +Test it with: + + var addon = require('./build/Release/addon'); + + var obj = new addon.MyObject(10); + console.log( obj.plusOne() ); // 11 + console.log( obj.plusOne() ); // 12 + console.log( obj.plusOne() ); // 13 + + +### Factory of wrapped objects + +This is useful when you want to be able to create native objects without +explicitly instantiating them with the `new` operator in JavaScript, e.g. + + var obj = addon.createObject(); + // instead of: + // var obj = new addon.Object(); + +Let's register our `createObject` method in `addon.cc`: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + #include "myobject.h" + + using namespace v8; + + Handle<Value> CreateObject(const Arguments& args) { + HandleScope scope; + return scope.Close(MyObject::NewInstance(args)); + } + + void InitAll(Handle<Object> target) { + MyObject::Init(); + + target->Set(String::NewSymbol("createObject"), + FunctionTemplate::New(CreateObject)->GetFunction()); + } + + NODE_MODULE(addon, InitAll) + +In `myobject.h` we now introduce the static method `NewInstance` that takes +care of instantiating the object (i.e. it does the job of `new` in JavaScript): + + #define BUILDING_NODE_EXTENSION + #ifndef MYOBJECT_H + #define MYOBJECT_H + + #include <node.h> + + class MyObject : public node::ObjectWrap { + public: + static void Init(); + static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args); + + private: + MyObject(); + ~MyObject(); + + static v8::Persistent<v8::Function> constructor; + static v8::Handle<v8::Value> New(const v8::Arguments& args); + static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args); + double counter_; + }; + + #endif + +The implementation is similar to the above in `myobject.cc`: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + #include "myobject.h" + + using namespace v8; + + MyObject::MyObject() {}; + MyObject::~MyObject() {}; + + Persistent<Function> MyObject::constructor; + + void MyObject::Init() { + // Prepare constructor template + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("MyObject")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + // Prototype + tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"), + FunctionTemplate::New(PlusOne)->GetFunction()); + + constructor = Persistent<Function>::New(tpl->GetFunction()); + } + + Handle<Value> MyObject::New(const Arguments& args) { + HandleScope scope; + + MyObject* obj = new MyObject(); + obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); + obj->Wrap(args.This()); + + return args.This(); + } + + Handle<Value> MyObject::NewInstance(const Arguments& args) { + HandleScope scope; + + const unsigned argc = 1; + Handle<Value> argv[argc] = { args[0] }; + Local<Object> instance = constructor->NewInstance(argc, argv); + + return scope.Close(instance); + } + + Handle<Value> MyObject::PlusOne(const Arguments& args) { + HandleScope scope; + + MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This()); + obj->counter_ += 1; + + return scope.Close(Number::New(obj->counter_)); + } + +Test it with: + + var addon = require('./build/Release/addon'); + + var obj = addon.createObject(10); + console.log( obj.plusOne() ); // 11 + console.log( obj.plusOne() ); // 12 + console.log( obj.plusOne() ); // 13 + + var obj2 = addon.createObject(20); + console.log( obj2.plusOne() ); // 21 + console.log( obj2.plusOne() ); // 22 + console.log( obj2.plusOne() ); // 23 + + +### Passing wrapped objects around + +In addition to wrapping and returning C++ objects, you can pass them around +by unwrapping them with Node's `node::ObjectWrap::Unwrap` helper function. +In the following `addon.cc` we introduce a function `add()` that can take on two +`MyObject` objects: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + #include "myobject.h" + + using namespace v8; + + Handle<Value> CreateObject(const Arguments& args) { + HandleScope scope; + return scope.Close(MyObject::NewInstance(args)); + } + + Handle<Value> Add(const Arguments& args) { + HandleScope scope; + + MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>( + args[0]->ToObject()); + MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>( + args[1]->ToObject()); + + double sum = obj1->Val() + obj2->Val(); + return scope.Close(Number::New(sum)); + } + + void InitAll(Handle<Object> target) { + MyObject::Init(); + + target->Set(String::NewSymbol("createObject"), + FunctionTemplate::New(CreateObject)->GetFunction()); + + target->Set(String::NewSymbol("add"), + FunctionTemplate::New(Add)->GetFunction()); + } + + NODE_MODULE(addon, InitAll) + +To make things interesting we introduce a public method in `myobject.h` so we +can probe private values after unwrapping the object: + + #define BUILDING_NODE_EXTENSION + #ifndef MYOBJECT_H + #define MYOBJECT_H + + #include <node.h> + + class MyObject : public node::ObjectWrap { + public: + static void Init(); + static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args); + double Val() const { return val_; } + + private: + MyObject(); + ~MyObject(); + + static v8::Persistent<v8::Function> constructor; + static v8::Handle<v8::Value> New(const v8::Arguments& args); + double val_; + }; + + #endif + +The implementation of `myobject.cc` is similar as before: + + #define BUILDING_NODE_EXTENSION + #include <node.h> + #include "myobject.h" + + using namespace v8; + + MyObject::MyObject() {}; + MyObject::~MyObject() {}; + + Persistent<Function> MyObject::constructor; + + void MyObject::Init() { + // Prepare constructor template + Local<FunctionTemplate> tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("MyObject")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + constructor = Persistent<Function>::New(tpl->GetFunction()); + } + + Handle<Value> MyObject::New(const Arguments& args) { + HandleScope scope; + + MyObject* obj = new MyObject(); + obj->val_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); + obj->Wrap(args.This()); + + return args.This(); + } + + Handle<Value> MyObject::NewInstance(const Arguments& args) { + HandleScope scope; + + const unsigned argc = 1; + Handle<Value> argv[argc] = { args[0] }; + Local<Object> instance = constructor->NewInstance(argc, argv); + + return scope.Close(instance); + } + +Test it with: + + var addon = require('./build/Release/addon'); + + var obj1 = addon.createObject(10); + var obj2 = addon.createObject(20); + var result = addon.add(obj1, obj2); + + console.log(result); // 30 diff --git a/doc/api/all.markdown b/doc/api/all.markdown new file mode 100644 index 0000000..c625267 --- /dev/null +++ b/doc/api/all.markdown @@ -0,0 +1,36 @@ +@include documentation +@include synopsis +@include globals +@include stdio +@include timers +@include modules +@include addons +@include process +@include util +@include events +@include domain +@include buffer +@include stream +@include crypto +@include tls +@include string_decoder +@include fs +@include path +@include net +@include dgram +@include dns +@include http +@include https +@include url +@include querystring +@include readline +@include repl +@include vm +@include child_process +@include assert +@include tty +@include zlib +@include os +@include debugger +@include cluster +@include appendix_1 diff --git a/doc/api/appendix_1.markdown b/doc/api/appendix_1.markdown new file mode 100644 index 0000000..c309b0c --- /dev/null +++ b/doc/api/appendix_1.markdown @@ -0,0 +1,44 @@ +# Appendix 1 - Third Party Modules + +There are many third party modules for Node. At the time of writing, August +2010, the master repository of modules is +[the wiki page](https://github.com/joyent/node/wiki/modules). + +This appendix is intended as a SMALL guide to new-comers to help them +quickly find what are considered to be quality modules. It is not intended +to be a complete list. There may be better more complete modules found +elsewhere. + +- Module Installer: [npm](https://github.com/isaacs/npm) + +- HTTP Middleware: [Connect](https://github.com/senchalabs/connect) + +- Web Framework: [Express](https://github.com/visionmedia/express) + +- Web Sockets: [Socket.IO](https://github.com/learnboost/socket.io) + +- HTML Parsing: [HTML5](https://github.com/aredridel/html5) + +- [mDNS/Zeroconf/Bonjour](https://github.com/agnat/node_mdns) + +- [RabbitMQ, AMQP](https://github.com/postwait/node-amqp) + +- [mysql](https://github.com/felixge/node-mysql) + +- Serialization: [msgpack](https://github.com/pgriess/node-msgpack) + +- Scraping: [Apricot](https://github.com/silentrob/Apricot) + +- Debugger: [ndb](https://github.com/smtlaissezfaire/ndb) is a CLI debugger + [inspector](https://github.com/dannycoates/node-inspector) is a web based + tool. + +- [pcap binding](https://github.com/mranney/node_pcap) + +- [ncurses](https://github.com/mscdex/node-ncurses) + +- Testing/TDD/BDD: [vows](http://vowsjs.org/), + [mocha](https://github.com/visionmedia/mocha), + [mjsunit.runner](https://github.com/tmpvar/mjsunit.runner) + +Patches to this list are welcome. diff --git a/doc/api/assert.markdown b/doc/api/assert.markdown new file mode 100644 index 0000000..c93c1e7 --- /dev/null +++ b/doc/api/assert.markdown @@ -0,0 +1,84 @@ +# Assert + + Stability: 5 - Locked + +This module is used for writing unit tests for your applications, you can +access it with `require('assert')`. + +## assert.fail(actual, expected, message, operator) + +Throws an exception that displays the values for `actual` and `expected` separated by the provided operator. + +## assert(value, message), assert.ok(value, [message]) + +Tests if value is truthy, it is equivalent to `assert.equal(true, !!value, message);` + +## assert.equal(actual, expected, [message]) + +Tests shallow, coercive equality with the equal comparison operator ( `==` ). + +## assert.notEqual(actual, expected, [message]) + +Tests shallow, coercive non-equality with the not equal comparison operator ( `!=` ). + +## assert.deepEqual(actual, expected, [message]) + +Tests for deep equality. + +## assert.notDeepEqual(actual, expected, [message]) + +Tests for any deep inequality. + +## assert.strictEqual(actual, expected, [message]) + +Tests strict equality, as determined by the strict equality operator ( `===` ) + +## assert.notStrictEqual(actual, expected, [message]) + +Tests strict non-equality, as determined by the strict not equal operator ( `!==` ) + +## assert.throws(block, [error], [message]) + +Expects `block` to throw an error. `error` can be constructor, regexp or +validation function. + +Validate instanceof using constructor: + + assert.throws( + function() { + throw new Error("Wrong value"); + }, + Error + ); + +Validate error message using RegExp: + + assert.throws( + function() { + throw new Error("Wrong value"); + }, + /value/ + ); + +Custom error validation: + + assert.throws( + function() { + throw new Error("Wrong value"); + }, + function(err) { + if ( (err instanceof Error) && /value/.test(err) ) { + return true; + } + }, + "unexpected error" + ); + +## assert.doesNotThrow(block, [error], [message]) + +Expects `block` not to throw an error, see assert.throws for details. + +## assert.ifError(value) + +Tests if value is not a false value, throws if it is a true value. Useful when +testing the first argument, `error` in callbacks. diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown new file mode 100644 index 0000000..2447869 --- /dev/null +++ b/doc/api/buffer.markdown @@ -0,0 +1,645 @@ +# Buffer + + Stability: 3 - Stable + +Pure Javascript is Unicode friendly but not nice to binary data. When +dealing with TCP streams or the file system, it's necessary to handle octet +streams. Node has several strategies for manipulating, creating, and +consuming octet streams. + +Raw data is stored in instances of the `Buffer` class. A `Buffer` is similar +to an array of integers but corresponds to a raw memory allocation outside +the V8 heap. A `Buffer` cannot be resized. + +The `Buffer` class is a global, making it very rare that one would need +to ever `require('buffer')`. + +Converting between Buffers and JavaScript string objects requires an explicit +encoding method. Here are the different string encodings. + +* `'ascii'` - for 7 bit ASCII data only. This encoding method is very fast, and + will strip the high bit if set. + Note that this encoding converts a null character (`'\0'` or `'\u0000'`) into + `0x20` (character code of a space). If you want to convert a null character + into `0x00`, you should use `'utf8'`. + +* `'utf8'` - Multi byte encoded Unicode characters. Many web pages and other document formats use UTF-8. + +* `'ucs2'` - 2-bytes, little endian encoded Unicode characters. It can encode + only BMP(Basic Multilingual Plane, U+0000 - U+FFFF). + +* `'base64'` - Base64 string encoding. + +* `'binary'` - A way of encoding raw binary data into strings by using only + the first 8 bits of each character. This encoding method is deprecated and + should be avoided in favor of `Buffer` objects where possible. This encoding + will be removed in future versions of Node. + +* `'hex'` - Encode each byte as two hexidecimal characters. + +## Class: Buffer + +The Buffer class is a global type for dealing with binary data directly. +It can be constructed in a variety of ways. + +### new Buffer(size) + +* `size` Number + +Allocates a new buffer of `size` octets. + +### new Buffer(array) + +* `array` Array + +Allocates a new buffer using an `array` of octets. + +### new Buffer(str, [encoding]) + +* `str` String - string to encode. +* `encoding` String - encoding to use, Optional. + +Allocates a new buffer containing the given `str`. +`encoding` defaults to `'utf8'`. + +### buf.write(string, [offset], [length], [encoding]) + +* `string` String - data to be written to buffer +* `offset` Number, Optional, Default: 0 +* `length` Number, Optional +* `encoding` String, Optional, Default: 'utf8' + +Writes `string` to the buffer at `offset` using the given encoding. +`offset` defaults to `0`, `encoding` defaults to `'utf8'`. `length` is +the number of bytes to write. Returns number of octets written. If `buffer` did +not contain enough space to fit the entire string, it will write a partial +amount of the string. `length` defaults to `buffer.length - offset`. +The method will not write partial characters. + + buf = new Buffer(256); + len = buf.write('\u00bd + \u00bc = \u00be', 0); + console.log(len + " bytes: " + buf.toString('utf8', 0, len)); + +The number of characters written (which may be different than the number of +bytes written) is set in `Buffer._charsWritten` and will be overwritten the +next time `buf.write()` is called. + + +### buf.toString([encoding], [start], [end]) + +* `encoding` String, Optional, Default: 'utf8' +* `start` Number, Optional, Default: 0 +* `end` Number, Optional + +Decodes and returns a string from buffer data encoded with `encoding` +(defaults to `'utf8'`) beginning at `start` (defaults to `0`) and ending at +`end` (defaults to `buffer.length`). + +See `buffer.write()` example, above. + + +### buf[index] + +<!--type=property--> +<!--name=[index]--> + +Get and set the octet at `index`. The values refer to individual bytes, +so the legal range is between `0x00` and `0xFF` hex or `0` and `255`. + +Example: copy an ASCII string into a buffer, one byte at a time: + + str = "node.js"; + buf = new Buffer(str.length); + + for (var i = 0; i < str.length ; i++) { + buf[i] = str.charCodeAt(i); + } + + console.log(buf); + + // node.js + +### Class Method: Buffer.isBuffer(obj) + +* `obj` Object +* Return: Boolean + +Tests if `obj` is a `Buffer`. + +### Class Method: Buffer.byteLength(string, [encoding]) + +* `string` String +* `encoding` String, Optional, Default: 'utf8' +* Return: Number + +Gives the actual byte length of a string. `encoding` defaults to `'utf8'`. +This is not the same as `String.prototype.length` since that returns the +number of *characters* in a string. + +Example: + + str = '\u00bd + \u00bc = \u00be'; + + console.log(str + ": " + str.length + " characters, " + + Buffer.byteLength(str, 'utf8') + " bytes"); + + // ½ + ¼ = ¾: 9 characters, 12 bytes + +### buf.length + +* Number + +The size of the buffer in bytes. Note that this is not necessarily the size +of the contents. `length` refers to the amount of memory allocated for the +buffer object. It does not change when the contents of the buffer are changed. + + buf = new Buffer(1234); + + console.log(buf.length); + buf.write("some string", 0, "ascii"); + console.log(buf.length); + + // 1234 + // 1234 + +### buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd]) + +* `targetBuffer` Buffer object - Buffer to copy into +* `targetStart` Number, Optional, Default: 0 +* `sourceStart` Number, Optional, Default: 0 +* `sourceEnd` Number, Optional, Default: 0 + +Does copy between buffers. The source and target regions can be overlapped. +`targetStart` and `sourceStart` default to `0`. +`sourceEnd` defaults to `buffer.length`. + +Example: build two Buffers, then copy `buf1` from byte 16 through byte 19 +into `buf2`, starting at the 8th byte in `buf2`. + + buf1 = new Buffer(26); + buf2 = new Buffer(26); + + for (var i = 0 ; i < 26 ; i++) { + buf1[i] = i + 97; // 97 is ASCII a + buf2[i] = 33; // ASCII ! + } + + buf1.copy(buf2, 8, 16, 20); + console.log(buf2.toString('ascii', 0, 25)); + + // !!!!!!!!qrst!!!!!!!!!!!!! + + +### buf.slice([start], [end]) + +* `start` Number, Optional, Default: 0 +* `end` Number, Optional, Default: 0 + +Returns a new buffer which references the same memory as the old, but offset +and cropped by the `start` (defaults to `0`) and `end` (defaults to +`buffer.length`) indexes. + +**Modifying the new buffer slice will modify memory in the original buffer!** + +Example: build a Buffer with the ASCII alphabet, take a slice, then modify one +byte from the original Buffer. + + var buf1 = new Buffer(26); + + for (var i = 0 ; i < 26 ; i++) { + buf1[i] = i + 97; // 97 is ASCII a + } + + var buf2 = buf1.slice(0, 3); + console.log(buf2.toString('ascii', 0, buf2.length)); + buf1[0] = 33; + console.log(buf2.toString('ascii', 0, buf2.length)); + + // abc + // !bc + +### buf.readUInt8(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads an unsigned 8 bit integer from the buffer at the specified offset. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + + buf[0] = 0x3; + buf[1] = 0x4; + buf[2] = 0x23; + buf[3] = 0x42; + + for (ii = 0; ii < buf.length; ii++) { + console.log(buf.readUInt8(ii)); + } + + // 0x3 + // 0x4 + // 0x23 + // 0x42 + +### buf.readUInt16LE(offset, [noAssert]) +### buf.readUInt16BE(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads an unsigned 16 bit integer from the buffer at the specified offset with +specified endian format. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + + buf[0] = 0x3; + buf[1] = 0x4; + buf[2] = 0x23; + buf[3] = 0x42; + + console.log(buf.readUInt16BE(0)); + console.log(buf.readUInt16LE(0)); + console.log(buf.readUInt16BE(1)); + console.log(buf.readUInt16LE(1)); + console.log(buf.readUInt16BE(2)); + console.log(buf.readUInt16LE(2)); + + // 0x0304 + // 0x0403 + // 0x0423 + // 0x2304 + // 0x2342 + // 0x4223 + +### buf.readUInt32LE(offset, [noAssert]) +### buf.readUInt32BE(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads an unsigned 32 bit integer from the buffer at the specified offset with +specified endian format. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + + buf[0] = 0x3; + buf[1] = 0x4; + buf[2] = 0x23; + buf[3] = 0x42; + + console.log(buf.readUInt32BE(0)); + console.log(buf.readUInt32LE(0)); + + // 0x03042342 + // 0x42230403 + +### buf.readInt8(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads a signed 8 bit integer from the buffer at the specified offset. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Works as `buffer.readUInt8`, except buffer contents are treated as two's +complement signed values. + +### buf.readInt16LE(offset, [noAssert]) +### buf.readInt16BE(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads a signed 16 bit integer from the buffer at the specified offset with +specified endian format. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Works as `buffer.readUInt16*`, except buffer contents are treated as two's +complement signed values. + +### buf.readInt32LE(offset, [noAssert]) +### buf.readInt32BE(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads a signed 32 bit integer from the buffer at the specified offset with +specified endian format. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Works as `buffer.readUInt32*`, except buffer contents are treated as two's +complement signed values. + +### buf.readFloatLE(offset, [noAssert]) +### buf.readFloatBE(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads a 32 bit float from the buffer at the specified offset with specified +endian format. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x80; + buf[3] = 0x3f; + + console.log(buf.readFloatLE(0)); + + // 0x01 + +### buf.readDoubleLE(offset, [noAssert]) +### buf.readDoubleBE(offset, [noAssert]) + +* `offset` Number +* `noAssert` Boolean, Optional, Default: false +* Return: Number + +Reads a 64 bit double from the buffer at the specified offset with specified +endian format. + +Set `noAssert` to true to skip validation of `offset`. This means that `offset` +may be beyond the end of the buffer. Defaults to `false`. + +Example: + + var buf = new Buffer(8); + + buf[0] = 0x55; + buf[1] = 0x55; + buf[2] = 0x55; + buf[3] = 0x55; + buf[4] = 0x55; + buf[5] = 0x55; + buf[6] = 0xd5; + buf[7] = 0x3f; + + console.log(buf.readDoubleLE(0)); + + // 0.3333333333333333 + +### buf.writeUInt8(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset. Note, `value` must be a +valid unsigned 8 bit integer. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + buf.writeUInt8(0x3, 0); + buf.writeUInt8(0x4, 1); + buf.writeUInt8(0x23, 2); + buf.writeUInt8(0x42, 3); + + console.log(buf); + + // <Buffer 03 04 23 42> + +### buf.writeUInt16LE(value, offset, [noAssert]) +### buf.writeUInt16BE(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset with specified endian +format. Note, `value` must be a valid unsigned 16 bit integer. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + buf.writeUInt16BE(0xdead, 0); + buf.writeUInt16BE(0xbeef, 2); + + console.log(buf); + + buf.writeUInt16LE(0xdead, 0); + buf.writeUInt16LE(0xbeef, 2); + + console.log(buf); + + // <Buffer de ad be ef> + // <Buffer ad de ef be> + +### buf.writeUInt32LE(value, offset, [noAssert]) +### buf.writeUInt32BE(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset with specified endian +format. Note, `value` must be a valid unsigned 32 bit integer. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + buf.writeUInt32BE(0xfeedface, 0); + + console.log(buf); + + buf.writeUInt32LE(0xfeedface, 0); + + console.log(buf); + + // <Buffer fe ed fa ce> + // <Buffer ce fa ed fe> + +### buf.writeInt8(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset. Note, `value` must be a +valid signed 8 bit integer. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Works as `buffer.writeUInt8`, except value is written out as a two's complement +signed integer into `buffer`. + +### buf.writeInt16LE(value, offset, [noAssert]) +### buf.writeInt16BE(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset with specified endian +format. Note, `value` must be a valid signed 16 bit integer. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Works as `buffer.writeUInt16*`, except value is written out as a two's +complement signed integer into `buffer`. + +### buf.writeInt32LE(value, offset, [noAssert]) +### buf.writeInt32BE(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset with specified endian +format. Note, `value` must be a valid signed 32 bit integer. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Works as `buffer.writeUInt32*`, except value is written out as a two's +complement signed integer into `buffer`. + +### buf.writeFloatLE(value, offset, [noAssert]) +### buf.writeFloatBE(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset with specified endian +format. Note, `value` must be a valid 32 bit float. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Example: + + var buf = new Buffer(4); + buf.writeFloatBE(0xcafebabe, 0); + + console.log(buf); + + buf.writeFloatLE(0xcafebabe, 0); + + console.log(buf); + + // <Buffer 4f 4a fe bb> + // <Buffer bb fe 4a 4f> + +### buf.writeDoubleLE(value, offset, [noAssert]) +### buf.writeDoubleBE(value, offset, [noAssert]) + +* `value` Number +* `offset` Number +* `noAssert` Boolean, Optional, Default: false + +Writes `value` to the buffer at the specified offset with specified endian +format. Note, `value` must be a valid 64 bit double. + +Set `noAssert` to true to skip validation of `value` and `offset`. This means +that `value` may be too large for the specific function and `offset` may be +beyond the end of the buffer leading to the values being silently dropped. This +should not be used unless you are certain of correctness. Defaults to `false`. + +Example: + + var buf = new Buffer(8); + buf.writeDoubleBE(0xdeadbeefcafebabe, 0); + + console.log(buf); + + buf.writeDoubleLE(0xdeadbeefcafebabe, 0); + + console.log(buf); + + // <Buffer 43 eb d5 b7 dd f9 5f d7> + // <Buffer d7 5f f9 dd b7 d5 eb 43> + +### buf.fill(value, [offset], [end]) + +* `value` +* `offset` Number, Optional +* `end` Number, Optional + +Fills the buffer with the specified value. If the `offset` (defaults to `0`) +and `end` (defaults to `buffer.length`) are not given it will fill the entire +buffer. + + var b = new Buffer(50); + b.fill("h"); + +## buffer.INSPECT_MAX_BYTES + +* Number, Default: 50 + +How many bytes will be returned when `buffer.inspect()` is called. This can +be overridden by user modules. + +Note that this is a property on the buffer module returned by +`require('buffer')`, not on the Buffer global, or a buffer instance. + +## Class: SlowBuffer + +This class is primarily for internal use. JavaScript programs should +use Buffer instead of using SlowBuffer. + +In order to avoid the overhead of allocating many C++ Buffer objects for +small blocks of memory in the lifetime of a server, Node allocates memory +in 8Kb (8192 byte) chunks. If a buffer is smaller than this size, then it +will be backed by a parent SlowBuffer object. If it is larger than this, +then Node will allocate a SlowBuffer slab for it directly. diff --git a/doc/api/child_process.markdown b/doc/api/child_process.markdown new file mode 100644 index 0000000..489b072 --- /dev/null +++ b/doc/api/child_process.markdown @@ -0,0 +1,407 @@ +# Child Process + + Stability: 3 - Stable + +Node provides a tri-directional `popen(3)` facility through the +`child_process` module. + +It is possible to stream data through a child's `stdin`, `stdout`, and +`stderr` in a fully non-blocking way. + +To create a child process use `require('child_process').spawn()` or +`require('child_process').fork()`. The semantics of each are slightly +different, and explained below. + +## Class: ChildProcess + +`ChildProcess` is an `EventEmitter`. + +Child processes always have three streams associated with them. `child.stdin`, +`child.stdout`, and `child.stderr`. These may be shared with the stdio +streams of the parent process, or they may be separate stream objects +which can be piped to and from. + +The ChildProcess class is not intended to be used directly. Use the +`spawn()` or `fork()` methods to create a Child Process instance. + +### Event: 'exit' + +* `code` {Number} the exit code, if it exited normally. +* `signal` {String} the signal passed to kill the child process, if it + was killed by the parent. + +This event is emitted after the child process ends. If the process terminated +normally, `code` is the final exit code of the process, otherwise `null`. If +the process terminated due to receipt of a signal, `signal` is the string name +of the signal, otherwise `null`. + +Note that the child process stdio streams might still be open. + +See `waitpid(2)`. + +### Event: 'close' + +This event is emitted when the stdio streams of a child process have all +terminated. This is distinct from 'exit', since multiple processes +might share the same stdio streams. + +### Event: 'disconnect' + +This event is emitted after using the `.disconnect()` method in the parent or +in the child. After disconnecting it is no longer possible to send messages. +An alternative way to check if you can send messages is to see if the +`child.connected` property is `true`. + +### child.stdin + +* {Stream object} + +A `Writable Stream` that represents the child process's `stdin`. +Closing this stream via `end()` often causes the child process to terminate. + +If the child stdio streams are shared with the parent, then this will +not be set. + +### child.stdout + +* {Stream object} + +A `Readable Stream` that represents the child process's `stdout`. + +If the child stdio streams are shared with the parent, then this will +not be set. + +### child.stderr + +* {Stream object} + +A `Readable Stream` that represents the child process's `stderr`. + +If the child stdio streams are shared with the parent, then this will +not be set. + +### child.pid + +* {Integer} + +The PID of the child process. + +Example: + + var spawn = require('child_process').spawn, + grep = spawn('grep', ['ssh']); + + console.log('Spawned child pid: ' + grep.pid); + grep.stdin.end(); + +### child.kill([signal]) + +* `signal` {String} + +Send a signal to the child process. If no argument is given, the process will +be sent `'SIGTERM'`. See `signal(7)` for a list of available signals. + + var spawn = require('child_process').spawn, + grep = spawn('grep', ['ssh']); + + grep.on('exit', function (code, signal) { + console.log('child process terminated due to receipt of signal '+signal); + }); + + // send SIGHUP to process + grep.kill('SIGHUP'); + +Note that while the function is called `kill`, the signal delivered to the child +process may not actually kill it. `kill` really just sends a signal to a process. + +See `kill(2)` + + +### child.send(message, [sendHandle]) + +* `message` {Object} +* `sendHandle` {Handle object} + +Send a message (and, optionally, a handle object) to a child process. + +See `child_process.fork()` for details. + +## child_process.spawn(command, [args], [options]) + +* `command` {String} The command to run +* `args` {Array} List of string arguments +* `options` {Object} + * `cwd` {String} Current working directory of the child process + * `customFds` {Array} **Deprecated** File descriptors for the child to use + for stdio. (See below) + * `env` {Object} Environment key-value pairs + * `setsid` {Boolean} +* return: {ChildProcess object} + +Launches a new process with the given `command`, with command line arguments in `args`. +If omitted, `args` defaults to an empty Array. + +The third argument is used to specify additional options, which defaults to: + + { cwd: undefined, + env: process.env + } + +`cwd` allows you to specify the working directory from which the process is spawned. +Use `env` to specify environment variables that will be visible to the new process. + +Example of running `ls -lh /usr`, capturing `stdout`, `stderr`, and the exit code: + + var util = require('util'), + spawn = require('child_process').spawn, + ls = spawn('ls', ['-lh', '/usr']); + + ls.stdout.on('data', function (data) { + console.log('stdout: ' + data); + }); + + ls.stderr.on('data', function (data) { + console.log('stderr: ' + data); + }); + + ls.on('exit', function (code) { + console.log('child process exited with code ' + code); + }); + + +Example: A very elaborate way to run 'ps ax | grep ssh' + + var util = require('util'), + spawn = require('child_process').spawn, + ps = spawn('ps', ['ax']), + grep = spawn('grep', ['ssh']); + + ps.stdout.on('data', function (data) { + grep.stdin.write(data); + }); + + ps.stderr.on('data', function (data) { + console.log('ps stderr: ' + data); + }); + + ps.on('exit', function (code) { + if (code !== 0) { + console.log('ps process exited with code ' + code); + } + grep.stdin.end(); + }); + + grep.stdout.on('data', function (data) { + console.log(data); + }); + + grep.stderr.on('data', function (data) { + console.log('grep stderr: ' + data); + }); + + grep.on('exit', function (code) { + if (code !== 0) { + console.log('grep process exited with code ' + code); + } + }); + + +Example of checking for failed exec: + + var spawn = require('child_process').spawn, + child = spawn('bad_command'); + + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function (data) { + if (/^execvp\(\)/.test(data)) { + console.log('Failed to start child process.'); + } + }); + +Note that if spawn receives an empty options object, it will result in +spawning the process with an empty environment rather than using +`process.env`. This due to backwards compatibility issues with a deprecated +API. + +There is a deprecated option called `customFds` which allows one to specify +specific file descriptors for the stdio of the child process. This API was +not portable to all platforms and therefore removed. +With `customFds` it was possible to hook up the new process' `[stdin, stdout, +stderr]` to existing streams; `-1` meant that a new stream should be created. +Use at your own risk. + +There are several internal options. In particular `stdinStream`, +`stdoutStream`, `stderrStream`. They are for INTERNAL USE ONLY. As with all +undocumented APIs in Node, they should not be used. + +See also: `child_process.exec()` and `child_process.fork()` + +## child_process.exec(command, [options], callback) + +* `command` {String} The command to run, with space-separated arguments +* `options` {Object} + * `cwd` {String} Current working directory of the child process + * `customFds` {Array} **Deprecated** File descriptors for the child to use + for stdio. (See below) + * `env` {Object} Environment key-value pairs + * `setsid` {Boolean} + * `encoding` {String} (Default: 'utf8') + * `timeout` {Number} (Default: 0) + * `maxBuffer` {Number} (Default: 200*1024) + * `killSignal` {String} (Default: 'SIGTERM') +* `callback` {Function} called with the output when process terminates + * `code` {Integer} Exit code + * `stdout` {Buffer} + * `stderr` {Buffer} +* Return: ChildProcess object + +Runs a command in a shell and buffers the output. + + var util = require('util'), + exec = require('child_process').exec, + child; + + child = exec('cat *.js bad_file | wc -l', + function (error, stdout, stderr) { + console.log('stdout: ' + stdout); + console.log('stderr: ' + stderr); + if (error !== null) { + console.log('exec error: ' + error); + } + }); + +The callback gets the arguments `(error, stdout, stderr)`. On success, `error` +will be `null`. On error, `error` will be an instance of `Error` and `err.code` +will be the exit code of the child process, and `err.signal` will be set to the +signal that terminated the process. + +There is a second optional argument to specify several options. The +default options are + + { encoding: 'utf8', + timeout: 0, + maxBuffer: 200*1024, + killSignal: 'SIGTERM', + cwd: null, + env: null } + +If `timeout` is greater than 0, then it will kill the child process +if it runs longer than `timeout` milliseconds. The child process is killed with +`killSignal` (default: `'SIGTERM'`). `maxBuffer` specifies the largest +amount of data allowed on stdout or stderr - if this value is exceeded then +the child process is killed. + + +## child_process.execFile(file, args, options, callback) + +* `file` {String} The filename of the program to run +* `args` {Array} List of string arguments +* `options` {Object} + * `cwd` {String} Current working directory of the child process + * `customFds` {Array} **Deprecated** File descriptors for the child to use + for stdio. (See below) + * `env` {Object} Environment key-value pairs + * `setsid` {Boolean} + * `encoding` {String} (Default: 'utf8') + * `timeout` {Number} (Default: 0) + * `maxBuffer` {Number} (Default: 200*1024) + * `killSignal` {String} (Default: 'SIGTERM') +* `callback` {Function} called with the output when process terminates + * `code` {Integer} Exit code + * `stdout` {Buffer} + * `stderr` {Buffer} +* Return: ChildProcess object + +This is similar to `child_process.exec()` except it does not execute a +subshell but rather the specified file directly. This makes it slightly +leaner than `child_process.exec`. It has the same options. + + +## child_process.fork(modulePath, [args], [options]) + +* `modulePath` {String} The module to run in the child +* `args` {Array} List of string arguments +* `options` {Object} + * `cwd` {String} Current working directory of the child process + * `customFds` {Array} **Deprecated** File descriptors for the child to use + for stdio. (See below) + * `env` {Object} Environment key-value pairs + * `setsid` {Boolean} + * `encoding` {String} (Default: 'utf8') + * `timeout` {Number} (Default: 0) +* `callback` {Function} called with the output when process terminates + * `code` {Integer} Exit code + * `stdout` {Buffer} + * `stderr` {Buffer} +* Return: ChildProcess object + +This is a special case of the `spawn()` functionality for spawning Node +processes. In addition to having all the methods in a normal ChildProcess +instance, the returned object has a communication channel built-in. The +channel is written to with `child.send(message, [sendHandle])` and messages +are received by a `'message'` event on the child. + +For example: + + var cp = require('child_process'); + + var n = cp.fork(__dirname + '/sub.js'); + + n.on('message', function(m) { + console.log('PARENT got message:', m); + }); + + n.send({ hello: 'world' }); + +And then the child script, `'sub.js'` might look like this: + + process.on('message', function(m) { + console.log('CHILD got message:', m); + }); + + process.send({ foo: 'bar' }); + +In the child the `process` object will have a `send()` method, and `process` +will emit objects each time it receives a message on its channel. + +There is a special case when sending a `{cmd: 'NODE_foo'}` message. All messages +containing a `NODE_` prefix in its `cmd` property will not be emitted in +the `message` event, since they are internal messages used by node core. +Messages containing the prefix are emitted in the `internalMessage` event, you +should by all means avoid using this feature, it may change without warranty. + +By default the spawned Node process will have the stdout, stderr associated +with the parent's. To change this behavior set the `silent` property in the +`options` object to `true`. + +These child Nodes are still whole new instances of V8. Assume at least 30ms +startup and 10mb memory for each new Node. That is, you cannot create many +thousands of them. + +The `sendHandle` option to `child.send()` is for sending a handle object to +another process. Child will receive the handle as as second argument to the +`message` event. Here is an example of sending a handle: + + var server = require('net').createServer(); + var child = require('child_process').fork(__dirname + '/child.js'); + // Open up the server object and send the handle. + server.listen(1337, function() { + child.send({ server: true }, server._handle); + }); + +Here is an example of receiving the server handle and sharing it between +processes: + + process.on('message', function(m, serverHandle) { + if (serverHandle) { + var server = require('net').createServer(); + server.listen(serverHandle); + } + }); + +To close the IPC connection between parent and child use the +`child.disconnect()` method. This allows the child to exit gracefully since +there is no IPC channel keeping it alive. When calling this method the +`disconnect` event will be emitted in both parent and child, and the +`connected` flag will be set to `false`. Please note that you can also call +`process.disconnect()` in the child process. diff --git a/doc/api/cluster.markdown b/doc/api/cluster.markdown new file mode 100644 index 0000000..dc41a9c --- /dev/null +++ b/doc/api/cluster.markdown @@ -0,0 +1,439 @@ +# Cluster + + Stability: 1 - Experimental + +A single instance of Node runs in a single thread. To take advantage of +multi-core systems the user will sometimes want to launch a cluster of Node +processes to handle the load. + +The cluster module allows you to easily create a network of processes that +all share server ports. + + var cluster = require('cluster'); + var http = require('http'); + var numCPUs = require('os').cpus().length; + + if (cluster.isMaster) { + // Fork workers. + for (var i = 0; i < numCPUs; i++) { + cluster.fork(); + } + + cluster.on('exit', function(worker) { + console.log('worker ' + worker.pid + ' died'); + }); + } else { + // Workers can share any TCP connection + // In this case its a HTTP server + http.createServer(function(req, res) { + res.writeHead(200); + res.end("hello world\n"); + }).listen(8000); + } + +Running node will now share port 8000 between the workers: + + % node server.js + Worker 2438 online + Worker 2437 online + + +This feature was introduced recently, and may change in future versions. +Please try it out and provide feedback. + +## cluster.settings + +* {Object} + * `exec` {String} file path to worker file. (Default=`__filename`) + * `args` {Array} string arguments passed to worker. + (Default=`process.argv.slice(2)`) + * `silent` {Boolean} whether or not to send output to parent's stdio. + (Default=`false`) + +All settings set by the `.setupMaster` is stored in this settings object. +This object is not supposed to be change or set manually, by you. + +## cluster.isMaster + +* {Boolean} + +True if the process is a master. This is determined +by the `process.env.NODE_UNIQUE_ID`. If `process.env.NODE_UNIQUE_ID` is +undefined, then `isMaster` is `true`. + +## cluster.isWorker + +* {Boolean} + +This boolean flag is true if the process is a worker forked from a master. +If the `process.env.NODE_UNIQUE_ID` is set to a value, then +`isWorker` is `true`. + +## Event: 'fork' + +* `worker` {Worker object} + +When a new worker is forked the cluster module will emit a 'fork' event. +This can be used to log worker activity, and create you own timeout. + + var timeouts = []; + var errorMsg = function () { + console.error("Something must be wrong with the connection ..."); + }); + + cluster.on('fork', function (worker) { + timeouts[worker.uniqueID] = setTimeout(errorMsg, 2000); + }); + cluster.on('listening', function (worker) { + clearTimeout(timeouts[worker.uniqueID]); + }); + cluster.on('exit', function (worker) { + clearTimeout(timeouts[worker.uniqueID]); + errorMsg(); + }); + +## Event: 'online' + +* `worker` {Worker object} + +After forking a new worker, the worker should respond with a online message. +When the master receives a online message it will emit such event. +The difference between 'fork' and 'online' is that fork is emitted when the +master tries to fork a worker, and 'online' is emitted when the worker is +being executed. + + cluster.on('online', function (worker) { + console.log("Yay, the worker responded after it was forked"); + }); + +## Event: 'listening' + +* `worker` {Worker object} + +When calling `listen()` from a worker, a 'listening' event is automatically assigned +to the server instance. When the server is listening a message is send to the master +where the 'listening' event is emitted. + + cluster.on('listening', function (worker) { + console.log("We are now connected"); + }); + +## Event: 'disconnect' + +* `worker` {Worker object} + +When a workers IPC channel has disconnected this event is emitted. This will happen +when the worker dies, usually after calling `.destroy()`. + +When calling `.disconnect()`, there may be a delay between the +`disconnect` and `exit` events. This event can be used to detect if +the process is stuck in a cleanup or if there are long-living +connections. + + cluster.on('disconnect', function(worker) { + console.log('The worker #' + worker.uniqueID + ' has disconnected'); + }); + +## Event: 'exit' + +* `worker` {Worker object} + +When any of the workers die the cluster module will emit the 'exit' event. +This can be used to restart the worker by calling `fork()` again. + + cluster.on('exit', function(worker) { + console.log('worker ' + worker.pid + ' died. restart...'); + cluster.fork(); + }); + +## Event: 'setup' + +* `worker` {Worker object} + +When the `.setupMaster()` function has been executed this event emits. +If `.setupMaster()` was not executed before `fork()` this function will +call `.setupMaster()` with no arguments. + +## cluster.setupMaster([settings]) + +* `settings` {Object} + * `exec` {String} file path to worker file. (Default=`__filename`) + * `args` {Array} string arguments passed to worker. + (Default=`process.argv.slice(2)`) + * `silent` {Boolean} whether or not to send output to parent's stdio. + (Default=`false`) + +The `setupMaster` is used to change the default 'fork' behavior. It takes +one option object argument. + +Example: + + var cluster = require("cluster"); + cluster.setupMaster({ + exec : "worker.js", + args : ["--use", "https"], + silent : true + }); + cluster.autoFork(); + +## cluster.fork([env]) + +* `env` {Object} Key/value pairs to add to child process environment. +* return {Worker object} + +Spawn a new worker process. This can only be called from the master process. + +## cluster.settings + +* {Object} + * `exec` {String} file path to worker file. Default: `__filename` + * `args` {Array} string arguments passed to worker. + (Default=`process.argv.slice(2)`) + * `silent` {Boolean} whether or not to send output to parent's stdio. + (Default=`false`) + +All settings set by the `.setupMaster` is stored in this settings object. +This object is not supposed to be change or set manually. + +## cluster.disconnect([callback]) + +* `callback` {Function} called when all workers are disconnected and handlers are closed + +When calling this method, all workers will commit a graceful suicide. When they are +disconnected all internal handlers will be closed, allowing the master process to +die graceful if no other event is waiting. + +The method takes an optional callback argument which will be called when finished. + +## cluster.workers + +* {Object} + +In the cluster all living worker objects are stored in this object by there +`uniqueID` as the key. This makes it easy to loop through all living workers. + + // Go through all workers + function eachWorker(callback) { + for (var uniqueID in cluster.workers) { + callback(cluster.workers[uniqueID]); + } + } + eachWorker(function (worker) { + worker.send('big announcement to all workers'); + }); + +Should you wish to reference a worker over a communication channel, using +the worker's uniqueID is the easiest way to find the worker. + + socket.on('data', function (uniqueID) { + var worker = cluster.workers[uniqueID]; + }); + +## Class: Worker + +A Worker object contains all public information and method about a worker. +In the master it can be obtained using `cluster.workers`. In a worker +it can be obtained using `cluster.worker`. + +### worker.uniqueID + +* {String} + +Each new worker is given its own unique id, this id is stored in the +`uniqueID`. + +While a worker is alive, this is the key that indexes it in +cluster.workers + +### worker.process + +* {ChildProcess object} + +All workers are created using `child_process.fork()`, the returned object +from this function is stored in process. + +See: [Child Process module](child_process.html) + +### worker.suicide + +* {Boolean} + +This property is a boolean. It is set when a worker dies after calling `.destroy()` +or immediately after calling the `.disconnect()` method. Until then it is `undefined`. + +### worker.send(message, [sendHandle]) + +* `message` {Object} +* `sendHandle` {Handle object} + +This function is equal to the send methods provided by +`child_process.fork()`. In the master you should use this function to +send a message to a specific worker. However in a worker you can also use +`process.send(message)`, since this is the same function. + +This example will echo back all messages from the master: + + if (cluster.isMaster) { + var worker = cluster.fork(); + worker.send('hi there'); + + } else if (cluster.isWorker) { + process.on('message', function (msg) { + process.send(msg); + }); + } + +### worker.destroy() + +This function will kill the worker, and inform the master to not spawn a +new worker. The boolean `suicide` lets you distinguish between voluntary +and accidental exit. + + cluster.on('exit', function (worker) { + if (worker.suicide === true) { + console.log('Oh, it was just suicide\' – no need to worry'). + } + }); + + // destroy worker + worker.destroy(); + + +### worker.disconnect() + +When calling this function the worker will no longer accept new connections, but +they will be handled by any other listening worker. Existing connection will be +allowed to exit as usual. When no more connections exist, the IPC channel to the worker +will close allowing it to die graceful. When the IPC channel is closed the `disconnect` +event will emit, this is then followed by the `exit` event, there is emitted when +the worker finally die. + +Because there might be long living connections, it is useful to implement a timeout. +This example ask the worker to disconnect and after 2 seconds it will destroy the +server. An alternative wound be to execute `worker.destroy()` after 2 seconds, but +that would normally not allow the worker to do any cleanup if needed. + + if (cluster.isMaster) { + var worker = cluser.fork(); + var timeout; + + worker.on('listening', function () { + worker.disconnect(); + timeout = setTimeout(function () { + worker.send('force kill'); + }, 2000); + }); + + worker.on('disconnect', function () { + clearTimeout(timeout); + }); + + } else if (cluster.isWorker) { + var net = require('net'); + var server = net.createServer(function (socket) { + // connection never end + }); + + server.listen(8000); + + server.on('close', function () { + // cleanup + }); + + process.on('message', function (msg) { + if (msg === 'force kill') { + server.destroy(); + } + }); + } + +### Event: 'message' + +* `message` {Object} + +This event is the same as the one provided by `child_process.fork()`. +In the master you should use this event, however in a worker you can also use +`process.on('message')` + +As an example, here is a cluster that keeps count of the number of requests +in the master process using the message system: + + var cluster = require('cluster'); + var http = require('http'); + + if (cluster.isMaster) { + + // Keep track of http requests + var numReqs = 0; + setInterval(function() { + console.log("numReqs =", numReqs); + }, 1000); + + // Count requestes + var messageHandler = function (msg) { + if (msg.cmd && msg.cmd == 'notifyRequest') { + numReqs += 1; + } + }; + + // Start workers and listen for messages containing notifyRequest + cluster.autoFork(); + Object.keys(cluster.workers).forEach(function (uniqueID) { + cluster.workers[uniqueID].on('message', messageHandler); + }); + + } else { + + // Worker processes have a http server. + http.Server(function(req, res) { + res.writeHead(200); + res.end("hello world\n"); + + // notify master about the request + process.send({ cmd: 'notifyRequest' }); + }).listen(8000); + } + +### Event: 'online' + +* `worker` {Worker object} + +Same as the `cluster.on('online')` event, but emits only when the state change +on the specified worker. + + cluster.fork().on('online', function (worker) { + // Worker is online + }; + +### Event: 'listening' + +* `worker` {Worker object} + +Same as the `cluster.on('listening')` event, but emits only when the state change +on the specified worker. + + cluster.fork().on('listening', function (worker) { + // Worker is listening + }; + +### Event: 'disconnect' + +* `worker` {Worker object} + +Same as the `cluster.on('disconnect')` event, but emits only when the state change +on the specified worker. + + cluster.fork().on('disconnect', function (worker) { + // Worker has disconnected + }; + +### Event: 'exit' + +* `worker` {Worker object} + +Same as the `cluster.on('exit')` event, but emits only when the state change +on the specified worker. + + cluster.fork().on('exit', function (worker) { + // Worker has died + }; diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown new file mode 100644 index 0000000..610154c --- /dev/null +++ b/doc/api/crypto.markdown @@ -0,0 +1,363 @@ +# Crypto + + Stability: 3 - Stable + +Use `require('crypto')` to access this module. + +The crypto module requires OpenSSL to be available on the underlying platform. +It offers a way of encapsulating secure credentials to be used as part +of a secure HTTPS net or http connection. + +It also offers a set of wrappers for OpenSSL's hash, hmac, cipher, decipher, sign and verify methods. + +## crypto.createCredentials(details) + +Creates a credentials object, with the optional details being a dictionary with keys: + +* `key` : A string holding the PEM encoded private key +* `passphrase` : A string of passphrase for the private key +* `cert` : A string holding the PEM encoded certificate +* `ca` : Either a string or list of strings of PEM encoded CA certificates to trust. +* `crl` : Either a string or list of strings of PEM encoded CRLs (Certificate Revocation List) +* `ciphers`: A string describing the ciphers to use or exclude. Consult + <http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT> for details + on the format. + +If no 'ca' details are given, then node.js will use the default publicly trusted list of CAs as given in +<http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt>. + + +## crypto.createHash(algorithm) + +Creates and returns a hash object, a cryptographic hash with the given algorithm +which can be used to generate hash digests. + +`algorithm` is dependent on the available algorithms supported by the version +of OpenSSL on the platform. Examples are `'sha1'`, `'md5'`, `'sha256'`, `'sha512'`, etc. +On recent releases, `openssl list-message-digest-algorithms` will display the available digest algorithms. + +Example: this program that takes the sha1 sum of a file + + var filename = process.argv[2]; + var crypto = require('crypto'); + var fs = require('fs'); + + var shasum = crypto.createHash('sha1'); + + var s = fs.ReadStream(filename); + s.on('data', function(d) { + shasum.update(d); + }); + + s.on('end', function() { + var d = shasum.digest('hex'); + console.log(d + ' ' + filename); + }); + +## Class: Hash + +The class for creating hash digests of data. + +Returned by `crypto.createHash`. + +### hash.update(data, [input_encoding]) + +Updates the hash content with the given `data`, the encoding of which is given +in `input_encoding` and can be `'utf8'`, `'ascii'` or `'binary'`. +Defaults to `'binary'`. +This can be called many times with new data as it is streamed. + +### hash.digest([encoding]) + +Calculates the digest of all of the passed data to be hashed. +The `encoding` can be `'hex'`, `'binary'` or `'base64'`. +Defaults to `'binary'`. + +Note: `hash` object can not be used after `digest()` method been called. + + +## crypto.createHmac(algorithm, key) + +Creates and returns a hmac object, a cryptographic hmac with the given algorithm and key. + +`algorithm` is dependent on the available algorithms supported by OpenSSL - see createHash above. +`key` is the hmac key to be used. + +## Class: Hmac + +Class for creating cryptographic hmac content. + +Returned by `crypto.createHmac`. + +### hmac.update(data) + +Update the hmac content with the given `data`. +This can be called many times with new data as it is streamed. + +### hmac.digest([encoding]) + +Calculates the digest of all of the passed data to the hmac. +The `encoding` can be `'hex'`, `'binary'` or `'base64'`. +Defaults to `'binary'`. + +Note: `hmac` object can not be used after `digest()` method been called. + + +## crypto.createCipher(algorithm, password) + +Creates and returns a cipher object, with the given algorithm and password. + +`algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. +On recent releases, `openssl list-cipher-algorithms` will display the +available cipher algorithms. +`password` is used to derive key and IV, which must be `'binary'` encoded +string (See the [Buffer section](buffer.html) for more information). + +## crypto.createCipheriv(algorithm, key, iv) + +Creates and returns a cipher object, with the given algorithm, key and iv. + +`algorithm` is the same as the `createCipher()`. `key` is a raw key used in +algorithm. `iv` is an Initialization vector. `key` and `iv` must be `'binary'` +encoded string (See the [Buffer section](buffer.html) for more information). + +## Class: Cipher + +Class for encrypting data. + +Returned by `crypto.createCipher` and `crypto.createCipheriv`. + +### cipher.update(data, [input_encoding], [output_encoding]) + +Updates the cipher with `data`, the encoding of which is given in +`input_encoding` and can be `'utf8'`, `'ascii'` or `'binary'`. +Defaults to `'binary'`. + +The `output_encoding` specifies the output format of the enciphered data, +and can be `'binary'`, `'base64'` or `'hex'`. Defaults to `'binary'`. + +Returns the enciphered contents, and can be called many times with new data as it is streamed. + +### cipher.final([output_encoding]) + +Returns any remaining enciphered contents, with `output_encoding` being one of: +`'binary'`, `'base64'` or `'hex'`. Defaults to `'binary'`. + +Note: `cipher` object can not be used after `final()` method been called. + +### cipher.setAutoPadding(auto_padding=true) + +You can disable automatic padding of the input data to block size. If `auto_padding` is false, +the length of the entire input data must be a multiple of the cipher's block size or `final` will fail. +Useful for non-standard padding, e.g. using `0x0` instead of PKCS padding. You must call this before `cipher.final`. + + +## crypto.createDecipher(algorithm, password) + +Creates and returns a decipher object, with the given algorithm and key. +This is the mirror of the [createCipher()](#crypto.createCipher) above. + +## crypto.createDecipheriv(algorithm, key, iv) + +Creates and returns a decipher object, with the given algorithm, key and iv. +This is the mirror of the [createCipheriv()](#crypto.createCipheriv) above. + +## Class: Decipher + +Class for decrypting data. + +Returned by `crypto.createDecipher` and `crypto.createDecipheriv`. + +### decipher.update(data, [input_encoding], [output_encoding]) + +Updates the decipher with `data`, which is encoded in `'binary'`, `'base64'` +or `'hex'`. Defaults to `'binary'`. + +The `output_decoding` specifies in what format to return the deciphered +plaintext: `'binary'`, `'ascii'` or `'utf8'`. Defaults to `'binary'`. + +### decipher.final([output_encoding]) + +Returns any remaining plaintext which is deciphered, +with `output_encoding` being one of: `'binary'`, `'ascii'` or `'utf8'`. +Defaults to `'binary'`. + +Note: `decipher` object can not be used after `final()` method been called. + +### decipher.setAutoPadding(auto_padding=true) + +You can disable auto padding if the data has been encrypted without standard block padding to prevent +`decipher.final` from checking and removing it. Can only work if the input data's length is a multiple of the +ciphers block size. You must call this before streaming data to `decipher.update`. + +## crypto.createSign(algorithm) + +Creates and returns a signing object, with the given algorithm. +On recent OpenSSL releases, `openssl list-public-key-algorithms` will display +the available signing algorithms. Examples are `'RSA-SHA256'`. + +## Class: Signer + +Class for generating signatures. + +Returned by `crypto.createSign`. + +### signer.update(data) + +Updates the signer object with data. +This can be called many times with new data as it is streamed. + +### signer.sign(private_key, [output_format]) + +Calculates the signature on all the updated data passed through the signer. +`private_key` is a string containing the PEM encoded private key for signing. + +Returns the signature in `output_format` which can be `'binary'`, `'hex'` or +`'base64'`. Defaults to `'binary'`. + +Note: `signer` object can not be used after `sign()` method been called. + +## crypto.createVerify(algorithm) + +Creates and returns a verification object, with the given algorithm. +This is the mirror of the signing object above. + +## Class: Verify + +Class for verifying signatures. + +Returned by `crypto.createVerify`. + +### verifier.update(data) + +Updates the verifier object with data. +This can be called many times with new data as it is streamed. + +### verifier.verify(object, signature, [signature_format]) + +Verifies the signed data by using the `object` and `signature`. `object` is a +string containing a PEM encoded object, which can be one of RSA public key, +DSA public key, or X.509 certificate. `signature` is the previously calculated +signature for the data, in the `signature_format` which can be `'binary'`, +`'hex'` or `'base64'`. Defaults to `'binary'`. + +Returns true or false depending on the validity of the signature for the data and public key. + +Note: `verifier` object can not be used after `verify()` method been called. + +## crypto.createDiffieHellman(prime_length) + +Creates a Diffie-Hellman key exchange object and generates a prime of the +given bit length. The generator used is `2`. + +## crypto.createDiffieHellman(prime, [encoding]) + +Creates a Diffie-Hellman key exchange object using the supplied prime. The +generator used is `2`. Encoding can be `'binary'`, `'hex'`, or `'base64'`. +Defaults to `'binary'`. + +## Class: DiffieHellman + +The class for creating Diffie-Hellman key exchanges. + +Returned by `crypto.createDiffieHellman`. + +### diffieHellman.generateKeys([encoding]) + +Generates private and public Diffie-Hellman key values, and returns the +public key in the specified encoding. This key should be transferred to the +other party. Encoding can be `'binary'`, `'hex'`, or `'base64'`. +Defaults to `'binary'`. + +### diffieHellman.computeSecret(other_public_key, [input_encoding], [output_encoding]) + +Computes the shared secret using `other_public_key` as the other party's +public key and returns the computed shared secret. Supplied key is +interpreted using specified `input_encoding`, and secret is encoded using +specified `output_encoding`. Encodings can be `'binary'`, `'hex'`, or +`'base64'`. The input encoding defaults to `'binary'`. +If no output encoding is given, the input encoding is used as output encoding. + +### diffieHellman.getPrime([encoding]) + +Returns the Diffie-Hellman prime in the specified encoding, which can be +`'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`. + +### diffieHellman.getGenerator([encoding]) + +Returns the Diffie-Hellman prime in the specified encoding, which can be +`'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`. + +### diffieHellman.getPublicKey([encoding]) + +Returns the Diffie-Hellman public key in the specified encoding, which can +be `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`. + +### diffieHellman.getPrivateKey([encoding]) + +Returns the Diffie-Hellman private key in the specified encoding, which can +be `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`. + +### diffieHellman.setPublicKey(public_key, [encoding]) + +Sets the Diffie-Hellman public key. Key encoding can be `'binary'`, `'hex'`, +or `'base64'`. Defaults to `'binary'`. + +### diffieHellman.setPrivateKey(public_key, [encoding]) + +Sets the Diffie-Hellman private key. Key encoding can be `'binary'`, `'hex'`, +or `'base64'`. Defaults to `'binary'`. + +## crypto.getDiffieHellman(group_name) + +Creates a predefined Diffie-Hellman key exchange object. +The supported groups are: `'modp1'`, `'modp2'`, `'modp5'` +(defined in [RFC 2412](http://www.rfc-editor.org/rfc/rfc2412.txt )) +and `'modp14'`, `'modp15'`, `'modp16'`, `'modp17'`, `'modp18'` +(defined in [RFC 3526](http://www.rfc-editor.org/rfc/rfc3526.txt )). +The returned object mimics the interface of objects created by +[crypto.createDiffieHellman()](#crypto.createDiffieHellman) above, but +will not allow to change the keys (with +[diffieHellman.setPublicKey()](#diffieHellman.setPublicKey) for example). +The advantage of using this routine is that the parties don't have to +generate nor exchange group modulus beforehand, saving both processor and +communication time. + +Example (obtaining a shared secret): + + var crypto = require('crypto'); + var alice = crypto.getDiffieHellman('modp5'); + var bob = crypto.getDiffieHellman('modp5'); + + alice.generateKeys(); + bob.generateKeys(); + + var alice_secret = alice.computeSecret(bob.getPublicKey(), 'binary', 'hex'); + var bob_secret = bob.computeSecret(alice.getPublicKey(), 'binary', 'hex'); + + /* alice_secret and bob_secret should be the same */ + console.log(alice_secret == bob_secret); + +## crypto.pbkdf2(password, salt, iterations, keylen, callback) + +Asynchronous PBKDF2 applies pseudorandom function HMAC-SHA1 to derive +a key of given length from the given password, salt and iterations. +The callback gets two arguments `(err, derivedKey)`. + +## crypto.randomBytes(size, [callback]) + +Generates cryptographically strong pseudo-random data. Usage: + + // async + crypto.randomBytes(256, function(ex, buf) { + if (ex) throw ex; + console.log('Have %d bytes of random data: %s', buf.length, buf); + }); + + // sync + try { + var buf = crypto.randomBytes(256); + console.log('Have %d bytes of random data: %s', buf.length, buf); + } catch (ex) { + // handle error + } diff --git a/doc/api/debugger.markdown b/doc/api/debugger.markdown new file mode 100644 index 0000000..ed37f0c --- /dev/null +++ b/doc/api/debugger.markdown @@ -0,0 +1,138 @@ +# Debugger + + Stability: 3 - Stable + +<!-- type=misc --> + +V8 comes with an extensive debugger which is accessible out-of-process via a +simple [TCP protocol](http://code.google.com/p/v8/wiki/DebuggerProtocol). +Node has a built-in client for this debugger. To use this, start Node with the +`debug` argument; a prompt will appear: + + % node debug myscript.js + < debugger listening on port 5858 + connecting... ok + break in /home/indutny/Code/git/indutny/myscript.js:1 + 1 x = 5; + 2 setTimeout(function () { + 3 debugger; + debug> + +Node's debugger client doesn't support the full range of commands, but +simple step and inspection is possible. By putting the statement `debugger;` +into the source code of your script, you will enable a breakpoint. + +For example, suppose `myscript.js` looked like this: + + // myscript.js + x = 5; + setTimeout(function () { + debugger; + console.log("world"); + }, 1000); + console.log("hello"); + +Then once the debugger is run, it will break on line 4. + + % node debug myscript.js + < debugger listening on port 5858 + connecting... ok + break in /home/indutny/Code/git/indutny/myscript.js:1 + 1 x = 5; + 2 setTimeout(function () { + 3 debugger; + debug> cont + < hello + break in /home/indutny/Code/git/indutny/myscript.js:3 + 1 x = 5; + 2 setTimeout(function () { + 3 debugger; + 4 console.log("world"); + 5 }, 1000); + debug> next + break in /home/indutny/Code/git/indutny/myscript.js:4 + 2 setTimeout(function () { + 3 debugger; + 4 console.log("world"); + 5 }, 1000); + 6 console.log("hello"); + debug> repl + Press Ctrl + C to leave debug repl + > x + 5 + > 2+2 + 4 + debug> next + < world + break in /home/indutny/Code/git/indutny/myscript.js:5 + 3 debugger; + 4 console.log("world"); + 5 }, 1000); + 6 console.log("hello"); + 7 + debug> quit + % + + +The `repl` command allows you to evaluate code remotely. The `next` command +steps over to the next line. There are a few other commands available and more +to come. Type `help` to see others. + +## Watchers + +You can watch expression and variable values while debugging your code. +On every breakpoint each expression from the watchers list will be evaluated +in the current context and displayed just before the breakpoint's source code +listing. + +To start watching an expression, type `watch("my_expression")`. `watchers` +prints the active watchers. To remove a watcher, type +`unwatch("my_expression")`. + +## Commands reference + +### Stepping + +* `cont`, `c` - Continue execution +* `next`, `n` - Step next +* `step`, `s` - Step in +* `out`, `o` - Step out +* `pause` - Pause running code (like pause button in Developer TOols) + +### Breakpoints + +* `setBreakpoint()`, `sb()` - Set breakpoint on current line +* `setBreakpoint(line)`, `sb(line)` - Set breakpoint on specific line +* `setBreakpoint('fn()')`, `sb(...)` - Set breakpoint on a first statement in +functions body +* `setBreakpoint('script.js', 1)`, `sb(...)` - Set breakpoint on first line of +script.js +* `clearBreakpoint`, `cb(...)` - Clear breakpoint + +### Info + +* `backtrace`, `bt` - Print backtrace of current execution frame +* `list(5)` - List scripts source code with 5 line context (5 lines before and +after) +* `watch(expr)` - Add expression to watch list +* `unwatch(expr)` - Remove expression from watch list +* `watchers` - List all watchers and their values (automatically listed on each +breakpoint) +* `repl` - Open debugger's repl for evaluation in debugging script's context + +### Execution control + +* `run` - Run script (automatically runs on debugger's start) +* `restart` - Restart script +* `kill` - Kill script + +### Various + +* `scripts` - List all loaded scripts +* `version` - Display v8's version + +## Advanced Usage + +The V8 debugger can be enabled and accessed either by starting Node with +the `--debug` command-line flag or by signaling an existing Node process +with `SIGUSR1`. diff --git a/doc/api/dgram.markdown b/doc/api/dgram.markdown new file mode 100644 index 0000000..6a804f7 --- /dev/null +++ b/doc/api/dgram.markdown @@ -0,0 +1,209 @@ +# UDP / Datagram Sockets + + Stability: 3 - Stable + +<!-- name=dgram --> + +Datagram sockets are available through `require('dgram')`. + +## dgram.createSocket(type, [callback]) + +* `type` String. Either 'udp4' or 'udp6' +* `callback` Function. Attached as a listener to `message` events. + Optional +* Returns: Socket object + +Creates a datagram Socket of the specified types. Valid types are `udp4` +and `udp6`. + +Takes an optional callback which is added as a listener for `message` events. + +Call `socket.bind` if you want to receive datagrams. `socket.bind()` will bind +to the "all interfaces" address on a random port (it does the right thing for +both `udp4` and `udp6` sockets). You can then retrieve the address and port +with `socket.address().address` and `socket.address().port`. + +## Class: Socket + +The dgram Socket class encapsulates the datagram functionality. It +should be created via `dgram.createSocket(type, [callback])`. + +### Event: 'message' + +* `msg` Buffer object. The message +* `rinfo` Object. Remote address information + +Emitted when a new datagram is available on a socket. `msg` is a `Buffer` and `rinfo` is +an object with the sender's address information and the number of bytes in the datagram. + +### Event: 'listening' + +Emitted when a socket starts listening for datagrams. This happens as soon as UDP sockets +are created. + +### Event: 'close' + +Emitted when a socket is closed with `close()`. No new `message` events will be emitted +on this socket. + +### Event: 'error' + +* `exception` Error object + +Emitted when an error occurs. + +### dgram.send(buf, offset, length, port, address, [callback]) + +* `buf` Buffer object. Message to be sent +* `offset` Integer. Offset in the buffer where the message starts. +* `length` Integer. Number of bytes in the message. +* `port` Integer. destination port +* `address` String. destination IP +* `callback` Function. Callback when message is done being delivered. + Optional. + +For UDP sockets, the destination port and IP address must be specified. A string +may be supplied for the `address` parameter, and it will be resolved with DNS. An +optional callback may be specified to detect any DNS errors and when `buf` may be +re-used. Note that DNS lookups will delay the time that a send takes place, at +least until the next tick. The only way to know for sure that a send has taken place +is to use the callback. + +If the socket has not been previously bound with a call to `bind`, it's +assigned a random port number and bound to the "all interfaces" address +(0.0.0.0 for `udp4` sockets, ::0 for `udp6` sockets). + +Example of sending a UDP packet to a random port on `localhost`; + + var dgram = require('dgram'); + var message = new Buffer("Some bytes"); + var client = dgram.createSocket("udp4"); + client.send(message, 0, message.length, 41234, "localhost", function(err, bytes) { + client.close(); + }); + +**A Note about UDP datagram size** + +The maximum size of an `IPv4/v6` datagram depends on the `MTU` (_Maximum Transmission Unit_) +and on the `Payload Length` field size. + +- The `Payload Length` field is `16 bits` wide, which means that a normal payload + cannot be larger than 64K octets including internet header and data + (65,507 bytes = 65,535 − 8 bytes UDP header − 20 bytes IP header); + this is generally true for loopback interfaces, but such long datagrams + are impractical for most hosts and networks. + +- The `MTU` is the largest size a given link layer technology can support for datagrams. + For any link, `IPv4` mandates a minimum `MTU` of `68` octets, while the recommended `MTU` + for IPv4 is `576` (typically recommended as the `MTU` for dial-up type applications), + whether they arrive whole or in fragments. + + For `IPv6`, the minimum `MTU` is `1280` octets, however, the mandatory minimum + fragment reassembly buffer size is `1500` octets. + The value of `68` octets is very small, since most current link layer technologies have + a minimum `MTU` of `1500` (like Ethernet). + +Note that it's impossible to know in advance the MTU of each link through which +a packet might travel, and that generally sending a datagram greater than +the (receiver) `MTU` won't work (the packet gets silently dropped, without +informing the source that the data did not reach its intended recipient). + +### dgram.bind(port, [address]) + +* `port` Integer +* `address` String, Optional + +For UDP sockets, listen for datagrams on a named `port` and optional `address`. If +`address` is not specified, the OS will try to listen on all addresses. + +Example of a UDP server listening on port 41234: + + var dgram = require("dgram"); + + var server = dgram.createSocket("udp4"); + + server.on("message", function (msg, rinfo) { + console.log("server got: " + msg + " from " + + rinfo.address + ":" + rinfo.port); + }); + + server.on("listening", function () { + var address = server.address(); + console.log("server listening " + + address.address + ":" + address.port); + }); + + server.bind(41234); + // server listening 0.0.0.0:41234 + + +### dgram.close() + +Close the underlying socket and stop listening for data on it. + +### dgram.address() + +Returns an object containing the address information for a socket. For UDP sockets, +this object will contain `address` and `port`. + +### dgram.setBroadcast(flag) + +* `flag` Boolean + +Sets or clears the `SO_BROADCAST` socket option. When this option is set, UDP packets +may be sent to a local interface's broadcast address. + +### dgram.setTTL(ttl) + +* `ttl` Integer + +Sets the `IP_TTL` socket option. TTL stands for "Time to Live," but in this context it +specifies the number of IP hops that a packet is allowed to go through. Each router or +gateway that forwards a packet decrements the TTL. If the TTL is decremented to 0 by a +router, it will not be forwarded. Changing TTL values is typically done for network +probes or when multicasting. + +The argument to `setTTL()` is a number of hops between 1 and 255. The default on most +systems is 64. + +### dgram.setMulticastTTL(ttl) + +* `ttl` Integer + +Sets the `IP_MULTICAST_TTL` socket option. TTL stands for "Time to Live," but in this +context it specifies the number of IP hops that a packet is allowed to go through, +specifically for multicast traffic. Each router or gateway that forwards a packet +decrements the TTL. If the TTL is decremented to 0 by a router, it will not be forwarded. + +The argument to `setMulticastTTL()` is a number of hops between 0 and 255. The default on most +systems is 1. + +### dgram.setMulticastLoopback(flag) + +* `flag` Boolean + +Sets or clears the `IP_MULTICAST_LOOP` socket option. When this option is set, multicast +packets will also be received on the local interface. + +### dgram.addMembership(multicastAddress, [multicastInterface]) + +* `multicastAddress` String +* `multicastInterface` String, Optional + +Tells the kernel to join a multicast group with `IP_ADD_MEMBERSHIP` socket option. + +If `multicastInterface` is not specified, the OS will try to add membership to all valid +interfaces. + +### dgram.dropMembership(multicastAddress, [multicastInterface]) + +* `multicastAddress` String +* `multicastInterface` String, Optional + +Opposite of `addMembership` - tells the kernel to leave a multicast group with +`IP_DROP_MEMBERSHIP` socket option. This is automatically called by the kernel +when the socket is closed or process terminates, so most apps will never need to call +this. + +If `multicastInterface` is not specified, the OS will try to drop membership to all valid +interfaces. diff --git a/doc/api/dns.markdown b/doc/api/dns.markdown new file mode 100644 index 0000000..f6cec4c --- /dev/null +++ b/doc/api/dns.markdown @@ -0,0 +1,148 @@ +# DNS + + Stability: 3 - Stable + +Use `require('dns')` to access this module. All methods in the dns module +use C-Ares except for `dns.lookup` which uses `getaddrinfo(3)` in a thread +pool. C-Ares is much faster than `getaddrinfo` but the system resolver is +more constant with how other programs operate. When a user does +`net.connect(80, 'google.com')` or `http.get({ host: 'google.com' })` the +`dns.lookup` method is used. Users who need to do a large number of look ups +quickly should use the methods that go through C-Ares. + +Here is an example which resolves `'www.google.com'` then reverse +resolves the IP addresses which are returned. + + var dns = require('dns'); + + dns.resolve4('www.google.com', function (err, addresses) { + if (err) throw err; + + console.log('addresses: ' + JSON.stringify(addresses)); + + addresses.forEach(function (a) { + dns.reverse(a, function (err, domains) { + if (err) { + throw err; + } + + console.log('reverse for ' + a + ': ' + JSON.stringify(domains)); + }); + }); + }); + +## dns.lookup(domain, [family], callback) + +Resolves a domain (e.g. `'google.com'`) into the first found A (IPv4) or +AAAA (IPv6) record. +The `family` can be the integer `4` or `6`. Defaults to `null` that indicates +both Ip v4 and v6 address family. + +The callback has arguments `(err, address, family)`. The `address` argument +is a string representation of a IP v4 or v6 address. The `family` argument +is either the integer 4 or 6 and denotes the family of `address` (not +necessarily the value initially passed to `lookup`). + +On error, `err` is an `Error` object, where `err.code` is the error code. +Keep in mind that `err.code` will be set to `'ENOENT'` not only when +the domain does not exist but also when the lookup fails in other ways +such as no available file descriptors. + + +## dns.resolve(domain, [rrtype], callback) + +Resolves a domain (e.g. `'google.com'`) into an array of the record types +specified by rrtype. Valid rrtypes are `'A'` (IPV4 addresses, default), +`'AAAA'` (IPV6 addresses), `'MX'` (mail exchange records), `'TXT'` (text +records), `'SRV'` (SRV records), `'PTR'` (used for reverse IP lookups), +`'NS'` (name server records) and `'CNAME'` (canonical name records). + +The callback has arguments `(err, addresses)`. The type of each item +in `addresses` is determined by the record type, and described in the +documentation for the corresponding lookup methods below. + +On error, `err` is an `Error` object, where `err.code` is +one of the error codes listed below. + + +## dns.resolve4(domain, callback) + +The same as `dns.resolve()`, but only for IPv4 queries (`A` records). +`addresses` is an array of IPv4 addresses (e.g. +`['74.125.79.104', '74.125.79.105', '74.125.79.106']`). + +## dns.resolve6(domain, callback) + +The same as `dns.resolve4()` except for IPv6 queries (an `AAAA` query). + + +## dns.resolveMx(domain, callback) + +The same as `dns.resolve()`, but only for mail exchange queries (`MX` records). + +`addresses` is an array of MX records, each with a priority and an exchange +attribute (e.g. `[{'priority': 10, 'exchange': 'mx.example.com'},...]`). + +## dns.resolveTxt(domain, callback) + +The same as `dns.resolve()`, but only for text queries (`TXT` records). +`addresses` is an array of the text records available for `domain` (e.g., +`['v=spf1 ip4:0.0.0.0 ~all']`). + +## dns.resolveSrv(domain, callback) + +The same as `dns.resolve()`, but only for service records (`SRV` records). +`addresses` is an array of the SRV records available for `domain`. Properties +of SRV records are priority, weight, port, and name (e.g., +`[{'priority': 10, {'weight': 5, 'port': 21223, 'name': 'service.example.com'}, ...]`). + +## dns.resolveNs(domain, callback) + +The same as `dns.resolve()`, but only for name server records (`NS` records). +`addresses` is an array of the name server records available for `domain` +(e.g., `['ns1.example.com', 'ns2.example.com']`). + +## dns.resolveCname(domain, callback) + +The same as `dns.resolve()`, but only for canonical name records (`CNAME` +records). `addresses` is an array of the canonical name records available for +`domain` (e.g., `['bar.example.com']`). + +## dns.reverse(ip, callback) + +Reverse resolves an ip address to an array of domain names. + +The callback has arguments `(err, domains)`. + +On error, `err` is an `Error` object, where `err.code` is +one of the error codes listed below. + +## Error codes + +Each DNS query can return one of the following error codes: + +- `dns.NODATA`: DNS server returned answer with no data. +- `dns.FORMERR`: DNS server claims query was misformatted. +- `dns.SERVFAIL`: DNS server returned general failure. +- `dns.NOTFOUND`: Domain name not found. +- `dns.NOTIMP`: DNS server does not implement requested operation. +- `dns.REFUSED`: DNS server refused query. +- `dns.BADQUERY`: Misformatted DNS query. +- `dns.BADNAME`: Misformatted domain name. +- `dns.BADFAMILY`: Unsupported address family. +- `dns.BADRESP`: Misformatted DNS reply. +- `dns.CONNREFUSED`: Could not contact DNS servers. +- `dns.TIMEOUT`: Timeout while contacting DNS servers. +- `dns.EOF`: End of file. +- `dns.FILE`: Error reading file. +- `dns.NOMEM`: Out of memory. +- `dns.DESTRUCTION`: Channel is being destroyed. +- `dns.BADSTR`: Misformatted string. +- `dns.BADFLAGS`: Illegal flags specified. +- `dns.NONAME`: Given hostname is not numeric. +- `dns.BADHINTS`: Illegal hints flags specified. +- `dns.NOTINITIALIZED`: c-ares library initialization not yet performed. +- `dns.LOADIPHLPAPI`: Error loading iphlpapi.dll. +- `dns.ADDRGETNETWORKPARAMS`: Could not find GetNetworkParams function. +- `dns.CANCELLED`: DNS query cancelled. + diff --git a/doc/api/documentation.markdown b/doc/api/documentation.markdown new file mode 100644 index 0000000..70a76c2 --- /dev/null +++ b/doc/api/documentation.markdown @@ -0,0 +1,68 @@ +# About this Documentation + +<!-- type=misc --> + +The goal of this documentation is to comprehensively explain the Node.js +API, both from a reference as well as a conceptual point of view. Each +section describes a built-in module or high-level concept. + +Where appropriate, property types, method arguments, and the arguments +provided to event handlers are detailed in a list underneath the topic +heading. + +Every `.html` document has a corresponding `.json` document presenting +the same information in a structured manner. This feature is +experimental, and added for the benefit of IDEs and other utilities that +wish to do programmatic things with the documentation. + +Every `.html` and `.json` file is generated based on the corresponding +`.markdown` file in the `doc/api/` folder in node's source tree. The +documentation is generated using the `tools/doc/generate.js` program. +The HTML template is located at `doc/template.html`. + +## Stability Index + +<!--type=misc--> + +Throughout the documentation, you will see indications of a section's +stability. The Node.js API is still somewhat changing, and as it +matures, certain parts are more reliable than others. Some are so +proven, and so relied upon, that they are unlikely to ever change at +all. Others are brand new and experimental, or known to be hazardous +and in the process of being redesigned. + +The notices look like this: + + Stability: 1 Experimental + +The stability indices are as follows: + +* **0 - Deprecated** This feature is known to be problematic, and changes are +planned. Do not rely on it. Use of the feature may cause warnings. Backwards +compatibility should not be expected. + +* **1 - Experimental** This feature was introduced recently, and may change +or be removed in future versions. Please try it out and provide feedback. +If it addresses a use-case that is important to you, tell the node core team. + +* **2 - Unstable** The API is in the process of settling, but has not yet had +sufficient real-world testing to be considered stable. Backwards-compatibility +will be maintained if reasonable. + +* **3 - Stable** The API has proven satisfactory, but cleanup in the underlying +code may cause minor changes. Backwards-compatibility is guaranteed. + +* **4 - API Frozen** This API has been tested extensively in production and is +unlikely to ever have to change. + +* **5 - Locked** Unless serious bugs are found, this code will not ever +change. Please do not suggest changes in this area; they will be refused. + +## JSON Output + + Stability: 1 - Experimental + +Every HTML file in the markdown has a corresponding JSON file with the +same data. + +This feature is new as of node v0.6.12. It is experimental. diff --git a/doc/api/domain.markdown b/doc/api/domain.markdown new file mode 100644 index 0000000..5bbdcc3 --- /dev/null +++ b/doc/api/domain.markdown @@ -0,0 +1,261 @@ +# Domain + + Stability: 1 - Experimental + +Domains provide a way to handle multiple different IO operations as a +single group. If any of the event emitters or callbacks registered to a +domain emit an `error` event, or throw an error, then the domain object +will be notified, rather than losing the context of the error in the +`process.on('uncaughtException')` handler, or causing the program to +exit with an error code. + +This feature is new in Node version 0.8. It is a first pass, and is +expected to change significantly in future versions. Please use it and +provide feedback. + +Due to their experimental nature, the Domains features are disabled unless +the `domain` module is loaded at least once. No domains are created or +registered by default. This is by design, to prevent adverse effects on +current programs. It is expected to be enabled by default in future +Node.js versions. + +## Additions to Error objects + +<!-- type=misc --> + +Any time an Error object is routed through a domain, a few extra fields +are added to it. + +* `error.domain` The domain that first handled the error. +* `error.domain_emitter` The event emitter that emitted an 'error' event + with the error object. +* `error.domain_bound` The callback function which was bound to the + domain, and passed an error as its first argument. +* `error.domain_thrown` A boolean indicating whether the error was + thrown, emitted, or passed to a bound callback function. + +## Implicit Binding + +<!--type=misc--> + +If domains are in use, then all new EventEmitter objects (including +Stream objects, requests, responses, etc.) will be implicitly bound to +the active domain at the time of their creation. + +Additionally, callbacks passed to lowlevel event loop requests (such as +to fs.open, or other callback-taking methods) will automatically be +bound to the active domain. If they throw, then the domain will catch +the error. + +In order to prevent excessive memory usage, Domain objects themselves +are not implicitly added as children of the active domain. If they +were, then it would be too easy to prevent request and response objects +from being properly garbage collected. + +If you *want* to nest Domain objects as children of a parent Domain, +then you must explicitly add them, and then dispose of them later. + +Implicit binding routes thrown errors and `'error'` events to the +Domain's `error` event, but does not register the EventEmitter on the +Domain, so `domain.dispose()` will not shut down the EventEmitter. +Implicit binding only takes care of thrown errors and `'error'` events. + +## Explicit Binding + +<!--type=misc--> + +Sometimes, the domain in use is not the one that ought to be used for a +specific event emitter. Or, the event emitter could have been created +in the context of one domain, but ought to instead be bound to some +other domain. + +For example, there could be one domain in use for an HTTP server, but +perhaps we would like to have a separate domain to use for each request. + +That is possible via explicit binding. + +For example: + +``` +// create a top-level domain for the server +var serverDomain = domain.create(); + +serverDomain.run(function() { + // server is created in the scope of serverDomain + http.createServer(function(req, res) { + // req and res are also created in the scope of serverDomain + // however, we'd prefer to have a separate domain for each request. + // create it first thing, and add req and res to it. + var reqd = domain.create(); + reqd.add(req); + reqd.add(res); + reqd.on('error', function(er) { + console.error('Error', er, req.url); + try { + res.writeHead(500); + res.end('Error occurred, sorry.'); + res.on('close', function() { + // forcibly shut down any other things added to this domain + reqd.dispose(); + }); + } catch (er) { + console.error('Error sending 500', er, req.url); + // tried our best. clean up anything remaining. + reqd.dispose(); + } + }); + }).listen(1337); +}); +``` + +## domain.create() + +* return: {Domain} + +Returns a new Domain object. + +## Class: Domain + +The Domain class encapsulates the functionality of routing errors and +uncaught exceptions to the active Domain object. + +Domain is a child class of EventEmitter. To handle the errors that it +catches, listen to its `error` event. + +### domain.run(fn) + +* `fn` {Function} + +Run the supplied function in the context of the domain, implicitly +binding all event emitters, timers, and lowlevel requests that are +created in that context. + +This is the most basic way to use a domain. + +Example: + +``` +var d = domain.create(); +d.on('error', function(er) { + console.error('Caught error!', er); +}); +d.run(function() { + process.nextTick(function() { + setTimeout(function() { // simulating some various async stuff + fs.open('non-existent file', 'r', function(er, fd) { + if (er) throw er; + // proceed... + }); + }, 100); + }); +}); +``` + +In this example, the `d.on('error')` handler will be triggered, rather +than crashing the program. + +### domain.members + +* {Array} + +An array of timers and event emitters that have been explicitly added +to the domain. + +### domain.add(emitter) + +* `emitter` {EventEmitter | Timer} emitter or timer to be added to the domain + +Explicitly adds an emitter to the domain. If any event handlers called by +the emitter throw an error, or if the emitter emits an `error` event, it +will be routed to the domain's `error` event, just like with implicit +binding. + +This also works with timers that are returned from `setInterval` and +`setTimeout`. If their callback function throws, it will be caught by +the domain 'error' handler. + +If the Timer or EventEmitter was already bound to a domain, it is removed +from that one, and bound to this one instead. + +### domain.remove(emitter) + +* `emitter` {EventEmitter | Timer} emitter or timer to be removed from the domain + +The opposite of `domain.add(emitter)`. Removes domain handling from the +specified emitter. + +### domain.bind(cb) + +* `cb` {Function} The callback function +* return: {Function} The bound function + +The returned function will be a wrapper around the supplied callback +function. When the returned function is called, any errors that are +thrown will be routed to the domain's `error` event. + +#### Example + + var d = domain.create(); + + function readSomeFile(filename, cb) { + fs.readFile(filename, d.bind(function(er, data) { + // if this throws, it will also be passed to the domain + return cb(er, JSON.parse(data)); + })); + } + + d.on('error', function(er) { + // an error occurred somewhere. + // if we throw it now, it will crash the program + // with the normal line number and stack message. + }); + +### domain.intercept(cb) + +* `cb` {Function} The callback function +* return: {Function} The intercepted function + +This method is almost identical to `domain.bind(cb)`. However, in +addition to catching thrown errors, it will also intercept `Error` +objects sent as the first argument to the function. + +In this way, the common `if (er) return cb(er);` pattern can be replaced +with a single error handler in a single place. + +#### Example + + var d = domain.create(); + + function readSomeFile(filename, cb) { + fs.readFile(filename, d.intercept(function(er, data) { + // if this throws, it will also be passed to the domain + // additionally, we know that 'er' will always be null, + // so the error-handling logic can be moved to the 'error' + // event on the domain instead of being repeated throughout + // the program. + return cb(er, JSON.parse(data)); + })); + } + + d.on('error', function(er) { + // an error occurred somewhere. + // if we throw it now, it will crash the program + // with the normal line number and stack message. + }); + +### domain.dispose() + +The dispose method destroys a domain, and makes a best effort attempt to +clean up any and all IO that is associated with the domain. Streams are +aborted, ended, closed, and/or destroyed. Timers are cleared. +Explicitly bound callbacks are no longer called. Any error events that +are raised as a result of this are ignored. + +The intention of calling `dispose` is generally to prevent cascading +errors when a critical part of the Domain context is found to be in an +error state. + +Note that IO might still be performed. However, to the highest degree +possible, once a domain is disposed, further errors from the emitters in +that set will be ignored. So, even if some remaining actions are still +in flight, Node.js will not communicate further about them. diff --git a/doc/api/events.markdown b/doc/api/events.markdown new file mode 100644 index 0000000..fed957d --- /dev/null +++ b/doc/api/events.markdown @@ -0,0 +1,95 @@ +# Events + + Stability: 4 - API Frozen + +<!--type=module--> + +Many objects in Node emit events: a `net.Server` emits an event each time +a peer connects to it, a `fs.readStream` emits an event when the file is +opened. All objects which emit events are instances of `events.EventEmitter`. +You can access this module by doing: `require("events");` + +Typically, event names are represented by a camel-cased string, however, +there aren't any strict restrictions on that, as any string will be accepted. + +Functions can then be attached to objects, to be executed when an event +is emitted. These functions are called _listeners_. + + +## Class: events.EventEmitter + +To access the EventEmitter class, `require('events').EventEmitter`. + +When an `EventEmitter` instance experiences an error, the typical action is +to emit an `'error'` event. Error events are treated as a special case in node. +If there is no listener for it, then the default action is to print a stack +trace and exit the program. + +All EventEmitters emit the event `'newListener'` when new listeners are +added. + +### emitter.addListener(event, listener) +### emitter.on(event, listener) + +Adds a listener to the end of the listeners array for the specified event. + + server.on('connection', function (stream) { + console.log('someone connected!'); + }); + +### emitter.once(event, listener) + +Adds a **one time** listener for the event. This listener is +invoked only the next time the event is fired, after which +it is removed. + + server.once('connection', function (stream) { + console.log('Ah, we have our first user!'); + }); + +### emitter.removeListener(event, listener) + +Remove a listener from the listener array for the specified event. +**Caution**: changes array indices in the listener array behind the listener. + + var callback = function(stream) { + console.log('someone connected!'); + }; + server.on('connection', callback); + // ... + server.removeListener('connection', callback); + + +### emitter.removeAllListeners([event]) + +Removes all listeners, or those of the specified event. + + +### emitter.setMaxListeners(n) + +By default EventEmitters will print a warning if more than 10 listeners are +added for a particular event. This is a useful default which helps finding memory leaks. +Obviously not all Emitters should be limited to 10. This function allows +that to be increased. Set to zero for unlimited. + + +### emitter.listeners(event) + +Returns an array of listeners for the specified event. This array can be +manipulated, e.g. to remove listeners. + + server.on('connection', function (stream) { + console.log('someone connected!'); + }); + console.log(util.inspect(server.listeners('connection'))); // [ [Function] ] + +### emitter.emit(event, [arg1], [arg2], [...]) + +Execute each of the listeners in order with the supplied arguments. + +### Event: 'newListener' + +* `event` {String} The event name +* `listener` {Function} The event handler function + +This event is emitted any time someone adds a new listener. diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown new file mode 100644 index 0000000..96bef91 --- /dev/null +++ b/doc/api/fs.markdown @@ -0,0 +1,667 @@ +# File System + + Stability: 3 - Stable + +<!--name=fs--> + +File I/O is provided by simple wrappers around standard POSIX functions. To +use this module do `require('fs')`. All the methods have asynchronous and +synchronous forms. + +The asynchronous form always take a completion callback as its last argument. +The arguments passed to the completion callback depend on the method, but the +first argument is always reserved for an exception. If the operation was +completed successfully, then the first argument will be `null` or `undefined`. + +When using the synchronous form any exceptions are immediately thrown. +You can use try/catch to handle exceptions or allow them to bubble up. + +Here is an example of the asynchronous version: + + var fs = require('fs'); + + fs.unlink('/tmp/hello', function (err) { + if (err) throw err; + console.log('successfully deleted /tmp/hello'); + }); + +Here is the synchronous version: + + var fs = require('fs'); + + fs.unlinkSync('/tmp/hello') + console.log('successfully deleted /tmp/hello'); + +With the asynchronous methods there is no guaranteed ordering. So the +following is prone to error: + + fs.rename('/tmp/hello', '/tmp/world', function (err) { + if (err) throw err; + console.log('renamed complete'); + }); + fs.stat('/tmp/world', function (err, stats) { + if (err) throw err; + console.log('stats: ' + JSON.stringify(stats)); + }); + +It could be that `fs.stat` is executed before `fs.rename`. +The correct way to do this is to chain the callbacks. + + fs.rename('/tmp/hello', '/tmp/world', function (err) { + if (err) throw err; + fs.stat('/tmp/world', function (err, stats) { + if (err) throw err; + console.log('stats: ' + JSON.stringify(stats)); + }); + }); + +In busy processes, the programmer is _strongly encouraged_ to use the +asynchronous versions of these calls. The synchronous versions will block +the entire process until they complete--halting all connections. + +Relative path to filename can be used, remember however that this path will be relative +to `process.cwd()`. + +## fs.rename(oldPath, newPath, [callback]) + +Asynchronous rename(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.renameSync(oldPath, newPath) + +Synchronous rename(2). + +## fs.truncate(fd, len, [callback]) + +Asynchronous ftruncate(2). No arguments other than a possible exception are +given to the completion callback. + +## fs.truncateSync(fd, len) + +Synchronous ftruncate(2). + +## fs.chown(path, uid, gid, [callback]) + +Asynchronous chown(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.chownSync(path, uid, gid) + +Synchronous chown(2). + +## fs.fchown(fd, uid, gid, [callback]) + +Asynchronous fchown(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.fchownSync(fd, uid, gid) + +Synchronous fchown(2). + +## fs.lchown(path, uid, gid, [callback]) + +Asynchronous lchown(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.lchownSync(path, uid, gid) + +Synchronous lchown(2). + +## fs.chmod(path, mode, [callback]) + +Asynchronous chmod(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.chmodSync(path, mode) + +Synchronous chmod(2). + +## fs.fchmod(fd, mode, [callback]) + +Asynchronous fchmod(2). No arguments other than a possible exception +are given to the completion callback. + +## fs.fchmodSync(fd, mode) + +Synchronous fchmod(2). + +## fs.lchmod(path, mode, [callback]) + +Asynchronous lchmod(2). No arguments other than a possible exception +are given to the completion callback. + +## fs.lchmodSync(path, mode) + +Synchronous lchmod(2). + +## fs.stat(path, [callback]) + +Asynchronous stat(2). The callback gets two arguments `(err, stats)` where +`stats` is a [fs.Stats](#fs_class_fs_stats) object. See the [fs.Stats](#fs_class_fs_stats) +section below for more information. + +## fs.lstat(path, [callback]) + +Asynchronous lstat(2). The callback gets two arguments `(err, stats)` where +`stats` is a `fs.Stats` object. `lstat()` is identical to `stat()`, except that if +`path` is a symbolic link, then the link itself is stat-ed, not the file that it +refers to. + +## fs.fstat(fd, [callback]) + +Asynchronous fstat(2). The callback gets two arguments `(err, stats)` where +`stats` is a `fs.Stats` object. `fstat()` is identical to `stat()`, except that +the file to be stat-ed is specified by the file descriptor `fd`. + +## fs.statSync(path) + +Synchronous stat(2). Returns an instance of `fs.Stats`. + +## fs.lstatSync(path) + +Synchronous lstat(2). Returns an instance of `fs.Stats`. + +## fs.fstatSync(fd) + +Synchronous fstat(2). Returns an instance of `fs.Stats`. + +## fs.link(srcpath, dstpath, [callback]) + +Asynchronous link(2). No arguments other than a possible exception are given to +the completion callback. + +## fs.linkSync(srcpath, dstpath) + +Synchronous link(2). + +## fs.symlink(destination, path, [type], [callback]) + +Asynchronous symlink(2). No arguments other than a possible exception are given +to the completion callback. +`type` argument can be either `'dir'` or `'file'` (default is `'file'`). It is only +used on Windows (ignored on other platforms). + +## fs.symlinkSync(destination, path, [type]) + +Synchronous symlink(2). + +## fs.readlink(path, [callback]) + +Asynchronous readlink(2). The callback gets two arguments `(err, +linkString)`. + +## fs.readlinkSync(path) + +Synchronous readlink(2). Returns the symbolic link's string value. + +## fs.realpath(path, [cache], callback) + +Asynchronous realpath(2). The `callback` gets two arguments `(err, +resolvedPath)`. May use `process.cwd` to resolve relative paths. `cache` is an +object literal of mapped paths that can be used to force a specific path +resolution or avoid additional `fs.stat` calls for known real paths. + +Example: + + var cache = {'/etc':'/private/etc'}; + fs.realpath('/etc/passwd', cache, function (err, resolvedPath) { + if (err) throw err; + console.log(resolvedPath); + }); + +## fs.realpathSync(path, [cache]) + +Synchronous realpath(2). Returns the resolved path. + +## fs.unlink(path, [callback]) + +Asynchronous unlink(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.unlinkSync(path) + +Synchronous unlink(2). + +## fs.rmdir(path, [callback]) + +Asynchronous rmdir(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.rmdirSync(path) + +Synchronous rmdir(2). + +## fs.mkdir(path, [mode], [callback]) + +Asynchronous mkdir(2). No arguments other than a possible exception are given +to the completion callback. `mode` defaults to `0777`. + +## fs.mkdirSync(path, [mode]) + +Synchronous mkdir(2). + +## fs.readdir(path, [callback]) + +Asynchronous readdir(3). Reads the contents of a directory. +The callback gets two arguments `(err, files)` where `files` is an array of +the names of the files in the directory excluding `'.'` and `'..'`. + +## fs.readdirSync(path) + +Synchronous readdir(3). Returns an array of filenames excluding `'.'` and +`'..'`. + +## fs.close(fd, [callback]) + +Asynchronous close(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.closeSync(fd) + +Synchronous close(2). + +## fs.open(path, flags, [mode], [callback]) + +Asynchronous file open. See open(2). `flags` can be: + +* `'r'` - Open file for reading. +An exception occurs if the file does not exist. + +* `'r+'` - Open file for reading and writing. +An exception occurs if the file does not exist. + +* `'w'` - Open file for writing. +The file is created (if it does not exist) or truncated (if it exists). + +* `'wx'` - Like `'w'` but opens the file in exclusive mode. + +* `'w+'` - Open file for reading and writing. +The file is created (if it does not exist) or truncated (if it exists). + +* `'wx+'` - Like `'w+'` but opens the file in exclusive mode. + +* `'a'` - Open file for appending. +The file is created if it does not exist. + +* `'ax'` - Like `'a'` but opens the file in exclusive mode. + +* `'a+'` - Open file for reading and appending. +The file is created if it does not exist. + +* `'ax+'` - Like `'a+'` but opens the file in exclusive mode. + +`mode` defaults to `0666`. The callback gets two arguments `(err, fd)`. + +Exclusive mode (`O_EXCL`) ensures that `path` is newly created. `fs.open()` +fails if a file by that name already exists. On POSIX systems, symlinks are +not followed. Exclusive mode may or may not work with network file systems. + +## fs.openSync(path, flags, [mode]) + +Synchronous open(2). + +## fs.utimes(path, atime, mtime, [callback]) +## fs.utimesSync(path, atime, mtime) + +Change file timestamps of the file referenced by the supplied path. + +## fs.futimes(fd, atime, mtime, [callback]) +## fs.futimesSync(fd, atime, mtime) + +Change the file timestamps of a file referenced by the supplied file +descriptor. + +## fs.fsync(fd, [callback]) + +Asynchronous fsync(2). No arguments other than a possible exception are given +to the completion callback. + +## fs.fsyncSync(fd) + +Synchronous fsync(2). + +## fs.write(fd, buffer, offset, length, position, [callback]) + +Write `buffer` to the file specified by `fd`. + +`offset` and `length` determine the part of the buffer to be written. + +`position` refers to the offset from the beginning of the file where this data +should be written. If `position` is `null`, the data will be written at the +current position. +See pwrite(2). + +The callback will be given three arguments `(err, written, buffer)` where `written` +specifies how many _bytes_ were written from `buffer`. + +Note that it is unsafe to use `fs.write` multiple times on the same file +without waiting for the callback. For this scenario, +`fs.createWriteStream` is strongly recommended. + +## fs.writeSync(fd, buffer, offset, length, position) + +Synchronous version of buffer-based `fs.write()`. Returns the number of bytes +written. + +## fs.writeSync(fd, str, position, [encoding]) + +Synchronous version of string-based `fs.write()`. `encoding` defaults to +`'utf8'`. Returns the number of _bytes_ written. + +## fs.read(fd, buffer, offset, length, position, [callback]) + +Read data from the file specified by `fd`. + +`buffer` is the buffer that the data will be written to. + +`offset` is offset within the buffer where writing will start. + +`length` is an integer specifying the number of bytes to read. + +`position` is an integer specifying where to begin reading from in the file. +If `position` is `null`, data will be read from the current file position. + +The callback is given the three arguments, `(err, bytesRead, buffer)`. + +## fs.readSync(fd, buffer, offset, length, position) + +Synchronous version of buffer-based `fs.read`. Returns the number of +`bytesRead`. + +## fs.readSync(fd, length, position, encoding) + +Synchronous version of string-based `fs.read`. Returns the number of +`bytesRead`. + +## fs.readFile(filename, [encoding], [callback]) + +Asynchronously reads the entire contents of a file. Example: + + fs.readFile('/etc/passwd', function (err, data) { + if (err) throw err; + console.log(data); + }); + +The callback is passed two arguments `(err, data)`, where `data` is the +contents of the file. + +If no encoding is specified, then the raw buffer is returned. + + +## fs.readFileSync(filename, [encoding]) + +Synchronous version of `fs.readFile`. Returns the contents of the `filename`. + +If `encoding` is specified then this function returns a string. Otherwise it +returns a buffer. + + +## fs.writeFile(filename, data, [encoding], [callback]) + +Asynchronously writes data to a file, replacing the file if it already exists. +`data` can be a string or a buffer. The `encoding` argument is ignored if +`data` is a buffer. It defaults to `'utf8'`. + +Example: + + fs.writeFile('message.txt', 'Hello Node', function (err) { + if (err) throw err; + console.log('It\'s saved!'); + }); + +## fs.writeFileSync(filename, data, [encoding]) + +The synchronous version of `fs.writeFile`. + +## fs.appendFile(filename, data, encoding='utf8', [callback]) + +Asynchronously append data to a file, creating the file if it not yet exists. +`data` can be a string or a buffer. The `encoding` argument is ignored if +`data` is a buffer. + +Example: + + fs.appendFile('message.txt', 'data to append', function (err) { + if (err) throw err; + console.log('The "data to append" was appended to file!'); + }); + +## fs.appendFileSync(filename, data, encoding='utf8') + +The synchronous version of `fs.appendFile`. + +## fs.watchFile(filename, [options], listener) + + Stability: 2 - Unstable. Use fs.watch instead, if available. + +Watch for changes on `filename`. The callback `listener` will be called each +time the file is accessed. + +The second argument is optional. The `options` if provided should be an object +containing two members a boolean, `persistent`, and `interval`. `persistent` +indicates whether the process should continue to run as long as files are +being watched. `interval` indicates how often the target should be polled, +in milliseconds. (On Linux systems with inotify, `interval` is ignored.) The +default is `{ persistent: true, interval: 0 }`. + +The `listener` gets two arguments the current stat object and the previous +stat object: + + fs.watchFile('message.text', function (curr, prev) { + console.log('the current mtime is: ' + curr.mtime); + console.log('the previous mtime was: ' + prev.mtime); + }); + +These stat objects are instances of `fs.Stat`. + +If you want to be notified when the file was modified, not just accessed +you need to compare `curr.mtime` and `prev.mtime`. + + +## fs.unwatchFile(filename) + + Stability: 2 - Unstable. Use fs.watch instead, if available. + +Stop watching for changes on `filename`. + +## fs.watch(filename, [options], listener) + + Stability: 2 - Unstable. Not available on all platforms. + +Watch for changes on `filename`, where `filename` is either a file or a +directory. The returned object is a [fs.FSWatcher](#fs_class_fs_fswatcher). + +The second argument is optional. The `options` if provided should be an object +containing a boolean member `persistent`, which indicates whether the process +should continue to run as long as files are being watched. The default is +`{ persistent: true }`. + +The listener callback gets two arguments `(event, filename)`. `event` is either +'rename' or 'change', and `filename` is the name of the file which triggered +the event. + +### Caveats + +<!--type=misc--> + +The `fs.watch` API is not 100% consistent across platforms, and is +unavailable in some situations. + +#### Availability + +<!--type=misc--> + +This feature depends on the underlying operating system providing a way +to be notified of filesystem changes. + +* On Linux systems, this uses `inotify`. +* On BSD systems (including OS X), this uses `kqueue`. +* On SunOS systems (including Solaris and SmartOS), this uses `event ports`. +* On Windows systems, this feature depends on `ReadDirectoryChangesW`. + +If the underlying functionality is not available for some reason, then +`fs.watch` will not be able to function. You can still use +`fs.watchFile`, which uses stat polling, but it is slower and less +reliable. + +#### Filename Argument + +<!--type=misc--> + +Providing `filename` argument in the callback is not supported +on every platform (currently it's only supported on Linux and Windows). Even +on supported platforms `filename` is not always guaranteed to be provided. +Therefore, don't assume that `filename` argument is always provided in the +callback, and have some fallback logic if it is null. + + fs.watch('somedir', function (event, filename) { + console.log('event is: ' + event); + if (filename) { + console.log('filename provided: ' + filename); + } else { + console.log('filename not provided'); + } + }); + +## fs.exists(path, [callback]) + +Test whether or not the given path exists by checking with the file system. +Then call the `callback` argument with either true or false. Example: + + fs.exists('/etc/passwd', function (exists) { + util.debug(exists ? "it's there" : "no passwd!"); + }); + + +## fs.existsSync(path) + +Synchronous version of `fs.exists`. + +## Class: fs.Stats + +Objects returned from `fs.stat()`, `fs.lstat()` and `fs.fstat()` and their +synchronous counterparts are of this type. + + - `stats.isFile()` + - `stats.isDirectory()` + - `stats.isBlockDevice()` + - `stats.isCharacterDevice()` + - `stats.isSymbolicLink()` (only valid with `fs.lstat()`) + - `stats.isFIFO()` + - `stats.isSocket()` + +For a regular file `util.inspect(stats)` would return a string very +similar to this: + + { dev: 2114, + ino: 48064969, + mode: 33188, + nlink: 1, + uid: 85, + gid: 100, + rdev: 0, + size: 527, + blksize: 4096, + blocks: 8, + atime: Mon, 10 Oct 2011 23:24:11 GMT, + mtime: Mon, 10 Oct 2011 23:24:11 GMT, + ctime: Mon, 10 Oct 2011 23:24:11 GMT } + +Please note that `atime`, `mtime` and `ctime` are instances +of [Date][MDN-Date] object and to compare the values of +these objects you should use appropriate methods. For most +general uses [getTime()][MDN-Date-getTime] will return +the number of milliseconds elapsed since _1 January 1970 +00:00:00 UTC_ and this integer should be sufficient for +any comparison, however there additional methods which can +be used for displaying fuzzy information. More details can +be found in the [MDN JavaScript Reference][MDN-Date] page. + +[MDN-Date]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date +[MDN-Date-getTime]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/getTime + + +## fs.createReadStream(path, [options]) + +Returns a new ReadStream object (See `Readable Stream`). + +`options` is an object with the following defaults: + + { flags: 'r', + encoding: null, + fd: null, + mode: 0666, + bufferSize: 64 * 1024 + } + +`options` can include `start` and `end` values to read a range of bytes from +the file instead of the entire file. Both `start` and `end` are inclusive and +start at 0. The `encoding` can be `'utf8'`, `'ascii'`, or `'base64'`. + +An example to read the last 10 bytes of a file which is 100 bytes long: + + fs.createReadStream('sample.txt', {start: 90, end: 99}); + + +## Class: fs.ReadStream + +`ReadStream` is a [Readable Stream](stream.html#stream_readable_stream). + +### Event: 'open' + +* `fd` {Integer} file descriptor used by the ReadStream. + +Emitted when the ReadStream's file is opened. + + +## fs.createWriteStream(path, [options]) + +Returns a new WriteStream object (See `Writable Stream`). + +`options` is an object with the following defaults: + + { flags: 'w', + encoding: null, + mode: 0666 } + +`options` may also include a `start` option to allow writing data at +some position past the beginning of the file. Modifying a file rather +than replacing it may require a `flags` mode of `r+` rather than the +default mode `w`. + +## fs.WriteStream + +`WriteStream` is a [Writable Stream](stream.html#stream_writable_stream). + +### Event: 'open' + +* `fd` {Integer} file descriptor used by the WriteStream. + +Emitted when the WriteStream's file is opened. + +### file.bytesWritten + +The number of bytes written so far. Does not include data that is still queued +for writing. + +## Class: fs.FSWatcher + +Objects returned from `fs.watch()` are of this type. + +### watcher.close() + +Stop watching for changes on the given `fs.FSWatcher`. + +### Event: 'change' + +* `event` {String} The type of fs change +* `filename` {String} The filename that changed (if relevant/available) + +Emitted when something changes in a watched directory or file. +See more details in [fs.watch](#fs_fs_watch_filename_options_listener). + +### Event: 'error' + +* `error` {Error object} + +Emitted when an error occurs. diff --git a/doc/api/globals.markdown b/doc/api/globals.markdown new file mode 100644 index 0000000..d8fb257 --- /dev/null +++ b/doc/api/globals.markdown @@ -0,0 +1,150 @@ +# Global Objects + +<!-- type=misc --> + +These objects are available in all modules. Some of these objects aren't +actually in the global scope but in the module scope - this will be noted. + +## global + +<!-- type=global --> + +* {Object} The global namespace object. + +In browsers, the top-level scope is the global scope. That means that in +browsers if you're in the global scope `var something` will define a global +variable. In Node this is different. The top-level scope is not the global +scope; `var something` inside a Node module will be local to that module. + +## process + +<!-- type=global --> + +* {Object} + +The process object. See the [process object](process.html#process) section. + +## console + +<!-- type=global --> + +* {Object} + +Used to print to stdout and stderr. See the [stdio](stdio.html) section. + +## Class: Buffer + +<!-- type=global --> + +* {Function} + +Used to handle binary data. See the [buffer section](buffer.html). + +## require() + +<!-- type=var --> + +* {Function} + +To require modules. See the [Modules](modules.html#modules) section. +`require` isn't actually a global but rather local to each module. + +### require.resolve() + +Use the internal `require()` machinery to look up the location of a module, +but rather than loading the module, just return the resolved filename. + +### require.cache + +* {Object} + +Modules are cached in this object when they are required. By deleting a key +value from this object, the next `require` will reload the module. + +### require.extensions + +* {Array} + +Instruct `require` on how to handle certain file extensions. + +Process files with the extension `.sjs` as `.js`: + + require.extensions['.sjs'] = require.extensions['.js']; + +Write your own extension handler: + + require.extensions['.sjs'] = function(module, filename) { + var content = fs.readFileSync(filename, 'utf8'); + // Parse the file content and give to module.exports + module.exports = content; + }; + +## __filename + +<!-- type=var --> + +* {String} + +The filename of the code being executed. This is the resolved absolute path +of this code file. For a main program this is not necessarily the same +filename used in the command line. The value inside a module is the path +to that module file. + +Example: running `node example.js` from `/Users/mjr` + + console.log(__filename); + // /Users/mjr/example.js + +`__filename` isn't actually a global but rather local to each module. + +## __dirname + +<!-- type=var --> + +* {String} + +The name of the directory that the currently executing script resides in. + +Example: running `node example.js` from `/Users/mjr` + + console.log(__dirname); + // /Users/mjr + +`__dirname` isn't actually a global but rather local to each module. + + +## module + +<!-- type=var --> + +* {Object} + +A reference to the current module. In particular +`module.exports` is the same as the `exports` object. +`module` isn't actually a global but rather local to each module. + +See the [module system documentation](modules.html) for more +information. + +## exports + +<!-- type=var --> + +An object which is shared between all instances of the current module and +made accessible through `require()`. +`exports` is the same as the `module.exports` object. +`exports` isn't actually a global but rather local to each module. + +See the [module system documentation](modules.html) for more +information. + +See the [module section](modules.html) for more information. + +## setTimeout(cb, ms) +## clearTimeout(t) +## setInterval(cb, ms) +## clearInterval(t) + +<!--type=global--> + +The timer functions are global variables. See the [timers](timers.html) section. diff --git a/doc/api/http.markdown b/doc/api/http.markdown new file mode 100644 index 0000000..d7f4352 --- /dev/null +++ b/doc/api/http.markdown @@ -0,0 +1,886 @@ +# HTTP + + Stability: 3 - Stable + +To use the HTTP server and client one must `require('http')`. + +The HTTP interfaces in Node are designed to support many features +of the protocol which have been traditionally difficult to use. +In particular, large, possibly chunk-encoded, messages. The interface is +careful to never buffer entire requests or responses--the +user is able to stream data. + +HTTP message headers are represented by an object like this: + + { 'content-length': '123', + 'content-type': 'text/plain', + 'connection': 'keep-alive', + 'accept': '*/*' } + +Keys are lowercased. Values are not modified. + +In order to support the full spectrum of possible HTTP applications, Node's +HTTP API is very low-level. It deals with stream handling and message +parsing only. It parses a message into headers and body but it does not +parse the actual headers or the body. + + +## http.createServer([requestListener]) + +Returns a new web server object. + +The `requestListener` is a function which is automatically +added to the `'request'` event. + +## http.createClient([port], [host]) + +This function is **deprecated**; please use +[http.request()](#http_http_request_options_callback) instead. Constructs a new +HTTP client. `port` and `host` refer to the server to be connected to. + +## Class: http.Server + +This is an `EventEmitter` with the following events: + +### Event: 'request' + +`function (request, response) { }` + +Emitted each time there is a request. Note that there may be multiple requests +per connection (in the case of keep-alive connections). + `request` is an instance of `http.ServerRequest` and `response` is + an instance of `http.ServerResponse` + +### Event: 'connection' + +`function (socket) { }` + + When a new TCP stream is established. `socket` is an object of type + `net.Socket`. Usually users will not want to access this event. The + `socket` can also be accessed at `request.connection`. + +### Event: 'close' + +`function () { }` + + Emitted when the server closes. + +### Event: 'checkContinue' + +`function (request, response) { }` + +Emitted each time a request with an http Expect: 100-continue is received. +If this event isn't listened for, the server will automatically respond +with a 100 Continue as appropriate. + +Handling this event involves calling `response.writeContinue` if the client +should continue to send the request body, or generating an appropriate HTTP +response (e.g., 400 Bad Request) if the client should not continue to send the +request body. + +Note that when this event is emitted and handled, the `request` event will +not be emitted. + +### Event: 'connect' + +`function (request, socket, head) { }` + +Emitted each time a client requests a http CONNECT method. If this event isn't +listened for, then clients requesting a CONNECT method will have their +connections closed. + +* `request` is the arguments for the http request, as it is in the request + event. +* `socket` is the network socket between the server and client. +* `head` is an instance of Buffer, the first packet of the tunneling stream, + this may be empty. + +After this event is emitted, the request's socket will not have a `data` +event listener, meaning you will need to bind to it in order to handle data +sent to the server on that socket. + +### Event: 'upgrade' + +`function (request, socket, head) { }` + +Emitted each time a client requests a http upgrade. If this event isn't +listened for, then clients requesting an upgrade will have their connections +closed. + +* `request` is the arguments for the http request, as it is in the request + event. +* `socket` is the network socket between the server and client. +* `head` is an instance of Buffer, the first packet of the upgraded stream, + this may be empty. + +After this event is emitted, the request's socket will not have a `data` +event listener, meaning you will need to bind to it in order to handle data +sent to the server on that socket. + +### Event: 'clientError' + +`function (exception) { }` + +If a client connection emits an 'error' event - it will forwarded here. + +### server.listen(port, [hostname], [backlog], [callback]) + +Begin accepting connections on the specified port and hostname. If the +hostname is omitted, the server will accept connections directed to any +IPv4 address (`INADDR_ANY`). + +To listen to a unix socket, supply a filename instead of port and hostname. + +Backlog is the maximum length of the queue of pending connections. +The actual length will be determined by your OS through sysctl settings such as +`tcp_max_syn_backlog` and `somaxconn` on linux. The default value of this +parameter is 511 (not 512). + +This function is asynchronous. The last parameter `callback` will be added as +a listener for the ['listening'](net.html#event_listening_) event. +See also [net.Server.listen()](net.html#server.listen). + + +### server.listen(path, [callback]) + +Start a UNIX socket server listening for connections on the given `path`. + +This function is asynchronous. The last parameter `callback` will be added as +a listener for the ['listening'](net.html#event_listening_) event. +See also [net.Server.listen()](net.html#server.listen). + + +### server.close([cb]) + +Stops the server from accepting new connections. +See [net.Server.close()](net.html#server.close). + + +### server.maxHeadersCount + +Limits maximum incoming headers count, equal to 1000 by default. If set to 0 - +no limit will be applied. + + +## Class: http.ServerRequest + +This object is created internally by a HTTP server -- not by +the user -- and passed as the first argument to a `'request'` listener. + +The request implements the [Readable Stream](stream.html#readable_stream) +interface. This is an `EventEmitter` with the following events: + +### Event: 'data' + +`function (chunk) { }` + +Emitted when a piece of the message body is received. The chunk is a string if +an encoding has been set with `request.setEncoding()`, otherwise it's a +[Buffer](buffer.html). + +Note that the __data will be lost__ if there is no listener when a +`ServerRequest` emits a `'data'` event. + +### Event: 'end' + +`function () { }` + +Emitted exactly once for each request. After that, no more `'data'` events +will be emitted on the request. + +### Event: 'close' + +`function () { }` + +Indicates that the underlaying connection was terminated before +`response.end()` was called or able to flush. + +Just like `'end'`, this event occurs only once per request, and no more `'data'` +events will fire afterwards. + +Note: `'close'` can fire after `'end'`, but not vice versa. + +### request.method + +The request method as a string. Read only. Example: +`'GET'`, `'DELETE'`. + + +### request.url + +Request URL string. This contains only the URL that is +present in the actual HTTP request. If the request is: + + GET /status?name=ryan HTTP/1.1\r\n + Accept: text/plain\r\n + \r\n + +Then `request.url` will be: + + '/status?name=ryan' + +If you would like to parse the URL into its parts, you can use +`require('url').parse(request.url)`. Example: + + node> require('url').parse('/status?name=ryan') + { href: '/status?name=ryan', + search: '?name=ryan', + query: 'name=ryan', + pathname: '/status' } + +If you would like to extract the params from the query string, +you can use the `require('querystring').parse` function, or pass +`true` as the second argument to `require('url').parse`. Example: + + node> require('url').parse('/status?name=ryan', true) + { href: '/status?name=ryan', + search: '?name=ryan', + query: { name: 'ryan' }, + pathname: '/status' } + + + +### request.headers + +Read only. + +### request.trailers + +Read only; HTTP trailers (if present). Only populated after the 'end' event. + +### request.httpVersion + +The HTTP protocol version as a string. Read only. Examples: +`'1.1'`, `'1.0'`. +Also `request.httpVersionMajor` is the first integer and +`request.httpVersionMinor` is the second. + + +### request.setEncoding([encoding]) + +Set the encoding for the request body. Either `'utf8'` or `'binary'`. Defaults +to `null`, which means that the `'data'` event will emit a `Buffer` object.. + + +### request.pause() + +Pauses request from emitting events. Useful to throttle back an upload. + + +### request.resume() + +Resumes a paused request. + +### request.connection + +The `net.Socket` object associated with the connection. + + +With HTTPS support, use request.connection.verifyPeer() and +request.connection.getPeerCertificate() to obtain the client's +authentication details. + + + +## Class: http.ServerResponse + +This object is created internally by a HTTP server--not by the user. It is +passed as the second parameter to the `'request'` event. + +The response implements the [Writable Stream](stream.html#writable_stream) +interface. This is an `EventEmitter` with the following events: + +### Event: 'close' + +`function () { }` + +Indicates that the underlaying connection was terminated before +`response.end()` was called or able to flush. + +### response.writeContinue() + +Sends a HTTP/1.1 100 Continue message to the client, indicating that +the request body should be sent. See the [checkContinue](#event_checkContinue_) event on +`Server`. + +### response.writeHead(statusCode, [reasonPhrase], [headers]) + +Sends a response header to the request. The status code is a 3-digit HTTP +status code, like `404`. The last argument, `headers`, are the response headers. +Optionally one can give a human-readable `reasonPhrase` as the second +argument. + +Example: + + var body = 'hello world'; + response.writeHead(200, { + 'Content-Length': body.length, + 'Content-Type': 'text/plain' }); + +This method must only be called once on a message and it must +be called before `response.end()` is called. + +If you call `response.write()` or `response.end()` before calling this, the +implicit/mutable headers will be calculated and call this function for you. + +Note: that Content-Length is given in bytes not characters. The above example +works because the string `'hello world'` contains only single byte characters. +If the body contains higher coded characters then `Buffer.byteLength()` +should be used to determine the number of bytes in a given encoding. +And Node does not check whether Content-Length and the length of the body +which has been transmitted are equal or not. + +### response.statusCode + +When using implicit headers (not calling `response.writeHead()` explicitly), this property +controls the status code that will be send to the client when the headers get +flushed. + +Example: + + response.statusCode = 404; + +After response header was sent to the client, this property indicates the +status code which was sent out. + +### response.setHeader(name, value) + +Sets a single header value for implicit headers. If this header already exists +in the to-be-sent headers, its value will be replaced. Use an array of strings +here if you need to send multiple headers with the same name. + +Example: + + response.setHeader("Content-Type", "text/html"); + +or + + response.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]); + +### response.sendDate + +When true, the Date header will be automatically generated and sent in +the response if it is not already present in the headers. Defaults to true. + +This should only be disabled for testing; HTTP requires the Date header +in responses. + +### response.getHeader(name) + +Reads out a header that's already been queued but not sent to the client. Note +that the name is case insensitive. This can only be called before headers get +implicitly flushed. + +Example: + + var contentType = response.getHeader('content-type'); + +### response.removeHeader(name) + +Removes a header that's queued for implicit sending. + +Example: + + response.removeHeader("Content-Encoding"); + + +### response.write(chunk, [encoding]) + +If this method is called and `response.writeHead()` has not been called, it will +switch to implicit header mode and flush the implicit headers. + +This sends a chunk of the response body. This method may +be called multiple times to provide successive parts of the body. + +`chunk` can be a string or a buffer. If `chunk` is a string, +the second parameter specifies how to encode it into a byte stream. +By default the `encoding` is `'utf8'`. + +**Note**: This is the raw HTTP body and has nothing to do with +higher-level multi-part body encodings that may be used. + +The first time `response.write()` is called, it will send the buffered +header information and the first body to the client. The second time +`response.write()` is called, Node assumes you're going to be streaming +data, and sends that separately. That is, the response is buffered up to the +first chunk of body. + +### response.addTrailers(headers) + +This method adds HTTP trailing headers (a header but at the end of the +message) to the response. + +Trailers will **only** be emitted if chunked encoding is used for the +response; if it is not (e.g., if the request was HTTP/1.0), they will +be silently discarded. + +Note that HTTP requires the `Trailer` header to be sent if you intend to +emit trailers, with a list of the header fields in its value. E.g., + + response.writeHead(200, { 'Content-Type': 'text/plain', + 'Trailer': 'Content-MD5' }); + response.write(fileData); + response.addTrailers({'Content-MD5': "7895bf4b8828b55ceaf47747b4bca667"}); + response.end(); + + +### response.end([data], [encoding]) + +This method signals to the server that all of the response headers and body +has been sent; that server should consider this message complete. +The method, `response.end()`, MUST be called on each +response. + +If `data` is specified, it is equivalent to calling `response.write(data, encoding)` +followed by `response.end()`. + + +## http.request(options, callback) + +Node maintains several connections per server to make HTTP requests. +This function allows one to transparently issue requests. `options` align +with [url.parse()](url.html#url.parse). + +Options: + +- `host`: A domain name or IP address of the server to issue the request to. + Defaults to `'localhost'`. +- `hostname`: To support `url.parse()` `hostname` is preferred over `host` +- `port`: Port of remote server. Defaults to 80. +- `localAddress`: Local interface to bind for network connections. +- `socketPath`: Unix Domain Socket (use one of host:port or socketPath) +- `method`: A string specifying the HTTP request method. Defaults to `'GET'`. +- `path`: Request path. Defaults to `'/'`. Should include query string if any. + E.G. `'/index.html?page=12'` +- `headers`: An object containing request headers. +- `auth`: Basic authentication i.e. `'user:password'` to compute an + Authorization header. +- `agent`: Controls [Agent](#http.Agent) behavior. When an Agent is used + request will default to `Connection: keep-alive`. Possible values: + - `undefined` (default): use [global Agent](#http.globalAgent) for this host + and port. + - `Agent` object: explicitly use the passed in `Agent`. + - `false`: opts out of connection pooling with an Agent, defaults request to + `Connection: close`. + +`http.request()` returns an instance of the `http.ClientRequest` +class. The `ClientRequest` instance is a writable stream. If one needs to +upload a file with a POST request, then write to the `ClientRequest` object. + +Example: + + var options = { + host: 'www.google.com', + port: 80, + path: '/upload', + method: 'POST' + }; + + var req = http.request(options, function(res) { + console.log('STATUS: ' + res.statusCode); + console.log('HEADERS: ' + JSON.stringify(res.headers)); + res.setEncoding('utf8'); + res.on('data', function (chunk) { + console.log('BODY: ' + chunk); + }); + }); + + req.on('error', function(e) { + console.log('problem with request: ' + e.message); + }); + + // write data to request body + req.write('data\n'); + req.write('data\n'); + req.end(); + +Note that in the example `req.end()` was called. With `http.request()` one +must always call `req.end()` to signify that you're done with the request - +even if there is no data being written to the request body. + +If any error is encountered during the request (be that with DNS resolution, +TCP level errors, or actual HTTP parse errors) an `'error'` event is emitted +on the returned request object. + +There are a few special headers that should be noted. + +* Sending a 'Connection: keep-alive' will notify Node that the connection to + the server should be persisted until the next request. + +* Sending a 'Content-length' header will disable the default chunked encoding. + +* Sending an 'Expect' header will immediately send the request headers. + Usually, when sending 'Expect: 100-continue', you should both set a timeout + and listen for the `continue` event. See RFC2616 Section 8.2.3 for more + information. + +* Sending an Authorization header will override using the `auth` option + to compute basic authentication. + +## http.get(options, callback) + +Since most requests are GET requests without bodies, Node provides this +convenience method. The only difference between this method and `http.request()` is +that it sets the method to GET and calls `req.end()` automatically. + +Example: + + var options = { + host: 'www.google.com', + port: 80, + path: '/index.html' + }; + + http.get(options, function(res) { + console.log("Got response: " + res.statusCode); + }).on('error', function(e) { + console.log("Got error: " + e.message); + }); + + +## Class: http.Agent + +In node 0.5.3+ there is a new implementation of the HTTP Agent which is used +for pooling sockets used in HTTP client requests. + +Previously, a single agent instance help the pool for single host+port. The +current implementation now holds sockets for any number of hosts. + +The current HTTP Agent also defaults client requests to using +Connection:keep-alive. If no pending HTTP requests are waiting on a socket +to become free the socket is closed. This means that node's pool has the +benefit of keep-alive when under load but still does not require developers +to manually close the HTTP clients using keep-alive. + +Sockets are removed from the agent's pool when the socket emits either a +"close" event or a special "agentRemove" event. This means that if you intend +to keep one HTTP request open for a long time and don't want it to stay in the +pool you can do something along the lines of: + + http.get(options, function(res) { + // Do stuff + }).on("socket", function (socket) { + socket.emit("agentRemove"); + }); + +Alternatively, you could just opt out of pooling entirely using `agent:false`: + + http.get({host:'localhost', port:80, path:'/', agent:false}, function (res) { + // Do stuff + }) + +### agent.maxSockets + +By default set to 5. Determines how many concurrent sockets the agent can have +open per host. + +### agent.sockets + +An object which contains arrays of sockets currently in use by the Agent. Do not +modify. + +### agent.requests + +An object which contains queues of requests that have not yet been assigned to +sockets. Do not modify. + +## http.globalAgent + +Global instance of Agent which is used as the default for all http client +requests. + + +## Class: http.ClientRequest + +This object is created internally and returned from `http.request()`. It +represents an _in-progress_ request whose header has already been queued. The +header is still mutable using the `setHeader(name, value)`, `getHeader(name)`, +`removeHeader(name)` API. The actual header will be sent along with the first +data chunk or when closing the connection. + +To get the response, add a listener for `'response'` to the request object. +`'response'` will be emitted from the request object when the response +headers have been received. The `'response'` event is executed with one +argument which is an instance of `http.ClientResponse`. + +During the `'response'` event, one can add listeners to the +response object; particularly to listen for the `'data'` event. Note that +the `'response'` event is called before any part of the response body is received, +so there is no need to worry about racing to catch the first part of the +body. As long as a listener for `'data'` is added during the `'response'` +event, the entire body will be caught. + + + // Good + request.on('response', function (response) { + response.on('data', function (chunk) { + console.log('BODY: ' + chunk); + }); + }); + + // Bad - misses all or part of the body + request.on('response', function (response) { + setTimeout(function () { + response.on('data', function (chunk) { + console.log('BODY: ' + chunk); + }); + }, 10); + }); + +Note: Node does not check whether Content-Length and the length of the body +which has been transmitted are equal or not. + +The request implements the [Writable Stream](stream.html#writable_stream) +interface. This is an `EventEmitter` with the following events: + +### Event 'response' + +`function (response) { }` + +Emitted when a response is received to this request. This event is emitted only once. The +`response` argument will be an instance of `http.ClientResponse`. + +Options: + +- `host`: A domain name or IP address of the server to issue the request to. +- `port`: Port of remote server. +- `socketPath`: Unix Domain Socket (use one of host:port or socketPath) + +### Event: 'socket' + +`function (socket) { }` + +Emitted after a socket is assigned to this request. + +### Event: 'connect' + +`function (response, socket, head) { }` + +Emitted each time a server responds to a request with a CONNECT method. If this +event isn't being listened for, clients receiving a CONNECT method will have +their connections closed. + +A client server pair that show you how to listen for the `connect` event. + + var http = require('http'); + var net = require('net'); + var url = require('url'); + + // Create an HTTP tunneling proxy + var proxy = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('okay'); + }); + proxy.on('connect', function(req, cltSocket, head) { + // connect to an origin server + var srvUrl = url.parse('http://' + req.url); + var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, function() { + cltSocket.write('HTTP/1.1 200 Connection Established\r\n' + + 'Proxy-agent: Node-Proxy\r\n' + + '\r\n'); + srvSocket.write(head); + srvSocket.pipe(cltSocket); + cltSocket.pipe(srvSocket); + }); + }); + + // now that proxy is running + proxy.listen(1337, '127.0.0.1', function() { + + // make a request to a tunneling proxy + var options = { + port: 1337, + host: '127.0.0.1', + method: 'CONNECT', + path: 'www.google.com:80' + }; + + var req = http.request(options); + req.end(); + + req.on('connect', function(res, socket, head) { + console.log('got connected!'); + + // make a request over an HTTP tunnel + socket.write('GET / HTTP/1.1\r\n' + + 'Host: www.google.com:80\r\n' + + 'Connection: close\r\n' + + '\r\n'); + socket.on('data', function(chunk) { + console.log(chunk.toString()); + }); + socket.on('end', function() { + proxy.close(); + }); + }); + }); + +### Event: 'upgrade' + +`function (response, socket, head) { }` + +Emitted each time a server responds to a request with an upgrade. If this +event isn't being listened for, clients receiving an upgrade header will have +their connections closed. + +A client server pair that show you how to listen for the `upgrade` event. + + var http = require('http'); + + // Create an HTTP server + var srv = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('okay'); + }); + srv.on('upgrade', function(req, socket, head) { + socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + + 'Upgrade: WebSocket\r\n' + + 'Connection: Upgrade\r\n' + + '\r\n'); + + socket.pipe(socket); // echo back + }); + + // now that server is running + srv.listen(1337, '127.0.0.1', function() { + + // make a request + var options = { + port: 1337, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket' + } + }; + + var req = http.request(options); + req.end(); + + req.on('upgrade', function(res, socket, upgradeHead) { + console.log('got upgraded!'); + socket.end(); + process.exit(0); + }); + }); + +### Event: 'continue' + +`function () { }` + +Emitted when the server sends a '100 Continue' HTTP response, usually because +the request contained 'Expect: 100-continue'. This is an instruction that +the client should send the request body. + +### request.write(chunk, [encoding]) + +Sends a chunk of the body. By calling this method +many times, the user can stream a request body to a +server--in that case it is suggested to use the +`['Transfer-Encoding', 'chunked']` header line when +creating the request. + +The `chunk` argument should be a [buffer](buffer.html) or a string. + +The `encoding` argument is optional and only applies when `chunk` is a string. +Defaults to `'utf8'`. + + +### request.end([data], [encoding]) + +Finishes sending the request. If any parts of the body are +unsent, it will flush them to the stream. If the request is +chunked, this will send the terminating `'0\r\n\r\n'`. + +If `data` is specified, it is equivalent to calling +`request.write(data, encoding)` followed by `request.end()`. + +### request.abort() + +Aborts a request. (New since v0.3.8.) + +### request.setTimeout(timeout, [callback]) + +Once a socket is assigned to this request and is connected +[socket.setTimeout(timeout, [callback])](net.html#socket.setTimeout) +will be called. + +### request.setNoDelay([noDelay]) + +Once a socket is assigned to this request and is connected +[socket.setNoDelay(noDelay)](net.html#socket.setNoDelay) +will be called. + +### request.setSocketKeepAlive([enable], [initialDelay]) + +Once a socket is assigned to this request and is connected +[socket.setKeepAlive(enable, [initialDelay])](net.html#socket.setKeepAlive) +will be called. + +## http.ClientResponse + +This object is created when making a request with `http.request()`. It is +passed to the `'response'` event of the request object. + +The response implements the [Readable Stream](stream.html#readable_stream) +interface. This is an `EventEmitter` with the following events: + + +### Event: 'data' + +`function (chunk) { }` + +Emitted when a piece of the message body is received. + +Note that the __data will be lost__ if there is no listener when a +`ClientResponse` emits a `'data'` event. + + +### Event: 'end' + +`function () { }` + +Emitted exactly once for each message. No arguments. After +emitted no other events will be emitted on the response. + +### Event: 'close' + +`function (err) { }` + +Indicates that the underlaying connection was terminated before +`end` event was emitted. +See [http.ServerRequest](#http.ServerRequest)'s `'close'` event for more +information. + +### response.statusCode + +The 3-digit HTTP response status code. E.G. `404`. + +### response.httpVersion + +The HTTP version of the connected-to server. Probably either +`'1.1'` or `'1.0'`. +Also `response.httpVersionMajor` is the first integer and +`response.httpVersionMinor` is the second. + +### response.headers + +The response headers object. + +### response.trailers + +The response trailers object. Only populated after the 'end' event. + +### response.setEncoding([encoding]) + +Set the encoding for the response body. Either `'utf8'`, `'ascii'`, or +`'base64'`. Defaults to `null`, which means that the `'data'` event will emit +a `Buffer` object. + +### response.pause() + +Pauses response from emitting events. Useful to throttle back a download. + +### response.resume() + +Resumes a paused response. diff --git a/doc/api/https.markdown b/doc/api/https.markdown new file mode 100644 index 0000000..9f51e5f --- /dev/null +++ b/doc/api/https.markdown @@ -0,0 +1,173 @@ +# HTTPS + + Stability: 3 - Stable + +HTTPS is the HTTP protocol over TLS/SSL. In Node this is implemented as a +separate module. + +## Class: https.Server + +This class is a subclass of `tls.Server` and emits events same as +`http.Server`. See `http.Server` for more information. + +## https.createServer(options, [requestListener]) + +Returns a new HTTPS web server object. The `options` is similar to +[tls.createServer()](tls.html#tls.createServer). The `requestListener` is +a function which is automatically added to the `'request'` event. + +Example: + + // curl -k https://localhost:8000/ + var https = require('https'); + var fs = require('fs'); + + var options = { + key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), + cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem') + }; + + https.createServer(options, function (req, res) { + res.writeHead(200); + res.end("hello world\n"); + }).listen(8000); + + +## https.request(options, callback) + +Makes a request to a secure web server. +All options from [http.request()](http.html#http.request) are valid. + +Example: + + var https = require('https'); + + var options = { + host: 'encrypted.google.com', + port: 443, + path: '/', + method: 'GET' + }; + + var req = https.request(options, function(res) { + console.log("statusCode: ", res.statusCode); + console.log("headers: ", res.headers); + + res.on('data', function(d) { + process.stdout.write(d); + }); + }); + req.end(); + + req.on('error', function(e) { + console.error(e); + }); + +The options argument has the following options + +- host: IP or domain of host to make request to. Defaults to `'localhost'`. +- port: port of host to request to. Defaults to 443. +- path: Path to request. Default `'/'`. +- method: HTTP request method. Default `'GET'`. + +- `host`: A domain name or IP address of the server to issue the request to. + Defaults to `'localhost'`. +- `hostname`: To support `url.parse()` `hostname` is preferred over `host` +- `port`: Port of remote server. Defaults to 443. +- `method`: A string specifying the HTTP request method. Defaults to `'GET'`. +- `path`: Request path. Defaults to `'/'`. Should include query string if any. + E.G. `'/index.html?page=12'` +- `headers`: An object containing request headers. +- `auth`: Basic authentication i.e. `'user:password'` to compute an + Authorization header. +- `agent`: Controls [Agent](#https.Agent) behavior. When an Agent is + used request will default to `Connection: keep-alive`. Possible values: + - `undefined` (default): use [globalAgent](#https.globalAgent) for this + host and port. + - `Agent` object: explicitly use the passed in `Agent`. + - `false`: opts out of connection pooling with an Agent, defaults request to + `Connection: close`. + +The following options from [tls.connect()](tls.html#tls.connect) can also be +specified. However, a [globalAgent](#https.globalAgent) silently ignores these. + +- `key`: Private key to use for SSL. Default `null`. +- `passphrase`: A string of passphrase for the private key. Default `null`. +- `cert`: Public x509 certificate to use. Default `null`. +- `ca`: An authority certificate or array of authority certificates to check + the remote host against. +- `ciphers`: A string describing the ciphers to use or exclude. Consult + <http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT> for + details on the format. +- `rejectUnauthorized`: If `true`, the server certificate is verified against + the list of supplied CAs. An `'error'` event is emitted if verification + fails. Verification happens at the connection level, *before* the HTTP + request is sent. Default `false`. + +In order to specify these options, use a custom `Agent`. + +Example: + + var options = { + host: 'encrypted.google.com', + port: 443, + path: '/', + method: 'GET', + key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), + cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem') + }; + options.agent = new https.Agent(options); + + var req = https.request(options, function(res) { + ... + } + +Or does not use an `Agent`. + +Example: + + var options = { + host: 'encrypted.google.com', + port: 443, + path: '/', + method: 'GET', + key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), + cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'), + agent: false + }; + + var req = https.request(options, function(res) { + ... + } + +## https.get(options, callback) + +Like `http.get()` but for HTTPS. + +Example: + + var https = require('https'); + + https.get({ host: 'encrypted.google.com', path: '/' }, function(res) { + console.log("statusCode: ", res.statusCode); + console.log("headers: ", res.headers); + + res.on('data', function(d) { + process.stdout.write(d); + }); + + }).on('error', function(e) { + console.error(e); + }); + + +## Class: https.Agent + +An Agent object for HTTPS similar to [http.Agent](http.html#http.Agent). +See [https.request()](#https.request) for more information. + + +## https.globalAgent + +Global instance of [https.Agent](#https.Agent) which is used as the default +for all HTTPS client requests. diff --git a/doc/api/index.markdown b/doc/api/index.markdown new file mode 100644 index 0000000..1a77450 --- /dev/null +++ b/doc/api/index.markdown @@ -0,0 +1 @@ +@include _toc.markdown diff --git a/doc/api/modules.markdown b/doc/api/modules.markdown new file mode 100644 index 0000000..d90cf57 --- /dev/null +++ b/doc/api/modules.markdown @@ -0,0 +1,461 @@ +# Modules + + Stability: 5 - Locked + +<!--name=module--> + +Node has a simple module loading system. In Node, files and modules are in +one-to-one correspondence. As an example, `foo.js` loads the module +`circle.js` in the same directory. + +The contents of `foo.js`: + + var circle = require('./circle.js'); + console.log( 'The area of a circle of radius 4 is ' + + circle.area(4)); + +The contents of `circle.js`: + + var PI = Math.PI; + + exports.area = function (r) { + return PI * r * r; + }; + + exports.circumference = function (r) { + return 2 * PI * r; + }; + +The module `circle.js` has exported the functions `area()` and +`circumference()`. To export an object, add to the special `exports` +object. + +Variables +local to the module will be private. In this example the variable `PI` is +private to `circle.js`. + +The module system is implemented in the `require("module")` module. + +## Cycles + +<!--type=misc--> + +When there are circular `require()` calls, a module might not be +done being executed when it is returned. + +Consider this situation: + +`a.js`: + + console.log('a starting'); + exports.done = false; + var b = require('./b.js'); + console.log('in a, b.done = %j', b.done); + exports.done = true; + console.log('a done'); + +`b.js`: + + console.log('b starting'); + exports.done = false; + var a = require('./a.js'); + console.log('in b, a.done = %j', a.done); + exports.done = true; + console.log('b done'); + +`main.js`: + + console.log('main starting'); + var a = require('./a.js'); + var b = require('./b.js'); + console.log('in main, a.done=%j, b.done=%j', a.done, b.done); + +When `main.js` loads `a.js`, then `a.js` in turn loads `b.js`. At that +point, `b.js` tries to load `a.js`. In order to prevent an infinite +loop an **unfinished copy** of the `a.js` exports object is returned to the +`b.js` module. `b.js` then finishes loading, and its exports object is +provided to the `a.js` module. + +By the time `main.js` has loaded both modules, they're both finished. +The output of this program would thus be: + + $ node main.js + main starting + a starting + b starting + in b, a.done = false + b done + in a, b.done = true + a done + in main, a.done=true, b.done=true + +If you have cyclic module dependencies in your program, make sure to +plan accordingly. + +## Core Modules + +<!--type=misc--> + +Node has several modules compiled into the binary. These modules are +described in greater detail elsewhere in this documentation. + +The core modules are defined in node's source in the `lib/` folder. + +Core modules are always preferentially loaded if their identifier is +passed to `require()`. For instance, `require('http')` will always +return the built in HTTP module, even if there is a file by that name. + +## File Modules + +<!--type=misc--> + +If the exact filename is not found, then node will attempt to load the +required filename with the added extension of `.js`, `.json`, and then `.node`. + +`.js` files are interpreted as JavaScript text files, and `.json` files are +parsed as JSON text files. `.node` files are interpreted as compiled addon +modules loaded with `dlopen`. + +A module prefixed with `'/'` is an absolute path to the file. For +example, `require('/home/marco/foo.js')` will load the file at +`/home/marco/foo.js`. + +A module prefixed with `'./'` is relative to the file calling `require()`. +That is, `circle.js` must be in the same directory as `foo.js` for +`require('./circle')` to find it. + +Without a leading '/' or './' to indicate a file, the module is either a +"core module" or is loaded from a `node_modules` folder. + +If the given path does not exist, `require()` will throw an Error with its +`code` property set to `'MODULE_NOT_FOUND'`. + +## Loading from `node_modules` Folders + +<!--type=misc--> + +If the module identifier passed to `require()` is not a native module, +and does not begin with `'/'`, `'../'`, or `'./'`, then node starts at the +parent directory of the current module, and adds `/node_modules`, and +attempts to load the module from that location. + +If it is not found there, then it moves to the parent directory, and so +on, until the root of the tree is reached. + +For example, if the file at `'/home/ry/projects/foo.js'` called +`require('bar.js')`, then node would look in the following locations, in +this order: + +* `/home/ry/projects/node_modules/bar.js` +* `/home/ry/node_modules/bar.js` +* `/home/node_modules/bar.js` +* `/node_modules/bar.js` + +This allows programs to localize their dependencies, so that they do not +clash. + +## Folders as Modules + +<!--type=misc--> + +It is convenient to organize programs and libraries into self-contained +directories, and then provide a single entry point to that library. +There are three ways in which a folder may be passed to `require()` as +an argument. + +The first is to create a `package.json` file in the root of the folder, +which specifies a `main` module. An example package.json file might +look like this: + + { "name" : "some-library", + "main" : "./lib/some-library.js" } + +If this was in a folder at `./some-library`, then +`require('./some-library')` would attempt to load +`./some-library/lib/some-library.js`. + +This is the extent of Node's awareness of package.json files. + +If there is no package.json file present in the directory, then node +will attempt to load an `index.js` or `index.node` file out of that +directory. For example, if there was no package.json file in the above +example, then `require('./some-library')` would attempt to load: + +* `./some-library/index.js` +* `./some-library/index.node` + +## Caching + +<!--type=misc--> + +Modules are cached after the first time they are loaded. This means +(among other things) that every call to `require('foo')` will get +exactly the same object returned, if it would resolve to the same file. + +Multiple calls to `require('foo')` may not cause the module code to be +executed multiple times. This is an important feature. With it, +"partially done" objects can be returned, thus allowing transitive +dependencies to be loaded even when they would cause cycles. + +If you want to have a module execute code multiple times, then export a +function, and call that function. + +### Module Caching Caveats + +<!--type=misc--> + +Modules are cached based on their resolved filename. Since modules may +resolve to a different filename based on the location of the calling +module (loading from `node_modules` folders), it is not a *guarantee* +that `require('foo')` will always return the exact same object, if it +would resolve to different files. + +## The `module` Object + +<!-- type=var --> +<!-- name=module --> + +* {Object} + +In each module, the `module` free variable is a reference to the object +representing the current module. In particular +`module.exports` is the same as the `exports` object. +`module` isn't actually a global but rather local to each module. + +### module.exports + +* {Object} + +The `exports` object is created by the Module system. Sometimes this is not +acceptable, many want their module to be an instance of some class. To do this +assign the desired export object to `module.exports`. For example suppose we +were making a module called `a.js` + + var EventEmitter = require('events').EventEmitter; + + module.exports = new EventEmitter(); + + // Do some work, and after some time emit + // the 'ready' event from the module itself. + setTimeout(function() { + module.exports.emit('ready'); + }, 1000); + +Then in another file we could do + + var a = require('./a'); + a.on('ready', function() { + console.log('module a is ready'); + }); + + +Note that assignment to `module.exports` must be done immediately. It cannot be +done in any callbacks. This does not work: + +x.js: + + setTimeout(function() { + module.exports = { a: "hello" }; + }, 0); + +y.js: + + var x = require('./x'); + console.log(x.a); + + +### module.require(id) + +* `id` {String} +* Return: {Object} `exports` from the resolved module + +The `module.require` method provides a way to load a module as if +`require()` was called from the original module. + +Note that in order to do this, you must get a reference to the `module` +object. Since `require()` returns the `exports`, and the `module` is +typically *only* available within a specific module's code, it must be +explicitly exported in order to be used. + + +### module.id + +* {String} + +The identifier for the module. Typically this is the fully resolved +filename. + + +### module.filename + +* {String} + +The fully resolved filename to the module. + + +### module.loaded + +* {Boolean} + +Whether or not the module is done loading, or is in the process of +loading. + + +### module.parent + +* {Module Object} + +The module that required this one. + + +### module.children + +* {Array} + +The module objects required by this one. + + + +## All Together... + +<!-- type=misc --> + +To get the exact filename that will be loaded when `require()` is called, use +the `require.resolve()` function. + +Putting together all of the above, here is the high-level algorithm +in pseudocode of what require.resolve does: + + require(X) from module at path Y + 1. If X is a core module, + a. return the core module + b. STOP + 2. If X begins with './' or '/' or '../' + a. LOAD_AS_FILE(Y + X) + b. LOAD_AS_DIRECTORY(Y + X) + 3. LOAD_NODE_MODULES(X, dirname(Y)) + 4. THROW "not found" + + LOAD_AS_FILE(X) + 1. If X is a file, load X as JavaScript text. STOP + 2. If X.js is a file, load X.js as JavaScript text. STOP + 3. If X.node is a file, load X.node as binary addon. STOP + + LOAD_AS_DIRECTORY(X) + 1. If X/package.json is a file, + a. Parse X/package.json, and look for "main" field. + b. let M = X + (json main field) + c. LOAD_AS_FILE(M) + 2. If X/index.js is a file, load X/index.js as JavaScript text. STOP + 3. If X/index.node is a file, load X/index.node as binary addon. STOP + + LOAD_NODE_MODULES(X, START) + 1. let DIRS=NODE_MODULES_PATHS(START) + 2. for each DIR in DIRS: + a. LOAD_AS_FILE(DIR/X) + b. LOAD_AS_DIRECTORY(DIR/X) + + NODE_MODULES_PATHS(START) + 1. let PARTS = path split(START) + 2. let ROOT = index of first instance of "node_modules" in PARTS, or 0 + 3. let I = count of PARTS - 1 + 4. let DIRS = [] + 5. while I > ROOT, + a. if PARTS[I] = "node_modules" CONTINUE + c. DIR = path join(PARTS[0 .. I] + "node_modules") + b. DIRS = DIRS + DIR + c. let I = I - 1 + 6. return DIRS + +## Loading from the global folders + +<!-- type=misc --> + +If the `NODE_PATH` environment variable is set to a colon-delimited list +of absolute paths, then node will search those paths for modules if they +are not found elsewhere. (Note: On Windows, `NODE_PATH` is delimited by +semicolons instead of colons.) + +Additionally, node will search in the following locations: + +* 1: `$HOME/.node_modules` +* 2: `$HOME/.node_libraries` +* 3: `$PREFIX/lib/node` + +Where `$HOME` is the user's home directory, and `$PREFIX` is node's +configured `installPrefix`. + +These are mostly for historic reasons. You are highly encouraged to +place your dependencies locally in `node_modules` folders. They will be +loaded faster, and more reliably. + +## Accessing the main module + +<!-- type=misc --> + +When a file is run directly from Node, `require.main` is set to its +`module`. That means that you can determine whether a file has been run +directly by testing + + require.main === module + +For a file `foo.js`, this will be `true` if run via `node foo.js`, but +`false` if run by `require('./foo')`. + +Because `module` provides a `filename` property (normally equivalent to +`__filename`), the entry point of the current application can be obtained +by checking `require.main.filename`. + +## Addenda: Package Manager Tips + +<!-- type=misc --> + +The semantics of Node's `require()` function were designed to be general +enough to support a number of sane directory structures. Package manager +programs such as `dpkg`, `rpm`, and `npm` will hopefully find it possible to +build native packages from Node modules without modification. + +Below we give a suggested directory structure that could work: + +Let's say that we wanted to have the folder at +`/usr/lib/node/<some-package>/<some-version>` hold the contents of a +specific version of a package. + +Packages can depend on one another. In order to install package `foo`, you +may have to install a specific version of package `bar`. The `bar` package +may itself have dependencies, and in some cases, these dependencies may even +collide or form cycles. + +Since Node looks up the `realpath` of any modules it loads (that is, +resolves symlinks), and then looks for their dependencies in the +`node_modules` folders as described above, this situation is very simple to +resolve with the following architecture: + +* `/usr/lib/node/foo/1.2.3/` - Contents of the `foo` package, version 1.2.3. +* `/usr/lib/node/bar/4.3.2/` - Contents of the `bar` package that `foo` + depends on. +* `/usr/lib/node/foo/1.2.3/node_modules/bar` - Symbolic link to + `/usr/lib/node/bar/4.3.2/`. +* `/usr/lib/node/bar/4.3.2/node_modules/*` - Symbolic links to the packages + that `bar` depends on. + +Thus, even if a cycle is encountered, or if there are dependency +conflicts, every module will be able to get a version of its dependency +that it can use. + +When the code in the `foo` package does `require('bar')`, it will get the +version that is symlinked into `/usr/lib/node/foo/1.2.3/node_modules/bar`. +Then, when the code in the `bar` package calls `require('quux')`, it'll get +the version that is symlinked into +`/usr/lib/node/bar/4.3.2/node_modules/quux`. + +Furthermore, to make the module lookup process even more optimal, rather +than putting packages directly in `/usr/lib/node`, we could put them in +`/usr/lib/node_modules/<name>/<version>`. Then node will not bother +looking for missing dependencies in `/usr/node_modules` or `/node_modules`. + +In order to make modules available to the node REPL, it might be useful to +also add the `/usr/lib/node_modules` folder to the `$NODE_PATH` environment +variable. Since the module lookups using `node_modules` folders are all +relative, and based on the real path of the files making the calls to +`require()`, the packages themselves can be anywhere. diff --git a/doc/api/net.markdown b/doc/api/net.markdown new file mode 100644 index 0000000..7fb07df --- /dev/null +++ b/doc/api/net.markdown @@ -0,0 +1,467 @@ +# net + + Stability: 3 - Stable + +The `net` module provides you with an asynchronous network wrapper. It contains +methods for creating both servers and clients (called streams). You can include +this module with `require('net');` + +## net.createServer([options], [connectionListener]) + +Creates a new TCP server. The `connectionListener` argument is +automatically set as a listener for the ['connection'](#event_connection_) +event. + +`options` is an object with the following defaults: + + { allowHalfOpen: false + } + +If `allowHalfOpen` is `true`, then the socket won't automatically send a FIN +packet when the other end of the socket sends a FIN packet. The socket becomes +non-readable, but still writable. You should call the `end()` method explicitly. +See ['end'](#event_end_) event for more information. + +Here is an example of a echo server which listens for connections +on port 8124: + + var net = require('net'); + var server = net.createServer(function(c) { //'connection' listener + console.log('server connected'); + c.on('end', function() { + console.log('server disconnected'); + }); + c.write('hello\r\n'); + c.pipe(c); + }); + server.listen(8124, function() { //'listening' listener + console.log('server bound'); + }); + +Test this by using `telnet`: + + telnet localhost 8124 + +To listen on the socket `/tmp/echo.sock` the third line from the last would +just be changed to + + server.listen('/tmp/echo.sock', function() { //'listening' listener + +Use `nc` to connect to a UNIX domain socket server: + + nc -U /tmp/echo.sock + +## net.connect(options, [connectionListener]) +## net.createConnection(options, [connectionListener]) + +Constructs a new socket object and opens the socket to the given location. +When the socket is established, the ['connect'](#event_connect_) event will be +emitted. + +For TCP sockets, `options` argument should be an object which specifies: + + - `port`: Port the client should connect to (Required). + + - `host`: Host the client should connect to. Defaults to `'localhost'`. + + - `localAddress`: Local interface to bind to for network connections. + +For UNIX domain sockets, `options` argument should be an object which specifies: + + - `path`: Path the client should connect to (Required). + +Common options are: + + - `allowHalfOpen`: if `true`, the socket won't automatically send + a FIN packet when the other end of the socket sends a FIN packet. + Defaults to `false`. + See ['end'](#event_end_) event for more information. + +The `connectListener` parameter will be added as an listener for the +['connect'](#event_connect_) event. + +Here is an example of a client of echo server as described previously: + + var net = require('net'); + var client = net.connect({port: 8124}, + function() { //'connect' listener + console.log('client connected'); + client.write('world!\r\n'); + }); + client.on('data', function(data) { + console.log(data.toString()); + client.end(); + }); + client.on('end', function() { + console.log('client disconnected'); + }); + +To connect on the socket `/tmp/echo.sock` the second line would just be +changed to + + var client = net.connect({path: '/tmp/echo.sock'}, + +## net.connect(port, [host], [connectListener]) +## net.createConnection(port, [host], [connectListener]) + +Creates a TCP connection to `port` on `host`. If `host` is omitted, +`'localhost'` will be assumed. +The `connectListener` parameter will be added as an listener for the +['connect'](#event_connect_) event. + +## net.connect(path, [connectListener]) +## net.createConnection(path, [connectListener]) + +Creates unix socket connection to `path`. +The `connectListener` parameter will be added as an listener for the +['connect'](#event_connect_) event. + +## Class: net.Server + +This class is used to create a TCP or UNIX server. +A server is a `net.Socket` that can listen for new incoming connections. + +### server.listen(port, [host], [backlog], [listeningListener]) + +Begin accepting connections on the specified `port` and `host`. If the +`host` is omitted, the server will accept connections directed to any +IPv4 address (`INADDR_ANY`). A port value of zero will assign a random port. + +Backlog is the maximum length of the queue of pending connections. +The actual length will be determined by your OS through sysctl settings such as +`tcp_max_syn_backlog` and `somaxconn` on linux. The default value of this +parameter is 511 (not 512). + +This function is asynchronous. When the server has been bound, +['listening'](#event_listening_) event will be emitted. +the last parameter `listeningListener` will be added as an listener for the +['listening'](#event_listening_) event. + +One issue some users run into is getting `EADDRINUSE` errors. This means that +another server is already running on the requested port. One way of handling this +would be to wait a second and then try again. This can be done with + + server.on('error', function (e) { + if (e.code == 'EADDRINUSE') { + console.log('Address in use, retrying...'); + setTimeout(function () { + server.close(); + server.listen(PORT, HOST); + }, 1000); + } + }); + +(Note: All sockets in Node set `SO_REUSEADDR` already) + + +### server.listen(path, [listeningListener]) + +Start a UNIX socket server listening for connections on the given `path`. + +This function is asynchronous. When the server has been bound, +['listening'](#event_listening_) event will be emitted. +the last parameter `listeningListener` will be added as an listener for the +['listening'](#event_listening_) event. + +### server.close([cb]) + +Stops the server from accepting new connections and keeps existing +connections. This function is asynchronous, the server is finally +closed when all connections are ended and the server emits a `'close'` +event. Optionally, you can pass a callback to listen for the `'close'` +event. + +### server.address() + +Returns the bound address, the address family name and port of the server +as reported by the operating system. +Useful to find which port was assigned when giving getting an OS-assigned address. +Returns an object with three properties, e.g. +`{ port: 12346, family: 'IPv4', address: '127.0.0.1' }` + +Example: + + var server = net.createServer(function (socket) { + socket.end("goodbye\n"); + }); + + // grab a random port. + server.listen(function() { + address = server.address(); + console.log("opened server on %j", address); + }); + +Don't call `server.address()` until the `'listening'` event has been emitted. + +### server.maxConnections + +Set this property to reject connections when the server's connection count gets +high. + +It is not recommended to use this option once a socket has been sent to a child +with `child_process.fork()`. + +### server.connections + +The number of concurrent connections on the server. + +This becomes `null` when sending a socket to a child with `child_process.fork()`. + +`net.Server` is an `EventEmitter` with the following events: + +### Event: 'listening' + +Emitted when the server has been bound after calling `server.listen`. + +### Event: 'connection' + +* {Socket object} The connection object + +Emitted when a new connection is made. `socket` is an instance of +`net.Socket`. + +### Event: 'close' + +Emitted when the server closes. Note that if connections exist, this +event is not emitted until all connections are ended. + +### Event: 'error' + +* {Error Object} + +Emitted when an error occurs. The `'close'` event will be called directly +following this event. See example in discussion of `server.listen`. + +## Class: net.Socket + +This object is an abstraction of a TCP or UNIX socket. `net.Socket` +instances implement a duplex Stream interface. They can be created by the +user and used as a client (with `connect()`) or they can be created by Node +and passed to the user through the `'connection'` event of a server. + +### new net.Socket([options]) + +Construct a new socket object. + +`options` is an object with the following defaults: + + { fd: null + type: null + allowHalfOpen: false + } + +`fd` allows you to specify the existing file descriptor of socket. `type` +specified underlying protocol. It can be `'tcp4'`, `'tcp6'`, or `'unix'`. +About `allowHalfOpen`, refer to `createServer()` and `'end'` event. + +### socket.connect(port, [host], [connectListener]) +### socket.connect(path, [connectListener]) + +Opens the connection for a given socket. If `port` and `host` are given, +then the socket will be opened as a TCP socket, if `host` is omitted, +`localhost` will be assumed. If a `path` is given, the socket will be +opened as a unix socket to that path. + +Normally this method is not needed, as `net.createConnection` opens the +socket. Use this only if you are implementing a custom Socket or if a +Socket is closed and you want to reuse it to connect to another server. + +This function is asynchronous. When the ['connect'](#event_connect_) event is +emitted the socket is established. If there is a problem connecting, the +`'connect'` event will not be emitted, the `'error'` event will be emitted with +the exception. + +The `connectListener` parameter will be added as an listener for the +['connect'](#event_connect_) event. + + +### socket.bufferSize + +`net.Socket` has the property that `socket.write()` always works. This is to +help users get up and running quickly. The computer cannot always keep up +with the amount of data that is written to a socket - the network connection +simply might be too slow. Node will internally queue up the data written to a +socket and send it out over the wire when it is possible. (Internally it is +polling on the socket's file descriptor for being writable). + +The consequence of this internal buffering is that memory may grow. This +property shows the number of characters currently buffered to be written. +(Number of characters is approximately equal to the number of bytes to be +written, but the buffer may contain strings, and the strings are lazily +encoded, so the exact number of bytes is not known.) + +Users who experience large or growing `bufferSize` should attempt to +"throttle" the data flows in their program with `pause()` and `resume()`. + + +### socket.setEncoding([encoding]) + +Set the encoding for the socket as a Readable Stream. See +[stream.setEncoding()](stream.html#stream_stream_setencoding_encoding) +for more information. + +### socket.write(data, [encoding], [callback]) + +Sends data on the socket. The second parameter specifies the encoding in the +case of a string--it defaults to UTF8 encoding. + +Returns `true` if the entire data was flushed successfully to the kernel +buffer. Returns `false` if all or part of the data was queued in user memory. +`'drain'` will be emitted when the buffer is again free. + +The optional `callback` parameter will be executed when the data is finally +written out - this may not be immediately. + +### socket.end([data], [encoding]) + +Half-closes the socket. i.e., it sends a FIN packet. It is possible the +server will still send some data. + +If `data` is specified, it is equivalent to calling +`socket.write(data, encoding)` followed by `socket.end()`. + +### socket.destroy() + +Ensures that no more I/O activity happens on this socket. Only necessary in +case of errors (parse error or so). + +### socket.pause() + +Pauses the reading of data. That is, `'data'` events will not be emitted. +Useful to throttle back an upload. + +### socket.resume() + +Resumes reading after a call to `pause()`. + +### socket.setTimeout(timeout, [callback]) + +Sets the socket to timeout after `timeout` milliseconds of inactivity on +the socket. By default `net.Socket` do not have a timeout. + +When an idle timeout is triggered the socket will receive a `'timeout'` +event but the connection will not be severed. The user must manually `end()` +or `destroy()` the socket. + +If `timeout` is 0, then the existing idle timeout is disabled. + +The optional `callback` parameter will be added as a one time listener for the +`'timeout'` event. + +### socket.setNoDelay([noDelay]) + +Disables the Nagle algorithm. By default TCP connections use the Nagle +algorithm, they buffer data before sending it off. Setting `true` for +`noDelay` will immediately fire off data each time `socket.write()` is called. +`noDelay` defaults to `true`. + +### socket.setKeepAlive([enable], [initialDelay]) + +Enable/disable keep-alive functionality, and optionally set the initial +delay before the first keepalive probe is sent on an idle socket. +`enable` defaults to `false`. + +Set `initialDelay` (in milliseconds) to set the delay between the last +data packet received and the first keepalive probe. Setting 0 for +initialDelay will leave the value unchanged from the default +(or previous) setting. Defaults to `0`. + +### socket.address() + +Returns the bound address, the address family name and port of the +socket as reported by the operating system. Returns an object with +three properties, e.g. +`{ port: 12346, family: 'IPv4', address: '127.0.0.1' }` + +### socket.remoteAddress + +The string representation of the remote IP address. For example, +`'74.125.127.100'` or `'2001:4860:a005::68'`. + +### socket.remotePort + +The numeric representation of the remote port. For example, +`80` or `21`. + +### socket.bytesRead + +The amount of received bytes. + +### socket.bytesWritten + +The amount of bytes sent. + + +`net.Socket` instances are EventEmitters with the following events: + +### Event: 'connect' + +Emitted when a socket connection is successfully established. +See `connect()`. + +### Event: 'data' + +* {Buffer object} + +Emitted when data is received. The argument `data` will be a `Buffer` or +`String`. Encoding of data is set by `socket.setEncoding()`. +(See the [Readable Stream](stream.html#readable_stream) section for more +information.) + +Note that the __data will be lost__ if there is no listener when a `Socket` +emits a `'data'` event. + +### Event: 'end' + +Emitted when the other end of the socket sends a FIN packet. + +By default (`allowHalfOpen == false`) the socket will destroy its file +descriptor once it has written out its pending write queue. However, by +setting `allowHalfOpen == true` the socket will not automatically `end()` +its side allowing the user to write arbitrary amounts of data, with the +caveat that the user is required to `end()` their side now. + + +### Event: 'timeout' + +Emitted if the socket times out from inactivity. This is only to notify that +the socket has been idle. The user must manually close the connection. + +See also: `socket.setTimeout()` + + +### Event: 'drain' + +Emitted when the write buffer becomes empty. Can be used to throttle uploads. + +See also: the return values of `socket.write()` + +### Event: 'error' + +* {Error object} + +Emitted when an error occurs. The `'close'` event will be called directly +following this event. + +### Event: 'close' + +* `had_error` {Boolean} true if the socket had a transmission error + +Emitted once the socket is fully closed. The argument `had_error` is a boolean +which says if the socket was closed due to a transmission error. + +## net.isIP(input) + +Tests if input is an IP address. Returns 0 for invalid strings, +returns 4 for IP version 4 addresses, and returns 6 for IP version 6 addresses. + + +## net.isIPv4(input) + +Returns true if input is a version 4 IP address, otherwise returns false. + + +## net.isIPv6(input) + +Returns true if input is a version 6 IP address, otherwise returns false. + diff --git a/doc/api/os.markdown b/doc/api/os.markdown new file mode 100644 index 0000000..33eb9b6 --- /dev/null +++ b/doc/api/os.markdown @@ -0,0 +1,134 @@ +# os + + Stability: 4 - API Frozen + +Provides a few basic operating-system related utility functions. + +Use `require('os')` to access this module. + +## os.hostname() + +Returns the hostname of the operating system. + +## os.type() + +Returns the operating system name. + +## os.platform() + +Returns the operating system platform. + +## os.arch() + +Returns the operating system CPU architecture. + +## os.release() + +Returns the operating system release. + +## os.uptime() + +Returns the system uptime in seconds. + +## os.loadavg() + +Returns an array containing the 1, 5, and 15 minute load averages. + +## os.totalmem() + +Returns the total amount of system memory in bytes. + +## os.freemem() + +Returns the amount of free system memory in bytes. + +## os.cpus() + +Returns an array of objects containing information about each CPU/core installed: model, speed (in MHz), and times (an object containing the number of CPU ticks spent in: user, nice, sys, idle, and irq). + +Example inspection of os.cpus: + + [ { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 252020, + nice: 0, + sys: 30340, + idle: 1070356870, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 306960, + nice: 0, + sys: 26980, + idle: 1071569080, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 248450, + nice: 0, + sys: 21750, + idle: 1070919370, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 256880, + nice: 0, + sys: 19430, + idle: 1070905480, + irq: 20 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 511580, + nice: 20, + sys: 40900, + idle: 1070842510, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 291660, + nice: 0, + sys: 34360, + idle: 1070888000, + irq: 10 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 308260, + nice: 0, + sys: 55410, + idle: 1071129970, + irq: 880 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 266450, + nice: 1480, + sys: 34920, + idle: 1072572010, + irq: 30 } } ] + +## os.networkInterfaces() + +Get a list of network interfaces: + + { lo0: + [ { address: '::1', family: 'IPv6', internal: true }, + { address: 'fe80::1', family: 'IPv6', internal: true }, + { address: '127.0.0.1', family: 'IPv4', internal: true } ], + en1: + [ { address: 'fe80::cabc:c8ff:feef:f996', family: 'IPv6', + internal: false }, + { address: '10.0.1.123', family: 'IPv4', internal: false } ], + vmnet1: [ { address: '10.99.99.254', family: 'IPv4', internal: false } ], + vmnet8: [ { address: '10.88.88.1', family: 'IPv4', internal: false } ], + ppp0: [ { address: '10.2.0.231', family: 'IPv4', internal: false } ] } + +## os.EOL + +A constant defining the appropriate End-of-line marker for the operating system. diff --git a/doc/api/path.markdown b/doc/api/path.markdown new file mode 100644 index 0000000..d178b53 --- /dev/null +++ b/doc/api/path.markdown @@ -0,0 +1,156 @@ +# Path + + Stability: 3 - Stable + +This module contains utilities for handling and transforming file +paths. Almost all these methods perform only string transformations. +The file system is not consulted to check whether paths are valid. + +Use `require('path')` to use this module. The following methods are provided: + +## path.normalize(p) + +Normalize a string path, taking care of `'..'` and `'.'` parts. + +When multiple slashes are found, they're replaced by a single one; +when the path contains a trailing slash, it is preserved. +On windows backslashes are used. + +Example: + + path.normalize('/foo/bar//baz/asdf/quux/..') + // returns + '/foo/bar/baz/asdf' + +## path.join([path1], [path2], [...]) + +Join all arguments together and normalize the resulting path. +Non-string arguments are ignored. + +Example: + + path.join('/foo', 'bar', 'baz/asdf', 'quux', '..') + // returns + '/foo/bar/baz/asdf' + + path.join('foo', {}, 'bar') + // returns + 'foo/bar' + +## path.resolve([from ...], to) + +Resolves `to` to an absolute path. + +If `to` isn't already absolute `from` arguments are prepended in right to left +order, until an absolute path is found. If after using all `from` paths still +no absolute path is found, the current working directory is used as well. The +resulting path is normalized, and trailing slashes are removed unless the path +gets resolved to the root directory. Non-string arguments are ignored. + +Another way to think of it is as a sequence of `cd` commands in a shell. + + path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile') + +Is similar to: + + cd foo/bar + cd /tmp/file/ + cd .. + cd a/../subfile + pwd + +The difference is that the different paths don't need to exist and may also be +files. + +Examples: + + path.resolve('/foo/bar', './baz') + // returns + '/foo/bar/baz' + + path.resolve('/foo/bar', '/tmp/file/') + // returns + '/tmp/file' + + path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif') + // if currently in /home/myself/node, it returns + '/home/myself/node/wwwroot/static_files/gif/image.gif' + +## path.relative(from, to) + +Solve the relative path from `from` to `to`. + +At times we have two absolute paths, and we need to derive the relative +path from one to the other. This is actually the reverse transform of +`path.resolve`, which means we see that: + + path.resolve(from, path.relative(from, to)) == path.resolve(to) + +Examples: + + path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb') + // returns + '..\\..\\impl\\bbb' + + path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb') + // returns + '../../impl/bbb' + +## path.dirname(p) + +Return the directory name of a path. Similar to the Unix `dirname` command. + +Example: + + path.dirname('/foo/bar/baz/asdf/quux') + // returns + '/foo/bar/baz/asdf' + +## path.basename(p, [ext]) + +Return the last portion of a path. Similar to the Unix `basename` command. + +Example: + + path.basename('/foo/bar/baz/asdf/quux.html') + // returns + 'quux.html' + + path.basename('/foo/bar/baz/asdf/quux.html', '.html') + // returns + 'quux' + +## path.extname(p) + +Return the extension of the path, from the last '.' to end of string +in the last portion of the path. If there is no '.' in the last portion +of the path or the first character of it is '.', then it returns +an empty string. Examples: + + path.extname('index.html') + // returns + '.html' + + path.extname('index.') + // returns + '.' + + path.extname('index') + // returns + '' + +## path.sep + +The platform-specific file separator. `'\\'` or `'/'`. + +An example on linux: + + 'foo/bar/baz'.split(path.sep) + // returns + ['foo', 'bar', 'baz'] + +An example on windows: + + 'foo\\bar\\baz'.split(path.sep) + // returns + ['foo', 'bar', 'baz'] diff --git a/doc/api/process.markdown b/doc/api/process.markdown new file mode 100644 index 0000000..b7ab3b7 --- /dev/null +++ b/doc/api/process.markdown @@ -0,0 +1,410 @@ +# process + +<!-- type=global --> + +The `process` object is a global object and can be accessed from anywhere. +It is an instance of `EventEmitter`. + + +## Event: 'exit' + +Emitted when the process is about to exit. This is a good hook to perform +constant time checks of the module's state (like for unit tests). The main +event loop will no longer be run after the 'exit' callback finishes, so +timers may not be scheduled. + +Example of listening for `exit`: + + process.on('exit', function () { + process.nextTick(function () { + console.log('This will not run'); + }); + console.log('About to exit.'); + }); + +## Event: 'uncaughtException' + +Emitted when an exception bubbles all the way back to the event loop. If a +listener is added for this exception, the default action (which is to print +a stack trace and exit) will not occur. + +Example of listening for `uncaughtException`: + + 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.'); + +Note that `uncaughtException` is a very crude mechanism for exception +handling. Using try / catch in your program will give you more control over +your program's flow. Especially for server programs that are designed to +stay running forever, `uncaughtException` can be a useful safety mechanism. + + +## Signal Events + +<!--type=event--> +<!--name=SIGINT, SIGUSR1, etc.--> + +Emitted when the processes receives a signal. See sigaction(2) for a list of +standard POSIX signal names such as SIGINT, SIGUSR1, etc. + +Example of listening for `SIGINT`: + + // Start reading from stdin so we don't exit. + process.stdin.resume(); + + process.on('SIGINT', function () { + console.log('Got SIGINT. Press Control-D to exit.'); + }); + +An easy way to send the `SIGINT` signal is with `Control-C` in most terminal +programs. + + +## process.stdout + +A `Writable Stream` to `stdout`. + +Example: the definition of `console.log` + + console.log = function (d) { + process.stdout.write(d + '\n'); + }; + +`process.stderr` and `process.stdout` are unlike other streams in Node in +that writes to them are usually blocking. They are blocking in the case +that they refer to regular files or TTY file descriptors. In the case they +refer to pipes, they are non-blocking like other streams. + + +## process.stderr + +A writable stream to stderr. + +`process.stderr` and `process.stdout` are unlike other streams in Node in +that writes to them are usually blocking. They are blocking in the case +that they refer to regular files or TTY file descriptors. In the case they +refer to pipes, they are non-blocking like other streams. + + +## process.stdin + +A `Readable Stream` for stdin. The stdin stream is paused by default, so one +must call `process.stdin.resume()` to read from it. + +Example of opening standard input and listening for both events: + + 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'); + }); + + +## process.argv + +An array containing the command line arguments. The first element will be +'node', the second element will be the name of the JavaScript file. The +next elements will be any additional command line arguments. + + // print process.argv + process.argv.forEach(function (val, index, array) { + console.log(index + ': ' + val); + }); + +This will generate: + + $ node process-2.js one two=three four + 0: node + 1: /Users/mjr/work/node/process-2.js + 2: one + 3: two=three + 4: four + + +## process.execPath + +This is the absolute pathname of the executable that started the process. + +Example: + + /usr/local/bin/node + + +## process.abort() + +This causes node to emit an abort. This will cause node to exit and +generate a core file. + +## process.chdir(directory) + +Changes the current working directory of the process or throws an exception if that fails. + + console.log('Starting directory: ' + process.cwd()); + try { + process.chdir('/tmp'); + console.log('New directory: ' + process.cwd()); + } + catch (err) { + console.log('chdir: ' + err); + } + + + +## process.cwd() + +Returns the current working directory of the process. + + console.log('Current directory: ' + process.cwd()); + + +## process.env + +An object containing the user environment. See environ(7). + + +## process.exit([code]) + +Ends the process with the specified `code`. If omitted, exit uses the +'success' code `0`. + +To exit with a 'failure' code: + + process.exit(1); + +The shell that executed node should see the exit code as 1. + + +## process.getgid() + +Gets the group identity of the process. (See getgid(2).) +This is the numerical group id, not the group name. + + console.log('Current gid: ' + process.getgid()); + + +## process.setgid(id) + +Sets the group identity of the process. (See setgid(2).) This accepts either +a numerical ID or a groupname string. If a groupname is specified, this method +blocks while resolving it to a numerical ID. + + console.log('Current gid: ' + process.getgid()); + try { + process.setgid(501); + console.log('New gid: ' + process.getgid()); + } + catch (err) { + console.log('Failed to set gid: ' + err); + } + + +## process.getuid() + +Gets the user identity of the process. (See getuid(2).) +This is the numerical userid, not the username. + + console.log('Current uid: ' + process.getuid()); + + +## process.setuid(id) + +Sets the user identity of the process. (See setuid(2).) This accepts either +a numerical ID or a username string. If a username is specified, this method +blocks while resolving it to a numerical ID. + + console.log('Current uid: ' + process.getuid()); + try { + process.setuid(501); + console.log('New uid: ' + process.getuid()); + } + catch (err) { + console.log('Failed to set uid: ' + err); + } + + +## process.version + +A compiled-in property that exposes `NODE_VERSION`. + + console.log('Version: ' + process.version); + +## process.versions + +A property exposing version strings of node and its dependencies. + + console.log(process.versions); + +Will output: + + { node: '0.4.12', + v8: '3.1.8.26', + ares: '1.7.4', + ev: '4.4', + openssl: '1.0.0e-fips' } + +## process.config + +An Object containing the JavaScript representation of the configure options +that were used to compile the current node executable. This is the same as +the "config.gypi" file that was produced when running the `./configure` script. + +An example of the possible output looks like: + + { target_defaults: + { cflags: [], + default_configuration: 'Release', + defines: [], + include_dirs: [], + libraries: [] }, + variables: + { host_arch: 'x64', + node_install_npm: 'true', + node_install_waf: 'true', + node_prefix: '', + node_shared_v8: 'false', + node_shared_zlib: 'false', + node_use_dtrace: 'false', + node_use_openssl: 'true', + node_use_system_openssl: 'false', + strict_aliasing: 'true', + target_arch: 'x64', + v8_use_snapshot: 'true' } } + +## process.installPrefix + +A compiled-in property that exposes `NODE_PREFIX`. + + console.log('Prefix: ' + process.installPrefix); + + +## process.kill(pid, [signal]) + +Send a signal to a process. `pid` is the process id and `signal` is the +string describing the signal to send. Signal names are strings like +'SIGINT' or 'SIGUSR1'. If omitted, the signal will be 'SIGTERM'. +See kill(2) for more information. + +Note that just because the name of this function is `process.kill`, it is +really just a signal sender, like the `kill` system call. The signal sent +may do something other than kill the target process. + +Example of sending a signal to yourself: + + process.on('SIGHUP', function () { + console.log('Got SIGHUP signal.'); + }); + + setTimeout(function () { + console.log('Exiting.'); + process.exit(0); + }, 100); + + process.kill(process.pid, 'SIGHUP'); + + +## process.pid + +The PID of the process. + + console.log('This process is pid ' + process.pid); + +## process.title + +Getter/setter to set what is displayed in 'ps'. + + +## process.arch + +What processor architecture you're running on: `'arm'`, `'ia32'`, or `'x64'`. + + console.log('This processor architecture is ' + process.arch); + + +## process.platform + +What platform you're running on. `'linux2'`, `'darwin'`, etc. + + console.log('This platform is ' + process.platform); + + +## process.memoryUsage() + +Returns an object describing the memory usage of the Node process +measured in bytes. + + var util = require('util'); + + console.log(util.inspect(process.memoryUsage())); + +This will generate: + + { rss: 4935680, + heapTotal: 1826816, + heapUsed: 650472 } + +`heapTotal` and `heapUsed` refer to V8's memory usage. + + +## process.nextTick(callback) + +On the next loop around the event loop call this callback. +This is *not* a simple alias to `setTimeout(fn, 0)`, it's much more +efficient. + + process.nextTick(function () { + console.log('nextTick callback'); + }); + + +## process.umask([mask]) + +Sets or reads the process's file mode creation mask. Child processes inherit +the mask from the parent process. Returns the old mask if `mask` argument is +given, otherwise returns the current mask. + + var oldmask, newmask = 0644; + + oldmask = process.umask(newmask); + console.log('Changed umask from: ' + oldmask.toString(8) + + ' to ' + newmask.toString(8)); + + +## process.uptime() + +Number of seconds Node has been running. + + +## process.hrtime() + +Returns the current high-resolution real time in a `[seconds, nanoseconds]` +tuple Array. It is relative to an arbitrary time in the past. It is not +related to the time of day and therefore not subject to clock drift. The +primary use is for measuring performance between intervals. + +You may pass in the result of a previous call to `process.hrtime()` to get +a diff reading, useful for benchmarks and measuring intervals: + + var t = process.hrtime(); + // [ 1800216, 927643717 ] + + setTimeout(function () { + t = process.hrtime(t); + // [ 1, 6962306 ] + + console.log('benchmark took %d seconds and %d nanoseconds', t[0], t[1]); + // benchmark took 1 seconds and 6962306 nanoseconds + }, 1000); diff --git a/doc/api/querystring.markdown b/doc/api/querystring.markdown new file mode 100644 index 0000000..2d5613a --- /dev/null +++ b/doc/api/querystring.markdown @@ -0,0 +1,49 @@ +# Query String + + Stability: 3 - Stable + +<!--name=querystring--> + +This module provides utilities for dealing with query strings. +It provides the following methods: + +## querystring.stringify(obj, [sep], [eq]) + +Serialize an object to a query string. +Optionally override the default separator (`'&'`) and assignment (`'='`) +characters. + +Example: + + querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' }) + // returns + 'foo=bar&baz=qux&baz=quux&corge=' + + querystring.stringify({foo: 'bar', baz: 'qux'}, ';', ':') + // returns + 'foo:bar;baz:qux' + +## querystring.parse(str, [sep], [eq], [options]) + +Deserialize a query string to an object. +Optionally override the default separator (`'&'`) and assignment (`'='`) +characters. + +Options object may contain `maxKeys` property (equal to 1000 by default), it'll +be used to limit processed keys. Set it to 0 to remove key count limitation. + +Example: + + querystring.parse('foo=bar&baz=qux&baz=quux&corge') + // returns + { foo: 'bar', baz: ['qux', 'quux'], corge: '' } + +## querystring.escape + +The escape function used by `querystring.stringify`, +provided so that it could be overridden if necessary. + +## querystring.unescape + +The unescape function used by `querystring.parse`, +provided so that it could be overridden if necessary. diff --git a/doc/api/readline.markdown b/doc/api/readline.markdown new file mode 100644 index 0000000..d586b64 --- /dev/null +++ b/doc/api/readline.markdown @@ -0,0 +1,283 @@ +# Readline + + Stability: 2 - Unstable + +To use this module, do `require('readline')`. Readline allows reading of a +stream (such as `process.stdin`) on a line-by-line basis. + +Note that once you've invoked this module, your node program will not +terminate until you've closed the interface. Here's how to allow your +program to gracefully exit: + + var readline = require('readline'); + + var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + rl.question("What do you think of node.js? ", function(answer) { + // TODO: Log the answer in a database + console.log("Thank you for your valuable feedback:", answer); + + rl.close(); + }); + +## readline.createInterface(options) + +Creates a readline `Interface` instance. Accepts an "options" Object that takes +the following values: + + - `input` - the readable stream to listen to (Required). + + - `output` - the writable stream to write readline data to (Required). + + - `completer` - an optional function that is used for Tab autocompletion. See + below for an example of using this. + + - `terminal` - pass `true` if the `input` and `output` streams should be + treated like a TTY, and have ANSI/VT100 escape codes written to it. + Defaults to checking `isTTY` on the `output` stream upon instantiation. + +The `completer` function is given a the current line entered by the user, and +is supposed to return an Array with 2 entries: + + 1. An Array with matching entries for the completion. + + 2. The substring that was used for the matching. + +Which ends up looking something like: +`[[substr1, substr2, ...], originalsubstring]`. + +Example: + + function completer(line) { + var completions = '.help .error .exit .quit .q'.split(' ') + var hits = completions.filter(function(c) { return c.indexOf(line) == 0 }) + // show all completions if none found + return [hits.length ? hits : completions, line] + } + +Also `completer` can be run in async mode if it accepts two arguments: + + function completer(linePartial, callback) { + callback(null, [['123'], linePartial]); + } + +`createInterface` is commonly used with `process.stdin` and +`process.stdout` in order to accept user input: + + var readline = require('readline'); + var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + +Once you have a readline instance, you most commonly listen for the +`"line"` event. + +If `terminal` is `true` for this instance then the `output` stream will get +the best compatibility if it defines an `output.columns` property, and fires +a `"resize"` event on the `output` if/when the columns ever change +(`process.stdout` does this automatically when it is a TTY). + +## Class: Interface + +The class that represents a readline interface with an input and output +stream. + +### rl.setPrompt(prompt, length) + +Sets the prompt, for example when you run `node` on the command line, you see +`> `, which is node's prompt. + +### rl.prompt([preserveCursor]) + +Readies readline for input from the user, putting the current `setPrompt` +options on a new line, giving the user a new spot to write. Set `preserveCursor` +to `true` to prevent the cursor placement being reset to `0`. + +This will also resume the `input` stream used with `createInterface` if it has +been paused. + +### rl.question(query, callback) + +Prepends the prompt with `query` and invokes `callback` with the user's +response. Displays the query to the user, and then invokes `callback` +with the user's response after it has been typed. + +This will also resume the `input` stream used with `createInterface` if +it has been paused. + +Example usage: + + interface.question('What is your favorite food?', function(answer) { + console.log('Oh, so your favorite food is ' + answer); + }); + +### rl.pause() + +Pauses the readline `input` stream, allowing it to be resumed later if needed. + +### rl.resume() + +Resumes the readline `input` stream. + +### rl.close() + +Closes the `Interface` instance, relinquishing control on the `input` and +`output` streams. The "close" event will also be emitted. + +### rl.write(data, [key]) + +Writes `data` to `output` stream. `key` is an object literal to represent a key +sequence; available if the terminal is a TTY. + +This will also resume the `input` stream if it has been paused. + +Example: + + rl.write('Delete me!'); + // Simulate ctrl+u to delete the line written previously + rl.write(null, {ctrl: true, name: 'u'}); + +## Events + +### Event: 'line' + +`function (line) {}` + +Emitted whenever the `input` stream receives a `\n`, usually received when the +user hits enter, or return. This is a good hook to listen for user input. + +Example of listening for `line`: + + rl.on('line', function (cmd) { + console.log('You just typed: '+cmd); + }); + +### Event: 'pause' + +`function () {}` + +Emitted whenever the `input` stream is paused. + +Also emitted whenever the `input` stream is not paused and receives the +`SIGCONT` event. (See events `SIGTSTP` and `SIGCONT`) + +Example of listening for `pause`: + + rl.on('pause', function() { + console.log('Readline paused.'); + }); + +### Event: 'resume' + +`function () {}` + +Emitted whenever the `input` stream is resumed. + +Example of listening for `resume`: + + rl.on('resume', function() { + console.log('Readline resumed.'); + }); + +### Event: 'close' + +`function () {}` + +Emitted when `close()` is called. + +Also emitted when the `input` stream receives its "end" event. The `Interface` +instance should be considered "finished" once this is emitted. For example, when +the `input` stream receives `^D`, respectively known as `EOT`. + +This event is also called if there is no `SIGINT` event listener present when +the `input` stream receives a `^C`, respectively known as `SIGINT`. + +### Event: 'SIGINT' + +`function () {}` + +Emitted whenever the `input` stream receives a `^C`, respectively known as +`SIGINT`. If there is no `SIGINT` event listener present when the `input` +stream receives a `SIGINT`, `pause` will be triggered. + +Example of listening for `SIGINT`: + + rl.on('SIGINT', function() { + rl.question('Are you sure you want to exit?', function(answer) { + if (answer.match(/^y(es)?$/i)) rl.pause(); + }); + }); + +### Event: 'SIGTSTP' + +`function () {}` + +**This does not work on Windows.** + +Emitted whenever the `input` stream receives a `^Z`, respectively known as +`SIGTSTP`. If there is no `SIGTSTP` event listener present when the `input` +stream receives a `SIGTSTP`, the program will be sent to the background. + +When the program is resumed with `fg`, the `pause` and `SIGCONT` events will be +emitted. You can use either to resume the stream. + +The `pause` and `SIGCONT` events will not be triggered if the stream was paused +before the program was sent to the background. + +Example of listening for `SIGTSTP`: + + rl.on('SIGTSTP', function() { + // This will override SIGTSTP and prevent the program from going to the + // background. + console.log('Caught SIGTSTP.'); + }); + +### Event: 'SIGCONT' + +`function () {}` + +**This does not work on Windows.** + +Emitted whenever the `input` stream is sent to the background with `^Z`, +respectively known as `SIGTSTP`, and then continued with `fg(1)`. This event +only emits if the stream was not paused before sending the program to the +background. + +Example of listening for `SIGCONT`: + + rl.on('SIGCONT', function() { + // `prompt` will automatically resume the stream + rl.prompt(); + }); + + +## Example: Tiny CLI + +Here's an example of how to use all these together to craft a tiny command +line interface: + + var readline = require('readline'), + rl = readline.createInterface(process.stdin, process.stdout); + + rl.setPrompt('OHAI> '); + rl.prompt(); + + rl.on('line', function(line) { + switch(line.trim()) { + case 'hello': + console.log('world!'); + break; + default: + console.log('Say what? I might have heard `' + line.trim() + '`'); + break; + } + rl.prompt(); + }).on('close', function() { + console.log('Have a great day!'); + process.exit(0); + }); + diff --git a/doc/api/repl.markdown b/doc/api/repl.markdown new file mode 100644 index 0000000..2104136 --- /dev/null +++ b/doc/api/repl.markdown @@ -0,0 +1,188 @@ +# REPL + +A Read-Eval-Print-Loop (REPL) is available both as a standalone program and +easily includable in other programs. The REPL provides a way to interactively +run JavaScript and see the results. It can be used for debugging, testing, or +just trying things out. + +By executing `node` without any arguments from the command-line you will be +dropped into the REPL. It has simplistic emacs line-editing. + + mjr:~$ node + Type '.help' for options. + > a = [ 1, 2, 3]; + [ 1, 2, 3 ] + > a.forEach(function (v) { + ... console.log(v); + ... }); + 1 + 2 + 3 + +For advanced line-editors, start node with the environmental variable +`NODE_NO_READLINE=1`. This will start the main and debugger REPL in canonical +terminal settings which will allow you to use with `rlwrap`. + +For example, you could add this to your bashrc file: + + alias node="env NODE_NO_READLINE=1 rlwrap node" + + +## repl.start(options) + +Returns and starts a `REPLServer` instance. Accepts an "options" Object that +takes the following values: + + - `prompt` - the prompt and `stream` for all I/O. Defaults to `> `. + + - `input` - the readable stream to listen to. Defaults to `process.stdin`. + + - `output` - the writable stream to write readline data to. Defaults to + `process.stdout`. + + - `terminal` - pass `true` if the `stream` should be treated like a TTY, and + have ANSI/VT100 escape codes written to it. Defaults to checking `isTTY` + on the `output` stream upon instantiation. + + - `eval` - function that will be used to eval each given line. Defaults to + an async wrapper for `eval()`. See below for an example of a custom `eval`. + + - `useColors` - a boolean which specifies whether or not the `writer` function + should output colors. If a different `writer` function is set then this does + nothing. Defaults to the repl's `terminal` value. + + - `useGlobal` - if set to `true`, then the repl will use the `global` object, + instead of running scripts in a separate context. Defaults to `false`. + + - `ignoreUndefined` - if set to `true`, then the repl will not output the + return value of command if it's `undefined`. Defaults to `false`. + + - `writer` - the function to invoke for each command that gets evaluated which + returns the formatting (including coloring) to display. Defaults to + `util.inspect`. + +You can use your own `eval` function if it has following signature: + + function eval(cmd, context, filename, callback) { + callback(null, result); + } + +Multiple REPLs may be started against the same running instance of node. Each +will share the same global object but will have unique I/O. + +Here is an example that starts a REPL on stdin, a Unix socket, and a TCP socket: + + var net = require("net"), + repl = require("repl"); + + connections = 0; + + repl.start({ + prompt: "node via stdin> ", + input: process.stdin, + output: process.stdout + }); + + net.createServer(function (socket) { + connections += 1; + repl.start({ + prompt: "node via Unix socket> ", + input: socket, + output: socket + }).on('exit', function() { + socket.end(); + }) + }).listen("/tmp/node-repl-sock"); + + net.createServer(function (socket) { + connections += 1; + repl.start({ + prompt: "node via TCP socket> ", + input: socket, + output: socket + }).on('exit', function() { + socket.end(); + }); + }).listen(5001); + +Running this program from the command line will start a REPL on stdin. Other +REPL clients may connect through the Unix socket or TCP socket. `telnet` is useful +for connecting to TCP sockets, and `socat` can be used to connect to both Unix and +TCP sockets. + +By starting a REPL from a Unix socket-based server instead of stdin, you can +connect to a long-running node process without restarting it. + +For an example of running a "full-featured" (`terminal`) REPL over +a `net.Server` and `net.Socket` instance, see: https://gist.github.com/2209310 + +For an example of running a REPL instance over `curl(1)`, +see: https://gist.github.com/2053342 + +### Event: 'exit' + +`function () {}` + +Emitted when the user exits the REPL in any of the defined ways. Namely, typing +`.exit` at the repl, pressing Ctrl+C twice to signal SIGINT, or pressing Ctrl+D +to signal "end" on the `input` stream. + +Example of listening for `exit`: + + r.on('exit', function () { + console.log('Got "exit" event from repl!'); + process.exit(); + }); + + +## REPL Features + +<!-- type=misc --> + +Inside the REPL, Control+D will exit. Multi-line expressions can be input. +Tab completion is supported for both global and local variables. + +The special variable `_` (underscore) contains the result of the last expression. + + > [ "a", "b", "c" ] + [ 'a', 'b', 'c' ] + > _.length + 3 + > _ += 1 + 4 + +The REPL provides access to any variables in the global scope. You can expose +a variable to the REPL explicitly by assigning it to the `context` object +associated with each `REPLServer`. For example: + + // repl_test.js + var repl = require("repl"), + msg = "message"; + + repl.start().context.m = msg; + +Things in the `context` object appear as local within the REPL: + + mjr:~$ node repl_test.js + > m + 'message' + +There are a few special REPL commands: + + - `.break` - While inputting a multi-line expression, sometimes you get lost + or just don't care about completing it. `.break` will start over. + - `.clear` - Resets the `context` object to an empty object and clears any + multi-line expression. + - `.exit` - Close the I/O stream, which will cause the REPL to exit. + - `.help` - Show this list of special commands. + - `.save` - Save the current REPL session to a file + >.save ./file/to/save.js + - `.load` - Load a file into the current REPL session. + >.load ./file/to/load.js + +The following key combinations in the REPL have these special effects: + + - `<ctrl>C` - Similar to the `.break` keyword. Terminates the current + command. Press twice on a blank line to forcibly exit. + - `<ctrl>D` - Similar to the `.exit` keyword. + diff --git a/doc/api/stdio.markdown b/doc/api/stdio.markdown new file mode 100644 index 0000000..a70dcef --- /dev/null +++ b/doc/api/stdio.markdown @@ -0,0 +1,61 @@ +# console + + Stability: 4 - API Frozen + +* {Object} + +<!--type=global--> + +For printing to stdout and stderr. Similar to the console object functions +provided by most web browsers, here the output is sent to stdout or stderr. + +## console.log([data], [...]) + +Prints to stdout with newline. This function can take multiple arguments in a +`printf()`-like way. Example: + + console.log('count: %d', count); + +If formatting elements are not found in the first string then `util.inspect` +is used on each argument. +See [util.format()](util.html#util.format) for more information. + +## console.info([data], [...]) + +Same as `console.log`. + +## console.error([data], [...]) + +Same as `console.log` but prints to stderr. + +## console.warn([data], [...]) + +Same as `console.error`. + +## console.dir(obj) + +Uses `util.inspect` on `obj` and prints resulting string to stdout. + +## console.time(label) + +Mark a time. + +## console.timeEnd(label) + +Finish timer, record output. Example: + + console.time('100-elements'); + for (var i = 0; i < 100; i++) { + ; + } + console.timeEnd('100-elements'); + +## console.trace(label) + +Print a stack trace to stderr of the current position. + +## console.assert(expression, [message]) + +Same as [assert.ok()](assert.html#assert_assert_value_message_assert_ok_value_message) +where if the `expression` evaluates as `false` throw an AssertionError with `message`. + diff --git a/doc/api/stream.markdown b/doc/api/stream.markdown new file mode 100644 index 0000000..72d9c9c --- /dev/null +++ b/doc/api/stream.markdown @@ -0,0 +1,184 @@ +# Stream + + Stability: 2 - Unstable + +A stream is an abstract interface implemented by various objects in Node. +For example a request to an HTTP server is a stream, as is stdout. Streams +are readable, writable, or both. All streams are instances of `EventEmitter`. + +You can load up the Stream base class by doing `require('stream')`. + +## Readable Stream + +<!--type=class--> + +A `Readable Stream` has the following methods, members, and events. + +### Event: 'data' + +`function (data) { }` + +The `'data'` event emits either a `Buffer` (by default) or a string if +`setEncoding()` was used. + +Note that the __data will be lost__ if there is no listener when a +`Readable Stream` emits a `'data'` event. + +### Event: 'end' + +`function () { }` + +Emitted when the stream has received an EOF (FIN in TCP terminology). +Indicates that no more `'data'` events will happen. If the stream is also +writable, it may be possible to continue writing. + +### Event: 'error' + +`function (exception) { }` + +Emitted if there was an error receiving data. + +### Event: 'close' + +`function () { }` + +Emitted when the underlying resource (for example, the backing file descriptor) +has been closed. Not all streams will emit this. + +### stream.readable + +A boolean that is `true` by default, but turns `false` after an `'error'` +occurred, the stream came to an `'end'`, or `destroy()` was called. + +### stream.setEncoding([encoding]) + +Makes the `'data'` event emit a string instead of a `Buffer`. `encoding` can be +`'utf8'`, `'utf16le'` (`'ucs2'`), `'ascii'`, or `'hex'`. Defaults to `'utf8'`. + +### stream.pause() + +Issues an advisory signal to the underlying communication layer, requesting +that no further data be sent until `resume()` is called. + +Note that, due to the advisory nature, certain streams will not be paused +immediately, and so `'data'` events may be emitted for some indeterminate +period of time even after `pause()` is called. You may wish to buffer such +`'data'` events. + +### stream.resume() + +Resumes the incoming `'data'` events after a `pause()`. + +### stream.destroy() + +Closes the underlying file descriptor. Stream will not emit any more events. + +### stream.pipe(destination, [options]) + +This is a `Stream.prototype` method available on all `Stream`s. + +Connects this read stream to `destination` WriteStream. Incoming +data on this stream gets written to `destination`. The destination and source +streams are kept in sync by pausing and resuming as necessary. + +This function returns the `destination` stream. + +Emulating the Unix `cat` command: + + process.stdin.resume(); + process.stdin.pipe(process.stdout); + + +By default `end()` is called on the destination when the source stream emits +`end`, so that `destination` is no longer writable. Pass `{ end: false }` as +`options` to keep the destination stream open. + +This keeps `process.stdout` open so that "Goodbye" can be written at the end. + + process.stdin.resume(); + + process.stdin.pipe(process.stdout, { end: false }); + + process.stdin.on("end", function() { + process.stdout.write("Goodbye\n"); + }); + + +## Writable Stream + +<!--type=class--> + +A `Writable Stream` has the following methods, members, and events. + +### Event: 'drain' + +`function () { }` + +After a `write()` method returned `false`, this event is emitted to +indicate that it is safe to write again. + +### Event: 'error' + +`function (exception) { }` + +Emitted on error with the exception `exception`. + +### Event: 'close' + +`function () { }` + +Emitted when the underlying file descriptor has been closed. + +### Event: 'pipe' + +`function (src) { }` + +Emitted when the stream is passed to a readable stream's pipe method. + +### stream.writable + +A boolean that is `true` by default, but turns `false` after an `'error'` +occurred or `end()` / `destroy()` was called. + +### stream.write(string, [encoding], [fd]) + +Writes `string` with the given `encoding` to the stream. Returns `true` if +the string has been flushed to the kernel buffer. Returns `false` to +indicate that the kernel buffer is full, and the data will be sent out in +the future. The `'drain'` event will indicate when the kernel buffer is +empty again. The `encoding` defaults to `'utf8'`. + +If the optional `fd` parameter is specified, it is interpreted as an integral +file descriptor to be sent over the stream. This is only supported for UNIX +streams, and is silently ignored otherwise. When writing a file descriptor in +this manner, closing the descriptor before the stream drains risks sending an +invalid (closed) FD. + +### stream.write(buffer) + +Same as the above except with a raw buffer. + +### stream.end() + +Terminates the stream with EOF or FIN. +This call will allow queued write data to be sent before closing the stream. + +### stream.end(string, encoding) + +Sends `string` with the given `encoding` and terminates the stream with EOF +or FIN. This is useful to reduce the number of packets sent. + +### stream.end(buffer) + +Same as above but with a `buffer`. + +### stream.destroy() + +Closes the underlying file descriptor. Stream will not emit any more events. +Any queued write data will not be sent. + +### stream.destroySoon() + +After the write queue is drained, close the file descriptor. `destroySoon()` +can still destroy straight away, as long as there is no data left in the queue +for writes. diff --git a/doc/api/string_decoder.markdown b/doc/api/string_decoder.markdown new file mode 100644 index 0000000..2ad2fb1 --- /dev/null +++ b/doc/api/string_decoder.markdown @@ -0,0 +1,24 @@ +# StringDecoder + + Stability: 3 - Stable + +To use this module, do `require('string_decoder')`. StringDecoder decodes a +buffer to a string. It is a simple interface to `buffer.toString()` but provides +additional support for utf8. + + var StringDecoder = require('string_decoder').StringDecoder; + var decoder = new StringDecoder('utf8'); + + var cent = new Buffer([0xC2, 0xA2]); + console.log(decoder.write(cent)); + + var euro = new Buffer([0xE2, 0x82, 0xAC]); + console.log(decoder.write(euro)); + +## Class: StringDecoder + +Accepts a single argument, `encoding` which defaults to `utf8`. + +### StringDecoder.write(buffer) + +Returns a decoded string. \ No newline at end of file diff --git a/doc/api/synopsis.markdown b/doc/api/synopsis.markdown new file mode 100644 index 0000000..b3b4608 --- /dev/null +++ b/doc/api/synopsis.markdown @@ -0,0 +1,23 @@ +# Synopsis + +<!--type=misc--> + +An example of a [web server](http.html) written with Node which responds with 'Hello +World': + + var http = require('http'); + + http.createServer(function (request, response) { + response.writeHead(200, {'Content-Type': 'text/plain'}); + response.end('Hello World\n'); + }).listen(8124); + + console.log('Server running at http://127.0.0.1:8124/'); + +To run the server, put the code into a file called `example.js` and execute +it with the node program + + > node example.js + Server running at http://127.0.0.1:8124/ + +All of the examples in the documentation can be run similarly. diff --git a/doc/api/timers.markdown b/doc/api/timers.markdown new file mode 100644 index 0000000..114a4b7 --- /dev/null +++ b/doc/api/timers.markdown @@ -0,0 +1,31 @@ +# Timers + + Stability: 5 - Locked + +All of the timer functions are globals. You do not need to `require()` +this module in order to use them. + +## setTimeout(callback, delay, [arg], [...]) + +To schedule execution of a one-time `callback` after `delay` milliseconds. Returns a +`timeoutId` for possible use with `clearTimeout()`. Optionally you can +also pass arguments to the callback. + +It is important to note that your callback will probably not be called in exactly +`delay` milliseconds - Node.js makes no guarantees about the exact timing of when +the callback will fire, nor of the ordering things will fire in. The callback will +be called as close as possible to the time specified. + +## clearTimeout(timeoutId) + +Prevents a timeout from triggering. + +## setInterval(callback, delay, [arg], [...]) + +To schedule the repeated execution of `callback` every `delay` milliseconds. +Returns a `intervalId` for possible use with `clearInterval()`. Optionally +you can also pass arguments to the callback. + +## clearInterval(intervalId) + +Stops a interval from triggering. diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown new file mode 100644 index 0000000..f5283c3 --- /dev/null +++ b/doc/api/tls.markdown @@ -0,0 +1,491 @@ +# TLS (SSL) + + Stability: 3 - Stable + +Use `require('tls')` to access this module. + +The `tls` module uses OpenSSL to provide Transport Layer Security and/or +Secure Socket Layer: encrypted stream communication. + +TLS/SSL is a public/private key infrastructure. Each client and each +server must have a private key. A private key is created like this + + openssl genrsa -out ryans-key.pem 1024 + +All severs and some clients need to have a certificate. Certificates are public +keys signed by a Certificate Authority or self-signed. The first step to +getting a certificate is to create a "Certificate Signing Request" (CSR) +file. This is done with: + + openssl req -new -key ryans-key.pem -out ryans-csr.pem + +To create a self-signed certificate with the CSR, do this: + + openssl x509 -req -in ryans-csr.pem -signkey ryans-key.pem -out ryans-cert.pem + +Alternatively you can send the CSR to a Certificate Authority for signing. + +(TODO: docs on creating a CA, for now interested users should just look at +`test/fixtures/keys/Makefile` in the Node source code) + +To create .pfx or .p12, do this: + + openssl pkcs12 -export -in agent5-cert.pem -inkey agent5-key.pem \ + -certfile ca-cert.pem -out agent5.pfx + + - `in`: certificate + - `inkey`: private key + - `certfile`: all CA certs concatenated in one file like + `cat ca1-cert.pem ca2-cert.pem > ca-cert.pem` + + +## Client-initiated renegotiation attack mitigation + +<!-- type=misc --> + +The TLS protocol lets the client renegotiate certain aspects of the TLS session. +Unfortunately, session renegotiation requires a disproportional amount of +server-side resources, which makes it a potential vector for denial-of-service +attacks. + +To mitigate this, renegotiations are limited to three times every 10 minutes. An +error is emitted on the [CleartextStream](#tls.CleartextStream) instance when +the threshold is exceeded. The limits are configurable: + + - `tls.CLIENT_RENEG_LIMIT`: renegotiation limit, default is 3. + + - `tls.CLIENT_RENEG_WINDOW`: renegotiation window in seconds, default is + 10 minutes. + +Don't change the defaults unless you know what you are doing. + +To test your server, connect to it with `openssl s_client -connect address:port` +and tap `R<CR>` (that's the letter `R` followed by a carriage return) a few +times. + + +## NPN and SNI + +<!-- type=misc --> + +NPN (Next Protocol Negotiation) and SNI (Server Name Indication) are TLS +handshake extensions allowing you: + + * NPN - to use one TLS server for multiple protocols (HTTP, SPDY) + * SNI - to use one TLS server for multiple hostnames with different SSL + certificates. + + +## tls.createServer(options, [secureConnectionListener]) + +Creates a new [tls.Server](#tls.Server). +The `connectionListener` argument is automatically set as a listener for the +[secureConnection](#event_secureConnection_) event. +The `options` object has these possibilities: + + - `pfx`: A string or `Buffer` containing the private key, certificate and + CA certs of the server in PFX or PKCS12 format. (Mutually exclusive with + the `key`, `cert` and `ca` options.) + + - `key`: A string or `Buffer` containing the private key of the server in + PEM format. (Required) + + - `passphrase`: A string of passphrase for the private key or pfx. + + - `cert`: A string or `Buffer` containing the certificate key of the server in + PEM format. (Required) + + - `ca`: An array of strings or `Buffer`s of trusted certificates. If this is + omitted several well known "root" CAs will be used, like VeriSign. + These are used to authorize connections. + + - `crl` : Either a string or list of strings of PEM encoded CRLs (Certificate + Revocation List) + + - `ciphers`: A string describing the ciphers to use or exclude. Consult + <http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT> for + details on the format. + To mitigate [BEAST attacks] + (http://blog.ivanristic.com/2011/10/mitigating-the-beast-attack-on-tls.html), + it is recommended that you use this option in conjunction with the + `honorCipherOrder` option described below to prioritize the RC4 algorithm, + since it is a non-CBC cipher. A recommended cipher list follows: + `ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM` + + - `honorCipherOrder` : + When choosing a cipher, use the server's preferences instead of the client + preferences. + Note that if SSLv2 is used, the server will send its list of preferences + to the client, and the client chooses the cipher. + Although, this option is disabled by default, it is *recommended* that you + use this option in conjunction with the `ciphers` option to mitigate + BEAST attacks. + + - `requestCert`: If `true` the server will request a certificate from + clients that connect and attempt to verify that certificate. Default: + `false`. + + - `rejectUnauthorized`: If `true` the server will reject any connection + which is not authorized with the list of supplied CAs. This option only + has an effect if `requestCert` is `true`. Default: `false`. + + - `NPNProtocols`: An array or `Buffer` of possible NPN protocols. (Protocols + should be ordered by their priority). + + - `SNICallback`: A function that will be called if client supports SNI TLS + extension. Only one argument will be passed to it: `servername`. And + `SNICallback` should return SecureContext instance. + (You can use `crypto.createCredentials(...).context` to get proper + SecureContext). If `SNICallback` wasn't provided - default callback with + high-level API will be used (see below). + + - `sessionIdContext`: A string containing a opaque identifier for session + resumption. If `requestCert` is `true`, the default is MD5 hash value + generated from command-line. Otherwise, the default is not provided. + +Here is a simple example echo server: + + var tls = require('tls'); + var fs = require('fs'); + + var options = { + key: fs.readFileSync('server-key.pem'), + cert: fs.readFileSync('server-cert.pem'), + + // This is necessary only if using the client certificate authentication. + requestCert: true, + + // This is necessary only if the client uses the self-signed certificate. + ca: [ fs.readFileSync('client-cert.pem') ] + }; + + var server = tls.createServer(options, function(cleartextStream) { + console.log('server connected', + cleartextStream.authorized ? 'authorized' : 'unauthorized'); + cleartextStream.write("welcome!\n"); + cleartextStream.setEncoding('utf8'); + cleartextStream.pipe(cleartextStream); + }); + server.listen(8000, function() { + console.log('server bound'); + }); + +Or + + var tls = require('tls'); + var fs = require('fs'); + + var options = { + pfx: fs.readFileSync('server.pfx'), + + // This is necessary only if using the client certificate authentication. + requestCert: true, + + }; + + var server = tls.createServer(options, function(cleartextStream) { + console.log('server connected', + cleartextStream.authorized ? 'authorized' : 'unauthorized'); + cleartextStream.write("welcome!\n"); + cleartextStream.setEncoding('utf8'); + cleartextStream.pipe(cleartextStream); + }); + server.listen(8000, function() { + console.log('server bound'); + }); +You can test this server by connecting to it with `openssl s_client`: + + + openssl s_client -connect 127.0.0.1:8000 + + +## tls.connect(options, [secureConnectListener]) +## tls.connect(port, [host], [options], [secureConnectListener]) + +Creates a new client connection to the given `port` and `host` (old API) or +`options.port` and `options.host`. (If `host` is omitted, it defaults to +`localhost`.) `options` should be an object which specifies: + + - `host`: Host the client should connect to + + - `port`: Port the client should connect to + + - `socket`: Establish secure connection on a given socket rather than + creating a new socket. If this option is specified, `host` and `port` + are ignored. + + - `pfx`: A string or `Buffer` containing the private key, certificate and + CA certs of the server in PFX or PKCS12 format. + + - `key`: A string or `Buffer` containing the private key of the client in + PEM format. + + - `passphrase`: A string of passphrase for the private key or pfx. + + - `cert`: A string or `Buffer` containing the certificate key of the client in + PEM format. + + - `ca`: An array of strings or `Buffer`s of trusted certificates. If this is + omitted several well known "root" CAs will be used, like VeriSign. + These are used to authorize connections. + + - `rejectUnauthorized`: If `true`, the server certificate is verified against + the list of supplied CAs. An `'error'` event is emitted if verification + fails. Default: `false`. + + - `NPNProtocols`: An array of string or `Buffer` containing supported NPN + protocols. `Buffer` should have following format: `0x05hello0x05world`, + where first byte is next protocol name's length. (Passing array should + usually be much simpler: `['hello', 'world']`.) + + - `servername`: Servername for SNI (Server Name Indication) TLS extension. + +The `secureConnectListener` parameter will be added as a listener for the +['secureConnect'](#event_secureConnect_) event. + +`tls.connect()` returns a [CleartextStream](#tls.CleartextStream) object. + +Here is an example of a client of echo server as described previously: + + var tls = require('tls'); + var fs = require('fs'); + + var options = { + // These are necessary only if using the client certificate authentication + key: fs.readFileSync('client-key.pem'), + cert: fs.readFileSync('client-cert.pem'), + + // This is necessary only if the server uses the self-signed certificate + ca: [ fs.readFileSync('server-cert.pem') ] + }; + + var cleartextStream = tls.connect(8000, options, function() { + console.log('client connected', + cleartextStream.authorized ? 'authorized' : 'unauthorized'); + process.stdin.pipe(cleartextStream); + process.stdin.resume(); + }); + cleartextStream.setEncoding('utf8'); + cleartextStream.on('data', function(data) { + console.log(data); + }); + cleartextStream.on('end', function() { + server.close(); + }); + +Or + + var tls = require('tls'); + var fs = require('fs'); + + var options = { + pfx: fs.readFileSync('client.pfx') + }; + + var cleartextStream = tls.connect(8000, options, function() { + console.log('client connected', + cleartextStream.authorized ? 'authorized' : 'unauthorized'); + process.stdin.pipe(cleartextStream); + process.stdin.resume(); + }); + cleartextStream.setEncoding('utf8'); + cleartextStream.on('data', function(data) { + console.log(data); + }); + cleartextStream.on('end', function() { + server.close(); + }); + +## tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized]) + +Creates a new secure pair object with two streams, one of which reads/writes +encrypted data, and one reads/writes cleartext data. +Generally the encrypted one is piped to/from an incoming encrypted data stream, +and the cleartext one is used as a replacement for the initial encrypted stream. + + - `credentials`: A credentials object from crypto.createCredentials( ... ) + + - `isServer`: A boolean indicating whether this tls connection should be + opened as a server or a client. + + - `requestCert`: A boolean indicating whether a server should request a + certificate from a connecting client. Only applies to server connections. + + - `rejectUnauthorized`: A boolean indicating whether a server should + automatically reject clients with invalid certificates. Only applies to + servers with `requestCert` enabled. + +`tls.createSecurePair()` returns a SecurePair object with +[cleartext](#tls.CleartextStream) and `encrypted` stream properties. + +## Class: SecurePair + +Returned by tls.createSecurePair. + +### Event: 'secure' + +The event is emitted from the SecurePair once the pair has successfully +established a secure connection. + +Similarly to the checking for the server 'secureConnection' event, +pair.cleartext.authorized should be checked to confirm whether the certificate +used properly authorized. + +## Class: tls.Server + +This class is a subclass of `net.Server` and has the same methods on it. +Instead of accepting just raw TCP connections, this accepts encrypted +connections using TLS or SSL. + +### Event: 'secureConnection' + +`function (cleartextStream) {}` + +This event is emitted after a new connection has been successfully +handshaked. The argument is a instance of +[CleartextStream](#tls.CleartextStream). It has all the common stream methods +and events. + +`cleartextStream.authorized` is a boolean value which indicates if the +client has verified by one of the supplied certificate authorities for the +server. If `cleartextStream.authorized` is false, then +`cleartextStream.authorizationError` is set to describe how authorization +failed. Implied but worth mentioning: depending on the settings of the TLS +server, you unauthorized connections may be accepted. +`cleartextStream.npnProtocol` is a string containing selected NPN protocol. +`cleartextStream.servername` is a string containing servername requested with +SNI. + + +### Event: 'clientError' + +`function (exception) { }` + +When a client connection emits an 'error' event before secure connection is +established - it will be forwarded here. + + +### server.listen(port, [host], [callback]) + +Begin accepting connections on the specified `port` and `host`. If the +`host` is omitted, the server will accept connections directed to any +IPv4 address (`INADDR_ANY`). + +This function is asynchronous. The last parameter `callback` will be called +when the server has been bound. + +See `net.Server` for more information. + + +### server.close() + +Stops the server from accepting new connections. This function is +asynchronous, the server is finally closed when the server emits a `'close'` +event. + +### server.address() + +Returns the bound address, the address family name and port of the +server as reported by the operating system. +See [net.Server.address()](net.html#server.address) for more information. + +### server.addContext(hostname, credentials) + +Add secure context that will be used if client request's SNI hostname is +matching passed `hostname` (wildcards can be used). `credentials` can contain +`key`, `cert` and `ca`. + +### server.maxConnections + +Set this property to reject connections when the server's connection count +gets high. + +### server.connections + +The number of concurrent connections on the server. + + +## Class: tls.CleartextStream + +This is a stream on top of the *Encrypted* stream that makes it possible to +read/write an encrypted data as a cleartext data. + +This instance implements a duplex [Stream](stream.html) interfaces. +It has all the common stream methods and events. + +A ClearTextStream is the `clear` member of a SecurePair object. + +### Event: 'secureConnect' + +This event is emitted after a new connection has been successfully handshaked. +The listener will be called no matter if the server's certificate was +authorized or not. It is up to the user to test `cleartextStream.authorized` +to see if the server certificate was signed by one of the specified CAs. +If `cleartextStream.authorized === false` then the error can be found in +`cleartextStream.authorizationError`. Also if NPN was used - you can check +`cleartextStream.npnProtocol` for negotiated protocol. + +### cleartextStream.authorized + +A boolean that is `true` if the peer certificate was signed by one of the +specified CAs, otherwise `false` + +### cleartextStream.authorizationError + +The reason why the peer's certificate has not been verified. This property +becomes available only when `cleartextStream.authorized === false`. + +### cleartextStream.getPeerCertificate() + +Returns an object representing the peer's certificate. The returned object has +some properties corresponding to the field of the certificate. + +Example: + + { subject: + { C: 'UK', + ST: 'Acknack Ltd', + L: 'Rhys Jones', + O: 'node.js', + OU: 'Test TLS Certificate', + CN: 'localhost' }, + issuer: + { C: 'UK', + ST: 'Acknack Ltd', + L: 'Rhys Jones', + O: 'node.js', + OU: 'Test TLS Certificate', + CN: 'localhost' }, + valid_from: 'Nov 11 09:52:22 2009 GMT', + valid_to: 'Nov 6 09:52:22 2029 GMT', + fingerprint: '2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF' } + +If the peer does not provide a certificate, it returns `null` or an empty +object. + +### cleartextStream.getCipher() +Returns an object representing the cipher name and the SSL/TLS +protocol version of the current connection. + +Example: +{ name: 'AES256-SHA', version: 'TLSv1/SSLv3' } + +See SSL_CIPHER_get_name() and SSL_CIPHER_get_version() in +http://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_CIPHERS for more +information. + +### cleartextStream.address() + +Returns the bound address, the address family name and port of the +underlying socket as reported by the operating system. Returns an +object with three properties, e.g. +`{ port: 12346, family: 'IPv4', address: '127.0.0.1' }` + +### cleartextStream.remoteAddress + +The string representation of the remote IP address. For example, +`'74.125.127.100'` or `'2001:4860:a005::68'`. + +### cleartextStream.remotePort + +The numeric representation of the remote port. For example, `443`. diff --git a/doc/api/tty.markdown b/doc/api/tty.markdown new file mode 100644 index 0000000..104fb8c --- /dev/null +++ b/doc/api/tty.markdown @@ -0,0 +1,75 @@ +# TTY + + Stability: 2 - Unstable + +The `tty` module houses the `tty.ReadStream` and `tty.WriteStream` classes. In +most cases, you will not need to use this module directly. + +When node detects that it is being run inside a TTY context, then `process.stdin` +will be a `tty.ReadStream` instance and `process.stdout` will be +a `tty.WriteStream` instance. The preferred way to check if node is being run in +a TTY context is to check `process.stdout.isTTY`: + + $ node -p -e "Boolean(process.stdout.isTTY)" + true + $ node -p -e "Boolean(process.stdout.isTTY)" | cat + false + + +## tty.isatty(fd) + +Returns `true` or `false` depending on if the `fd` is associated with a +terminal. + + +## tty.setRawMode(mode) + +Deprecated. Use `tty.ReadStream#setRawMode()` +(i.e. `process.stdin.setRawMode()`) instead. + + +## Class: ReadStream + +A `net.Socket` subclass that represents the readable portion of a tty. In normal +circumstances, `process.stdin` will be the only `tty.ReadStream` instance in any +node program (only when `isatty(0)` is true). + +### rs.isRaw + +A `Boolean` that is initialized to `false`. It represents the current "raw" state +of the `tty.ReadStream` instance. + +### rs.setRawMode(mode) + +`mode` should be `true` or `false`. This sets the properties of the +`tty.ReadStream` to act either as a raw device or default. `isRaw` will be set +to the resulting mode. + + +## Class WriteStream + +A `net.Socket` subclass that represents the writable portion of a tty. In normal +circumstances, `process.stdout` will be the only `tty.WriteStream` instance +ever created (and only when `isatty(1)` is true). + +### ws.columns + +A `Number` that gives the number of columns the TTY currently has. This property +gets updated on "resize" events. + +### ws.rows + +A `Number` that gives the number of rows the TTY currently has. This property +gets updated on "resize" events. + +### Event: 'resize' + +`function () {}` + +Emitted by `refreshSize()` when either of the `columns` or `rows` properties +has changed. + + process.stdout.on('resize', function() { + console.log('screen size has changed!'); + console.log(process.stdout.columns + 'x' + process.stdout.rows); + }); diff --git a/doc/api/url.markdown b/doc/api/url.markdown new file mode 100644 index 0000000..8e6a63d --- /dev/null +++ b/doc/api/url.markdown @@ -0,0 +1,99 @@ +# URL + + Stability: 3 - Stable + +This module has utilities for URL resolution and parsing. +Call `require('url')` to use it. + +Parsed URL objects have some or all of the following fields, depending on +whether or not they exist in the URL string. Any parts that are not in the URL +string will not be in the parsed object. Examples are shown for the URL + +`'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'` + +* `href`: The full URL that was originally parsed. Both the protocol and host are lowercased. + + Example: `'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'` + +* `protocol`: The request protocol, lowercased. + + Example: `'http:'` + +* `host`: The full lowercased host portion of the URL, including port + information. + + Example: `'host.com:8080'` + +* `auth`: The authentication information portion of a URL. + + Example: `'user:pass'` + +* `hostname`: Just the lowercased hostname portion of the host. + + Example: `'host.com'` + +* `port`: The port number portion of the host. + + Example: `'8080'` + +* `pathname`: The path section of the URL, that comes after the host and + before the query, including the initial slash if present. + + Example: `'/p/a/t/h'` + +* `search`: The 'query string' portion of the URL, including the leading + question mark. + + Example: `'?query=string'` + +* `path`: Concatenation of `pathname` and `search`. + + Example: `'/p/a/t/h?query=string'` + +* `query`: Either the 'params' portion of the query string, or a + querystring-parsed object. + + Example: `'query=string'` or `{'query':'string'}` + +* `hash`: The 'fragment' portion of the URL including the pound-sign. + + Example: `'#hash'` + +The following methods are provided by the URL module: + +## url.parse(urlStr, [parseQueryString], [slashesDenoteHost]) + +Take a URL string, and return an object. + +Pass `true` as the second argument to also parse +the query string using the `querystring` module. +Defaults to `false`. + +Pass `true` as the third argument to treat `//foo/bar` as +`{ host: 'foo', pathname: '/bar' }` rather than +`{ pathname: '//foo/bar' }`. Defaults to `false`. + +## url.format(urlObj) + +Take a parsed URL object, and return a formatted URL string. + +* `href` will be ignored. +* `protocol`is treated the same with or without the trailing `:` (colon). + * The protocols `http`, `https`, `ftp`, `gopher`, `file` will be + postfixed with `://` (colon-slash-slash). + * All other protocols `mailto`, `xmpp`, `aim`, `sftp`, `foo`, etc will + be postfixed with `:` (colon) +* `auth` will be used if present. +* `hostname` will only be used if `host` is absent. +* `port` will only be used if `host` is absent. +* `host` will be used in place of `hostname` and `port` +* `pathname` is treated the same with or without the leading `/` (slash) +* `search` will be used in place of `query` +* `query` (object; see `querystring`) will only be used if `search` is absent. +* `search` is treated the same with or without the leading `?` (question mark) +* `hash` is treated the same with or without the leading `#` (pound sign, anchor) + +## url.resolve(from, to) + +Take a base URL, and a href URL, and resolve them as a browser would for +an anchor tag. diff --git a/doc/api/util.markdown b/doc/api/util.markdown new file mode 100644 index 0000000..db12ab5 --- /dev/null +++ b/doc/api/util.markdown @@ -0,0 +1,190 @@ +# util + + Stability: 5 - Locked + +These functions are in the module `'util'`. Use `require('util')` to access +them. + + +## util.format(format, [...]) + +Returns a formatted string using the first argument as a `printf`-like format. + +The first argument is a string that contains zero or more *placeholders*. +Each placeholder is replaced with the converted value from its corresponding +argument. Supported placeholders are: + +* `%s` - String. +* `%d` - Number (both integer and float). +* `%j` - JSON. +* `%%` - single percent sign (`'%'`). This does not consume an argument. + +If the placeholder does not have a corresponding argument, the placeholder is +not replaced. + + util.format('%s:%s', 'foo'); // 'foo:%s' + +If there are more arguments than placeholders, the extra arguments are +converted to strings with `util.inspect()` and these strings are concatenated, +delimited by a space. + + util.format('%s:%s', 'foo', 'bar', 'baz'); // 'foo:bar baz' + +If the first argument is not a format string then `util.format()` returns +a string that is the concatenation of all its arguments separated by spaces. +Each argument is converted to a string with `util.inspect()`. + + util.format(1, 2, 3); // '1 2 3' + + +## util.debug(string) + +A synchronous output function. Will block the process and +output `string` immediately to `stderr`. + + require('util').debug('message on stderr'); + +## util.error([...]) + +Same as `util.debug()` except this will output all arguments immediately to +`stderr`. + +## util.puts([...]) + +A synchronous output function. Will block the process and output all arguments +to `stdout` with newlines after each argument. + +## util.print([...]) + +A synchronous output function. Will block the process, cast each argument to a +string then output to `stdout`. Does not place newlines after each argument. + +## util.log(string) + +Output with timestamp on `stdout`. + + require('util').log('Timestamped message.'); + + +## util.inspect(object, [showHidden], [depth], [colors]) + +Return a string representation of `object`, which is useful for debugging. + +If `showHidden` is `true`, then the object's non-enumerable properties will be +shown too. Defaults to `false`. + +If `depth` is provided, it tells `inspect` how many times to recurse while +formatting the object. This is useful for inspecting large complicated objects. + +The default is to only recurse twice. To make it recurse indefinitely, pass +in `null` for `depth`. + +If `colors` is `true`, the output will be styled with ANSI color codes. +Defaults to `false`. + +Example of inspecting all properties of the `util` object: + + var util = require('util'); + + console.log(util.inspect(util, true, null)); + + +## util.isArray(object) + +Returns `true` if the given "object" is an `Array`. `false` otherwise. + + var util = require('util'); + + util.isArray([]) + // true + util.isArray(new Array) + // true + util.isArray({}) + // false + + +## util.isRegExp(object) + +Returns `true` if the given "object" is a `RegExp`. `false` otherwise. + + var util = require('util'); + + util.isRegExp(/some regexp/) + // true + util.isRegExp(new RegExp('another regexp')) + // true + util.isRegExp({}) + // false + + +## util.isDate(object) + +Returns `true` if the given "object" is a `Date`. `false` otherwise. + + var util = require('util'); + + util.isDate(new Date()) + // true + util.isDate(Date()) + // false (without 'new' returns a String) + util.isDate({}) + // false + + +## util.isError(object) + +Returns `true` if the given "object" is an `Error`. `false` otherwise. + + var util = require('util'); + + util.isError(new Error()) + // true + util.isError(new TypeError()) + // true + util.isError({ name: 'Error', message: 'an error occurred' }) + // false + + +## util.pump(readableStream, writableStream, [callback]) + +Experimental + +Read the data from `readableStream` and send it to the `writableStream`. +When `writableStream.write(data)` returns `false` `readableStream` will be +paused until the `drain` event occurs on the `writableStream`. `callback` gets +an error as its only argument and is called when `writableStream` is closed or +when an error occurs. + + +## util.inherits(constructor, superConstructor) + +Inherit the prototype methods from one +[constructor](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor) +into another. The prototype of `constructor` will be set to a new +object created from `superConstructor`. + +As an additional convenience, `superConstructor` will be accessible +through the `constructor.super_` property. + + var util = require("util"); + var events = require("events"); + + function MyStream() { + events.EventEmitter.call(this); + } + + util.inherits(MyStream, events.EventEmitter); + + MyStream.prototype.write = function(data) { + this.emit("data", data); + } + + var stream = new MyStream(); + + console.log(stream instanceof events.EventEmitter); // true + console.log(MyStream.super_ === events.EventEmitter); // true + + stream.on("data", function(data) { + console.log('Received data: "' + data + '"'); + }) + stream.write("It works!"); // Received data: "It works!" diff --git a/doc/api/vm.markdown b/doc/api/vm.markdown new file mode 100644 index 0000000..49df036 --- /dev/null +++ b/doc/api/vm.markdown @@ -0,0 +1,220 @@ +# Executing JavaScript + + Stability: 2 - Unstable. See Caveats, below. + +<!--name=vm--> + +You can access this module with: + + var vm = require('vm'); + +JavaScript code can be compiled and run immediately or compiled, saved, and run later. + +## Caveats + +The `vm` module has many known issues and edge cases. If you run into +issues or unexpected behavior, please consult +[the open issues on GitHub](https://github.com/joyent/node/issues/search?q=vm). +Some of the biggest problems are described below. + +### Sandboxes + +The `sandbox` argument to `vm.runInNewContext` and `vm.createContext`, +along with the `initSandbox` argument to `vm.createContext`, do not +behave as one might normally expect and their behavior varies +between different versions of Node. + +The key issue to be aware of is that V8 provides no way to directly +control the global object used within a context. As a result, while +properties of your `sandbox` object will be available in the context, +any properties from the `prototype`s of the `sandbox` may not be +available. Furthermore, the `this` expression within the global scope +of the context evaluates to the empty object (`{}`) instead of to +your sandbox. + +Your sandbox's properties are also not shared directly with the script. +Instead, the properties of the sandbox are copied into the context at +the beginning of execution, and then after execution, the properties +are copied back out in an attempt to propagate any changes. + +### Globals + +Properties of the global object, like `Array` and `String`, have +different values inside of a context. This means that common +expressions like `[] instanceof Array` or +`Object.getPrototypeOf([]) === Array.prototype` may not produce +expected results when used inside of scripts evaluated via the `vm` module. + +Some of these problems have known workarounds listed in the issues for +`vm` on GitHub. for example, `Array.isArray` works around +the example problem with `Array`. + +## vm.runInThisContext(code, [filename]) + +`vm.runInThisContext()` compiles `code`, runs it and returns the result. Running +code does not have access to local scope. `filename` is optional, it's used only +in stack traces. + +Example of using `vm.runInThisContext` and `eval` to run the same code: + + var localVar = 123, + usingscript, evaled, + vm = require('vm'); + + usingscript = vm.runInThisContext('localVar = 1;', + 'myfile.vm'); + console.log('localVar: ' + localVar + ', usingscript: ' + + usingscript); + evaled = eval('localVar = 1;'); + console.log('localVar: ' + localVar + ', evaled: ' + + evaled); + + // localVar: 123, usingscript: 1 + // localVar: 1, evaled: 1 + +`vm.runInThisContext` does not have access to the local scope, so `localVar` is unchanged. +`eval` does have access to the local scope, so `localVar` is changed. + +In case of syntax error in `code`, `vm.runInThisContext` emits the syntax error to stderr +and throws an exception. + + +## vm.runInNewContext(code, [sandbox], [filename]) + +`vm.runInNewContext` compiles `code`, then runs it in `sandbox` and returns the +result. Running code does not have access to local scope. The object `sandbox` +will be used as the global object for `code`. +`sandbox` and `filename` are optional, `filename` is only used in stack traces. + +Example: compile and execute code that increments a global variable and sets a new one. +These globals are contained in the sandbox. + + var util = require('util'), + vm = require('vm'), + sandbox = { + animal: 'cat', + count: 2 + }; + + vm.runInNewContext('count += 1; name = "kitty"', sandbox, 'myfile.vm'); + console.log(util.inspect(sandbox)); + + // { animal: 'cat', count: 3, name: 'kitty' } + +Note that running untrusted code is a tricky business requiring great care. To prevent accidental +global variable leakage, `vm.runInNewContext` is quite useful, but safely running untrusted code +requires a separate process. + +In case of syntax error in `code`, `vm.runInNewContext` emits the syntax error to stderr +and throws an exception. + +## vm.runInContext(code, context, [filename]) + +`vm.runInContext` compiles `code`, then runs it in `context` and returns the +result. A (V8) context comprises a global object, together with a set of +built-in objects and functions. Running code does not have access to local scope +and the global object held within `context` will be used as the global object +for `code`. +`filename` is optional, it's used only in stack traces. + +Example: compile and execute code in a existing context. + + var util = require('util'), + vm = require('vm'), + initSandbox = { + animal: 'cat', + count: 2 + }, + context = vm.createContext(initSandbox); + + vm.runInContext('count += 1; name = "CATT"', context, 'myfile.vm'); + console.log(util.inspect(context)); + + // { animal: 'cat', count: 3, name: 'CATT' } + +Note that `createContext` will perform a shallow clone of the supplied sandbox object in order to +initialize the global object of the freshly constructed context. + +Note that running untrusted code is a tricky business requiring great care. To prevent accidental +global variable leakage, `vm.runInContext` is quite useful, but safely running untrusted code +requires a separate process. + +In case of syntax error in `code`, `vm.runInContext` emits the syntax error to stderr +and throws an exception. + +## vm.createContext([initSandbox]) + +`vm.createContext` creates a new context which is suitable for use as the 2nd argument of a subsequent +call to `vm.runInContext`. A (V8) context comprises a global object together with a set of +build-in objects and functions. The optional argument `initSandbox` will be shallow-copied +to seed the initial contents of the global object used by the context. + +## vm.createScript(code, [filename]) + +`createScript` compiles `code` but does not run it. Instead, it returns a +`vm.Script` object representing this compiled code. This script can be run +later many times using methods below. The returned script is not bound to any +global object. It is bound before each run, just for that run. `filename` is +optional, it's only used in stack traces. + +In case of syntax error in `code`, `createScript` prints the syntax error to stderr +and throws an exception. + + +## Class: Script + +A class for running scripts. Returned by vm.createScript. + +### script.runInThisContext() + +Similar to `vm.runInThisContext` but a method of a precompiled `Script` object. +`script.runInThisContext` runs the code of `script` and returns the result. +Running code does not have access to local scope, but does have access to the `global` object +(v8: in actual context). + +Example of using `script.runInThisContext` to compile code once and run it multiple times: + + var vm = require('vm'); + + globalVar = 0; + + var script = vm.createScript('globalVar += 1', 'myfile.vm'); + + for (var i = 0; i < 1000 ; i += 1) { + script.runInThisContext(); + } + + console.log(globalVar); + + // 1000 + + +### script.runInNewContext([sandbox]) + +Similar to `vm.runInNewContext` a method of a precompiled `Script` object. +`script.runInNewContext` runs the code of `script` with `sandbox` as the global object and returns the result. +Running code does not have access to local scope. `sandbox` is optional. + +Example: compile code that increments a global variable and sets one, then execute this code multiple times. +These globals are contained in the sandbox. + + var util = require('util'), + vm = require('vm'), + sandbox = { + animal: 'cat', + count: 2 + }; + + var script = vm.createScript('count += 1; name = "kitty"', 'myfile.vm'); + + for (var i = 0; i < 10 ; i += 1) { + script.runInNewContext(sandbox); + } + + console.log(util.inspect(sandbox)); + + // { animal: 'cat', count: 12, name: 'kitty' } + +Note that running untrusted code is a tricky business requiring great care. To prevent accidental +global variable leakage, `script.runInNewContext` is quite useful, but safely running untrusted code +requires a separate process. diff --git a/doc/api/zlib.markdown b/doc/api/zlib.markdown new file mode 100644 index 0000000..a4c3a93 --- /dev/null +++ b/doc/api/zlib.markdown @@ -0,0 +1,276 @@ +# Zlib + + Stability: 3 - Stable + +You can access this module with: + + var zlib = require('zlib'); + +This provides bindings to Gzip/Gunzip, Deflate/Inflate, and +DeflateRaw/InflateRaw classes. Each class takes the same options, and +is a readable/writable Stream. + +## Examples + +Compressing or decompressing a file can be done by piping an +fs.ReadStream into a zlib stream, then into an fs.WriteStream. + + var gzip = zlib.createGzip(); + var fs = require('fs'); + var inp = fs.createReadStream('input.txt'); + var out = fs.createWriteStream('input.txt.gz'); + + inp.pipe(gzip).pipe(out); + +Compressing or decompressing data in one step can be done by using +the convenience methods. + + var input = '.................................'; + zlib.deflate(input, function(err, buffer) { + if (!err) { + console.log(buffer.toString('base64')); + } + }); + + var buffer = new Buffer('eJzT0yMAAGTvBe8=', 'base64'); + zlib.unzip(buffer, function(err, buffer) { + if (!err) { + console.log(buffer.toString()); + } + }); + +To use this module in an HTTP client or server, use the +[accept-encoding](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3) +on requests, and the +[content-encoding](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11) +header on responses. + +**Note: these examples are drastically simplified to show +the basic concept.** Zlib encoding can be expensive, and the results +ought to be cached. See [Memory Usage Tuning](#zlib_memory_usage_tuning) +below for more information on the speed/memory/compression +tradeoffs involved in zlib usage. + + // client request example + var zlib = require('zlib'); + var http = require('http'); + var fs = require('fs'); + var request = http.get({ host: 'izs.me', + path: '/', + port: 80, + headers: { 'accept-encoding': 'gzip,deflate' } }); + request.on('response', function(response) { + var output = fs.createWriteStream('izs.me_index.html'); + + switch (response.headers['content-encoding']) { + // or, just use zlib.createUnzip() to handle both cases + case 'gzip': + response.pipe(zlib.createGunzip()).pipe(output); + break; + case 'deflate': + response.pipe(zlib.createInflate()).pipe(output); + break; + default: + response.pipe(output); + break; + } + }); + + // server example + // Running a gzip operation on every request is quite expensive. + // It would be much more efficient to cache the compressed buffer. + var zlib = require('zlib'); + var http = require('http'); + var fs = require('fs'); + http.createServer(function(request, response) { + var raw = fs.createReadStream('index.html'); + var acceptEncoding = request.headers['accept-encoding']; + if (!acceptEncoding) { + acceptEncoding = ''; + } + + // Note: this is not a conformant accept-encoding parser. + // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 + if (acceptEncoding.match(/\bdeflate\b/)) { + response.writeHead(200, { 'content-encoding': 'deflate' }); + raw.pipe(zlib.createDeflate()).pipe(response); + } else if (acceptEncoding.match(/\bgzip\b/)) { + response.writeHead(200, { 'content-encoding': 'gzip' }); + raw.pipe(zlib.createGzip()).pipe(response); + } else { + response.writeHead(200, {}); + raw.pipe(response); + } + }).listen(1337); + +## Constants + +<!--type=misc--> + +All of the constants defined in zlib.h are also defined on +`require('zlib')`. They are described in more detail in the zlib +documentation. See <http://zlib.net/manual.html#Constants> +for more details. + +## zlib.createGzip([options]) + +Returns a new [Gzip](#zlib_class_zlib_gzip) object with an +[options](#zlib_options). + +## zlib.createGunzip([options]) + +Returns a new [Gunzip](#zlib_class_zlib_gunzip) object with an +[options](#zlib_options). + +## zlib.createDeflate([options]) + +Returns a new [Deflate](#zlib_class_zlib_deflate) object with an +[options](#zlib_options). + +## zlib.createInflate([options]) + +Returns a new [Inflate](#zlib_class_zlib_inflate) object with an +[options](#zlib_options). + +## zlib.createDeflateRaw([options]) + +Returns a new [DeflateRaw](#zlib_class_zlib_deflateraw) object with an +[options](#zlib_options). + +## zlib.createInflateRaw([options]) + +Returns a new [InflateRaw](#zlib_class_zlib_inflateraw) object with an +[options](#zlib_options). + +## zlib.createUnzip([options]) + +Returns a new [Unzip](#zlib_class_zlib_unzip) object with an +[options](#zlib_options). + + +## Class: zlib.Gzip + +Compress data using gzip. + +## Class: zlib.Gunzip + +Decompress a gzip stream. + +## Class: zlib.Deflate + +Compress data using deflate. + +## Class: zlib.Inflate + +Decompress a deflate stream. + +## Class: zlib.DeflateRaw + +Compress data using deflate, and do not append a zlib header. + +## Class: zlib.InflateRaw + +Decompress a raw deflate stream. + +## Class: zlib.Unzip + +Decompress either a Gzip- or Deflate-compressed stream by auto-detecting +the header. + +## Convenience Methods + +<!--type=misc--> + +All of these take a string or buffer as the first argument, and call the +supplied callback with `callback(error, result)`. The +compression/decompression engine is created using the default settings +in all convenience methods. To supply different options, use the +zlib classes directly. + +## zlib.deflate(buf, callback) + +Compress a string with Deflate. + +## zlib.deflateRaw(buf, callback) + +Compress a string with DeflateRaw. + +## zlib.gzip(buf, callback) + +Compress a string with Gzip. + +## zlib.gunzip(buf, callback) + +Decompress a raw Buffer with Gunzip. + +## zlib.inflate(buf, callback) + +Decompress a raw Buffer with Inflate. + +## zlib.inflateRaw(buf, callback) + +Decompress a raw Buffer with InflateRaw. + +## zlib.unzip(buf, callback) + +Decompress a raw Buffer with Unzip. + +## Options + +<!--type=misc--> + +Each class takes an options object. All options are optional. (The +convenience methods use the default settings for all options.) + +Note that some options are only +relevant when compressing, and are ignored by the decompression classes. + +* chunkSize (default: 16*1024) +* windowBits +* level (compression only) +* memLevel (compression only) +* strategy (compression only) +* dictionary (deflate/inflate only, empty dictionary by default) + +See the description of `deflateInit2` and `inflateInit2` at +<http://zlib.net/manual.html#Advanced> for more information on these. + +## Memory Usage Tuning + +<!--type=misc--> + +From `zlib/zconf.h`, modified to node's usage: + +The memory requirements for deflate are (in bytes): + + (1 << (windowBits+2)) + (1 << (memLevel+9)) + +that is: 128K for windowBits=15 + 128K for memLevel = 8 +(default values) plus a few kilobytes for small objects. + +For example, if you want to reduce +the default memory requirements from 256K to 128K, set the options to: + + { windowBits: 14, memLevel: 7 } + +Of course this will generally degrade compression (there's no free lunch). + +The memory requirements for inflate are (in bytes) + + 1 << windowBits + +that is, 32K for windowBits=15 (default value) plus a few kilobytes +for small objects. + +This is in addition to a single internal output slab buffer of size +`chunkSize`, which defaults to 16K. + +The speed of zlib compression is affected most dramatically by the +`level` setting. A higher level will result in better compression, but +will take longer to complete. A lower level will result in less +compression, but will be much faster. + +In general, greater memory usage options will mean that node has to make +fewer calls to zlib, since it'll be able to process more data in a +single `write` operation. So, this is another factor that affects the +speed, at the cost of memory usage. diff --git a/package.json b/package.json new file mode 100644 index 0000000..d85166d --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "nodedoc", + "description": "a fledgling perldoc for node", + "version": "1.0.0", + "repository": { + "type": "git", + "url": "git://github.com/trentm/nodedoc.git" + }, + "author": "Trent Mick <trentm@gmail.com> (http://trentm.com)", + "bin": { + "nodedoc": "./bin/nodedoc.py", + }, + "keywords": ["doc", "docs", "documentation", "perldoc"] +}