天马HTTP服务器套件使用手册

入门

当前端工程师需要一个HTTP服务器来辅助开发或自娱自乐时,有以下选择——

Apache     PHP 
   Nginx   Java
IIS  .NET  ...

但如果你觉得以上技术要么太重、要么太难、要么不好玩的时候,请试试看基于NodeJS的天马HTTP服务器套件(以下简称天马)。

天马构建在基于路由器和流水线的HTTP处理模型之上,不但提供静态服务、模板渲染、反向代理、请求合并、缓存、HTTP压缩等内置模块,还提供了简单易懂的三方模块编写机制,允许前端快速定制各种HTTP服务器。

安装

首先请安装NodeJS 0.10.0以上版本,然后在终端下使用以下命令安装天马。

$ npm install tianma -g

使用

安装好天马后,可以在终端下通过tianma命令来使用各种功能,例如查看当前版本:

$ tianma -V
0.8.0

工作目录

搭建服务器前需要准备一个工作目录,用于保存服务器配置、SSL证书、日志等文件。原则上可以手工创建工作目录,不过使用tianma命令会更方便些:

$ tianma deploy /home/user/www
create : /home/user/www
...

自动创建的工作目录内容如下:

- www/                # 工作目录
    + certificates/   # SSL证书目录
    + htdocs/         # 网站根目录
    config.js         # 服务器默认配置文件
    readme.txt        # 说明文件

配置文件

配置文件决定了服务器提供的功能,首先来看看默认配置文件(config.js)有什么内容。

var tianma = require('tianma'),
    pipe = tianma.pipe;

tianma
    .createHost({ port: 80 })
        .mount('/', [
            pipe.static({ root: './htdocs' })
        ])
        .start();

可以看到,对于配置天马而言,与其说是填写配置项,不如说是使用天马提供的API来编写一个NodeJS程序。

解惑: 细心的同学会发现require('tianma')有些奇怪,因为工作目录下并没有tianma这个三方包。其中的奥妙在于配置文件是通过tianma命令间接运行的,在运行前天马会通过NODE_PATH环境变量指定tianma三方包所在位置。

启动和停止服务

服务器可以使用前台和后台两种运行模式。在工作目录下打开终端,通过以下命令可控制服务。

$ tianma start              # 启动后台服务
[i] Service started.

$ tianma restart            # 重启当前后台服务
[i] Service killed.
[i] Service started.

$ tianma stop               # 停止后台服务
[i] Service killed.

$ tianma run                # 启动前台服务
[i] Press [Ctrl+C] to stop service..

启动前台服务时,通过console.logconsole.error输出的信息会直接打印在终端上,并且关闭当前终端窗口会停掉服务。而启动后台服务时,console.log的输出会被忽略,console.error的输出会被重定向到日志文件里,并且关闭当前终端窗口后服务依然继续运行。

另外,如果配置文件不叫做config.js,或者不位于当前工作目录下,启动服务时需要在命令后边加上配置文件的路径,例如tianma start config2.js

注意: 由于HTTP(S)的默认端口是80和443,而*nix系统下又只有root用户能使用1024以下端口号,因此需要使用sudo来控制服务,例如sudo tianma start

快捷服务

使用以下命令可以指定网站根目录并使用80端口快速启动一个HTTP静态服务器。省略路径时,使用当前目录作为根目录。

$ tianma express /home/admin/htdocs
Press [Ctrl+C] to stop service..

想在两台机器间拷贝些文件时,就可以使用这招,通过HTTP下载的方式达到目的。

配置文件

天马HTTP服务器套件构建于天马HTTP中间件之上,因此编写天马配置文件类似于使用tianmapegasus(可选)两个三方包提供的API来编写一个NodeJS程序。

创建主机

主机是提供HTTP服务的基本单位,可以通过tianma.createHost方法创建。

var host = tianma.createHost(config);

端口监听

portportssl字段分别用于指定主机需要监听的HTTP和HTTPS端口。缺少某个字段时,主机不监听对应端口。

tianma
    .createHost({ port: 80, portssl: 443 });

SSL证书

certkey字段用于指定提供HTTPS服务时使用的公钥和私钥。缺少这两个字段时,默认使用天马根证书。

