https
express server läuft jetzt mit https
This commit is contained in:
		
							
								
								
									
										37
									
								
								express-server/node_modules/le-challenge-fs/.npmignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								express-server/node_modules/le-challenge-fs/.npmignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
# Logs
 | 
			
		||||
logs
 | 
			
		||||
*.log
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
 | 
			
		||||
# Runtime data
 | 
			
		||||
pids
 | 
			
		||||
*.pid
 | 
			
		||||
*.seed
 | 
			
		||||
 | 
			
		||||
# Directory for instrumented libs generated by jscoverage/JSCover
 | 
			
		||||
lib-cov
 | 
			
		||||
 | 
			
		||||
# Coverage directory used by tools like istanbul
 | 
			
		||||
coverage
 | 
			
		||||
 | 
			
		||||
# nyc test coverage
 | 
			
		||||
.nyc_output
 | 
			
		||||
 | 
			
		||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
 | 
			
		||||
.grunt
 | 
			
		||||
 | 
			
		||||
# node-waf configuration
 | 
			
		||||
.lock-wscript
 | 
			
		||||
 | 
			
		||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
 | 
			
		||||
build/Release
 | 
			
		||||
 | 
			
		||||
# Dependency directories
 | 
			
		||||
node_modules
 | 
			
		||||
jspm_packages
 | 
			
		||||
 | 
			
		||||
# Optional npm cache directory
 | 
			
		||||
.npm
 | 
			
		||||
 | 
			
		||||
# Optional REPL history
 | 
			
		||||
.node_repl_history
 | 
			
		||||
							
								
								
									
										21
									
								
								express-server/node_modules/le-challenge-fs/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								express-server/node_modules/le-challenge-fs/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2016 Daplie, Inc
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										63
									
								
								express-server/node_modules/le-challenge-fs/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								express-server/node_modules/le-challenge-fs/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
