├── test
├── pages
│ ├── empty-array.jsx
│ ├── auth.jsx
│ ├── bigfile.jsx
│ ├── file.jsx
│ ├── locale
│ │ └── ru.json
│ ├── auth-cached.jsx
│ ├── common
│ │ └── common.jsx
│ ├── template.js
│ ├── bigfile-cached.jsx
│ ├── hello.jsx
│ ├── file-cached.jsx
│ ├── http-cached.jsx
│ ├── json
│ │ ├── album.27.json
│ │ └── photo.42.json
│ ├── redirect.jsx
│ ├── http.jsx
│ ├── cookie.jsx
│ ├── index.jsx
│ ├── result.jsx
│ ├── local.jsx
│ ├── test.01.jsx
│ ├── state.jsx
│ └── auths.json
├── config.js
├── dummy
│ ├── chunk-json
│ ├── chunk-xml
│ ├── chunk-txt
│ └── dummy.js
└── modules
│ └── ya.js
├── .gitignore
├── Makefile
├── .jshintrc
├── README.md
├── lib
├── de.js
├── de.logger.js
├── index.js
├── de.log.js
├── de.request.js
├── de.sandbox.js
├── de.response.js
├── de.context.js
├── de.server.js
├── de.common.js
├── de.script.js
├── de.file.js
├── de.http.js
├── de.result.js
└── de.block.js
├── descript
├── package.json
├── todo.md
└── changelog.md
/test/pages/empty-array.jsx:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/test/pages/auth.jsx:
--------------------------------------------------------------------------------
1 | 'ya:auth()'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | node_modules
3 |
4 |
--------------------------------------------------------------------------------
/test/pages/bigfile.jsx:
--------------------------------------------------------------------------------
1 | 'auths.json'
2 |
--------------------------------------------------------------------------------
/test/pages/file.jsx:
--------------------------------------------------------------------------------
1 | de.file('locale/ru.json')
2 |
--------------------------------------------------------------------------------
/test/pages/locale/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "hello": "Привет"
3 | }
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | jshint:
2 | jshint lib/*.js
3 |
4 | .PHONY: jshint
5 |
6 |
--------------------------------------------------------------------------------
/test/pages/auth-cached.jsx:
--------------------------------------------------------------------------------
1 | de.call('ya:auth()', {
2 | key: 'auth',
3 | maxage: '+1h'
4 | })
5 |
--------------------------------------------------------------------------------
/test/pages/common/common.jsx:
--------------------------------------------------------------------------------
1 | {
2 | auth: 'ya:auth()',
3 |
4 | locale: '../locale/ru.json'
5 | }
6 |
--------------------------------------------------------------------------------
/test/pages/template.js:
--------------------------------------------------------------------------------
1 | function(data) {
2 | return 'Hello, ' + data.username + '';
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/test/pages/bigfile-cached.jsx:
--------------------------------------------------------------------------------
1 | de.file('auths.json', {
2 | key: 'auths.json',
3 | maxage: '+1h'
4 | })
5 |
--------------------------------------------------------------------------------
/test/pages/hello.jsx:
--------------------------------------------------------------------------------
1 | de.block({
2 | username: 'nop'
3 | }, {
4 | template: 'template.js'
5 | })
6 |
7 |
--------------------------------------------------------------------------------
/test/pages/file-cached.jsx:
--------------------------------------------------------------------------------
1 | de.file('locale/ru.json', {
2 | key: 'locale/ru.json',
3 | maxage: '+1h'
4 | })
5 |
6 |
--------------------------------------------------------------------------------
/test/pages/http-cached.jsx:
--------------------------------------------------------------------------------
1 | de.http('http://www.yandex.ru', {
2 | key: 'yandex',
3 | maxage: '+1h'
4 | })
5 |
6 |
--------------------------------------------------------------------------------
/test/pages/json/album.27.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": 27,
3 | "data": {
4 | "title": "Котики"
5 | }
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/test/pages/redirect.jsx:
--------------------------------------------------------------------------------
1 | function(context) {
2 | context.response.setRedirect('http://www.yandex.ru');
3 | return null;
4 | }
5 |
--------------------------------------------------------------------------------
/test/pages/http.jsx:
--------------------------------------------------------------------------------
1 | de.http(
2 | 'http://www.yandex.ru',
3 | {
4 | method: 'POST',
5 | timeout: 3000
6 | }
7 | )
8 |
--------------------------------------------------------------------------------
/test/pages/json/photo.42.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": 42,
3 | "album_id": 27,
4 | "data": {
5 | "url": "42.jpg"
6 | }
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "eqnull": true,
3 | "shadow": true,
4 | "evil": true,
5 | "expr": true,
6 | "loopfunc": true,
7 | "proto": true,
8 | "sub": true
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/test/pages/cookie.jsx:
--------------------------------------------------------------------------------
1 | function(context) {
2 | var response = context.response;
3 |
4 | response.setCookie('a', 42);
5 | response.setCookie('b', 77);
6 |
7 | return 'Cookies set'
8 | }
9 |
--------------------------------------------------------------------------------
/test/pages/index.jsx:
--------------------------------------------------------------------------------
1 | {
2 | common: 'common/common.jsx' +100,
3 |
4 | answer: 42// ,
5 |
6 | // yandex: 'http://www.yandex.ru',
7 |
8 | // search: 'http://yandex.ru/yandsearch?'
9 | }
10 |
--------------------------------------------------------------------------------
/test/pages/result.jsx:
--------------------------------------------------------------------------------
1 | de.func(function() {
2 | return {
3 | s1: 1,
4 | s2: 2,
5 | s3: 3
6 | };
7 | }, {
8 | result: {
9 | d1: ".s1",
10 | d2: ".s2",
11 | d3: ".s3"
12 | }
13 | }
14 | )
15 |
--------------------------------------------------------------------------------
/test/config.js:
--------------------------------------------------------------------------------
1 | {
2 | port: 2000,
3 |
4 | rootdir: './pages',
5 |
6 | id: 42,
7 |
8 | log: {
9 | level: 'debug'
10 | }
11 |
12 | /*
13 | modules: {
14 | ya: 'modules/ya.js'
15 | },
16 |
17 | blackbox: {
18 | url: 'blackbox-mimino.yandex.net/blackbox',
19 | domain: 'fotki.yandex.ru'
20 | }
21 | */
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | descript
2 | ========
3 |
4 | Прокся и агрегатор данных, являющаяся в (каком-то смысле) аналогом `xscript`,
5 | но работающая с данными в формате `json`.
6 |
7 | Подробнее в [вики](https://github.com/pasaran/descript/wiki/syntax).
8 |
9 | ## quick start
10 |
11 | ```
12 | npm install
13 | ./descript --config test/config.js
14 | # Open http://localhost:2000/hello.jsx in your favorite browser.
15 | ```
16 |
--------------------------------------------------------------------------------
/lib/de.js:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------- //
2 | // de
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var de = {};
6 |
7 | // --------------------------------------------------------------------------------------------------------------- //
8 |
9 | module.exports = de;
10 |
11 | // --------------------------------------------------------------------------------------------------------------- //
12 |
13 |
--------------------------------------------------------------------------------
/descript:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var de = require('./lib/de.js');
6 |
7 | require('./lib/de.server.js');
8 |
9 | // --------------------------------------------------------------------------------------------------------------- //
10 |
11 | de.server.init();
12 |
13 | de.server.start();
14 |
15 | // --------------------------------------------------------------------------------------------------------------- //
16 |
17 | // vim: set filetype=javascript:
18 |
19 |
--------------------------------------------------------------------------------
/test/dummy/chunk-json:
--------------------------------------------------------------------------------
1 | {
2 | "status": {
3 | "value": "VALID",
4 | "id": 0
5 | },
6 | "error": "OK",
7 | "age": 228005,
8 | "auth": {
9 | "password_verification_age": 228005,
10 | "have_password": true,
11 | "secure": false,
12 | "allow_plain_text": true
13 | },
14 | "uid": {
15 | "value": "24171229",
16 | "lite": false,
17 | "hosted": false,
18 | "domid": "",
19 | "domain": "",
20 | "mx": ""
21 | },
22 | "login": "alpha-san",
23 | "karma": {
24 | "value": 0
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/pages/local.jsx:
--------------------------------------------------------------------------------
1 | de.object(
2 | {
3 | foo: de.value(
4 | 42,
5 | {
6 | after: function(params, context) {
7 | context.state.foo = 42;
8 | }
9 | }
10 | ),
11 | bar: de.value(
12 | 24,
13 | {
14 | after: function(params, context) {
15 | context.state.bar = 24;
16 | }
17 | }
18 | )
19 | },
20 | {
21 | result: {
22 | result: '.',
23 | state: 'state'
24 | },
25 | local: true
26 | }
27 | )
28 |
29 |
--------------------------------------------------------------------------------
/test/dummy/chunk-xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | VALID
4 |
5 | OK
6 | 228005
7 |
8 | 228005
9 | true
10 | false
11 | true
12 |
13 |
14 | 24171229
15 | false
16 | false
17 |
18 |
19 |
20 |
21 | alpha-san
22 |
23 | 0
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/de.logger.js:
--------------------------------------------------------------------------------
1 | require('no.colors');
2 |
3 | function logger(level, msg) {
4 | switch (level) {
5 | case 'info':
6 | console.log(msg.green);
7 | break;
8 |
9 | case 'debug':
10 | console.log(msg);
11 | break;
12 |
13 | case 'error':
14 | console.error(msg.red);
15 | break;
16 |
17 | case 'warn':
18 | console.error(msg.yellow);
19 | break;
20 | }
21 | }
22 |
23 | // --------------------------------------------------------------------------------------------------------------- //
24 |
25 | module.exports = logger;
26 |
27 | // --------------------------------------------------------------------------------------------------------------- //
28 |
29 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------- //
2 | // de.*
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var de = require('./de.js');
6 |
7 | require('./de.file.js');
8 | require('./de.http.js');
9 |
10 | require('./de.common.js');
11 | require('./de.block.js');
12 | require('./de.context.js');
13 | require('./de.request.js');
14 | require('./de.response.js');
15 | require('./de.result.js');
16 | require('./de.script.js');
17 | require('./de.server.js');
18 |
19 | // --------------------------------------------------------------------------------------------------------------- //
20 |
21 | module.exports = de;
22 |
23 |
--------------------------------------------------------------------------------
/test/pages/test.01.jsx:
--------------------------------------------------------------------------------
1 | // /test.01.jsx
2 | // /test.01.jsx?skip=yes
3 |
4 | de.block({
5 | /// photo: de.file('json/photo.{ config.id }.json', {
6 | photo: de.file('json/photo.{ state.photo_id }.json', {
7 | selectBefore: {
8 | photo_id: 42
9 | },
10 | selectAfter: {
11 | album_id: '.album_id'
12 | },
13 | guard: '.skip != "yes"'
14 | }) +10,
15 | album: de.file('json/album.{ state.album_id }.json', {
16 | selectAfter: {
17 | album_data: '.data'
18 | }
19 | })
20 | }, {
21 | result: {
22 | album: '.album',
23 | photo_data: '.photo.data',
24 | state: 'state'
25 | }
26 | /*
27 | result: function(params, context) {
28 | return "hello";
29 | }
30 | */
31 | })
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": {
3 | "name": "Sergey Nikitin",
4 | "email": "nik.pasaran@gmail.com",
5 | "url": "https://github.com/pasaran"
6 | },
7 | "name": "descript",
8 | "description": "descript",
9 | "version": "0.0.64",
10 | "homepage": "https://github.com/pasaran/descript",
11 | "repository": {
12 | "type": "git",
13 | "url": "git://github.com/pasaran/descript.git"
14 | },
15 | "dependencies": {
16 | "nopt": "*",
17 | "nommon": "0.0.28",
18 | "no.colors": "0.0.1"
19 | },
20 | "devDependencies": {},
21 | "optionalDependencies": {},
22 | "engines": {
23 | "node": "*"
24 | },
25 | "bin": "descript",
26 | "main": "lib/index.js",
27 | "files": [
28 | "lib/index.js",
29 | "lib/de*.js",
30 | "descript"
31 | ]
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/lib/de.log.js:
--------------------------------------------------------------------------------
1 | var no = require('nommon');
2 |
3 | var de = require('./de.js');
4 |
5 | // --------------------------------------------------------------------------------------------------------------- //
6 |
7 | var LEVELS = {
8 | off: 0,
9 | error: 1,
10 | warn: 2,
11 | info: 3,
12 | debug: 4
13 | };
14 |
15 | var LEVEL = LEVELS[ de.config.log.level ];
16 | var LOGGER = de.config.log.logger;
17 | var DATE_FORMAT = de.config.log.dateformat || '[%d.%m.%Y %H:%M:%S.%f]';
18 |
19 | // --------------------------------------------------------------------------------------------------------------- //
20 |
21 | var format_date = no.date.formatter(DATE_FORMAT);
22 |
23 | de.log = function(level, msg) {
24 | if ( LEVELS[level] <= LEVEL ) {
25 | var timestamp = format_date( new Date() );
26 |
27 | LOGGER(level, timestamp + ' [' + level + '] ' + msg);
28 | }
29 | };
30 |
31 | // --------------------------------------------------------------------------------------------------------------- //
32 |
33 | de.log.error = function(msg) {
34 | de.log('error', msg);
35 | };
36 |
37 | de.log.warn = function(msg) {
38 | de.log('warn', msg);
39 | };
40 |
41 | de.log.info = function(msg) {
42 | de.log('info', msg);
43 | };
44 |
45 | de.log.debug = function(msg) {
46 | de.log('debug', msg);
47 | };
48 |
49 | // --------------------------------------------------------------------------------------------------------------- //
50 |
51 |
--------------------------------------------------------------------------------
/lib/de.request.js:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------- //
2 | // de.Request
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var url_ = require('url');
6 |
7 | // --------------------------------------------------------------------------------------------------------------- //
8 |
9 | var no = require('nommon');
10 | var de = require('./de.js');
11 |
12 | require('./de.common.js');
13 |
14 | // --------------------------------------------------------------------------------------------------------------- //
15 |
16 | var http_ = require('http');
17 |
18 | // --------------------------------------------------------------------------------------------------------------- //
19 |
20 | /**
21 | @param {http_.IncomingMessage} request Входящий http-реквест.
22 | @param {Object=} extra_params Дополнительные параметры, например, из post-запроса.
23 | */
24 | de.Request = function(request, extra_params) {
25 | this.headers = request.headers;
26 | this.cookies = de.parseCookies(this.headers.cookie || '');
27 |
28 | this.url = url_.parse(request.url, true);
29 | if (extra_params) {
30 | no.extend(this.url.query, extra_params);
31 | }
32 |
33 | this.method = request.method;
34 | };
35 |
36 | // --------------------------------------------------------------------------------------------------------------- //
37 |
38 |
--------------------------------------------------------------------------------
/test/pages/state.jsx:
--------------------------------------------------------------------------------
1 | de.object({
2 | func: de.func(function() {
3 | return {
4 | idUser: '213948712394',
5 | devices: {
6 | desktop: {os: 'mac'}
7 | },
8 | emails: [
9 | 'user@yo.ru', 'user@yo.com'
10 | ]
11 | };
12 | }, {
13 | state: function(data, context) {
14 | return {
15 | models: {
16 | user: {
17 | id: data.idUser,
18 | emails: data.emails
19 | },
20 | devices: data.devices
21 | }
22 | }
23 | }
24 | }
25 | ),
26 | jresult: de.func(function() {
27 | return {
28 | lang: 'ru',
29 | settings: {
30 | view: 'tiles',
31 | hasCamera: false,
32 | hasPublished: true
33 | },
34 | accounts: [
35 | 'user@mail.ru', 'user@gmail.com'
36 | ]
37 | };
38 | }, {
39 | state: {
40 | models: {
41 | user: {
42 | language: '.lang',
43 | emails: '.accounts'
44 | },
45 | settings: '.settings'
46 | }
47 | }
48 | }
49 | )
50 | }, {
51 | result: function(data, context) {
52 | return context.state;
53 | }
54 | })
--------------------------------------------------------------------------------
/todo.md:
--------------------------------------------------------------------------------
1 | TODO
2 | ====
3 |
4 | Делать
5 | ------
6 |
7 | * У всех de.Result должно быть свойство datatype.
8 | Выставлять content-type соответственно ему.
9 |
10 | * Сейчас no.de.http не возвращает content-type ответа,
11 | так что сложно автоматически выставить datatype.
12 |
13 | * options.template -- перблочные шаблоны.
14 | Как минимум для yate и каких-нибудь популярных (mustache/handlebars/...).
15 |
16 | * Нужны тесты.
17 |
18 | * Пробрасывать таймуаты между составными блоками и примитивами.
19 | Например, если в объекте одному из блоков сделать таймаут больше дефолтного,
20 | его все равно тормознет дефолтный таймаут объекта.
21 |
22 | Тоже самое про datatype.
23 |
24 | * Использовать path в nopt'е, чтобы сразу резолвить пути, заданные в командной строке.
25 |
26 | * Использовать http.STATUS_CODES http://nodejs.org/api/http.html#http_http_status_codes
27 |
28 | * Сделать метод у de.Script'а для запуска .jsx файла.
29 |
30 | * Подумать, нельзя ли заюзать no.Future как-нибудь.
31 |
32 | * Путь к сокету (из конфига) нужно резолвить.
33 |
34 | Сделано или закрыто
35 | -------------------
36 |
37 | + Переопределяемый usage().
38 |
39 | + Унести de.Script.create внутрь конструктора.
40 |
41 | + Перестать хотеть одновременной работы нескольких инстансов de.Script.
42 | Сделать de.Script синглтоном?
43 |
44 | - В основном цикле de.Script'а использовать this._includes вместо _cache.
45 | NOTE: Этого делать нельзя, т.к. в этом случае run зацикливается.
46 | Это разные сущности -- страницы и выполненные файлы .jsx.
47 |
48 | + Унести тело основного цикла de.Script'а в отдельный метод,
49 | чтобы его можно было бы переопределить.
50 |
51 | + В частности нужен заглушечный веб-сервер с набором разных ручек (с задержками и т.д.).
52 |
53 |
--------------------------------------------------------------------------------
/lib/de.sandbox.js:
--------------------------------------------------------------------------------
1 | var de = require('./de.js');
2 |
3 | require('./de.block.js');
4 | require('./de.log.js');
5 |
6 | // --------------------------------------------------------------------------------------------------------------- //
7 |
8 | de.sandbox = {};
9 |
10 | // --------------------------------------------------------------------------------------------------------------- //
11 |
12 | de.sandbox.config = de.config;
13 |
14 | de.sandbox.block = de.Block.compile;
15 |
16 | de.sandbox.http = function(url, options) {
17 | return new de.Block.Http(url, options);
18 | };
19 |
20 | de.sandbox.file = function(filename, options) {
21 | return new de.Block.File(filename, options);
22 | };
23 |
24 | de.sandbox.include = function(filename, options) {
25 | return new de.Block.Include(filename, options);
26 | };
27 |
28 | de.sandbox.call = function(call, options) {
29 | return new de.Block.Call(call, options);
30 | };
31 |
32 | de.sandbox.array = function(array, options) {
33 | return new de.Block.Array(array, options);
34 | };
35 |
36 | de.sandbox.object = function(object, options) {
37 | return new de.Block.Object(object, options);
38 | };
39 |
40 | de.sandbox.value = function(value, options) {
41 | return new de.Block.Value(value, options);
42 | };
43 |
44 | de.sandbox.func = function(func, options) {
45 | return new de.Block.Function(func, options);
46 | };
47 |
48 | de.sandbox.expr = function(expr, options) {
49 | return new de.Block.Expr(expr, options);
50 | };
51 |
52 | de.sandbox.Result = de.Result;
53 |
54 | // --------------------------------------------------------------------------------------------------------------- //
55 |
56 | de.sandbox.log = de.log;
57 |
58 | // --------------------------------------------------------------------------------------------------------------- //
59 |
60 |
--------------------------------------------------------------------------------
/test/modules/ya.js:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------- //
2 | // module.ya
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var querystring_ = require('querystring');
6 |
7 | var de = require('../../lib/de.js');
8 | var Result = require('../../lib/de.result.js');
9 |
10 | // --------------------------------------------------------------------------------------------------------------- //
11 |
12 | var ya = {};
13 |
14 | // --------------------------------------------------------------------------------------------------------------- //
15 |
16 | ya.auth = function(descript, promise, context, params) {
17 | var blackboxConfig = descript.config.blackbox;
18 | var request = context.request;
19 |
20 | var host = blackboxConfig.host;
21 | var path = blackboxConfig.path + '?' + querystring_.stringify({
22 | 'method': 'sessionid',
23 | 'userip': request.headers['x-real-ip'],
24 | 'sessionid': request.cookies['Session_id'] || '',
25 | 'host': blackboxConfig['domain'],
26 | 'format': 'json'
27 | });
28 |
29 | de.http.get(
30 | {
31 | 'host': host,
32 | 'path': path,
33 | 'port': 80
34 | }
35 | )
36 | .then(function(result) {
37 | promise.resolve( new Result.Raw(result, true) );
38 | })
39 | .else_(function(error) {
40 | promise.resolve( new Result.Error(error) );
41 | });
42 | };
43 |
44 | // --------------------------------------------------------------------------------------------------------------- //
45 |
46 | module.exports = ya;
47 |
48 | // --------------------------------------------------------------------------------------------------------------- //
49 |
50 |
--------------------------------------------------------------------------------
/test/dummy/chunk-txt:
--------------------------------------------------------------------------------
1 | Парцелла двумерно растворяет ил, что дает возможность использования данной методики как универсальной.
2 | В первом приближении водопотребление концентрирует ион-селективный аллювий,
3 | и этот процесс может повторяться многократно. В случае смены водного режима
4 | журавчик последовательно ускоряет журавчик даже в том случае,
5 | если непосредственное наблюдение этого явления затруднительно.
6 | Траншея наблюдаема.
7 |
8 | В ходе почвенно-мелиоративного исследования территории было установлено,
9 | что парарендзина методологически увлажняет педон даже в том случае,
10 | если непосредственное наблюдение этого явления затруднительно.
11 | Заиливание иссушает пахотный шаг смешения, что дает возможность использования данной методики
12 | как универсальной. Коллоид, как того требуют законы термодинамики,
13 | отталкивает водонасыщенный ортштейн даже в том случае, если непосредственное
14 | наблюдение этого явления затруднительно. Расклинивание горизонтально
15 | продуцирует турбулентный псевдомицелий, вне зависимости от предсказаний
16 | теоретической модели явления. Суффозия флуктуационно дает иловатый горизонт,
17 | вне зависимости от предсказаний теоретической модели явления.
18 |
19 | Гистерезис ОГХ концентрирует влагомер в полном соответствии с законом Дарси.
20 | Переуплотнение продуцирует почвообразующий капилляр даже в том случае,
21 | если непосредственное наблюдение этого явления затруднительно.
22 | Конечно, нельзя не принять во внимание тот факт,
23 | что реология иссушает почвенно-мелиоративный почвообразовательный процесс
24 | только в отсутствие тепло- и массообмена с окружающей средой.
25 | Явление по определению локально растягивает генетический режим,
26 | хотя этот факт нуждается в дальнейшей тщательной экспериментальной проверке.
27 | Конечно, нельзя не принять во внимание тот факт, что воздухосодержание
28 | вызывает ламинарный бур в полном соответствии с законом Дарси.
29 | Впитывание, как следствие уникальности почвообразования в данных условиях, синфазно.
30 |
31 |
--------------------------------------------------------------------------------
/lib/de.response.js:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------- //
2 | // de.Response
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var de = require('./de.js');
6 |
7 | // --------------------------------------------------------------------------------------------------------------- //
8 |
9 | de.Response = function() {
10 | // FIXME: Кажется тут должен быть массив, а не объект.
11 | // Вполне может быть несколько одинаковых заголовков.
12 | this.headers = {};
13 |
14 | this.cookies = {};
15 |
16 | this.status = 200;
17 | };
18 |
19 | // --------------------------------------------------------------------------------------------------------------- //
20 |
21 | de.Response.prototype.setHeader = function(name, value) {
22 | this.headers[name] = String(value);
23 | };
24 |
25 | // FIXME: Expires, encoding, ...
26 | de.Response.prototype.setCookie = function(name, value) {
27 | this.cookies[name] = value;
28 | };
29 |
30 | de.Response.prototype.setStatus = function(status) {
31 | this.status = status;
32 | };
33 |
34 | de.Response.prototype.setRedirect = function(location) {
35 | this.location = location;
36 | };
37 |
38 | // --------------------------------------------------------------------------------------------------------------- //
39 |
40 | function escapeHeader(header) {
41 | return header
42 | .replace(/([\uD800-\uDBFF][\uDC00-\uDFFF])+/g, encodeURI) // валидные суррогатные пары
43 | .replace(/[\uD800-\uDFFF]/g, '') // невалидные половинки суррогатных пар
44 | .replace(/[\u0000-\u001F\u007F-\uFFFF]+/g, encodeURI); // всё остальное непечатное
45 | }
46 |
47 | de.Response.prototype.end = function(response, result) {
48 | var headers = this.headers;
49 | for (var header in headers) {
50 | response.setHeader( header, escapeHeader(headers[header]) );
51 | }
52 |
53 | var cookies = this.cookies;
54 | var cookie = [];
55 | for (var name in cookies) {
56 | cookie.push(escapeHeader(name + '=' + cookies[name]));
57 | }
58 | response.setHeader('Set-Cookie', cookie); // FIXME: Выставлять expire и т.д.
59 |
60 | if (this.location) {
61 | response.statusCode = 302;
62 | response.setHeader('Location', escapeHeader(this.location));
63 | response.end();
64 | return;
65 | }
66 |
67 | response.statusCode = this.status;
68 |
69 | if (result) {
70 | response.setHeader( 'Content-Type', result.contentType() );
71 |
72 | result.write(response);
73 | response.end();
74 | }
75 | };
76 |
77 | // --------------------------------------------------------------------------------------------------------------- //
78 |
79 |
--------------------------------------------------------------------------------
/lib/de.context.js:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------- //
2 | // de.Context
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var no = require('nommon');
6 |
7 | var de = require('./de.js');
8 |
9 | require('./de.request.js');
10 | require('./de.response.js');
11 |
12 | var http_ = require('http');
13 | var url_ = require('url');
14 | var qs_ = require('querystring');
15 |
16 | // --------------------------------------------------------------------------------------------------------------- //
17 |
18 | var _cid = 0;
19 |
20 | /**
21 | @param {(Object | http_.IncomingMessage)} request
22 | @param {Object=} extra_params
23 | */
24 | de.Context = function(request, extra_params) {
25 | this.config = de.config;
26 |
27 | if (request instanceof http_.IncomingMessage) {
28 | this.request = new de.Request(request, extra_params);
29 | this.query = this.request.url.query;
30 | } else {
31 | this.request = null;
32 | this.query = request;
33 | }
34 |
35 | this.response = new de.Response();
36 |
37 | this.state = {};
38 | this.now = Date.now();
39 |
40 | this.id = process.pid + '.' + _cid++;
41 | };
42 |
43 | // Нечестный "клон".
44 | // Нужен для подмены state'а в локальных блоках.
45 | //
46 | de.Context.prototype.clone = function() {
47 | var clone = {};
48 | clone.__proto__ = this;
49 |
50 | return clone;
51 | };
52 |
53 | // --------------------------------------------------------------------------------------------------------------- //
54 |
55 | /**
56 | @param {(Object | http_.IncomingMessage)} request
57 | */
58 | de.Context.create = function(request) {
59 | var promise = new no.Promise();
60 |
61 | if (
62 | request.method === 'POST' &&
63 | de.mime(request.headers) === 'application/x-www-form-urlencoded'
64 | ) {
65 | var body = '';
66 |
67 | request.on('data', function(data) {
68 | body += data;
69 | });
70 | request.on('end', function() {
71 | var extra_params = qs_.parse(body);
72 |
73 | promise.resolve( new de.Context(request, extra_params) );
74 | });
75 | } else {
76 | promise.resolve( new de.Context(request) );
77 | }
78 |
79 | return promise;
80 | };
81 |
82 | // --------------------------------------------------------------------------------------------------------------- //
83 |
84 | de.Context.prototype.log = function(level, msg) {
85 | de.log(level, '[' + this.id + '] ' + msg);
86 | };
87 |
88 | de.Context.prototype.error = function(msg) {
89 | this.log('error', msg);
90 | };
91 |
92 | de.Context.prototype.warn = function(msg) {
93 | this.log('warn', msg);
94 | };
95 |
96 | de.Context.prototype.info = function(msg) {
97 | this.log('info', msg);
98 | };
99 |
100 | de.Context.prototype.debug = function(msg) {
101 | this.log('debug', msg);
102 | };
103 |
104 | de.Context.prototype.log_end = function(level, msg, t1) {
105 | var diff = Date.now() - t1;
106 | if (this.config.log.slow && diff > this.config.log.slow) {
107 | level = 'warn';
108 | }
109 | this.log( level, msg + ' (' + diff + 'ms)' );
110 | };
111 |
112 | // --------------------------------------------------------------------------------------------------------------- //
113 |
114 |
--------------------------------------------------------------------------------
/lib/de.server.js:
--------------------------------------------------------------------------------
1 | var os_ = require('os');
2 | var cluster_ = require('cluster');
3 | var path_ = require('path');
4 | var http_ = require('http');
5 | var fs_ = require('fs');
6 |
7 | var de = require('./de.js');
8 |
9 | require('./de.file.js');
10 | require('./de.script.js');
11 | require('./de.block.js');
12 | require('./de.context.js');
13 | require('./de.result.js');
14 |
15 | // --------------------------------------------------------------------------------------------------------------- //
16 |
17 | de.server = {};
18 |
19 | // --------------------------------------------------------------------------------------------------------------- //
20 |
21 | var _server;
22 |
23 | // --------------------------------------------------------------------------------------------------------------- //
24 |
25 | de.server.init = function(config) {
26 | config = de.script.init(config);
27 |
28 | if ( !(config.port || config.socket) || config.help ) {
29 | usage();
30 | }
31 | };
32 |
33 | // --------------------------------------------------------------------------------------------------------------- //
34 |
35 | function usage() {
36 | var name = ' ' + path_.basename(require.main.filename);
37 |
38 | console.log('Usage:');
39 | console.log(name + ' --port 2000 --rootdir test/pages');
40 | console.log(name + ' --socket descript.sock --rootdir test/pages');
41 | console.log(name + ' --config test/config.json');
42 | console.log();
43 |
44 | process.exit(0);
45 | }
46 |
47 | // --------------------------------------------------------------------------------------------------------------- //
48 |
49 | de.server.start = function() {
50 | var workers = de.config.workers || ( os_.cpus().length - 1 );
51 |
52 | if (cluster_.isMaster) {
53 | // cluster_.setupMaster({ silent: true });
54 |
55 | de.log.info('master started. pid = ' + process.pid);
56 |
57 | var fork_worker = function() {
58 | var forked = cluster_.fork();
59 | de.log.info('process forked. pid = ' + forked.process.pid);
60 |
61 | /*
62 | forked.process.stdout.on('data', function(data) {
63 | process.stdout.write(data);
64 | });
65 | forked.process.stderr.on('data', function(data) {
66 | process.stderr.write(data);
67 | });
68 | */
69 | };
70 |
71 | // Fork workers.
72 | for (var i = 0; i < workers; i++) {
73 | fork_worker();
74 | }
75 |
76 | cluster_.on('exit', function(worker, code, signal) {
77 | de.log.error('process died. pid = ' + worker.process.pid + ' code = ' + code + ' signal = ' + signal);
78 | fork_worker();
79 | });
80 |
81 | } else {
82 | _server = http_.createServer(function(req, res) {
83 | // Если это post-запрос, то его параметры приходится получать
84 | // асинхронно. Поэтому de.Context.create() возвращает promise.
85 | //
86 | de.Context.create(req).done(function(context) {
87 | de.server.onrequest(req, res, context);
88 | });
89 | });
90 |
91 | if (de.config.socket) {
92 | _server.listen(de.config.socket, function() {
93 | // FIXME: Опять забыл, зачем нужна эта строчка.
94 | fs_.chmodSync(this.address(), 0777);
95 | });
96 | } else {
97 | _server.listen(de.config.port, '::', 'localhost');
98 | }
99 |
100 | }
101 | };
102 |
103 | // --------------------------------------------------------------------------------------------------------------- //
104 |
105 | de.server.stop = function() {
106 | if (_server) {
107 | _server.close();
108 | _server = null;
109 | }
110 |
111 | de.file.unwatch();
112 | };
113 |
114 | // --------------------------------------------------------------------------------------------------------------- //
115 |
116 | de.server.route = function(req, res, context) {
117 | var path = context.request.url.pathname || '';
118 | if ( path.charAt(0) === '/' ) {
119 | path = path.substr(1);
120 | }
121 | path = path || 'index.jsx';
122 |
123 | return path;
124 | };
125 |
126 | de.server.onrequest = function(req, res, context) {
127 | var t1 = Date.now();
128 |
129 | var block = de.server.route(req, res, context);
130 | if ( !(block instanceof de.Block) ) {
131 | block = new de.Block.Include(block);
132 | }
133 |
134 | // FIXME: Добавить сюда ip, headers, честный path из запроса.
135 | block.run(context.query, context)
136 | .done(function(result) {
137 | if (result instanceof de.Result.Error && result.get('id') === 'FILE_OPEN_ERROR') {
138 | res.statusCode = 404;
139 | res.end( result.formatted() );
140 | return;
141 | }
142 |
143 | context.response.end(res, result);
144 |
145 | })
146 | .always(function() {
147 | context.log_end('info', '[request ' + JSON.stringify(req.url) + '] ended', t1);
148 | });
149 | };
150 |
151 | // --------------------------------------------------------------------------------------------------------------- //
152 |
153 |
--------------------------------------------------------------------------------
/lib/de.common.js:
--------------------------------------------------------------------------------
1 | var de = require('./de.js');
2 |
3 | var no = require('nommon');
4 |
5 | var path_ = require('path');
6 | var vm_ = require('vm');
7 |
8 | // --------------------------------------------------------------------------------------------------------------- //
9 |
10 | de.parseCookies = function(cookie) {
11 | var cookies = {};
12 |
13 | var parts = cookie.split(';');
14 | for (var i = 0, l = parts.length; i < l; i++) {
15 | var r = parts[i].match(/^\s*([^=]+)=(.*)$/);
16 | if (r) {
17 | cookies[ r[1] ] = r[2];
18 | }
19 | }
20 |
21 | return cookies;
22 | };
23 |
24 | // --------------------------------------------------------------------------------------------------------------- //
25 |
26 | de.duration = function(s) {
27 | if (typeof s === 'number') {
28 | return s;
29 | }
30 |
31 | var parts = s.split(/(\d+)([dhms])/);
32 | var d = 0;
33 |
34 | for (var i = 0, l = parts.length; i < l; i += 3) {
35 | var n = +parts[i + 1];
36 |
37 | switch (parts[i + 2]) {
38 | case 'd':
39 | d += n * (60 * 60 * 24);
40 | break;
41 | case 'h':
42 | d += n * (60 * 60);
43 | break;
44 | case 'm':
45 | d += n * (60);
46 | break;
47 | case 's':
48 | d += n;
49 | break;
50 | }
51 | }
52 |
53 | return d * 1000;
54 | };
55 |
56 | // --------------------------------------------------------------------------------------------------------------- //
57 |
58 | de.eval = function(js, namespace, sandbox, filename) {
59 | // FIXME: Таки пострелять в оба вариант.
60 | // Вроде в 0.10.2 разница в пределах погрешности измерения.
61 | // Если это действительно так, то лучше использовать vm.
62 |
63 | var sb = {};
64 | if (namespace && sandbox) {
65 | sb[namespace] = sandbox;
66 | }
67 |
68 | // Прокидываем в песочницу для удобства:
69 | if (filename) {
70 | var dirname = path_.dirname( path_.resolve(filename) );
71 | sb.require = function(filename) {
72 | if ( filename.charAt(0) === '.' ) {
73 | return require( path_.resolve(dirname, filename) );
74 | } else {
75 | return require(filename);
76 | }
77 | };
78 | } else {
79 | sb.require = require;
80 | }
81 | sb.console = console;
82 | // Array нужен, чтобы правильно работал instanceof Array.
83 | // FIXME: Лучше бы везде заменить на Array.isArray.
84 | // sb.Array = Array;
85 |
86 | return vm_.runInNewContext('(' + js + ')', sb, filename);
87 | /*
88 | */
89 |
90 | /*
91 | if (namespace) {
92 | return Function( namespace, 'global', '"use strict"; return (' + js + ');' )(sandbox);
93 | } else {
94 | return Function( 'global', '"use strict"; return (' + js + ');' )();
95 | }
96 | */
97 | };
98 |
99 | // --------------------------------------------------------------------------------------------------------------- //
100 |
101 | de.error = function(error) {
102 | return new de.Result.Error(error);
103 | };
104 |
105 | // --------------------------------------------------------------------------------------------------------------- //
106 |
107 | de.forward = function(src, dest) {
108 | src.pipe(dest);
109 | dest.forward('abort', src);
110 | };
111 |
112 | // --------------------------------------------------------------------------------------------------------------- //
113 |
114 | de.mime = function(headers) {
115 | var s = headers['content-type'] || '';
116 |
117 | var i = s.indexOf(';');
118 | return (i === -1) ? s : s.substr(0, i);
119 | };
120 |
121 | // --------------------------------------------------------------------------------------------------------------- //
122 |
123 | de.events = no.extend( {}, no.Events );
124 |
125 | // --------------------------------------------------------------------------------------------------------------- //
126 |
127 | var pid = process.pid;
128 | var gid = 0;
129 |
130 | de.id = function() {
131 | return pid + '.' + gid++;
132 | };
133 |
134 | // --------------------------------------------------------------------------------------------------------------- //
135 |
136 | /**
137 | * Merges (deeply extends) `dest` object with `src`. At each node
138 | * - concats old and new value if both are arrays,
139 | * - walks deeper only if both values are objects (and not null of course),
140 | * - replaces old value with new if one of them is not an object.
141 | * @param {!Object} dest
142 | * @param {!Object} src
143 | * @return {!Object}
144 | */
145 | de.mergeObjects = function(dest, src) {
146 | for (var key in src) {
147 | var value = src[key];
148 | if (Array.isArray(value) && Array.isArray(dest[key])) {
149 | dest[key] = dest[key].concat(value);
150 | } else if (isObjectSimple(dest[key]) && isObjectSimple(value)) {
151 | dest[key] = de.mergeObjects(dest[key], value);
152 | } else {
153 | dest[key] = value;
154 | }
155 | }
156 | return dest;
157 | };
158 |
159 | var isObjectSimple = function(source) {
160 | return source !== null && typeof source === 'object' && !Array.isArray(source);
161 | };
--------------------------------------------------------------------------------
/lib/de.script.js:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------- //
2 | // de.script
3 | // --------------------------------------------------------------------------------------------------------------- //
4 |
5 | var de = require('./de.js');
6 |
7 | require('./de.common.js');
8 |
9 | var no = require('nommon');
10 |
11 | // --------------------------------------------------------------------------------------------------------------- //
12 |
13 | var fs_ = require('fs');
14 | var path_ = require('path');
15 |
16 | var nopt = require('nopt');
17 |
18 | // --------------------------------------------------------------------------------------------------------------- //
19 |
20 | de.script = {};
21 |
22 | // --------------------------------------------------------------------------------------------------------------- //
23 |
24 | de._modules = {};
25 |
26 | // --------------------------------------------------------------------------------------------------------------- //
27 |
28 | // Конфиг для работы descript'а составляется из трех частей:
29 | //
30 | // * Конфиг из файла. Имя файла либо задано параметром --config,
31 | // либо передано в de.script.init().
32 | //
33 | // * Объект, переданный в de.script.init().
34 | //
35 | // * Параметры, переданные в командной строке.
36 | //
37 | de.script.init = function(config) {
38 | // В случае, когда конфиг берется не из файла,
39 | // относительные имена файлов в нем нужно резолвить
40 | // относительно текущей директории.
41 | var cwd = path_.resolve('.');
42 |
43 | // Читаем все параметры из командной строки.
44 | var args = nopt(
45 | {
46 | port: String,
47 | socket: String,
48 | rootdir: String,
49 | config: String,
50 | help: Boolean,
51 | workers: Number
52 | }
53 | );
54 | // Удаляем лишние поля, генерируемые nopt'ом.
55 | delete args.argv;
56 |
57 | // Параметры из командной строки.
58 | var config_cmdline = prepare_config(args, cwd);
59 | // Параметры из файла с конфигом.
60 | var config_file;
61 | // Параметры, переданные в de.script.init() в виде объекта.
62 | var config_arg;
63 |
64 | var basedir;
65 |
66 | if (config_cmdline.config || typeof config === 'string') {
67 | // Задано имя файла с конфигом.
68 | var config_filename = config_cmdline.config || config;
69 |
70 | basedir = path_.dirname( path_.resolve(config_filename) );
71 |
72 | config_file = prepare_config(
73 | // Читаем конфиг из файла.
74 | read_config(config_filename),
75 | // Относительные имена нужно резолвить от места,
76 | // где лежит файл с конфигом.
77 | basedir
78 | );
79 | }
80 |
81 | if (typeof config === 'object') {
82 | // Объект с конфигом передан в de.script.init().
83 | basedir = basedir || cwd;
84 |
85 | config_arg = prepare_config(config, basedir);
86 | }
87 |
88 | // Склеиваем все три конфига вместе.
89 | // (на самом деле, один из них всегда undefined).
90 | config = no.extend( {}, config_file, config_arg, config_cmdline );
91 |
92 | config = default_config(config, basedir);
93 |
94 | // Если ни в одном конфиге не задан rootdir, то
95 | // используем текущую директорию.
96 | config.rootdir = config.rootdir || cwd;
97 |
98 | de.config = config;
99 |
100 | if (config.modules) {
101 | read_modules(config.modules, basedir);
102 | }
103 |
104 | require('./de.log.js');
105 | require('./de.sandbox.js');
106 |
107 | // Делаем доступным config в .jsx-файлах.
108 | // de.sandbox.config = config;
109 |
110 | return config;
111 |
112 | };
113 |
114 | // --------------------------------------------------------------------------------------------------------------- //
115 |
116 | /**
117 | Резолвим относительные имена файлов, загружаем модули и т.д.
118 |
119 | @param {Object} config
120 | @param {string} basedir Директория, относительно которой нужно резолвить относительные пути файлов.
121 | */
122 | function prepare_config(config, basedir) {
123 | if (!config) {
124 | return;
125 | }
126 |
127 | if (config.socket) {
128 | config.socket = path_.resolve(basedir, config.socket);
129 | }
130 | if (config.rootdir) {
131 | config.rootdir = path_.resolve(basedir, config.rootdir);
132 | }
133 |
134 | return config;
135 | }
136 |
137 | // --------------------------------------------------------------------------------------------------------------- //
138 |
139 | function default_config(config, basedir) {
140 | if (!config) {
141 | return;
142 | }
143 |
144 | if (!config.log) {
145 | config.log = {};
146 | }
147 | if (!config.log.level) {
148 | config.log.level = 'debug';
149 | }
150 | if (!config.log.logger) {
151 | config.log.logger = './de.logger.js';
152 | } else {
153 | config.log.logger = path_.resolve(basedir, config.log.logger);
154 | }
155 | config.log.logger = require(config.log.logger);
156 |
157 | return config;
158 | }
159 |
160 | // --------------------------------------------------------------------------------------------------------------- //
161 |
162 | function read_config(filename) {
163 | var content = fs_.readFileSync(filename, 'utf-8');
164 |
165 | return de.eval(content);
166 | }
167 |
168 | // --------------------------------------------------------------------------------------------------------------- //
169 |
170 | function read_modules(modules, dirname) {
171 | for (var id in modules) {
172 | var filename = path_.join( dirname, modules[id] );
173 |
174 | de._modules[id] = require(filename);
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/lib/de.file.js:
--------------------------------------------------------------------------------
1 | var no = require('nommon');
2 | var de = require('./de.js');
3 |
4 | require('./de.common.js');
5 | require('./de.result.js');
6 |
7 | // --------------------------------------------------------------------------------------------------------------- //
8 |
9 | var fs_ = require('fs');
10 |
11 | // --------------------------------------------------------------------------------------------------------------- //
12 |
13 | de.file = {};
14 |
15 | // --------------------------------------------------------------------------------------------------------------- //
16 |
17 | // Кэш с уже считанными файлами (или файлы, которые в процессе чтения).
18 | // В кэше хранятся promise'ы, в которых хранятся инстансы класса de.Result.File.
19 | var _get_cache = {};
20 |
21 | // Кэш со считанными и исполненными файлами.
22 | // В кэше хранятся promise'ы, в которых хранится результат выполнения файлов.
23 | var _eval_cache = {};
24 |
25 | // За какими файлами мы уже следим (чтобы не делать повторный watch).
26 | var _watched = {};
27 |
28 | // --------------------------------------------------------------------------------------------------------------- //
29 |
30 | de.file.get = function(filename, context) {
31 | var promise = _get_cache[filename];
32 |
33 | if (!promise) {
34 | promise = _get_cache[filename] = new no.Promise();
35 |
36 | var rootdir = de.config.rootdir;
37 | // Проверяем, что файл лежит внутри rootdir.
38 | var is_outside = ( filename.substr(0, rootdir.length) !== rootdir );
39 | if (is_outside) {
40 | var message = 'path \'' + filename + '\' is outside of rootdir';
41 | context.error('[file.open ' + JSON.stringify(filename) + '] ' + message);
42 | return promise.reject( de.error({
43 | id: 'FILE_INVALID_PATH',
44 | message: message
45 | }) );
46 | }
47 |
48 | fs_.readFile(filename, function(error, buffer) {
49 | if (error) {
50 | // Если не удалось считать файл, в следующий раз нужно повторить попытку,
51 | // а не брать из кэша ошибку.
52 | _get_cache[filename] = null;
53 |
54 | // FIXME: Разные коды ошибок в зависимости от.
55 | // Как минимум 404.
56 | context.error('[file.open ' + JSON.stringify(filename) + '] ' + error.message);
57 | promise.reject( de.error({
58 | id: 'FILE_OPEN_ERROR',
59 | message: error.message
60 | }) );
61 | } else {
62 | // Содержимое файла закэшировано внутри promise'а. Следим, не изменился ли файл.
63 | de.file.watch('file-changed', filename);
64 |
65 | promise.resolve( new de.Result.File(filename, buffer) );
66 | }
67 | });
68 | }
69 |
70 | return promise;
71 | };
72 |
73 | de.events.on('file-changed', function(e, filename) {
74 | // Файл изменился, выкидываем его из кэша.
75 | _get_cache[filename] = null;
76 | });
77 |
78 | // --------------------------------------------------------------------------------------------------------------- //
79 |
80 | // Читаем файл и затем исполняем его.
81 | //
82 | // Смысл второго и третьего параметра:
83 | //
84 | // de.file.eval(filename, 'de', {
85 | // file: function() { ... },
86 | // http: function() { ... },
87 | // ...
88 | // }, context);
89 | //
90 | // В этом случае в файле можно будет использовать переменную de,
91 | // в частности, методы de.file, de.http, ...
92 | // Т.е. это некий аналог global.
93 | //
94 | de.file.eval = function(filename, namespace, sandbox, context) {
95 | var promise = _eval_cache[filename];
96 |
97 | if (!promise) {
98 | promise = _eval_cache[filename] = new no.Promise();
99 |
100 | // FIXME: По идее, эти файлы не нужно кэшировать в _get_cache.
101 | de.file.get(filename, context)
102 | .done(function(/** @type {de.Result.File} */ result) {
103 | var evaled;
104 |
105 | try {
106 | evaled = de.eval(result, namespace, sandbox, filename);
107 | } catch (e) {
108 | context.error('[file.eval ' + JSON.stringify(filename) + '] ' + e.message);
109 | promise.reject( de.error({
110 | id: 'EVAL_ERROR',
111 | message: e.message
112 | }) );
113 | }
114 |
115 | de.file.watch('loaded-file-changed', filename);
116 |
117 | promise.resolve(evaled);
118 | })
119 | .fail(function(error) {
120 | _eval_cache[filename] = null;
121 |
122 | promise.reject(error);
123 | });
124 | }
125 |
126 | return promise;
127 | };
128 |
129 | de.events.on('loaded-file-changed', function(e, filename) {
130 | // Файл изменился, выкидываем его из кэша.
131 | _eval_cache[filename] = null;
132 | });
133 |
134 | // --------------------------------------------------------------------------------------------------------------- //
135 |
136 | de.file.watch = function(type, filename) {
137 | var isWatched = ( _watched[type] || (( _watched[type] = {} )) )[filename];
138 |
139 | if (!isWatched) {
140 | _watched[type][filename] = true;
141 |
142 | // FIXME: Непонятно, как это будет жить, когда файлов будет много.
143 | fs_.watchFile(filename, function (curr, prev) {
144 | if ( prev.mtime.getTime() !== curr.mtime.getTime() ) {
145 | de.events.trigger(type, filename);
146 | }
147 | });
148 | }
149 | };
150 |
151 | // NOTE: Если сделать просто de.file.eval() и не вызвать no.file.de.unwatch(),
152 | // то процесс не завершится никогда. Так как будет висеть слушатель изменений файла.
153 | //
154 | de.file.unwatch = function(type, filename) {
155 | if (type) {
156 | var files = _watched[type];
157 |
158 | if (filename) {
159 | if ( files && files[filename] ) {
160 | fs_.unwatchFile(filename);
161 |
162 | delete files[filename];
163 | }
164 | } else {
165 | for (var filename in files) {
166 | fs_.unwatchFile(filename);
167 | }
168 |
169 | _watched[type] = {};
170 | }
171 | } else {
172 | for (type in _watched) {
173 | de.file.unwatch(type);
174 | }
175 |
176 | _watched = {};
177 | }
178 | };
179 |
180 | // --------------------------------------------------------------------------------------------------------------- //
181 |
--------------------------------------------------------------------------------
/lib/de.http.js:
--------------------------------------------------------------------------------
1 | var no = require('nommon');
2 |
3 | var de = require('./de.js');
4 | require('./de.common.js');
5 | require('./de.result.js');
6 |
7 | // --------------------------------------------------------------------------------------------------------------- //
8 |
9 | var url_ = require('url');
10 | var http_ = require('http');
11 | var https_ = require('https');
12 | var qs_ = require('querystring');
13 |
14 | // --------------------------------------------------------------------------------------------------------------- //
15 |
16 | de.http = function(options, params, context) {
17 | var parsed = url_.parse(options.url, true, true);
18 |
19 | parsed = no.extend(parsed, options.http_options);
20 |
21 | if (params) {
22 | parsed.query = no.extend(parsed.query || {}, params);
23 | }
24 |
25 | // в случае пост-запроса и явного указания body в настройках блока,
26 | // нужно передать значение в качестве тела запроса
27 | if ( (options.method === 'post' || options.method === 'put' || options.method === 'patch') && options.body ) {
28 | parsed.query = options.body;
29 | }
30 |
31 | var max_redirects = ( options.max_redirects === undefined ) ? 3 : options.max_redirects;
32 | var only_status = options.only_status;
33 |
34 | parsed.headers = options.headers;
35 | parsed.method = options.method;
36 |
37 | var req;
38 | var promise = new no.Promise();
39 |
40 | promise.on('abort', function() {
41 | req.abort();
42 | });
43 |
44 | var full_path = '[http ' + url_.format(parsed) + '] ';
45 | var http_status = 0;
46 | var received_length = 0;
47 |
48 | var t1 = Date.now();
49 | promise.done(function() {
50 | context.log_end('info', full_path + http_status + ' ' + received_length + ' ended', t1);
51 | });
52 | promise.fail(function() {
53 | context.log_end('info', full_path + http_status + ' failed', t1);
54 | });
55 |
56 | doHttp(parsed, 0);
57 |
58 | return promise;
59 |
60 | function doHttp(options, count) {
61 | var post_data;
62 | if (options.method === 'post' || options.method === 'put' || options.method === 'patch') {
63 | var headers = options.headers || (( options.headers = {} ));
64 |
65 | if ('application/json' === headers['Content-Type']) {
66 | post_data = JSON.stringify(options.query);
67 | } else {
68 | post_data = qs_.stringify(options.query);
69 | }
70 |
71 | if (post_data) {
72 | // FIXME: Если никаких данных не передается, то, кажется,
73 | // эти заголовки и не нужны вовсе?
74 | //
75 | if (!headers['Content-Type']) {
76 | headers['Content-Type'] = 'application/x-www-form-urlencoded';
77 | }
78 | // вычислим значение заголовка Content-Length с учетом
79 | // наличия кириллицы в строке,
80 | // String.prototype.length в данном случае врет
81 | headers['Content-Length'] = Buffer.byteLength(post_data);
82 | }
83 |
84 | } else {
85 | options.path = url_.format({
86 | pathname: options.pathname,
87 | query: options.query
88 | });
89 | }
90 |
91 | req = ( (options.protocol === 'https:') ? https_ : http_).request(options, function(res) {
92 | var status = res.statusCode;
93 |
94 | if (only_status) {
95 | // FIXME: Непонятно, нужно ли это?
96 | // Но если там длинная дата, то ждать ее нет смысла.
97 | req.abort();
98 |
99 | return promise.resolve( new de.Result.Value({
100 | status: status,
101 | headers: res.headers
102 | }) );
103 | }
104 |
105 | if (status === 204) {
106 | res.resume();
107 | return promise.resolve(new de.Result.Value({}));
108 | }
109 |
110 | http_status = status;
111 |
112 | var error;
113 | if (status === 301 || status === 302) {
114 | // TODO: Кэшировать 301 запросы.
115 |
116 | // FIXME: А это нельзя вынести повыше?
117 | res.resume();
118 |
119 | var location = res.headers['location'] || '';
120 |
121 | // FIXME: MAX_REDIRECTS.
122 | if (count >= max_redirects) {
123 | context.error('Too many redirects');
124 | return promise.reject( de.error({
125 | 'id': 'HTTP_TOO_MANY_REDIRECTS',
126 | 'status': status,
127 | 'location': location
128 | }) );
129 | }
130 |
131 | location = url_.resolve(options.href, location);
132 | options = url_.parse(location, true, true);
133 |
134 | context.info(full_path + 'redirected to ' + location);
135 |
136 | return doHttp(options, count + 1);
137 | }
138 |
139 | var result = new de.Result.Http(res.headers);
140 |
141 | res.on('data', function(data) {
142 | received_length += data.length;
143 | result.data(data);
144 | });
145 | res.on('end', function() {
146 | result.end();
147 |
148 | if (status >= 400 && status <= 599) {
149 | context.error(full_path + 'error ' + status);
150 |
151 | var mime = de.mime( res.headers );
152 |
153 | var body;
154 | if ( mime === 'application/json' ) {
155 | try {
156 | body = JSON.parse( result.buffer );
157 | } catch ( e ) {
158 | body = 'JSON.parse: Cannot parse response body';
159 | }
160 | }
161 |
162 | return promise.reject( de.error({
163 | 'id': 'HTTP_' + status,
164 | 'message': http_.STATUS_CODES[status],
165 | 'body': body
166 | }) );
167 |
168 | } else {
169 | promise.resolve(result);
170 | }
171 | });
172 | res.on('close', function(error) {
173 | context.error(full_path + 'error ' + error.message);
174 | promise.reject( de.error({
175 | 'id': 'HTTP_CONNECTION_CLOSED',
176 | 'message': error.message
177 | }) );
178 | });
179 | });
180 |
181 | req.on('error', function(error) {
182 | context.error(full_path + 'error ' + error.message);
183 | promise.reject( de.error({
184 | 'id': 'HTTP_UNKNOWN_ERROR',
185 | 'message': error.message
186 | }) );
187 | });
188 |
189 | if (post_data) {
190 | req.write(post_data);
191 | }
192 | req.end();
193 | }
194 |
195 | };
196 |
197 | // ----------------------------------------------------------------------------------------------------------------- //
198 |
199 | de.http.get = function(url, params, headers, context) {
200 | return de.http(
201 | {
202 | url: url,
203 | headers: headers,
204 | method: 'get'
205 | },
206 | params,
207 | context
208 | );
209 | };
210 |
211 | de.http.post = function(url, params, headers, context) {
212 | return de.http(
213 | {
214 | url: url,
215 | headers: headers,
216 | method: 'post'
217 | },
218 | params,
219 | context
220 | );
221 | };
222 |
223 | // --------------------------------------------------------------------------------------------------------------- //
224 |
--------------------------------------------------------------------------------
/test/dummy/dummy.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Фейковый http-сервер для тестирования http-приложений.
4 | Управляется набором параметров в урле.
5 |
6 | Может возвращать заданную http-ошибку:
7 |
8 | /error=500
9 |
10 | /error=500&message=GOAWAY -- с заданным сообщением об ошибке.
11 |
12 | /error=500&delay=1000 -- вернуть ошибку через секунду, а не сразу.
13 |
14 | Может вернуть содержимое заданного файла:
15 |
16 | /filename=foo.txt
17 |
18 | /filename=foo.txt&delay=1000 -- вернуть весь файл через секунду.
19 |
20 | /filename=foo.txt&delay=1000x5 -- возвращать файл порциями, порций будет 5 штук, интервалы между ними будут в 1 секунду.
21 |
22 | /filename=foo.txt&delay=1000,2000,3000 -- тоже самое, но 3 порции, первая через секунду, вторая еще через 2, третья -- еще через 3.
23 | т.е. весь запрос закончится через 6 секунд.
24 |
25 | Если имя файла не задано, то используется предопределенный контент.
26 | В формате json, txt или xml. Задается параметром datatype.
27 |
28 | / -- возвращает дефолтный json (json -- это дефолтное значение параметра datatype).
29 |
30 | /delay=1000 -- тоже самое, но через секунду.
31 |
32 | /delay=1000&datatype=txt -- тоже самое, но возвращает текстовый контент.
33 |
34 | /delay=1000x5 -- дефолтный json в 5 порций, между порциями задержка в 1 секунду.
35 |
36 | /delay=1000,2000,3000 -- аналогично, но задержки между порциями заданы явно.
37 |
38 |
39 | Как использовать:
40 |
41 | var dummy = require('./dummy.js');
42 |
43 | var port = 2000;
44 | // Запускает сервер на указанном порту.
45 | var server = dummy(port);
46 |
47 | // И где-нибудь можно остановить сервер.
48 | server.close();
49 |
50 |
51 | Или же прямо из командной строки:
52 |
53 | node -e 'require("./dummy.js")(2000);'
54 |
55 | */
56 |
57 | // --------------------------------------------------------------------------------------------------------------- //
58 |
59 | var fs_ = require('fs');
60 | var http_ = require('http');
61 | var url_ = require('url');
62 | var path_ = require('path');
63 |
64 | // --------------------------------------------------------------------------------------------------------------- //
65 |
66 | var contentTypes = {
67 | json: 'application/json',
68 | xml: 'text/xml',
69 | txt: 'text/plain'
70 | };
71 |
72 | var fragments = {
73 | json: fs_.readFileSync( path_.resolve(__dirname, './chunk-json'), 'utf-8'),
74 | xml: fs_.readFileSync( path_.resolve(__dirname, './chunk-xml'), 'utf-8'),
75 | txt: fs_.readFileSync( path_.resolve(__dirname, './chunk-txt'), 'utf-8')
76 | };
77 |
78 | var infos = {
79 | json: {
80 | open: '[',
81 | delim: ',\n',
82 | close: ']'
83 | },
84 | xml: {
85 | open: '',
86 | delim: '\n',
87 | close: ''
88 | },
89 | txt: {
90 | open: '',
91 | delim: '\n\n',
92 | close: ''
93 | },
94 | file: {
95 | open: '',
96 | delim: '',
97 | close: ''
98 | }
99 | };
100 |
101 | // --------------------------------------------------------------------------------------------------------------- //
102 |
103 | module.exports = function(port) {
104 |
105 | return http_
106 | .createServer(function(req, res) {
107 | var params = url_.parse(req.url, true, true).query;
108 |
109 | var error = params.error;
110 | if (error) {
111 | var delay = params.delay || 0;
112 |
113 | setTimeout(function() {
114 | res.writeHead( error, { 'Content-Type': 'text/plain' } );
115 | res.end( params.message || http_.STATUS_CODES[error] || 'Unknown error' );
116 | }, delay);
117 |
118 | return;
119 | }
120 |
121 | var filename = params.filename;
122 | var datatype = getDatatype(filename, params.datatype);
123 |
124 | var delays = getDelays(params.delay);
125 | var l = delays.length;
126 |
127 | var chunks;
128 | var info;
129 |
130 | if (filename) {
131 | var content = fs_.readFileSync(filename, 'utf-8');
132 | chunks = splitString(content, l);
133 | info = infos['file'];
134 | } else {
135 | var fragment = fragments[datatype];
136 | chunks = [];
137 | for (var i = 0; i < l; i++) {
138 | chunks.push(fragment);
139 | }
140 | info = infos[datatype];
141 | }
142 |
143 | res.writeHead(200, { 'Content-Type': contentTypes[datatype] });
144 | res.write(info.open);
145 | for (var i = 0; i < l; i++) {
146 | (function(i) {
147 | setTimeout(function() {
148 | if (i) {
149 | res.write(info.delim);
150 | }
151 | res.write( chunks[i] );
152 | }, delays[i]);
153 | })(i);
154 | }
155 | setTimeout(function() {
156 | res.end(info.close);
157 | }, delays[l - 1]);
158 | })
159 | .listen(port || 8000, '127.0.0.1');
160 |
161 | };
162 |
163 | // --------------------------------------------------------------------------------------------------------------- //
164 |
165 | // Разбиваем строку на n примерно равных кусков.
166 | function splitString(s, n) {
167 | var p = Math.round(s.length / n);
168 |
169 | var chunks = [];
170 | for (var i = 0; i < n - 1; i++) {
171 | chunks.push( s.substr(i * p, p) );
172 | }
173 | chunks.push( s.substr( (n - 1) * p ) );
174 |
175 | return chunks;
176 | }
177 |
178 | // --------------------------------------------------------------------------------------------------------------- //
179 |
180 | // Вычисляем тип ответа. Либо по расширению имени файла (если передано),
181 | // либо по параметру datatype.
182 | function getDatatype(filename, datatype) {
183 | if (!filename) {
184 | return datatype || 'json';
185 | }
186 |
187 | var ext = path_.extname(filename);
188 |
189 | switch (ext) {
190 | case '.json':
191 | return 'json';
192 |
193 | case '.xml':
194 | return 'xml';
195 | }
196 |
197 | return 'txt';
198 | }
199 |
200 | // --------------------------------------------------------------------------------------------------------------- //
201 |
202 | function getDelays(delay) {
203 | if (delay) {
204 | var r;
205 | if (( r = /^(\d+)x(\d+)/.exec(delay) )) {
206 | // Вариант, когда задержки заданы в виде: 1000x5.
207 | // Т.е. 5 интервалов по 1 секунде каждый.
208 | var step = +r[1];
209 | var n = +r[2];
210 |
211 | delays = [];
212 | for (var i = 0; i < n; i++) {
213 | delays.push(step);
214 | }
215 | } else {
216 | // Все задержки заданы явно, например: 1000,2000,3000.
217 | // Т.е. первый чанк выдать через секунду, второй через 2 секунды после этого
218 | // и третий еще через 3 секунды после второго.
219 | delays = delay
220 | .split(',')
221 | // Приводим строки к числу.
222 | .map(function(x) {
223 | return +x;
224 | });
225 | }
226 | } else {
227 | // Дефолтное поведение: отдать все одним чанком без задержек.
228 | delays = [ 0 ];
229 | }
230 |
231 | // Преобразуем относительные задержки в абсолютные (от начала запроса).
232 | // Т.е. [ 1000, 2000, 3000 ] превратятся в [ 1000, 3000, 6000 ].
233 | for (var i = 1; i < delays.length; i++) {
234 | delays[i] += delays[i - 1];
235 | }
236 |
237 | return delays;
238 | }
239 |
240 | // --------------------------------------------------------------------------------------------------------------- //
241 |
242 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # ChangeLog
2 |
3 | ## 0.0.64
4 |
5 | * Merged pull request #130. Header value should be a string.
6 |
7 | ## 0.0.63
8 |
9 | * Merged pull request #129. Не падать, если ответ пуст или сформирован некорректно.
10 | * Merged pull request #126. Экранируем HTTP-заголовки в ответе.
11 | * Merged pull request #124. Don't treat `//` specially.
12 |
13 | ## 0.0.62
14 |
15 | * Merged pull request #118. Fix for PUT and body option.
16 | * Fixed #123. Fix for PATCH and body option.
17 |
18 | ## 0.0.61
19 |
20 | * Merged pull request #117. Небольшие улучшения логирования.
21 |
22 | ## 0.0.60
23 |
24 | * Merged pull request #114. Параметр body http-блока, для явного задания тела пост-запроса.
25 | * Merged pull request #108. Фикс: когда блок успевает выполниться до того, как мы установили таймер таймаута в логи пишется ошибка таймаута.
26 |
27 | ## 0.0.59
28 |
29 | * Merged pull request #103. Вычислять значение Content-Length с учетом UTF-8.
30 |
31 | ## 0.0.58
32 |
33 | * Merged pull request #102. fix overwrite dirname in include block.
34 | * Merged pull request #101. support of JSON in post requests.
35 | * Merged pull request #100. clear timeout when block fails.
36 | * Merged pull request #99. https support + http_options support.
37 |
38 | ## 0.0.57
39 |
40 | * Fix для 0.0.56.
41 |
42 | ## 0.0.56
43 |
44 | * Fixed #96. Странное поведение include блока.
45 |
46 | ## 0.0.55
47 |
48 | * Date format support in `de.config`. Merged pull requests #90, #92.
49 | * Fixed #93. Флаг `only_status` возвращает заголовки. Merged pull request #94.
50 | * Merged pull request #95. `options.headers` in http block.
51 |
52 | ## 0.0.54
53 |
54 | * Fixed #84. Local block.
55 | * Merged pull request #83. Log code and signal on fork process exit
56 | * Merged pull request #82. Removed duplicated method
57 | * Merged pull request #80. Debug log found in cache with key message
58 |
59 | ## 0.0.53
60 |
61 | * Merged pull request #78. Декларативная конструкция для гибкой работы со state.
62 | * Merged pull request #76. de.script.init() config support.
63 | * Merged pull request #75. Fixes #73.
64 | * Merged pull request #72. logging http get params.
65 | * Merged pull request #56. Specify filename for running block.
66 |
67 | ## 0.0.52
68 |
69 | * Fix для 0.0.49. Опечатка.
70 |
71 | ## 0.0.51
72 |
73 | * Fix для 0.0.49. Парсим тело ошибки, если там JSON.
74 |
75 | ## 0.0.50
76 |
77 | * Fix для 0.0.49. Приводим буфер к строке.
78 |
79 | ## 0.0.49
80 |
81 | * Fixed #41. Добавляем в http-ошибки тело ответа.
82 |
83 | ## 0.0.48
84 |
85 | * В `de.http.js` ловим все 4xx/5xx ошибки (раньше был фиксированные и неполный список).
86 |
87 | ## 0.0.47
88 |
89 | * Merged pull request #60.
90 |
91 | ## 0.0.46
92 |
93 | * Откатил #57.
94 |
95 | ## 0.0.45
96 |
97 | * Merged pull request #57.
98 |
99 | ## 0.0.44
100 |
101 | * Merged pull request #55.
102 |
103 | ## 0.0.43
104 |
105 | * Merged pull request #54.
106 |
107 | ## 0.0.42
108 |
109 | * Merged pull request #51.
110 |
111 | * `require` должен работать в исполняемых descript'ом файлах. Например, в шаблонах,
112 | в jsx-файлах и т.д.
113 |
114 | * `nommon` версии `0.0.28`.
115 |
116 | ## 0.0.41
117 |
118 | * Merged pull request #50.
119 |
120 | ## 0.0.40
121 |
122 | * `de.server.route` — метод, возвращающий либо строку с путем к jsx-файлу, либо инсанс `de.Block.*`.
123 |
124 | ## 0.0.39
125 |
126 | * Пофикшен баг, когда внутри `de.Result.Value` оказывается `undefined` (например, если в блоке есть `options.result`,
127 | который вычисляется в `undefined`), после чего метод `write()` падает, потому что в поток можно вывести только `Buffer` или `String`.
128 |
129 | ## 0.0.38
130 |
131 | * Разные доработки по логированию.
132 |
133 | * `nommon` версии `0.0.26`.
134 |
135 | ## 0.0.37
136 |
137 | * Merged pull request #48.
138 |
139 | ## 0.0.36
140 |
141 | * Базовое [логирование](https://github.com/pasaran/descript/issues/46).
142 |
143 | В конфиге можно задать уровень логирования и logger:
144 |
145 | log: {
146 | // По-умолчанию: 'debug'.
147 | // Возможные значения: 'off', 'error', 'warn', 'info', 'debug'.
148 | level: 'debug',
149 |
150 | // См. 'lib/de.logger.js' — дефолтный logger.
151 | logger: './my-logger.js'
152 | }
153 |
154 | * `nommon` версии `0.0.24`.
155 |
156 | ## 0.0.35
157 |
158 | * Merged pull request #44.
159 |
160 | ## 0.0.34
161 |
162 | * Merged pull request #42.
163 |
164 | ## 0.0.33
165 |
166 | * В `.jsx`-файлах доступен конфиг:
167 |
168 | de.object({
169 | // Выводим весь конфиг целиком.
170 | config: de.value(de.config),
171 |
172 | // Выводим отдельное поле конфига
173 | host: de.value(de.config.backend.host)
174 | })
175 |
176 |
177 | ## 0.0.32
178 |
179 | * Ключик `--cpus` переименован в `--workers`.
180 | Вместо `--cpus 2` нужно использовать `--workers 1`.
181 | Дефолтное значение параметра `--workers` — `require('os').cpus().length - 1`.
182 |
183 | * Изменена сигнатура в `options.after` на `(params, context, result)`.
184 | При этом `result` это инстанс класса `de.Result.*`, а не готовый объект с данными.
185 | Если нужен доступ к какому-то его содержимому нужно использовать метод `object()`:
186 |
187 | after: function(params, context, result) {
188 | var o = result.object();
189 |
190 | console.log(o.foo.bar);
191 | }
192 |
193 | * Поправлено JS API. Минимально работающий вариант:
194 |
195 | var de = require('descript');
196 |
197 | de.script.init();
198 |
199 | de.Block.compile('http://yandex.ru/yandsearch?')
200 | .run({ text: 'descript' })
201 | .then(function(result) {
202 | console.log( result.string() );
203 | });
204 |
205 | В этом примере контекст создается автоматически (при этом и доступа к нему нет).
206 | В `result` находится инстанс `de.Result`.
207 |
208 | Более сложный вариант:
209 |
210 | var http = require('http');
211 | var de = require('descript');
212 |
213 | de.script.init();
214 |
215 | var block = de.Block.compile('http://yandex.ru/yandsearch?');
216 |
217 | http
218 | .createServer(function(req, res) {
219 | // Создаем (асинхронно) контекст.
220 | de.Context.create(req).done(function(context) {
221 | block
222 | .run({ text: 'descript' })
223 | .then(function(result) {
224 | // Этот метод выставляет все заголовки и т.д.,
225 | // а также выводит результат в выходной поток (res) и закрывает поток.
226 | context.response.end(res, result);
227 | });
228 | });
229 | })
230 | .listen(2000);
231 |
232 | Альтернативный вариант — использовать `de.server.start()` для поднятия сервера автоматически:
233 |
234 | var de = require('descript');
235 |
236 | de.server.init();
237 | de.server.start();
238 |
239 |
240 | * Изменения в `de.Context`:
241 |
242 | * Конструктор `de.Context()` первым параметром принимает или нодовский реквест (`http.IncomingMessage`),
243 | или просто объект с параметрами.
244 |
245 | * Инстанс `de.Context` содержит поля:
246 |
247 | * `request` — может быть null, если в конструктор были переданы просто параметры.
248 | Либо инстанс `de.Request`.
249 |
250 | * `response` — инстанс `de.Response`.
251 |
252 | * `query` — все параметры (включая извлеченные из body запроса при `post`-запросе).
253 |
254 | * `state` — общий стейт для обмена информацией между блоками.
255 |
256 | * `config` — ссылка на `de.config`.
257 |
258 | * `de.Context.create(request)` — метод для создания контекста, возвращает promise,
259 | в который уже приходит готовый контекст. Сделано так потому, что если это `post`-запрос,
260 | то тело запроса (и параметры из него) получаются асинхронно.
261 |
262 | * Изменения в `de.Response`:
263 |
264 | * Больше не содержит ссылку на нодовский `http.ServerResponse`.
265 |
266 | * Появился метод `end(response, result)` — первым параметром принимает `http.ServerResponse`,
267 | второй (опциональный) — `de.Result`.
268 | Метод выставляет заголовки, код ответа, редиректы и т.д.
269 | Если передан `result`, то выводит его в поток (попутно выставляя `content-type`) и закрывает поток.
270 |
271 | * Изменения в `de.Request`:
272 |
273 | * Больше не содержит ссылку на нодовский `http.IncomingMessage`.
274 |
275 | * Имеет поля:
276 |
277 | * `headers` — http-заголовки, пришедшие в реквесте.
278 |
279 | * `cookies` — куки.
280 |
281 | * `url` — объект, получающийся из `require('url').parse(req.url, true, true)`.
282 | (http://nodejs.org/api/url.html#url_url)[http://nodejs.org/api/url.html#url_url].
283 |
284 | Если это был `POST`-запрос, то в `url.query` будут параметры из тела запроса.
285 |
286 | * `method` — метод (`GET`, `POST`, ...).
287 |
288 |
--------------------------------------------------------------------------------
/lib/de.result.js:
--------------------------------------------------------------------------------
1 | var no = require('nommon');
2 | var de = require('./de.js');
3 |
4 | var path_ = require('path');
5 |
6 |
7 | // --------------------------------------------------------------------------------------------------------------- //
8 | // Consts
9 | // --------------------------------------------------------------------------------------------------------------- //
10 |
11 | // Преобразования между расширениями файлов, content-type и data-type.
12 |
13 | var content_type_by_ext = {
14 | '.json': 'application/json',
15 |
16 | '.html': 'text/html',
17 | '.htm': 'text/html',
18 |
19 | '.text': 'text/plain',
20 | '.txt': 'text/plain',
21 |
22 | '.css': 'text/css',
23 | '.js': 'application/javascript',
24 |
25 | '.xml': 'text/xml',
26 |
27 | '.png': 'image/png',
28 | '.jpg': 'image/jpeg',
29 | '.jpeg': 'image/jpeg',
30 | '.gif': 'image/gif'
31 | };
32 |
33 | var data_type_by_content_type = {
34 | 'text/json': 'json',
35 | 'application/json': 'json',
36 |
37 | 'text/html': 'html',
38 |
39 | 'text/plain': 'text',
40 | 'text/xml': 'text',
41 | 'application/xml': 'text',
42 | 'text/css': 'text',
43 | 'text/javascript': 'text',
44 | 'text/x-javascript': 'text',
45 | 'application/javascript': 'text',
46 | 'application/x-javascript': 'text'
47 | };
48 |
49 | var content_type_by_data_type = {
50 | 'text': 'text/plain',
51 | 'html': 'text/html',
52 | 'json': 'application/json'
53 | };
54 |
55 | // --------------------------------------------------------------------------------------------------------------- //
56 |
57 | // В классах de.Result.* хранятся результаты выполнения блоков.
58 | //
59 | // de.Result --- базовый абстрактный класс, от него наследуются все остальные
60 | // (главным образом, для того, чтобы делать instanceof de.Result).
61 | //
62 | // de.Result.File и de.Result.Http возвращаются из de.file и de.http соответственно.
63 | // Сами по себе не используются для вывода, из них всегда создается de.Result.Raw.
64 | // Смысл этого преобразования в том, что при создании de.Result.Raw можно переопределить
65 | // флаги data_type и output_type. Это позволяет кэшировать de.Result.File/Http
66 | // (с тем data_type, который определяется самим контентом) и при выводе менять data_type.
67 | //
68 | // de.Result.Value --- для константных значений (числа, строки, объекты, ...).
69 | //
70 | // de.Result.Error --- для ошибок.
71 | //
72 | // de.Result.Array и de.Result.Object --- композитные объекты, для массивов и объектов соответственно.
73 | //
74 | // Все эти классы должны (как минимум) определить методы:
75 | //
76 | // * string() --- строковое представление объекта
77 | // * object() --- объект, с которым можно работать в js.
78 | // * write() --- записать объект в поток.
79 | //
80 |
81 | // --------------------------------------------------------------------------------------------------------------- //
82 | // de.Result
83 | // --------------------------------------------------------------------------------------------------------------- //
84 |
85 | de.Result = function(result) {};
86 |
87 | de.Result.prototype.data_type = 'json';
88 | de.Result.prototype.content_type = 'application/json';
89 |
90 | de.Result.prototype.string = function() {};
91 |
92 | de.Result.prototype.object = function() {};
93 |
94 | de.Result.prototype.write = function(stream) {};
95 |
96 | de.Result.prototype.formatted = function() {
97 | return JSON.stringify( this.object(), null, 4 );
98 | };
99 |
100 | de.Result.prototype.select = function(jpath, vars) {
101 | return no.jpath( jpath, this.object(), vars );
102 | };
103 |
104 | de.Result.prototype.contentType = function() {
105 | var content_type = this.content_type;
106 |
107 | if (this.data_type !== 'binary') {
108 | content_type += '; charset=utf-8';
109 | }
110 |
111 | return content_type;
112 | };
113 |
114 | // --------------------------------------------------------------------------------------------------------------- //
115 | // de.Result.Value
116 | // --------------------------------------------------------------------------------------------------------------- //
117 |
118 | de.Result.Value = function(result) {
119 | this.result = (result === undefined) ? null : result;
120 | };
121 |
122 | no.inherit(de.Result.Value, de.Result);
123 |
124 | de.Result.Value.prototype._id = 'value';
125 |
126 | de.Result.Value.prototype.string = function() {
127 | var s = this._string;
128 |
129 | if (s === undefined) {
130 | s = this._string = JSON.stringify(this.result);
131 | }
132 |
133 | return s;
134 | };
135 |
136 | de.Result.Value.prototype.object = function() {
137 | return this.result;
138 | };
139 |
140 | de.Result.Value.prototype.write = function(stream) {
141 | stream.write( this.string() );
142 | };
143 |
144 |
145 | // --------------------------------------------------------------------------------------------------------------- //
146 | // de.Result.Raw
147 | // --------------------------------------------------------------------------------------------------------------- //
148 |
149 | // У блока de.Result.Raw есть два флага: data_type и output_type.
150 | // Первый определяется тем, какой контент на самом деле в этом блоке.
151 | // Например:
152 | //
153 | // "hello.txt"
154 | //
155 | // data_type === 'text'.
156 | //
157 | // Но, вот здесь:
158 | //
159 | // {
160 | // hello: "hello.txt"
161 | // }
162 | //
163 | // file-блок нужно выводить не как текстовый файл, но как строку,
164 | // т.е. output_type === 'json' (и по-прежнему data_type === 'text').
165 | //
166 | // data_type может принимать одно из значений: 'json', 'text', 'html', 'binary',
167 | // а output_type в данный момент может быть 'json' или undefined.
168 |
169 | de.Result.Raw = function(result, data_type, output_type) {
170 | /** @type {Buffer} */
171 | this.result = result.buffer;
172 |
173 | if (data_type) {
174 | // data_type задан явно в options.
175 | this.data_type = data_type;
176 | this.content_type = content_type_by_data_type[data_type];
177 | } else {
178 | // Используем data_type и content_type из result'а.
179 | this.data_type = result.data_type;
180 | this.content_type = result.content_type;
181 | }
182 |
183 | // output_type задан явно в options.
184 | if (output_type) {
185 | this.output_type = output_type;
186 | this.content_type = content_type_by_data_type[output_type];
187 | }
188 | };
189 |
190 | no.inherit(de.Result.Raw, de.Result);
191 |
192 | de.Result.Raw.prototype._id = 'raw';
193 |
194 | de.Result.Raw.prototype.string = function() {
195 | var s = this._string;
196 |
197 | if (s === undefined) {
198 | s = this._string = (this.data_type === 'json') ? this.result.toString() : JSON.stringify( this.result.toString() );
199 | }
200 |
201 | return s;
202 | };
203 |
204 | de.Result.Raw.prototype.object = function() {
205 | var o = this._object;
206 |
207 | if (o === undefined) {
208 | if (this.data_type === 'json') {
209 | try {
210 | o = JSON.parse( this.string() );
211 | } catch ( e ) {
212 | o = { error: 'JSON.parse: Cannot parse response body' };
213 | }
214 | } else {
215 | o = this.string();
216 | }
217 |
218 | this._object = o;
219 | }
220 |
221 | return o;
222 | };
223 |
224 | // Значением output_type может быть либо 'json', либо undefined.
225 | //
226 | de.Result.Raw.prototype.write = function(stream, output_type) {
227 | output_type = output_type || this.output_type;
228 |
229 | if (this.data_type === 'json' || output_type !== 'json') {
230 | // Либо это у нас изначально json,
231 | // либо это text или html, который не внутри объекта/массива
232 | // и для которого не задан явно output_type.
233 | //
234 | // Выводим результат как есть.
235 | //
236 | stream.write(this.result);
237 | } else {
238 | // Преобразуем текстовый контент в строку (JSON.stringify).
239 | stream.write( this.string() );
240 | }
241 |
242 | // FIXME: binary не может быть внутри объекта/массива.
243 | };
244 |
245 |
246 | // --------------------------------------------------------------------------------------------------------------- //
247 | // de.Result.Array
248 | // --------------------------------------------------------------------------------------------------------------- //
249 |
250 | de.Result.Array = function(result) {
251 | this.result = result;
252 | };
253 |
254 | no.inherit(de.Result.Array, de.Result);
255 |
256 | de.Result.Array.prototype._id = 'array';
257 |
258 | de.Result.Array.prototype.string = function() {
259 | var s = this._string;
260 |
261 | if (s === undefined) {
262 | var result = this.result;
263 |
264 | s = '[';
265 | for (var i = 0, l = result.length; i < l; i++) {
266 | if (i) {
267 | s += ',';
268 | }
269 | s += result[i].string();
270 | }
271 | s += ']';
272 |
273 | this._string = s;
274 | }
275 |
276 | return s;
277 | };
278 |
279 | de.Result.Array.prototype.object = function() {
280 | var o = this._object;
281 |
282 | if (!o) {
283 | var result = this.result;
284 |
285 | o = this._object = [];
286 | for (var i = 0, l = result.length; i < l; i++) {
287 | o.push( result[i].object() );
288 | }
289 | }
290 |
291 | return o;
292 | };
293 |
294 | de.Result.Array.prototype.write = function(stream) {
295 | stream.write('[');
296 | var result = this.result;
297 | for (var i = 0, l = result.length; i < l; i++) {
298 | if (i) {
299 | stream.write(',');
300 | }
301 | result[i].write(stream, 'json');
302 | }
303 | stream.write(']');
304 | };
305 |
306 |
307 | // --------------------------------------------------------------------------------------------------------------- //
308 | // de.Result.Object
309 | // --------------------------------------------------------------------------------------------------------------- //
310 |
311 | de.Result.Object = function(result) {
312 | this.result = result;
313 | };
314 |
315 | no.inherit(de.Result.Object, de.Result);
316 |
317 | de.Result.Object.prototype._id = 'object';
318 |
319 | de.Result.Object.prototype.string = function() {
320 | var s = this._string;
321 |
322 | if (s === undefined) {
323 | var result = this.result;
324 |
325 | s = '{';
326 | var i = 0;
327 | for (var key in result) {
328 | if (i++) {
329 | s += ',';
330 | }
331 | s += JSON.stringify(key) + ':' + result[key].string();
332 | }
333 | s += '}';
334 |
335 | this._string = s;
336 | }
337 |
338 | return s;
339 | };
340 |
341 | de.Result.Object.prototype.object = function() {
342 | var o = this._object;
343 |
344 | if (!o) {
345 | var result = this.result;
346 |
347 | o = this._object = {};
348 | for (var key in result) {
349 | o[key] = result[key].object();
350 | }
351 | }
352 |
353 | return o;
354 | };
355 |
356 | de.Result.Object.prototype.write = function(stream) {
357 | stream.write('{');
358 | var i = 0;
359 | var result = this.result;
360 | for (var key in result) {
361 | if (i++) {
362 | stream.write(',');
363 | }
364 | stream.write( JSON.stringify(key) + ':' );
365 | result[key].write(stream, 'json');
366 | }
367 | stream.write('}');
368 | };
369 |
370 | // --------------------------------------------------------------------------------------------------------------- //
371 |
372 | de.Result.HTML = function(result) {
373 | this.result = result;
374 | };
375 |
376 | no.inherit(de.Result.HTML, de.Result.Value);
377 |
378 | de.Result.HTML.prototype._id = 'html';
379 |
380 | de.Result.HTML.prototype.data_type = 'html';
381 | de.Result.HTML.prototype.content_type = 'text/html';
382 |
383 | de.Result.HTML.prototype.write = function(stream) {
384 | stream.write(this.result);
385 | };
386 |
387 |
388 | // --------------------------------------------------------------------------------------------------------------- //
389 | // de.Result.Error
390 | // --------------------------------------------------------------------------------------------------------------- //
391 |
392 | de.Result.Error = function(error) {
393 | this.result = {
394 | 'error': error
395 | };
396 | };
397 |
398 | no.inherit(de.Result.Error, de.Result.Value);
399 |
400 | de.Result.Error.prototype._id = 'error';
401 |
402 | de.Result.Error.prototype.get = function(field) {
403 | return this.result.error[field];
404 | };
405 |
406 |
407 | // --------------------------------------------------------------------------------------------------------------- //
408 | // de.Result.File
409 | // --------------------------------------------------------------------------------------------------------------- //
410 |
411 | de.Result.File = function(filename, buffer) {
412 | this.buffer = buffer;
413 |
414 | var ext = path_.extname(filename);
415 |
416 | this.content_type = content_type_by_ext[ext] || 'application/octet-stream';
417 | this.data_type = data_type_by_content_type[this.content_type] || 'binary';
418 | };
419 |
420 | de.Result.File.prototype._id = 'file';
421 |
422 | de.Result.File.prototype.toString = function() {
423 | return this.buffer.toString();
424 | };
425 |
426 |
427 | // --------------------------------------------------------------------------------------------------------------- //
428 | // de.Result.Http
429 | // --------------------------------------------------------------------------------------------------------------- //
430 |
431 | de.Result.Http = function(headers) {
432 | // Здесь будет итоговый буфер с данными (после события 'end').
433 | this.buffer = null;
434 |
435 | this.content_type = de.mime(headers) || 'application/octet-stream';
436 | this.data_type = data_type_by_content_type[this.content_type] || 'binary';
437 |
438 | // Сохраняем все буфера, приходящие в событие 'data'.
439 | this._buffers = [];
440 | // И считаем их суммарную длину.
441 | this._length = 0;
442 | };
443 |
444 | de.Result.Http.prototype._id = 'http';
445 |
446 | de.Result.Http.prototype.data = function(data) {
447 | this._buffers.push(data);
448 | this._length += data.length;
449 | };
450 |
451 | de.Result.Http.prototype.end = function() {
452 | this.buffer = Buffer.concat(this._buffers, this._length);
453 |
454 | this._buffers = null;
455 | };
456 |
457 |
458 | // --------------------------------------------------------------------------------------------------------------- //
459 |
460 | /*
461 | de.result = function(data) {
462 | if (data && typeof data === 'object') {
463 | if ( Array.isArray(data) ) {
464 | return new de.Result.Array(data);
465 | } else {
466 | return new de.Result.Object(data);
467 | }
468 | }
469 |
470 | return new de.Result.Value(data);
471 | };
472 | */
473 |
474 | // --------------------------------------------------------------------------------------------------------------- //
475 |
476 |
--------------------------------------------------------------------------------
/lib/de.block.js:
--------------------------------------------------------------------------------
1 | var path_ = require('path');
2 |
3 | var no = require('nommon');
4 |
5 | var de = require('./de.js');
6 |
7 | require('./de.common.js');
8 | require('./de.file.js');
9 | require('./de.http.js');
10 | require('./de.result.js');
11 | require('./de.context.js');
12 |
13 |
14 | // --------------------------------------------------------------------------------------------------------------- //
15 | // Vars and consts
16 | // --------------------------------------------------------------------------------------------------------------- //
17 |
18 | var _id = 0;
19 | var _blocks = {};
20 |
21 | // Кэш результатов выполнения блоков. В кэше хранятся структуры вида:
22 | //
23 | // {
24 | // // Время добавления в кэш.
25 | // timestamp: ...,
26 | // // promise, зарезолвленный инстансом de.Result.*.
27 | // promise: ...
28 | // }
29 | //
30 | var _results = {};
31 |
32 | // Кэш инклюдов, в кэше хранятся скомпилированные блоки.
33 | var _includes = {};
34 |
35 |
36 | // --------------------------------------------------------------------------------------------------------------- //
37 | // de.Block
38 | // --------------------------------------------------------------------------------------------------------------- //
39 |
40 | de.Block = function(block, options) {};
41 |
42 | // --------------------------------------------------------------------------------------------------------------- //
43 |
44 | de.Block.prototype._init = function(options) {
45 | this.priority = this.priority || 0;
46 |
47 | this._block = '';
48 |
49 | options = options || {};
50 |
51 | var _options = this.options = {};
52 |
53 | _options.dirname = options.dirname || de.config.rootdir;
54 |
55 | // options.guard может быть либо функцией вида:
56 | //
57 | // function guard(params, context) { ... }
58 | //
59 | // либо строкой вида:
60 | //
61 | // '.id == 42 && !state.foo'
62 | //
63 | _options.guard = compileBoolean(options.guard);
64 |
65 | // Функция вида:
66 | //
67 | // function before(params, context) { ... }
68 | //
69 | _options.before = options.before || null;
70 |
71 | // Функция вида:
72 | //
73 | // function after(params, context, result) { ... }
74 | //
75 | _options.after = options.after || null;
76 |
77 | // Объект вида:
78 | //
79 | // {
80 | // foo: no.jpath.expr('.foo'),
81 | // ...
82 | // }
83 | //
84 | _options.select = compileObject(options.select);
85 |
86 | // Функция вида:
87 | //
88 | // function(result, context) { ... }
89 | //
90 | // или объект типа jresult:
91 | //
92 | // {
93 | // id: '.id',
94 | // content: '.data'
95 | // }
96 | //
97 | // FIXME: точно нужна проверка, или достаточно написать в документации
98 | // про допустимые форматы?
99 | if (typeof options.state === 'object' || typeof options.state === 'function') {
100 | _options.state = compileExpr(options.state);
101 | }
102 |
103 | // Функция вида:
104 | //
105 | // function(result, context) { ... }
106 | //
107 | // или jpath:
108 | //
109 | // '.foo.bar'
110 | //
111 | // или объект типа jresult:
112 | //
113 | // {
114 | // id: '.id',
115 | // content: '.data'
116 | // }
117 | //
118 | _options.result = compileExpr(options.result);
119 |
120 | // То же, что и в options.result.
121 | _options.params = compileExpr(options.params);
122 |
123 | // Функция с сигнатурой (params, context) или jstring.
124 | _options.key = compileString(options.key);
125 | // Число миллисекунд, на которое нужно закэшировать блок.
126 | _options.maxage = de.duration(options.maxage || 0);
127 |
128 | // Тип результата блока: json, text, ...
129 | // FIXME: Дефолтный data_type?
130 | _options.data_type = options.data_type || '';
131 |
132 | // Нужно ли преобразовать реальный тип во что-то еще.
133 | // Например, text -> json.
134 | _options.output_type = options.output_type || '';
135 |
136 | // Таймаут для блока.
137 | _options.timeout = options.timeout || 0;
138 |
139 | // Имя файла с шаблоном, который нужно наложить на результат выполнения блока.
140 | _options.template = compileString(options.template);
141 |
142 | _options.local = options.local;
143 |
144 | return _options;
145 | };
146 |
147 | function compileBoolean(expr) {
148 | if (expr == null) { return null; }
149 |
150 | return (typeof expr === 'function') ? expr : no.jpath.boolean(expr);
151 | }
152 |
153 | function compileExpr(expr) {
154 | if (expr == null) { return null; }
155 |
156 | return (typeof expr === 'function') ? expr : no.jpath.scalar(expr);
157 | }
158 |
159 | function compileString(str) {
160 | if (str == null) { return null; }
161 |
162 | return (typeof str === 'function') ? str : no.jpath.string(str);
163 | }
164 |
165 | function compileObject(obj) {
166 | if (obj == null) { return null; }
167 |
168 | if (typeof obj === 'function') { return obj; }
169 |
170 | var r = {};
171 | for (var key in obj) {
172 | r[key] = compileExpr( obj[key] );
173 | }
174 | return r;
175 | }
176 |
177 | // --------------------------------------------------------------------------------------------------------------- //
178 |
179 | de.Block.prototype.resolveFilename = function(filename) {
180 | return path_.resolve(this.options.dirname, filename);
181 | };
182 |
183 | // --------------------------------------------------------------------------------------------------------------- //
184 |
185 | de.Block.prototype.valueOf = function() {
186 | var id = this.__valueOf;
187 | if (!id) {
188 | id = this.__valueOf = '@block' + _id++ + '@';
189 | _blocks[id] = this;
190 | }
191 |
192 | return id;
193 | };
194 |
195 | // --------------------------------------------------------------------------------------------------------------- //
196 |
197 | de.Block.prototype.log_end = function(context, msg, t1) {
198 | context.log_end('debug', this.debug_id() + ' ' + msg, t1 );
199 | };
200 |
201 | de.Block.prototype.debug_id = function() {
202 | return '[block.' + this._id + ' ' + JSON.stringify(this._block) + ']';
203 | };
204 |
205 |
206 | de.Block.prototype.run = function(params, context) {
207 | context = context || new de.Context(params);
208 |
209 | var promise = new no.Promise();
210 |
211 | var options = this.options;
212 |
213 | // Проверяем гвард, если он есть.
214 | if ( options.guard && !options.guard(params, context) ) {
215 | return promise.resolve( new de.Result.Value(null) );
216 | }
217 |
218 | var that = this;
219 |
220 | var t1 = Date.now();
221 | promise.done(function() {
222 | that.log_end(context, 'ended', t1);
223 | /*
224 | // См. pull request #57.
225 | promise.done(function(result) {
226 | var result_log = (result && result instanceof de.Result) ? ('[' + JSON.stringify(result.object()) + '] ') : '';
227 | that.log_end(context, result_log + 'ended', t1);
228 | */
229 | });
230 |
231 | if (options.before) {
232 | options.before(params, context);
233 | }
234 |
235 | var running;
236 |
237 | // Смотрим, определен ли ключ для этого блока.
238 | var key;
239 | if (options.key) {
240 | // Вычисляем ключ.
241 | key = options.key(params, context);
242 |
243 | // Смотрим, не закэширован ли блок с таким же ключом.
244 | var cached = _results[key];
245 | if (cached) {
246 | // Не протух ли еще наш кэш?
247 | if (cached.timestamp + options.maxage > context.now) {
248 | // Нет, берем из кэша promise с результатом.
249 | running = cached.promise;
250 | context.debug('[found in cache key=' + key + ']');
251 | } else {
252 | // Протух. Выкидываем из кэша.
253 | // FIXME: Может тут таки нужно делать delete.
254 | _results[key] = null;
255 | }
256 | }
257 | }
258 |
259 | if (!running) {
260 | // Если блок все-таки не закэширован, запускаем его.
261 | var _params = (options.params) ? options.params(params, context) : params;
262 | var _context;
263 | if (options.local) {
264 | _context = context.clone();
265 | _context.state = {};
266 | } else {
267 | _context = context;
268 | }
269 | var running = this._run(_params, _context);
270 |
271 | // Если определен таймаут для блока.
272 | if (options.timeout) {
273 | var hTimeout = null;
274 |
275 | hTimeout = setTimeout(function() {
276 | // Если через options.timeout ms ничего не случится, кидаем ошибку.
277 | context.error(that.debug_id() + ' timeout');
278 | running.reject( de.error({
279 | id: 'TIMEOUT',
280 | message: 'Timeout' // FIXME: Вменяемый текст.
281 | }) );
282 |
283 | // И отменяем все запросы этого блока.
284 | // Пока что отменяются только http-запросы.
285 | running.trigger('abort');
286 |
287 | }, options.timeout);
288 |
289 | // Если блок выполнился быстрее, чем таймаут.
290 | running.always(function() {
291 | if (hTimeout) {
292 | // Отменяем setTimeout.
293 | clearTimeout(hTimeout);
294 | hTimeout = null;
295 | }
296 | });
297 | }
298 |
299 | // Кэша нет, но ключ есть.
300 | if (key) {
301 | // Кэшируем блок на будущее.
302 | // Можно не ждать окончания выполнения блока, т.к. там все равно promise кэшируется.
303 | _results[key] = {
304 | timestamp: context.now,
305 | promise: running
306 | };
307 |
308 | running.fail(function() {
309 | // Если выполнение привело к ошибке, выкидываем ключ из кэша.
310 | // Чтобы в следующий раз блок выполнился еще раз.
311 | // FIXME: Может лучше использовать delete?
312 | _results[key] = null;
313 | });
314 | }
315 | }
316 |
317 | var that = this;
318 |
319 | running.done(function(result) {
320 | // Возможность положить что-нибудь в state после выполнения блока.
321 | var select = options.select;
322 | if (select) {
323 | var state = context.state;
324 |
325 | de.log.warn('You are using deprecated `select` section of the block options. It will dissapear soon. Use `state` instead.');
326 |
327 | var obj = result.object();
328 |
329 | for (var key in select) {
330 | // FIXME: Сигнатура?!
331 | state[key] = select[key](obj, context);
332 | }
333 | }
334 |
335 | if (options.state) {
336 | var patch = options.state(result.object(), context, params);
337 | context.state = de.mergeObjects(context.state, patch);
338 | }
339 |
340 | if (options.after) {
341 | options.after(params, context, result);
342 | }
343 |
344 | if (options.result) {
345 | result = new de.Result.Value( options.result( result.object(), context, params ) );
346 | }
347 |
348 | if ( options.template && !(result instanceof de.Result.Error) ) {
349 | var filename = that.resolveFilename( options.template(params, context) );
350 |
351 | de.file.eval(filename, 'de', de.sandbox, context)
352 | .done(function(template) {
353 | var t1 = Date.now();
354 | var r = template( result.object() );
355 | context.log_end('info', '[template ' + JSON.stringify(filename) + '] ended', t1);
356 |
357 | /*
358 | if (data && typeof data === 'object') {
359 | if ( Array.isArray(data) ) {
360 | return new de.Result.Array(data);
361 | } else {
362 | return new de.Result.Object(data);
363 | }
364 | }
365 | */
366 | if (typeof r === 'string') {
367 | promise.resolve( new de.Result.HTML(r) );
368 | } else if (r instanceof de.Result) {
369 | promise.resolve(r);
370 | } else {
371 | // FIXME: Что тогда?
372 | }
373 | })
374 | .fail(function(error) {
375 | promise.resolve(error);
376 | });
377 | } else {
378 | promise.resolve(result);
379 | }
380 |
381 | });
382 |
383 | running.fail(function(error) {
384 | promise.resolve(error);
385 | });
386 |
387 | return promise;
388 | };
389 |
390 | de.Block.prototype._run = function(params, context) {};
391 |
392 | // --------------------------------------------------------------------------------------------------------------- //
393 |
394 | de.Block.prototype.params = function(params) {
395 | return new de.Block.Curry(this, params);
396 | };
397 |
398 | // --------------------------------------------------------------------------------------------------------------- //
399 |
400 | de.Block.compile = function(block, options) {
401 | // options = options || {};
402 |
403 | var compiled;
404 | var priority;
405 |
406 | switch (typeof block) {
407 |
408 | case 'string':
409 |
410 | var r;
411 |
412 | if (( r = /^https?:\/\//.test(block) )) { // Строка начинается с 'http://' -- это http-блок.
413 | // FIXME: Поддержка https, post, get и т.д.
414 | compiled = new de.Block.Http(block, options);
415 |
416 | } else if (( r = block.match(/^(.*\(\))(\d+)?$/) )) { // Строка оканчивается на '()' -- это call-блок.
417 | compiled = new de.Block.Call(r[1], options);
418 | priority = r[2];
419 |
420 | } else if (( r = block.match(/^(.*\.jsx)(\d+)?$/) )) { // Строка оканчивается на '.jsx' -- это include-блок.
421 | compiled = new de.Block.Include(r[1], options);
422 | priority = r[2];
423 |
424 | // FIXME: Уметь задавать список расширений для file-блока через конфиг.
425 | } else if (( r = block.match(/^(.*\.(?:json|txt|html|xml))(\d+)?$/) )) { // Строка оканчивается на '.(json|txt|html|xml)' -- это file-блок.
426 | compiled = new de.Block.File(r[1], options);
427 | priority = r[2];
428 |
429 | // В предыдущих трех случаях в конце строки может быть число, означающее приоритет.
430 | // Например:
431 | // {
432 | // common: 'common.jsx' + 25,
433 | // ...
434 | // }
435 | //
436 | // В случае http-блока, приоритет нужно задавать так (потому, что число на конце может быть частью урла):
437 | // {
438 | // common: http('http://foo.com/bar') +25,
439 | // ...
440 | // }
441 | //
442 | // Работает это за счет того, что у de.Block переопределен метод valueOf,
443 | // который возвращает уникальную строку вида '@block25@'.
444 |
445 | } else if (( r = block.match(/^(@block\d+@)(\d+)$/) ) || ( r = block.match(/^(\d+)(@block\d+@)$/) )) {
446 | // Строка вида '@block25@45' или '45@block25@',
447 | // где 25 это порядковый номер блока, а 45 -- приоритет.
448 |
449 | var id = r[1];
450 | priority = r[2];
451 |
452 | compiled = _blocks[id];
453 |
454 | // FIXME после фикса include блока в #96 мы делаем eval один раз, а compile - несколько раз.
455 | // Поэтому нам нужно, чтобы eval-енные блоки оставались в _blocks.
456 | // delete _blocks[id];
457 | }
458 |
459 | break;
460 |
461 | case 'object':
462 |
463 | // NOTE: Тут нельзя использовать (block instanceof Array) потому, что .jsx файлы эвалятся
464 | // в другом контексте и там другой Array. Для справки -- util.isArray примерно в 10 раз медленнее, чем instanceof.
465 | if ( Array.isArray(block) ) {
466 | compiled = new de.Block.Array(block, options);
467 |
468 | } else if ( block && !(block instanceof de.Block) ) {
469 | compiled = new de.Block.Object(block, options);
470 |
471 | } else {
472 | compiled = block;
473 |
474 | }
475 |
476 | break;
477 |
478 | case 'function':
479 |
480 | compiled = new de.Block.Function(block, options);
481 |
482 | break;
483 |
484 | }
485 |
486 | if (!compiled) {
487 | compiled = new de.Block.Value(block, options);
488 | }
489 |
490 | if (priority) {
491 | compiled.priority = +priority;
492 | }
493 |
494 | return compiled;
495 |
496 | };
497 |
498 |
499 | // --------------------------------------------------------------------------------------------------------------- //
500 | // de.Block.File
501 | // --------------------------------------------------------------------------------------------------------------- //
502 |
503 | de.Block.File = function(filename, options) {
504 | this._init(options);
505 |
506 | this._block = filename;
507 | this.filename = no.jpath.string(filename);
508 | };
509 |
510 | no.inherit(de.Block.File, de.Block);
511 |
512 | de.Block.File.prototype._id = 'file';
513 |
514 | // --------------------------------------------------------------------------------------------------------------- //
515 |
516 | de.Block.File.prototype._run = function(params, context) {
517 | var filename = this.resolveFilename( this.filename(params, context) );
518 |
519 | var options = this.options;
520 |
521 | var promise = new no.Promise();
522 |
523 | de.file.get(filename, context)
524 | .done(function(result) {
525 | promise.resolve( new de.Result.Raw(result, options.data_type, options.output_type) );
526 | })
527 | .fail(function(error) {
528 | promise.resolve(error);
529 | });
530 |
531 | return promise;
532 | };
533 |
534 |
535 | // --------------------------------------------------------------------------------------------------------------- //
536 | // de.Block.Http
537 | // --------------------------------------------------------------------------------------------------------------- //
538 |
539 | de.Block.Http = function(url, options) {
540 | options = options || {};
541 |
542 | var _options = this._init(options);
543 |
544 | // Пробрасывать ли http-заголовки.
545 | // По дефолту -- пробрасывать.
546 | _options.proxy = (options.proxy === undefined) ? true : options.proxy;
547 |
548 | _options.method = ( options.method || 'get' ).toLowerCase();
549 |
550 | _options.max_redirects = options.max_redirects;
551 | _options.only_status = options.only_status;
552 | _options.headers = options.headers;
553 | _options.http_options = options.http_options || {};
554 |
555 | // Тело пост-запроса
556 | _options.body = options.body || '';
557 |
558 | this._block = url;
559 |
560 | var ch = url.slice(-1);
561 | // Если урл заканчивается на '?' или '&', значит в запрос нужно добавить
562 | // параметры из реквеста.
563 | if (ch === '?' || ch === '&') {
564 | _options.extend = true;
565 | url = url.slice(0, -1);
566 | }
567 |
568 | this.url = no.jpath.string(url);
569 | };
570 |
571 | no.inherit(de.Block.Http, de.Block);
572 |
573 | de.Block.Http.prototype._id = 'http';
574 |
575 | // --------------------------------------------------------------------------------------------------------------- //
576 |
577 | // Список http-заголовков, которые нужно выкидывать при проксировании.
578 | // Стырено из xscript'а (наверное, они знали, что делали :).
579 | //
580 | var disallow_headers = {
581 | 'host': true,
582 | 'if-modified-since': true,
583 | 'accept-encoding': true,
584 | 'keep-alive': true,
585 | 'connection': true,
586 | 'content-length': true
587 | };
588 |
589 | de.Block.Http.prototype._run = function(params, context) {
590 | var url = this.url(params, context);
591 |
592 | var options = this.options;
593 |
594 | var query = (options.extend) ? params : null;
595 |
596 | var headers = null;
597 | if (options.proxy && context.request) {
598 | var req_headers = context.request.headers;
599 |
600 | // Копируем все http-заголовки, кроме тех, которые указаны в disallow_headers.
601 | headers = {};
602 | for (var header in req_headers) {
603 | if (!disallow_headers[header]) {
604 | headers[header] = req_headers[header];
605 | }
606 | }
607 | }
608 |
609 | if (options.headers) {
610 | headers = no.extend(headers || {}, options.headers);
611 | }
612 |
613 | var promise = new no.Promise();
614 |
615 | var httpPromise = de.http(
616 | {
617 | url: url,
618 | method: options.method,
619 | headers: headers,
620 | max_redirects: options.max_redirects,
621 | only_status: options.only_status,
622 | http_options: options.http_options,
623 | body: options.body
624 | },
625 | query,
626 | context
627 | )
628 | .done(function(result) {
629 | if ( result instanceof de.Result ) {
630 | promise.resolve(result);
631 | } else {
632 | promise.resolve( new de.Result.Raw(result, options.data_type, options.output_type) );
633 | }
634 | })
635 | .fail(function(error) {
636 | promise.reject(error);
637 | });
638 |
639 | promise.forward('abort', httpPromise);
640 |
641 | return promise;
642 | };
643 |
644 |
645 | // --------------------------------------------------------------------------------------------------------------- //
646 | // de.Block.Call
647 | // --------------------------------------------------------------------------------------------------------------- //
648 |
649 | // FIXME: Нужна ли тут интерполяция строк?
650 | // Типа: 'get{ .method }()'
651 | //
652 | de.Block.Call = function(call, options) {
653 | this._init(options);
654 |
655 | this._block = call;
656 |
657 | var r = call.match(/^(?:(.*?):)?(.*)\(\)$/);
658 |
659 | this.module = de._modules[ r[1] || '' ] || null;
660 | this.method = r[2];
661 | };
662 |
663 | no.inherit(de.Block.Call, de.Block);
664 |
665 | de.Block.Call.prototype._id = 'call';
666 |
667 | // --------------------------------------------------------------------------------------------------------------- //
668 |
669 | de.Block.Call.prototype._run = function(params, context) {
670 | var module = this.module;
671 | var method = this.method;
672 |
673 | if (module) {
674 | var call = module[method] || module;
675 | try {
676 | return call(params, context, method);
677 | } catch (e) {
678 | return no.Promise.resolved( de.error({
679 | id: 'MODULE_CALL',
680 | message: e.message
681 | }) );
682 | }
683 | } else {
684 | return no.Promise.resolved( de.error({
685 | id: 'MODULE_NOT_FOUND',
686 | message: 'Cannot find module "' + this.moduleName + '"'
687 | }) );
688 | }
689 | };
690 |
691 |
692 | // --------------------------------------------------------------------------------------------------------------- //
693 | // de.Block.Function
694 | // --------------------------------------------------------------------------------------------------------------- //
695 |
696 | de.Block.Function = function(func, options) {
697 | this._init(options);
698 |
699 | this.func = func;
700 | };
701 |
702 | no.inherit(de.Block.Function, de.Block);
703 |
704 | de.Block.Function.prototype._id = 'function';
705 |
706 | de.Block.Function.prototype.log_end = no.nop;
707 |
708 | // --------------------------------------------------------------------------------------------------------------- //
709 |
710 | de.Block.Function.prototype._run = function(params, context) {
711 | var result;
712 | try {
713 | result = this.func(params, context);
714 | } catch (e) {
715 | return no.Promise.resolved( de.error({
716 | id: 'FUNC_CALL',
717 | message: e.message
718 | }) );
719 | }
720 |
721 | var block = de.Block.compile( result, { dirname: this.options.dirname } );
722 |
723 | return block.run(params, context);
724 | };
725 |
726 |
727 | // --------------------------------------------------------------------------------------------------------------- //
728 | // de.Block.Include
729 | // --------------------------------------------------------------------------------------------------------------- //
730 |
731 | de.Block.Include = function(filename, options) {
732 | this._init(options);
733 |
734 | this._block = filename;
735 |
736 | this.filename = no.jpath.string(filename);
737 | };
738 |
739 | no.inherit(de.Block.Include, de.Block);
740 |
741 | de.Block.Include.prototype._id = 'include';
742 |
743 | // --------------------------------------------------------------------------------------------------------------- //
744 |
745 | de.Block.Include.prototype._run = function(params, context) {
746 | var promise = new no.Promise();
747 |
748 | var filename = this.resolveFilename( this.filename(params, context) );
749 |
750 | var options = no.extend({}, this.options, {dirname: path_.dirname(filename)});
751 |
752 | var including = _includes[filename];
753 | if (!including) {
754 | including = _includes[filename] = new no.Promise();
755 |
756 | de.file.eval(filename, 'de', de.sandbox, context)
757 | .done(function(include) {
758 | including.resolve(include);
759 | })
760 | .fail(function(error) {
761 | _includes[filename] = null;
762 |
763 | including.reject(error);
764 | });
765 | }
766 |
767 | including
768 | .done(function(include) {
769 | var block = de.Block.compile(include, options);
770 |
771 | de.forward( block.run(params, context), promise );
772 | })
773 | .fail(function(error) {
774 | promise.resolve(error);
775 | });
776 |
777 | return promise;
778 | };
779 |
780 | // FIXME: Получается, что у нас в кэше лежит и исполненный код,
781 | // и созданный из него блок.
782 | //
783 | de.events.on('loaded-file-changed', function(e, filename) {
784 | _includes[filename] = null;
785 | });
786 |
787 |
788 | // --------------------------------------------------------------------------------------------------------------- //
789 | // de.Block.Value
790 | // --------------------------------------------------------------------------------------------------------------- //
791 |
792 | de.Block.Value = function(value, options) {
793 | this._init(options);
794 |
795 | this.value = new de.Result.Value(value);
796 | };
797 |
798 | no.inherit(de.Block.Value, de.Block);
799 |
800 | de.Block.Value.prototype._id = 'value';
801 |
802 | de.Block.Value.prototype.log_end = no.nop;
803 |
804 | // --------------------------------------------------------------------------------------------------------------- //
805 |
806 | de.Block.Value.prototype._run = function(params, context) {
807 | return no.Promise.resolved(this.value);
808 | };
809 |
810 |
811 | // --------------------------------------------------------------------------------------------------------------- //
812 | // de.Block.Array
813 | // --------------------------------------------------------------------------------------------------------------- //
814 |
815 | function groupItems(items) {
816 | var l = items.length;
817 | if (!l) {
818 | return [];
819 | }
820 |
821 | var sorted = items.sort(function(a, b) { return b.block.priority - a.block.priority; });
822 |
823 | var groups = [];
824 | var group = [];
825 |
826 | var i = 0;
827 | var item = sorted[0];
828 | var next;
829 | while (i < l) {
830 | group.push(item);
831 |
832 | i++;
833 | if (i < l) {
834 | next = sorted[i];
835 | if (item.block.priority !== next.block.priority) {
836 | groups.push(group);
837 | group = [];
838 | }
839 | } else {
840 | groups.push(group);
841 | break;
842 | }
843 |
844 | item = next;
845 | }
846 |
847 | return groups;
848 | }
849 |
850 | de.Block.Array = function(array, options) {
851 | options = this._init(options);
852 |
853 | var items = [];
854 | var item_options = { dirname: options.dirname };
855 | for (var i = 0, l = array.length; i < l; i++) {
856 | items.push({
857 | index: i,
858 | block: de.Block.compile(array[i], item_options)
859 | });
860 | }
861 |
862 | this.groups = groupItems(items);
863 |
864 | };
865 |
866 | no.inherit(de.Block.Array, de.Block);
867 |
868 | de.Block.Array.prototype._id = 'array';
869 |
870 | de.Block.Array.prototype.log_end = no.nop;
871 |
872 | // --------------------------------------------------------------------------------------------------------------- //
873 |
874 | de.Block.Array.prototype._run = function(params, context) {
875 | var promise = new no.Promise();
876 |
877 | var that = this;
878 |
879 | var results = [];
880 | var groups = this.groups;
881 |
882 | var i = 0;
883 | var l = groups.length;
884 |
885 | var workers;
886 | var wait;
887 |
888 | promise.on('abort', function() {
889 | // Останавливаем run(), чтобы он не запускал больше ничего.
890 | i = l;
891 |
892 | promise.resolve( de.error({
893 | id: 'ERROR_ABORTED'
894 | }) );
895 |
896 |
897 | if (workers) {
898 | // FIXME: Нужно ли это?
899 | wait.reject();
900 |
901 | for (var j = 0, m = workers.length; j < m; j++) {
902 | workers[j].trigger('abort');
903 | }
904 | }
905 | });
906 |
907 | (function run() {
908 | if (i < l) {
909 | workers = [];
910 |
911 | var group = groups[i];
912 | for (var j = 0, m = group.length; j < m; j++) {
913 | (function(item) {
914 | var worker = item.block.run(params, context)
915 | .done(function(r) {
916 | results[item.index] = r;
917 | });
918 |
919 | workers.push(worker);
920 | })( group[j] );
921 | }
922 |
923 | i++;
924 |
925 | wait = no.Promise.wait(workers).done(run);
926 |
927 | } else {
928 | workers = null;
929 |
930 | promise.resolve( that._getResult(results) );
931 | }
932 | })();
933 |
934 | return promise;
935 | };
936 |
937 | // --------------------------------------------------------------------------------------------------------------- //
938 |
939 | de.Block.Array.prototype._getResult = function(results) {
940 | return new de.Result.Array(results);
941 | };
942 |
943 |
944 | // --------------------------------------------------------------------------------------------------------------- //
945 | // de.Block.Object
946 | // --------------------------------------------------------------------------------------------------------------- //
947 |
948 | de.Block.Object = function(object, options) {
949 | options = this._init(options);
950 |
951 | var items = [];
952 | var keys = this.keys = [];
953 |
954 | var i = 0;
955 | var item_options = { dirname: options.dirname };
956 | for (var key in object) {
957 | items.push({
958 | index: i++,
959 | block: de.Block.compile(object[key], item_options)
960 | });
961 | keys.push(key);
962 | }
963 |
964 | this.groups = groupItems(items);
965 | };
966 |
967 | no.inherit(de.Block.Object, de.Block);
968 |
969 | de.Block.Object.prototype._id = 'object';
970 |
971 | de.Block.Object.prototype.log_end = no.nop;
972 |
973 | // --------------------------------------------------------------------------------------------------------------- //
974 |
975 | de.Block.Object.prototype._run = de.Block.Array.prototype._run;
976 |
977 | de.Block.Object.prototype._getResult = function(results) {
978 | var keys = this.keys;
979 |
980 | var r = {};
981 |
982 | for (var i = 0, l = results.length; i < l; i++) {
983 | r[ keys[i] ] = results[i];
984 | }
985 |
986 | return new de.Result.Object(r);
987 | };
988 |
989 |
990 | /*
991 | // --------------------------------------------------------------------------------------------------------------- //
992 | // de.Block.Page
993 | // --------------------------------------------------------------------------------------------------------------- //
994 |
995 | de.Block.Page = function(page, options) {
996 | this._init(options);
997 |
998 | // FIXME: Вообще может убрать дефолтный таймаут?
999 | this.options.timeout = this.options.timeout || de.config.timeout || 0;
1000 |
1001 | this.page = de.Block.compile(page, options);
1002 | };
1003 |
1004 | no.inherit(de.Block.Page, de.Block);
1005 |
1006 | de.Block.Page.prototype._id = 'page';
1007 |
1008 | // --------------------------------------------------------------------------------------------------------------- //
1009 |
1010 | de.Block.Page.prototype._run = function(params, context) {
1011 | return this.page.run(params, context);
1012 | };
1013 | */
1014 |
1015 | // --------------------------------------------------------------------------------------------------------------- //
1016 |
1017 | // FIXME: А это вообще где-нибудь используется?
1018 |
1019 | de.Block.Curry = function(block, params) {
1020 | this.block = block;
1021 | this.params = params;
1022 | };
1023 |
1024 | no.inherit(de.Block.Curry, de.Block);
1025 |
1026 | de.Block.Curry.prototype.run = function(params, context) {
1027 | return this.block.run(this.params, context);
1028 | };
1029 |
1030 | // --------------------------------------------------------------------------------------------------------------- //
1031 |
1032 | de.Block.Expr = function(expr, options) {
1033 | this._init(options);
1034 |
1035 | this.expr = compileExpr(expr);
1036 | };
1037 |
1038 | no.inherit(de.Block.Expr, de.Block);
1039 |
1040 | de.Block.Expr.prototype._id = 'expr';
1041 |
1042 | de.Block.Expr.prototype.log_end = no.nop;
1043 |
1044 | de.Block.Expr.prototype._run = function(params, context) {
1045 | var promise = new no.Promise();
1046 |
1047 | promise.resolve( new de.Result.Value( this.expr(params, context) ) );
1048 |
1049 | return promise;
1050 | };
1051 |
1052 | // --------------------------------------------------------------------------------------------------------------- //
1053 |
1054 |
--------------------------------------------------------------------------------
/test/pages/auths.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "status": {
4 | "value": "VALID",
5 | "id": 0
6 | },
7 | "error": "OK",
8 | "age": 228005,
9 | "auth": {
10 | "password_verification_age": 228005,
11 | "have_password": true,
12 | "secure": false,
13 | "allow_plain_text": true
14 | },
15 | "uid": {
16 | "value": "24171229",
17 | "lite": false,
18 | "hosted": false,
19 | "domid": "",
20 | "domain": "",
21 | "mx": ""
22 | },
23 | "login": "alpha-san",
24 | "karma": {
25 | "value": 0
26 | }
27 | },
28 | {
29 | "status": {
30 | "value": "VALID",
31 | "id": 0
32 | },
33 | "error": "OK",
34 | "age": 228005,
35 | "auth": {
36 | "password_verification_age": 228005,
37 | "have_password": true,
38 | "secure": false,
39 | "allow_plain_text": true
40 | },
41 | "uid": {
42 | "value": "24171229",
43 | "lite": false,
44 | "hosted": false,
45 | "domid": "",
46 | "domain": "",
47 | "mx": ""
48 | },
49 | "login": "alpha-san",
50 | "karma": {
51 | "value": 0
52 | }
53 | },
54 | {
55 | "status": {
56 | "value": "VALID",
57 | "id": 0
58 | },
59 | "error": "OK",
60 | "age": 228005,
61 | "auth": {
62 | "password_verification_age": 228005,
63 | "have_password": true,
64 | "secure": false,
65 | "allow_plain_text": true
66 | },
67 | "uid": {
68 | "value": "24171229",
69 | "lite": false,
70 | "hosted": false,
71 | "domid": "",
72 | "domain": "",
73 | "mx": ""
74 | },
75 | "login": "alpha-san",
76 | "karma": {
77 | "value": 0
78 | }
79 | },
80 | {
81 | "status": {
82 | "value": "VALID",
83 | "id": 0
84 | },
85 | "error": "OK",
86 | "age": 228005,
87 | "auth": {
88 | "password_verification_age": 228005,
89 | "have_password": true,
90 | "secure": false,
91 | "allow_plain_text": true
92 | },
93 | "uid": {
94 | "value": "24171229",
95 | "lite": false,
96 | "hosted": false,
97 | "domid": "",
98 | "domain": "",
99 | "mx": ""
100 | },
101 | "login": "alpha-san",
102 | "karma": {
103 | "value": 0
104 | }
105 | },
106 | {
107 | "status": {
108 | "value": "VALID",
109 | "id": 0
110 | },
111 | "error": "OK",
112 | "age": 228005,
113 | "auth": {
114 | "password_verification_age": 228005,
115 | "have_password": true,
116 | "secure": false,
117 | "allow_plain_text": true
118 | },
119 | "uid": {
120 | "value": "24171229",
121 | "lite": false,
122 | "hosted": false,
123 | "domid": "",
124 | "domain": "",
125 | "mx": ""
126 | },
127 | "login": "alpha-san",
128 | "karma": {
129 | "value": 0
130 | }
131 | },
132 | {
133 | "status": {
134 | "value": "VALID",
135 | "id": 0
136 | },
137 | "error": "OK",
138 | "age": 228005,
139 | "auth": {
140 | "password_verification_age": 228005,
141 | "have_password": true,
142 | "secure": false,
143 | "allow_plain_text": true
144 | },
145 | "uid": {
146 | "value": "24171229",
147 | "lite": false,
148 | "hosted": false,
149 | "domid": "",
150 | "domain": "",
151 | "mx": ""
152 | },
153 | "login": "alpha-san",
154 | "karma": {
155 | "value": 0
156 | }
157 | },
158 | {
159 | "status": {
160 | "value": "VALID",
161 | "id": 0
162 | },
163 | "error": "OK",
164 | "age": 228005,
165 | "auth": {
166 | "password_verification_age": 228005,
167 | "have_password": true,
168 | "secure": false,
169 | "allow_plain_text": true
170 | },
171 | "uid": {
172 | "value": "24171229",
173 | "lite": false,
174 | "hosted": false,
175 | "domid": "",
176 | "domain": "",
177 | "mx": ""
178 | },
179 | "login": "alpha-san",
180 | "karma": {
181 | "value": 0
182 | }
183 | },
184 | {
185 | "status": {
186 | "value": "VALID",
187 | "id": 0
188 | },
189 | "error": "OK",
190 | "age": 228005,
191 | "auth": {
192 | "password_verification_age": 228005,
193 | "have_password": true,
194 | "secure": false,
195 | "allow_plain_text": true
196 | },
197 | "uid": {
198 | "value": "24171229",
199 | "lite": false,
200 | "hosted": false,
201 | "domid": "",
202 | "domain": "",
203 | "mx": ""
204 | },
205 | "login": "alpha-san",
206 | "karma": {
207 | "value": 0
208 | }
209 | },
210 | {
211 | "status": {
212 | "value": "VALID",
213 | "id": 0
214 | },
215 | "error": "OK",
216 | "age": 228005,
217 | "auth": {
218 | "password_verification_age": 228005,
219 | "have_password": true,
220 | "secure": false,
221 | "allow_plain_text": true
222 | },
223 | "uid": {
224 | "value": "24171229",
225 | "lite": false,
226 | "hosted": false,
227 | "domid": "",
228 | "domain": "",
229 | "mx": ""
230 | },
231 | "login": "alpha-san",
232 | "karma": {
233 | "value": 0
234 | }
235 | },
236 | {
237 | "status": {
238 | "value": "VALID",
239 | "id": 0
240 | },
241 | "error": "OK",
242 | "age": 228005,
243 | "auth": {
244 | "password_verification_age": 228005,
245 | "have_password": true,
246 | "secure": false,
247 | "allow_plain_text": true
248 | },
249 | "uid": {
250 | "value": "24171229",
251 | "lite": false,
252 | "hosted": false,
253 | "domid": "",
254 | "domain": "",
255 | "mx": ""
256 | },
257 | "login": "alpha-san",
258 | "karma": {
259 | "value": 0
260 | }
261 | },
262 | {
263 | "status": {
264 | "value": "VALID",
265 | "id": 0
266 | },
267 | "error": "OK",
268 | "age": 228005,
269 | "auth": {
270 | "password_verification_age": 228005,
271 | "have_password": true,
272 | "secure": false,
273 | "allow_plain_text": true
274 | },
275 | "uid": {
276 | "value": "24171229",
277 | "lite": false,
278 | "hosted": false,
279 | "domid": "",
280 | "domain": "",
281 | "mx": ""
282 | },
283 | "login": "alpha-san",
284 | "karma": {
285 | "value": 0
286 | }
287 | },
288 | {
289 | "status": {
290 | "value": "VALID",
291 | "id": 0
292 | },
293 | "error": "OK",
294 | "age": 228005,
295 | "auth": {
296 | "password_verification_age": 228005,
297 | "have_password": true,
298 | "secure": false,
299 | "allow_plain_text": true
300 | },
301 | "uid": {
302 | "value": "24171229",
303 | "lite": false,
304 | "hosted": false,
305 | "domid": "",
306 | "domain": "",
307 | "mx": ""
308 | },
309 | "login": "alpha-san",
310 | "karma": {
311 | "value": 0
312 | }
313 | },
314 | {
315 | "status": {
316 | "value": "VALID",
317 | "id": 0
318 | },
319 | "error": "OK",
320 | "age": 228005,
321 | "auth": {
322 | "password_verification_age": 228005,
323 | "have_password": true,
324 | "secure": false,
325 | "allow_plain_text": true
326 | },
327 | "uid": {
328 | "value": "24171229",
329 | "lite": false,
330 | "hosted": false,
331 | "domid": "",
332 | "domain": "",
333 | "mx": ""
334 | },
335 | "login": "alpha-san",
336 | "karma": {
337 | "value": 0
338 | }
339 | },
340 | {
341 | "status": {
342 | "value": "VALID",
343 | "id": 0
344 | },
345 | "error": "OK",
346 | "age": 228005,
347 | "auth": {
348 | "password_verification_age": 228005,
349 | "have_password": true,
350 | "secure": false,
351 | "allow_plain_text": true
352 | },
353 | "uid": {
354 | "value": "24171229",
355 | "lite": false,
356 | "hosted": false,
357 | "domid": "",
358 | "domain": "",
359 | "mx": ""
360 | },
361 | "login": "alpha-san",
362 | "karma": {
363 | "value": 0
364 | }
365 | },
366 | {
367 | "status": {
368 | "value": "VALID",
369 | "id": 0
370 | },
371 | "error": "OK",
372 | "age": 228005,
373 | "auth": {
374 | "password_verification_age": 228005,
375 | "have_password": true,
376 | "secure": false,
377 | "allow_plain_text": true
378 | },
379 | "uid": {
380 | "value": "24171229",
381 | "lite": false,
382 | "hosted": false,
383 | "domid": "",
384 | "domain": "",
385 | "mx": ""
386 | },
387 | "login": "alpha-san",
388 | "karma": {
389 | "value": 0
390 | }
391 | },
392 | {
393 | "status": {
394 | "value": "VALID",
395 | "id": 0
396 | },
397 | "error": "OK",
398 | "age": 228005,
399 | "auth": {
400 | "password_verification_age": 228005,
401 | "have_password": true,
402 | "secure": false,
403 | "allow_plain_text": true
404 | },
405 | "uid": {
406 | "value": "24171229",
407 | "lite": false,
408 | "hosted": false,
409 | "domid": "",
410 | "domain": "",
411 | "mx": ""
412 | },
413 | "login": "alpha-san",
414 | "karma": {
415 | "value": 0
416 | }
417 | },
418 | {
419 | "status": {
420 | "value": "VALID",
421 | "id": 0
422 | },
423 | "error": "OK",
424 | "age": 228005,
425 | "auth": {
426 | "password_verification_age": 228005,
427 | "have_password": true,
428 | "secure": false,
429 | "allow_plain_text": true
430 | },
431 | "uid": {
432 | "value": "24171229",
433 | "lite": false,
434 | "hosted": false,
435 | "domid": "",
436 | "domain": "",
437 | "mx": ""
438 | },
439 | "login": "alpha-san",
440 | "karma": {
441 | "value": 0
442 | }
443 | },
444 | {
445 | "status": {
446 | "value": "VALID",
447 | "id": 0
448 | },
449 | "error": "OK",
450 | "age": 228005,
451 | "auth": {
452 | "password_verification_age": 228005,
453 | "have_password": true,
454 | "secure": false,
455 | "allow_plain_text": true
456 | },
457 | "uid": {
458 | "value": "24171229",
459 | "lite": false,
460 | "hosted": false,
461 | "domid": "",
462 | "domain": "",
463 | "mx": ""
464 | },
465 | "login": "alpha-san",
466 | "karma": {
467 | "value": 0
468 | }
469 | },
470 | {
471 | "status": {
472 | "value": "VALID",
473 | "id": 0
474 | },
475 | "error": "OK",
476 | "age": 228005,
477 | "auth": {
478 | "password_verification_age": 228005,
479 | "have_password": true,
480 | "secure": false,
481 | "allow_plain_text": true
482 | },
483 | "uid": {
484 | "value": "24171229",
485 | "lite": false,
486 | "hosted": false,
487 | "domid": "",
488 | "domain": "",
489 | "mx": ""
490 | },
491 | "login": "alpha-san",
492 | "karma": {
493 | "value": 0
494 | }
495 | },
496 | {
497 | "status": {
498 | "value": "VALID",
499 | "id": 0
500 | },
501 | "error": "OK",
502 | "age": 228005,
503 | "auth": {
504 | "password_verification_age": 228005,
505 | "have_password": true,
506 | "secure": false,
507 | "allow_plain_text": true
508 | },
509 | "uid": {
510 | "value": "24171229",
511 | "lite": false,
512 | "hosted": false,
513 | "domid": "",
514 | "domain": "",
515 | "mx": ""
516 | },
517 | "login": "alpha-san",
518 | "karma": {
519 | "value": 0
520 | }
521 | },
522 | {
523 | "status": {
524 | "value": "VALID",
525 | "id": 0
526 | },
527 | "error": "OK",
528 | "age": 228005,
529 | "auth": {
530 | "password_verification_age": 228005,
531 | "have_password": true,
532 | "secure": false,
533 | "allow_plain_text": true
534 | },
535 | "uid": {
536 | "value": "24171229",
537 | "lite": false,
538 | "hosted": false,
539 | "domid": "",
540 | "domain": "",
541 | "mx": ""
542 | },
543 | "login": "alpha-san",
544 | "karma": {
545 | "value": 0
546 | }
547 | },
548 | {
549 | "status": {
550 | "value": "VALID",
551 | "id": 0
552 | },
553 | "error": "OK",
554 | "age": 228005,
555 | "auth": {
556 | "password_verification_age": 228005,
557 | "have_password": true,
558 | "secure": false,
559 | "allow_plain_text": true
560 | },
561 | "uid": {
562 | "value": "24171229",
563 | "lite": false,
564 | "hosted": false,
565 | "domid": "",
566 | "domain": "",
567 | "mx": ""
568 | },
569 | "login": "alpha-san",
570 | "karma": {
571 | "value": 0
572 | }
573 | },
574 | {
575 | "status": {
576 | "value": "VALID",
577 | "id": 0
578 | },
579 | "error": "OK",
580 | "age": 228005,
581 | "auth": {
582 | "password_verification_age": 228005,
583 | "have_password": true,
584 | "secure": false,
585 | "allow_plain_text": true
586 | },
587 | "uid": {
588 | "value": "24171229",
589 | "lite": false,
590 | "hosted": false,
591 | "domid": "",
592 | "domain": "",
593 | "mx": ""
594 | },
595 | "login": "alpha-san",
596 | "karma": {
597 | "value": 0
598 | }
599 | },
600 | {
601 | "status": {
602 | "value": "VALID",
603 | "id": 0
604 | },
605 | "error": "OK",
606 | "age": 228005,
607 | "auth": {
608 | "password_verification_age": 228005,
609 | "have_password": true,
610 | "secure": false,
611 | "allow_plain_text": true
612 | },
613 | "uid": {
614 | "value": "24171229",
615 | "lite": false,
616 | "hosted": false,
617 | "domid": "",
618 | "domain": "",
619 | "mx": ""
620 | },
621 | "login": "alpha-san",
622 | "karma": {
623 | "value": 0
624 | }
625 | },
626 | {
627 | "status": {
628 | "value": "VALID",
629 | "id": 0
630 | },
631 | "error": "OK",
632 | "age": 228005,
633 | "auth": {
634 | "password_verification_age": 228005,
635 | "have_password": true,
636 | "secure": false,
637 | "allow_plain_text": true
638 | },
639 | "uid": {
640 | "value": "24171229",
641 | "lite": false,
642 | "hosted": false,
643 | "domid": "",
644 | "domain": "",
645 | "mx": ""
646 | },
647 | "login": "alpha-san",
648 | "karma": {
649 | "value": 0
650 | }
651 | },
652 | {
653 | "status": {
654 | "value": "VALID",
655 | "id": 0
656 | },
657 | "error": "OK",
658 | "age": 228005,
659 | "auth": {
660 | "password_verification_age": 228005,
661 | "have_password": true,
662 | "secure": false,
663 | "allow_plain_text": true
664 | },
665 | "uid": {
666 | "value": "24171229",
667 | "lite": false,
668 | "hosted": false,
669 | "domid": "",
670 | "domain": "",
671 | "mx": ""
672 | },
673 | "login": "alpha-san",
674 | "karma": {
675 | "value": 0
676 | }
677 | },
678 | {
679 | "status": {
680 | "value": "VALID",
681 | "id": 0
682 | },
683 | "error": "OK",
684 | "age": 228005,
685 | "auth": {
686 | "password_verification_age": 228005,
687 | "have_password": true,
688 | "secure": false,
689 | "allow_plain_text": true
690 | },
691 | "uid": {
692 | "value": "24171229",
693 | "lite": false,
694 | "hosted": false,
695 | "domid": "",
696 | "domain": "",
697 | "mx": ""
698 | },
699 | "login": "alpha-san",
700 | "karma": {
701 | "value": 0
702 | }
703 | },
704 | {
705 | "status": {
706 | "value": "VALID",
707 | "id": 0
708 | },
709 | "error": "OK",
710 | "age": 228005,
711 | "auth": {
712 | "password_verification_age": 228005,
713 | "have_password": true,
714 | "secure": false,
715 | "allow_plain_text": true
716 | },
717 | "uid": {
718 | "value": "24171229",
719 | "lite": false,
720 | "hosted": false,
721 | "domid": "",
722 | "domain": "",
723 | "mx": ""
724 | },
725 | "login": "alpha-san",
726 | "karma": {
727 | "value": 0
728 | }
729 | },
730 | {
731 | "status": {
732 | "value": "VALID",
733 | "id": 0
734 | },
735 | "error": "OK",
736 | "age": 228005,
737 | "auth": {
738 | "password_verification_age": 228005,
739 | "have_password": true,
740 | "secure": false,
741 | "allow_plain_text": true
742 | },
743 | "uid": {
744 | "value": "24171229",
745 | "lite": false,
746 | "hosted": false,
747 | "domid": "",
748 | "domain": "",
749 | "mx": ""
750 | },
751 | "login": "alpha-san",
752 | "karma": {
753 | "value": 0
754 | }
755 | },
756 | {
757 | "status": {
758 | "value": "VALID",
759 | "id": 0
760 | },
761 | "error": "OK",
762 | "age": 228005,
763 | "auth": {
764 | "password_verification_age": 228005,
765 | "have_password": true,
766 | "secure": false,
767 | "allow_plain_text": true
768 | },
769 | "uid": {
770 | "value": "24171229",
771 | "lite": false,
772 | "hosted": false,
773 | "domid": "",
774 | "domain": "",
775 | "mx": ""
776 | },
777 | "login": "alpha-san",
778 | "karma": {
779 | "value": 0
780 | }
781 | },
782 | {
783 | "status": {
784 | "value": "VALID",
785 | "id": 0
786 | },
787 | "error": "OK",
788 | "age": 228005,
789 | "auth": {
790 | "password_verification_age": 228005,
791 | "have_password": true,
792 | "secure": false,
793 | "allow_plain_text": true
794 | },
795 | "uid": {
796 | "value": "24171229",
797 | "lite": false,
798 | "hosted": false,
799 | "domid": "",
800 | "domain": "",
801 | "mx": ""
802 | },
803 | "login": "alpha-san",
804 | "karma": {
805 | "value": 0
806 | }
807 | },
808 | {
809 | "status": {
810 | "value": "VALID",
811 | "id": 0
812 | },
813 | "error": "OK",
814 | "age": 228005,
815 | "auth": {
816 | "password_verification_age": 228005,
817 | "have_password": true,
818 | "secure": false,
819 | "allow_plain_text": true
820 | },
821 | "uid": {
822 | "value": "24171229",
823 | "lite": false,
824 | "hosted": false,
825 | "domid": "",
826 | "domain": "",
827 | "mx": ""
828 | },
829 | "login": "alpha-san",
830 | "karma": {
831 | "value": 0
832 | }
833 | },
834 | {
835 | "status": {
836 | "value": "VALID",
837 | "id": 0
838 | },
839 | "error": "OK",
840 | "age": 228005,
841 | "auth": {
842 | "password_verification_age": 228005,
843 | "have_password": true,
844 | "secure": false,
845 | "allow_plain_text": true
846 | },
847 | "uid": {
848 | "value": "24171229",
849 | "lite": false,
850 | "hosted": false,
851 | "domid": "",
852 | "domain": "",
853 | "mx": ""
854 | },
855 | "login": "alpha-san",
856 | "karma": {
857 | "value": 0
858 | }
859 | },
860 | {
861 | "status": {
862 | "value": "VALID",
863 | "id": 0
864 | },
865 | "error": "OK",
866 | "age": 228005,
867 | "auth": {
868 | "password_verification_age": 228005,
869 | "have_password": true,
870 | "secure": false,
871 | "allow_plain_text": true
872 | },
873 | "uid": {
874 | "value": "24171229",
875 | "lite": false,
876 | "hosted": false,
877 | "domid": "",
878 | "domain": "",
879 | "mx": ""
880 | },
881 | "login": "alpha-san",
882 | "karma": {
883 | "value": 0
884 | }
885 | },
886 | {
887 | "status": {
888 | "value": "VALID",
889 | "id": 0
890 | },
891 | "error": "OK",
892 | "age": 228005,
893 | "auth": {
894 | "password_verification_age": 228005,
895 | "have_password": true,
896 | "secure": false,
897 | "allow_plain_text": true
898 | },
899 | "uid": {
900 | "value": "24171229",
901 | "lite": false,
902 | "hosted": false,
903 | "domid": "",
904 | "domain": "",
905 | "mx": ""
906 | },
907 | "login": "alpha-san",
908 | "karma": {
909 | "value": 0
910 | }
911 | },
912 | {
913 | "status": {
914 | "value": "VALID",
915 | "id": 0
916 | },
917 | "error": "OK",
918 | "age": 228005,
919 | "auth": {
920 | "password_verification_age": 228005,
921 | "have_password": true,
922 | "secure": false,
923 | "allow_plain_text": true
924 | },
925 | "uid": {
926 | "value": "24171229",
927 | "lite": false,
928 | "hosted": false,
929 | "domid": "",
930 | "domain": "",
931 | "mx": ""
932 | },
933 | "login": "alpha-san",
934 | "karma": {
935 | "value": 0
936 | }
937 | },
938 | {
939 | "status": {
940 | "value": "VALID",
941 | "id": 0
942 | },
943 | "error": "OK",
944 | "age": 228005,
945 | "auth": {
946 | "password_verification_age": 228005,
947 | "have_password": true,
948 | "secure": false,
949 | "allow_plain_text": true
950 | },
951 | "uid": {
952 | "value": "24171229",
953 | "lite": false,
954 | "hosted": false,
955 | "domid": "",
956 | "domain": "",
957 | "mx": ""
958 | },
959 | "login": "alpha-san",
960 | "karma": {
961 | "value": 0
962 | }
963 | },
964 | {
965 | "status": {
966 | "value": "VALID",
967 | "id": 0
968 | },
969 | "error": "OK",
970 | "age": 228005,
971 | "auth": {
972 | "password_verification_age": 228005,
973 | "have_password": true,
974 | "secure": false,
975 | "allow_plain_text": true
976 | },
977 | "uid": {
978 | "value": "24171229",
979 | "lite": false,
980 | "hosted": false,
981 | "domid": "",
982 | "domain": "",
983 | "mx": ""
984 | },
985 | "login": "alpha-san",
986 | "karma": {
987 | "value": 0
988 | }
989 | },
990 | {
991 | "status": {
992 | "value": "VALID",
993 | "id": 0
994 | },
995 | "error": "OK",
996 | "age": 228005,
997 | "auth": {
998 | "password_verification_age": 228005,
999 | "have_password": true,
1000 | "secure": false,
1001 | "allow_plain_text": true
1002 | },
1003 | "uid": {
1004 | "value": "24171229",
1005 | "lite": false,
1006 | "hosted": false,
1007 | "domid": "",
1008 | "domain": "",
1009 | "mx": ""
1010 | },
1011 | "login": "alpha-san",
1012 | "karma": {
1013 | "value": 0
1014 | }
1015 | },
1016 | {
1017 | "status": {
1018 | "value": "VALID",
1019 | "id": 0
1020 | },
1021 | "error": "OK",
1022 | "age": 228005,
1023 | "auth": {
1024 | "password_verification_age": 228005,
1025 | "have_password": true,
1026 | "secure": false,
1027 | "allow_plain_text": true
1028 | },
1029 | "uid": {
1030 | "value": "24171229",
1031 | "lite": false,
1032 | "hosted": false,
1033 | "domid": "",
1034 | "domain": "",
1035 | "mx": ""
1036 | },
1037 | "login": "alpha-san",
1038 | "karma": {
1039 | "value": 0
1040 | }
1041 | },
1042 | {
1043 | "status": {
1044 | "value": "VALID",
1045 | "id": 0
1046 | },
1047 | "error": "OK",
1048 | "age": 228005,
1049 | "auth": {
1050 | "password_verification_age": 228005,
1051 | "have_password": true,
1052 | "secure": false,
1053 | "allow_plain_text": true
1054 | },
1055 | "uid": {
1056 | "value": "24171229",
1057 | "lite": false,
1058 | "hosted": false,
1059 | "domid": "",
1060 | "domain": "",
1061 | "mx": ""
1062 | },
1063 | "login": "alpha-san",
1064 | "karma": {
1065 | "value": 0
1066 | }
1067 | },
1068 | {
1069 | "status": {
1070 | "value": "VALID",
1071 | "id": 0
1072 | },
1073 | "error": "OK",
1074 | "age": 228005,
1075 | "auth": {
1076 | "password_verification_age": 228005,
1077 | "have_password": true,
1078 | "secure": false,
1079 | "allow_plain_text": true
1080 | },
1081 | "uid": {
1082 | "value": "24171229",
1083 | "lite": false,
1084 | "hosted": false,
1085 | "domid": "",
1086 | "domain": "",
1087 | "mx": ""
1088 | },
1089 | "login": "alpha-san",
1090 | "karma": {
1091 | "value": 0
1092 | }
1093 | },
1094 | {
1095 | "status": {
1096 | "value": "VALID",
1097 | "id": 0
1098 | },
1099 | "error": "OK",
1100 | "age": 228005,
1101 | "auth": {
1102 | "password_verification_age": 228005,
1103 | "have_password": true,
1104 | "secure": false,
1105 | "allow_plain_text": true
1106 | },
1107 | "uid": {
1108 | "value": "24171229",
1109 | "lite": false,
1110 | "hosted": false,
1111 | "domid": "",
1112 | "domain": "",
1113 | "mx": ""
1114 | },
1115 | "login": "alpha-san",
1116 | "karma": {
1117 | "value": 0
1118 | }
1119 | },
1120 | {
1121 | "status": {
1122 | "value": "VALID",
1123 | "id": 0
1124 | },
1125 | "error": "OK",
1126 | "age": 228005,
1127 | "auth": {
1128 | "password_verification_age": 228005,
1129 | "have_password": true,
1130 | "secure": false,
1131 | "allow_plain_text": true
1132 | },
1133 | "uid": {
1134 | "value": "24171229",
1135 | "lite": false,
1136 | "hosted": false,
1137 | "domid": "",
1138 | "domain": "",
1139 | "mx": ""
1140 | },
1141 | "login": "alpha-san",
1142 | "karma": {
1143 | "value": 0
1144 | }
1145 | },
1146 | {
1147 | "status": {
1148 | "value": "VALID",
1149 | "id": 0
1150 | },
1151 | "error": "OK",
1152 | "age": 228005,
1153 | "auth": {
1154 | "password_verification_age": 228005,
1155 | "have_password": true,
1156 | "secure": false,
1157 | "allow_plain_text": true
1158 | },
1159 | "uid": {
1160 | "value": "24171229",
1161 | "lite": false,
1162 | "hosted": false,
1163 | "domid": "",
1164 | "domain": "",
1165 | "mx": ""
1166 | },
1167 | "login": "alpha-san",
1168 | "karma": {
1169 | "value": 0
1170 | }
1171 | },
1172 | {
1173 | "status": {
1174 | "value": "VALID",
1175 | "id": 0
1176 | },
1177 | "error": "OK",
1178 | "age": 228005,
1179 | "auth": {
1180 | "password_verification_age": 228005,
1181 | "have_password": true,
1182 | "secure": false,
1183 | "allow_plain_text": true
1184 | },
1185 | "uid": {
1186 | "value": "24171229",
1187 | "lite": false,
1188 | "hosted": false,
1189 | "domid": "",
1190 | "domain": "",
1191 | "mx": ""
1192 | },
1193 | "login": "alpha-san",
1194 | "karma": {
1195 | "value": 0
1196 | }
1197 | },
1198 | {
1199 | "status": {
1200 | "value": "VALID",
1201 | "id": 0
1202 | },
1203 | "error": "OK",
1204 | "age": 228005,
1205 | "auth": {
1206 | "password_verification_age": 228005,
1207 | "have_password": true,
1208 | "secure": false,
1209 | "allow_plain_text": true
1210 | },
1211 | "uid": {
1212 | "value": "24171229",
1213 | "lite": false,
1214 | "hosted": false,
1215 | "domid": "",
1216 | "domain": "",
1217 | "mx": ""
1218 | },
1219 | "login": "alpha-san",
1220 | "karma": {
1221 | "value": 0
1222 | }
1223 | },
1224 | {
1225 | "status": {
1226 | "value": "VALID",
1227 | "id": 0
1228 | },
1229 | "error": "OK",
1230 | "age": 228005,
1231 | "auth": {
1232 | "password_verification_age": 228005,
1233 | "have_password": true,
1234 | "secure": false,
1235 | "allow_plain_text": true
1236 | },
1237 | "uid": {
1238 | "value": "24171229",
1239 | "lite": false,
1240 | "hosted": false,
1241 | "domid": "",
1242 | "domain": "",
1243 | "mx": ""
1244 | },
1245 | "login": "alpha-san",
1246 | "karma": {
1247 | "value": 0
1248 | }
1249 | },
1250 | {
1251 | "status": {
1252 | "value": "VALID",
1253 | "id": 0
1254 | },
1255 | "error": "OK",
1256 | "age": 228005,
1257 | "auth": {
1258 | "password_verification_age": 228005,
1259 | "have_password": true,
1260 | "secure": false,
1261 | "allow_plain_text": true
1262 | },
1263 | "uid": {
1264 | "value": "24171229",
1265 | "lite": false,
1266 | "hosted": false,
1267 | "domid": "",
1268 | "domain": "",
1269 | "mx": ""
1270 | },
1271 | "login": "alpha-san",
1272 | "karma": {
1273 | "value": 0
1274 | }
1275 | },
1276 | {
1277 | "status": {
1278 | "value": "VALID",
1279 | "id": 0
1280 | },
1281 | "error": "OK",
1282 | "age": 228005,
1283 | "auth": {
1284 | "password_verification_age": 228005,
1285 | "have_password": true,
1286 | "secure": false,
1287 | "allow_plain_text": true
1288 | },
1289 | "uid": {
1290 | "value": "24171229",
1291 | "lite": false,
1292 | "hosted": false,
1293 | "domid": "",
1294 | "domain": "",
1295 | "mx": ""
1296 | },
1297 | "login": "alpha-san",
1298 | "karma": {
1299 | "value": 0
1300 | }
1301 | },
1302 | {
1303 | "status": {
1304 | "value": "VALID",
1305 | "id": 0
1306 | },
1307 | "error": "OK",
1308 | "age": 228005,
1309 | "auth": {
1310 | "password_verification_age": 228005,
1311 | "have_password": true,
1312 | "secure": false,
1313 | "allow_plain_text": true
1314 | },
1315 | "uid": {
1316 | "value": "24171229",
1317 | "lite": false,
1318 | "hosted": false,
1319 | "domid": "",
1320 | "domain": "",
1321 | "mx": ""
1322 | },
1323 | "login": "alpha-san",
1324 | "karma": {
1325 | "value": 0
1326 | }
1327 | },
1328 | {
1329 | "status": {
1330 | "value": "VALID",
1331 | "id": 0
1332 | },
1333 | "error": "OK",
1334 | "age": 228005,
1335 | "auth": {
1336 | "password_verification_age": 228005,
1337 | "have_password": true,
1338 | "secure": false,
1339 | "allow_plain_text": true
1340 | },
1341 | "uid": {
1342 | "value": "24171229",
1343 | "lite": false,
1344 | "hosted": false,
1345 | "domid": "",
1346 | "domain": "",
1347 | "mx": ""
1348 | },
1349 | "login": "alpha-san",
1350 | "karma": {
1351 | "value": 0
1352 | }
1353 | },
1354 | {
1355 | "status": {
1356 | "value": "VALID",
1357 | "id": 0
1358 | },
1359 | "error": "OK",
1360 | "age": 228005,
1361 | "auth": {
1362 | "password_verification_age": 228005,
1363 | "have_password": true,
1364 | "secure": false,
1365 | "allow_plain_text": true
1366 | },
1367 | "uid": {
1368 | "value": "24171229",
1369 | "lite": false,
1370 | "hosted": false,
1371 | "domid": "",
1372 | "domain": "",
1373 | "mx": ""
1374 | },
1375 | "login": "alpha-san",
1376 | "karma": {
1377 | "value": 0
1378 | }
1379 | },
1380 | {
1381 | "status": {
1382 | "value": "VALID",
1383 | "id": 0
1384 | },
1385 | "error": "OK",
1386 | "age": 228005,
1387 | "auth": {
1388 | "password_verification_age": 228005,
1389 | "have_password": true,
1390 | "secure": false,
1391 | "allow_plain_text": true
1392 | },
1393 | "uid": {
1394 | "value": "24171229",
1395 | "lite": false,
1396 | "hosted": false,
1397 | "domid": "",
1398 | "domain": "",
1399 | "mx": ""
1400 | },
1401 | "login": "alpha-san",
1402 | "karma": {
1403 | "value": 0
1404 | }
1405 | },
1406 | {
1407 | "status": {
1408 | "value": "VALID",
1409 | "id": 0
1410 | },
1411 | "error": "OK",
1412 | "age": 228005,
1413 | "auth": {
1414 | "password_verification_age": 228005,
1415 | "have_password": true,
1416 | "secure": false,
1417 | "allow_plain_text": true
1418 | },
1419 | "uid": {
1420 | "value": "24171229",
1421 | "lite": false,
1422 | "hosted": false,
1423 | "domid": "",
1424 | "domain": "",
1425 | "mx": ""
1426 | },
1427 | "login": "alpha-san",
1428 | "karma": {
1429 | "value": 0
1430 | }
1431 | },
1432 | {
1433 | "status": {
1434 | "value": "VALID",
1435 | "id": 0
1436 | },
1437 | "error": "OK",
1438 | "age": 228005,
1439 | "auth": {
1440 | "password_verification_age": 228005,
1441 | "have_password": true,
1442 | "secure": false,
1443 | "allow_plain_text": true
1444 | },
1445 | "uid": {
1446 | "value": "24171229",
1447 | "lite": false,
1448 | "hosted": false,
1449 | "domid": "",
1450 | "domain": "",
1451 | "mx": ""
1452 | },
1453 | "login": "alpha-san",
1454 | "karma": {
1455 | "value": 0
1456 | }
1457 | },
1458 | {
1459 | "status": {
1460 | "value": "VALID",
1461 | "id": 0
1462 | },
1463 | "error": "OK",
1464 | "age": 228005,
1465 | "auth": {
1466 | "password_verification_age": 228005,
1467 | "have_password": true,
1468 | "secure": false,
1469 | "allow_plain_text": true
1470 | },
1471 | "uid": {
1472 | "value": "24171229",
1473 | "lite": false,
1474 | "hosted": false,
1475 | "domid": "",
1476 | "domain": "",
1477 | "mx": ""
1478 | },
1479 | "login": "alpha-san",
1480 | "karma": {
1481 | "value": 0
1482 | }
1483 | },
1484 | {
1485 | "status": {
1486 | "value": "VALID",
1487 | "id": 0
1488 | },
1489 | "error": "OK",
1490 | "age": 228005,
1491 | "auth": {
1492 | "password_verification_age": 228005,
1493 | "have_password": true,
1494 | "secure": false,
1495 | "allow_plain_text": true
1496 | },
1497 | "uid": {
1498 | "value": "24171229",
1499 | "lite": false,
1500 | "hosted": false,
1501 | "domid": "",
1502 | "domain": "",
1503 | "mx": ""
1504 | },
1505 | "login": "alpha-san",
1506 | "karma": {
1507 | "value": 0
1508 | }
1509 | },
1510 | {
1511 | "status": {
1512 | "value": "VALID",
1513 | "id": 0
1514 | },
1515 | "error": "OK",
1516 | "age": 228005,
1517 | "auth": {
1518 | "password_verification_age": 228005,
1519 | "have_password": true,
1520 | "secure": false,
1521 | "allow_plain_text": true
1522 | },
1523 | "uid": {
1524 | "value": "24171229",
1525 | "lite": false,
1526 | "hosted": false,
1527 | "domid": "",
1528 | "domain": "",
1529 | "mx": ""
1530 | },
1531 | "login": "alpha-san",
1532 | "karma": {
1533 | "value": 0
1534 | }
1535 | },
1536 | {
1537 | "status": {
1538 | "value": "VALID",
1539 | "id": 0
1540 | },
1541 | "error": "OK",
1542 | "age": 228005,
1543 | "auth": {
1544 | "password_verification_age": 228005,
1545 | "have_password": true,
1546 | "secure": false,
1547 | "allow_plain_text": true
1548 | },
1549 | "uid": {
1550 | "value": "24171229",
1551 | "lite": false,
1552 | "hosted": false,
1553 | "domid": "",
1554 | "domain": "",
1555 | "mx": ""
1556 | },
1557 | "login": "alpha-san",
1558 | "karma": {
1559 | "value": 0
1560 | }
1561 | },
1562 | {
1563 | "status": {
1564 | "value": "VALID",
1565 | "id": 0
1566 | },
1567 | "error": "OK",
1568 | "age": 228005,
1569 | "auth": {
1570 | "password_verification_age": 228005,
1571 | "have_password": true,
1572 | "secure": false,
1573 | "allow_plain_text": true
1574 | },
1575 | "uid": {
1576 | "value": "24171229",
1577 | "lite": false,
1578 | "hosted": false,
1579 | "domid": "",
1580 | "domain": "",
1581 | "mx": ""
1582 | },
1583 | "login": "alpha-san",
1584 | "karma": {
1585 | "value": 0
1586 | }
1587 | },
1588 | {
1589 | "status": {
1590 | "value": "VALID",
1591 | "id": 0
1592 | },
1593 | "error": "OK",
1594 | "age": 228005,
1595 | "auth": {
1596 | "password_verification_age": 228005,
1597 | "have_password": true,
1598 | "secure": false,
1599 | "allow_plain_text": true
1600 | },
1601 | "uid": {
1602 | "value": "24171229",
1603 | "lite": false,
1604 | "hosted": false,
1605 | "domid": "",
1606 | "domain": "",
1607 | "mx": ""
1608 | },
1609 | "login": "alpha-san",
1610 | "karma": {
1611 | "value": 0
1612 | }
1613 | },
1614 | {
1615 | "status": {
1616 | "value": "VALID",
1617 | "id": 0
1618 | },
1619 | "error": "OK",
1620 | "age": 228005,
1621 | "auth": {
1622 | "password_verification_age": 228005,
1623 | "have_password": true,
1624 | "secure": false,
1625 | "allow_plain_text": true
1626 | },
1627 | "uid": {
1628 | "value": "24171229",
1629 | "lite": false,
1630 | "hosted": false,
1631 | "domid": "",
1632 | "domain": "",
1633 | "mx": ""
1634 | },
1635 | "login": "alpha-san",
1636 | "karma": {
1637 | "value": 0
1638 | }
1639 | },
1640 | {
1641 | "status": {
1642 | "value": "VALID",
1643 | "id": 0
1644 | },
1645 | "error": "OK",
1646 | "age": 228005,
1647 | "auth": {
1648 | "password_verification_age": 228005,
1649 | "have_password": true,
1650 | "secure": false,
1651 | "allow_plain_text": true
1652 | },
1653 | "uid": {
1654 | "value": "24171229",
1655 | "lite": false,
1656 | "hosted": false,
1657 | "domid": "",
1658 | "domain": "",
1659 | "mx": ""
1660 | },
1661 | "login": "alpha-san",
1662 | "karma": {
1663 | "value": 0
1664 | }
1665 | },
1666 | {
1667 | "status": {
1668 | "value": "VALID",
1669 | "id": 0
1670 | },
1671 | "error": "OK",
1672 | "age": 228005,
1673 | "auth": {
1674 | "password_verification_age": 228005,
1675 | "have_password": true,
1676 | "secure": false,
1677 | "allow_plain_text": true
1678 | },
1679 | "uid": {
1680 | "value": "24171229",
1681 | "lite": false,
1682 | "hosted": false,
1683 | "domid": "",
1684 | "domain": "",
1685 | "mx": ""
1686 | },
1687 | "login": "alpha-san",
1688 | "karma": {
1689 | "value": 0
1690 | }
1691 | },
1692 | {
1693 | "status": {
1694 | "value": "VALID",
1695 | "id": 0
1696 | },
1697 | "error": "OK",
1698 | "age": 228005,
1699 | "auth": {
1700 | "password_verification_age": 228005,
1701 | "have_password": true,
1702 | "secure": false,
1703 | "allow_plain_text": true
1704 | },
1705 | "uid": {
1706 | "value": "24171229",
1707 | "lite": false,
1708 | "hosted": false,
1709 | "domid": "",
1710 | "domain": "",
1711 | "mx": ""
1712 | },
1713 | "login": "alpha-san",
1714 | "karma": {
1715 | "value": 0
1716 | }
1717 | },
1718 | {
1719 | "status": {
1720 | "value": "VALID",
1721 | "id": 0
1722 | },
1723 | "error": "OK",
1724 | "age": 228005,
1725 | "auth": {
1726 | "password_verification_age": 228005,
1727 | "have_password": true,
1728 | "secure": false,
1729 | "allow_plain_text": true
1730 | },
1731 | "uid": {
1732 | "value": "24171229",
1733 | "lite": false,
1734 | "hosted": false,
1735 | "domid": "",
1736 | "domain": "",
1737 | "mx": ""
1738 | },
1739 | "login": "alpha-san",
1740 | "karma": {
1741 | "value": 0
1742 | }
1743 | },
1744 | {
1745 | "status": {
1746 | "value": "VALID",
1747 | "id": 0
1748 | },
1749 | "error": "OK",
1750 | "age": 228005,
1751 | "auth": {
1752 | "password_verification_age": 228005,
1753 | "have_password": true,
1754 | "secure": false,
1755 | "allow_plain_text": true
1756 | },
1757 | "uid": {
1758 | "value": "24171229",
1759 | "lite": false,
1760 | "hosted": false,
1761 | "domid": "",
1762 | "domain": "",
1763 | "mx": ""
1764 | },
1765 | "login": "alpha-san",
1766 | "karma": {
1767 | "value": 0
1768 | }
1769 | },
1770 | {
1771 | "status": {
1772 | "value": "VALID",
1773 | "id": 0
1774 | },
1775 | "error": "OK",
1776 | "age": 228005,
1777 | "auth": {
1778 | "password_verification_age": 228005,
1779 | "have_password": true,
1780 | "secure": false,
1781 | "allow_plain_text": true
1782 | },
1783 | "uid": {
1784 | "value": "24171229",
1785 | "lite": false,
1786 | "hosted": false,
1787 | "domid": "",
1788 | "domain": "",
1789 | "mx": ""
1790 | },
1791 | "login": "alpha-san",
1792 | "karma": {
1793 | "value": 0
1794 | }
1795 | },
1796 | {
1797 | "status": {
1798 | "value": "VALID",
1799 | "id": 0
1800 | },
1801 | "error": "OK",
1802 | "age": 228005,
1803 | "auth": {
1804 | "password_verification_age": 228005,
1805 | "have_password": true,
1806 | "secure": false,
1807 | "allow_plain_text": true
1808 | },
1809 | "uid": {
1810 | "value": "24171229",
1811 | "lite": false,
1812 | "hosted": false,
1813 | "domid": "",
1814 | "domain": "",
1815 | "mx": ""
1816 | },
1817 | "login": "alpha-san",
1818 | "karma": {
1819 | "value": 0
1820 | }
1821 | },
1822 | {
1823 | "status": {
1824 | "value": "VALID",
1825 | "id": 0
1826 | },
1827 | "error": "OK",
1828 | "age": 228005,
1829 | "auth": {
1830 | "password_verification_age": 228005,
1831 | "have_password": true,
1832 | "secure": false,
1833 | "allow_plain_text": true
1834 | },
1835 | "uid": {
1836 | "value": "24171229",
1837 | "lite": false,
1838 | "hosted": false,
1839 | "domid": "",
1840 | "domain": "",
1841 | "mx": ""
1842 | },
1843 | "login": "alpha-san",
1844 | "karma": {
1845 | "value": 0
1846 | }
1847 | },
1848 | {
1849 | "status": {
1850 | "value": "VALID",
1851 | "id": 0
1852 | },
1853 | "error": "OK",
1854 | "age": 228005,
1855 | "auth": {
1856 | "password_verification_age": 228005,
1857 | "have_password": true,
1858 | "secure": false,
1859 | "allow_plain_text": true
1860 | },
1861 | "uid": {
1862 | "value": "24171229",
1863 | "lite": false,
1864 | "hosted": false,
1865 | "domid": "",
1866 | "domain": "",
1867 | "mx": ""
1868 | },
1869 | "login": "alpha-san",
1870 | "karma": {
1871 | "value": 0
1872 | }
1873 | },
1874 | {
1875 | "status": {
1876 | "value": "VALID",
1877 | "id": 0
1878 | },
1879 | "error": "OK",
1880 | "age": 228005,
1881 | "auth": {
1882 | "password_verification_age": 228005,
1883 | "have_password": true,
1884 | "secure": false,
1885 | "allow_plain_text": true
1886 | },
1887 | "uid": {
1888 | "value": "24171229",
1889 | "lite": false,
1890 | "hosted": false,
1891 | "domid": "",
1892 | "domain": "",
1893 | "mx": ""
1894 | },
1895 | "login": "alpha-san",
1896 | "karma": {
1897 | "value": 0
1898 | }
1899 | },
1900 | {
1901 | "status": {
1902 | "value": "VALID",
1903 | "id": 0
1904 | },
1905 | "error": "OK",
1906 | "age": 228005,
1907 | "auth": {
1908 | "password_verification_age": 228005,
1909 | "have_password": true,
1910 | "secure": false,
1911 | "allow_plain_text": true
1912 | },
1913 | "uid": {
1914 | "value": "24171229",
1915 | "lite": false,
1916 | "hosted": false,
1917 | "domid": "",
1918 | "domain": "",
1919 | "mx": ""
1920 | },
1921 | "login": "alpha-san",
1922 | "karma": {
1923 | "value": 0
1924 | }
1925 | },
1926 | {
1927 | "status": {
1928 | "value": "VALID",
1929 | "id": 0
1930 | },
1931 | "error": "OK",
1932 | "age": 228005,
1933 | "auth": {
1934 | "password_verification_age": 228005,
1935 | "have_password": true,
1936 | "secure": false,
1937 | "allow_plain_text": true
1938 | },
1939 | "uid": {
1940 | "value": "24171229",
1941 | "lite": false,
1942 | "hosted": false,
1943 | "domid": "",
1944 | "domain": "",
1945 | "mx": ""
1946 | },
1947 | "login": "alpha-san",
1948 | "karma": {
1949 | "value": 0
1950 | }
1951 | },
1952 | {
1953 | "status": {
1954 | "value": "VALID",
1955 | "id": 0
1956 | },
1957 | "error": "OK",
1958 | "age": 228005,
1959 | "auth": {
1960 | "password_verification_age": 228005,
1961 | "have_password": true,
1962 | "secure": false,
1963 | "allow_plain_text": true
1964 | },
1965 | "uid": {
1966 | "value": "24171229",
1967 | "lite": false,
1968 | "hosted": false,
1969 | "domid": "",
1970 | "domain": "",
1971 | "mx": ""
1972 | },
1973 | "login": "alpha-san",
1974 | "karma": {
1975 | "value": 0
1976 | }
1977 | },
1978 | {
1979 | "status": {
1980 | "value": "VALID",
1981 | "id": 0
1982 | },
1983 | "error": "OK",
1984 | "age": 228005,
1985 | "auth": {
1986 | "password_verification_age": 228005,
1987 | "have_password": true,
1988 | "secure": false,
1989 | "allow_plain_text": true
1990 | },
1991 | "uid": {
1992 | "value": "24171229",
1993 | "lite": false,
1994 | "hosted": false,
1995 | "domid": "",
1996 | "domain": "",
1997 | "mx": ""
1998 | },
1999 | "login": "alpha-san",
2000 | "karma": {
2001 | "value": 0
2002 | }
2003 | },
2004 | {
2005 | "status": {
2006 | "value": "VALID",
2007 | "id": 0
2008 | },
2009 | "error": "OK",
2010 | "age": 228005,
2011 | "auth": {
2012 | "password_verification_age": 228005,
2013 | "have_password": true,
2014 | "secure": false,
2015 | "allow_plain_text": true
2016 | },
2017 | "uid": {
2018 | "value": "24171229",
2019 | "lite": false,
2020 | "hosted": false,
2021 | "domid": "",
2022 | "domain": "",
2023 | "mx": ""
2024 | },
2025 | "login": "alpha-san",
2026 | "karma": {
2027 | "value": 0
2028 | }
2029 | },
2030 | {
2031 | "status": {
2032 | "value": "VALID",
2033 | "id": 0
2034 | },
2035 | "error": "OK",
2036 | "age": 228005,
2037 | "auth": {
2038 | "password_verification_age": 228005,
2039 | "have_password": true,
2040 | "secure": false,
2041 | "allow_plain_text": true
2042 | },
2043 | "uid": {
2044 | "value": "24171229",
2045 | "lite": false,
2046 | "hosted": false,
2047 | "domid": "",
2048 | "domain": "",
2049 | "mx": ""
2050 | },
2051 | "login": "alpha-san",
2052 | "karma": {
2053 | "value": 0
2054 | }
2055 | },
2056 | {
2057 | "status": {
2058 | "value": "VALID",
2059 | "id": 0
2060 | },
2061 | "error": "OK",
2062 | "age": 228005,
2063 | "auth": {
2064 | "password_verification_age": 228005,
2065 | "have_password": true,
2066 | "secure": false,
2067 | "allow_plain_text": true
2068 | },
2069 | "uid": {
2070 | "value": "24171229",
2071 | "lite": false,
2072 | "hosted": false,
2073 | "domid": "",
2074 | "domain": "",
2075 | "mx": ""
2076 | },
2077 | "login": "alpha-san",
2078 | "karma": {
2079 | "value": 0
2080 | }
2081 | },
2082 | {
2083 | "status": {
2084 | "value": "VALID",
2085 | "id": 0
2086 | },
2087 | "error": "OK",
2088 | "age": 228005,
2089 | "auth": {
2090 | "password_verification_age": 228005,
2091 | "have_password": true,
2092 | "secure": false,
2093 | "allow_plain_text": true
2094 | },
2095 | "uid": {
2096 | "value": "24171229",
2097 | "lite": false,
2098 | "hosted": false,
2099 | "domid": "",
2100 | "domain": "",
2101 | "mx": ""
2102 | },
2103 | "login": "alpha-san",
2104 | "karma": {
2105 | "value": 0
2106 | }
2107 | },
2108 | {
2109 | "status": {
2110 | "value": "VALID",
2111 | "id": 0
2112 | },
2113 | "error": "OK",
2114 | "age": 228005,
2115 | "auth": {
2116 | "password_verification_age": 228005,
2117 | "have_password": true,
2118 | "secure": false,
2119 | "allow_plain_text": true
2120 | },
2121 | "uid": {
2122 | "value": "24171229",
2123 | "lite": false,
2124 | "hosted": false,
2125 | "domid": "",
2126 | "domain": "",
2127 | "mx": ""
2128 | },
2129 | "login": "alpha-san",
2130 | "karma": {
2131 | "value": 0
2132 | }
2133 | },
2134 | {
2135 | "status": {
2136 | "value": "VALID",
2137 | "id": 0
2138 | },
2139 | "error": "OK",
2140 | "age": 228005,
2141 | "auth": {
2142 | "password_verification_age": 228005,
2143 | "have_password": true,
2144 | "secure": false,
2145 | "allow_plain_text": true
2146 | },
2147 | "uid": {
2148 | "value": "24171229",
2149 | "lite": false,
2150 | "hosted": false,
2151 | "domid": "",
2152 | "domain": "",
2153 | "mx": ""
2154 | },
2155 | "login": "alpha-san",
2156 | "karma": {
2157 | "value": 0
2158 | }
2159 | },
2160 | {
2161 | "status": {
2162 | "value": "VALID",
2163 | "id": 0
2164 | },
2165 | "error": "OK",
2166 | "age": 228005,
2167 | "auth": {
2168 | "password_verification_age": 228005,
2169 | "have_password": true,
2170 | "secure": false,
2171 | "allow_plain_text": true
2172 | },
2173 | "uid": {
2174 | "value": "24171229",
2175 | "lite": false,
2176 | "hosted": false,
2177 | "domid": "",
2178 | "domain": "",
2179 | "mx": ""
2180 | },
2181 | "login": "alpha-san",
2182 | "karma": {
2183 | "value": 0
2184 | }
2185 | },
2186 | {
2187 | "status": {
2188 | "value": "VALID",
2189 | "id": 0
2190 | },
2191 | "error": "OK",
2192 | "age": 228005,
2193 | "auth": {
2194 | "password_verification_age": 228005,
2195 | "have_password": true,
2196 | "secure": false,
2197 | "allow_plain_text": true
2198 | },
2199 | "uid": {
2200 | "value": "24171229",
2201 | "lite": false,
2202 | "hosted": false,
2203 | "domid": "",
2204 | "domain": "",
2205 | "mx": ""
2206 | },
2207 | "login": "alpha-san",
2208 | "karma": {
2209 | "value": 0
2210 | }
2211 | },
2212 | {
2213 | "status": {
2214 | "value": "VALID",
2215 | "id": 0
2216 | },
2217 | "error": "OK",
2218 | "age": 228005,
2219 | "auth": {
2220 | "password_verification_age": 228005,
2221 | "have_password": true,
2222 | "secure": false,
2223 | "allow_plain_text": true
2224 | },
2225 | "uid": {
2226 | "value": "24171229",
2227 | "lite": false,
2228 | "hosted": false,
2229 | "domid": "",
2230 | "domain": "",
2231 | "mx": ""
2232 | },
2233 | "login": "alpha-san",
2234 | "karma": {
2235 | "value": 0
2236 | }
2237 | },
2238 | {
2239 | "status": {
2240 | "value": "VALID",
2241 | "id": 0
2242 | },
2243 | "error": "OK",
2244 | "age": 228005,
2245 | "auth": {
2246 | "password_verification_age": 228005,
2247 | "have_password": true,
2248 | "secure": false,
2249 | "allow_plain_text": true
2250 | },
2251 | "uid": {
2252 | "value": "24171229",
2253 | "lite": false,
2254 | "hosted": false,
2255 | "domid": "",
2256 | "domain": "",
2257 | "mx": ""
2258 | },
2259 | "login": "alpha-san",
2260 | "karma": {
2261 | "value": 0
2262 | }
2263 | },
2264 | {
2265 | "status": {
2266 | "value": "VALID",
2267 | "id": 0
2268 | },
2269 | "error": "OK",
2270 | "age": 228005,
2271 | "auth": {
2272 | "password_verification_age": 228005,
2273 | "have_password": true,
2274 | "secure": false,
2275 | "allow_plain_text": true
2276 | },
2277 | "uid": {
2278 | "value": "24171229",
2279 | "lite": false,
2280 | "hosted": false,
2281 | "domid": "",
2282 | "domain": "",
2283 | "mx": ""
2284 | },
2285 | "login": "alpha-san",
2286 | "karma": {
2287 | "value": 0
2288 | }
2289 | },
2290 | {
2291 | "status": {
2292 | "value": "VALID",
2293 | "id": 0
2294 | },
2295 | "error": "OK",
2296 | "age": 228005,
2297 | "auth": {
2298 | "password_verification_age": 228005,
2299 | "have_password": true,
2300 | "secure": false,
2301 | "allow_plain_text": true
2302 | },
2303 | "uid": {
2304 | "value": "24171229",
2305 | "lite": false,
2306 | "hosted": false,
2307 | "domid": "",
2308 | "domain": "",
2309 | "mx": ""
2310 | },
2311 | "login": "alpha-san",
2312 | "karma": {
2313 | "value": 0
2314 | }
2315 | },
2316 | {
2317 | "status": {
2318 | "value": "VALID",
2319 | "id": 0
2320 | },
2321 | "error": "OK",
2322 | "age": 228005,
2323 | "auth": {
2324 | "password_verification_age": 228005,
2325 | "have_password": true,
2326 | "secure": false,
2327 | "allow_plain_text": true
2328 | },
2329 | "uid": {
2330 | "value": "24171229",
2331 | "lite": false,
2332 | "hosted": false,
2333 | "domid": "",
2334 | "domain": "",
2335 | "mx": ""
2336 | },
2337 | "login": "alpha-san",
2338 | "karma": {
2339 | "value": 0
2340 | }
2341 | },
2342 | {
2343 | "status": {
2344 | "value": "VALID",
2345 | "id": 0
2346 | },
2347 | "error": "OK",
2348 | "age": 228005,
2349 | "auth": {
2350 | "password_verification_age": 228005,
2351 | "have_password": true,
2352 | "secure": false,
2353 | "allow_plain_text": true
2354 | },
2355 | "uid": {
2356 | "value": "24171229",
2357 | "lite": false,
2358 | "hosted": false,
2359 | "domid": "",
2360 | "domain": "",
2361 | "mx": ""
2362 | },
2363 | "login": "alpha-san",
2364 | "karma": {
2365 | "value": 0
2366 | }
2367 | },
2368 | {
2369 | "status": {
2370 | "value": "VALID",
2371 | "id": 0
2372 | },
2373 | "error": "OK",
2374 | "age": 228005,
2375 | "auth": {
2376 | "password_verification_age": 228005,
2377 | "have_password": true,
2378 | "secure": false,
2379 | "allow_plain_text": true
2380 | },
2381 | "uid": {
2382 | "value": "24171229",
2383 | "lite": false,
2384 | "hosted": false,
2385 | "domid": "",
2386 | "domain": "",
2387 | "mx": ""
2388 | },
2389 | "login": "alpha-san",
2390 | "karma": {
2391 | "value": 0
2392 | }
2393 | },
2394 | {
2395 | "status": {
2396 | "value": "VALID",
2397 | "id": 0
2398 | },
2399 | "error": "OK",
2400 | "age": 228005,
2401 | "auth": {
2402 | "password_verification_age": 228005,
2403 | "have_password": true,
2404 | "secure": false,
2405 | "allow_plain_text": true
2406 | },
2407 | "uid": {
2408 | "value": "24171229",
2409 | "lite": false,
2410 | "hosted": false,
2411 | "domid": "",
2412 | "domain": "",
2413 | "mx": ""
2414 | },
2415 | "login": "alpha-san",
2416 | "karma": {
2417 | "value": 0
2418 | }
2419 | },
2420 | {
2421 | "status": {
2422 | "value": "VALID",
2423 | "id": 0
2424 | },
2425 | "error": "OK",
2426 | "age": 228005,
2427 | "auth": {
2428 | "password_verification_age": 228005,
2429 | "have_password": true,
2430 | "secure": false,
2431 | "allow_plain_text": true
2432 | },
2433 | "uid": {
2434 | "value": "24171229",
2435 | "lite": false,
2436 | "hosted": false,
2437 | "domid": "",
2438 | "domain": "",
2439 | "mx": ""
2440 | },
2441 | "login": "alpha-san",
2442 | "karma": {
2443 | "value": 0
2444 | }
2445 | },
2446 | {
2447 | "status": {
2448 | "value": "VALID",
2449 | "id": 0
2450 | },
2451 | "error": "OK",
2452 | "age": 228005,
2453 | "auth": {
2454 | "password_verification_age": 228005,
2455 | "have_password": true,
2456 | "secure": false,
2457 | "allow_plain_text": true
2458 | },
2459 | "uid": {
2460 | "value": "24171229",
2461 | "lite": false,
2462 | "hosted": false,
2463 | "domid": "",
2464 | "domain": "",
2465 | "mx": ""
2466 | },
2467 | "login": "alpha-san",
2468 | "karma": {
2469 | "value": 0
2470 | }
2471 | },
2472 | {
2473 | "status": {
2474 | "value": "VALID",
2475 | "id": 0
2476 | },
2477 | "error": "OK",
2478 | "age": 228005,
2479 | "auth": {
2480 | "password_verification_age": 228005,
2481 | "have_password": true,
2482 | "secure": false,
2483 | "allow_plain_text": true
2484 | },
2485 | "uid": {
2486 | "value": "24171229",
2487 | "lite": false,
2488 | "hosted": false,
2489 | "domid": "",
2490 | "domain": "",
2491 | "mx": ""
2492 | },
2493 | "login": "alpha-san",
2494 | "karma": {
2495 | "value": 0
2496 | }
2497 | },
2498 | {
2499 | "status": {
2500 | "value": "VALID",
2501 | "id": 0
2502 | },
2503 | "error": "OK",
2504 | "age": 228005,
2505 | "auth": {
2506 | "password_verification_age": 228005,
2507 | "have_password": true,
2508 | "secure": false,
2509 | "allow_plain_text": true
2510 | },
2511 | "uid": {
2512 | "value": "24171229",
2513 | "lite": false,
2514 | "hosted": false,
2515 | "domid": "",
2516 | "domain": "",
2517 | "mx": ""
2518 | },
2519 | "login": "alpha-san",
2520 | "karma": {
2521 | "value": 0
2522 | }
2523 | },
2524 | {
2525 | "status": {
2526 | "value": "VALID",
2527 | "id": 0
2528 | },
2529 | "error": "OK",
2530 | "age": 228005,
2531 | "auth": {
2532 | "password_verification_age": 228005,
2533 | "have_password": true,
2534 | "secure": false,
2535 | "allow_plain_text": true
2536 | },
2537 | "uid": {
2538 | "value": "24171229",
2539 | "lite": false,
2540 | "hosted": false,
2541 | "domid": "",
2542 | "domain": "",
2543 | "mx": ""
2544 | },
2545 | "login": "alpha-san",
2546 | "karma": {
2547 | "value": 0
2548 | }
2549 | },
2550 | {
2551 | "status": {
2552 | "value": "VALID",
2553 | "id": 0
2554 | },
2555 | "error": "OK",
2556 | "age": 228005,
2557 | "auth": {
2558 | "password_verification_age": 228005,
2559 | "have_password": true,
2560 | "secure": false,
2561 | "allow_plain_text": true
2562 | },
2563 | "uid": {
2564 | "value": "24171229",
2565 | "lite": false,
2566 | "hosted": false,
2567 | "domid": "",
2568 | "domain": "",
2569 | "mx": ""
2570 | },
2571 | "login": "alpha-san",
2572 | "karma": {
2573 | "value": 0
2574 | }
2575 | },
2576 | {
2577 | "status": {
2578 | "value": "VALID",
2579 | "id": 0
2580 | },
2581 | "error": "OK",
2582 | "age": 228005,
2583 | "auth": {
2584 | "password_verification_age": 228005,
2585 | "have_password": true,
2586 | "secure": false,
2587 | "allow_plain_text": true
2588 | },
2589 | "uid": {
2590 | "value": "24171229",
2591 | "lite": false,
2592 | "hosted": false,
2593 | "domid": "",
2594 | "domain": "",
2595 | "mx": ""
2596 | },
2597 | "login": "alpha-san",
2598 | "karma": {
2599 | "value": 0
2600 | }
2601 | }
2602 | ]
2603 |
2604 |
--------------------------------------------------------------------------------