tianma
    .createHost({
        cert: 'pathname/of/certificates.cer',  // 公钥文件路径
        key: 'pathname/of/certificates.key',   // 私钥文件路径
        portssl: 443,
    });

天马服务器支持SNI技术,一般不需要手工配置这两个字段,在后边的章节中会有详细介绍。

IP绑定

当服务器上有多个IP时,可以通过ip字段指定绑定哪个IP提供服务。缺少该字段时,绑定所有IP。

tianma
    .createHost({ ip: '127.0.0.1', port: 80 });

编码

charset字段用于指定主机处理HTTP请求和响应时使用的编码。缺少该字段时,默认使用utf-8编码。

tianma
    .createHost({ charset: 'gbk', port: 80 });

天马支持的编码类型请参考iconv-lite

配置服务

主机根据路由规则,使用相应的流水线来处理HTTP请求,并在完成之后将HTTP响应返回给客户端。这一切都通过主机对象的.mount方法来配置。

tianma
    .createHost({ port: 80 })
        .mount('*.example.com', [
            function (context, next) {
                context.response
                    .status(200)
                    .write('Hello World');
                next();
            }
        ])
        .mount('/', [
            function (context, next) {
                context.response
                    .status(500)
                    .write('Bad Request');
                next();
            }
        ]);

传送门: 请查看天马HTTP中间件使用手册学习路由规则的配置和流水线模块的编写。

启动服务

通过主机对象的.start方法可以启动服务。存在回调函数时,服务启动后调用回调函数,并传入主机对象。

tianma
    .createHost({ port: 80 })
        .start(function (host) {});

停止服务

通过主机对象的.stop方法可以停止服务。存在回调函数时,服务停止后调用回调函数,并传入主机对象。

tianma
    .createHost({ port: 80 })
        .stop(function (host) {});

内置模块

天马提供了一些内置模块,合理组合之后能解决很多问题。内置模块使用pegasus.createPipe方法编写,因此在使用前需要先实例化,在处理每个请求前需要先判定生效条件。

cache

tianma.pipe.cache(pipeline, config)

缓存模块,为HTTP GET请求提供客户端缓存支持。

该模块完成以下两项工作:

  1. 使用子流水线处理普通请求后,为GET请求的200响应头添加客户端缓存字段。

  2. 检查后续收到的条件GET请求是否在有效期内,有效时返回304响应,无效时重复第1项工作。

生效条件

无限制。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.cache([
                tianma.pipe.static({ root: './htdocs' })
            ])
        ])
        .start();

以上代码实现了带客户端缓存的静态文件服务器。客户端第一次请求某个URL后,收到的是指定了半小时缓存的200响应。客户端如果在半小时内第二次条件请求同一URL,收到的将是304响应,否则收到的是重新指定了缓存有效期的200响应。

combo

tianma.pipe.combo(config)

请求合并模块,用于将多个同类型的GET请求合并为一个。

该模块除了支持处理/foo/??bar.js,baz.js形式的文件合并请求外,还支持/foo/bar.js这种普通请求。该模块会去源服务器(例如为http://localhost/)读取请求的文件(例如http://localhost/foo/bar.jshttp://localhost/foo/baz.js)后,使用指定的分隔符合并文件内容后返回响应。

当源服器在处理某个请求后返回的不是200响应时,根据配置,该模块可选择将整个合并请求视作异常,或是忽略掉部分异常后返回正常响应的内容合并结果。

生效条件

  1. 传入的响应状态码等于404

2, 传入的请求方法等于GET

  1. 传入的请求不是由combo模块自身发起的。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.combo(),
            tianma.pipe.static({ root: './htdocs' })
        ])
        .start();

以上代码实现了一个静态文件合并服务器。当请求http://localhost/foo/??bar.js,baz.js到达combo模块后,会按照以下流程处理:

  1. 由于使用默认配置,combo模块使用loop://localhost/作为源服务器,并接着发起loop://localhost/foo/bar.jsloop://localhost/foo/baz.js两个子请求。

  2. 子请求再次到达combo模块后,combo模块会忽略掉自身发起的请求。

  3. 子请求接着到达static模块被处理。

  4. combo模块收到两个子请求的响应后依次合并内容,并最终返回响应。