[](https://gitter.im/Daplie/letsencrypt-express?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
			
		||||
 | 
			
		||||
| [letsencrypt](https://github.com/Daplie/node-letsencrypt) (library)
 | 
			
		||||
| [letsencrypt-cli](https://github.com/Daplie/letsencrypt-cli)
 | 
			
		||||
| [letsencrypt-express](https://github.com/Daplie/letsencrypt-express)
 | 
			
		||||
| [letsencrypt-cluster](https://github.com/Daplie/letsencrypt-cluster)
 | 
			
		||||
| [letsencrypt-koa](https://github.com/Daplie/letsencrypt-koa)
 | 
			
		||||
| [letsencrypt-hapi](https://github.com/Daplie/letsencrypt-hapi)
 | 
			
		||||
|
 | 
			
		||||
 | 
			
		||||
# le-challenge-webroot
 | 
			
		||||
 | 
			
		||||
An fs-based strategy for node-letsencrypt for setting, retrieving,
 | 
			
		||||
and clearing ACME challenges issued by the ACME server
 | 
			
		||||
 | 
			
		||||
This places the acme challenge in an appropriate directory in the specified `webrootPath`
 | 
			
		||||
and removes it once the challenge has either completed or failed.
 | 
			
		||||
 | 
			
		||||
* Safe to use with node cluster
 | 
			
		||||
* Safe to use with ephemeral services (Heroku, Joyent, etc)
 | 
			
		||||
 | 
			
		||||
Install
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm install --save le-challenge-fs@2.x
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
var leChallenge = require('le-challenge-fs').create({
 | 
			
		||||
  webrootPath: '~/letsencrypt/srv/www/:hostname/.well-known/acme-challenge'   // defaults to os.tmpdir()
 | 
			
		||||
, loopbackPort: 5001                                                          // defaults to 80
 | 
			
		||||
, loopbackTimeout: 3000                                                       // defaults to 3000ms
 | 
			
		||||
, debug: false
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
var LE = require('letsencrypt');
 | 
			
		||||
 | 
			
		||||
LE.create({
 | 
			
		||||
  server: LE.stagingServerUrl                               // Change to LE.productionServerUrl in production
 | 
			
		||||
, challenge: leChallenge
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
NOTE: If you request a certificate with 6 domains listed,
 | 
			
		||||
it will require 6 individual challenges.
 | 
			
		||||
 | 
			
		||||
Exposed Methods
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
For ACME Challenge:
 | 
			
		||||
 | 
			
		||||
* `set(opts, domain, key, val, done)`
 | 
			
		||||
* `get(defaults, domain, key, done)`
 | 
			
		||||
* `remove(defaults, domain, key, done)`
 | 
			
		||||
 | 
			
		||||
For node-letsencrypt internals:
 | 
			
		||||
 | 
			
		||||
* `getOptions()` returns the internal defaults merged with the user-supplied options
 | 
			
		||||
* `loopback(defaults, domain, key, value, done)` test, by external means, that the ACME server's challenge server will succeed
 | 
			
		||||
							
								
								
									
										121
									
								
								express-server/node_modules/le-challenge-fs/index.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								express-server/node_modules/le-challenge-fs/index.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
var path = require('path');
 | 
			
		||||
 | 
			
		||||
var myDefaults = {
 | 
			
		||||
  //webrootPath: [ '~', 'letsencrypt', 'var', 'lib' ].join(path.sep)
 | 
			
		||||
  webrootPath: path.join(require('os').tmpdir(), 'acme-challenge')
 | 
			
		||||
, loopbackTimeout: 5 * 1000
 | 
			
		||||
, debug: false
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var Challenge = module.exports;
 | 
			
		||||
 | 
			
		||||
Challenge.create = function (options) {
 | 
			
		||||
  var results = {};
 | 
			
		||||
 | 
			
		||||
  Object.keys(Challenge).forEach(function (key) {
 | 
			
		||||
    results[key] = Challenge[key];
 | 
			
		||||
  });
 | 
			
		||||
  results.create = undefined;
 | 
			
		||||
 | 
			
		||||
  Object.keys(myDefaults).forEach(function (key) {
 | 
			
		||||
    if ('undefined' === typeof options[key]) {
 | 
			
		||||
      options[key] = myDefaults[key];
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  results._options = options;
 | 
			
		||||
 | 
			
		||||
  results.getOptions = function () {
 | 
			
		||||
    return results._options;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return results;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// NOTE: the "args" here in `set()` are NOT accessible to `get()` and `remove()`
 | 
			
		||||
// They are provided so that you can store them in an implementation-specific way
 | 
			
		||||
// if you need access to them.
 | 
			
		||||
//
 | 
			
		||||
Challenge.set = function (args, domain, challengePath, keyAuthorization, done) {
 | 
			
		||||
  var mkdirp = require('mkdirp');
 | 
			
		||||
  keyAuthorization = String(keyAuthorization);
 | 
			
		||||
 | 
			
		||||
  mkdirp(args.webrootPath, function (err) {
 | 
			
		||||
    if (err) {
 | 
			
		||||
      done(err);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fs.writeFile(path.join(args.webrootPath, challengePath), keyAuthorization, 'utf8', function (err) {
 | 
			
		||||
      done(err);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// NOTE: the "defaults" here are still merged and templated, just like "args" would be,
 | 
			
		||||
// but if you specifically need "args" you must retrieve them from some storage mechanism
 | 
			
		||||
// based on domain and key
 | 
			
		||||
//
 | 
			
		||||
Challenge.get = function (defaults, domain, key, done) {
 | 
			
		||||
  fs.readFile(path.join(defaults.webrootPath, key), 'utf8', done);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Challenge.remove = function (defaults, domain, key, done) {
 | 
			
		||||
  fs.unlink(path.join(defaults.webrootPath, key), done);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Challenge.loopback = function (defaults, domain, key, done) {
 | 
			
		||||
  var hostname = domain + (defaults.loopbackPort ? ':' + defaults.loopbackPort : '');
 | 
			
		||||
  var urlstr = 'http://' + hostname + '/.well-known/acme-challenge/' + key;
 | 
			
		||||
 | 
			
		||||
  require('http').get(urlstr, function (res) {
 | 
			
		||||
    if (200 !== res.statusCode) {
 | 
			
		||||
      done(new Error("local loopback failed with statusCode " + res.statusCode));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    var chunks = [];
 | 
			
		||||
    res.on('data', function (chunk) {
 | 
			
		||||
      chunks.push(chunk);
 | 
			
		||||
    });
 | 
			
		||||
    res.on('end', function () {
 | 
			
		||||
      var str = Buffer.concat(chunks).toString('utf8').trim();
 | 
			
		||||
      done(null, str);
 | 
			
		||||
    });
 | 
			
		||||
  }).setTimeout(defaults.loopbackTimeout, function () {
 | 
			
		||||
    done(new Error("loopback timeout, could not reach server"));
 | 
			
		||||
  }).on('error', function (err) {
 | 
			
		||||
    done(err);
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Challenge.test = function (args, domain, challenge, keyAuthorization, done) {
 | 
			
		||||
  var me = this;
 | 
			
		||||
  var key = keyAuthorization || challenge;
 | 
			
		||||
 | 
			
		||||
  me.set(args, domain, challenge, key, function (err) {
 | 
			
		||||
    if (err) { done(err); return; }
 | 
			
		||||
 | 
			
		||||
    myDefaults.loopbackPort = args.loopbackPort;
 | 
			
		||||
    myDefaults.webrootPath = args.webrootPath;
 | 
			
		||||
    me.loopback(args, domain, challenge, function (err, _key) {
 | 
			
		||||
      if (err) { done(err); return; }
 | 
			
		||||
 | 
			
		||||
      if (key !== _key) {
 | 
			
		||||
        err = new Error("keyAuthorization [original] '" + key + "'"
 | 
			
		||||
          + " did not match [result] '" + _key + "'");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      me.remove(myDefaults, domain, challenge, function (_err) {
 | 
			
		||||
        if (_err) { done(_err); return; }
 | 
			
		||||
 | 
			
		||||
        done(err);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										100
									
								
								express-server/node_modules/le-challenge-fs/package.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								express-server/node_modules/le-challenge-fs/package.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
{
 | 
			
		||||
  "_args": [
 | 
			
		||||
    [
 | 
			
		||||
      "le-challenge-fs@^2.0.8",
 | 
			
		||||
      "/nodeapps/https-test/greenlock-express.js"
 | 
			
		||||
    ]
 | 
			
		||||
  ],
 | 
			
		||||
  "_from": "le-challenge-fs@>=2.0.8 <3.0.0",
 | 
			
		||||
  "_id": "le-challenge-fs@2.0.8",
 | 
			
		||||
  "_inCache": true,
 | 
			
		||||
  "_installable": true,
 | 
			
		||||
  "_location": "/le-challenge-fs",
 | 
			
		||||
  "_nodeVersion": "6.7.0",
 | 
			
		||||
  "_npmOperationalInternal": {
 | 
			
		||||
    "host": "packages-12-west.internal.npmjs.com",
 | 
			
		||||
    "tmp": "tmp/le-challenge-fs-2.0.8.tgz_1476312857466_0.7560109794139862"
 | 
			
		||||
  },
 | 
			
		||||
  "_npmUser": {
 | 
			
		||||
    "email": "coolaj86@gmail.com",
 | 
			
		||||
    "name": "coolaj86"
 | 
			
		||||
  },
 | 
			
		||||
  "_npmVersion": "3.10.3",
 | 
			
		||||
  "_phantomChildren": {},
 | 
			
		||||
  "_requested": {
 | 
			
		||||
    "name": "le-challenge-fs",
 | 
			
		||||
    "raw": "le-challenge-fs@^2.0.8",
 | 
			
		||||
    "rawSpec": "^2.0.8",
 | 
			
		||||
    "scope": null,
 | 
			
		||||
    "spec": ">=2.0.8 <3.0.0",
 | 
			
		||||
    "type": "range"
 | 
			
		||||
  },
 | 
			
		||||
  "_requiredBy": [
 | 
			
		||||
    "/",
 | 
			
		||||
    "/greenlock"
 | 
			
		||||
  ],
 | 
			
		||||
  "_resolved": "https://registry.npmjs.org/le-challenge-fs/-/le-challenge-fs-2.0.8.tgz",
 | 
			
		||||
  "_shasum": "b6d458a37f097e87df3d8b5ff67013737ab9d5a2",
 | 
			
		||||
  "_shrinkwrap": null,
 | 
			
		||||
  "_spec": "le-challenge-fs@^2.0.8",
 | 
			
		||||
  "_where": "/nodeapps/https-test/greenlock-express.js",
 | 
			
		||||
  "author": {
 | 
			
		||||
    "email": "coolaj86@gmail.com",
 | 
			
		||||
    "name": "AJ ONeal",
 | 
			
		||||
    "url": "https://coolaj86.com/"
 | 
			
		||||
  },
 | 
			
		||||
  "bugs": {
 | 
			
		||||
    "url": "https://github.com/Daplie/le-challenge-fs/issues"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "mkdirp": "^0.5.1"
 | 
			
		||||
  },
 | 
			
		||||
  "description": "A fs-based strategy for node-letsencrypt for setting, retrieving, and clearing ACME challenges issued by the ACME server",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "finalhandler": "^0.5.0",
 | 
			
		||||
    "serve-static": "^1.11.1"
 | 
			
		||||
  },
 | 
			
		||||
  "directories": {},
 | 
			
		||||
  "dist": {
 | 
			
		||||
    "shasum": "b6d458a37f097e87df3d8b5ff67013737ab9d5a2",
 | 
			
		||||
    "tarball": "https://registry.npmjs.org/le-challenge-fs/-/le-challenge-fs-2.0.8.tgz"
 | 
			
		||||
  },
 | 
			
		||||
  "gitHead": "3cc123034837d6e3535937665f2227b80d9cb535",
 | 
			
		||||
  "homepage": "https://github.com/Daplie/le-challenge-fs#readme",
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "acme",
 | 
			
		||||
    "challenge",
 | 
			
		||||
    "cluster",
 | 
			
		||||
    "ephemeral",
 | 
			
		||||
    "fs",
 | 
			
		||||
    "http",
 | 
			
		||||
    "http-01",
 | 
			
		||||
    "le",
 | 
			
		||||
    "le-challenge",
 | 
			
		||||
    "le-challenge-",
 | 
			
		||||
    "le-challenge-fs",
 | 
			
		||||
    "le-challenge-http",
 | 
			
		||||
    "le-challenge-webroot",
 | 
			
		||||
    "letsencrypt",
 | 
			
		||||
    "webroot"
 | 
			
		||||
  ],
 | 
			
		||||
  "license": "(MIT OR Apache-2.0)",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "maintainers": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "coolaj86",
 | 
			
		||||
      "email": "coolaj86@gmail.com"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "name": "le-challenge-fs",
 | 
			
		||||
  "optionalDependencies": {},
 | 
			
		||||
  "readme": "ERROR: No README data found!",
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git+https://github.com/Daplie/le-challenge-fs.git"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "node test.js"
 | 
			
		||||
  },
 | 
			
		||||
  "version": "2.0.8"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								express-server/node_modules/le-challenge-fs/test.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								express-server/node_modules/le-challenge-fs/test.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
//var httpsOptions = require('localhost.daplie.com-certificates').merge({});
 | 
			
		||||
var webrootPath = '/tmp/acme-challenge';
 | 
			
		||||
var challenge = require('./').create({ debug: true, webrootPath: webrootPath });
 | 
			
		||||
 | 
			
		||||
var opts = challenge.getOptions();
 | 
			
		||||
var domain = 'example.com';
 | 
			
		||||
var token = 'token-id';
 | 
			
		||||
var key = 'secret-key';
 | 
			
		||||
 | 
			
		||||
challenge.remove(opts, domain, token, function () {
 | 
			
		||||
  // ignore error, if any
 | 
			
		||||
 | 
			
		||||
  challenge.set(opts, domain, token, key, function (err) {
 | 
			
		||||
    // if there's an error, there's a problem
 | 
			
		||||
    if (err) { throw err; }
 | 
			
		||||
 | 
			
		||||
    // throw new Error("manually check /tmp/acme-challenge");
 | 
			
		||||
 | 
			
		||||
    challenge.get(opts, domain, token, function (err, _key) {
 | 
			
		||||
      // if there's an error, there's a problem
 | 
			
		||||
      if (err) { throw err; }
 | 
			
		||||
 | 
			
		||||
      // should retrieve the key
 | 
			
		||||
      if (key !== _key) {
 | 
			
		||||
        throw new Error("FAIL: could not get key by token");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      challenge.remove(opts, domain, token, function () {
 | 
			
		||||
        // if there's an error, there's a problem
 | 
			
		||||
        if (err) { throw err; }
 | 
			
		||||
 | 
			
		||||
        challenge.get(opts, domain, token, function (err, _key) {
 | 
			
		||||
          // error here is okay
 | 
			
		||||
 | 
			
		||||
          // should NOT retrieve the key
 | 
			
		||||
          if (_key) {
 | 
			
		||||
            throw new Error("FAIL: should not get key");
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          console.info('[PASS] unit test');
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function loopbackTest() {
 | 
			
		||||
  var http = require('http');
 | 
			
		||||
  var serveStatic = require('serve-static')(webrootPath, { dotfiles: 'allow' });
 | 
			
		||||
  var finalhandler = require('finalhandler');
 | 
			
		||||
  var server = http.createServer(function (req, res) {
 | 
			
		||||
    req.url = req.url.replace(/^\/\.well-known\/acme-challenge\//, '/');
 | 
			
		||||
    serveStatic(req, res, finalhandler(req, res));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server.listen(0, function () {
 | 
			
		||||
    var port = server.address().port;
 | 
			
		||||
 | 
			
		||||
    opts.webrootPath = webrootPath;
 | 
			
		||||
    opts.loopbackPort = port;
 | 
			
		||||
    opts.loopbackTimeout = 500;
 | 
			
		||||
    challenge.test(opts, 'localhost', 'foo', 'bar', function (err) {
 | 
			
		||||
      server.close();
 | 
			
		||||
      if (err) { console.error(err.stack); return; }
 | 
			
		||||
 | 
			
		||||
      console.info('[PASS] localhost loopback');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
loopbackTest();
 | 
			
		||||
		Reference in New Issue
	
	Block a user