compress

tianma.pipe.compress(config)

HTTP压缩模块,用于压缩响应体数据。

生效条件

  1. 传入的响应状态码等于200

  2. 传入的请求头中包含accept-encoding字段,并且申明支持gzipdeflate压缩方式。

  3. 传入的响应头中包含content-type字段,并且其中的MIME与config.extnames中的某个扩展名匹配。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.static({ root: './htdocs' }),
            tianma.pipe.compress()
        ])
        .start();

以上代码实现了一个带HTTP压缩功能的静态文件服务器。

debug

tianma.pipe.debug()

代码调试模块,用于将包裹在以下格式的注释中的调试代码反注释,使之生效。

/*@debug
    console.log('debug');
*/

生效条件

  1. 传入的响应状态码等于200

  2. 传入的响应头中包含content-type字段,并且其中的MIME与JS或CSS文件匹配。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.static({ root: './htdocs' }),
            tianma.pipe.debug()
        ])
        .start();

以上代码实现了一个静态文件调试服务器,可以将JS或CSS代码中的调试注释反注释掉。

dynamic

tianma.pipe.dynamic(config)

动态内容模块,用于渲染模板。

该模块支持渲染使用类似ASP JavaScript的语法编写的模块,并部分支持SSI语法。以下是一个示例模板。

<!DOCTYPE html>
<!-- #include virtual="./head.inc" -->
<body>
<dl>
<dt>Request URL:</dt>
<dd><% response.write(request.href); %></dd>
<dt>Request Method:</dt>
<dd><%= request.method %></dd>
</dl>
</body>
</html>

在模板中,包含在<% %>之间的JS代码可以直接用变量名访问context对象的成员,而<%= xxx %><% response.write(xxx); %>的语法糖。

生效条件

  1. 传入的响应状态码等于200

  2. 传入的请求地址的扩展名与config.extname相同。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            function (context, next) {
                // Put template variables into context.
                next();
            },
            tianma.pipe.static({ root: './tmpl' }),
            tianma.pipe.dynamic()
        ])
        .start();

以上代码实现了一个动态服务器,可根据请求路径先准备好模块变量,再使用static模块读取模板文件,再使用dynamic模块渲染模板后输出响应。

modular

tianma.pipe.modular(config)

JS模块化模块,遵循CMD规范。

该模块支持为CMD匿名模块自动加上模块ID和依赖信息。例如有foo/bar.js内容如下:

define(function (require, exports, module) {
    exports.baz = require('./baz');
});

经过modular模块转换后内容变为:

define("foo/bar.js", [ "./baz" ], function (require, exports, module) {
    exports.baz = require('./baz');
});

另外,如果config.auto配置为truefoo/bar.js的内容可以简写如下:

exports.baz = require('./baz');

生效条件

  1. 传入的响应状态码等于200

  2. 传入的响应头中包含content-type字段,并且其中的MIME与JS文件匹配。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.static({ root: './htdocs' }),
            tianma.pipe.modular()
        ])
        .start();

以上代码实现了一个CMD模块服务器,浏览器端可以使用SeaJS等CMD模块管理器来加载JS模块。

pipe

tianma.pipe.pipe(pipeline)

管道模块,用于将一个流水线转换为单个模块使用。

即使在同一个流水线中,有时候需要为不同的请求提供不同的处理流程,此时就可以使用该模块来简化代码的组织和编写。

生效条件

无限制。

示例

var action1 = tianma.pipe.pipe([
        function (context, next) { /* Do something */ },
        function (context, next) { /* Do something */ }
    ]),

    action2 = tianma.pipe.pipe([
        function (context, next) { /* Do something */ }
    ]);

    tianma
        .createHost({ port: 80 })
            .mount('/', [
                function (context, next) {
                    if (context.request.method === 'GET') {
                        action1(context, next);
                    } else {
                        action2(context, next);
                    }
                }
            ])
            .start();

以上代码根据请求方法使用了不同的处理流程。

proxy

tianma.pipe.proxy(config)

反向代理模块,用于在服务端转发请求。

可以通过键值对方式配置多组target(字符串) : pattern(正则)代理规则,当一个请求到达该模块时,会按以下流程处理:

  1. 首先使用url.match(pattern)的方式依次使用每条代理规则匹配请求的完整URL。

  2. 如果匹配成功,使用匹配结果替换掉target中的$n占位符。

  3. 使用替换之后的target转发请求。

另外,由于proxy模块使用context.request方法转发请求,因此target可以使用该方法支持的任何格式的地址。

生效条件

  1. 传入的响应状态码等于404

  2. 传入的请求的完整URL与某条代理规则匹配。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.static({ root: './htdocs' }),
            tianma.pipe.proxy({
                'http://assets.example.com/$1': /\/\/.*?\/(.*)/
            })
        ])
        .start();

以上代码实现了一个具备服务端代理功能的静态文件服务器。当请求的文件在本地不存在时,服务器会去远程服务器读取请求的文件并返回结果。

redirect

tianma.pipe.redirect(config)

请求重定向模块,用于在请求处理过程中更改请求路径。

可以通过键值对方式配置多组target(字符串): pattern(正则)代理规则,当一个请求到达该模块时,会按以下流程处理:

  1. 首先使用pathname.match(pattern)的方式依次使用每条代理规则匹配请求地址的pathname部分。

  2. 如果匹配成功,使用匹配结果替换掉target中的$n占位符。

  3. 使用替换之后的target更新当前request对象中的相关字段。

生效条件

  1. 传入的响应状态码等于404

  2. 传入的请求地址的pathname部分与某条重定向规则匹配。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.redirect({
                '/new/$1': /^\/old\/(.*)$/
            }),
            tianma.pipe.static({ root: './htdocs' })
        ])
        .start();

以上代码实现了一个具备请求重定向功能的静态文件服务器,可以将对/old/*下所有文件的请求重定向到/new/*下边。

refine

tianma.pipe.refine(config)

内容替换模块,用于替换指定类型的响应体内容。

该模块可以对响应体内容做一些简单替换,所有工作都需要在内容替换函数中同步完成。

生效条件

  1. 传入的响应状态码等于200

  2. 传入的响应头包含content-type字段,并且其中的MIME与config中的某个扩展名匹配。

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.static({ root: './htdocs' }),
            tianma.pipe.refine({
                '.js': function (data) {
                    return jsmin(data);
                }
            })
        ])
        .start();

以上代码实现了一个具备JS代码压缩功能的静态文件服务器,在refine模块中配置了JS内容替换函数并使用JSMIN来压缩代码。

static

tianma.pipe.static(config)

静态服务模块,用于从服务器本地读取请求的文件并返回内容。

该模块根据请求路径(pathnanme)从根目录中读取文件并返回响应。文件存在时返回200响应,否则返回404响应。当请求路径对应某个目录时,如果目录下存在默认索引文件,则改为读取默认索引文件,否则要么自动生成目录索引,要么返回403响应。

另外,为了避免*nix与Windows系统对文件名大小写的处理差异导致潜在问题,该模块在Windows系统下也对文件名大小写敏感。

生效条件

  1. 传入的响应状态码等于404

示例

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe.static({
                indexes: [ 'index.html' ], root: './htdocs' })
        ])
        .start();

以上代码实现了一个静态网站,使用./htdocs作为网站根目录,并使用index.html作为默认索引文件。

三方模块

当天马的内置模块无法满足需求时,可以尝试编写或使用三方模块。天马定义了一套三方模块编写规范,并借助NPM构建了三方模块生态圈系统,能够简化开发者编写和发布三方模块,也能够简化用户下载和使用三方模块。

目录结构

三方模块按以下目录结构存放在网站工作目录下。

- www/
    + certificates/
    + htdocs/
    - node_modules/       # 三方模块存放目录
        - tianma-jsmin/   # 三方模块
            + lib/
            package.json
            ...
        ...
    config.js
    readme.txt

可以看到,这是NodeJS三方包的标准存放方式。在编写天马三方模块时,建议使用tianma-作为模块名前缀,从而方便在GitHub或NPM仓库中搜索天马相关代码。

模块的加载

由于使用了node_modules目录存放三方模块,在配置文件中加载模块时当然可以使用require('tianma-jsmin')这种方式。不过天马自有一套更方便的方式。

tianma
    .createHost({ port: 80 })
        .mount('/', [
            tianma.pipe('tianma-jsmin', { level: 1 })
        ])
        .start();

可以看到,可以通过tianma.pipe方法加载三方模块。该方法要求三方模块的导出对象是一个工厂函数。工厂函数被调用时,接收传递给pipe方法的第2到第N个参数,并返回一个流水线模块函数。

另外,当node_modules目录下不存在指定模块时,pipe方法会自动调用NPM下载指定模块。如果模块名中使用tianma-jsmin@1.0.0的方式指定了版本号,pipe方法除了会下载指定版本的模块外,当node_modules目录下的模块与指定版本不匹配时,也会重新下载模块。

模块的编写

最简单的三方模块可以按以下方式编写:

module.exports = function (config) {
    return function (context, next) {
        // Do something.
    };
};

不过通常情况下我们可以使用pegasus.createPipe方法来简化复杂模块的编写,并使用标准的NPM目录结构来简化模块的发布和部署。

tianma-jsmin模块为例,以下完整地说明一个标准的三方模块该怎么编写。首先是目录结构:

- tianma-jsmin/
    - lib/
        index.js    # 入口模块
    package.json    # 包描述文件
    README.md       # 说明书

由于该模块的功能比较简单,所有代码都可以封装在index.js中。如果功能再复杂一些,就需要把代码拆分到lib目录下的多个文件中。index.js内容如下:

var jsmin = require('jsmin').jsmin,
    pegasus = require('pegasus'),
    util = pegasus.util;

var CONTENT_TYPES = [
        'text/javascript',
        'application/x-javascript',
        'application/javascript'
    ];

module.exports = pegasus.createPipe({
    _initialize: function (config) {
        this._config = util.mix({
            level: 2,
            comment: ''
        }, config);
    },

    main: function (request, response) {
        var config = this._config,
            data = response.body();

        data = jsmin(data, config.level, config.comment);

        response
            .clear()
            .write(data);

        this.next();
    },

    match: function (request, response) {
        var contentType = response.head('content-type') || '';

        return response.status() === 200
            && CONTENT_TYPES.some(function (value) {
                return contentType.indexOf(value) !== -1;
            });
    }
});

package.json中申明了模块名、版本号、入口模块位置和依赖,内容如下:

{
    "name": "tianma-jsmin",
    "version": "1.0.0",
    "dependencies": {
        "jsmin": "1.0.x"
      },
    "main": "lib/index.js"
}

有了以上目录结构和描述文件之后,该模块就可以通过NPM发布和下载了。

异常处理

通常情况下,流水线模块代码在运行过程中产生的未捕获异常会让服务挂掉。如果希望仅为发生异常的请求返回500响应,而让服务保持运行的话,就需要手动捕获异常并使用pegasus.util.throwError方法再次抛出异常。

例如在上边的tianma-jsmin模块的代码中,当JS代码有语法错误时jsmin方法会抛出异常。我们需要对main方法做出以下调整,使得有异常发生时服务不至于挂掉。

try {
    data = jsmin(data, config.level, config.comment);
} catch (err) {
    util.throwError(err);
}

另外,当模块代码在运行过程中发现有逻辑错误,需要主动抛出一个异常时,也需要使用throwError方法来代替throw关键字抛出异常,以保证仅仅是产生一个500响应,而不是让服务挂掉。

开发流程

开发一个三方模块时可以使用以下流程:

  1. 使用tianma deploy命令部署一个工作目录,作为模块的调试环境。

  2. 在工作目录下创建node_modules目录,并在其中创建模块目录并编写代码。

  3. 在配置文件中使用tianma.pipe函数加载和调试模块。

  4. 在模块目录下使用npm publish命令将三方模块发布到NPM仓库。

使用流程

使用一个三方模块时可以使用以下流程:

  1. 在配置文件中使用tianma.pipe函数指定需要使用的三方模块名,以及可选的版本号,并根据需要传入模块配置。

  2. 第一次使用某个三方模块时,天马会自动调用NPM下载。在下载完成前,三方模块所在流水线暂时处于不可用状态。如果希望服务启动后立即可用,或者因为各种原因三方模块自动下载失败,就可以在工作目录下运行类似npm install tianma-jsmin@1.0.0的命令来手动下载模块。

云配置

当同一个天马配置文件需要提供给多人使用时,每个人复制一份配置文件到自己的机器上不但麻烦,而且不利于统一维护和更新。使用云配置可以解决这个问题。

使用方法

可以按以下方式使用tianma.play方法加载和运行云配置。

tianma.play('http://www.example.com/config.js');

可以看到,云配置文件存放在某台远程服务器上。本地服务器第一次使用某个配置文件时,会先将配置文件缓存到本地后再运行。而后续使用时,如果远程服务器上的配置文件的最后修改日期未发生变化,或者连接不上远程服务器,则继续使用本地缓存的版本,否则会先更新本地缓存后再运行新的配置文件。play方法支持http:https:file:三种协议。

另外,云配置文件本身也是可配置的,示例如下:

tianma.play('http://www.example.com/config.js', {
    root: '/home/user/htdocs/'
});

可以看到,以上配置文件允许用户自定义网站根目录。

编写方法

编写一个云配置文件和编写普通的天马配置文件类似,只是云配置文件中可以直接访问通过play方法传入的config对象,示例如下。

var tianma = require('tianma'),
    pipe = tianma.pipe;

tianma
    .createHost({ port: config.port || 80 })
        .mount('/', [
            pipe.static({ root: config.root || './htdocs' })
        ])
        .start();

以上云配置文件实现了一个静态服务器,允许用户自行配置服务器端口和网站根目录,并提供了默认配置项。

SSL证书

搭建HTTPS服务器时需要为提供服务的域名配置SSL证书。天马不但简化了自制证书的制作过程,也简化了证书的配置方式。

证书制作

如果还没有从颁发机构购买证书,或者因各种原因得不到生产环境使用的证书,至少在开发环境下可以自己做一个。天马命令行工具封装了OpenSSL,可以简单制作出某个域名用的公钥和私钥。以下是为www.example.com域名制作证书的一个示例。

$ tianma ca www.example.com
create: www.example.com.cer
create: www.example.com.key

..done

安装根证书

使用tianma ca命令制作的证书都基于天马根证书颁发,为了避免使用自制证书时遭遇浏览器安全警告,需要在浏览器中安装天马根证书。使用tianma deploy命令部署一个工作目录后,天马根证书位于certificates/tianma.cer,请按照以下说明安装。

安装OpenSSL

一般*nix系统下自带OpenSSL,而Windows系统木有。运行tianma ca命令时如果提示要先安装OpenSSL时,Windows用户可以在以下位置找到合适的安装包。

http://slproweb.com/products/Win32OpenSSL.html

之后按以下步骤安装。

  1. 根据系统是32位还是64位,分别下载和安装Visual C++ 2008 Redistributables / Visual C++ 2008 Redistributables (x64)以及Win32 OpenSSL v1.0.1c / Win64 OpenSSL v1.0.1c

  2. 按照我的电脑 -> 右键 -> 属性 -> 高级 -> 环境变量的顺序打开环境变量设置对话框。

  3. 假设默认安装到了C:\OpenSSL-Win32,把C:\OpenSSL-Win32\bin目录添加到PATH环境变量末尾,不同目录之间使用;分隔。

  4. 新增一个环境变量OPENSSL_CONF,设置为C:\OpenSSL-Win32\bin\openssl.cfg

  5. 重启系统生效。

SNI

天马服务器支持SNI技术,可以根据HTTPS请求使用的域名动态返回相应的证书,因此可以在一台服务器上使用多个域名提供不同服务。该功能使用简单,只要把每个域名的公钥和私钥文件放在工作目录下的certificates/子目录中,并让文件名与域名相同即可。例如有以下工作目录。

- /home/user/www/
    - certificates/
        www.example.com.cer
        www.example.com.key
    config.js

并且config.js内容如下。

require('tianma')
    .createHost({ portssl: 443 })
        .mount('www.example.com', [])
        .start();

启动服务后,当HTTPS请求使用www.example.com这个域名时,服务器会自动使用certificates/目录下对应的证书提供服务。

降级方案

Wndows XP系统下的IE不支持SNI技术,如果要解决这部分浏览器的证书问题的话,有以下两个方案: