@sentry/node 8.0.0-alpha.2 → 8.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -93
- package/cjs/index.js +3665 -77
- package/cjs/index.js.map +1 -1
- package/esm/index.js +3614 -31
- package/esm/index.js.map +1 -1
- package/esm/integrations/anr/worker-script.js +2 -2
- package/esm/package.json +1 -0
- package/package.json +21 -6
- package/types/index.d.ts +6 -5
- package/types/index.d.ts.map +1 -1
- package/types/integrations/anr/worker-script.d.ts +1 -1
- package/types/integrations/anr/worker-script.d.ts.map +1 -1
- package/types/integrations/http.d.ts +4 -0
- package/types/integrations/http.d.ts.map +1 -1
- package/types/integrations/tracing/fastify.d.ts +9 -0
- package/types/integrations/tracing/fastify.d.ts.map +1 -1
- package/types/integrations/tracing/prisma.d.ts.map +1 -1
- package/types/proxy/helpers.d.ts +2 -2
- package/types/proxy/helpers.d.ts.map +1 -1
- package/types/proxy/index.d.ts.map +1 -1
- package/types/sdk/hub.d.ts.map +1 -1
- package/types/sdk/init.d.ts.map +1 -1
- package/types/transports/http-module.d.ts +2 -2
- package/types/transports/http-module.d.ts.map +1 -1
- package/types/types.d.ts +3 -4
- package/types/types.d.ts.map +1 -1
- package/types-ts3.8/index.d.ts +6 -5
- package/types-ts3.8/integrations/anr/worker-script.d.ts +1 -1
- package/types-ts3.8/integrations/http.d.ts +4 -0
- package/types-ts3.8/integrations/tracing/fastify.d.ts +9 -0
- package/types-ts3.8/proxy/helpers.d.ts +2 -2
- package/types-ts3.8/transports/http-module.d.ts +2 -2
- package/types-ts3.8/types.d.ts +3 -4
- package/cjs/cron/common.js +0 -56
- package/cjs/cron/common.js.map +0 -1
- package/cjs/cron/cron.js +0 -102
- package/cjs/cron/cron.js.map +0 -1
- package/cjs/cron/index.js +0 -15
- package/cjs/cron/index.js.map +0 -1
- package/cjs/cron/node-cron.js +0 -61
- package/cjs/cron/node-cron.js.map +0 -1
- package/cjs/cron/node-schedule.js +0 -57
- package/cjs/cron/node-schedule.js.map +0 -1
- package/cjs/debug-build.js +0 -11
- package/cjs/debug-build.js.map +0 -1
- package/cjs/integrations/anr/index.js +0 -144
- package/cjs/integrations/anr/index.js.map +0 -1
- package/cjs/integrations/anr/worker-script.js +0 -2
- package/cjs/integrations/anr/worker-script.js.map +0 -1
- package/cjs/integrations/console.js +0 -40
- package/cjs/integrations/console.js.map +0 -1
- package/cjs/integrations/context.js +0 -435
- package/cjs/integrations/context.js.map +0 -1
- package/cjs/integrations/contextlines.js +0 -137
- package/cjs/integrations/contextlines.js.map +0 -1
- package/cjs/integrations/http.js +0 -137
- package/cjs/integrations/http.js.map +0 -1
- package/cjs/integrations/local-variables/common.js +0 -87
- package/cjs/integrations/local-variables/common.js.map +0 -1
- package/cjs/integrations/local-variables/index.js +0 -8
- package/cjs/integrations/local-variables/index.js.map +0 -1
- package/cjs/integrations/local-variables/local-variables-sync.js +0 -385
- package/cjs/integrations/local-variables/local-variables-sync.js.map +0 -1
- package/cjs/integrations/modules.js +0 -99
- package/cjs/integrations/modules.js.map +0 -1
- package/cjs/integrations/node-fetch.js +0 -82
- package/cjs/integrations/node-fetch.js.map +0 -1
- package/cjs/integrations/onuncaughtexception.js +0 -137
- package/cjs/integrations/onuncaughtexception.js.map +0 -1
- package/cjs/integrations/onunhandledrejection.js +0 -90
- package/cjs/integrations/onunhandledrejection.js.map +0 -1
- package/cjs/integrations/spotlight.js +0 -109
- package/cjs/integrations/spotlight.js.map +0 -1
- package/cjs/integrations/tracing/express.js +0 -103
- package/cjs/integrations/tracing/express.js.map +0 -1
- package/cjs/integrations/tracing/fastify.js +0 -33
- package/cjs/integrations/tracing/fastify.js.map +0 -1
- package/cjs/integrations/tracing/graphql.js +0 -34
- package/cjs/integrations/tracing/graphql.js.map +0 -1
- package/cjs/integrations/tracing/hapi/index.js +0 -81
- package/cjs/integrations/tracing/hapi/index.js.map +0 -1
- package/cjs/integrations/tracing/index.js +0 -37
- package/cjs/integrations/tracing/index.js.map +0 -1
- package/cjs/integrations/tracing/koa.js +0 -21
- package/cjs/integrations/tracing/koa.js.map +0 -1
- package/cjs/integrations/tracing/mongo.js +0 -33
- package/cjs/integrations/tracing/mongo.js.map +0 -1
- package/cjs/integrations/tracing/mongoose.js +0 -33
- package/cjs/integrations/tracing/mongoose.js.map +0 -1
- package/cjs/integrations/tracing/mysql.js +0 -26
- package/cjs/integrations/tracing/mysql.js.map +0 -1
- package/cjs/integrations/tracing/mysql2.js +0 -33
- package/cjs/integrations/tracing/mysql2.js.map +0 -1
- package/cjs/integrations/tracing/nest.js +0 -26
- package/cjs/integrations/tracing/nest.js.map +0 -1
- package/cjs/integrations/tracing/postgres.js +0 -34
- package/cjs/integrations/tracing/postgres.js.map +0 -1
- package/cjs/integrations/tracing/prisma.js +0 -33
- package/cjs/integrations/tracing/prisma.js.map +0 -1
- package/cjs/nodeVersion.js +0 -12
- package/cjs/nodeVersion.js.map +0 -1
- package/cjs/otel/contextManager.js +0 -16
- package/cjs/otel/contextManager.js.map +0 -1
- package/cjs/proxy/base.js +0 -126
- package/cjs/proxy/base.js.map +0 -1
- package/cjs/proxy/index.js +0 -209
- package/cjs/proxy/index.js.map +0 -1
- package/cjs/proxy/parse-proxy-response.js +0 -104
- package/cjs/proxy/parse-proxy-response.js.map +0 -1
- package/cjs/sdk/api.js +0 -74
- package/cjs/sdk/api.js.map +0 -1
- package/cjs/sdk/client.js +0 -57
- package/cjs/sdk/client.js.map +0 -1
- package/cjs/sdk/hub.js +0 -104
- package/cjs/sdk/hub.js.map +0 -1
- package/cjs/sdk/init.js +0 -252
- package/cjs/sdk/init.js.map +0 -1
- package/cjs/sdk/initOtel.js +0 -70
- package/cjs/sdk/initOtel.js.map +0 -1
- package/cjs/sdk/scope.js +0 -18
- package/cjs/sdk/scope.js.map +0 -1
- package/cjs/transports/http.js +0 -160
- package/cjs/transports/http.js.map +0 -1
- package/cjs/utils/addOriginToSpan.js +0 -11
- package/cjs/utils/addOriginToSpan.js.map +0 -1
- package/cjs/utils/errorhandling.js +0 -44
- package/cjs/utils/errorhandling.js.map +0 -1
- package/cjs/utils/getRequestUrl.js +0 -18
- package/cjs/utils/getRequestUrl.js.map +0 -1
- package/cjs/utils/module.js +0 -62
- package/cjs/utils/module.js.map +0 -1
- package/esm/cron/common.js +0 -54
- package/esm/cron/common.js.map +0 -1
- package/esm/cron/cron.js +0 -100
- package/esm/cron/cron.js.map +0 -1
- package/esm/cron/index.js +0 -13
- package/esm/cron/index.js.map +0 -1
- package/esm/cron/node-cron.js +0 -56
- package/esm/cron/node-cron.js.map +0 -1
- package/esm/cron/node-schedule.js +0 -55
- package/esm/cron/node-schedule.js.map +0 -1
- package/esm/debug-build.js +0 -9
- package/esm/debug-build.js.map +0 -1
- package/esm/integrations/anr/index.js +0 -138
- package/esm/integrations/anr/index.js.map +0 -1
- package/esm/integrations/anr/worker-script.js.map +0 -1
- package/esm/integrations/console.js +0 -38
- package/esm/integrations/console.js.map +0 -1
- package/esm/integrations/context.js +0 -427
- package/esm/integrations/context.js.map +0 -1
- package/esm/integrations/contextlines.js +0 -131
- package/esm/integrations/contextlines.js.map +0 -1
- package/esm/integrations/http.js +0 -132
- package/esm/integrations/http.js.map +0 -1
- package/esm/integrations/local-variables/common.js +0 -81
- package/esm/integrations/local-variables/common.js.map +0 -1
- package/esm/integrations/local-variables/index.js +0 -6
- package/esm/integrations/local-variables/index.js.map +0 -1
- package/esm/integrations/local-variables/local-variables-sync.js +0 -379
- package/esm/integrations/local-variables/local-variables-sync.js.map +0 -1
- package/esm/integrations/modules.js +0 -97
- package/esm/integrations/modules.js.map +0 -1
- package/esm/integrations/node-fetch.js +0 -80
- package/esm/integrations/node-fetch.js.map +0 -1
- package/esm/integrations/onuncaughtexception.js +0 -134
- package/esm/integrations/onuncaughtexception.js.map +0 -1
- package/esm/integrations/onunhandledrejection.js +0 -87
- package/esm/integrations/onunhandledrejection.js.map +0 -1
- package/esm/integrations/spotlight.js +0 -106
- package/esm/integrations/spotlight.js.map +0 -1
- package/esm/integrations/tracing/express.js +0 -96
- package/esm/integrations/tracing/express.js.map +0 -1
- package/esm/integrations/tracing/fastify.js +0 -31
- package/esm/integrations/tracing/fastify.js.map +0 -1
- package/esm/integrations/tracing/graphql.js +0 -32
- package/esm/integrations/tracing/graphql.js.map +0 -1
- package/esm/integrations/tracing/hapi/index.js +0 -77
- package/esm/integrations/tracing/hapi/index.js.map +0 -1
- package/esm/integrations/tracing/index.js +0 -35
- package/esm/integrations/tracing/index.js.map +0 -1
- package/esm/integrations/tracing/koa.js +0 -19
- package/esm/integrations/tracing/koa.js.map +0 -1
- package/esm/integrations/tracing/mongo.js +0 -31
- package/esm/integrations/tracing/mongo.js.map +0 -1
- package/esm/integrations/tracing/mongoose.js +0 -31
- package/esm/integrations/tracing/mongoose.js.map +0 -1
- package/esm/integrations/tracing/mysql.js +0 -24
- package/esm/integrations/tracing/mysql.js.map +0 -1
- package/esm/integrations/tracing/mysql2.js +0 -31
- package/esm/integrations/tracing/mysql2.js.map +0 -1
- package/esm/integrations/tracing/nest.js +0 -24
- package/esm/integrations/tracing/nest.js.map +0 -1
- package/esm/integrations/tracing/postgres.js +0 -32
- package/esm/integrations/tracing/postgres.js.map +0 -1
- package/esm/integrations/tracing/prisma.js +0 -31
- package/esm/integrations/tracing/prisma.js.map +0 -1
- package/esm/nodeVersion.js +0 -9
- package/esm/nodeVersion.js.map +0 -1
- package/esm/otel/contextManager.js +0 -14
- package/esm/otel/contextManager.js.map +0 -1
- package/esm/proxy/base.js +0 -121
- package/esm/proxy/base.js.map +0 -1
- package/esm/proxy/index.js +0 -203
- package/esm/proxy/index.js.map +0 -1
- package/esm/proxy/parse-proxy-response.js +0 -102
- package/esm/proxy/parse-proxy-response.js.map +0 -1
- package/esm/sdk/api.js +0 -69
- package/esm/sdk/api.js.map +0 -1
- package/esm/sdk/client.js +0 -52
- package/esm/sdk/client.js.map +0 -1
- package/esm/sdk/hub.js +0 -102
- package/esm/sdk/hub.js.map +0 -1
- package/esm/sdk/init.js +0 -249
- package/esm/sdk/init.js.map +0 -1
- package/esm/sdk/initOtel.js +0 -67
- package/esm/sdk/initOtel.js.map +0 -1
- package/esm/sdk/scope.js +0 -16
- package/esm/sdk/scope.js.map +0 -1
- package/esm/transports/http.js +0 -155
- package/esm/transports/http.js.map +0 -1
- package/esm/utils/addOriginToSpan.js +0 -9
- package/esm/utils/addOriginToSpan.js.map +0 -1
- package/esm/utils/errorhandling.js +0 -42
- package/esm/utils/errorhandling.js.map +0 -1
- package/esm/utils/getRequestUrl.js +0 -16
- package/esm/utils/getRequestUrl.js.map +0 -1
- package/esm/utils/module.js +0 -60
- package/esm/utils/module.js.map +0 -1
package/cjs/index.js
CHANGED
|
@@ -1,84 +1,3619 @@
|
|
|
1
|
+
var {
|
|
2
|
+
_optionalChainDelete,
|
|
3
|
+
_nullishCoalesce,
|
|
4
|
+
_optionalChain
|
|
5
|
+
} = require('@sentry/utils');
|
|
6
|
+
|
|
1
7
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
8
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
9
|
+
const api = require('@opentelemetry/api');
|
|
10
|
+
const instrumentation = require('@opentelemetry/instrumentation');
|
|
11
|
+
const instrumentationHttp = require('@opentelemetry/instrumentation-http');
|
|
12
|
+
const core = require('@sentry/core');
|
|
13
|
+
const opentelemetry = require('@sentry/opentelemetry');
|
|
14
|
+
const utils = require('@sentry/utils');
|
|
15
|
+
const util = require('util');
|
|
16
|
+
const child_process = require('child_process');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const inspector = require('inspector');
|
|
21
|
+
const worker_threads = require('worker_threads');
|
|
22
|
+
const instrumentationExpress = require('@opentelemetry/instrumentation-express');
|
|
23
|
+
const instrumentationFastify = require('@opentelemetry/instrumentation-fastify');
|
|
24
|
+
const instrumentationGraphql = require('@opentelemetry/instrumentation-graphql');
|
|
25
|
+
const instrumentationMongodb = require('@opentelemetry/instrumentation-mongodb');
|
|
26
|
+
const instrumentationMongoose = require('@opentelemetry/instrumentation-mongoose');
|
|
27
|
+
const instrumentationMysql = require('@opentelemetry/instrumentation-mysql');
|
|
28
|
+
const instrumentationMysql2 = require('@opentelemetry/instrumentation-mysql2');
|
|
29
|
+
const instrumentationNestjsCore = require('@opentelemetry/instrumentation-nestjs-core');
|
|
30
|
+
const instrumentationPg = require('@opentelemetry/instrumentation-pg');
|
|
31
|
+
const prismaInstrumentation = require('@prisma/instrumentation');
|
|
32
|
+
const instrumentationHapi = require('@opentelemetry/instrumentation-hapi');
|
|
33
|
+
const http = require('http');
|
|
34
|
+
const instrumentationKoa = require('@opentelemetry/instrumentation-koa');
|
|
35
|
+
const http$1 = require('node:http');
|
|
36
|
+
const https = require('node:https');
|
|
37
|
+
const stream = require('stream');
|
|
38
|
+
const zlib = require('zlib');
|
|
39
|
+
const net = require('net');
|
|
40
|
+
const tls = require('tls');
|
|
41
|
+
const resources = require('@opentelemetry/resources');
|
|
42
|
+
const sdkTraceBase = require('@opentelemetry/sdk-trace-base');
|
|
43
|
+
const semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
44
|
+
const contextAsyncHooks = require('@opentelemetry/context-async-hooks');
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Update the active isolation scope.
|
|
48
|
+
* Should be used with caution!
|
|
49
|
+
*/
|
|
50
|
+
function setIsolationScope(isolationScope) {
|
|
51
|
+
const scopes = opentelemetry.getScopesFromContext(api.context.active());
|
|
52
|
+
if (scopes) {
|
|
53
|
+
scopes.isolationScope = isolationScope;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Adds an origin to an OTEL Span. */
|
|
58
|
+
function addOriginToSpan(span, origin) {
|
|
59
|
+
span.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Build a full URL from request options. */
|
|
63
|
+
function getRequestUrl(requestOptions) {
|
|
64
|
+
const protocol = requestOptions.protocol || '';
|
|
65
|
+
const hostname = requestOptions.hostname || requestOptions.host || '';
|
|
66
|
+
// Don't log standard :80 (http) and :443 (https) ports to reduce the noise
|
|
67
|
+
// Also don't add port if the hostname already includes a port
|
|
68
|
+
const port =
|
|
69
|
+
!requestOptions.port || requestOptions.port === 80 || requestOptions.port === 443 || /^(.*):(\d+)$/.test(hostname)
|
|
70
|
+
? ''
|
|
71
|
+
: `:${requestOptions.port}`;
|
|
72
|
+
const path = requestOptions.path ? requestOptions.path : '/';
|
|
73
|
+
return `${protocol}//${hostname}${port}${path}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _optionalChain$8(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
77
|
+
|
|
78
|
+
const _httpIntegration = ((options = {}) => {
|
|
79
|
+
const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
|
|
80
|
+
const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;
|
|
81
|
+
const _ignoreIncomingRequests = options.ignoreIncomingRequests;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
name: 'Http',
|
|
85
|
+
setupOnce() {
|
|
86
|
+
const instrumentations = [
|
|
87
|
+
new instrumentationHttp.HttpInstrumentation({
|
|
88
|
+
ignoreOutgoingRequestHook: request => {
|
|
89
|
+
const url = getRequestUrl(request);
|
|
90
|
+
|
|
91
|
+
if (!url) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (core.isSentryRequestUrl(url, opentelemetry.getClient())) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (_ignoreOutgoingRequests && _ignoreOutgoingRequests(url)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return false;
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
ignoreIncomingRequestHook: request => {
|
|
107
|
+
const url = getRequestUrl(request);
|
|
108
|
+
|
|
109
|
+
const method = _optionalChain$8([request, 'access', _ => _.method, 'optionalAccess', _2 => _2.toUpperCase, 'call', _3 => _3()]);
|
|
110
|
+
// We do not capture OPTIONS/HEAD requests as transactions
|
|
111
|
+
if (method === 'OPTIONS' || method === 'HEAD') {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (_ignoreIncomingRequests && _ignoreIncomingRequests(url)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
requireParentforOutgoingSpans: true,
|
|
123
|
+
requireParentforIncomingSpans: false,
|
|
124
|
+
requestHook: (span, req) => {
|
|
125
|
+
_updateSpan$1(span);
|
|
126
|
+
|
|
127
|
+
// Update the isolation scope, isolate this request
|
|
128
|
+
if (opentelemetry.getSpanKind(span) === api.SpanKind.SERVER) {
|
|
129
|
+
const isolationScope = core.getIsolationScope().clone();
|
|
130
|
+
isolationScope.setSDKProcessingMetadata({ request: req });
|
|
131
|
+
|
|
132
|
+
const client = opentelemetry.getClient();
|
|
133
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
134
|
+
isolationScope.setRequestSession({ status: 'ok' });
|
|
135
|
+
}
|
|
136
|
+
setIsolationScope(isolationScope);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
responseHook: (span, res) => {
|
|
140
|
+
if (_breadcrumbs) {
|
|
141
|
+
_addRequestBreadcrumb$1(span, res);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const client = opentelemetry.getClient();
|
|
145
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
146
|
+
setImmediate(() => {
|
|
147
|
+
client['_captureRequestSession']();
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
instrumentation.registerInstrumentations({
|
|
155
|
+
instrumentations,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}) ;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The http integration instruments Node's internal http and https modules.
|
|
163
|
+
* It creates breadcrumbs and spans for outgoing HTTP requests which will be attached to the currently active span.
|
|
164
|
+
*/
|
|
165
|
+
const httpIntegration = core.defineIntegration(_httpIntegration);
|
|
166
|
+
|
|
167
|
+
/** Update the span with data we need. */
|
|
168
|
+
function _updateSpan$1(span) {
|
|
169
|
+
addOriginToSpan(span, 'auto.http.otel.http');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Add a breadcrumb for outgoing requests. */
|
|
173
|
+
function _addRequestBreadcrumb$1(span, response) {
|
|
174
|
+
if (opentelemetry.getSpanKind(span) !== api.SpanKind.CLIENT) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const data = opentelemetry._INTERNAL.getRequestSpanData(span);
|
|
179
|
+
core.addBreadcrumb(
|
|
180
|
+
{
|
|
181
|
+
category: 'http',
|
|
182
|
+
data: {
|
|
183
|
+
status_code: response.statusCode,
|
|
184
|
+
...data,
|
|
185
|
+
},
|
|
186
|
+
type: 'http',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
event: 'response',
|
|
190
|
+
// TODO FN: Do we need access to `request` here?
|
|
191
|
+
// If we do, we'll have to use the `applyCustomAttributesOnSpan` hook instead,
|
|
192
|
+
// but this has worse context semantics than request/responseHook.
|
|
193
|
+
response,
|
|
194
|
+
},
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const NODE_VERSION = utils.parseSemver(process.versions.node)
|
|
199
|
+
|
|
200
|
+
;
|
|
201
|
+
const NODE_MAJOR = NODE_VERSION.major || 0;
|
|
202
|
+
|
|
203
|
+
const _nativeNodeFetchIntegration = ((options = {}) => {
|
|
204
|
+
const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
|
|
205
|
+
const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;
|
|
206
|
+
|
|
207
|
+
async function getInstrumentation() {
|
|
208
|
+
// Only add NodeFetch if Node >= 16, as previous versions do not support it
|
|
209
|
+
if (NODE_MAJOR < 16) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const pkg = await import('opentelemetry-instrumentation-fetch-node');
|
|
215
|
+
return [
|
|
216
|
+
new pkg.FetchInstrumentation({
|
|
217
|
+
ignoreRequestHook: (request) => {
|
|
218
|
+
const url = request.origin;
|
|
219
|
+
return _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);
|
|
220
|
+
},
|
|
221
|
+
onRequest: ({ span }) => {
|
|
222
|
+
_updateSpan(span);
|
|
223
|
+
|
|
224
|
+
if (_breadcrumbs) {
|
|
225
|
+
_addRequestBreadcrumb(span);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
229
|
+
} ),
|
|
230
|
+
];
|
|
231
|
+
} catch (error) {
|
|
232
|
+
// Could not load instrumentation
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
name: 'NodeFetch',
|
|
238
|
+
setupOnce() {
|
|
239
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
240
|
+
getInstrumentation().then(instrumentations => {
|
|
241
|
+
if (instrumentations) {
|
|
242
|
+
instrumentation.registerInstrumentations({
|
|
243
|
+
instrumentations,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}) ;
|
|
250
|
+
|
|
251
|
+
const nativeNodeFetchIntegration = core.defineIntegration(_nativeNodeFetchIntegration);
|
|
252
|
+
|
|
253
|
+
/** Update the span with data we need. */
|
|
254
|
+
function _updateSpan(span) {
|
|
255
|
+
addOriginToSpan(span, 'auto.http.otel.node_fetch');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Add a breadcrumb for outgoing requests. */
|
|
259
|
+
function _addRequestBreadcrumb(span) {
|
|
260
|
+
if (opentelemetry.getSpanKind(span) !== api.SpanKind.CLIENT) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const data = opentelemetry._INTERNAL.getRequestSpanData(span);
|
|
265
|
+
core.addBreadcrumb({
|
|
266
|
+
category: 'http',
|
|
267
|
+
data: {
|
|
268
|
+
...data,
|
|
269
|
+
},
|
|
270
|
+
type: 'http',
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const INTEGRATION_NAME$8 = 'Console';
|
|
275
|
+
|
|
276
|
+
const _consoleIntegration = (() => {
|
|
277
|
+
return {
|
|
278
|
+
name: INTEGRATION_NAME$8,
|
|
279
|
+
setup(client) {
|
|
280
|
+
utils.addConsoleInstrumentationHandler(({ args, level }) => {
|
|
281
|
+
if (core.getClient() !== client) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
core.addBreadcrumb(
|
|
286
|
+
{
|
|
287
|
+
category: 'console',
|
|
288
|
+
level: utils.severityLevelFromString(level),
|
|
289
|
+
message: util.format.apply(undefined, args),
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
input: [...args],
|
|
293
|
+
level,
|
|
294
|
+
},
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
}) ;
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Capture console logs as breadcrumbs.
|
|
303
|
+
*/
|
|
304
|
+
const consoleIntegration = core.defineIntegration(_consoleIntegration);
|
|
305
|
+
|
|
306
|
+
function _optionalChain$7(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
307
|
+
const readFileAsync$1 = util.promisify(fs.readFile);
|
|
308
|
+
const readDirAsync = util.promisify(fs.readdir);
|
|
309
|
+
|
|
310
|
+
const INTEGRATION_NAME$7 = 'Context';
|
|
311
|
+
|
|
312
|
+
const _nodeContextIntegration = ((options = {}) => {
|
|
313
|
+
let cachedContext;
|
|
314
|
+
|
|
315
|
+
const _options = {
|
|
316
|
+
app: true,
|
|
317
|
+
os: true,
|
|
318
|
+
device: true,
|
|
319
|
+
culture: true,
|
|
320
|
+
cloudResource: true,
|
|
321
|
+
...options,
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/** Add contexts to the event. Caches the context so we only look it up once. */
|
|
325
|
+
async function addContext(event) {
|
|
326
|
+
if (cachedContext === undefined) {
|
|
327
|
+
cachedContext = _getContexts();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const updatedContext = _updateContext(await cachedContext);
|
|
331
|
+
|
|
332
|
+
event.contexts = {
|
|
333
|
+
...event.contexts,
|
|
334
|
+
app: { ...updatedContext.app, ..._optionalChain$7([event, 'access', _ => _.contexts, 'optionalAccess', _2 => _2.app]) },
|
|
335
|
+
os: { ...updatedContext.os, ..._optionalChain$7([event, 'access', _3 => _3.contexts, 'optionalAccess', _4 => _4.os]) },
|
|
336
|
+
device: { ...updatedContext.device, ..._optionalChain$7([event, 'access', _5 => _5.contexts, 'optionalAccess', _6 => _6.device]) },
|
|
337
|
+
culture: { ...updatedContext.culture, ..._optionalChain$7([event, 'access', _7 => _7.contexts, 'optionalAccess', _8 => _8.culture]) },
|
|
338
|
+
cloud_resource: { ...updatedContext.cloud_resource, ..._optionalChain$7([event, 'access', _9 => _9.contexts, 'optionalAccess', _10 => _10.cloud_resource]) },
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
return event;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Get the contexts from node. */
|
|
345
|
+
async function _getContexts() {
|
|
346
|
+
const contexts = {};
|
|
347
|
+
|
|
348
|
+
if (_options.os) {
|
|
349
|
+
contexts.os = await getOsContext();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (_options.app) {
|
|
353
|
+
contexts.app = getAppContext();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (_options.device) {
|
|
357
|
+
contexts.device = getDeviceContext(_options.device);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (_options.culture) {
|
|
361
|
+
const culture = getCultureContext();
|
|
362
|
+
|
|
363
|
+
if (culture) {
|
|
364
|
+
contexts.culture = culture;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (_options.cloudResource) {
|
|
369
|
+
contexts.cloud_resource = getCloudResourceContext();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return contexts;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
name: INTEGRATION_NAME$7,
|
|
377
|
+
processEvent(event) {
|
|
378
|
+
return addContext(event);
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
}) ;
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Capture context about the environment and the device that the client is running on, to events.
|
|
385
|
+
*/
|
|
386
|
+
const nodeContextIntegration = core.defineIntegration(_nodeContextIntegration);
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Updates the context with dynamic values that can change
|
|
390
|
+
*/
|
|
391
|
+
function _updateContext(contexts) {
|
|
392
|
+
// Only update properties if they exist
|
|
393
|
+
if (_optionalChain$7([contexts, 'optionalAccess', _11 => _11.app, 'optionalAccess', _12 => _12.app_memory])) {
|
|
394
|
+
contexts.app.app_memory = process.memoryUsage().rss;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (_optionalChain$7([contexts, 'optionalAccess', _13 => _13.device, 'optionalAccess', _14 => _14.free_memory])) {
|
|
398
|
+
contexts.device.free_memory = os.freemem();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return contexts;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Returns the operating system context.
|
|
406
|
+
*
|
|
407
|
+
* Based on the current platform, this uses a different strategy to provide the
|
|
408
|
+
* most accurate OS information. Since this might involve spawning subprocesses
|
|
409
|
+
* or accessing the file system, this should only be executed lazily and cached.
|
|
410
|
+
*
|
|
411
|
+
* - On macOS (Darwin), this will execute the `sw_vers` utility. The context
|
|
412
|
+
* has a `name`, `version`, `build` and `kernel_version` set.
|
|
413
|
+
* - On Linux, this will try to load a distribution release from `/etc` and set
|
|
414
|
+
* the `name`, `version` and `kernel_version` fields.
|
|
415
|
+
* - On all other platforms, only a `name` and `version` will be returned. Note
|
|
416
|
+
* that `version` might actually be the kernel version.
|
|
417
|
+
*/
|
|
418
|
+
async function getOsContext() {
|
|
419
|
+
const platformId = os.platform();
|
|
420
|
+
switch (platformId) {
|
|
421
|
+
case 'darwin':
|
|
422
|
+
return getDarwinInfo();
|
|
423
|
+
case 'linux':
|
|
424
|
+
return getLinuxInfo();
|
|
425
|
+
default:
|
|
426
|
+
return {
|
|
427
|
+
name: PLATFORM_NAMES[platformId] || platformId,
|
|
428
|
+
version: os.release(),
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function getCultureContext() {
|
|
434
|
+
try {
|
|
435
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
436
|
+
if (typeof (process.versions ).icu !== 'string') {
|
|
437
|
+
// Node was built without ICU support
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Check that node was built with full Intl support. Its possible it was built without support for non-English
|
|
442
|
+
// locales which will make resolvedOptions inaccurate
|
|
443
|
+
//
|
|
444
|
+
// https://nodejs.org/api/intl.html#detecting-internationalization-support
|
|
445
|
+
const january = new Date(9e8);
|
|
446
|
+
const spanish = new Intl.DateTimeFormat('es', { month: 'long' });
|
|
447
|
+
if (spanish.format(january) === 'enero') {
|
|
448
|
+
const options = Intl.DateTimeFormat().resolvedOptions();
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
locale: options.locale,
|
|
452
|
+
timezone: options.timeZone,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
} catch (err) {
|
|
456
|
+
//
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function getAppContext() {
|
|
463
|
+
const app_memory = process.memoryUsage().rss;
|
|
464
|
+
const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString();
|
|
465
|
+
|
|
466
|
+
return { app_start_time, app_memory };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Gets device information from os
|
|
471
|
+
*/
|
|
472
|
+
function getDeviceContext(deviceOpt) {
|
|
473
|
+
const device = {};
|
|
474
|
+
|
|
475
|
+
// Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202
|
|
476
|
+
let uptime;
|
|
477
|
+
try {
|
|
478
|
+
uptime = os.uptime && os.uptime();
|
|
479
|
+
} catch (e) {
|
|
480
|
+
// noop
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions).
|
|
484
|
+
// Hence, we only set boot time, if we get a valid uptime value.
|
|
485
|
+
// @see https://github.com/getsentry/sentry-javascript/issues/5856
|
|
486
|
+
if (typeof uptime === 'number') {
|
|
487
|
+
device.boot_time = new Date(Date.now() - uptime * 1000).toISOString();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
device.arch = os.arch();
|
|
491
|
+
|
|
492
|
+
if (deviceOpt === true || deviceOpt.memory) {
|
|
493
|
+
device.memory_size = os.totalmem();
|
|
494
|
+
device.free_memory = os.freemem();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (deviceOpt === true || deviceOpt.cpu) {
|
|
498
|
+
const cpuInfo = os.cpus();
|
|
499
|
+
if (cpuInfo && cpuInfo.length) {
|
|
500
|
+
const firstCpu = cpuInfo[0];
|
|
501
|
+
|
|
502
|
+
device.processor_count = cpuInfo.length;
|
|
503
|
+
device.cpu_description = firstCpu.model;
|
|
504
|
+
device.processor_frequency = firstCpu.speed;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return device;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Mapping of Node's platform names to actual OS names. */
|
|
512
|
+
const PLATFORM_NAMES = {
|
|
513
|
+
aix: 'IBM AIX',
|
|
514
|
+
freebsd: 'FreeBSD',
|
|
515
|
+
openbsd: 'OpenBSD',
|
|
516
|
+
sunos: 'SunOS',
|
|
517
|
+
win32: 'Windows',
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
/** Linux version file to check for a distribution. */
|
|
521
|
+
|
|
522
|
+
/** Mapping of linux release files located in /etc to distributions. */
|
|
523
|
+
const LINUX_DISTROS = [
|
|
524
|
+
{ name: 'fedora-release', distros: ['Fedora'] },
|
|
525
|
+
{ name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] },
|
|
526
|
+
{ name: 'redhat_version', distros: ['Red Hat Linux'] },
|
|
527
|
+
{ name: 'SuSE-release', distros: ['SUSE Linux'] },
|
|
528
|
+
{ name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] },
|
|
529
|
+
{ name: 'debian_version', distros: ['Debian'] },
|
|
530
|
+
{ name: 'debian_release', distros: ['Debian'] },
|
|
531
|
+
{ name: 'arch-release', distros: ['Arch Linux'] },
|
|
532
|
+
{ name: 'gentoo-release', distros: ['Gentoo Linux'] },
|
|
533
|
+
{ name: 'novell-release', distros: ['SUSE Linux'] },
|
|
534
|
+
{ name: 'alpine-release', distros: ['Alpine Linux'] },
|
|
535
|
+
];
|
|
536
|
+
|
|
537
|
+
/** Functions to extract the OS version from Linux release files. */
|
|
538
|
+
const LINUX_VERSIONS
|
|
539
|
+
|
|
540
|
+
= {
|
|
541
|
+
alpine: content => content,
|
|
542
|
+
arch: content => matchFirst(/distrib_release=(.*)/, content),
|
|
543
|
+
centos: content => matchFirst(/release ([^ ]+)/, content),
|
|
544
|
+
debian: content => content,
|
|
545
|
+
fedora: content => matchFirst(/release (..)/, content),
|
|
546
|
+
mint: content => matchFirst(/distrib_release=(.*)/, content),
|
|
547
|
+
red: content => matchFirst(/release ([^ ]+)/, content),
|
|
548
|
+
suse: content => matchFirst(/VERSION = (.*)\n/, content),
|
|
549
|
+
ubuntu: content => matchFirst(/distrib_release=(.*)/, content),
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Executes a regular expression with one capture group.
|
|
554
|
+
*
|
|
555
|
+
* @param regex A regular expression to execute.
|
|
556
|
+
* @param text Content to execute the RegEx on.
|
|
557
|
+
* @returns The captured string if matched; otherwise undefined.
|
|
558
|
+
*/
|
|
559
|
+
function matchFirst(regex, text) {
|
|
560
|
+
const match = regex.exec(text);
|
|
561
|
+
return match ? match[1] : undefined;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** Loads the macOS operating system context. */
|
|
565
|
+
async function getDarwinInfo() {
|
|
566
|
+
// Default values that will be used in case no operating system information
|
|
567
|
+
// can be loaded. The default version is computed via heuristics from the
|
|
568
|
+
// kernel version, but the build ID is missing.
|
|
569
|
+
const darwinInfo = {
|
|
570
|
+
kernel_version: os.release(),
|
|
571
|
+
name: 'Mac OS X',
|
|
572
|
+
version: `10.${Number(os.release().split('.')[0]) - 4}`,
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
// We try to load the actual macOS version by executing the `sw_vers` tool.
|
|
577
|
+
// This tool should be available on every standard macOS installation. In
|
|
578
|
+
// case this fails, we stick with the values computed above.
|
|
579
|
+
|
|
580
|
+
const output = await new Promise((resolve, reject) => {
|
|
581
|
+
child_process.execFile('/usr/bin/sw_vers', (error, stdout) => {
|
|
582
|
+
if (error) {
|
|
583
|
+
reject(error);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
resolve(stdout);
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
darwinInfo.name = matchFirst(/^ProductName:\s+(.*)$/m, output);
|
|
591
|
+
darwinInfo.version = matchFirst(/^ProductVersion:\s+(.*)$/m, output);
|
|
592
|
+
darwinInfo.build = matchFirst(/^BuildVersion:\s+(.*)$/m, output);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
// ignore
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return darwinInfo;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** Returns a distribution identifier to look up version callbacks. */
|
|
601
|
+
function getLinuxDistroId(name) {
|
|
602
|
+
return name.split(' ')[0].toLowerCase();
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** Loads the Linux operating system context. */
|
|
606
|
+
async function getLinuxInfo() {
|
|
607
|
+
// By default, we cannot assume anything about the distribution or Linux
|
|
608
|
+
// version. `os.release()` returns the kernel version and we assume a generic
|
|
609
|
+
// "Linux" name, which will be replaced down below.
|
|
610
|
+
const linuxInfo = {
|
|
611
|
+
kernel_version: os.release(),
|
|
612
|
+
name: 'Linux',
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
// We start guessing the distribution by listing files in the /etc
|
|
617
|
+
// directory. This is were most Linux distributions (except Knoppix) store
|
|
618
|
+
// release files with certain distribution-dependent meta data. We search
|
|
619
|
+
// for exactly one known file defined in `LINUX_DISTROS` and exit if none
|
|
620
|
+
// are found. In case there are more than one file, we just stick with the
|
|
621
|
+
// first one.
|
|
622
|
+
const etcFiles = await readDirAsync('/etc');
|
|
623
|
+
const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name));
|
|
624
|
+
if (!distroFile) {
|
|
625
|
+
return linuxInfo;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Once that file is known, load its contents. To make searching in those
|
|
629
|
+
// files easier, we lowercase the file contents. Since these files are
|
|
630
|
+
// usually quite small, this should not allocate too much memory and we only
|
|
631
|
+
// hold on to it for a very short amount of time.
|
|
632
|
+
const distroPath = path.join('/etc', distroFile.name);
|
|
633
|
+
const contents = ((await readFileAsync$1(distroPath, { encoding: 'utf-8' })) ).toLowerCase();
|
|
634
|
+
|
|
635
|
+
// Some Linux distributions store their release information in the same file
|
|
636
|
+
// (e.g. RHEL and Centos). In those cases, we scan the file for an
|
|
637
|
+
// identifier, that basically consists of the first word of the linux
|
|
638
|
+
// distribution name (e.g. "red" for Red Hat). In case there is no match, we
|
|
639
|
+
// just assume the first distribution in our list.
|
|
640
|
+
const { distros } = distroFile;
|
|
641
|
+
linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];
|
|
642
|
+
|
|
643
|
+
// Based on the found distribution, we can now compute the actual version
|
|
644
|
+
// number. This is different for every distribution, so several strategies
|
|
645
|
+
// are computed in `LINUX_VERSIONS`.
|
|
646
|
+
const id = getLinuxDistroId(linuxInfo.name);
|
|
647
|
+
linuxInfo.version = LINUX_VERSIONS[id](contents);
|
|
648
|
+
} catch (e) {
|
|
649
|
+
// ignore
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return linuxInfo;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Grabs some information about hosting provider based on best effort.
|
|
657
|
+
*/
|
|
658
|
+
function getCloudResourceContext() {
|
|
659
|
+
if (process.env.VERCEL) {
|
|
660
|
+
// https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables
|
|
661
|
+
return {
|
|
662
|
+
'cloud.provider': 'vercel',
|
|
663
|
+
'cloud.region': process.env.VERCEL_REGION,
|
|
664
|
+
};
|
|
665
|
+
} else if (process.env.AWS_REGION) {
|
|
666
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
|
|
667
|
+
return {
|
|
668
|
+
'cloud.provider': 'aws',
|
|
669
|
+
'cloud.region': process.env.AWS_REGION,
|
|
670
|
+
'cloud.platform': process.env.AWS_EXECUTION_ENV,
|
|
671
|
+
};
|
|
672
|
+
} else if (process.env.GCP_PROJECT) {
|
|
673
|
+
// https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables
|
|
674
|
+
return {
|
|
675
|
+
'cloud.provider': 'gcp',
|
|
676
|
+
};
|
|
677
|
+
} else if (process.env.ALIYUN_REGION_ID) {
|
|
678
|
+
// TODO: find where I found these environment variables - at least gc.github.com returns something
|
|
679
|
+
return {
|
|
680
|
+
'cloud.provider': 'alibaba_cloud',
|
|
681
|
+
'cloud.region': process.env.ALIYUN_REGION_ID,
|
|
682
|
+
};
|
|
683
|
+
} else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {
|
|
684
|
+
// https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment
|
|
685
|
+
return {
|
|
686
|
+
'cloud.provider': 'azure',
|
|
687
|
+
'cloud.region': process.env.REGION_NAME,
|
|
688
|
+
};
|
|
689
|
+
} else if (process.env.IBM_CLOUD_REGION) {
|
|
690
|
+
// TODO: find where I found these environment variables - at least gc.github.com returns something
|
|
691
|
+
return {
|
|
692
|
+
'cloud.provider': 'ibm_cloud',
|
|
693
|
+
'cloud.region': process.env.IBM_CLOUD_REGION,
|
|
694
|
+
};
|
|
695
|
+
} else if (process.env.TENCENTCLOUD_REGION) {
|
|
696
|
+
// https://www.tencentcloud.com/document/product/583/32748
|
|
697
|
+
return {
|
|
698
|
+
'cloud.provider': 'tencent_cloud',
|
|
699
|
+
'cloud.region': process.env.TENCENTCLOUD_REGION,
|
|
700
|
+
'cloud.account.id': process.env.TENCENTCLOUD_APPID,
|
|
701
|
+
'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE,
|
|
702
|
+
};
|
|
703
|
+
} else if (process.env.NETLIFY) {
|
|
704
|
+
// https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables
|
|
705
|
+
return {
|
|
706
|
+
'cloud.provider': 'netlify',
|
|
707
|
+
};
|
|
708
|
+
} else if (process.env.FLY_REGION) {
|
|
709
|
+
// https://fly.io/docs/reference/runtime-environment/
|
|
710
|
+
return {
|
|
711
|
+
'cloud.provider': 'fly.io',
|
|
712
|
+
'cloud.region': process.env.FLY_REGION,
|
|
713
|
+
};
|
|
714
|
+
} else if (process.env.DYNO) {
|
|
715
|
+
// https://devcenter.heroku.com/articles/dynos#local-environment-variables
|
|
716
|
+
return {
|
|
717
|
+
'cloud.provider': 'heroku',
|
|
718
|
+
};
|
|
719
|
+
} else {
|
|
720
|
+
return undefined;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function _optionalChain$6(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
725
|
+
const FILE_CONTENT_CACHE = new utils.LRUMap(100);
|
|
726
|
+
const DEFAULT_LINES_OF_CONTEXT = 7;
|
|
727
|
+
const INTEGRATION_NAME$6 = 'ContextLines';
|
|
728
|
+
|
|
729
|
+
const readFileAsync = fs.promises.readFile;
|
|
730
|
+
|
|
731
|
+
/** Exported only for tests, as a type-safe variant. */
|
|
732
|
+
const _contextLinesIntegration = ((options = {}) => {
|
|
733
|
+
const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT;
|
|
734
|
+
|
|
735
|
+
return {
|
|
736
|
+
name: INTEGRATION_NAME$6,
|
|
737
|
+
processEvent(event) {
|
|
738
|
+
return addSourceContext(event, contextLines);
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
}) ;
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Capture the lines before and after the frame's context.
|
|
745
|
+
*/
|
|
746
|
+
const contextLinesIntegration = core.defineIntegration(_contextLinesIntegration);
|
|
747
|
+
|
|
748
|
+
async function addSourceContext(event, contextLines) {
|
|
749
|
+
// keep a lookup map of which files we've already enqueued to read,
|
|
750
|
+
// so we don't enqueue the same file multiple times which would cause multiple i/o reads
|
|
751
|
+
const enqueuedReadSourceFileTasks = {};
|
|
752
|
+
const readSourceFileTasks = [];
|
|
753
|
+
|
|
754
|
+
if (contextLines > 0 && _optionalChain$6([event, 'access', _2 => _2.exception, 'optionalAccess', _3 => _3.values])) {
|
|
755
|
+
for (const exception of event.exception.values) {
|
|
756
|
+
if (!_optionalChain$6([exception, 'access', _4 => _4.stacktrace, 'optionalAccess', _5 => _5.frames])) {
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// We want to iterate in reverse order as calling cache.get will bump the file in our LRU cache.
|
|
761
|
+
// This ends up prioritizes source context for frames at the top of the stack instead of the bottom.
|
|
762
|
+
for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {
|
|
763
|
+
const frame = exception.stacktrace.frames[i];
|
|
764
|
+
// Call cache.get to bump the file to the top of the cache and ensure we have not already
|
|
765
|
+
// enqueued a read operation for this filename
|
|
766
|
+
if (frame.filename && !enqueuedReadSourceFileTasks[frame.filename] && !FILE_CONTENT_CACHE.get(frame.filename)) {
|
|
767
|
+
readSourceFileTasks.push(_readSourceFile(frame.filename));
|
|
768
|
+
enqueuedReadSourceFileTasks[frame.filename] = 1;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// check if files to read > 0, if so, await all of them to be read before adding source contexts.
|
|
775
|
+
// Normally, Promise.all here could be short circuited if one of the promises rejects, but we
|
|
776
|
+
// are guarding from that by wrapping the i/o read operation in a try/catch.
|
|
777
|
+
if (readSourceFileTasks.length > 0) {
|
|
778
|
+
await Promise.all(readSourceFileTasks);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Perform the same loop as above, but this time we can assume all files are in the cache
|
|
782
|
+
// and attempt to add source context to frames.
|
|
783
|
+
if (contextLines > 0 && _optionalChain$6([event, 'access', _6 => _6.exception, 'optionalAccess', _7 => _7.values])) {
|
|
784
|
+
for (const exception of event.exception.values) {
|
|
785
|
+
if (exception.stacktrace && exception.stacktrace.frames) {
|
|
786
|
+
await addSourceContextToFrames(exception.stacktrace.frames, contextLines);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return event;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/** Adds context lines to frames */
|
|
795
|
+
function addSourceContextToFrames(frames, contextLines) {
|
|
796
|
+
for (const frame of frames) {
|
|
797
|
+
// Only add context if we have a filename and it hasn't already been added
|
|
798
|
+
if (frame.filename && frame.context_line === undefined) {
|
|
799
|
+
const sourceFileLines = FILE_CONTENT_CACHE.get(frame.filename);
|
|
800
|
+
|
|
801
|
+
if (sourceFileLines) {
|
|
802
|
+
try {
|
|
803
|
+
utils.addContextToFrame(sourceFileLines, frame, contextLines);
|
|
804
|
+
} catch (e) {
|
|
805
|
+
// anomaly, being defensive in case
|
|
806
|
+
// unlikely to ever happen in practice but can definitely happen in theory
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Reads file contents and caches them in a global LRU cache.
|
|
815
|
+
* If reading fails, mark the file as null in the cache so we don't try again.
|
|
816
|
+
*
|
|
817
|
+
* @param filename filepath to read content from.
|
|
818
|
+
*/
|
|
819
|
+
async function _readSourceFile(filename) {
|
|
820
|
+
const cachedFile = FILE_CONTENT_CACHE.get(filename);
|
|
821
|
+
|
|
822
|
+
// We have already attempted to read this file and failed, do not try again
|
|
823
|
+
if (cachedFile === null) {
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// We have a cache hit, return it
|
|
828
|
+
if (cachedFile !== undefined) {
|
|
829
|
+
return cachedFile;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Guard from throwing if readFile fails, this enables us to use Promise.all and
|
|
833
|
+
// not have it short circuiting if one of the promises rejects + since context lines are added
|
|
834
|
+
// on a best effort basis, we want to throw here anyways.
|
|
835
|
+
|
|
836
|
+
// If we made it to here, it means that our file is not cache nor marked as failed, so attempt to read it
|
|
837
|
+
let content = null;
|
|
838
|
+
try {
|
|
839
|
+
const rawFileContents = await readFileAsync(filename, 'utf-8');
|
|
840
|
+
content = rawFileContents.split('\n');
|
|
841
|
+
} catch (_) {
|
|
842
|
+
// if we fail, we will mark the file as null in the cache and short circuit next time we try to read it
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
FILE_CONTENT_CACHE.set(filename, content);
|
|
846
|
+
return content;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback
|
|
851
|
+
* when a timeout has occurred.
|
|
852
|
+
* @param maxPerSecond Maximum number of calls per second
|
|
853
|
+
* @param enable Callback to enable capture
|
|
854
|
+
* @param disable Callback to disable capture
|
|
855
|
+
* @returns A function to call to increment the rate limiter count
|
|
856
|
+
*/
|
|
857
|
+
function createRateLimiter(
|
|
858
|
+
maxPerSecond,
|
|
859
|
+
enable,
|
|
860
|
+
disable,
|
|
861
|
+
) {
|
|
862
|
+
let count = 0;
|
|
863
|
+
let retrySeconds = 5;
|
|
864
|
+
let disabledTimeout = 0;
|
|
865
|
+
|
|
866
|
+
setInterval(() => {
|
|
867
|
+
if (disabledTimeout === 0) {
|
|
868
|
+
if (count > maxPerSecond) {
|
|
869
|
+
retrySeconds *= 2;
|
|
870
|
+
disable(retrySeconds);
|
|
871
|
+
|
|
872
|
+
// Cap at one day
|
|
873
|
+
if (retrySeconds > 86400) {
|
|
874
|
+
retrySeconds = 86400;
|
|
875
|
+
}
|
|
876
|
+
disabledTimeout = retrySeconds;
|
|
877
|
+
}
|
|
878
|
+
} else {
|
|
879
|
+
disabledTimeout -= 1;
|
|
880
|
+
|
|
881
|
+
if (disabledTimeout === 0) {
|
|
882
|
+
enable();
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
count = 0;
|
|
887
|
+
}, 1000).unref();
|
|
888
|
+
|
|
889
|
+
return () => {
|
|
890
|
+
count += 1;
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Add types for the exception event data
|
|
895
|
+
|
|
896
|
+
/** Could this be an anonymous function? */
|
|
897
|
+
function isAnonymous(name) {
|
|
898
|
+
return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>');
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/** Do the function names appear to match? */
|
|
902
|
+
function functionNamesMatch(a, b) {
|
|
903
|
+
return a === b || (isAnonymous(a) && isAnonymous(b));
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/** Creates a unique hash from stack frames */
|
|
907
|
+
function hashFrames(frames) {
|
|
908
|
+
if (frames === undefined) {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Only hash the 10 most recent frames (ie. the last 10)
|
|
913
|
+
return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, '');
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* We use the stack parser to create a unique hash from the exception stack trace
|
|
918
|
+
* This is used to lookup vars when the exception passes through the event processor
|
|
919
|
+
*/
|
|
920
|
+
function hashFromStack(stackParser, stack) {
|
|
921
|
+
if (stack === undefined) {
|
|
922
|
+
return undefined;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return hashFrames(stackParser(stack, 1));
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function _optionalChain$5(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
929
|
+
/** Creates a container for callbacks to be called sequentially */
|
|
930
|
+
function createCallbackList(complete) {
|
|
931
|
+
// A collection of callbacks to be executed last to first
|
|
932
|
+
let callbacks = [];
|
|
933
|
+
|
|
934
|
+
let completedCalled = false;
|
|
935
|
+
function checkedComplete(result) {
|
|
936
|
+
callbacks = [];
|
|
937
|
+
if (completedCalled) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
completedCalled = true;
|
|
941
|
+
complete(result);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// complete should be called last
|
|
945
|
+
callbacks.push(checkedComplete);
|
|
946
|
+
|
|
947
|
+
function add(fn) {
|
|
948
|
+
callbacks.push(fn);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function next(result) {
|
|
952
|
+
const popped = callbacks.pop() || checkedComplete;
|
|
953
|
+
|
|
954
|
+
try {
|
|
955
|
+
popped(result);
|
|
956
|
+
} catch (_) {
|
|
957
|
+
// If there is an error, we still want to call the complete callback
|
|
958
|
+
checkedComplete(result);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
return { add, next };
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Promise API is available as `Experimental` and in Node 19 only.
|
|
967
|
+
*
|
|
968
|
+
* Callback-based API is `Stable` since v14 and `Experimental` since v8.
|
|
969
|
+
* Because of that, we are creating our own `AsyncSession` class.
|
|
970
|
+
*
|
|
971
|
+
* https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api
|
|
972
|
+
* https://nodejs.org/docs/latest-v14.x/api/inspector.html
|
|
973
|
+
*/
|
|
974
|
+
class AsyncSession {
|
|
975
|
+
|
|
976
|
+
/** Throws if inspector API is not available */
|
|
977
|
+
constructor() {
|
|
978
|
+
this._session = new inspector.Session();
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/** @inheritdoc */
|
|
982
|
+
configureAndConnect(onPause, captureAll) {
|
|
983
|
+
this._session.connect();
|
|
984
|
+
|
|
985
|
+
this._session.on('Debugger.paused', event => {
|
|
986
|
+
onPause(event, () => {
|
|
987
|
+
// After the pause work is complete, resume execution or the exception context memory is leaked
|
|
988
|
+
this._session.post('Debugger.resume');
|
|
989
|
+
});
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
this._session.post('Debugger.enable');
|
|
993
|
+
this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
setPauseOnExceptions(captureAll) {
|
|
997
|
+
this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/** @inheritdoc */
|
|
1001
|
+
getLocalVariables(objectId, complete) {
|
|
1002
|
+
this._getProperties(objectId, props => {
|
|
1003
|
+
const { add, next } = createCallbackList(complete);
|
|
1004
|
+
|
|
1005
|
+
for (const prop of props) {
|
|
1006
|
+
if (_optionalChain$5([prop, 'optionalAccess', _2 => _2.value, 'optionalAccess', _3 => _3.objectId]) && _optionalChain$5([prop, 'optionalAccess', _4 => _4.value, 'access', _5 => _5.className]) === 'Array') {
|
|
1007
|
+
const id = prop.value.objectId;
|
|
1008
|
+
add(vars => this._unrollArray(id, prop.name, vars, next));
|
|
1009
|
+
} else if (_optionalChain$5([prop, 'optionalAccess', _6 => _6.value, 'optionalAccess', _7 => _7.objectId]) && _optionalChain$5([prop, 'optionalAccess', _8 => _8.value, 'optionalAccess', _9 => _9.className]) === 'Object') {
|
|
1010
|
+
const id = prop.value.objectId;
|
|
1011
|
+
add(vars => this._unrollObject(id, prop.name, vars, next));
|
|
1012
|
+
} else if (_optionalChain$5([prop, 'optionalAccess', _10 => _10.value])) {
|
|
1013
|
+
add(vars => this._unrollOther(prop, vars, next));
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
next({});
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Gets all the PropertyDescriptors of an object
|
|
1023
|
+
*/
|
|
1024
|
+
_getProperties(objectId, next) {
|
|
1025
|
+
this._session.post(
|
|
1026
|
+
'Runtime.getProperties',
|
|
1027
|
+
{
|
|
1028
|
+
objectId,
|
|
1029
|
+
ownProperties: true,
|
|
1030
|
+
},
|
|
1031
|
+
(err, params) => {
|
|
1032
|
+
if (err) {
|
|
1033
|
+
next([]);
|
|
1034
|
+
} else {
|
|
1035
|
+
next(params.result);
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Unrolls an array property
|
|
1043
|
+
*/
|
|
1044
|
+
_unrollArray(objectId, name, vars, next) {
|
|
1045
|
+
this._getProperties(objectId, props => {
|
|
1046
|
+
vars[name] = props
|
|
1047
|
+
.filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10)))
|
|
1048
|
+
.sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10))
|
|
1049
|
+
.map(v => _optionalChain$5([v, 'optionalAccess', _11 => _11.value, 'optionalAccess', _12 => _12.value]));
|
|
1050
|
+
|
|
1051
|
+
next(vars);
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Unrolls an object property
|
|
1057
|
+
*/
|
|
1058
|
+
_unrollObject(objectId, name, vars, next) {
|
|
1059
|
+
this._getProperties(objectId, props => {
|
|
1060
|
+
vars[name] = props
|
|
1061
|
+
.map(v => [v.name, _optionalChain$5([v, 'optionalAccess', _13 => _13.value, 'optionalAccess', _14 => _14.value])])
|
|
1062
|
+
.reduce((obj, [key, val]) => {
|
|
1063
|
+
obj[key] = val;
|
|
1064
|
+
return obj;
|
|
1065
|
+
}, {} );
|
|
1066
|
+
|
|
1067
|
+
next(vars);
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Unrolls other properties
|
|
1073
|
+
*/
|
|
1074
|
+
_unrollOther(prop, vars, next) {
|
|
1075
|
+
if (prop.value) {
|
|
1076
|
+
if ('value' in prop.value) {
|
|
1077
|
+
if (prop.value.value === undefined || prop.value.value === null) {
|
|
1078
|
+
vars[prop.name] = `<${prop.value.value}>`;
|
|
1079
|
+
} else {
|
|
1080
|
+
vars[prop.name] = prop.value.value;
|
|
1081
|
+
}
|
|
1082
|
+
} else if ('description' in prop.value && prop.value.type !== 'function') {
|
|
1083
|
+
vars[prop.name] = `<${prop.value.description}>`;
|
|
1084
|
+
} else if (prop.value.type === 'undefined') {
|
|
1085
|
+
vars[prop.name] = '<undefined>';
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
next(vars);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* When using Vercel pkg, the inspector module is not available.
|
|
1095
|
+
* https://github.com/getsentry/sentry-javascript/issues/6769
|
|
1096
|
+
*/
|
|
1097
|
+
function tryNewAsyncSession() {
|
|
1098
|
+
try {
|
|
1099
|
+
return new AsyncSession();
|
|
1100
|
+
} catch (e) {
|
|
1101
|
+
return undefined;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const INTEGRATION_NAME$5 = 'LocalVariables';
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Adds local variables to exception frames
|
|
1109
|
+
*/
|
|
1110
|
+
const _localVariablesSyncIntegration = ((
|
|
1111
|
+
options = {},
|
|
1112
|
+
session = tryNewAsyncSession(),
|
|
1113
|
+
) => {
|
|
1114
|
+
const cachedFrames = new utils.LRUMap(20);
|
|
1115
|
+
let rateLimiter;
|
|
1116
|
+
let shouldProcessEvent = false;
|
|
1117
|
+
|
|
1118
|
+
function handlePaused(
|
|
1119
|
+
stackParser,
|
|
1120
|
+
{ params: { reason, data, callFrames } },
|
|
1121
|
+
complete,
|
|
1122
|
+
) {
|
|
1123
|
+
if (reason !== 'exception' && reason !== 'promiseRejection') {
|
|
1124
|
+
complete();
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
_optionalChain$5([rateLimiter, 'optionalCall', _15 => _15()]);
|
|
1129
|
+
|
|
1130
|
+
// data.description contains the original error.stack
|
|
1131
|
+
const exceptionHash = hashFromStack(stackParser, _optionalChain$5([data, 'optionalAccess', _16 => _16.description]));
|
|
1132
|
+
|
|
1133
|
+
if (exceptionHash == undefined) {
|
|
1134
|
+
complete();
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const { add, next } = createCallbackList(frames => {
|
|
1139
|
+
cachedFrames.set(exceptionHash, frames);
|
|
1140
|
+
complete();
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
// Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack
|
|
1144
|
+
// For this reason we only attempt to get local variables for the first 5 frames
|
|
1145
|
+
for (let i = 0; i < Math.min(callFrames.length, 5); i++) {
|
|
1146
|
+
const { scopeChain, functionName, this: obj } = callFrames[i];
|
|
1147
|
+
|
|
1148
|
+
const localScope = scopeChain.find(scope => scope.type === 'local');
|
|
1149
|
+
|
|
1150
|
+
// obj.className is undefined in ESM modules
|
|
1151
|
+
const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;
|
|
1152
|
+
|
|
1153
|
+
if (_optionalChain$5([localScope, 'optionalAccess', _17 => _17.object, 'access', _18 => _18.objectId]) === undefined) {
|
|
1154
|
+
add(frames => {
|
|
1155
|
+
frames[i] = { function: fn };
|
|
1156
|
+
next(frames);
|
|
1157
|
+
});
|
|
1158
|
+
} else {
|
|
1159
|
+
const id = localScope.object.objectId;
|
|
1160
|
+
add(frames =>
|
|
1161
|
+
_optionalChain$5([session, 'optionalAccess', _19 => _19.getLocalVariables, 'call', _20 => _20(id, vars => {
|
|
1162
|
+
frames[i] = { function: fn, vars };
|
|
1163
|
+
next(frames);
|
|
1164
|
+
})]),
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
next([]);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
function addLocalVariablesToException(exception) {
|
|
1173
|
+
const hash = hashFrames(_optionalChain$5([exception, 'optionalAccess', _21 => _21.stacktrace, 'optionalAccess', _22 => _22.frames]));
|
|
1174
|
+
|
|
1175
|
+
if (hash === undefined) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// Check if we have local variables for an exception that matches the hash
|
|
1180
|
+
// remove is identical to get but also removes the entry from the cache
|
|
1181
|
+
const cachedFrame = cachedFrames.remove(hash);
|
|
1182
|
+
|
|
1183
|
+
if (cachedFrame === undefined) {
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const frameCount = _optionalChain$5([exception, 'access', _23 => _23.stacktrace, 'optionalAccess', _24 => _24.frames, 'optionalAccess', _25 => _25.length]) || 0;
|
|
1188
|
+
|
|
1189
|
+
for (let i = 0; i < frameCount; i++) {
|
|
1190
|
+
// Sentry frames are in reverse order
|
|
1191
|
+
const frameIndex = frameCount - i - 1;
|
|
1192
|
+
|
|
1193
|
+
// Drop out if we run out of frames to match up
|
|
1194
|
+
if (!_optionalChain$5([exception, 'optionalAccess', _26 => _26.stacktrace, 'optionalAccess', _27 => _27.frames, 'optionalAccess', _28 => _28[frameIndex]]) || !cachedFrame[i]) {
|
|
1195
|
+
break;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
if (
|
|
1199
|
+
// We need to have vars to add
|
|
1200
|
+
cachedFrame[i].vars === undefined ||
|
|
1201
|
+
// We're not interested in frames that are not in_app because the vars are not relevant
|
|
1202
|
+
exception.stacktrace.frames[frameIndex].in_app === false ||
|
|
1203
|
+
// The function names need to match
|
|
1204
|
+
!functionNamesMatch(exception.stacktrace.frames[frameIndex].function, cachedFrame[i].function)
|
|
1205
|
+
) {
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
exception.stacktrace.frames[frameIndex].vars = cachedFrame[i].vars;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function addLocalVariablesToEvent(event) {
|
|
1214
|
+
for (const exception of _optionalChain$5([event, 'optionalAccess', _29 => _29.exception, 'optionalAccess', _30 => _30.values]) || []) {
|
|
1215
|
+
addLocalVariablesToException(exception);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
return event;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
return {
|
|
1222
|
+
name: INTEGRATION_NAME$5,
|
|
1223
|
+
setupOnce() {
|
|
1224
|
+
const client = core.getClient();
|
|
1225
|
+
const clientOptions = _optionalChain$5([client, 'optionalAccess', _31 => _31.getOptions, 'call', _32 => _32()]);
|
|
1226
|
+
|
|
1227
|
+
if (session && _optionalChain$5([clientOptions, 'optionalAccess', _33 => _33.includeLocalVariables])) {
|
|
1228
|
+
// Only setup this integration if the Node version is >= v18
|
|
1229
|
+
// https://github.com/getsentry/sentry-javascript/issues/7697
|
|
1230
|
+
const unsupportedNodeVersion = NODE_MAJOR < 18;
|
|
1231
|
+
|
|
1232
|
+
if (unsupportedNodeVersion) {
|
|
1233
|
+
utils.logger.log('The `LocalVariables` integration is only supported on Node >= v18.');
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const captureAll = options.captureAllExceptions !== false;
|
|
1238
|
+
|
|
1239
|
+
session.configureAndConnect(
|
|
1240
|
+
(ev, complete) =>
|
|
1241
|
+
handlePaused(clientOptions.stackParser, ev , complete),
|
|
1242
|
+
captureAll,
|
|
1243
|
+
);
|
|
1244
|
+
|
|
1245
|
+
if (captureAll) {
|
|
1246
|
+
const max = options.maxExceptionsPerSecond || 50;
|
|
1247
|
+
|
|
1248
|
+
rateLimiter = createRateLimiter(
|
|
1249
|
+
max,
|
|
1250
|
+
() => {
|
|
1251
|
+
utils.logger.log('Local variables rate-limit lifted.');
|
|
1252
|
+
_optionalChain$5([session, 'optionalAccess', _34 => _34.setPauseOnExceptions, 'call', _35 => _35(true)]);
|
|
1253
|
+
},
|
|
1254
|
+
seconds => {
|
|
1255
|
+
utils.logger.log(
|
|
1256
|
+
`Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`,
|
|
1257
|
+
);
|
|
1258
|
+
_optionalChain$5([session, 'optionalAccess', _36 => _36.setPauseOnExceptions, 'call', _37 => _37(false)]);
|
|
1259
|
+
},
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
shouldProcessEvent = true;
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
processEvent(event) {
|
|
1267
|
+
if (shouldProcessEvent) {
|
|
1268
|
+
return addLocalVariablesToEvent(event);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
return event;
|
|
1272
|
+
},
|
|
1273
|
+
// These are entirely for testing
|
|
1274
|
+
_getCachedFramesCount() {
|
|
1275
|
+
return cachedFrames.size;
|
|
1276
|
+
},
|
|
1277
|
+
_getFirstCachedFrame() {
|
|
1278
|
+
return cachedFrames.values()[0];
|
|
1279
|
+
},
|
|
1280
|
+
};
|
|
1281
|
+
}) ;
|
|
1282
|
+
|
|
1283
|
+
const localVariablesSyncIntegration = core.defineIntegration(_localVariablesSyncIntegration);
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Adds local variables to exception frames.
|
|
1287
|
+
* @deprecated Use `localVariablesSyncIntegration()` instead.
|
|
1288
|
+
*/
|
|
1289
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
1290
|
+
core.convertIntegrationFnToClass(
|
|
1291
|
+
INTEGRATION_NAME$5,
|
|
1292
|
+
localVariablesSyncIntegration,
|
|
1293
|
+
)
|
|
1294
|
+
|
|
1295
|
+
;
|
|
1296
|
+
|
|
1297
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
1298
|
+
|
|
1299
|
+
const localVariablesIntegration = localVariablesSyncIntegration;
|
|
1300
|
+
|
|
1301
|
+
let moduleCache;
|
|
1302
|
+
|
|
1303
|
+
const INTEGRATION_NAME$4 = 'Modules';
|
|
1304
|
+
|
|
1305
|
+
const _modulesIntegration = (() => {
|
|
1306
|
+
return {
|
|
1307
|
+
name: INTEGRATION_NAME$4,
|
|
1308
|
+
processEvent(event) {
|
|
1309
|
+
event.modules = {
|
|
1310
|
+
...event.modules,
|
|
1311
|
+
..._getModules(),
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1314
|
+
return event;
|
|
1315
|
+
},
|
|
1316
|
+
};
|
|
1317
|
+
}) ;
|
|
1318
|
+
|
|
1319
|
+
/**
|
|
1320
|
+
* Add node modules / packages to the event.
|
|
1321
|
+
*/
|
|
1322
|
+
const modulesIntegration = core.defineIntegration(_modulesIntegration);
|
|
1323
|
+
|
|
1324
|
+
/** Extract information about paths */
|
|
1325
|
+
function getPaths() {
|
|
1326
|
+
try {
|
|
1327
|
+
return require.cache ? Object.keys(require.cache ) : [];
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
return [];
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/** Extract information about package.json modules */
|
|
1334
|
+
function collectModules()
|
|
1335
|
+
|
|
1336
|
+
{
|
|
1337
|
+
const mainPaths = (require.main && require.main.paths) || [];
|
|
1338
|
+
const paths = getPaths();
|
|
1339
|
+
const infos
|
|
1340
|
+
|
|
1341
|
+
= {};
|
|
1342
|
+
const seen
|
|
1343
|
+
|
|
1344
|
+
= {};
|
|
1345
|
+
|
|
1346
|
+
paths.forEach(path$1 => {
|
|
1347
|
+
let dir = path$1;
|
|
1348
|
+
|
|
1349
|
+
/** Traverse directories upward in the search of package.json file */
|
|
1350
|
+
const updir = () => {
|
|
1351
|
+
const orig = dir;
|
|
1352
|
+
dir = path.dirname(orig);
|
|
1353
|
+
|
|
1354
|
+
if (!dir || orig === dir || seen[orig]) {
|
|
1355
|
+
return undefined;
|
|
1356
|
+
}
|
|
1357
|
+
if (mainPaths.indexOf(dir) < 0) {
|
|
1358
|
+
return updir();
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const pkgfile = path.join(orig, 'package.json');
|
|
1362
|
+
seen[orig] = true;
|
|
1363
|
+
|
|
1364
|
+
if (!fs.existsSync(pkgfile)) {
|
|
1365
|
+
return updir();
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
try {
|
|
1369
|
+
const info = JSON.parse(fs.readFileSync(pkgfile, 'utf8'))
|
|
1370
|
+
|
|
1371
|
+
;
|
|
1372
|
+
infos[info.name] = info.version;
|
|
1373
|
+
} catch (_oO) {
|
|
1374
|
+
// no-empty
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
|
|
1378
|
+
updir();
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
return infos;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */
|
|
1385
|
+
function _getModules() {
|
|
1386
|
+
if (!moduleCache) {
|
|
1387
|
+
moduleCache = collectModules();
|
|
1388
|
+
}
|
|
1389
|
+
return moduleCache;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
* This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
|
|
1394
|
+
*
|
|
1395
|
+
* ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
|
|
1396
|
+
*/
|
|
1397
|
+
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
|
|
1398
|
+
|
|
1399
|
+
const DEFAULT_SHUTDOWN_TIMEOUT = 2000;
|
|
1400
|
+
|
|
1401
|
+
/**
|
|
1402
|
+
* @hidden
|
|
1403
|
+
*/
|
|
1404
|
+
function logAndExitProcess(error) {
|
|
1405
|
+
utils.consoleSandbox(() => {
|
|
1406
|
+
// eslint-disable-next-line no-console
|
|
1407
|
+
console.error(error);
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1410
|
+
const client = core.getClient();
|
|
1411
|
+
|
|
1412
|
+
if (client === undefined) {
|
|
1413
|
+
DEBUG_BUILD && utils.logger.warn('No NodeClient was defined, we are exiting the process now.');
|
|
1414
|
+
global.process.exit(1);
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
const options = client.getOptions();
|
|
1419
|
+
const timeout =
|
|
1420
|
+
(options && options.shutdownTimeout && options.shutdownTimeout > 0 && options.shutdownTimeout) ||
|
|
1421
|
+
DEFAULT_SHUTDOWN_TIMEOUT;
|
|
1422
|
+
client.close(timeout).then(
|
|
1423
|
+
(result) => {
|
|
1424
|
+
if (!result) {
|
|
1425
|
+
DEBUG_BUILD && utils.logger.warn('We reached the timeout for emptying the request buffer, still exiting now!');
|
|
1426
|
+
}
|
|
1427
|
+
global.process.exit(1);
|
|
1428
|
+
},
|
|
1429
|
+
error => {
|
|
1430
|
+
DEBUG_BUILD && utils.logger.error(error);
|
|
1431
|
+
},
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
const INTEGRATION_NAME$3 = 'OnUncaughtException';
|
|
1436
|
+
|
|
1437
|
+
const _onUncaughtExceptionIntegration = ((options = {}) => {
|
|
1438
|
+
const _options = {
|
|
1439
|
+
exitEvenIfOtherHandlersAreRegistered: true,
|
|
1440
|
+
...options,
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
return {
|
|
1444
|
+
name: INTEGRATION_NAME$3,
|
|
1445
|
+
setup(client) {
|
|
1446
|
+
global.process.on('uncaughtException', makeErrorHandler(client, _options));
|
|
1447
|
+
},
|
|
1448
|
+
};
|
|
1449
|
+
}) ;
|
|
1450
|
+
|
|
1451
|
+
/**
|
|
1452
|
+
* Add a global exception handler.
|
|
1453
|
+
*/
|
|
1454
|
+
const onUncaughtExceptionIntegration = core.defineIntegration(_onUncaughtExceptionIntegration);
|
|
1455
|
+
|
|
1456
|
+
/** Exported only for tests */
|
|
1457
|
+
function makeErrorHandler(client, options) {
|
|
1458
|
+
const timeout = 2000;
|
|
1459
|
+
let caughtFirstError = false;
|
|
1460
|
+
let caughtSecondError = false;
|
|
1461
|
+
let calledFatalError = false;
|
|
1462
|
+
let firstError;
|
|
1463
|
+
|
|
1464
|
+
const clientOptions = client.getOptions();
|
|
1465
|
+
|
|
1466
|
+
return Object.assign(
|
|
1467
|
+
(error) => {
|
|
1468
|
+
let onFatalError = logAndExitProcess;
|
|
1469
|
+
|
|
1470
|
+
if (options.onFatalError) {
|
|
1471
|
+
onFatalError = options.onFatalError;
|
|
1472
|
+
} else if (clientOptions.onFatalError) {
|
|
1473
|
+
onFatalError = clientOptions.onFatalError ;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not
|
|
1477
|
+
// want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust
|
|
1478
|
+
// exit behaviour of the SDK accordingly:
|
|
1479
|
+
// - If other listeners are attached, do not exit.
|
|
1480
|
+
// - If the only listener attached is ours, exit.
|
|
1481
|
+
const userProvidedListenersCount = (global.process.listeners('uncaughtException') ).filter(
|
|
1482
|
+
listener => {
|
|
1483
|
+
// There are 3 listeners we ignore:
|
|
1484
|
+
return (
|
|
1485
|
+
// as soon as we're using domains this listener is attached by node itself
|
|
1486
|
+
listener.name !== 'domainUncaughtExceptionClear' &&
|
|
1487
|
+
// the handler we register for tracing
|
|
1488
|
+
listener.tag !== 'sentry_tracingErrorCallback' &&
|
|
1489
|
+
// the handler we register in this integration
|
|
1490
|
+
(listener )._errorHandler !== true
|
|
1491
|
+
);
|
|
1492
|
+
},
|
|
1493
|
+
).length;
|
|
1494
|
+
|
|
1495
|
+
const processWouldExit = userProvidedListenersCount === 0;
|
|
1496
|
+
const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;
|
|
1497
|
+
|
|
1498
|
+
if (!caughtFirstError) {
|
|
1499
|
+
// this is the first uncaught error and the ultimate reason for shutting down
|
|
1500
|
+
// we want to do absolutely everything possible to ensure it gets captured
|
|
1501
|
+
// also we want to make sure we don't go recursion crazy if more errors happen after this one
|
|
1502
|
+
firstError = error;
|
|
1503
|
+
caughtFirstError = true;
|
|
1504
|
+
|
|
1505
|
+
if (core.getClient() === client) {
|
|
1506
|
+
core.captureException(error, {
|
|
1507
|
+
originalException: error,
|
|
1508
|
+
captureContext: {
|
|
1509
|
+
level: 'fatal',
|
|
1510
|
+
},
|
|
1511
|
+
mechanism: {
|
|
1512
|
+
handled: false,
|
|
1513
|
+
type: 'onuncaughtexception',
|
|
1514
|
+
},
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
if (!calledFatalError && shouldApplyFatalHandlingLogic) {
|
|
1519
|
+
calledFatalError = true;
|
|
1520
|
+
onFatalError(error);
|
|
1521
|
+
}
|
|
1522
|
+
} else {
|
|
1523
|
+
if (shouldApplyFatalHandlingLogic) {
|
|
1524
|
+
if (calledFatalError) {
|
|
1525
|
+
// we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down
|
|
1526
|
+
DEBUG_BUILD &&
|
|
1527
|
+
utils.logger.warn(
|
|
1528
|
+
'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',
|
|
1529
|
+
);
|
|
1530
|
+
logAndExitProcess(error);
|
|
1531
|
+
} else if (!caughtSecondError) {
|
|
1532
|
+
// two cases for how we can hit this branch:
|
|
1533
|
+
// - capturing of first error blew up and we just caught the exception from that
|
|
1534
|
+
// - quit trying to capture, proceed with shutdown
|
|
1535
|
+
// - a second independent error happened while waiting for first error to capture
|
|
1536
|
+
// - want to avoid causing premature shutdown before first error capture finishes
|
|
1537
|
+
// it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff
|
|
1538
|
+
// so let's instead just delay a bit before we proceed with our action here
|
|
1539
|
+
// in case 1, we just wait a bit unnecessarily but ultimately do the same thing
|
|
1540
|
+
// in case 2, the delay hopefully made us wait long enough for the capture to finish
|
|
1541
|
+
// two potential nonideal outcomes:
|
|
1542
|
+
// nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError
|
|
1543
|
+
// nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error
|
|
1544
|
+
// note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)
|
|
1545
|
+
// we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish
|
|
1546
|
+
caughtSecondError = true;
|
|
1547
|
+
setTimeout(() => {
|
|
1548
|
+
if (!calledFatalError) {
|
|
1549
|
+
// it was probably case 1, let's treat err as the sendErr and call onFatalError
|
|
1550
|
+
calledFatalError = true;
|
|
1551
|
+
onFatalError(firstError, error);
|
|
1552
|
+
}
|
|
1553
|
+
}, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
},
|
|
1558
|
+
{ _errorHandler: true },
|
|
1559
|
+
);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
const INTEGRATION_NAME$2 = 'OnUnhandledRejection';
|
|
1563
|
+
|
|
1564
|
+
const _onUnhandledRejectionIntegration = ((options = {}) => {
|
|
1565
|
+
const mode = options.mode || 'warn';
|
|
1566
|
+
|
|
1567
|
+
return {
|
|
1568
|
+
name: INTEGRATION_NAME$2,
|
|
1569
|
+
setup(client) {
|
|
1570
|
+
global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, { mode }));
|
|
1571
|
+
},
|
|
1572
|
+
};
|
|
1573
|
+
}) ;
|
|
1574
|
+
|
|
1575
|
+
/**
|
|
1576
|
+
* Add a global promise rejection handler.
|
|
1577
|
+
*/
|
|
1578
|
+
const onUnhandledRejectionIntegration = core.defineIntegration(_onUnhandledRejectionIntegration);
|
|
1579
|
+
|
|
1580
|
+
/**
|
|
1581
|
+
* Send an exception with reason
|
|
1582
|
+
* @param reason string
|
|
1583
|
+
* @param promise promise
|
|
1584
|
+
*
|
|
1585
|
+
* Exported only for tests.
|
|
1586
|
+
*/
|
|
1587
|
+
function makeUnhandledPromiseHandler(
|
|
1588
|
+
client,
|
|
1589
|
+
options,
|
|
1590
|
+
) {
|
|
1591
|
+
return function sendUnhandledPromise(reason, promise) {
|
|
1592
|
+
if (core.getClient() !== client) {
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
core.captureException(reason, {
|
|
1597
|
+
originalException: promise,
|
|
1598
|
+
captureContext: {
|
|
1599
|
+
extra: { unhandledPromiseRejection: true },
|
|
1600
|
+
},
|
|
1601
|
+
mechanism: {
|
|
1602
|
+
handled: false,
|
|
1603
|
+
type: 'onunhandledrejection',
|
|
1604
|
+
},
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
handleRejection(reason, options);
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/**
|
|
1612
|
+
* Handler for `mode` option
|
|
1613
|
+
|
|
1614
|
+
*/
|
|
1615
|
+
function handleRejection(
|
|
1616
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1617
|
+
reason,
|
|
1618
|
+
options,
|
|
1619
|
+
) {
|
|
1620
|
+
// https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240
|
|
1621
|
+
const rejectionWarning =
|
|
1622
|
+
'This error originated either by ' +
|
|
1623
|
+
'throwing inside of an async function without a catch block, ' +
|
|
1624
|
+
'or by rejecting a promise which was not handled with .catch().' +
|
|
1625
|
+
' The promise rejected with the reason:';
|
|
1626
|
+
|
|
1627
|
+
/* eslint-disable no-console */
|
|
1628
|
+
if (options.mode === 'warn') {
|
|
1629
|
+
utils.consoleSandbox(() => {
|
|
1630
|
+
console.warn(rejectionWarning);
|
|
1631
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1632
|
+
console.error(reason && reason.stack ? reason.stack : reason);
|
|
1633
|
+
});
|
|
1634
|
+
} else if (options.mode === 'strict') {
|
|
1635
|
+
utils.consoleSandbox(() => {
|
|
1636
|
+
console.warn(rejectionWarning);
|
|
1637
|
+
});
|
|
1638
|
+
logAndExitProcess(reason);
|
|
1639
|
+
}
|
|
1640
|
+
/* eslint-enable no-console */
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// This string is a placeholder that gets overwritten with the worker code.
|
|
1644
|
+
const base64WorkerScript = 'aW1wb3J0IHsgU2Vzc2lvbiB9IGZyb20gJ2luc3BlY3Rvcic7CmltcG9ydCB7IHdvcmtlckRhdGEsIHBhcmVudFBvcnQgfSBmcm9tICd3b3JrZXJfdGhyZWFkcyc7CmltcG9ydCB7IHBvc2l4LCBzZXAgfSBmcm9tICdwYXRoJzsKaW1wb3J0ICogYXMgaHR0cCQxIGZyb20gJ25vZGU6aHR0cCc7CmltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ25vZGU6aHR0cHMnOwppbXBvcnQgeyBSZWFkYWJsZSB9IGZyb20gJ3N0cmVhbSc7CmltcG9ydCB7IGNyZWF0ZUd6aXAgfSBmcm9tICd6bGliJzsKaW1wb3J0ICogYXMgbmV0IGZyb20gJ25ldCc7CmltcG9ydCAqIGFzIHRscyBmcm9tICd0bHMnOwppbXBvcnQgKiBhcyBodHRwIGZyb20gJ2h0dHAnOwoKLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC91bmJvdW5kLW1ldGhvZApjb25zdCBvYmplY3RUb1N0cmluZyA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUncyB0eXBlIGlzIG9uZSBvZiBhIGZldyBFcnJvciBvciBFcnJvci1saWtlCiAqIHtAbGluayBpc0Vycm9yfS4KICoKICogQHBhcmFtIHdhdCBBIHZhbHVlIHRvIGJlIGNoZWNrZWQuCiAqIEByZXR1cm5zIEEgYm9vbGVhbiByZXByZXNlbnRpbmcgdGhlIHJlc3VsdC4KICovCmZ1bmN0aW9uIGlzRXJyb3Iod2F0KSB7CiAgc3dpdGNoIChvYmplY3RUb1N0cmluZy5jYWxsKHdhdCkpIHsKICAgIGNhc2UgJ1tvYmplY3QgRXJyb3JdJzoKICAgIGNhc2UgJ1tvYmplY3QgRXhjZXB0aW9uXSc6CiAgICBjYXNlICdbb2JqZWN0IERPTUV4Y2VwdGlvbl0nOgogICAgICByZXR1cm4gdHJ1ZTsKICAgIGRlZmF1bHQ6CiAgICAgIHJldHVybiBpc0luc3RhbmNlT2Yod2F0LCBFcnJvcik7CiAgfQp9Ci8qKgogKiBDaGVja3Mgd2hldGhlciBnaXZlbiB2YWx1ZSBpcyBhbiBpbnN0YW5jZSBvZiB0aGUgZ2l2ZW4gYnVpbHQtaW4gY2xhc3MuCiAqCiAqIEBwYXJhbSB3YXQgVGhlIHZhbHVlIHRvIGJlIGNoZWNrZWQKICogQHBhcmFtIGNsYXNzTmFtZQogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc0J1aWx0aW4od2F0LCBjbGFzc05hbWUpIHsKICByZXR1cm4gb2JqZWN0VG9TdHJpbmcuY2FsbCh3YXQpID09PSBgW29iamVjdCAke2NsYXNzTmFtZX1dYDsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhIHN0cmluZwogKiB7QGxpbmsgaXNTdHJpbmd9LgogKgogKiBAcGFyYW0gd2F0IEEgdmFsdWUgdG8gYmUgY2hlY2tlZC4KICogQHJldHVybnMgQSBib29sZWFuIHJlcHJlc2VudGluZyB0aGUgcmVzdWx0LgogKi8KZnVuY3Rpb24gaXNTdHJpbmcod2F0KSB7CiAgcmV0dXJuIGlzQnVpbHRpbih3YXQsICdTdHJpbmcnKTsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhbiBvYmplY3QgbGl0ZXJhbCwgb3IgYSBjbGFzcyBpbnN0YW5jZS4KICoge0BsaW5rIGlzUGxhaW5PYmplY3R9LgogKgogKiBAcGFyYW0gd2F0IEEgdmFsdWUgdG8gYmUgY2hlY2tlZC4KICogQHJldHVybnMgQSBib29sZWFuIHJlcHJlc2VudGluZyB0aGUgcmVzdWx0LgogKi8KZnVuY3Rpb24gaXNQbGFpbk9iamVjdCh3YXQpIHsKICByZXR1cm4gaXNCdWlsdGluKHdhdCwgJ09iamVjdCcpOwp9CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUncyB0eXBlIGlzIGFuIEV2ZW50IGluc3RhbmNlCiAqIHtAbGluayBpc0V2ZW50fS4KICoKICogQHBhcmFtIHdhdCBBIHZhbHVlIHRvIGJlIGNoZWNrZWQuCiAqIEByZXR1cm5zIEEgYm9vbGVhbiByZXByZXNlbnRpbmcgdGhlIHJlc3VsdC4KICovCmZ1bmN0aW9uIGlzRXZlbnQod2F0KSB7CiAgcmV0dXJuIHR5cGVvZiBFdmVudCAhPT0gJ3VuZGVmaW5lZCcgJiYgaXNJbnN0YW5jZU9mKHdhdCwgRXZlbnQpOwp9CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUncyB0eXBlIGlzIGFuIEVsZW1lbnQgaW5zdGFuY2UKICoge0BsaW5rIGlzRWxlbWVudH0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc0VsZW1lbnQod2F0KSB7CiAgcmV0dXJuIHR5cGVvZiBFbGVtZW50ICE9PSAndW5kZWZpbmVkJyAmJiBpc0luc3RhbmNlT2Yod2F0LCBFbGVtZW50KTsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlIGhhcyBhIHRoZW4gZnVuY3Rpb24uCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKi8KZnVuY3Rpb24gaXNUaGVuYWJsZSh3YXQpIHsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVuc2FmZS1tZW1iZXItYWNjZXNzCiAgcmV0dXJuIEJvb2xlYW4od2F0ICYmIHdhdC50aGVuICYmIHR5cGVvZiB3YXQudGhlbiA9PT0gJ2Z1bmN0aW9uJyk7Cn0KCi8qKgogKiBDaGVja3Mgd2hldGhlciBnaXZlbiB2YWx1ZSdzIHR5cGUgaXMgYSBTeW50aGV0aWNFdmVudAogKiB7QGxpbmsgaXNTeW50aGV0aWNFdmVudH0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc1N5bnRoZXRpY0V2ZW50KHdhdCkgewogIHJldHVybiBpc1BsYWluT2JqZWN0KHdhdCkgJiYgJ25hdGl2ZUV2ZW50JyBpbiB3YXQgJiYgJ3ByZXZlbnREZWZhdWx0JyBpbiB3YXQgJiYgJ3N0b3BQcm9wYWdhdGlvbicgaW4gd2F0Owp9CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUgaXMgTmFOCiAqIHtAbGluayBpc05hTn0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc05hTiQxKHdhdCkgewogIHJldHVybiB0eXBlb2Ygd2F0ID09PSAnbnVtYmVyJyAmJiB3YXQgIT09IHdhdDsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhbiBpbnN0YW5jZSBvZiBwcm92aWRlZCBjb25zdHJ1Y3Rvci4KICoge0BsaW5rIGlzSW5zdGFuY2VPZn0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcGFyYW0gYmFzZSBBIGNvbnN0cnVjdG9yIHRvIGJlIHVzZWQgaW4gYSBjaGVjay4KICogQHJldHVybnMgQSBib29sZWFuIHJlcHJlc2VudGluZyB0aGUgcmVzdWx0LgogKi8KZnVuY3Rpb24gaXNJbnN0YW5jZU9mKHdhdCwgYmFzZSkgewogIHRyeSB7CiAgICByZXR1cm4gd2F0IGluc3RhbmNlb2YgYmFzZTsKICB9IGNhdGNoIChfZSkgewogICAgcmV0dXJuIGZhbHNlOwogIH0KfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhIFZ1ZSBWaWV3TW9kZWwuCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc1Z1ZVZpZXdNb2RlbCh3YXQpIHsKICAvLyBOb3QgdXNpbmcgT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZyBiZWNhdXNlIGluIFZ1ZSAzIGl0IHdvdWxkIHJlYWQgdGhlIGluc3RhbmNlJ3MgU3ltYm9sKFN5bWJvbC50b1N0cmluZ1RhZykgcHJvcGVydHkuCiAgcmV0dXJuICEhKHR5cGVvZiB3YXQgPT09ICdvYmplY3QnICYmIHdhdCAhPT0gbnVsbCAmJiAoKHdhdCApLl9faXNWdWUgfHwgKHdhdCApLl9pc1Z1ZSkpOwp9CgovKiogSW50ZXJuYWwgZ2xvYmFsIHdpdGggY29tbW9uIHByb3BlcnRpZXMgYW5kIFNlbnRyeSBleHRlbnNpb25zICAqLwoKLy8gVGhlIGNvZGUgYmVsb3cgZm9yICdpc0dsb2JhbE9iaicgYW5kICdHTE9CQUxfT0JKJyB3YXMgY29waWVkIGZyb20gY29yZS1qcyBiZWZvcmUgbW9kaWZpY2F0aW9uCi8vIGh0dHBzOi8vZ2l0aHViLmNvbS96bG9pcm9jay9jb3JlLWpzL2Jsb2IvMWI5NDRkZjU1MjgyY2RjOTljOTBkYjVmNDllYjBiNmVkYTJjYzBhMy9wYWNrYWdlcy9jb3JlLWpzL2ludGVybmFscy9nbG9iYWwuanMKLy8gY29yZS1qcyBoYXMgdGhlIGZvbGxvd2luZyBsaWNlbmNlOgovLwovLyBDb3B5cmlnaHQgKGMpIDIwMTQtMjAyMiBEZW5pcyBQdXNoa2FyZXYKLy8KLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGEgY29weQovLyBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSAiU29mdHdhcmUiKSwgdG8gZGVhbAovLyBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzCi8vIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwKLy8gY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzCi8vIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6Ci8vCi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluCi8vIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLgovLwovLyBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgovLyBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKLy8gRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCi8vIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKLy8gTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKLy8gT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTgovLyBUSEUgU09GVFdBUkUuCgovKiogUmV0dXJucyAnb2JqJyBpZiBpdCdzIHRoZSBnbG9iYWwgb2JqZWN0LCBvdGhlcndpc2UgcmV0dXJucyB1bmRlZmluZWQgKi8KZnVuY3Rpb24gaXNHbG9iYWxPYmoob2JqKSB7CiAgcmV0dXJuIG9iaiAmJiBvYmouTWF0aCA9PSBNYXRoID8gb2JqIDogdW5kZWZpbmVkOwp9CgovKiogR2V0J3MgdGhlIGdsb2JhbCBvYmplY3QgZm9yIHRoZSBjdXJyZW50IEphdmFTY3JpcHQgcnVudGltZSAqLwpjb25zdCBHTE9CQUxfT0JKID0KICAodHlwZW9mIGdsb2JhbFRoaXMgPT0gJ29iamVjdCcgJiYgaXNHbG9iYWxPYmooZ2xvYmFsVGhpcykpIHx8CiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXJlc3RyaWN0ZWQtZ2xvYmFscwogICh0eXBlb2Ygd2luZG93ID09ICdvYmplY3QnICYmIGlzR2xvYmFsT2JqKHdpbmRvdykpIHx8CiAgKHR5cGVvZiBzZWxmID09ICdvYmplY3QnICYmIGlzR2xvYmFsT2JqKHNlbGYpKSB8fAogICh0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGlzR2xvYmFsT2JqKGdsb2JhbCkpIHx8CiAgKGZ1bmN0aW9uICgpIHsKICAgIHJldHVybiB0aGlzOwogIH0pKCkgfHwKICB7fTsKCi8qKgogKiBAZGVwcmVjYXRlZCBVc2UgR0xPQkFMX09CSiBpbnN0ZWFkIG9yIFdJTkRPVyBmcm9tIEBzZW50cnkvYnJvd3Nlci4gVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjgKICovCmZ1bmN0aW9uIGdldEdsb2JhbE9iamVjdCgpIHsKICByZXR1cm4gR0xPQkFMX09CSiA7Cn0KCi8qKgogKiBSZXR1cm5zIGEgZ2xvYmFsIHNpbmdsZXRvbiBjb250YWluZWQgaW4gdGhlIGdsb2JhbCBgX19TRU5UUllfX2Agb2JqZWN0LgogKgogKiBJZiB0aGUgc2luZ2xldG9uIGRvZXNuJ3QgYWxyZWFkeSBleGlzdCBpbiBgX19TRU5UUllfX2AsIGl0IHdpbGwgYmUgY3JlYXRlZCB1c2luZyB0aGUgZ2l2ZW4gZmFjdG9yeQogKiBmdW5jdGlvbiBhbmQgYWRkZWQgdG8gdGhlIGBfX1NFTlRSWV9fYCBvYmplY3QuCiAqCiAqIEBwYXJhbSBuYW1lIG5hbWUgb2YgdGhlIGdsb2JhbCBzaW5nbGV0b24gb24gX19TRU5UUllfXwogKiBAcGFyYW0gY3JlYXRvciBjcmVhdG9yIEZhY3RvcnkgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSBzaW5nbGV0b24gaWYgaXQgZG9lc24ndCBhbHJlYWR5IGV4aXN0IG9uIGBfX1NFTlRSWV9fYAogKiBAcGFyYW0gb2JqIChPcHRpb25hbCkgVGhlIGdsb2JhbCBvYmplY3Qgb24gd2hpY2ggdG8gbG9vayBmb3IgYF9fU0VOVFJZX19gLCBpZiBub3QgYEdMT0JBTF9PQkpgJ3MgcmV0dXJuIHZhbHVlCiAqIEByZXR1cm5zIHRoZSBzaW5nbGV0b24KICovCmZ1bmN0aW9uIGdldEdsb2JhbFNpbmdsZXRvbihuYW1lLCBjcmVhdG9yLCBvYmopIHsKICBjb25zdCBnYmwgPSAob2JqIHx8IEdMT0JBTF9PQkopIDsKICBjb25zdCBfX1NFTlRSWV9fID0gKGdibC5fX1NFTlRSWV9fID0gZ2JsLl9fU0VOVFJZX18gfHwge30pOwogIGNvbnN0IHNpbmdsZXRvbiA9IF9fU0VOVFJZX19bbmFtZV0gfHwgKF9fU0VOVFJZX19bbmFtZV0gPSBjcmVhdG9yKCkpOwogIHJldHVybiBzaW5nbGV0b247Cn0KCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgpjb25zdCBXSU5ET1ckNSA9IGdldEdsb2JhbE9iamVjdCgpOwoKY29uc3QgREVGQVVMVF9NQVhfU1RSSU5HX0xFTkdUSCA9IDgwOwoKLyoqCiAqIEdpdmVuIGEgY2hpbGQgRE9NIGVsZW1lbnQsIHJldHVybnMgYSBxdWVyeS1zZWxlY3RvciBzdGF0ZW1lbnQgZGVzY3JpYmluZyB0aGF0CiAqIGFuZCBpdHMgYW5jZXN0b3JzCiAqIGUuZy4gW0hUTUxFbGVtZW50XSA9PiBib2R5ID4gZGl2ID4gaW5wdXQjZm9vLmJ0bltuYW1lPWJhel0KICogQHJldHVybnMgZ2VuZXJhdGVkIERPTSBwYXRoCiAqLwpmdW5jdGlvbiBodG1sVHJlZUFzU3RyaW5nKAogIGVsZW0sCiAgb3B0aW9ucyA9IHt9LAopIHsKICBpZiAoIWVsZW0pIHsKICAgIHJldHVybiAnPHVua25vd24+JzsKICB9CgogIC8vIHRyeS9jYXRjaCBib3RoOgogIC8vIC0gYWNjZXNzaW5nIGV2ZW50LnRhcmdldCAoc2VlIGdldHNlbnRyeS9yYXZlbi1qcyM4MzgsICM3NjgpCiAgLy8gLSBgaHRtbFRyZWVBc1N0cmluZ2AgYmVjYXVzZSBpdCdzIGNvbXBsZXgsIGFuZCBqdXN0IGFjY2Vzc2luZyB0aGUgRE9NIGluY29ycmVjdGx5CiAgLy8gLSBjYW4gdGhyb3cgYW4gZXhjZXB0aW9uIGluIHNvbWUgY2lyY3Vtc3RhbmNlcy4KICB0cnkgewogICAgbGV0IGN1cnJlbnRFbGVtID0gZWxlbSA7CiAgICBjb25zdCBNQVhfVFJBVkVSU0VfSEVJR0hUID0gNTsKICAgIGNvbnN0IG91dCA9IFtdOwogICAgbGV0IGhlaWdodCA9IDA7CiAgICBsZXQgbGVuID0gMDsKICAgIGNvbnN0IHNlcGFyYXRvciA9ICcgPiAnOwogICAgY29uc3Qgc2VwTGVuZ3RoID0gc2VwYXJhdG9yLmxlbmd0aDsKICAgIGxldCBuZXh0U3RyOwogICAgY29uc3Qga2V5QXR0cnMgPSBBcnJheS5pc0FycmF5KG9wdGlvbnMpID8gb3B0aW9ucyA6IG9wdGlvbnMua2V5QXR0cnM7CiAgICBjb25zdCBtYXhTdHJpbmdMZW5ndGggPSAoIUFycmF5LmlzQXJyYXkob3B0aW9ucykgJiYgb3B0aW9ucy5tYXhTdHJpbmdMZW5ndGgpIHx8IERFRkFVTFRfTUFYX1NUUklOR19MRU5HVEg7CgogICAgd2hpbGUgKGN1cnJlbnRFbGVtICYmIGhlaWdodCsrIDwgTUFYX1RSQVZFUlNFX0hFSUdIVCkgewogICAgICBuZXh0U3RyID0gX2h0bWxFbGVtZW50QXNTdHJpbmcoY3VycmVudEVsZW0sIGtleUF0dHJzKTsKICAgICAgLy8gYmFpbCBvdXQgaWYKICAgICAgLy8gLSBuZXh0U3RyIGlzIHRoZSAnaHRtbCcgZWxlbWVudAogICAgICAvLyAtIHRoZSBsZW5ndGggb2YgdGhlIHN0cmluZyB0aGF0IHdvdWxkIGJlIGNyZWF0ZWQgZXhjZWVkcyBtYXhTdHJpbmdMZW5ndGgKICAgICAgLy8gICAoaWdub3JlIHRoaXMgbGltaXQgaWYgd2UgYXJlIG9uIHRoZSBmaXJzdCBpdGVyYXRpb24pCiAgICAgIGlmIChuZXh0U3RyID09PSAnaHRtbCcgfHwgKGhlaWdodCA+IDEgJiYgbGVuICsgb3V0Lmxlbmd0aCAqIHNlcExlbmd0aCArIG5leHRTdHIubGVuZ3RoID49IG1heFN0cmluZ0xlbmd0aCkpIHsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgb3V0LnB1c2gobmV4dFN0cik7CgogICAgICBsZW4gKz0gbmV4dFN0ci5sZW5ndGg7CiAgICAgIGN1cnJlbnRFbGVtID0gY3VycmVudEVsZW0ucGFyZW50Tm9kZTsKICAgIH0KCiAgICByZXR1cm4gb3V0LnJldmVyc2UoKS5qb2luKHNlcGFyYXRvcik7CiAgfSBjYXRjaCAoX29PKSB7CiAgICByZXR1cm4gJzx1bmtub3duPic7CiAgfQp9CgovKioKICogUmV0dXJucyBhIHNpbXBsZSwgcXVlcnktc2VsZWN0b3IgcmVwcmVzZW50YXRpb24gb2YgYSBET00gZWxlbWVudAogKiBlLmcuIFtIVE1MRWxlbWVudF0gPT4gaW5wdXQjZm9vLmJ0bltuYW1lPWJhel0KICogQHJldHVybnMgZ2VuZXJhdGVkIERPTSBwYXRoCiAqLwpmdW5jdGlvbiBfaHRtbEVsZW1lbnRBc1N0cmluZyhlbCwga2V5QXR0cnMpIHsKICBjb25zdCBlbGVtID0gZWwKCjsKCiAgY29uc3Qgb3V0ID0gW107CiAgbGV0IGNsYXNzTmFtZTsKICBsZXQgY2xhc3NlczsKICBsZXQga2V5OwogIGxldCBhdHRyOwogIGxldCBpOwoKICBpZiAoIWVsZW0gfHwgIWVsZW0udGFnTmFtZSkgewogICAgcmV0dXJuICcnOwogIH0KCiAgLy8gQHRzLWV4cGVjdC1lcnJvciBXSU5ET1cgaGFzIEhUTUxFbGVtZW50CiAgaWYgKFdJTkRPVyQ1LkhUTUxFbGVtZW50KSB7CiAgICAvLyBJZiB1c2luZyB0aGUgY29tcG9uZW50IG5hbWUgYW5ub3RhdGlvbiBwbHVnaW4sIHRoaXMgdmFsdWUgbWF5IGJlIGF2YWlsYWJsZSBvbiB0aGUgRE9NIG5vZGUKICAgIGlmIChlbGVtIGluc3RhbmNlb2YgSFRNTEVsZW1lbnQgJiYgZWxlbS5kYXRhc2V0ICYmIGVsZW0uZGF0YXNldFsnc2VudHJ5Q29tcG9uZW50J10pIHsKICAgICAgcmV0dXJuIGVsZW0uZGF0YXNldFsnc2VudHJ5Q29tcG9uZW50J107CiAgICB9CiAgfQoKICBvdXQucHVzaChlbGVtLnRhZ05hbWUudG9Mb3dlckNhc2UoKSk7CgogIC8vIFBhaXJzIG9mIGF0dHJpYnV0ZSBrZXlzIGRlZmluZWQgaW4gYHNlcmlhbGl6ZUF0dHJpYnV0ZWAgYW5kIHRoZWlyIHZhbHVlcyBvbiBlbGVtZW50LgogIGNvbnN0IGtleUF0dHJQYWlycyA9CiAgICBrZXlBdHRycyAmJiBrZXlBdHRycy5sZW5ndGgKICAgICAgPyBrZXlBdHRycy5maWx0ZXIoa2V5QXR0ciA9PiBlbGVtLmdldEF0dHJpYnV0ZShrZXlBdHRyKSkubWFwKGtleUF0dHIgPT4gW2tleUF0dHIsIGVsZW0uZ2V0QXR0cmlidXRlKGtleUF0dHIpXSkKICAgICAgOiBudWxsOwoKICBpZiAoa2V5QXR0clBhaXJzICYmIGtleUF0dHJQYWlycy5sZW5ndGgpIHsKICAgIGtleUF0dHJQYWlycy5mb3JFYWNoKGtleUF0dHJQYWlyID0+IHsKICAgICAgb3V0LnB1c2goYFske2tleUF0dHJQYWlyWzBdfT0iJHtrZXlBdHRyUGFpclsxXX0iXWApOwogICAgfSk7CiAgfSBlbHNlIHsKICAgIGlmIChlbGVtLmlkKSB7CiAgICAgIG91dC5wdXNoKGAjJHtlbGVtLmlkfWApOwogICAgfQoKICAgIGNsYXNzTmFtZSA9IGVsZW0uY2xhc3NOYW1lOwogICAgaWYgKGNsYXNzTmFtZSAmJiBpc1N0cmluZyhjbGFzc05hbWUpKSB7CiAgICAgIGNsYXNzZXMgPSBjbGFzc05hbWUuc3BsaXQoL1xzKy8pOwogICAgICBmb3IgKGkgPSAwOyBpIDwgY2xhc3Nlcy5sZW5ndGg7IGkrKykgewogICAgICAgIG91dC5wdXNoKGAuJHtjbGFzc2VzW2ldfWApOwogICAgICB9CiAgICB9CiAgfQogIGNvbnN0IGFsbG93ZWRBdHRycyA9IFsnYXJpYS1sYWJlbCcsICd0eXBlJywgJ25hbWUnLCAndGl0bGUnLCAnYWx0J107CiAgZm9yIChpID0gMDsgaSA8IGFsbG93ZWRBdHRycy5sZW5ndGg7IGkrKykgewogICAga2V5ID0gYWxsb3dlZEF0dHJzW2ldOwogICAgYXR0ciA9IGVsZW0uZ2V0QXR0cmlidXRlKGtleSk7CiAgICBpZiAoYXR0cikgewogICAgICBvdXQucHVzaChgWyR7a2V5fT0iJHthdHRyfSJdYCk7CiAgICB9CiAgfQogIHJldHVybiBvdXQuam9pbignJyk7Cn0KCi8qKgogKiBUaGlzIHNlcnZlcyBhcyBhIGJ1aWxkIHRpbWUgZmxhZyB0aGF0IHdpbGwgYmUgdHJ1ZSBieSBkZWZhdWx0LCBidXQgZmFsc2UgaW4gbm9uLWRlYnVnIGJ1aWxkcyBvciBpZiB1c2VycyByZXBsYWNlIGBfX1NFTlRSWV9ERUJVR19fYCBpbiB0aGVpciBnZW5lcmF0ZWQgY29kZS4KICoKICogQVRURU5USU9OOiBUaGlzIGNvbnN0YW50IG11c3QgbmV2ZXIgY3Jvc3MgcGFja2FnZSBib3VuZGFyaWVzIChpLmUuIGJlIGV4cG9ydGVkKSB0byBndWFyYW50ZWUgdGhhdCBpdCBjYW4gYmUgdXNlZCBmb3IgdHJlZSBzaGFraW5nLgogKi8KY29uc3QgREVCVUdfQlVJTEQkMSA9ICh0eXBlb2YgX19TRU5UUllfREVCVUdfXyA9PT0gJ3VuZGVmaW5lZCcgfHwgX19TRU5UUllfREVCVUdfXyk7CgovKiogUHJlZml4IGZvciBsb2dnaW5nIHN0cmluZ3MgKi8KY29uc3QgUFJFRklYID0gJ1NlbnRyeSBMb2dnZXIgJzsKCmNvbnN0IENPTlNPTEVfTEVWRUxTID0gWwogICdkZWJ1ZycsCiAgJ2luZm8nLAogICd3YXJuJywKICAnZXJyb3InLAogICdsb2cnLAogICdhc3NlcnQnLAogICd0cmFjZScsCl0gOwoKLyoqIFRoaXMgbWF5IGJlIG11dGF0ZWQgYnkgdGhlIGNvbnNvbGUgaW5zdHJ1bWVudGF0aW9uLiAqLwpjb25zdCBvcmlnaW5hbENvbnNvbGVNZXRob2RzCgogPSB7fTsKCi8qKiBKU0RvYyAqLwoKLyoqCiAqIFRlbXBvcmFyaWx5IGRpc2FibGUgc2VudHJ5IGNvbnNvbGUgaW5zdHJ1bWVudGF0aW9ucy4KICoKICogQHBhcmFtIGNhbGxiYWNrIFRoZSBmdW5jdGlvbiB0byBydW4gYWdhaW5zdCB0aGUgb3JpZ2luYWwgYGNvbnNvbGVgIG1lc3NhZ2VzCiAqIEByZXR1cm5zIFRoZSByZXN1bHRzIG9mIHRoZSBjYWxsYmFjawogKi8KZnVuY3Rpb24gY29uc29sZVNhbmRib3goY2FsbGJhY2spIHsKICBpZiAoISgnY29uc29sZScgaW4gR0xPQkFMX09CSikpIHsKICAgIHJldHVybiBjYWxsYmFjaygpOwogIH0KCiAgY29uc3QgY29uc29sZSA9IEdMT0JBTF9PQkouY29uc29sZSA7CiAgY29uc3Qgd3JhcHBlZEZ1bmNzID0ge307CgogIGNvbnN0IHdyYXBwZWRMZXZlbHMgPSBPYmplY3Qua2V5cyhvcmlnaW5hbENvbnNvbGVNZXRob2RzKSA7CgogIC8vIFJlc3RvcmUgYWxsIHdyYXBwZWQgY29uc29sZSBtZXRob2RzCiAgd3JhcHBlZExldmVscy5mb3JFYWNoKGxldmVsID0+IHsKICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZU1ldGhvZCA9IG9yaWdpbmFsQ29uc29sZU1ldGhvZHNbbGV2ZWxdIDsKICAgIHdyYXBwZWRGdW5jc1tsZXZlbF0gPSBjb25zb2xlW2xldmVsXSA7CiAgICBjb25zb2xlW2xldmVsXSA9IG9yaWdpbmFsQ29uc29sZU1ldGhvZDsKICB9KTsKCiAgdHJ5IHsKICAgIHJldHVybiBjYWxsYmFjaygpOwogIH0gZmluYWxseSB7CiAgICAvLyBSZXZlcnQgcmVzdG9yYXRpb24gdG8gd3JhcHBlZCBzdGF0ZQogICAgd3JhcHBlZExldmVscy5mb3JFYWNoKGxldmVsID0+IHsKICAgICAgY29uc29sZVtsZXZlbF0gPSB3cmFwcGVkRnVuY3NbbGV2ZWxdIDsKICAgIH0pOwogIH0KfQoKZnVuY3Rpb24gbWFrZUxvZ2dlcigpIHsKICBsZXQgZW5hYmxlZCA9IGZhbHNlOwogIGNvbnN0IGxvZ2dlciA9IHsKICAgIGVuYWJsZTogKCkgPT4gewogICAgICBlbmFibGVkID0gdHJ1ZTsKICAgIH0sCiAgICBkaXNhYmxlOiAoKSA9PiB7CiAgICAgIGVuYWJsZWQgPSBmYWxzZTsKICAgIH0sCiAgICBpc0VuYWJsZWQ6ICgpID0+IGVuYWJsZWQsCiAgfTsKCiAgaWYgKERFQlVHX0JVSUxEJDEpIHsKICAgIENPTlNPTEVfTEVWRUxTLmZvckVhY2gobmFtZSA9PiB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55CiAgICAgIGxvZ2dlcltuYW1lXSA9ICguLi5hcmdzKSA9PiB7CiAgICAgICAgaWYgKGVuYWJsZWQpIHsKICAgICAgICAgIGNvbnNvbGVTYW5kYm94KCgpID0+IHsKICAgICAgICAgICAgR0xPQkFMX09CSi5jb25zb2xlW25hbWVdKGAke1BSRUZJWH1bJHtuYW1lfV06YCwgLi4uYXJncyk7CiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgIH07CiAgICB9KTsKICB9IGVsc2UgewogICAgQ09OU09MRV9MRVZFTFMuZm9yRWFjaChuYW1lID0+IHsKICAgICAgbG9nZ2VyW25hbWVdID0gKCkgPT4gdW5kZWZpbmVkOwogICAgfSk7CiAgfQoKICByZXR1cm4gbG9nZ2VyIDsKfQoKY29uc3QgbG9nZ2VyID0gbWFrZUxvZ2dlcigpOwoKLyoqCiAqIFJlbmRlcnMgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIERzbi4KICoKICogQnkgZGVmYXVsdCwgdGhpcyB3aWxsIHJlbmRlciB0aGUgcHVibGljIHJlcHJlc2VudGF0aW9uIHdpdGhvdXQgdGhlIHBhc3N3b3JkCiAqIGNvbXBvbmVudC4gVG8gZ2V0IHRoZSBkZXByZWNhdGVkIHByaXZhdGUgcmVwcmVzZW50YXRpb24sIHNldCBgd2l0aFBhc3N3b3JkYAogKiB0byB0cnVlLgogKgogKiBAcGFyYW0gd2l0aFBhc3N3b3JkIFdoZW4gc2V0IHRvIHRydWUsIHRoZSBwYXNzd29yZCB3aWxsIGJlIGluY2x1ZGVkLgogKi8KZnVuY3Rpb24gZHNuVG9TdHJpbmcoZHNuLCB3aXRoUGFzc3dvcmQgPSBmYWxzZSkgewogIGNvbnN0IHsgaG9zdCwgcGF0aCwgcGFzcywgcG9ydCwgcHJvamVjdElkLCBwcm90b2NvbCwgcHVibGljS2V5IH0gPSBkc247CiAgcmV0dXJuICgKICAgIGAke3Byb3RvY29sfTovLyR7cHVibGljS2V5fSR7d2l0aFBhc3N3b3JkICYmIHBhc3MgPyBgOiR7cGFzc31gIDogJyd9YCArCiAgICBgQCR7aG9zdH0ke3BvcnQgPyBgOiR7cG9ydH1gIDogJyd9LyR7cGF0aCA/IGAke3BhdGh9L2AgOiBwYXRofSR7cHJvamVjdElkfWAKICApOwp9CgovKiogQW4gZXJyb3IgZW1pdHRlZCBieSBTZW50cnkgU0RLcyBhbmQgcmVsYXRlZCB1dGlsaXRpZXMuICovCmNsYXNzIFNlbnRyeUVycm9yIGV4dGVuZHMgRXJyb3IgewogIC8qKiBEaXNwbGF5IG5hbWUgb2YgdGhpcyBlcnJvciBpbnN0YW5jZS4gKi8KCiAgIGNvbnN0cnVjdG9yKCBtZXNzYWdlLCBsb2dMZXZlbCA9ICd3YXJuJykgewogICAgc3VwZXIobWVzc2FnZSk7dGhpcy5tZXNzYWdlID0gbWVzc2FnZTsKICAgIHRoaXMubmFtZSA9IG5ldy50YXJnZXQucHJvdG90eXBlLmNvbnN0cnVjdG9yLm5hbWU7CiAgICAvLyBUaGlzIHNldHMgdGhlIHByb3RvdHlwZSB0byBiZSBgRXJyb3JgLCBub3QgYFNlbnRyeUVycm9yYC4gSXQncyB1bmNsZWFyIHdoeSB3ZSBkbyB0aGlzLCBidXQgY29tbWVudGluZyB0aGlzIGxpbmUKICAgIC8vIG91dCBjYXVzZXMgdmFyaW91cyAoc2VlbWluZ2x5IHRvdGFsbHkgdW5yZWxhdGVkKSBwbGF5d3JpZ2h0IHRlc3RzIGNvbnNpc3RlbnRseSB0aW1lIG91dC4gRllJLCB0aGlzIG1ha2VzCiAgICAvLyBpbnN0YW5jZXMgb2YgYFNlbnRyeUVycm9yYCBmYWlsIGBvYmogaW5zdGFuY2VvZiBTZW50cnlFcnJvcmAgY2hlY2tzLgogICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIG5ldy50YXJnZXQucHJvdG90eXBlKTsKICAgIHRoaXMubG9nTGV2ZWwgPSBsb2dMZXZlbDsKICB9Cn0KCmNvbnN0IFNUQUNLVFJBQ0VfRlJBTUVfTElNSVQgPSA1MDsKY29uc3QgVU5LTk9XTl9GVU5DVElPTiA9ICc/JzsKY29uc3QgU1RSSVBfRlJBTUVfUkVHRVhQID0gL2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vOwoKLyoqCiAqIFJlbW92ZXMgU2VudHJ5IGZyYW1lcyBmcm9tIHRoZSB0b3AgYW5kIGJvdHRvbSBvZiB0aGUgc3RhY2sgaWYgcHJlc2VudCBhbmQgZW5mb3JjZXMgYSBsaW1pdCBvZiBtYXggbnVtYmVyIG9mIGZyYW1lcy4KICogQXNzdW1lcyBzdGFjayBpbnB1dCBpcyBvcmRlcmVkIGZyb20gdG9wIHRvIGJvdHRvbSBhbmQgcmV0dXJucyB0aGUgcmV2ZXJzZSByZXByZXNlbnRhdGlvbiBzbyBjYWxsIHNpdGUgb2YgdGhlCiAqIGZ1bmN0aW9uIHRoYXQgY2F1c2VkIHRoZSBjcmFzaCBpcyB0aGUgbGFzdCBmcmFtZSBpbiB0aGUgYXJyYXkuCiAqIEBoaWRkZW4KICovCmZ1bmN0aW9uIHN0cmlwU2VudHJ5RnJhbWVzQW5kUmV2ZXJzZShzdGFjaykgewogIGlmICghc3RhY2subGVuZ3RoKSB7CiAgICByZXR1cm4gW107CiAgfQoKICBjb25zdCBsb2NhbFN0YWNrID0gQXJyYXkuZnJvbShzdGFjayk7CgogIC8vIElmIHN0YWNrIHN0YXJ0cyB3aXRoIG9uZSBvZiBvdXIgQVBJIGNhbGxzLCByZW1vdmUgaXQgKHN0YXJ0cywgbWVhbmluZyBpdCdzIHRoZSB0b3Agb2YgdGhlIHN0YWNrIC0gYWthIGxhc3QgY2FsbCkKICBpZiAoL3NlbnRyeVdyYXBwZWQvLnRlc3QobG9jYWxTdGFja1tsb2NhbFN0YWNrLmxlbmd0aCAtIDFdLmZ1bmN0aW9uIHx8ICcnKSkgewogICAgbG9jYWxTdGFjay5wb3AoKTsKICB9CgogIC8vIFJldmVyc2luZyBpbiB0aGUgbWlkZGxlIG9mIHRoZSBwcm9jZWR1cmUgYWxsb3dzIHVzIHRvIGp1c3QgcG9wIHRoZSB2YWx1ZXMgb2ZmIHRoZSBzdGFjawogIGxvY2FsU3RhY2sucmV2ZXJzZSgpOwoKICAvLyBJZiBzdGFjayBlbmRzIHdpdGggb25lIG9mIG91ciBpbnRlcm5hbCBBUEkgY2FsbHMsIHJlbW92ZSBpdCAoZW5kcywgbWVhbmluZyBpdCdzIHRoZSBib3R0b20gb2YgdGhlIHN0YWNrIC0gYWthIHRvcC1tb3N0IGNhbGwpCiAgaWYgKFNUUklQX0ZSQU1FX1JFR0VYUC50ZXN0KGxvY2FsU3RhY2tbbG9jYWxTdGFjay5sZW5ndGggLSAxXS5mdW5jdGlvbiB8fCAnJykpIHsKICAgIGxvY2FsU3RhY2sucG9wKCk7CgogICAgLy8gV2hlbiB1c2luZyBzeW50aGV0aWMgZXZlbnRzLCB3ZSB3aWxsIGhhdmUgYSAyIGxldmVscyBkZWVwIHN0YWNrLCBhcyBgbmV3IEVycm9yKCdTZW50cnkgc3ludGhldGljRXhjZXB0aW9uJylgCiAgICAvLyBpcyBwcm9kdWNlZCB3aXRoaW4gdGhlIGh1YiBpdHNlbGYsIG1ha2luZyBpdDoKICAgIC8vCiAgICAvLyAgIFNlbnRyeS5jYXB0dXJlRXhjZXB0aW9uKCkKICAgIC8vICAgZ2V0Q3VycmVudEh1YigpLmNhcHR1cmVFeGNlcHRpb24oKQogICAgLy8KICAgIC8vIGluc3RlYWQgb2YganVzdCB0aGUgdG9wIGBTZW50cnlgIGNhbGwgaXRzZWxmLgogICAgLy8gVGhpcyBmb3JjZXMgdXMgdG8gcG9zc2libHkgc3RyaXAgYW4gYWRkaXRpb25hbCBmcmFtZSBpbiB0aGUgZXhhY3Qgc2FtZSB3YXMgYXMgYWJvdmUuCiAgICBpZiAoU1RSSVBfRlJBTUVfUkVHRVhQLnRlc3QobG9jYWxTdGFja1tsb2NhbFN0YWNrLmxlbmd0aCAtIDFdLmZ1bmN0aW9uIHx8ICcnKSkgewogICAgICBsb2NhbFN0YWNrLnBvcCgpOwogICAgfQogIH0KCiAgcmV0dXJuIGxvY2FsU3RhY2suc2xpY2UoMCwgU1RBQ0tUUkFDRV9GUkFNRV9MSU1JVCkubWFwKGZyYW1lID0+ICh7CiAgICAuLi5mcmFtZSwKICAgIGZpbGVuYW1lOiBmcmFtZS5maWxlbmFtZSB8fCBsb2NhbFN0YWNrW2xvY2FsU3RhY2subGVuZ3RoIC0gMV0uZmlsZW5hbWUsCiAgICBmdW5jdGlvbjogZnJhbWUuZnVuY3Rpb24gfHwgVU5LTk9XTl9GVU5DVElPTiwKICB9KSk7Cn0KCmNvbnN0IGRlZmF1bHRGdW5jdGlvbk5hbWUgPSAnPGFub255bW91cz4nOwoKLyoqCiAqIFNhZmVseSBleHRyYWN0IGZ1bmN0aW9uIG5hbWUgZnJvbSBpdHNlbGYKICovCmZ1bmN0aW9uIGdldEZ1bmN0aW9uTmFtZShmbikgewogIHRyeSB7CiAgICBpZiAoIWZuIHx8IHR5cGVvZiBmbiAhPT0gJ2Z1bmN0aW9uJykgewogICAgICByZXR1cm4gZGVmYXVsdEZ1bmN0aW9uTmFtZTsKICAgIH0KICAgIHJldHVybiBmbi5uYW1lIHx8IGRlZmF1bHRGdW5jdGlvbk5hbWU7CiAgfSBjYXRjaCAoZSkgewogICAgLy8gSnVzdCBhY2Nlc3NpbmcgY3VzdG9tIHByb3BzIGluIHNvbWUgU2VsZW5pdW0gZW52aXJvbm1lbnRzCiAgICAvLyBjYW4gY2F1c2UgYSAiUGVybWlzc2lvbiBkZW5pZWQiIGV4Y2VwdGlvbiAoc2VlIHJhdmVuLWpzIzQ5NSkuCiAgICByZXR1cm4gZGVmYXVsdEZ1bmN0aW9uTmFtZTsKICB9Cn0KCi8qKgogKiBUaGlzIGV4dHJhY3RzIHRoZSBvcmlnaW5hbCBmdW5jdGlvbiBpZiBhdmFpbGFibGUuICBTZWUKICogYG1hcmtGdW5jdGlvbldyYXBwZWRgIGZvciBtb3JlIGluZm9ybWF0aW9uLgogKgogKiBAcGFyYW0gZnVuYyB0aGUgZnVuY3Rpb24gdG8gdW53cmFwCiAqIEByZXR1cm5zIHRoZSB1bndyYXBwZWQgdmVyc2lvbiBvZiB0aGUgZnVuY3Rpb24gaWYgYXZhaWxhYmxlLgogKi8KZnVuY3Rpb24gZ2V0T3JpZ2luYWxGdW5jdGlvbihmdW5jKSB7CiAgcmV0dXJuIGZ1bmMuX19zZW50cnlfb3JpZ2luYWxfXzsKfQoKLyoqCiAqIEVuY29kZXMgZ2l2ZW4gb2JqZWN0IGludG8gdXJsLWZyaWVuZGx5IGZvcm1hdAogKgogKiBAcGFyYW0gb2JqZWN0IEFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIHNlcmlhbGl6YWJsZSB2YWx1ZXMKICogQHJldHVybnMgc3RyaW5nIEVuY29kZWQKICovCmZ1bmN0aW9uIHVybEVuY29kZShvYmplY3QpIHsKICByZXR1cm4gT2JqZWN0LmtleXMob2JqZWN0KQogICAgLm1hcChrZXkgPT4gYCR7ZW5jb2RlVVJJQ29tcG9uZW50KGtleSl9PSR7ZW5jb2RlVVJJQ29tcG9uZW50KG9iamVjdFtrZXldKX1gKQogICAgLmpvaW4oJyYnKTsKfQoKLyoqCiAqIFRyYW5zZm9ybXMgYW55IGBFcnJvcmAgb3IgYEV2ZW50YCBpbnRvIGEgcGxhaW4gb2JqZWN0IHdpdGggYWxsIG9mIHRoZWlyIGVudW1lcmFibGUgcHJvcGVydGllcywgYW5kIHNvbWUgb2YgdGhlaXIKICogbm9uLWVudW1lcmFibGUgcHJvcGVydGllcyBhdHRhY2hlZC4KICoKICogQHBhcmFtIHZhbHVlIEluaXRpYWwgc291cmNlIHRoYXQgd2UgaGF2ZSB0byB0cmFuc2Zvcm0gaW4gb3JkZXIgZm9yIGl0IHRvIGJlIHVzYWJsZSBieSB0aGUgc2VyaWFsaXplcgogKiBAcmV0dXJucyBBbiBFdmVudCBvciBFcnJvciB0dXJuZWQgaW50byBhbiBvYmplY3QgLSBvciB0aGUgdmFsdWUgYXJndXJtZW50IGl0c2VsZiwgd2hlbiB2YWx1ZSBpcyBuZWl0aGVyIGFuIEV2ZW50IG5vcgogKiAgYW4gRXJyb3IuCiAqLwpmdW5jdGlvbiBjb252ZXJ0VG9QbGFpbk9iamVjdCgKICB2YWx1ZSwKKQoKIHsKICBpZiAoaXNFcnJvcih2YWx1ZSkpIHsKICAgIHJldHVybiB7CiAgICAgIG1lc3NhZ2U6IHZhbHVlLm1lc3NhZ2UsCiAgICAgIG5hbWU6IHZhbHVlLm5hbWUsCiAgICAgIHN0YWNrOiB2YWx1ZS5zdGFjaywKICAgICAgLi4uZ2V0T3duUHJvcGVydGllcyh2YWx1ZSksCiAgICB9OwogIH0gZWxzZSBpZiAoaXNFdmVudCh2YWx1ZSkpIHsKICAgIGNvbnN0IG5ld09iagoKID0gewogICAgICB0eXBlOiB2YWx1ZS50eXBlLAogICAgICB0YXJnZXQ6IHNlcmlhbGl6ZUV2ZW50VGFyZ2V0KHZhbHVlLnRhcmdldCksCiAgICAgIGN1cnJlbnRUYXJnZXQ6IHNlcmlhbGl6ZUV2ZW50VGFyZ2V0KHZhbHVlLmN1cnJlbnRUYXJnZXQpLAogICAgICAuLi5nZXRPd25Qcm9wZXJ0aWVzKHZhbHVlKSwKICAgIH07CgogICAgaWYgKHR5cGVvZiBDdXN0b21FdmVudCAhPT0gJ3VuZGVmaW5lZCcgJiYgaXNJbnN0YW5jZU9mKHZhbHVlLCBDdXN0b21FdmVudCkpIHsKICAgICAgbmV3T2JqLmRldGFpbCA9IHZhbHVlLmRldGFpbDsKICAgIH0KCiAgICByZXR1cm4gbmV3T2JqOwogIH0gZWxzZSB7CiAgICByZXR1cm4gdmFsdWU7CiAgfQp9CgovKiogQ3JlYXRlcyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgdGFyZ2V0IG9mIGFuIGBFdmVudGAgb2JqZWN0ICovCmZ1bmN0aW9uIHNlcmlhbGl6ZUV2ZW50VGFyZ2V0KHRhcmdldCkgewogIHRyeSB7CiAgICByZXR1cm4gaXNFbGVtZW50KHRhcmdldCkgPyBodG1sVHJlZUFzU3RyaW5nKHRhcmdldCkgOiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodGFyZ2V0KTsKICB9IGNhdGNoIChfb08pIHsKICAgIHJldHVybiAnPHVua25vd24+JzsKICB9Cn0KCi8qKiBGaWx0ZXJzIG91dCBhbGwgYnV0IGFuIG9iamVjdCdzIG93biBwcm9wZXJ0aWVzICovCmZ1bmN0aW9uIGdldE93blByb3BlcnRpZXMob2JqKSB7CiAgaWYgKHR5cGVvZiBvYmogPT09ICdvYmplY3QnICYmIG9iaiAhPT0gbnVsbCkgewogICAgY29uc3QgZXh0cmFjdGVkUHJvcHMgPSB7fTsKICAgIGZvciAoY29uc3QgcHJvcGVydHkgaW4gb2JqKSB7CiAgICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wZXJ0eSkpIHsKICAgICAgICBleHRyYWN0ZWRQcm9wc1twcm9wZXJ0eV0gPSAob2JqIClbcHJvcGVydHldOwogICAgICB9CiAgICB9CiAgICByZXR1cm4gZXh0cmFjdGVkUHJvcHM7CiAgfSBlbHNlIHsKICAgIHJldHVybiB7fTsKICB9Cn0KCi8qKgogKiBHaXZlbiBhbnkgb2JqZWN0LCByZXR1cm4gYSBuZXcgb2JqZWN0IGhhdmluZyByZW1vdmVkIGFsbCBmaWVsZHMgd2hvc2UgdmFsdWUgd2FzIGB1bmRlZmluZWRgLgogKiBXb3JrcyByZWN1cnNpdmVseSBvbiBvYmplY3RzIGFuZCBhcnJheXMuCiAqCiAqIEF0dGVudGlvbjogVGhpcyBmdW5jdGlvbiBrZWVwcyBjaXJjdWxhciByZWZlcmVuY2VzIGluIHRoZSByZXR1cm5lZCBvYmplY3QuCiAqLwpmdW5jdGlvbiBkcm9wVW5kZWZpbmVkS2V5cyhpbnB1dFZhbHVlKSB7CiAgLy8gVGhpcyBtYXAga2VlcHMgdHJhY2sgb2Ygd2hhdCBhbHJlYWR5IHZpc2l0ZWQgbm9kZXMgbWFwIHRvLgogIC8vIE91ciBTZXQgLSBiYXNlZCBtZW1vQnVpbGRlciBkb2Vzbid0IHdvcmsgaGVyZSBiZWNhdXNlIHdlIHdhbnQgdG8gdGhlIG91dHB1dCBvYmplY3QgdG8gaGF2ZSB0aGUgc2FtZSBjaXJjdWxhcgogIC8vIHJlZmVyZW5jZXMgYXMgdGhlIGlucHV0IG9iamVjdC4KICBjb25zdCBtZW1vaXphdGlvbk1hcCA9IG5ldyBNYXAoKTsKCiAgLy8gVGhpcyBmdW5jdGlvbiBqdXN0IHByb3hpZXMgYF9kcm9wVW5kZWZpbmVkS2V5c2AgdG8ga2VlcCB0aGUgYG1lbW9CdWlsZGVyYCBvdXQgb2YgdGhpcyBmdW5jdGlvbidzIEFQSQogIHJldHVybiBfZHJvcFVuZGVmaW5lZEtleXMoaW5wdXRWYWx1ZSwgbWVtb2l6YXRpb25NYXApOwp9CgpmdW5jdGlvbiBfZHJvcFVuZGVmaW5lZEtleXMoaW5wdXRWYWx1ZSwgbWVtb2l6YXRpb25NYXApIHsKICBpZiAoaXNQb2pvKGlucHV0VmFsdWUpKSB7CiAgICAvLyBJZiB0aGlzIG5vZGUgaGFzIGFscmVhZHkgYmVlbiB2aXNpdGVkIGR1ZSB0byBhIGNpcmN1bGFyIHJlZmVyZW5jZSwgcmV0dXJuIHRoZSBvYmplY3QgaXQgd2FzIG1hcHBlZCB0byBpbiB0aGUgbmV3IG9iamVjdAogICAgY29uc3QgbWVtb1ZhbCA9IG1lbW9pemF0aW9uTWFwLmdldChpbnB1dFZhbHVlKTsKICAgIGlmIChtZW1vVmFsICE9PSB1bmRlZmluZWQpIHsKICAgICAgcmV0dXJuIG1lbW9WYWwgOwogICAgfQoKICAgIGNvbnN0IHJldHVyblZhbHVlID0ge307CiAgICAvLyBTdG9yZSB0aGUgbWFwcGluZyBvZiB0aGlzIHZhbHVlIGluIGNhc2Ugd2UgdmlzaXQgaXQgYWdhaW4sIGluIGNhc2Ugb2YgY2lyY3VsYXIgZGF0YQogICAgbWVtb2l6YXRpb25NYXAuc2V0KGlucHV0VmFsdWUsIHJldHVyblZhbHVlKTsKCiAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyhpbnB1dFZhbHVlKSkgewogICAgICBpZiAodHlwZW9mIGlucHV0VmFsdWVba2V5XSAhPT0gJ3VuZGVmaW5lZCcpIHsKICAgICAgICByZXR1cm5WYWx1ZVtrZXldID0gX2Ryb3BVbmRlZmluZWRLZXlzKGlucHV0VmFsdWVba2V5XSwgbWVtb2l6YXRpb25NYXApOwogICAgICB9CiAgICB9CgogICAgcmV0dXJuIHJldHVyblZhbHVlIDsKICB9CgogIGlmIChBcnJheS5pc0FycmF5KGlucHV0VmFsdWUpKSB7CiAgICAvLyBJZiB0aGlzIG5vZGUgaGFzIGFscmVhZHkgYmVlbiB2aXNpdGVkIGR1ZSB0byBhIGNpcmN1bGFyIHJlZmVyZW5jZSwgcmV0dXJuIHRoZSBhcnJheSBpdCB3YXMgbWFwcGVkIHRvIGluIHRoZSBuZXcgb2JqZWN0CiAgICBjb25zdCBtZW1vVmFsID0gbWVtb2l6YXRpb25NYXAuZ2V0KGlucHV0VmFsdWUpOwogICAgaWYgKG1lbW9WYWwgIT09IHVuZGVmaW5lZCkgewogICAgICByZXR1cm4gbWVtb1ZhbCA7CiAgICB9CgogICAgY29uc3QgcmV0dXJuVmFsdWUgPSBbXTsKICAgIC8vIFN0b3JlIHRoZSBtYXBwaW5nIG9mIHRoaXMgdmFsdWUgaW4gY2FzZSB3ZSB2aXNpdCBpdCBhZ2FpbiwgaW4gY2FzZSBvZiBjaXJjdWxhciBkYXRhCiAgICBtZW1vaXphdGlvbk1hcC5zZXQoaW5wdXRWYWx1ZSwgcmV0dXJuVmFsdWUpOwoKICAgIGlucHV0VmFsdWUuZm9yRWFjaCgoaXRlbSkgPT4gewogICAgICByZXR1cm5WYWx1ZS5wdXNoKF9kcm9wVW5kZWZpbmVkS2V5cyhpdGVtLCBtZW1vaXphdGlvbk1hcCkpOwogICAgfSk7CgogICAgcmV0dXJuIHJldHVyblZhbHVlIDsKICB9CgogIHJldHVybiBpbnB1dFZhbHVlOwp9CgpmdW5jdGlvbiBpc1Bvam8oaW5wdXQpIHsKICBpZiAoIWlzUGxhaW5PYmplY3QoaW5wdXQpKSB7CiAgICByZXR1cm4gZmFsc2U7CiAgfQoKICB0cnkgewogICAgY29uc3QgbmFtZSA9IChPYmplY3QuZ2V0UHJvdG90eXBlT2YoaW5wdXQpICkuY29uc3RydWN0b3IubmFtZTsKICAgIHJldHVybiAhbmFtZSB8fCBuYW1lID09PSAnT2JqZWN0JzsKICB9IGNhdGNoIChlKSB7CiAgICByZXR1cm4gdHJ1ZTsKICB9Cn0KCi8qKgogKiBVVUlENCBnZW5lcmF0b3IKICoKICogQHJldHVybnMgc3RyaW5nIEdlbmVyYXRlZCBVVUlENC4KICovCmZ1bmN0aW9uIHV1aWQ0KCkgewogIGNvbnN0IGdibCA9IEdMT0JBTF9PQkogOwogIGNvbnN0IGNyeXB0byA9IGdibC5jcnlwdG8gfHwgZ2JsLm1zQ3J5cHRvOwoKICBsZXQgZ2V0UmFuZG9tQnl0ZSA9ICgpID0+IE1hdGgucmFuZG9tKCkgKiAxNjsKICB0cnkgewogICAgaWYgKGNyeXB0byAmJiBjcnlwdG8ucmFuZG9tVVVJRCkgewogICAgICByZXR1cm4gY3J5cHRvLnJhbmRvbVVVSUQoKS5yZXBsYWNlKC8tL2csICcnKTsKICAgIH0KICAgIGlmIChjcnlwdG8gJiYgY3J5cHRvLmdldFJhbmRvbVZhbHVlcykgewogICAgICBnZXRSYW5kb21CeXRlID0gKCkgPT4gewogICAgICAgIC8vIGNyeXB0by5nZXRSYW5kb21WYWx1ZXMgbWlnaHQgcmV0dXJuIHVuZGVmaW5lZCBpbnN0ZWFkIG9mIHRoZSB0eXBlZCBhcnJheQogICAgICAgIC8vIGluIG9sZCBDaHJvbWl1bSB2ZXJzaW9ucyAoZS5nLiAyMy4wLjEyMzUuMCAoMTUxNDIyKSkKICAgICAgICAvLyBIb3dldmVyLCBgdHlwZWRBcnJheWAgaXMgc3RpbGwgZmlsbGVkIGluLXBsYWNlLgogICAgICAgIC8vIEBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0NyeXB0by9nZXRSYW5kb21WYWx1ZXMjdHlwZWRhcnJheQogICAgICAgIGNvbnN0IHR5cGVkQXJyYXkgPSBuZXcgVWludDhBcnJheSgxKTsKICAgICAgICBjcnlwdG8uZ2V0UmFuZG9tVmFsdWVzKHR5cGVkQXJyYXkpOwogICAgICAgIHJldHVybiB0eXBlZEFycmF5WzBdOwogICAgICB9OwogICAgfQogIH0gY2F0Y2ggKF8pIHsKICAgIC8vIHNvbWUgcnVudGltZXMgY2FuIGNyYXNoIGludm9raW5nIGNyeXB0bwogICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdC9pc3N1ZXMvODkzNQogIH0KCiAgLy8gaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMDUwMzQvaG93LXRvLWNyZWF0ZS1hLWd1aWQtdXVpZC1pbi1qYXZhc2NyaXB0LzIxMTc1MjMjMjExNzUyMwogIC8vIENvbmNhdGVuYXRpbmcgdGhlIGZvbGxvd2luZyBudW1iZXJzIGFzIHN0cmluZ3MgcmVzdWx0cyBpbiAnMTAwMDAwMDAxMDAwNDAwMDgwMDAxMDAwMDAwMDAwMDAnCiAgcmV0dXJuICgoWzFlN10gKSArIDFlMyArIDRlMyArIDhlMyArIDFlMTEpLnJlcGxhY2UoL1swMThdL2csIGMgPT4KICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1iaXR3aXNlCiAgICAoKGMgKSBeICgoZ2V0UmFuZG9tQnl0ZSgpICYgMTUpID4+ICgoYyApIC8gNCkpKS50b1N0cmluZygxNiksCiAgKTsKfQoKLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVuc2FmZS1tZW1iZXItYWNjZXNzICovCi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkgKi8KCi8qKgogKiBIZWxwZXIgdG8gZGVjeWNsZSBqc29uIG9iamVjdHMKICovCmZ1bmN0aW9uIG1lbW9CdWlsZGVyKCkgewogIGNvbnN0IGhhc1dlYWtTZXQgPSB0eXBlb2YgV2Vha1NldCA9PT0gJ2Z1bmN0aW9uJzsKICBjb25zdCBpbm5lciA9IGhhc1dlYWtTZXQgPyBuZXcgV2Vha1NldCgpIDogW107CiAgZnVuY3Rpb24gbWVtb2l6ZShvYmopIHsKICAgIGlmIChoYXNXZWFrU2V0KSB7CiAgICAgIGlmIChpbm5lci5oYXMob2JqKSkgewogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9CiAgICAgIGlubmVyLmFkZChvYmopOwogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L3ByZWZlci1mb3Itb2YKICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5uZXIubGVuZ3RoOyBpKyspIHsKICAgICAgY29uc3QgdmFsdWUgPSBpbm5lcltpXTsKICAgICAgaWYgKHZhbHVlID09PSBvYmopIHsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfQogICAgfQogICAgaW5uZXIucHVzaChvYmopOwogICAgcmV0dXJuIGZhbHNlOwogIH0KCiAgZnVuY3Rpb24gdW5tZW1vaXplKG9iaikgewogICAgaWYgKGhhc1dlYWtTZXQpIHsKICAgICAgaW5uZXIuZGVsZXRlKG9iaik7CiAgICB9IGVsc2UgewogICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlubmVyLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgaWYgKGlubmVyW2ldID09PSBvYmopIHsKICAgICAgICAgIGlubmVyLnNwbGljZShpLCAxKTsKICAgICAgICAgIGJyZWFrOwogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KICByZXR1cm4gW21lbW9pemUsIHVubWVtb2l6ZV07Cn0KCi8qKgogKiBSZWN1cnNpdmVseSBub3JtYWxpemVzIHRoZSBnaXZlbiBvYmplY3QuCiAqCiAqIC0gQ3JlYXRlcyBhIGNvcHkgdG8gcHJldmVudCBvcmlnaW5hbCBpbnB1dCBtdXRhdGlvbgogKiAtIFNraXBzIG5vbi1lbnVtZXJhYmxlIHByb3BlcnRpZXMKICogLSBXaGVuIHN0cmluZ2lmeWluZywgY2FsbHMgYHRvSlNPTmAgaWYgaW1wbGVtZW50ZWQKICogLSBSZW1vdmVzIGNpcmN1bGFyIHJlZmVyZW5jZXMKICogLSBUcmFuc2xhdGVzIG5vbi1zZXJpYWxpemFibGUgdmFsdWVzIChgdW5kZWZpbmVkYC9gTmFOYC9mdW5jdGlvbnMpIHRvIHNlcmlhbGl6YWJsZSBmb3JtYXQKICogLSBUcmFuc2xhdGVzIGtub3duIGdsb2JhbCBvYmplY3RzL2NsYXNzZXMgdG8gYSBzdHJpbmcgcmVwcmVzZW50YXRpb25zCiAqIC0gVGFrZXMgY2FyZSBvZiBgRXJyb3JgIG9iamVjdCBzZXJpYWxpemF0aW9uCiAqIC0gT3B0aW9uYWxseSBsaW1pdHMgZGVwdGggb2YgZmluYWwgb3V0cHV0CiAqIC0gT3B0aW9uYWxseSBsaW1pdHMgbnVtYmVyIG9mIHByb3BlcnRpZXMvZWxlbWVudHMgaW5jbHVkZWQgaW4gYW55IHNpbmdsZSBvYmplY3QvYXJyYXkKICoKICogQHBhcmFtIGlucHV0IFRoZSBvYmplY3QgdG8gYmUgbm9ybWFsaXplZC4KICogQHBhcmFtIGRlcHRoIFRoZSBtYXggZGVwdGggdG8gd2hpY2ggdG8gbm9ybWFsaXplIHRoZSBvYmplY3QuIChBbnl0aGluZyBkZWVwZXIgc3RyaW5naWZpZWQgd2hvbGUuKQogKiBAcGFyYW0gbWF4UHJvcGVydGllcyBUaGUgbWF4IG51bWJlciBvZiBlbGVtZW50cyBvciBwcm9wZXJ0aWVzIHRvIGJlIGluY2x1ZGVkIGluIGFueSBzaW5nbGUgYXJyYXkgb3IKICogb2JqZWN0IGluIHRoZSBub3JtYWxsaXplZCBvdXRwdXQuCiAqIEByZXR1cm5zIEEgbm9ybWFsaXplZCB2ZXJzaW9uIG9mIHRoZSBvYmplY3QsIG9yIGAiKipub24tc2VyaWFsaXphYmxlKioiYCBpZiBhbnkgZXJyb3JzIGFyZSB0aHJvd24gZHVyaW5nIG5vcm1hbGl6YXRpb24uCiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQpmdW5jdGlvbiBub3JtYWxpemUoaW5wdXQsIGRlcHRoID0gMTAwLCBtYXhQcm9wZXJ0aWVzID0gK0luZmluaXR5KSB7CiAgdHJ5IHsKICAgIC8vIHNpbmNlIHdlJ3JlIGF0IHRoZSBvdXRlcm1vc3QgbGV2ZWwsIHdlIGRvbid0IHByb3ZpZGUgYSBrZXkKICAgIHJldHVybiB2aXNpdCgnJywgaW5wdXQsIGRlcHRoLCBtYXhQcm9wZXJ0aWVzKTsKICB9IGNhdGNoIChlcnIpIHsKICAgIHJldHVybiB7IEVSUk9SOiBgKipub24tc2VyaWFsaXphYmxlKiogKCR7ZXJyfSlgIH07CiAgfQp9CgovKioKICogVmlzaXRzIGEgbm9kZSB0byBwZXJmb3JtIG5vcm1hbGl6YXRpb24gb24gaXQKICoKICogQHBhcmFtIGtleSBUaGUga2V5IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGdpdmVuIG5vZGUKICogQHBhcmFtIHZhbHVlIFRoZSBub2RlIHRvIGJlIHZpc2l0ZWQKICogQHBhcmFtIGRlcHRoIE9wdGlvbmFsIG51bWJlciBpbmRpY2F0aW5nIHRoZSBtYXhpbXVtIHJlY3Vyc2lvbiBkZXB0aAogKiBAcGFyYW0gbWF4UHJvcGVydGllcyBPcHRpb25hbCBtYXhpbXVtIG51bWJlciBvZiBwcm9wZXJ0aWVzL2VsZW1lbnRzIGluY2x1ZGVkIGluIGFueSBzaW5nbGUgb2JqZWN0L2FycmF5CiAqIEBwYXJhbSBtZW1vIE9wdGlvbmFsIE1lbW8gY2xhc3MgaGFuZGxpbmcgZGVjeWNsaW5nCiAqLwpmdW5jdGlvbiB2aXNpdCgKICBrZXksCiAgdmFsdWUsCiAgZGVwdGggPSArSW5maW5pdHksCiAgbWF4UHJvcGVydGllcyA9ICtJbmZpbml0eSwKICBtZW1vID0gbWVtb0J1aWxkZXIoKSwKKSB7CiAgY29uc3QgW21lbW9pemUsIHVubWVtb2l6ZV0gPSBtZW1vOwoKICAvLyBHZXQgdGhlIHNpbXBsZSBjYXNlcyBvdXQgb2YgdGhlIHdheSBmaXJzdAogIGlmICgKICAgIHZhbHVlID09IG51bGwgfHwgLy8gdGhpcyBtYXRjaGVzIG51bGwgYW5kIHVuZGVmaW5lZCAtPiBlcWVxIG5vdCBlcWVxZXEKICAgIChbJ251bWJlcicsICdib29sZWFuJywgJ3N0cmluZyddLmluY2x1ZGVzKHR5cGVvZiB2YWx1ZSkgJiYgIWlzTmFOJDEodmFsdWUpKQogICkgewogICAgcmV0dXJuIHZhbHVlIDsKICB9CgogIGNvbnN0IHN0cmluZ2lmaWVkID0gc3RyaW5naWZ5VmFsdWUoa2V5LCB2YWx1ZSk7CgogIC8vIEFueXRoaW5nIHdlIGNvdWxkIHBvdGVudGlhbGx5IGRpZyBpbnRvIG1vcmUgKG9iamVjdHMgb3IgYXJyYXlzKSB3aWxsIGhhdmUgY29tZSBiYWNrIGFzIGAiW29iamVjdCBYWFhYXSJgLgogIC8vIEV2ZXJ5dGhpbmcgZWxzZSB3aWxsIGhhdmUgYWxyZWFkeSBiZWVuIHNlcmlhbGl6ZWQsIHNvIGlmIHdlIGRvbid0IHNlZSB0aGF0IHBhdHRlcm4sIHdlJ3JlIGRvbmUuCiAgaWYgKCFzdHJpbmdpZmllZC5zdGFydHNXaXRoKCdbb2JqZWN0ICcpKSB7CiAgICByZXR1cm4gc3RyaW5naWZpZWQ7CiAgfQoKICAvLyBGcm9tIGhlcmUgb24sIHdlIGNhbiBhc3NlcnQgdGhhdCBgdmFsdWVgIGlzIGVpdGhlciBhbiBvYmplY3Qgb3IgYW4gYXJyYXkuCgogIC8vIERvIG5vdCBub3JtYWxpemUgb2JqZWN0cyB0aGF0IHdlIGtub3cgaGF2ZSBhbHJlYWR5IGJlZW4gbm9ybWFsaXplZC4gQXMgYSBnZW5lcmFsIHJ1bGUsIHRoZQogIC8vICJfX3NlbnRyeV9za2lwX25vcm1hbGl6YXRpb25fXyIgcHJvcGVydHkgc2hvdWxkIG9ubHkgYmUgdXNlZCBzcGFyaW5nbHkgYW5kIG9ubHkgc2hvdWxkIG9ubHkgYmUgc2V0IG9uIG9iamVjdHMgdGhhdAogIC8vIGhhdmUgYWxyZWFkeSBiZWVuIG5vcm1hbGl6ZWQuCiAgaWYgKCh2YWx1ZSApWydfX3NlbnRyeV9za2lwX25vcm1hbGl6YXRpb25fXyddKSB7CiAgICByZXR1cm4gdmFsdWUgOwogIH0KCiAgLy8gV2UgY2FuIHNldCBgX19zZW50cnlfb3ZlcnJpZGVfbm9ybWFsaXphdGlvbl9kZXB0aF9fYCBvbiBhbiBvYmplY3QgdG8gZW5zdXJlIHRoYXQgZnJvbSB0aGVyZQogIC8vIFdlIGtlZXAgYSBjZXJ0YWluIGFtb3VudCBvZiBkZXB0aC4KICAvLyBUaGlzIHNob3VsZCBiZSB1c2VkIHNwYXJpbmdseSwgZS5nLiB3ZSB1c2UgaXQgZm9yIHRoZSByZWR1eCBpbnRlZ3JhdGlvbiB0byBlbnN1cmUgd2UgZ2V0IGEgY2VydGFpbiBhbW91bnQgb2Ygc3RhdGUuCiAgY29uc3QgcmVtYWluaW5nRGVwdGggPQogICAgdHlwZW9mICh2YWx1ZSApWydfX3NlbnRyeV9vdmVycmlkZV9ub3JtYWxpemF0aW9uX2RlcHRoX18nXSA9PT0gJ251bWJlcicKICAgICAgPyAoKHZhbHVlIClbJ19fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXyddICkKICAgICAgOiBkZXB0aDsKCiAgLy8gV2UncmUgYWxzbyBkb25lIGlmIHdlJ3ZlIHJlYWNoZWQgdGhlIG1heCBkZXB0aAogIGlmIChyZW1haW5pbmdEZXB0aCA9PT0gMCkgewogICAgLy8gQXQgdGhpcyBwb2ludCB3ZSBrbm93IGBzZXJpYWxpemVkYCBpcyBhIHN0cmluZyBvZiB0aGUgZm9ybSBgIltvYmplY3QgWFhYWF0iYC4gQ2xlYW4gaXQgdXAgc28gaXQncyBqdXN0IGAiW1hYWFhdImAuCiAgICByZXR1cm4gc3RyaW5naWZpZWQucmVwbGFjZSgnb2JqZWN0ICcsICcnKTsKICB9CgogIC8vIElmIHdlJ3ZlIGFscmVhZHkgdmlzaXRlZCB0aGlzIGJyYW5jaCwgYmFpbCBvdXQsIGFzIGl0J3MgY2lyY3VsYXIgcmVmZXJlbmNlLiBJZiBub3QsIG5vdGUgdGhhdCB3ZSdyZSBzZWVpbmcgaXQgbm93LgogIGlmIChtZW1vaXplKHZhbHVlKSkgewogICAgcmV0dXJuICdbQ2lyY3VsYXIgfl0nOwogIH0KCiAgLy8gSWYgdGhlIHZhbHVlIGhhcyBhIGB0b0pTT05gIG1ldGhvZCwgd2UgY2FsbCBpdCB0byBleHRyYWN0IG1vcmUgaW5mb3JtYXRpb24KICBjb25zdCB2YWx1ZVdpdGhUb0pTT04gPSB2YWx1ZSA7CiAgaWYgKHZhbHVlV2l0aFRvSlNPTiAmJiB0eXBlb2YgdmFsdWVXaXRoVG9KU09OLnRvSlNPTiA9PT0gJ2Z1bmN0aW9uJykgewogICAgdHJ5IHsKICAgICAgY29uc3QganNvblZhbHVlID0gdmFsdWVXaXRoVG9KU09OLnRvSlNPTigpOwogICAgICAvLyBXZSBuZWVkIHRvIG5vcm1hbGl6ZSB0aGUgcmV0dXJuIHZhbHVlIG9mIGAudG9KU09OKClgIGluIGNhc2UgaXQgaGFzIGNpcmN1bGFyIHJlZmVyZW5jZXMKICAgICAgcmV0dXJuIHZpc2l0KCcnLCBqc29uVmFsdWUsIHJlbWFpbmluZ0RlcHRoIC0gMSwgbWF4UHJvcGVydGllcywgbWVtbyk7CiAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgLy8gcGFzcyAoVGhlIGJ1aWx0LWluIGB0b0pTT05gIGZhaWxlZCwgYnV0IHdlIGNhbiBzdGlsbCB0cnkgdG8gZG8gaXQgb3Vyc2VsdmVzKQogICAgfQogIH0KCiAgLy8gQXQgdGhpcyBwb2ludCB3ZSBrbm93IHdlIGVpdGhlciBoYXZlIGFuIG9iamVjdCBvciBhbiBhcnJheSwgd2UgaGF2ZW4ndCBzZWVuIGl0IGJlZm9yZSwgYW5kIHdlJ3JlIGdvaW5nIHRvIHJlY3Vyc2UKICAvLyBiZWNhdXNlIHdlIGhhdmVuJ3QgeWV0IHJlYWNoZWQgdGhlIG1heCBkZXB0aC4gQ3JlYXRlIGFuIGFjY3VtdWxhdG9yIHRvIGhvbGQgdGhlIHJlc3VsdHMgb2YgdmlzaXRpbmcgZWFjaAogIC8vIHByb3BlcnR5L2VudHJ5LCBhbmQga2VlcCB0cmFjayBvZiB0aGUgbnVtYmVyIG9mIGl0ZW1zIHdlIGFkZCB0byBpdC4KICBjb25zdCBub3JtYWxpemVkID0gKEFycmF5LmlzQXJyYXkodmFsdWUpID8gW10gOiB7fSkgOwogIGxldCBudW1BZGRlZCA9IDA7CgogIC8vIEJlZm9yZSB3ZSBiZWdpbiwgY29udmVydGBFcnJvcmAgYW5kYEV2ZW50YCBpbnN0YW5jZXMgaW50byBwbGFpbiBvYmplY3RzLCBzaW5jZSBzb21lIG9mIGVhY2ggb2YgdGhlaXIgcmVsZXZhbnQKICAvLyBwcm9wZXJ0aWVzIGFyZSBub24tZW51bWVyYWJsZSBhbmQgb3RoZXJ3aXNlIHdvdWxkIGdldCBtaXNzZWQuCiAgY29uc3QgdmlzaXRhYmxlID0gY29udmVydFRvUGxhaW5PYmplY3QodmFsdWUgKTsKCiAgZm9yIChjb25zdCB2aXNpdEtleSBpbiB2aXNpdGFibGUpIHsKICAgIC8vIEF2b2lkIGl0ZXJhdGluZyBvdmVyIGZpZWxkcyBpbiB0aGUgcHJvdG90eXBlIGlmIHRoZXkndmUgc29tZWhvdyBiZWVuIGV4cG9zZWQgdG8gZW51bWVyYXRpb24uCiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh2aXNpdGFibGUsIHZpc2l0S2V5KSkgewogICAgICBjb250aW51ZTsKICAgIH0KCiAgICBpZiAobnVtQWRkZWQgPj0gbWF4UHJvcGVydGllcykgewogICAgICBub3JtYWxpemVkW3Zpc2l0S2V5XSA9ICdbTWF4UHJvcGVydGllcyB+XSc7CiAgICAgIGJyZWFrOwogICAgfQoKICAgIC8vIFJlY3Vyc2l2ZWx5IHZpc2l0IGFsbCB0aGUgY2hpbGQgbm9kZXMKICAgIGNvbnN0IHZpc2l0VmFsdWUgPSB2aXNpdGFibGVbdmlzaXRLZXldOwogICAgbm9ybWFsaXplZFt2aXNpdEtleV0gPSB2aXNpdCh2aXNpdEtleSwgdmlzaXRWYWx1ZSwgcmVtYWluaW5nRGVwdGggLSAxLCBtYXhQcm9wZXJ0aWVzLCBtZW1vKTsKCiAgICBudW1BZGRlZCsrOwogIH0KCiAgLy8gT25jZSB3ZSd2ZSB2aXNpdGVkIGFsbCB0aGUgYnJhbmNoZXMsIHJlbW92ZSB0aGUgcGFyZW50IGZyb20gbWVtbyBzdG9yYWdlCiAgdW5tZW1vaXplKHZhbHVlKTsKCiAgLy8gUmV0dXJuIGFjY3VtdWxhdGVkIHZhbHVlcwogIHJldHVybiBub3JtYWxpemVkOwp9CgovKiBlc2xpbnQtZGlzYWJsZSBjb21wbGV4aXR5ICovCi8qKgogKiBTdHJpbmdpZnkgdGhlIGdpdmVuIHZhbHVlLiBIYW5kbGVzIHZhcmlvdXMga25vd24gc3BlY2lhbCB2YWx1ZXMgYW5kIHR5cGVzLgogKgogKiBOb3QgbWVhbnQgdG8gYmUgdXNlZCBvbiBzaW1wbGUgcHJpbWl0aXZlcyB3aGljaCBhbHJlYWR5IGhhdmUgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24sIGFzIGl0IHdpbGwsIGZvciBleGFtcGxlLCB0dXJuCiAqIHRoZSBudW1iZXIgMTIzMSBpbnRvICJbT2JqZWN0IE51bWJlcl0iLCBub3Igb24gYG51bGxgLCBhcyBpdCB3aWxsIHRocm93LgogKgogKiBAcGFyYW0gdmFsdWUgVGhlIHZhbHVlIHRvIHN0cmluZ2lmeQogKiBAcmV0dXJucyBBIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBnaXZlbiB2YWx1ZQogKi8KZnVuY3Rpb24gc3RyaW5naWZ5VmFsdWUoCiAga2V5LAogIC8vIHRoaXMgdHlwZSBpcyBhIHRpbnkgYml0IG9mIGEgY2hlYXQsIHNpbmNlIHRoaXMgZnVuY3Rpb24gZG9lcyBoYW5kbGUgTmFOICh3aGljaCBpcyB0ZWNobmljYWxseSBhIG51bWJlciksIGJ1dCBmb3IKICAvLyBvdXIgaW50ZXJuYWwgdXNlLCBpdCdsbCBkbwogIHZhbHVlLAopIHsKICB0cnkgewogICAgaWYgKGtleSA9PT0gJ2RvbWFpbicgJiYgdmFsdWUgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiAodmFsdWUgKS5fZXZlbnRzKSB7CiAgICAgIHJldHVybiAnW0RvbWFpbl0nOwogICAgfQoKICAgIGlmIChrZXkgPT09ICdkb21haW5FbWl0dGVyJykgewogICAgICByZXR1cm4gJ1tEb21haW5FbWl0dGVyXSc7CiAgICB9CgogICAgLy8gSXQncyBzYWZlIHRvIHVzZSBgZ2xvYmFsYCwgYHdpbmRvd2AsIGFuZCBgZG9jdW1lbnRgIGhlcmUgaW4gdGhpcyBtYW5uZXIsIGFzIHdlIGFyZSBhc3NlcnRpbmcgdXNpbmcgYHR5cGVvZmAgZmlyc3QKICAgIC8vIHdoaWNoIHdvbid0IHRocm93IGlmIHRoZXkgYXJlIG5vdCBwcmVzZW50LgoKICAgIGlmICh0eXBlb2YgZ2xvYmFsICE9PSAndW5kZWZpbmVkJyAmJiB2YWx1ZSA9PT0gZ2xvYmFsKSB7CiAgICAgIHJldHVybiAnW0dsb2JhbF0nOwogICAgfQoKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLWdsb2JhbHMKICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB2YWx1ZSA9PT0gd2luZG93KSB7CiAgICAgIHJldHVybiAnW1dpbmRvd10nOwogICAgfQoKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLWdsb2JhbHMKICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnICYmIHZhbHVlID09PSBkb2N1bWVudCkgewogICAgICByZXR1cm4gJ1tEb2N1bWVudF0nOwogICAgfQoKICAgIGlmIChpc1Z1ZVZpZXdNb2RlbCh2YWx1ZSkpIHsKICAgICAgcmV0dXJuICdbVnVlVmlld01vZGVsXSc7CiAgICB9CgogICAgLy8gUmVhY3QncyBTeW50aGV0aWNFdmVudCB0aGluZ3kKICAgIGlmIChpc1N5bnRoZXRpY0V2ZW50KHZhbHVlKSkgewogICAgICByZXR1cm4gJ1tTeW50aGV0aWNFdmVudF0nOwogICAgfQoKICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInICYmIHZhbHVlICE9PSB2YWx1ZSkgewogICAgICByZXR1cm4gJ1tOYU5dJzsKICAgIH0KCiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7CiAgICAgIHJldHVybiBgW0Z1bmN0aW9uOiAke2dldEZ1bmN0aW9uTmFtZSh2YWx1ZSl9XWA7CiAgICB9CgogICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N5bWJvbCcpIHsKICAgICAgcmV0dXJuIGBbJHtTdHJpbmcodmFsdWUpfV1gOwogICAgfQoKICAgIC8vIHN0cmluZ2lmaWVkIEJpZ0ludHMgYXJlIGluZGlzdGluZ3Vpc2hhYmxlIGZyb20gcmVndWxhciBudW1iZXJzLCBzbyB3ZSBuZWVkIHRvIGxhYmVsIHRoZW0gdG8gYXZvaWQgY29uZnVzaW9uCiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnYmlnaW50JykgewogICAgICByZXR1cm4gYFtCaWdJbnQ6ICR7U3RyaW5nKHZhbHVlKX1dYDsKICAgIH0KCiAgICAvLyBOb3cgdGhhdCB3ZSd2ZSBrbm9ja2VkIG91dCBhbGwgdGhlIHNwZWNpYWwgY2FzZXMgYW5kIHRoZSBwcmltaXRpdmVzLCBhbGwgd2UgaGF2ZSBsZWZ0IGFyZSBvYmplY3RzLiBTaW1wbHkgY2FzdGluZwogICAgLy8gdGhlbSB0byBzdHJpbmdzIG1lYW5zIHRoYXQgaW5zdGFuY2VzIG9mIGNsYXNzZXMgd2hpY2ggaGF2ZW4ndCBkZWZpbmVkIHRoZWlyIGB0b1N0cmluZ1RhZ2Agd2lsbCBqdXN0IGNvbWUgb3V0IGFzCiAgICAvLyBgIltvYmplY3QgT2JqZWN0XSJgLiBJZiB3ZSBpbnN0ZWFkIGxvb2sgYXQgdGhlIGNvbnN0cnVjdG9yJ3MgbmFtZSAod2hpY2ggaXMgdGhlIHNhbWUgYXMgdGhlIG5hbWUgb2YgdGhlIGNsYXNzKSwKICAgIC8vIHdlIGNhbiBtYWtlIHN1cmUgdGhhdCBvbmx5IHBsYWluIG9iamVjdHMgY29tZSBvdXQgdGhhdCB3YXkuCiAgICBjb25zdCBvYmpOYW1lID0gZ2V0Q29uc3RydWN0b3JOYW1lKHZhbHVlKTsKCiAgICAvLyBIYW5kbGUgSFRNTCBFbGVtZW50cwogICAgaWYgKC9eSFRNTChcdyopRWxlbWVudCQvLnRlc3Qob2JqTmFtZSkpIHsKICAgICAgcmV0dXJuIGBbSFRNTEVsZW1lbnQ6ICR7b2JqTmFtZX1dYDsKICAgIH0KCiAgICByZXR1cm4gYFtvYmplY3QgJHtvYmpOYW1lfV1gOwogIH0gY2F0Y2ggKGVycikgewogICAgcmV0dXJuIGAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHtlcnJ9KWA7CiAgfQp9Ci8qIGVzbGludC1lbmFibGUgY29tcGxleGl0eSAqLwoKZnVuY3Rpb24gZ2V0Q29uc3RydWN0b3JOYW1lKHZhbHVlKSB7CiAgY29uc3QgcHJvdG90eXBlID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKHZhbHVlKTsKCiAgcmV0dXJuIHByb3RvdHlwZSA/IHByb3RvdHlwZS5jb25zdHJ1Y3Rvci5uYW1lIDogJ251bGwgcHJvdG90eXBlJzsKfQoKLyoqCiAqIE5vcm1hbGl6ZXMgVVJMcyBpbiBleGNlcHRpb25zIGFuZCBzdGFja3RyYWNlcyB0byBhIGJhc2UgcGF0aCBzbyBTZW50cnkgY2FuIGZpbmdlcnByaW50CiAqIGFjcm9zcyBwbGF0Zm9ybXMgYW5kIHdvcmtpbmcgZGlyZWN0b3J5LgogKgogKiBAcGFyYW0gdXJsIFRoZSBVUkwgdG8gYmUgbm9ybWFsaXplZC4KICogQHBhcmFtIGJhc2VQYXRoIFRoZSBhcHBsaWNhdGlvbiBiYXNlIHBhdGguCiAqIEByZXR1cm5zIFRoZSBub3JtYWxpemVkIFVSTC4KICovCmZ1bmN0aW9uIG5vcm1hbGl6ZVVybFRvQmFzZSh1cmwsIGJhc2VQYXRoKSB7CiAgY29uc3QgZXNjYXBlZEJhc2UgPSBiYXNlUGF0aAogICAgLy8gQmFja3NsYXNoIHRvIGZvcndhcmQKICAgIC5yZXBsYWNlKC9cXC9nLCAnLycpCiAgICAvLyBFc2NhcGUgUmVnRXhwIHNwZWNpYWwgY2hhcmFjdGVycwogICAgLnJlcGxhY2UoL1t8XFx7fSgpW1xdXiQrKj8uXS9nLCAnXFwkJicpOwoKICBsZXQgbmV3VXJsID0gdXJsOwogIHRyeSB7CiAgICBuZXdVcmwgPSBkZWNvZGVVUkkodXJsKTsKICB9IGNhdGNoIChfT28pIHsKICAgIC8vIFNvbWV0aW1lIHRoaXMgYnJlYWtzCiAgfQogIHJldHVybiAoCiAgICBuZXdVcmwKICAgICAgLnJlcGxhY2UoL1xcL2csICcvJykKICAgICAgLnJlcGxhY2UoL3dlYnBhY2s6XC8/L2csICcnKSAvLyBSZW1vdmUgaW50ZXJtZWRpYXRlIGJhc2UgcGF0aAogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHNlbnRyeS1pbnRlcm5hbC9zZGsvbm8tcmVnZXhwLWNvbnN0cnVjdG9yCiAgICAgIC5yZXBsYWNlKG5ldyBSZWdFeHAoYChmaWxlOi8vKT8vKiR7ZXNjYXBlZEJhc2V9LypgLCAnaWcnKSwgJ2FwcDovLy8nKQogICk7Cn0KCi8vIFNwbGl0IGEgZmlsZW5hbWUgaW50byBbcm9vdCwgZGlyLCBiYXNlbmFtZSwgZXh0XSwgdW5peCB2ZXJzaW9uCi8vICdyb290JyBpcyBqdXN0IGEgc2xhc2gsIG9yIG5vdGhpbmcuCmNvbnN0IHNwbGl0UGF0aFJlID0gL14oXFMrOlxcfFwvPykoW1xzXFNdKj8pKCg/OlwuezEsMn18W14vXFxdKz98KShcLlteLi9cXF0qfCkpKD86Wy9cXF0qKSQvOwovKiogSlNEb2MgKi8KZnVuY3Rpb24gc3BsaXRQYXRoKGZpbGVuYW1lKSB7CiAgLy8gVHJ1bmNhdGUgZmlsZXMgbmFtZXMgZ3JlYXRlciB0aGFuIDEwMjQgY2hhcmFjdGVycyB0byBhdm9pZCByZWdleCBkb3MKICAvLyBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0L3B1bGwvODczNyNkaXNjdXNzaW9uX3IxMjg1NzE5MTcyCiAgY29uc3QgdHJ1bmNhdGVkID0gZmlsZW5hbWUubGVuZ3RoID4gMTAyNCA/IGA8dHJ1bmNhdGVkPiR7ZmlsZW5hbWUuc2xpY2UoLTEwMjQpfWAgOiBmaWxlbmFtZTsKICBjb25zdCBwYXJ0cyA9IHNwbGl0UGF0aFJlLmV4ZWModHJ1bmNhdGVkKTsKICByZXR1cm4gcGFydHMgPyBwYXJ0cy5zbGljZSgxKSA6IFtdOwp9CgovKiogSlNEb2MgKi8KZnVuY3Rpb24gZGlybmFtZShwYXRoKSB7CiAgY29uc3QgcmVzdWx0ID0gc3BsaXRQYXRoKHBhdGgpOwogIGNvbnN0IHJvb3QgPSByZXN1bHRbMF07CiAgbGV0IGRpciA9IHJlc3VsdFsxXTsKCiAgaWYgKCFyb290ICYmICFkaXIpIHsKICAgIC8vIE5vIGRpcm5hbWUgd2hhdHNvZXZlcgogICAgcmV0dXJuICcuJzsKICB9CgogIGlmIChkaXIpIHsKICAgIC8vIEl0IGhhcyBhIGRpcm5hbWUsIHN0cmlwIHRyYWlsaW5nIHNsYXNoCiAgICBkaXIgPSBkaXIuc2xpY2UoMCwgZGlyLmxlbmd0aCAtIDEpOwogIH0KCiAgcmV0dXJuIHJvb3QgKyBkaXI7Cn0KCi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9leHBsaWNpdC1mdW5jdGlvbi1yZXR1cm4tdHlwZSAqLwovKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55ICovCgovKiogU3luY1Byb21pc2UgaW50ZXJuYWwgc3RhdGVzICovCnZhciBTdGF0ZXM7IChmdW5jdGlvbiAoU3RhdGVzKSB7CiAgLyoqIFBlbmRpbmcgKi8KICBjb25zdCBQRU5ESU5HID0gMDsgU3RhdGVzW1N0YXRlc1siUEVORElORyJdID0gUEVORElOR10gPSAiUEVORElORyI7CiAgLyoqIFJlc29sdmVkIC8gT0sgKi8KICBjb25zdCBSRVNPTFZFRCA9IDE7IFN0YXRlc1tTdGF0ZXNbIlJFU09MVkVEIl0gPSBSRVNPTFZFRF0gPSAiUkVTT0xWRUQiOwogIC8qKiBSZWplY3RlZCAvIEVycm9yICovCiAgY29uc3QgUkVKRUNURUQgPSAyOyBTdGF0ZXNbU3RhdGVzWyJSRUpFQ1RFRCJdID0gUkVKRUNURURdID0gIlJFSkVDVEVEIjsKfSkoU3RhdGVzIHx8IChTdGF0ZXMgPSB7fSkpOwoKLy8gT3ZlcmxvYWRzIHNvIHdlIGNhbiBjYWxsIHJlc29sdmVkU3luY1Byb21pc2Ugd2l0aG91dCBhcmd1bWVudHMgYW5kIGdlbmVyaWMgYXJndW1lbnQKCi8qKgogKiBDcmVhdGVzIGEgcmVzb2x2ZWQgc3luYyBwcm9taXNlLgogKgogKiBAcGFyYW0gdmFsdWUgdGhlIHZhbHVlIHRvIHJlc29sdmUgdGhlIHByb21pc2Ugd2l0aAogKiBAcmV0dXJucyB0aGUgcmVzb2x2ZWQgc3luYyBwcm9taXNlCiAqLwpmdW5jdGlvbiByZXNvbHZlZFN5bmNQcm9taXNlKHZhbHVlKSB7CiAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZShyZXNvbHZlID0+IHsKICAgIHJlc29sdmUodmFsdWUpOwogIH0pOwp9CgovKioKICogQ3JlYXRlcyBhIHJlamVjdGVkIHN5bmMgcHJvbWlzZS4KICoKICogQHBhcmFtIHZhbHVlIHRoZSB2YWx1ZSB0byByZWplY3QgdGhlIHByb21pc2Ugd2l0aAogKiBAcmV0dXJucyB0aGUgcmVqZWN0ZWQgc3luYyBwcm9taXNlCiAqLwpmdW5jdGlvbiByZWplY3RlZFN5bmNQcm9taXNlKHJlYXNvbikgewogIHJldHVybiBuZXcgU3luY1Byb21pc2UoKF8sIHJlamVjdCkgPT4gewogICAgcmVqZWN0KHJlYXNvbik7CiAgfSk7Cn0KCi8qKgogKiBUaGVuYWJsZSBjbGFzcyB0aGF0IGJlaGF2ZXMgbGlrZSBhIFByb21pc2UgYW5kIGZvbGxvd3MgaXQncyBpbnRlcmZhY2UKICogYnV0IGlzIG5vdCBhc3luYyBpbnRlcm5hbGx5CiAqLwpjbGFzcyBTeW5jUHJvbWlzZSB7CgogICBjb25zdHJ1Y3RvcigKICAgIGV4ZWN1dG9yLAogICkge1N5bmNQcm9taXNlLnByb3RvdHlwZS5fX2luaXQuY2FsbCh0aGlzKTtTeW5jUHJvbWlzZS5wcm90b3R5cGUuX19pbml0Mi5jYWxsKHRoaXMpO1N5bmNQcm9taXNlLnByb3RvdHlwZS5fX2luaXQzLmNhbGwodGhpcyk7U3luY1Byb21pc2UucHJvdG90eXBlLl9faW5pdDQuY2FsbCh0aGlzKTsKICAgIHRoaXMuX3N0YXRlID0gU3RhdGVzLlBFTkRJTkc7CiAgICB0aGlzLl9oYW5kbGVycyA9IFtdOwoKICAgIHRyeSB7CiAgICAgIGV4ZWN1dG9yKHRoaXMuX3Jlc29sdmUsIHRoaXMuX3JlamVjdCk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHRoaXMuX3JlamVjdChlKTsKICAgIH0KICB9CgogIC8qKiBKU0RvYyAqLwogICB0aGVuKAogICAgb25mdWxmaWxsZWQsCiAgICBvbnJlamVjdGVkLAogICkgewogICAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIHRoaXMuX2hhbmRsZXJzLnB1c2goWwogICAgICAgIGZhbHNlLAogICAgICAgIHJlc3VsdCA9PiB7CiAgICAgICAgICBpZiAoIW9uZnVsZmlsbGVkKSB7CiAgICAgICAgICAgIC8vIFRPRE86IMKvXF8o44OEKV8vwq8KICAgICAgICAgICAgLy8gVE9ETzogRklYTUUKICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQgKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgcmVzb2x2ZShvbmZ1bGZpbGxlZChyZXN1bHQpKTsKICAgICAgICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgICAgICAgIHJlamVjdChlKTsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgcmVhc29uID0+IHsKICAgICAgICAgIGlmICghb25yZWplY3RlZCkgewogICAgICAgICAgICByZWplY3QocmVhc29uKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgcmVzb2x2ZShvbnJlamVjdGVkKHJlYXNvbikpOwogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgcmVqZWN0KGUpOwogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgXSk7CiAgICAgIHRoaXMuX2V4ZWN1dGVIYW5kbGVycygpOwogICAgfSk7CiAgfQoKICAvKiogSlNEb2MgKi8KICAgY2F0Y2goCiAgICBvbnJlamVjdGVkLAogICkgewogICAgcmV0dXJuIHRoaXMudGhlbih2YWwgPT4gdmFsLCBvbnJlamVjdGVkKTsKICB9CgogIC8qKiBKU0RvYyAqLwogICBmaW5hbGx5KG9uZmluYWxseSkgewogICAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIGxldCB2YWw7CiAgICAgIGxldCBpc1JlamVjdGVkOwoKICAgICAgcmV0dXJuIHRoaXMudGhlbigKICAgICAgICB2YWx1ZSA9PiB7CiAgICAgICAgICBpc1JlamVjdGVkID0gZmFsc2U7CiAgICAgICAgICB2YWwgPSB2YWx1ZTsKICAgICAgICAgIGlmIChvbmZpbmFsbHkpIHsKICAgICAgICAgICAgb25maW5hbGx5KCk7CiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICByZWFzb24gPT4gewogICAgICAgICAgaXNSZWplY3RlZCA9IHRydWU7CiAgICAgICAgICB2YWwgPSByZWFzb247CiAgICAgICAgICBpZiAob25maW5hbGx5KSB7CiAgICAgICAgICAgIG9uZmluYWxseSgpOwogICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICkudGhlbigoKSA9PiB7CiAgICAgICAgaWYgKGlzUmVqZWN0ZWQpIHsKICAgICAgICAgIHJlamVjdCh2YWwpOwogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KCiAgICAgICAgcmVzb2x2ZSh2YWwgKTsKICAgICAgfSk7CiAgICB9KTsKICB9CgogIC8qKiBKU0RvYyAqLwogICAgX19pbml0KCkge3RoaXMuX3Jlc29sdmUgPSAodmFsdWUpID0+IHsKICAgIHRoaXMuX3NldFJlc3VsdChTdGF0ZXMuUkVTT0xWRUQsIHZhbHVlKTsKICB9O30KCiAgLyoqIEpTRG9jICovCiAgICBfX2luaXQyKCkge3RoaXMuX3JlamVjdCA9IChyZWFzb24pID0+IHsKICAgIHRoaXMuX3NldFJlc3VsdChTdGF0ZXMuUkVKRUNURUQsIHJlYXNvbik7CiAgfTt9CgogIC8qKiBKU0RvYyAqLwogICAgX19pbml0MygpIHt0aGlzLl9zZXRSZXN1bHQgPSAoc3RhdGUsIHZhbHVlKSA9PiB7CiAgICBpZiAodGhpcy5fc3RhdGUgIT09IFN0YXRlcy5QRU5ESU5HKSB7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBpZiAoaXNUaGVuYWJsZSh2YWx1ZSkpIHsKICAgICAgdm9pZCAodmFsdWUgKS50aGVuKHRoaXMuX3Jlc29sdmUsIHRoaXMuX3JlamVjdCk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICB0aGlzLl9zdGF0ZSA9IHN0YXRlOwogICAgdGhpcy5fdmFsdWUgPSB2YWx1ZTsKCiAgICB0aGlzLl9leGVjdXRlSGFuZGxlcnMoKTsKICB9O30KCiAgLyoqIEpTRG9jICovCiAgICBfX2luaXQ0KCkge3RoaXMuX2V4ZWN1dGVIYW5kbGVycyA9ICgpID0+IHsKICAgIGlmICh0aGlzLl9zdGF0ZSA9PT0gU3RhdGVzLlBFTkRJTkcpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIGNvbnN0IGNhY2hlZEhhbmRsZXJzID0gdGhpcy5faGFuZGxlcnMuc2xpY2UoKTsKICAgIHRoaXMuX2hhbmRsZXJzID0gW107CgogICAgY2FjaGVkSGFuZGxlcnMuZm9yRWFjaChoYW5kbGVyID0+IHsKICAgICAgaWYgKGhhbmRsZXJbMF0pIHsKICAgICAgICByZXR1cm47CiAgICAgIH0KCiAgICAgIGlmICh0aGlzLl9zdGF0ZSA9PT0gU3RhdGVzLlJFU09MVkVEKSB7CiAgICAgICAgaGFuZGxlclsxXSh0aGlzLl92YWx1ZSApOwogICAgICB9CgogICAgICBpZiAodGhpcy5fc3RhdGUgPT09IFN0YXRlcy5SRUpFQ1RFRCkgewogICAgICAgIGhhbmRsZXJbMl0odGhpcy5fdmFsdWUpOwogICAgICB9CgogICAgICBoYW5kbGVyWzBdID0gdHJ1ZTsKICAgIH0pOwogIH07fQp9CgovKioKICogQ3JlYXRlcyBhbiBuZXcgUHJvbWlzZUJ1ZmZlciBvYmplY3Qgd2l0aCB0aGUgc3BlY2lmaWVkIGxpbWl0CiAqIEBwYXJhbSBsaW1pdCBtYXggbnVtYmVyIG9mIHByb21pc2VzIHRoYXQgY2FuIGJlIHN0b3JlZCBpbiB0aGUgYnVmZmVyCiAqLwpmdW5jdGlvbiBtYWtlUHJvbWlzZUJ1ZmZlcihsaW1pdCkgewogIGNvbnN0IGJ1ZmZlciA9IFtdOwoKICBmdW5jdGlvbiBpc1JlYWR5KCkgewogICAgcmV0dXJuIGxpbWl0ID09PSB1bmRlZmluZWQgfHwgYnVmZmVyLmxlbmd0aCA8IGxpbWl0OwogIH0KCiAgLyoqCiAgICogUmVtb3ZlIGEgcHJvbWlzZSBmcm9tIHRoZSBxdWV1ZS4KICAgKgogICAqIEBwYXJhbSB0YXNrIENhbiBiZSBhbnkgUHJvbWlzZUxpa2U8VD4KICAgKiBAcmV0dXJucyBSZW1vdmVkIHByb21pc2UuCiAgICovCiAgZnVuY3Rpb24gcmVtb3ZlKHRhc2spIHsKICAgIHJldHVybiBidWZmZXIuc3BsaWNlKGJ1ZmZlci5pbmRleE9mKHRhc2spLCAxKVswXTsKICB9CgogIC8qKgogICAqIEFkZCBhIHByb21pc2UgKHJlcHJlc2VudGluZyBhbiBpbi1mbGlnaHQgYWN0aW9uKSB0byB0aGUgcXVldWUsIGFuZCBzZXQgaXQgdG8gcmVtb3ZlIGl0c2VsZiBvbiBmdWxmaWxsbWVudC4KICAgKgogICAqIEBwYXJhbSB0YXNrUHJvZHVjZXIgQSBmdW5jdGlvbiBwcm9kdWNpbmcgYW55IFByb21pc2VMaWtlPFQ+OyBJbiBwcmV2aW91cyB2ZXJzaW9ucyB0aGlzIHVzZWQgdG8gYmUgYHRhc2s6CiAgICogICAgICAgIFByb21pc2VMaWtlPFQ+YCwgYnV0IHVuZGVyIHRoYXQgbW9kZWwsIFByb21pc2VzIHdlcmUgaW5zdGFudGx5IGNyZWF0ZWQgb24gdGhlIGNhbGwtc2l0ZSBhbmQgdGhlaXIgZXhlY3V0b3IKICAgKiAgICAgICAgZnVuY3Rpb25zIHRoZXJlZm9yZSByYW4gaW1tZWRpYXRlbHkuIFRodXMsIGV2ZW4gaWYgdGhlIGJ1ZmZlciB3YXMgZnVsbCwgdGhlIGFjdGlvbiBzdGlsbCBoYXBwZW5lZC4gQnkKICAgKiAgICAgICAgcmVxdWlyaW5nIHRoZSBwcm9taXNlIHRvIGJlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgd2UgY2FuIGRlZmVyIHByb21pc2UgY3JlYXRpb24gdW50aWwgYWZ0ZXIgdGhlIGJ1ZmZlcgogICAqICAgICAgICBsaW1pdCBjaGVjay4KICAgKiBAcmV0dXJucyBUaGUgb3JpZ2luYWwgcHJvbWlzZS4KICAgKi8KICBmdW5jdGlvbiBhZGQodGFza1Byb2R1Y2VyKSB7CiAgICBpZiAoIWlzUmVhZHkoKSkgewogICAgICByZXR1cm4gcmVqZWN0ZWRTeW5jUHJvbWlzZShuZXcgU2VudHJ5RXJyb3IoJ05vdCBhZGRpbmcgUHJvbWlzZSBiZWNhdXNlIGJ1ZmZlciBsaW1pdCB3YXMgcmVhY2hlZC4nKSk7CiAgICB9CgogICAgLy8gc3RhcnQgdGhlIHRhc2sgYW5kIGFkZCBpdHMgcHJvbWlzZSB0byB0aGUgcXVldWUKICAgIGNvbnN0IHRhc2sgPSB0YXNrUHJvZHVjZXIoKTsKICAgIGlmIChidWZmZXIuaW5kZXhPZih0YXNrKSA9PT0gLTEpIHsKICAgICAgYnVmZmVyLnB1c2godGFzayk7CiAgICB9CiAgICB2b2lkIHRhc2sKICAgICAgLnRoZW4oKCkgPT4gcmVtb3ZlKHRhc2spKQogICAgICAvLyBVc2UgYHRoZW4obnVsbCwgcmVqZWN0aW9uSGFuZGxlcilgIHJhdGhlciB0aGFuIGBjYXRjaChyZWplY3Rpb25IYW5kbGVyKWAgc28gdGhhdCB3ZSBjYW4gdXNlIGBQcm9taXNlTGlrZWAKICAgICAgLy8gcmF0aGVyIHRoYW4gYFByb21pc2VgLiBgUHJvbWlzZUxpa2VgIGRvZXNuJ3QgaGF2ZSBhIGAuY2F0Y2hgIG1ldGhvZCwgbWFraW5nIGl0cyBwb2x5ZmlsbCBzbWFsbGVyLiAoRVM1IGRpZG4ndAogICAgICAvLyBoYXZlIHByb21pc2VzLCBzbyBUUyBoYXMgdG8gcG9seWZpbGwgd2hlbiBkb3duLWNvbXBpbGluZy4pCiAgICAgIC50aGVuKG51bGwsICgpID0+CiAgICAgICAgcmVtb3ZlKHRhc2spLnRoZW4obnVsbCwgKCkgPT4gewogICAgICAgICAgLy8gV2UgaGF2ZSB0byBhZGQgYW5vdGhlciBjYXRjaCBoZXJlIGJlY2F1c2UgYHJlbW92ZSgpYCBzdGFydHMgYSBuZXcgcHJvbWlzZSBjaGFpbi4KICAgICAgICB9KSwKICAgICAgKTsKICAgIHJldHVybiB0YXNrOwogIH0KCiAgLyoqCiAgICogV2FpdCBmb3IgYWxsIHByb21pc2VzIGluIHRoZSBxdWV1ZSB0byByZXNvbHZlIG9yIGZvciB0aW1lb3V0IHRvIGV4cGlyZSwgd2hpY2hldmVyIGNvbWVzIGZpcnN0LgogICAqCiAgICogQHBhcmFtIHRpbWVvdXQgVGhlIHRpbWUsIGluIG1zLCBhZnRlciB3aGljaCB0byByZXNvbHZlIHRvIGBmYWxzZWAgaWYgdGhlIHF1ZXVlIGlzIHN0aWxsIG5vbi1lbXB0eS4gUGFzc2luZyBgMGAgKG9yCiAgICogbm90IHBhc3NpbmcgYW55dGhpbmcpIHdpbGwgbWFrZSB0aGUgcHJvbWlzZSB3YWl0IGFzIGxvbmcgYXMgaXQgdGFrZXMgZm9yIHRoZSBxdWV1ZSB0byBkcmFpbiBiZWZvcmUgcmVzb2x2aW5nIHRvCiAgICogYHRydWVgLgogICAqIEByZXR1cm5zIEEgcHJvbWlzZSB3aGljaCB3aWxsIHJlc29sdmUgdG8gYHRydWVgIGlmIHRoZSBxdWV1ZSBpcyBhbHJlYWR5IGVtcHR5IG9yIGRyYWlucyBiZWZvcmUgdGhlIHRpbWVvdXQsIGFuZAogICAqIGBmYWxzZWAgb3RoZXJ3aXNlCiAgICovCiAgZnVuY3Rpb24gZHJhaW4odGltZW91dCkgewogICAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIGxldCBjb3VudGVyID0gYnVmZmVyLmxlbmd0aDsKCiAgICAgIGlmICghY291bnRlcikgewogICAgICAgIHJldHVybiByZXNvbHZlKHRydWUpOwogICAgICB9CgogICAgICAvLyB3YWl0IGZvciBgdGltZW91dGAgbXMgYW5kIHRoZW4gcmVzb2x2ZSB0byBgZmFsc2VgIChpZiBub3QgY2FuY2VsbGVkIGZpcnN0KQogICAgICBjb25zdCBjYXB0dXJlZFNldFRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHsKICAgICAgICBpZiAodGltZW91dCAmJiB0aW1lb3V0ID4gMCkgewogICAgICAgICAgcmVzb2x2ZShmYWxzZSk7CiAgICAgICAgfQogICAgICB9LCB0aW1lb3V0KTsKCiAgICAgIC8vIGlmIGFsbCBwcm9taXNlcyByZXNvbHZlIGluIHRpbWUsIGNhbmNlbCB0aGUgdGltZXIgYW5kIHJlc29sdmUgdG8gYHRydWVgCiAgICAgIGJ1ZmZlci5mb3JFYWNoKGl0ZW0gPT4gewogICAgICAgIHZvaWQgcmVzb2x2ZWRTeW5jUHJvbWlzZShpdGVtKS50aGVuKCgpID0+IHsKICAgICAgICAgIGlmICghLS1jb3VudGVyKSB7CiAgICAgICAgICAgIGNsZWFyVGltZW91dChjYXB0dXJlZFNldFRpbWVvdXQpOwogICAgICAgICAgICByZXNvbHZlKHRydWUpOwogICAgICAgICAgfQogICAgICAgIH0sIHJlamVjdCk7CiAgICAgIH0pOwogICAgfSk7CiAgfQoKICByZXR1cm4gewogICAgJDogYnVmZmVyLAogICAgYWRkLAogICAgZHJhaW4sCiAgfTsKfQoKLyoqCiAqIFRoaXMgY29kZSB3YXMgb3JpZ2luYWxseSBjb3BpZWQgZnJvbSB0aGUgJ2Nvb2tpZWAgbW9kdWxlIGF0IHYwLjUuMCBhbmQgd2FzIHNpbXBsaWZpZWQgZm9yIG91ciB1c2UgY2FzZS4KICogaHR0cHM6Ly9naXRodWIuY29tL2pzaHR0cC9jb29raWUvYmxvYi9hMGM4NDE0N2FhYjYyNjZiZGIzOTk2Y2Y0MDYyZTkzOTA3YzBiMGZjL2luZGV4LmpzCiAqIEl0IGhhZCB0aGUgZm9sbG93aW5nIGxpY2Vuc2U6CiAqCiAqIChUaGUgTUlUIExpY2Vuc2UpCiAqCiAqIENvcHlyaWdodCAoYykgMjAxMi0yMDE0IFJvbWFuIFNodHlsbWFuIDxzaHR5bG1hbkBnbWFpbC5jb20+CiAqIENvcHlyaWdodCAoYykgMjAxNSBEb3VnbGFzIENocmlzdG9waGVyIFdpbHNvbiA8ZG91Z0Bzb21ldGhpbmdkb3VnLmNvbT4KICoKICogUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nCiAqIGEgY29weSBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZQogKiAnU29mdHdhcmUnKSwgdG8gZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nCiAqIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwKICogZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvCiAqIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0bwogKiB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6CiAqCiAqIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlCiAqIGluY2x1ZGVkIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLgogKgogKiBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgJ0FTIElTJywgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwKICogRVhQUkVTUyBPUiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GCiAqIE1FUkNIQU5UQUJJTElUWSwgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4KICogSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkKICogQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwKICogVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUKICogU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUgU09GVFdBUkUuCiAqLwoKLyoqCiAqIFBhcnNlcyBhIGNvb2tpZSBzdHJpbmcKICovCmZ1bmN0aW9uIHBhcnNlQ29va2llKHN0cikgewogIGNvbnN0IG9iaiA9IHt9OwogIGxldCBpbmRleCA9IDA7CgogIHdoaWxlIChpbmRleCA8IHN0ci5sZW5ndGgpIHsKICAgIGNvbnN0IGVxSWR4ID0gc3RyLmluZGV4T2YoJz0nLCBpbmRleCk7CgogICAgLy8gbm8gbW9yZSBjb29raWUgcGFpcnMKICAgIGlmIChlcUlkeCA9PT0gLTEpIHsKICAgICAgYnJlYWs7CiAgICB9CgogICAgbGV0IGVuZElkeCA9IHN0ci5pbmRleE9mKCc7JywgaW5kZXgpOwoKICAgIGlmIChlbmRJZHggPT09IC0xKSB7CiAgICAgIGVuZElkeCA9IHN0ci5sZW5ndGg7CiAgICB9IGVsc2UgaWYgKGVuZElkeCA8IGVxSWR4KSB7CiAgICAgIC8vIGJhY2t0cmFjayBvbiBwcmlvciBzZW1pY29sb24KICAgICAgaW5kZXggPSBzdHIubGFzdEluZGV4T2YoJzsnLCBlcUlkeCAtIDEpICsgMTsKICAgICAgY29udGludWU7CiAgICB9CgogICAgY29uc3Qga2V5ID0gc3RyLnNsaWNlKGluZGV4LCBlcUlkeCkudHJpbSgpOwoKICAgIC8vIG9ubHkgYXNzaWduIG9uY2UKICAgIGlmICh1bmRlZmluZWQgPT09IG9ialtrZXldKSB7CiAgICAgIGxldCB2YWwgPSBzdHIuc2xpY2UoZXFJZHggKyAxLCBlbmRJZHgpLnRyaW0oKTsKCiAgICAgIC8vIHF1b3RlZCB2YWx1ZXMKICAgICAgaWYgKHZhbC5jaGFyQ29kZUF0KDApID09PSAweDIyKSB7CiAgICAgICAgdmFsID0gdmFsLnNsaWNlKDEsIC0xKTsKICAgICAgfQoKICAgICAgdHJ5IHsKICAgICAgICBvYmpba2V5XSA9IHZhbC5pbmRleE9mKCclJykgIT09IC0xID8gZGVjb2RlVVJJQ29tcG9uZW50KHZhbCkgOiB2YWw7CiAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICBvYmpba2V5XSA9IHZhbDsKICAgICAgfQogICAgfQoKICAgIGluZGV4ID0gZW5kSWR4ICsgMTsKICB9CgogIHJldHVybiBvYmo7Cn0KCi8qKgogKiBTdHJpcCB0aGUgcXVlcnkgc3RyaW5nIGFuZCBmcmFnbWVudCBvZmYgb2YgYSBnaXZlbiBVUkwgb3IgcGF0aCAoaWYgcHJlc2VudCkKICoKICogQHBhcmFtIHVybFBhdGggRnVsbCBVUkwgb3IgcGF0aCwgaW5jbHVkaW5nIHBvc3NpYmxlIHF1ZXJ5IHN0cmluZyBhbmQvb3IgZnJhZ21lbnQKICogQHJldHVybnMgVVJMIG9yIHBhdGggd2l0aG91dCBxdWVyeSBzdHJpbmcgb3IgZnJhZ21lbnQKICovCmZ1bmN0aW9uIHN0cmlwVXJsUXVlcnlBbmRGcmFnbWVudCh1cmxQYXRoKSB7CiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVzZWxlc3MtZXNjYXBlCiAgcmV0dXJuIHVybFBhdGguc3BsaXQoL1tcPyNdLywgMSlbMF07Cn0KCmNvbnN0IERFRkFVTFRfSU5DTFVERVMgPSB7CiAgaXA6IGZhbHNlLAogIHJlcXVlc3Q6IHRydWUsCiAgdHJhbnNhY3Rpb246IHRydWUsCiAgdXNlcjogdHJ1ZSwKfTsKY29uc3QgREVGQVVMVF9SRVFVRVNUX0lOQ0xVREVTID0gWydjb29raWVzJywgJ2RhdGEnLCAnaGVhZGVycycsICdtZXRob2QnLCAncXVlcnlfc3RyaW5nJywgJ3VybCddOwpjb25zdCBERUZBVUxUX1VTRVJfSU5DTFVERVMgPSBbJ2lkJywgJ3VzZXJuYW1lJywgJ2VtYWlsJ107CgovKioKICogRXh0cmFjdHMgYSBjb21wbGV0ZSBhbmQgcGFyYW1ldGVyaXplZCBwYXRoIGZyb20gdGhlIHJlcXVlc3Qgb2JqZWN0IGFuZCB1c2VzIGl0IHRvIGNvbnN0cnVjdCB0cmFuc2FjdGlvbiBuYW1lLgogKiBJZiB0aGUgcGFyYW1ldGVyaXplZCB0cmFuc2FjdGlvbiBuYW1lIGNhbm5vdCBiZSBleHRyYWN0ZWQsIHdlIGZhbGwgYmFjayB0byB0aGUgcmF3IFVSTC4KICoKICogQWRkaXRpb25hbGx5LCB0aGlzIGZ1bmN0aW9uIGRldGVybWluZXMgYW5kIHJldHVybnMgdGhlIHRyYW5zYWN0aW9uIG5hbWUgc291cmNlCiAqCiAqIGVnLiBHRVQgL21vdW50cG9pbnQvdXNlci86aWQKICoKICogQHBhcmFtIHJlcSBBIHJlcXVlc3Qgb2JqZWN0CiAqIEBwYXJhbSBvcHRpb25zIFdoYXQgdG8gaW5jbHVkZSBpbiB0aGUgdHJhbnNhY3Rpb24gbmFtZSAobWV0aG9kLCBwYXRoLCBvciBhIGN1c3RvbSByb3V0ZSBuYW1lIHRvIGJlCiAqICAgICAgICAgICAgICAgIHVzZWQgaW5zdGVhZCBvZiB0aGUgcmVxdWVzdCdzIHJvdXRlKQogKgogKiBAcmV0dXJucyBBIHR1cGxlIG9mIHRoZSBmdWxseSBjb25zdHJ1Y3RlZCB0cmFuc2FjdGlvbiBuYW1lIFswXSBhbmQgaXRzIHNvdXJjZSBbMV0gKGNhbiBiZSBlaXRoZXIgJ3JvdXRlJyBvciAndXJsJykKICovCmZ1bmN0aW9uIGV4dHJhY3RQYXRoRm9yVHJhbnNhY3Rpb24oCiAgcmVxLAogIG9wdGlvbnMgPSB7fSwKKSB7CiAgY29uc3QgbWV0aG9kID0gcmVxLm1ldGhvZCAmJiByZXEubWV0aG9kLnRvVXBwZXJDYXNlKCk7CgogIGxldCBwYXRoID0gJyc7CiAgbGV0IHNvdXJjZSA9ICd1cmwnOwoKICAvLyBDaGVjayB0byBzZWUgaWYgdGhlcmUncyBhIHBhcmFtZXRlcml6ZWQgcm91dGUgd2UgY2FuIHVzZSAoYXMgdGhlcmUgaXMgaW4gRXhwcmVzcykKICBpZiAob3B0aW9ucy5jdXN0b21Sb3V0ZSB8fCByZXEucm91dGUpIHsKICAgIHBhdGggPSBvcHRpb25zLmN1c3RvbVJvdXRlIHx8IGAke3JlcS5iYXNlVXJsIHx8ICcnfSR7cmVxLnJvdXRlICYmIHJlcS5yb3V0ZS5wYXRofWA7CiAgICBzb3VyY2UgPSAncm91dGUnOwogIH0KCiAgLy8gT3RoZXJ3aXNlLCBqdXN0IHRha2UgdGhlIG9yaWdpbmFsIFVSTAogIGVsc2UgaWYgKHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsKSB7CiAgICBwYXRoID0gc3RyaXBVcmxRdWVyeUFuZEZyYWdtZW50KHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsIHx8ICcnKTsKICB9CgogIGxldCBuYW1lID0gJyc7CiAgaWYgKG9wdGlvbnMubWV0aG9kICYmIG1ldGhvZCkgewogICAgbmFtZSArPSBtZXRob2Q7CiAgfQogIGlmIChvcHRpb25zLm1ldGhvZCAmJiBvcHRpb25zLnBhdGgpIHsKICAgIG5hbWUgKz0gJyAnOwogIH0KICBpZiAob3B0aW9ucy5wYXRoICYmIHBhdGgpIHsKICAgIG5hbWUgKz0gcGF0aDsKICB9CgogIHJldHVybiBbbmFtZSwgc291cmNlXTsKfQoKLyoqIEpTRG9jICovCmZ1bmN0aW9uIGV4dHJhY3RUcmFuc2FjdGlvbihyZXEsIHR5cGUpIHsKICBzd2l0Y2ggKHR5cGUpIHsKICAgIGNhc2UgJ3BhdGgnOiB7CiAgICAgIHJldHVybiBleHRyYWN0UGF0aEZvclRyYW5zYWN0aW9uKHJlcSwgeyBwYXRoOiB0cnVlIH0pWzBdOwogICAgfQogICAgY2FzZSAnaGFuZGxlcic6IHsKICAgICAgcmV0dXJuIChyZXEucm91dGUgJiYgcmVxLnJvdXRlLnN0YWNrICYmIHJlcS5yb3V0ZS5zdGFja1swXSAmJiByZXEucm91dGUuc3RhY2tbMF0ubmFtZSkgfHwgJzxhbm9ueW1vdXM+JzsKICAgIH0KICAgIGNhc2UgJ21ldGhvZFBhdGgnOgogICAgZGVmYXVsdDogewogICAgICAvLyBpZiBleGlzdCBfcmVjb25zdHJ1Y3RlZFJvdXRlIHJldHVybiB0aGF0IHBhdGggaW5zdGVhZCBvZiByb3V0ZS5wYXRoCiAgICAgIGNvbnN0IGN1c3RvbVJvdXRlID0gcmVxLl9yZWNvbnN0cnVjdGVkUm91dGUgPyByZXEuX3JlY29uc3RydWN0ZWRSb3V0ZSA6IHVuZGVmaW5lZDsKICAgICAgcmV0dXJuIGV4dHJhY3RQYXRoRm9yVHJhbnNhY3Rpb24ocmVxLCB7IHBhdGg6IHRydWUsIG1ldGhvZDogdHJ1ZSwgY3VzdG9tUm91dGUgfSlbMF07CiAgICB9CiAgfQp9CgovKiogSlNEb2MgKi8KZnVuY3Rpb24gZXh0cmFjdFVzZXJEYXRhKAogIHVzZXIKCiwKICBrZXlzLAopIHsKICBjb25zdCBleHRyYWN0ZWRVc2VyID0ge307CiAgY29uc3QgYXR0cmlidXRlcyA9IEFycmF5LmlzQXJyYXkoa2V5cykgPyBrZXlzIDogREVGQVVMVF9VU0VSX0lOQ0xVREVTOwoKICBhdHRyaWJ1dGVzLmZvckVhY2goa2V5ID0+IHsKICAgIGlmICh1c2VyICYmIGtleSBpbiB1c2VyKSB7CiAgICAgIGV4dHJhY3RlZFVzZXJba2V5XSA9IHVzZXJba2V5XTsKICAgIH0KICB9KTsKCiAgcmV0dXJuIGV4dHJhY3RlZFVzZXI7Cn0KCi8qKgogKiBOb3JtYWxpemUgZGF0YSBmcm9tIHRoZSByZXF1ZXN0IG9iamVjdCwgYWNjb3VudGluZyBmb3IgZnJhbWV3b3JrIGRpZmZlcmVuY2VzLgogKgogKiBAcGFyYW0gcmVxIFRoZSByZXF1ZXN0IG9iamVjdCBmcm9tIHdoaWNoIHRvIGV4dHJhY3QgZGF0YQogKiBAcGFyYW0gb3B0aW9ucy5pbmNsdWRlIEFuIG9wdGlvbmFsIGFycmF5IG9mIGtleXMgdG8gaW5jbHVkZSBpbiB0aGUgbm9ybWFsaXplZCBkYXRhLiBEZWZhdWx0cyB0bwogKiBERUZBVUxUX1JFUVVFU1RfSU5DTFVERVMgaWYgbm90IHByb3ZpZGVkLgogKiBAcGFyYW0gb3B0aW9ucy5kZXBzIEluamVjdGVkLCBwbGF0Zm9ybS1zcGVjaWZpYyBkZXBlbmRlbmNpZXMKICogQHJldHVybnMgQW4gb2JqZWN0IGNvbnRhaW5pbmcgbm9ybWFsaXplZCByZXF1ZXN0IGRhdGEKICovCmZ1bmN0aW9uIGV4dHJhY3RSZXF1ZXN0RGF0YSgKICByZXEsCiAgb3B0aW9ucwoKLAopIHsKICBjb25zdCB7IGluY2x1ZGUgPSBERUZBVUxUX1JFUVVFU1RfSU5DTFVERVMgfSA9IG9wdGlvbnMgfHwge307CiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkKICBjb25zdCByZXF1ZXN0RGF0YSA9IHt9OwoKICAvLyBoZWFkZXJzOgogIC8vICAgbm9kZSwgZXhwcmVzcywga29hLCBuZXh0anM6IHJlcS5oZWFkZXJzCiAgY29uc3QgaGVhZGVycyA9IChyZXEuaGVhZGVycyB8fCB7fSkKCjsKICAvLyBtZXRob2Q6CiAgLy8gICBub2RlLCBleHByZXNzLCBrb2EsIG5leHRqczogcmVxLm1ldGhvZAogIGNvbnN0IG1ldGhvZCA9IHJlcS5tZXRob2Q7CiAgLy8gaG9zdDoKICAvLyAgIGV4cHJlc3M6IHJlcS5ob3N0bmFtZSBpbiA+IDQgYW5kIHJlcS5ob3N0IGluIDwgNAogIC8vICAga29hOiByZXEuaG9zdAogIC8vICAgbm9kZSwgbmV4dGpzOiByZXEuaGVhZGVycy5ob3N0CiAgLy8gRXhwcmVzcyA0IG1pc3Rha2VubHkgc3RyaXBzIG9mZiBwb3J0IG51bWJlciBmcm9tIHJlcS5ob3N0IC8gcmVxLmhvc3RuYW1lIHNvIHdlIGNhbid0IHJlbHkgb24gdGhlbQogIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2V4cHJlc3Nqcy9leHByZXNzL2lzc3Vlcy8zMDQ3I2lzc3VlY29tbWVudC0yMzY2NTMyMjMKICAvLyBBbHNvOiBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0L2lzc3Vlcy8xOTE3CiAgY29uc3QgaG9zdCA9IGhlYWRlcnMuaG9zdCB8fCByZXEuaG9zdG5hbWUgfHwgcmVxLmhvc3QgfHwgJzxubyBob3N0Pic7CiAgLy8gcHJvdG9jb2w6CiAgLy8gICBub2RlLCBuZXh0anM6IDxuL2E+CiAgLy8gICBleHByZXNzLCBrb2E6IHJlcS5wcm90b2NvbAogIGNvbnN0IHByb3RvY29sID0gcmVxLnByb3RvY29sID09PSAnaHR0cHMnIHx8IChyZXEuc29ja2V0ICYmIHJlcS5zb2NrZXQuZW5jcnlwdGVkKSA/ICdodHRwcycgOiAnaHR0cCc7CiAgLy8gdXJsIChpbmNsdWRpbmcgcGF0aCBhbmQgcXVlcnkgc3RyaW5nKToKICAvLyAgIG5vZGUsIGV4cHJlc3M6IHJlcS5vcmlnaW5hbFVybAogIC8vICAga29hLCBuZXh0anM6IHJlcS51cmwKICBjb25zdCBvcmlnaW5hbFVybCA9IHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsIHx8ICcnOwogIC8vIGFic29sdXRlIHVybAogIGNvbnN0IGFic29sdXRlVXJsID0gb3JpZ2luYWxVcmwuc3RhcnRzV2l0aChwcm90b2NvbCkgPyBvcmlnaW5hbFVybCA6IGAke3Byb3RvY29sfTovLyR7aG9zdH0ke29yaWdpbmFsVXJsfWA7CiAgaW5jbHVkZS5mb3JFYWNoKGtleSA9PiB7CiAgICBzd2l0Y2ggKGtleSkgewogICAgICBjYXNlICdoZWFkZXJzJzogewogICAgICAgIHJlcXVlc3REYXRhLmhlYWRlcnMgPSBoZWFkZXJzOwoKICAgICAgICAvLyBSZW1vdmUgdGhlIENvb2tpZSBoZWFkZXIgaW4gY2FzZSBjb29raWUgZGF0YSBzaG91bGQgbm90IGJlIGluY2x1ZGVkIGluIHRoZSBldmVudAogICAgICAgIGlmICghaW5jbHVkZS5pbmNsdWRlcygnY29va2llcycpKSB7CiAgICAgICAgICBkZWxldGUgKHJlcXVlc3REYXRhLmhlYWRlcnMgKS5jb29raWU7CiAgICAgICAgfQoKICAgICAgICBicmVhazsKICAgICAgfQogICAgICBjYXNlICdtZXRob2QnOiB7CiAgICAgICAgcmVxdWVzdERhdGEubWV0aG9kID0gbWV0aG9kOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICAgIGNhc2UgJ3VybCc6IHsKICAgICAgICByZXF1ZXN0RGF0YS51cmwgPSBhYnNvbHV0ZVVybDsKICAgICAgICBicmVhazsKICAgICAgfQogICAgICBjYXNlICdjb29raWVzJzogewogICAgICAgIC8vIGNvb2tpZXM6CiAgICAgICAgLy8gICBub2RlLCBleHByZXNzLCBrb2E6IHJlcS5oZWFkZXJzLmNvb2tpZQogICAgICAgIC8vICAgdmVyY2VsLCBzYWlscy5qcywgZXhwcmVzcyAody8gY29va2llIG1pZGRsZXdhcmUpLCBuZXh0anM6IHJlcS5jb29raWVzCiAgICAgICAgcmVxdWVzdERhdGEuY29va2llcyA9CiAgICAgICAgICAvLyBUT0RPICh2OCAvICM1MjU3KTogV2UncmUgb25seSBzZW5kaW5nIHRoZSBlbXB0eSBvYmplY3QgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5LCBzbyB0aGUgbGFzdCBiaXQgY2FuCiAgICAgICAgICAvLyBjb21lIG9mZiBpbiB2OAogICAgICAgICAgcmVxLmNvb2tpZXMgfHwgKGhlYWRlcnMuY29va2llICYmIHBhcnNlQ29va2llKGhlYWRlcnMuY29va2llKSkgfHwge307CiAgICAgICAgYnJlYWs7CiAgICAgIH0KICAgICAgY2FzZSAncXVlcnlfc3RyaW5nJzogewogICAgICAgIC8vIHF1ZXJ5IHN0cmluZzoKICAgICAgICAvLyAgIG5vZGU6IHJlcS51cmwgKHJhdykKICAgICAgICAvLyAgIGV4cHJlc3MsIGtvYSwgbmV4dGpzOiByZXEucXVlcnkKICAgICAgICByZXF1ZXN0RGF0YS5xdWVyeV9zdHJpbmcgPSBleHRyYWN0UXVlcnlQYXJhbXMocmVxKTsKICAgICAgICBicmVhazsKICAgICAgfQogICAgICBjYXNlICdkYXRhJzogewogICAgICAgIGlmIChtZXRob2QgPT09ICdHRVQnIHx8IG1ldGhvZCA9PT0gJ0hFQUQnKSB7CiAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICAgICAgLy8gYm9keSBkYXRhOgogICAgICAgIC8vICAgZXhwcmVzcywga29hLCBuZXh0anM6IHJlcS5ib2R5CiAgICAgICAgLy8KICAgICAgICAvLyAgIHdoZW4gdXNpbmcgbm9kZSBieSBpdHNlbGYsIHlvdSBoYXZlIHRvIHJlYWQgdGhlIGluY29taW5nIHN0cmVhbShzZWUKICAgICAgICAvLyAgIGh0dHBzOi8vbm9kZWpzLmRldi9sZWFybi9nZXQtaHR0cC1yZXF1ZXN0LWJvZHktZGF0YS11c2luZy1ub2RlanMpOyBpZiBhIHVzZXIgaXMgZG9pbmcgdGhhdCwgd2UgY2FuJ3Qga25vdwogICAgICAgIC8vICAgd2hlcmUgdGhleSdyZSBnb2luZyB0byBzdG9yZSB0aGUgZmluYWwgcmVzdWx0LCBzbyB0aGV5J2xsIGhhdmUgdG8gY2FwdHVyZSB0aGlzIGRhdGEgdGhlbXNlbHZlcwogICAgICAgIGlmIChyZXEuYm9keSAhPT0gdW5kZWZpbmVkKSB7CiAgICAgICAgICByZXF1ZXN0RGF0YS5kYXRhID0gaXNTdHJpbmcocmVxLmJvZHkpID8gcmVxLmJvZHkgOiBKU09OLnN0cmluZ2lmeShub3JtYWxpemUocmVxLmJvZHkpKTsKICAgICAgICB9CiAgICAgICAgYnJlYWs7CiAgICAgIH0KICAgICAgZGVmYXVsdDogewogICAgICAgIGlmICh7fS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHJlcSwga2V5KSkgewogICAgICAgICAgcmVxdWVzdERhdGFba2V5XSA9IChyZXEgKVtrZXldOwogICAgICAgIH0KICAgICAgfQogICAgfQogIH0pOwoKICByZXR1cm4gcmVxdWVzdERhdGE7Cn0KCi8qKgogKiBBZGQgZGF0YSBmcm9tIHRoZSBnaXZlbiByZXF1ZXN0IHRvIHRoZSBnaXZlbiBldmVudAogKgogKiBAcGFyYW0gZXZlbnQgVGhlIGV2ZW50IHRvIHdoaWNoIHRoZSByZXF1ZXN0IGRhdGEgd2lsbCBiZSBhZGRlZAogKiBAcGFyYW0gcmVxIFJlcXVlc3Qgb2JqZWN0CiAqIEBwYXJhbSBvcHRpb25zLmluY2x1ZGUgRmxhZ3MgdG8gY29udHJvbCB3aGF0IGRhdGEgaXMgaW5jbHVkZWQKICogQHBhcmFtIG9wdGlvbnMuZGVwcyBJbmplY3RlZCBwbGF0Zm9ybS1zcGVjaWZpYyBkZXBlbmRlbmNpZXMKICogQHJldHVybnMgVGhlIG11dGF0ZWQgYEV2ZW50YCBvYmplY3QKICovCmZ1bmN0aW9uIGFkZFJlcXVlc3REYXRhVG9FdmVudCgKICBldmVudCwKICByZXEsCiAgb3B0aW9ucywKKSB7CiAgY29uc3QgaW5jbHVkZSA9IHsKICAgIC4uLkRFRkFVTFRfSU5DTFVERVMsCiAgICAuLi4ob3B0aW9ucyAmJiBvcHRpb25zLmluY2x1ZGUpLAogIH07CgogIGlmIChpbmNsdWRlLnJlcXVlc3QpIHsKICAgIGNvbnN0IGV4dHJhY3RlZFJlcXVlc3REYXRhID0gQXJyYXkuaXNBcnJheShpbmNsdWRlLnJlcXVlc3QpCiAgICAgID8gZXh0cmFjdFJlcXVlc3REYXRhKHJlcSwgeyBpbmNsdWRlOiBpbmNsdWRlLnJlcXVlc3QgfSkKICAgICAgOiBleHRyYWN0UmVxdWVzdERhdGEocmVxKTsKCiAgICBldmVudC5yZXF1ZXN0ID0gewogICAgICAuLi5ldmVudC5yZXF1ZXN0LAogICAgICAuLi5leHRyYWN0ZWRSZXF1ZXN0RGF0YSwKICAgIH07CiAgfQoKICBpZiAoaW5jbHVkZS51c2VyKSB7CiAgICBjb25zdCBleHRyYWN0ZWRVc2VyID0gcmVxLnVzZXIgJiYgaXNQbGFpbk9iamVjdChyZXEudXNlcikgPyBleHRyYWN0VXNlckRhdGEocmVxLnVzZXIsIGluY2x1ZGUudXNlcikgOiB7fTsKCiAgICBpZiAoT2JqZWN0LmtleXMoZXh0cmFjdGVkVXNlcikubGVuZ3RoKSB7CiAgICAgIGV2ZW50LnVzZXIgPSB7CiAgICAgICAgLi4uZXZlbnQudXNlciwKICAgICAgICAuLi5leHRyYWN0ZWRVc2VyLAogICAgICB9OwogICAgfQogIH0KCiAgLy8gY2xpZW50IGlwOgogIC8vICAgbm9kZSwgbmV4dGpzOiByZXEuc29ja2V0LnJlbW90ZUFkZHJlc3MKICAvLyAgIGV4cHJlc3MsIGtvYTogcmVxLmlwCiAgaWYgKGluY2x1ZGUuaXApIHsKICAgIGNvbnN0IGlwID0gcmVxLmlwIHx8IChyZXEuc29ja2V0ICYmIHJlcS5zb2NrZXQucmVtb3RlQWRkcmVzcyk7CiAgICBpZiAoaXApIHsKICAgICAgZXZlbnQudXNlciA9IHsKICAgICAgICAuLi5ldmVudC51c2VyLAogICAgICAgIGlwX2FkZHJlc3M6IGlwLAogICAgICB9OwogICAgfQogIH0KCiAgaWYgKGluY2x1ZGUudHJhbnNhY3Rpb24gJiYgIWV2ZW50LnRyYW5zYWN0aW9uKSB7CiAgICAvLyBUT0RPIGRvIHdlIGV2ZW4gbmVlZCB0aGlzIGFueW1vcmU/CiAgICAvLyBUT0RPIG1ha2UgdGhpcyB3b3JrIGZvciBuZXh0anMKICAgIGV2ZW50LnRyYW5zYWN0aW9uID0gZXh0cmFjdFRyYW5zYWN0aW9uKHJlcSwgaW5jbHVkZS50cmFuc2FjdGlvbik7CiAgfQoKICByZXR1cm4gZXZlbnQ7Cn0KCmZ1bmN0aW9uIGV4dHJhY3RRdWVyeVBhcmFtcyhyZXEpIHsKICAvLyB1cmwgKGluY2x1ZGluZyBwYXRoIGFuZCBxdWVyeSBzdHJpbmcpOgogIC8vICAgbm9kZSwgZXhwcmVzczogcmVxLm9yaWdpbmFsVXJsCiAgLy8gICBrb2EsIG5leHRqczogcmVxLnVybAogIGxldCBvcmlnaW5hbFVybCA9IHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsIHx8ICcnOwoKICBpZiAoIW9yaWdpbmFsVXJsKSB7CiAgICByZXR1cm47CiAgfQoKICAvLyBUaGUgYFVSTGAgY29uc3RydWN0b3IgY2FuJ3QgaGFuZGxlIGludGVybmFsIFVSTHMgb2YgdGhlIGZvcm0gYC9zb21lL3BhdGgvaGVyZWAsIHNvIHN0aWNrIGEgZHVtbXkgcHJvdG9jb2wgYW5kCiAgLy8gaG9zdG5hbWUgb24gdGhlIGJlZ2lubmluZy4gU2luY2UgdGhlIHBvaW50IGhlcmUgaXMganVzdCB0byBncmFiIHRoZSBxdWVyeSBzdHJpbmcsIGl0IGRvZXNuJ3QgbWF0dGVyIHdoYXQgd2UgdXNlLgogIGlmIChvcmlnaW5hbFVybC5zdGFydHNXaXRoKCcvJykpIHsKICAgIG9yaWdpbmFsVXJsID0gYGh0dHA6Ly9kb2dzLmFyZS5ncmVhdCR7b3JpZ2luYWxVcmx9YDsKICB9CgogIHRyeSB7CiAgICBjb25zdCBxdWVyeVBhcmFtcyA9IHJlcS5xdWVyeSB8fCBuZXcgVVJMKG9yaWdpbmFsVXJsKS5zZWFyY2guc2xpY2UoMSk7CiAgICByZXR1cm4gcXVlcnlQYXJhbXMubGVuZ3RoID8gcXVlcnlQYXJhbXMgOiB1bmRlZmluZWQ7CiAgfSBjYXRjaCAoZTIpIHsKICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQp9CgovKioKICogRG9lcyB0aGlzIGZpbGVuYW1lIGxvb2sgbGlrZSBpdCdzIHBhcnQgb2YgdGhlIGFwcCBjb2RlPwogKi8KZnVuY3Rpb24gZmlsZW5hbWVJc0luQXBwKGZpbGVuYW1lLCBpc05hdGl2ZSA9IGZhbHNlKSB7CiAgY29uc3QgaXNJbnRlcm5hbCA9CiAgICBpc05hdGl2ZSB8fAogICAgKGZpbGVuYW1lICYmCiAgICAgIC8vIEl0J3Mgbm90IGludGVybmFsIGlmIGl0J3MgYW4gYWJzb2x1dGUgbGludXggcGF0aAogICAgICAhZmlsZW5hbWUuc3RhcnRzV2l0aCgnLycpICYmCiAgICAgIC8vIEl0J3Mgbm90IGludGVybmFsIGlmIGl0J3MgYW4gYWJzb2x1dGUgd2luZG93cyBwYXRoCiAgICAgICFmaWxlbmFtZS5tYXRjaCgvXltBLVpdOi8pICYmCiAgICAgIC8vIEl0J3Mgbm90IGludGVybmFsIGlmIHRoZSBwYXRoIGlzIHN0YXJ0aW5nIHdpdGggYSBkb3QKICAgICAgIWZpbGVuYW1lLnN0YXJ0c1dpdGgoJy4nKSAmJgogICAgICAvLyBJdCdzIG5vdCBpbnRlcm5hbCBpZiB0aGUgZnJhbWUgaGFzIGEgcHJvdG9jb2wuIEluIG5vZGUsIHRoaXMgaXMgdXN1YWxseSB0aGUgY2FzZSBpZiB0aGUgZmlsZSBnb3QgcHJlLXByb2Nlc3NlZCB3aXRoIGEgYnVuZGxlciBsaWtlIHdlYnBhY2sKICAgICAgIWZpbGVuYW1lLm1hdGNoKC9eW2EtekEtWl0oW2EtekEtWjAtOS5cLStdKSo6XC9cLy8pKTsgLy8gU2NoZW1hIGZyb206IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8zNjQxNzgyCgogIC8vIGluX2FwcCBpcyBhbGwgdGhhdCdzIG5vdCBhbiBpbnRlcm5hbCBOb2RlIGZ1bmN0aW9uIG9yIGEgbW9kdWxlIHdpdGhpbiBub2RlX21vZHVsZXMKICAvLyBub3RlIHRoYXQgaXNOYXRpdmUgYXBwZWFycyB0byByZXR1cm4gdHJ1ZSBldmVuIGZvciBub2RlIGNvcmUgbGlicmFyaWVzCiAgLy8gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9nZXRzZW50cnkvcmF2ZW4tbm9kZS9pc3N1ZXMvMTc2CgogIHJldHVybiAhaXNJbnRlcm5hbCAmJiBmaWxlbmFtZSAhPT0gdW5kZWZpbmVkICYmICFmaWxlbmFtZS5pbmNsdWRlcygnbm9kZV9tb2R1bGVzLycpOwp9Cgpjb25zdCBPTkVfU0VDT05EX0lOX01TID0gMTAwMDsKCi8qKgogKiBBIHBhcnRpYWwgZGVmaW5pdGlvbiBvZiB0aGUgW1BlcmZvcm1hbmNlIFdlYiBBUElde0BsaW5rIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9QZXJmb3JtYW5jZX0KICogZm9yIGFjY2Vzc2luZyBhIGhpZ2gtcmVzb2x1dGlvbiBtb25vdG9uaWMgY2xvY2suCiAqLwoKLyoqCiAqIFJldHVybnMgYSB0aW1lc3RhbXAgaW4gc2Vjb25kcyBzaW5jZSB0aGUgVU5JWCBlcG9jaCB1c2luZyB0aGUgRGF0ZSBBUEkuCiAqCiAqIFRPRE8odjgpOiBSZXR1cm4gdHlwZSBzaG91bGQgYmUgcm91bmRlZC4KICovCmZ1bmN0aW9uIGRhdGVUaW1lc3RhbXBJblNlY29uZHMoKSB7CiAgcmV0dXJuIERhdGUubm93KCkgLyBPTkVfU0VDT05EX0lOX01TOwp9CgovKioKICogUmV0dXJucyBhIHdyYXBwZXIgYXJvdW5kIHRoZSBuYXRpdmUgUGVyZm9ybWFuY2UgQVBJIGJyb3dzZXIgaW1wbGVtZW50YXRpb24sIG9yIHVuZGVmaW5lZCBmb3IgYnJvd3NlcnMgdGhhdCBkbyBub3QKICogc3VwcG9ydCB0aGUgQVBJLgogKgogKiBXcmFwcGluZyB0aGUgbmF0aXZlIEFQSSB3b3JrcyBhcm91bmQgZGlmZmVyZW5jZXMgaW4gYmVoYXZpb3IgZnJvbSBkaWZmZXJlbnQgYnJvd3NlcnMuCiAqLwpmdW5jdGlvbiBjcmVhdGVVbml4VGltZXN0YW1wSW5TZWNvbmRzRnVuYygpIHsKICBjb25zdCB7IHBlcmZvcm1hbmNlIH0gPSBHTE9CQUxfT0JKIDsKICBpZiAoIXBlcmZvcm1hbmNlIHx8ICFwZXJmb3JtYW5jZS5ub3cpIHsKICAgIHJldHVybiBkYXRlVGltZXN0YW1wSW5TZWNvbmRzOwogIH0KCiAgLy8gU29tZSBicm93c2VyIGFuZCBlbnZpcm9ubWVudHMgZG9uJ3QgaGF2ZSBhIHRpbWVPcmlnaW4sIHNvIHdlIGZhbGxiYWNrIHRvCiAgLy8gdXNpbmcgRGF0ZS5ub3coKSB0byBjb21wdXRlIHRoZSBzdGFydGluZyB0aW1lLgogIGNvbnN0IGFwcHJveFN0YXJ0aW5nVGltZU9yaWdpbiA9IERhdGUubm93KCkgLSBwZXJmb3JtYW5jZS5ub3coKTsKICBjb25zdCB0aW1lT3JpZ2luID0gcGVyZm9ybWFuY2UudGltZU9yaWdpbiA9PSB1bmRlZmluZWQgPyBhcHByb3hTdGFydGluZ1RpbWVPcmlnaW4gOiBwZXJmb3JtYW5jZS50aW1lT3JpZ2luOwoKICAvLyBwZXJmb3JtYW5jZS5ub3coKSBpcyBhIG1vbm90b25pYyBjbG9jaywgd2hpY2ggbWVhbnMgaXQgc3RhcnRzIGF0IDAgd2hlbiB0aGUgcHJvY2VzcyBiZWdpbnMuIFRvIGdldCB0aGUgY3VycmVudAogIC8vIHdhbGwgY2xvY2sgdGltZSAoYWN0dWFsIFVOSVggdGltZXN0YW1wKSwgd2UgbmVlZCB0byBhZGQgdGhlIHN0YXJ0aW5nIHRpbWUgb3JpZ2luIGFuZCB0aGUgY3VycmVudCB0aW1lIGVsYXBzZWQuCiAgLy8KICAvLyBUT0RPOiBUaGlzIGRvZXMgbm90IGFjY291bnQgZm9yIHRoZSBjYXNlIHdoZXJlIHRoZSBtb25vdG9uaWMgY2xvY2sgdGhhdCBwb3dlcnMgcGVyZm9ybWFuY2Uubm93KCkgZHJpZnRzIGZyb20gdGhlCiAgLy8gd2FsbCBjbG9jayB0aW1lLCB3aGljaCBjYXVzZXMgdGhlIHJldHVybmVkIHRpbWVzdGFtcCB0byBiZSBpbmFjY3VyYXRlLiBXZSBzaG91bGQgaW52ZXN0aWdhdGUgaG93IHRvIGRldGVjdCBhbmQKICAvLyBjb3JyZWN0IGZvciB0aGlzLgogIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdC9pc3N1ZXMvMjU5MAogIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL21kbi9jb250ZW50L2lzc3Vlcy80NzEzCiAgLy8gU2VlOiBodHRwczovL2Rldi50by9ub2Ftci93aGVuLWEtbWlsbGlzZWNvbmQtaXMtbm90LWEtbWlsbGlzZWNvbmQtM2g2CiAgcmV0dXJuICgpID0+IHsKICAgIHJldHVybiAodGltZU9yaWdpbiArIHBlcmZvcm1hbmNlLm5vdygpKSAvIE9ORV9TRUNPTkRfSU5fTVM7CiAgfTsKfQoKLyoqCiAqIFJldHVybnMgYSB0aW1lc3RhbXAgaW4gc2Vjb25kcyBzaW5jZSB0aGUgVU5JWCBlcG9jaCB1c2luZyBlaXRoZXIgdGhlIFBlcmZvcm1hbmNlIG9yIERhdGUgQVBJcywgZGVwZW5kaW5nIG9uIHRoZQogKiBhdmFpbGFiaWxpdHkgb2YgdGhlIFBlcmZvcm1hbmNlIEFQSS4KICoKICogQlVHOiBOb3RlIHRoYXQgYmVjYXVzZSBvZiBob3cgYnJvd3NlcnMgaW1wbGVtZW50IHRoZSBQZXJmb3JtYW5jZSBBUEksIHRoZSBjbG9jayBtaWdodCBzdG9wIHdoZW4gdGhlIGNvbXB1dGVyIGlzCiAqIGFzbGVlcC4gVGhpcyBjcmVhdGVzIGEgc2tldyBiZXR3ZWVuIGBkYXRlVGltZXN0YW1wSW5TZWNvbmRzYCBhbmQgYHRpbWVzdGFtcEluU2Vjb25kc2AuIFRoZQogKiBza2V3IGNhbiBncm93IHRvIGFyYml0cmFyeSBhbW91bnRzIGxpa2UgZGF5cywgd2Vla3Mgb3IgbW9udGhzLgogKiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdC9pc3N1ZXMvMjU5MC4KICovCmNvbnN0IHRpbWVzdGFtcEluU2Vjb25kcyA9IGNyZWF0ZVVuaXhUaW1lc3RhbXBJblNlY29uZHNGdW5jKCk7CgovKioKICogVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgc2luY2UgdGhlIFVOSVggZXBvY2guIFRoaXMgdmFsdWUgaXMgb25seSB1c2FibGUgaW4gYSBicm93c2VyLCBhbmQgb25seSB3aGVuIHRoZQogKiBwZXJmb3JtYW5jZSBBUEkgaXMgYXZhaWxhYmxlLgogKi8KKCgpID0+IHsKICAvLyBVbmZvcnR1bmF0ZWx5IGJyb3dzZXJzIG1heSByZXBvcnQgYW4gaW5hY2N1cmF0ZSB0aW1lIG9yaWdpbiBkYXRhLCB0aHJvdWdoIGVpdGhlciBwZXJmb3JtYW5jZS50aW1lT3JpZ2luIG9yCiAgLy8gcGVyZm9ybWFuY2UudGltaW5nLm5hdmlnYXRpb25TdGFydCwgd2hpY2ggcmVzdWx0cyBpbiBwb29yIHJlc3VsdHMgaW4gcGVyZm9ybWFuY2UgZGF0YS4gV2Ugb25seSB0cmVhdCB0aW1lIG9yaWdpbgogIC8vIGRhdGEgYXMgcmVsaWFibGUgaWYgdGhleSBhcmUgd2l0aGluIGEgcmVhc29uYWJsZSB0aHJlc2hvbGQgb2YgdGhlIGN1cnJlbnQgdGltZS4KCiAgY29uc3QgeyBwZXJmb3JtYW5jZSB9ID0gR0xPQkFMX09CSiA7CiAgaWYgKCFwZXJmb3JtYW5jZSB8fCAhcGVyZm9ybWFuY2Uubm93KSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgY29uc3QgdGhyZXNob2xkID0gMzYwMCAqIDEwMDA7CiAgY29uc3QgcGVyZm9ybWFuY2VOb3cgPSBwZXJmb3JtYW5jZS5ub3coKTsKICBjb25zdCBkYXRlTm93ID0gRGF0ZS5ub3coKTsKCiAgLy8gaWYgdGltZU9yaWdpbiBpc24ndCBhdmFpbGFibGUgc2V0IGRlbHRhIHRvIHRocmVzaG9sZCBzbyBpdCBpc24ndCB1c2VkCiAgY29uc3QgdGltZU9yaWdpbkRlbHRhID0gcGVyZm9ybWFuY2UudGltZU9yaWdpbgogICAgPyBNYXRoLmFicyhwZXJmb3JtYW5jZS50aW1lT3JpZ2luICsgcGVyZm9ybWFuY2VOb3cgLSBkYXRlTm93KQogICAgOiB0aHJlc2hvbGQ7CiAgY29uc3QgdGltZU9yaWdpbklzUmVsaWFibGUgPSB0aW1lT3JpZ2luRGVsdGEgPCB0aHJlc2hvbGQ7CgogIC8vIFdoaWxlIHBlcmZvcm1hbmNlLnRpbWluZy5uYXZpZ2F0aW9uU3RhcnQgaXMgZGVwcmVjYXRlZCBpbiBmYXZvciBvZiBwZXJmb3JtYW5jZS50aW1lT3JpZ2luLCBwZXJmb3JtYW5jZS50aW1lT3JpZ2luCiAgLy8gaXMgbm90IGFzIHdpZGVseSBzdXBwb3J0ZWQuIE5hbWVseSwgcGVyZm9ybWFuY2UudGltZU9yaWdpbiBpcyB1bmRlZmluZWQgaW4gU2FmYXJpIGFzIG9mIHdyaXRpbmcuCiAgLy8gQWxzbyBhcyBvZiB3cml0aW5nLCBwZXJmb3JtYW5jZS50aW1pbmcgaXMgbm90IGF2YWlsYWJsZSBpbiBXZWIgV29ya2VycyBpbiBtYWluc3RyZWFtIGJyb3dzZXJzLCBzbyBpdCBpcyBub3QgYWx3YXlzCiAgLy8gYSB2YWxpZCBmYWxsYmFjay4gSW4gdGhlIGFic2VuY2Ugb2YgYW4gaW5pdGlhbCB0aW1lIHByb3ZpZGVkIGJ5IHRoZSBicm93c2VyLCBmYWxsYmFjayB0byB0aGUgY3VycmVudCB0aW1lIGZyb20gdGhlCiAgLy8gRGF0ZSBBUEkuCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgY29uc3QgbmF2aWdhdGlvblN0YXJ0ID0gcGVyZm9ybWFuY2UudGltaW5nICYmIHBlcmZvcm1hbmNlLnRpbWluZy5uYXZpZ2F0aW9uU3RhcnQ7CiAgY29uc3QgaGFzTmF2aWdhdGlvblN0YXJ0ID0gdHlwZW9mIG5hdmlnYXRpb25TdGFydCA9PT0gJ251bWJlcic7CiAgLy8gaWYgbmF2aWdhdGlvblN0YXJ0IGlzbid0IGF2YWlsYWJsZSBzZXQgZGVsdGEgdG8gdGhyZXNob2xkIHNvIGl0IGlzbid0IHVzZWQKICBjb25zdCBuYXZpZ2F0aW9uU3RhcnREZWx0YSA9IGhhc05hdmlnYXRpb25TdGFydCA/IE1hdGguYWJzKG5hdmlnYXRpb25TdGFydCArIHBlcmZvcm1hbmNlTm93IC0gZGF0ZU5vdykgOiB0aHJlc2hvbGQ7CiAgY29uc3QgbmF2aWdhdGlvblN0YXJ0SXNSZWxpYWJsZSA9IG5hdmlnYXRpb25TdGFydERlbHRhIDwgdGhyZXNob2xkOwoKICBpZiAodGltZU9yaWdpbklzUmVsaWFibGUgfHwgbmF2aWdhdGlvblN0YXJ0SXNSZWxpYWJsZSkgewogICAgLy8gVXNlIHRoZSBtb3JlIHJlbGlhYmxlIHRpbWUgb3JpZ2luCiAgICBpZiAodGltZU9yaWdpbkRlbHRhIDw9IG5hdmlnYXRpb25TdGFydERlbHRhKSB7CiAgICAgIHJldHVybiBwZXJmb3JtYW5jZS50aW1lT3JpZ2luOwogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuIG5hdmlnYXRpb25TdGFydDsKICAgIH0KICB9CiAgcmV0dXJuIGRhdGVOb3c7Cn0pKCk7CgovKioKICogQ3JlYXRlcyBhbiBlbnZlbG9wZS4KICogTWFrZSBzdXJlIHRvIGFsd2F5cyBleHBsaWNpdGx5IHByb3ZpZGUgdGhlIGdlbmVyaWMgdG8gdGhpcyBmdW5jdGlvbgogKiBzbyB0aGF0IHRoZSBlbnZlbG9wZSB0eXBlcyByZXNvbHZlIGNvcnJlY3RseS4KICovCmZ1bmN0aW9uIGNyZWF0ZUVudmVsb3BlKGhlYWRlcnMsIGl0ZW1zID0gW10pIHsKICByZXR1cm4gW2hlYWRlcnMsIGl0ZW1zXSA7Cn0KCi8qKgogKiBDb252ZW5pZW5jZSBmdW5jdGlvbiB0byBsb29wIHRocm91Z2ggdGhlIGl0ZW1zIGFuZCBpdGVtIHR5cGVzIG9mIGFuIGVudmVsb3BlLgogKiAoVGhpcyBmdW5jdGlvbiB3YXMgbW9zdGx5IGNyZWF0ZWQgYmVjYXVzZSB3b3JraW5nIHdpdGggZW52ZWxvcGUgdHlwZXMgaXMgcGFpbmZ1bCBhdCB0aGUgbW9tZW50KQogKgogKiBJZiB0aGUgY2FsbGJhY2sgcmV0dXJucyB0cnVlLCB0aGUgcmVzdCBvZiB0aGUgaXRlbXMgd2lsbCBiZSBza2lwcGVkLgogKi8KZnVuY3Rpb24gZm9yRWFjaEVudmVsb3BlSXRlbSgKICBlbnZlbG9wZSwKICBjYWxsYmFjaywKKSB7CiAgY29uc3QgZW52ZWxvcGVJdGVtcyA9IGVudmVsb3BlWzFdOwoKICBmb3IgKGNvbnN0IGVudmVsb3BlSXRlbSBvZiBlbnZlbG9wZUl0ZW1zKSB7CiAgICBjb25zdCBlbnZlbG9wZUl0ZW1UeXBlID0gZW52ZWxvcGVJdGVtWzBdLnR5cGU7CiAgICBjb25zdCByZXN1bHQgPSBjYWxsYmFjayhlbnZlbG9wZUl0ZW0sIGVudmVsb3BlSXRlbVR5cGUpOwoKICAgIGlmIChyZXN1bHQpIHsKICAgICAgcmV0dXJuIHRydWU7CiAgICB9CiAgfQoKICByZXR1cm4gZmFsc2U7Cn0KCi8qKgogKiBFbmNvZGUgYSBzdHJpbmcgdG8gVVRGOCBhcnJheS4KICovCmZ1bmN0aW9uIGVuY29kZVVURjgoaW5wdXQpIHsKICByZXR1cm4gbmV3IFRleHRFbmNvZGVyKCkuZW5jb2RlKGlucHV0KTsKfQoKLyoqCiAqIFNlcmlhbGl6ZXMgYW4gZW52ZWxvcGUuCiAqLwpmdW5jdGlvbiBzZXJpYWxpemVFbnZlbG9wZShlbnZlbG9wZSkgewogIGNvbnN0IFtlbnZIZWFkZXJzLCBpdGVtc10gPSBlbnZlbG9wZTsKCiAgLy8gSW5pdGlhbGx5IHdlIGNvbnN0cnVjdCBvdXIgZW52ZWxvcGUgYXMgYSBzdHJpbmcgYW5kIG9ubHkgY29udmVydCB0byBiaW5hcnkgY2h1bmtzIGlmIHdlIGVuY291bnRlciBiaW5hcnkgZGF0YQogIGxldCBwYXJ0cyA9IEpTT04uc3RyaW5naWZ5KGVudkhlYWRlcnMpOwoKICBmdW5jdGlvbiBhcHBlbmQobmV4dCkgewogICAgaWYgKHR5cGVvZiBwYXJ0cyA9PT0gJ3N0cmluZycpIHsKICAgICAgcGFydHMgPSB0eXBlb2YgbmV4dCA9PT0gJ3N0cmluZycgPyBwYXJ0cyArIG5leHQgOiBbZW5jb2RlVVRGOChwYXJ0cyksIG5leHRdOwogICAgfSBlbHNlIHsKICAgICAgcGFydHMucHVzaCh0eXBlb2YgbmV4dCA9PT0gJ3N0cmluZycgPyBlbmNvZGVVVEY4KG5leHQpIDogbmV4dCk7CiAgICB9CiAgfQoKICBmb3IgKGNvbnN0IGl0ZW0gb2YgaXRlbXMpIHsKICAgIGNvbnN0IFtpdGVtSGVhZGVycywgcGF5bG9hZF0gPSBpdGVtOwoKICAgIGFwcGVuZChgXG4ke0pTT04uc3RyaW5naWZ5KGl0ZW1IZWFkZXJzKX1cbmApOwoKICAgIGlmICh0eXBlb2YgcGF5bG9hZCA9PT0gJ3N0cmluZycgfHwgcGF5bG9hZCBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkpIHsKICAgICAgYXBwZW5kKHBheWxvYWQpOwogICAgfSBlbHNlIHsKICAgICAgbGV0IHN0cmluZ2lmaWVkUGF5bG9hZDsKICAgICAgdHJ5IHsKICAgICAgICBzdHJpbmdpZmllZFBheWxvYWQgPSBKU09OLnN0cmluZ2lmeShwYXlsb2FkKTsKICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIC8vIEluIGNhc2UsIGRlc3BpdGUgYWxsIG91ciBlZmZvcnRzIHRvIGtlZXAgYHBheWxvYWRgIGNpcmN1bGFyLWRlcGVuZGVuY3ktZnJlZSwgYEpTT04uc3RyaW5pZnkoKWAgc3RpbGwKICAgICAgICAvLyBmYWlscywgd2UgdHJ5IGFnYWluIGFmdGVyIG5vcm1hbGl6aW5nIGl0IGFnYWluIHdpdGggaW5maW5pdGUgbm9ybWFsaXphdGlvbiBkZXB0aC4gVGhpcyBvZiBjb3Vyc2UgaGFzIGEKICAgICAgICAvLyBwZXJmb3JtYW5jZSBpbXBhY3QgYnV0IGluIHRoaXMgY2FzZSBhIHBlcmZvcm1hbmNlIGhpdCBpcyBiZXR0ZXIgdGhhbiB0aHJvd2luZy4KICAgICAgICBzdHJpbmdpZmllZFBheWxvYWQgPSBKU09OLnN0cmluZ2lmeShub3JtYWxpemUocGF5bG9hZCkpOwogICAgICB9CiAgICAgIGFwcGVuZChzdHJpbmdpZmllZFBheWxvYWQpOwogICAgfQogIH0KCiAgcmV0dXJuIHR5cGVvZiBwYXJ0cyA9PT0gJ3N0cmluZycgPyBwYXJ0cyA6IGNvbmNhdEJ1ZmZlcnMocGFydHMpOwp9CgpmdW5jdGlvbiBjb25jYXRCdWZmZXJzKGJ1ZmZlcnMpIHsKICBjb25zdCB0b3RhbExlbmd0aCA9IGJ1ZmZlcnMucmVkdWNlKChhY2MsIGJ1ZikgPT4gYWNjICsgYnVmLmxlbmd0aCwgMCk7CgogIGNvbnN0IG1lcmdlZCA9IG5ldyBVaW50OEFycmF5KHRvdGFsTGVuZ3RoKTsKICBsZXQgb2Zmc2V0ID0gMDsKICBmb3IgKGNvbnN0IGJ1ZmZlciBvZiBidWZmZXJzKSB7CiAgICBtZXJnZWQuc2V0KGJ1ZmZlciwgb2Zmc2V0KTsKICAgIG9mZnNldCArPSBidWZmZXIubGVuZ3RoOwogIH0KCiAgcmV0dXJuIG1lcmdlZDsKfQoKY29uc3QgSVRFTV9UWVBFX1RPX0RBVEFfQ0FURUdPUllfTUFQID0gewogIHNlc3Npb246ICdzZXNzaW9uJywKICBzZXNzaW9uczogJ3Nlc3Npb24nLAogIGF0dGFjaG1lbnQ6ICdhdHRhY2htZW50JywKICB0cmFuc2FjdGlvbjogJ3RyYW5zYWN0aW9uJywKICBldmVudDogJ2Vycm9yJywKICBjbGllbnRfcmVwb3J0OiAnaW50ZXJuYWwnLAogIHVzZXJfcmVwb3J0OiAnZGVmYXVsdCcsCiAgcHJvZmlsZTogJ3Byb2ZpbGUnLAogIHJlcGxheV9ldmVudDogJ3JlcGxheScsCiAgcmVwbGF5X3JlY29yZGluZzogJ3JlcGxheScsCiAgY2hlY2tfaW46ICdtb25pdG9yJywKICBmZWVkYmFjazogJ2ZlZWRiYWNrJywKICAvLyBUT0RPOiBUaGlzIGlzIGEgdGVtcG9yYXJ5IHdvcmthcm91bmQgdW50aWwgd2UgaGF2ZSBhIHByb3BlciBkYXRhIGNhdGVnb3J5IGZvciBtZXRyaWNzCiAgc3RhdHNkOiAndW5rbm93bicsCn07CgovKioKICogTWFwcyB0aGUgdHlwZSBvZiBhbiBlbnZlbG9wZSBpdGVtIHRvIGEgZGF0YSBjYXRlZ29yeS4KICovCmZ1bmN0aW9uIGVudmVsb3BlSXRlbVR5cGVUb0RhdGFDYXRlZ29yeSh0eXBlKSB7CiAgcmV0dXJuIElURU1fVFlQRV9UT19EQVRBX0NBVEVHT1JZX01BUFt0eXBlXTsKfQoKLyoqIEV4dHJhY3RzIHRoZSBtaW5pbWFsIFNESyBpbmZvIGZyb20gZnJvbSB0aGUgbWV0YWRhdGEgb3IgYW4gZXZlbnRzICovCmZ1bmN0aW9uIGdldFNka01ldGFkYXRhRm9yRW52ZWxvcGVIZWFkZXIobWV0YWRhdGFPckV2ZW50KSB7CiAgaWYgKCFtZXRhZGF0YU9yRXZlbnQgfHwgIW1ldGFkYXRhT3JFdmVudC5zZGspIHsKICAgIHJldHVybjsKICB9CiAgY29uc3QgeyBuYW1lLCB2ZXJzaW9uIH0gPSBtZXRhZGF0YU9yRXZlbnQuc2RrOwogIHJldHVybiB7IG5hbWUsIHZlcnNpb24gfTsKfQoKLyoqCiAqIENyZWF0ZXMgZXZlbnQgZW52ZWxvcGUgaGVhZGVycywgYmFzZWQgb24gZXZlbnQsIHNkayBpbmZvIGFuZCB0dW5uZWwKICogTm90ZTogVGhpcyBmdW5jdGlvbiB3YXMgZXh0cmFjdGVkIGZyb20gdGhlIGNvcmUgcGFja2FnZSB0byBtYWtlIGl0IGF2YWlsYWJsZSBpbiBSZXBsYXkKICovCmZ1bmN0aW9uIGNyZWF0ZUV2ZW50RW52ZWxvcGVIZWFkZXJzKAogIGV2ZW50LAogIHNka0luZm8sCiAgdHVubmVsLAogIGRzbiwKKSB7CiAgY29uc3QgZHluYW1pY1NhbXBsaW5nQ29udGV4dCA9IGV2ZW50LnNka1Byb2Nlc3NpbmdNZXRhZGF0YSAmJiBldmVudC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEuZHluYW1pY1NhbXBsaW5nQ29udGV4dDsKICByZXR1cm4gewogICAgZXZlbnRfaWQ6IGV2ZW50LmV2ZW50X2lkICwKICAgIHNlbnRfYXQ6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSwKICAgIC4uLihzZGtJbmZvICYmIHsgc2RrOiBzZGtJbmZvIH0pLAogICAgLi4uKCEhdHVubmVsICYmIGRzbiAmJiB7IGRzbjogZHNuVG9TdHJpbmcoZHNuKSB9KSwKICAgIC4uLihkeW5hbWljU2FtcGxpbmdDb250ZXh0ICYmIHsKICAgICAgdHJhY2U6IGRyb3BVbmRlZmluZWRLZXlzKHsgLi4uZHluYW1pY1NhbXBsaW5nQ29udGV4dCB9KSwKICAgIH0pLAogIH07Cn0KCi8vIEludGVudGlvbmFsbHkga2VlcGluZyB0aGUga2V5IGJyb2FkLCBhcyB3ZSBkb24ndCBrbm93IGZvciBzdXJlIHdoYXQgcmF0ZSBsaW1pdCBoZWFkZXJzIGdldCByZXR1cm5lZCBmcm9tIGJhY2tlbmQKCmNvbnN0IERFRkFVTFRfUkVUUllfQUZURVIgPSA2MCAqIDEwMDA7IC8vIDYwIHNlY29uZHMKCi8qKgogKiBFeHRyYWN0cyBSZXRyeS1BZnRlciB2YWx1ZSBmcm9tIHRoZSByZXF1ZXN0IGhlYWRlciBvciByZXR1cm5zIGRlZmF1bHQgdmFsdWUKICogQHBhcmFtIGhlYWRlciBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgJ1JldHJ5LUFmdGVyJyBoZWFkZXIKICogQHBhcmFtIG5vdyBjdXJyZW50IHVuaXggdGltZXN0YW1wCiAqCiAqLwpmdW5jdGlvbiBwYXJzZVJldHJ5QWZ0ZXJIZWFkZXIoaGVhZGVyLCBub3cgPSBEYXRlLm5vdygpKSB7CiAgY29uc3QgaGVhZGVyRGVsYXkgPSBwYXJzZUludChgJHtoZWFkZXJ9YCwgMTApOwogIGlmICghaXNOYU4oaGVhZGVyRGVsYXkpKSB7CiAgICByZXR1cm4gaGVhZGVyRGVsYXkgKiAxMDAwOwogIH0KCiAgY29uc3QgaGVhZGVyRGF0ZSA9IERhdGUucGFyc2UoYCR7aGVhZGVyfWApOwogIGlmICghaXNOYU4oaGVhZGVyRGF0ZSkpIHsKICAgIHJldHVybiBoZWFkZXJEYXRlIC0gbm93OwogIH0KCiAgcmV0dXJuIERFRkFVTFRfUkVUUllfQUZURVI7Cn0KCi8qKgogKiBHZXRzIHRoZSB0aW1lIHRoYXQgdGhlIGdpdmVuIGNhdGVnb3J5IGlzIGRpc2FibGVkIHVudGlsIGZvciByYXRlIGxpbWl0aW5nLgogKiBJbiBjYXNlIG5vIGNhdGVnb3J5LXNwZWNpZmljIGxpbWl0IGlzIHNldCBidXQgYSBnZW5lcmFsIHJhdGUgbGltaXQgYWNyb3NzIGFsbCBjYXRlZ29yaWVzIGlzIGFjdGl2ZSwKICogdGhhdCB0aW1lIGlzIHJldHVybmVkLgogKgogKiBAcmV0dXJuIHRoZSB0aW1lIGluIG1zIHRoYXQgdGhlIGNhdGVnb3J5IGlzIGRpc2FibGVkIHVudGlsIG9yIDAgaWYgdGhlcmUncyBubyBhY3RpdmUgcmF0ZSBsaW1pdC4KICovCmZ1bmN0aW9uIGRpc2FibGVkVW50aWwobGltaXRzLCBjYXRlZ29yeSkgewogIHJldHVybiBsaW1pdHNbY2F0ZWdvcnldIHx8IGxpbWl0cy5hbGwgfHwgMDsKfQoKLyoqCiAqIENoZWNrcyBpZiBhIGNhdGVnb3J5IGlzIHJhdGUgbGltaXRlZAogKi8KZnVuY3Rpb24gaXNSYXRlTGltaXRlZChsaW1pdHMsIGNhdGVnb3J5LCBub3cgPSBEYXRlLm5vdygpKSB7CiAgcmV0dXJuIGRpc2FibGVkVW50aWwobGltaXRzLCBjYXRlZ29yeSkgPiBub3c7Cn0KCi8qKgogKiBVcGRhdGUgcmF0ZWxpbWl0cyBmcm9tIGluY29taW5nIGhlYWRlcnMuCiAqCiAqIEByZXR1cm4gdGhlIHVwZGF0ZWQgUmF0ZUxpbWl0cyBvYmplY3QuCiAqLwpmdW5jdGlvbiB1cGRhdGVSYXRlTGltaXRzKAogIGxpbWl0cywKICB7IHN0YXR1c0NvZGUsIGhlYWRlcnMgfSwKICBub3cgPSBEYXRlLm5vdygpLAopIHsKICBjb25zdCB1cGRhdGVkUmF0ZUxpbWl0cyA9IHsKICAgIC4uLmxpbWl0cywKICB9OwoKICAvLyAiVGhlIG5hbWUgaXMgY2FzZS1pbnNlbnNpdGl2ZS4iCiAgLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0hlYWRlcnMvZ2V0CiAgY29uc3QgcmF0ZUxpbWl0SGVhZGVyID0gaGVhZGVycyAmJiBoZWFkZXJzWyd4LXNlbnRyeS1yYXRlLWxpbWl0cyddOwogIGNvbnN0IHJldHJ5QWZ0ZXJIZWFkZXIgPSBoZWFkZXJzICYmIGhlYWRlcnNbJ3JldHJ5LWFmdGVyJ107CgogIGlmIChyYXRlTGltaXRIZWFkZXIpIHsKICAgIC8qKgogICAgICogcmF0ZSBsaW1pdCBoZWFkZXJzIGFyZSBvZiB0aGUgZm9ybQogICAgICogICAgIDxoZWFkZXI+LDxoZWFkZXI+LC4uCiAgICAgKiB3aGVyZSBlYWNoIDxoZWFkZXI+IGlzIG9mIHRoZSBmb3JtCiAgICAgKiAgICAgPHJldHJ5X2FmdGVyPjogPGNhdGVnb3JpZXM+OiA8c2NvcGU+OiA8cmVhc29uX2NvZGU+CiAgICAgKiB3aGVyZQogICAgICogICAgIDxyZXRyeV9hZnRlcj4gaXMgYSBkZWxheSBpbiBzZWNvbmRzCiAgICAgKiAgICAgPGNhdGVnb3JpZXM+IGlzIHRoZSBldmVudCB0eXBlKHMpIChlcnJvciwgdHJhbnNhY3Rpb24sIGV0YykgYmVpbmcgcmF0ZSBsaW1pdGVkIGFuZCBpcyBvZiB0aGUgZm9ybQogICAgICogICAgICAgICA8Y2F0ZWdvcnk+OzxjYXRlZ29yeT47Li4uCiAgICAgKiAgICAgPHNjb3BlPiBpcyB3aGF0J3MgYmVpbmcgbGltaXRlZCAob3JnLCBwcm9qZWN0LCBvciBrZXkpIC0gaWdub3JlZCBieSBTREsKICAgICAqICAgICA8cmVhc29uX2NvZGU+IGlzIGFuIGFyYml0cmFyeSBzdHJpbmcgbGlrZSAib3JnX3F1b3RhIiAtIGlnbm9yZWQgYnkgU0RLCiAgICAgKi8KICAgIGZvciAoY29uc3QgbGltaXQgb2YgcmF0ZUxpbWl0SGVhZGVyLnRyaW0oKS5zcGxpdCgnLCcpKSB7CiAgICAgIGNvbnN0IFtyZXRyeUFmdGVyLCBjYXRlZ29yaWVzXSA9IGxpbWl0LnNwbGl0KCc6JywgMik7CiAgICAgIGNvbnN0IGhlYWRlckRlbGF5ID0gcGFyc2VJbnQocmV0cnlBZnRlciwgMTApOwogICAgICBjb25zdCBkZWxheSA9ICghaXNOYU4oaGVhZGVyRGVsYXkpID8gaGVhZGVyRGVsYXkgOiA2MCkgKiAxMDAwOyAvLyA2MHNlYyBkZWZhdWx0CiAgICAgIGlmICghY2F0ZWdvcmllcykgewogICAgICAgIHVwZGF0ZWRSYXRlTGltaXRzLmFsbCA9IG5vdyArIGRlbGF5OwogICAgICB9IGVsc2UgewogICAgICAgIGZvciAoY29uc3QgY2F0ZWdvcnkgb2YgY2F0ZWdvcmllcy5zcGxpdCgnOycpKSB7CiAgICAgICAgICB1cGRhdGVkUmF0ZUxpbWl0c1tjYXRlZ29yeV0gPSBub3cgKyBkZWxheTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9IGVsc2UgaWYgKHJldHJ5QWZ0ZXJIZWFkZXIpIHsKICAgIHVwZGF0ZWRSYXRlTGltaXRzLmFsbCA9IG5vdyArIHBhcnNlUmV0cnlBZnRlckhlYWRlcihyZXRyeUFmdGVySGVhZGVyLCBub3cpOwogIH0gZWxzZSBpZiAoc3RhdHVzQ29kZSA9PT0gNDI5KSB7CiAgICB1cGRhdGVkUmF0ZUxpbWl0cy5hbGwgPSBub3cgKyA2MCAqIDEwMDA7CiAgfQoKICByZXR1cm4gdXBkYXRlZFJhdGVMaW1pdHM7Cn0KCi8qKgogKiBBIG5vZGUuanMgd2F0Y2hkb2cgdGltZXIKICogQHBhcmFtIHBvbGxJbnRlcnZhbCBUaGUgaW50ZXJ2YWwgdGhhdCB3ZSBleHBlY3QgdG8gZ2V0IHBvbGxlZCBhdAogKiBAcGFyYW0gYW5yVGhyZXNob2xkIFRoZSB0aHJlc2hvbGQgZm9yIHdoZW4gd2UgY29uc2lkZXIgQU5SCiAqIEBwYXJhbSBjYWxsYmFjayBUaGUgY2FsbGJhY2sgdG8gY2FsbCBmb3IgQU5SCiAqIEByZXR1cm5zIEFuIG9iamVjdCB3aXRoIGBwb2xsYCBhbmQgYGVuYWJsZWRgIGZ1bmN0aW9ucyB7QGxpbmsgV2F0Y2hkb2dSZXR1cm59CiAqLwpmdW5jdGlvbiB3YXRjaGRvZ1RpbWVyKAogIGNyZWF0ZVRpbWVyLAogIHBvbGxJbnRlcnZhbCwKICBhbnJUaHJlc2hvbGQsCiAgY2FsbGJhY2ssCikgewogIGNvbnN0IHRpbWVyID0gY3JlYXRlVGltZXIoKTsKICBsZXQgdHJpZ2dlcmVkID0gZmFsc2U7CiAgbGV0IGVuYWJsZWQgPSB0cnVlOwoKICBzZXRJbnRlcnZhbCgoKSA9PiB7CiAgICBjb25zdCBkaWZmTXMgPSB0aW1lci5nZXRUaW1lTXMoKTsKCiAgICBpZiAodHJpZ2dlcmVkID09PSBmYWxzZSAmJiBkaWZmTXMgPiBwb2xsSW50ZXJ2YWwgKyBhbnJUaHJlc2hvbGQpIHsKICAgICAgdHJpZ2dlcmVkID0gdHJ1ZTsKICAgICAgaWYgKGVuYWJsZWQpIHsKICAgICAgICBjYWxsYmFjaygpOwogICAgICB9CiAgICB9CgogICAgaWYgKGRpZmZNcyA8IHBvbGxJbnRlcnZhbCArIGFuclRocmVzaG9sZCkgewogICAgICB0cmlnZ2VyZWQgPSBmYWxzZTsKICAgIH0KICB9LCAyMCk7CgogIHJldHVybiB7CiAgICBwb2xsOiAoKSA9PiB7CiAgICAgIHRpbWVyLnJlc2V0KCk7CiAgICB9LAogICAgZW5hYmxlZDogKHN0YXRlKSA9PiB7CiAgICAgIGVuYWJsZWQgPSBzdGF0ZTsKICAgIH0sCiAgfTsKfQoKLy8gdHlwZXMgY29waWVkIGZyb20gaW5zcGVjdG9yLmQudHMKCi8qKgogKiBDb252ZXJ0cyBEZWJ1Z2dlci5DYWxsRnJhbWUgdG8gU2VudHJ5IFN0YWNrRnJhbWUKICovCmZ1bmN0aW9uIGNhbGxGcmFtZVRvU3RhY2tGcmFtZSgKICBmcmFtZSwKICB1cmwsCiAgZ2V0TW9kdWxlRnJvbUZpbGVuYW1lLAopIHsKICBjb25zdCBmaWxlbmFtZSA9IHVybCA/IHVybC5yZXBsYWNlKC9eZmlsZTpcL1wvLywgJycpIDogdW5kZWZpbmVkOwoKICAvLyBDYWxsRnJhbWUgcm93L2NvbCBhcmUgMCBiYXNlZCwgd2hlcmVhcyBTdGFja0ZyYW1lIGFyZSAxIGJhc2VkCiAgY29uc3QgY29sbm8gPSBmcmFtZS5sb2NhdGlvbi5jb2x1bW5OdW1iZXIgPyBmcmFtZS5sb2NhdGlvbi5jb2x1bW5OdW1iZXIgKyAxIDogdW5kZWZpbmVkOwogIGNvbnN0IGxpbmVubyA9IGZyYW1lLmxvY2F0aW9uLmxpbmVOdW1iZXIgPyBmcmFtZS5sb2NhdGlvbi5saW5lTnVtYmVyICsgMSA6IHVuZGVmaW5lZDsKCiAgcmV0dXJuIGRyb3BVbmRlZmluZWRLZXlzKHsKICAgIGZpbGVuYW1lLAogICAgbW9kdWxlOiBnZXRNb2R1bGVGcm9tRmlsZW5hbWUoZmlsZW5hbWUpLAogICAgZnVuY3Rpb246IGZyYW1lLmZ1bmN0aW9uTmFtZSB8fCBVTktOT1dOX0ZVTkNUSU9OLAogICAgY29sbm8sCiAgICBsaW5lbm8sCiAgICBpbl9hcHA6IGZpbGVuYW1lID8gZmlsZW5hbWVJc0luQXBwKGZpbGVuYW1lKSA6IHVuZGVmaW5lZCwKICB9KTsKfQoKLyoqCiAqIFRoaXMgc2VydmVzIGFzIGEgYnVpbGQgdGltZSBmbGFnIHRoYXQgd2lsbCBiZSB0cnVlIGJ5IGRlZmF1bHQsIGJ1dCBmYWxzZSBpbiBub24tZGVidWcgYnVpbGRzIG9yIGlmIHVzZXJzIHJlcGxhY2UgYF9fU0VOVFJZX0RFQlVHX19gIGluIHRoZWlyIGdlbmVyYXRlZCBjb2RlLgogKgogKiBBVFRFTlRJT046IFRoaXMgY29uc3RhbnQgbXVzdCBuZXZlciBjcm9zcyBwYWNrYWdlIGJvdW5kYXJpZXMgKGkuZS4gYmUgZXhwb3J0ZWQpIHRvIGd1YXJhbnRlZSB0aGF0IGl0IGNhbiBiZSB1c2VkIGZvciB0cmVlIHNoYWtpbmcuCiAqLwpjb25zdCBERUJVR19CVUlMRCA9ICh0eXBlb2YgX19TRU5UUllfREVCVUdfXyA9PT0gJ3VuZGVmaW5lZCcgfHwgX19TRU5UUllfREVCVUdfXyk7CgovKioKICogQHByaXZhdGUgUHJpdmF0ZSBBUEkgd2l0aCBubyBzZW12ZXIgZ3VhcmFudGVlcyEKICoKICogU3RyYXRlZ3kgdXNlZCB0byB0cmFjayBhc3luYyBjb250ZXh0LgogKi8KCi8qKgogKiBSZXR1cm5zIHRoZSBnbG9iYWwgc2hpbSByZWdpc3RyeS4KICoKICogRklYTUU6IFRoaXMgZnVuY3Rpb24gaXMgcHJvYmxlbWF0aWMsIGJlY2F1c2UgZGVzcGl0ZSBhbHdheXMgcmV0dXJuaW5nIGEgdmFsaWQgQ2FycmllciwKICogaXQgaGFzIGFuIG9wdGlvbmFsIGBfX1NFTlRSWV9fYCBwcm9wZXJ0eSwgd2hpY2ggdGhlbiBpbiB0dXJuIHJlcXVpcmVzIHVzIHRvIGFsd2F5cyBwZXJmb3JtIGFuIHVubmVjZXNzYXJ5IGNoZWNrCiAqIGF0IHRoZSBjYWxsLXNpdGUuIFdlIGFsd2F5cyBhY2Nlc3MgdGhlIGNhcnJpZXIgdGhyb3VnaCB0aGlzIGZ1bmN0aW9uLCBzbyB3ZSBjYW4gZ3VhcmFudGVlIHRoYXQgYF9fU0VOVFJZX19gIGlzIHRoZXJlLgogKiovCmZ1bmN0aW9uIGdldE1haW5DYXJyaWVyKCkgewogIC8vIFRoaXMgZW5zdXJlcyBhIFNlbnRyeSBjYXJyaWVyIGV4aXN0cwogIGdldFNlbnRyeUNhcnJpZXIoR0xPQkFMX09CSik7CiAgcmV0dXJuIEdMT0JBTF9PQko7Cn0KCi8qKiBXaWxsIGVpdGhlciBnZXQgdGhlIGV4aXN0aW5nIHNlbnRyeSBjYXJyaWVyLCBvciBjcmVhdGUgYSBuZXcgb25lLiAqLwpmdW5jdGlvbiBnZXRTZW50cnlDYXJyaWVyKGNhcnJpZXIpIHsKICBpZiAoIWNhcnJpZXIuX19TRU5UUllfXykgewogICAgY2Fycmllci5fX1NFTlRSWV9fID0gewogICAgICBleHRlbnNpb25zOiB7fSwKICAgIH07CiAgfQogIHJldHVybiBjYXJyaWVyLl9fU0VOVFJZX187Cn0KCmNvbnN0IERFRkFVTFRfRU5WSVJPTk1FTlQgPSAncHJvZHVjdGlvbic7CgovKioKICogQ3JlYXRlcyBhIG5ldyBgU2Vzc2lvbmAgb2JqZWN0IGJ5IHNldHRpbmcgY2VydGFpbiBkZWZhdWx0IHBhcmFtZXRlcnMuIElmIG9wdGlvbmFsIEBwYXJhbSBjb250ZXh0CiAqIGlzIHBhc3NlZCwgdGhlIHBhc3NlZCBwcm9wZXJ0aWVzIGFyZSBhcHBsaWVkIHRvIHRoZSBzZXNzaW9uIG9iamVjdC4KICoKICogQHBhcmFtIGNvbnRleHQgKG9wdGlvbmFsKSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgdG8gYmUgYXBwbGllZCB0byB0aGUgcmV0dXJuZWQgc2Vzc2lvbiBvYmplY3QKICoKICogQHJldHVybnMgYSBuZXcgYFNlc3Npb25gIG9iamVjdAogKi8KZnVuY3Rpb24gbWFrZVNlc3Npb24oY29udGV4dCkgewogIC8vIEJvdGggdGltZXN0YW1wIGFuZCBzdGFydGVkIGFyZSBpbiBzZWNvbmRzIHNpbmNlIHRoZSBVTklYIGVwb2NoLgogIGNvbnN0IHN0YXJ0aW5nVGltZSA9IHRpbWVzdGFtcEluU2Vjb25kcygpOwoKICBjb25zdCBzZXNzaW9uID0gewogICAgc2lkOiB1dWlkNCgpLAogICAgaW5pdDogdHJ1ZSwKICAgIHRpbWVzdGFtcDogc3RhcnRpbmdUaW1lLAogICAgc3RhcnRlZDogc3RhcnRpbmdUaW1lLAogICAgZHVyYXRpb246IDAsCiAgICBzdGF0dXM6ICdvaycsCiAgICBlcnJvcnM6IDAsCiAgICBpZ25vcmVEdXJhdGlvbjogZmFsc2UsCiAgICB0b0pTT046ICgpID0+IHNlc3Npb25Ub0pTT04oc2Vzc2lvbiksCiAgfTsKCiAgaWYgKGNvbnRleHQpIHsKICAgIHVwZGF0ZVNlc3Npb24oc2Vzc2lvbiwgY29udGV4dCk7CiAgfQoKICByZXR1cm4gc2Vzc2lvbjsKfQoKLyoqCiAqIFVwZGF0ZXMgYSBzZXNzaW9uIG9iamVjdCB3aXRoIHRoZSBwcm9wZXJ0aWVzIHBhc3NlZCBpbiB0aGUgY29udGV4dC4KICoKICogTm90ZSB0aGF0IHRoaXMgZnVuY3Rpb24gbXV0YXRlcyB0aGUgcGFzc2VkIG9iamVjdCBhbmQgcmV0dXJucyB2b2lkLgogKiAoSGFkIHRvIGRvIHRoaXMgaW5zdGVhZCBvZiByZXR1cm5pbmcgYSBuZXcgYW5kIHVwZGF0ZWQgc2Vzc2lvbiBiZWNhdXNlIGNsb3NpbmcgYW5kIHNlbmRpbmcgYSBzZXNzaW9uCiAqIG1ha2VzIGFuIHVwZGF0ZSB0byB0aGUgc2Vzc2lvbiBhZnRlciBpdCB3YXMgcGFzc2VkIHRvIHRoZSBzZW5kaW5nIGxvZ2ljLgogKiBAc2VlIEJhc2VDbGllbnQuY2FwdHVyZVNlc3Npb24gKQogKgogKiBAcGFyYW0gc2Vzc2lvbiB0aGUgYFNlc3Npb25gIHRvIHVwZGF0ZQogKiBAcGFyYW0gY29udGV4dCB0aGUgYFNlc3Npb25Db250ZXh0YCBob2xkaW5nIHRoZSBwcm9wZXJ0aWVzIHRoYXQgc2hvdWxkIGJlIHVwZGF0ZWQgaW4gQHBhcmFtIHNlc3Npb24KICovCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5CmZ1bmN0aW9uIHVwZGF0ZVNlc3Npb24oc2Vzc2lvbiwgY29udGV4dCA9IHt9KSB7CiAgaWYgKGNvbnRleHQudXNlcikgewogICAgaWYgKCFzZXNzaW9uLmlwQWRkcmVzcyAmJiBjb250ZXh0LnVzZXIuaXBfYWRkcmVzcykgewogICAgICBzZXNzaW9uLmlwQWRkcmVzcyA9IGNvbnRleHQudXNlci5pcF9hZGRyZXNzOwogICAgfQoKICAgIGlmICghc2Vzc2lvbi5kaWQgJiYgIWNvbnRleHQuZGlkKSB7CiAgICAgIHNlc3Npb24uZGlkID0gY29udGV4dC51c2VyLmlkIHx8IGNvbnRleHQudXNlci5lbWFpbCB8fCBjb250ZXh0LnVzZXIudXNlcm5hbWU7CiAgICB9CiAgfQoKICBzZXNzaW9uLnRpbWVzdGFtcCA9IGNvbnRleHQudGltZXN0YW1wIHx8IHRpbWVzdGFtcEluU2Vjb25kcygpOwoKICBpZiAoY29udGV4dC5hYm5vcm1hbF9tZWNoYW5pc20pIHsKICAgIHNlc3Npb24uYWJub3JtYWxfbWVjaGFuaXNtID0gY29udGV4dC5hYm5vcm1hbF9tZWNoYW5pc207CiAgfQoKICBpZiAoY29udGV4dC5pZ25vcmVEdXJhdGlvbikgewogICAgc2Vzc2lvbi5pZ25vcmVEdXJhdGlvbiA9IGNvbnRleHQuaWdub3JlRHVyYXRpb247CiAgfQogIGlmIChjb250ZXh0LnNpZCkgewogICAgLy8gR29vZCBlbm91Z2ggdXVpZCB2YWxpZGF0aW9uLiDigJQgS2FtaWwKICAgIHNlc3Npb24uc2lkID0gY29udGV4dC5zaWQubGVuZ3RoID09PSAzMiA/IGNvbnRleHQuc2lkIDogdXVpZDQoKTsKICB9CiAgaWYgKGNvbnRleHQuaW5pdCAhPT0gdW5kZWZpbmVkKSB7CiAgICBzZXNzaW9uLmluaXQgPSBjb250ZXh0LmluaXQ7CiAgfQogIGlmICghc2Vzc2lvbi5kaWQgJiYgY29udGV4dC5kaWQpIHsKICAgIHNlc3Npb24uZGlkID0gYCR7Y29udGV4dC5kaWR9YDsKICB9CiAgaWYgKHR5cGVvZiBjb250ZXh0LnN0YXJ0ZWQgPT09ICdudW1iZXInKSB7CiAgICBzZXNzaW9uLnN0YXJ0ZWQgPSBjb250ZXh0LnN0YXJ0ZWQ7CiAgfQogIGlmIChzZXNzaW9uLmlnbm9yZUR1cmF0aW9uKSB7CiAgICBzZXNzaW9uLmR1cmF0aW9uID0gdW5kZWZpbmVkOwogIH0gZWxzZSBpZiAodHlwZW9mIGNvbnRleHQuZHVyYXRpb24gPT09ICdudW1iZXInKSB7CiAgICBzZXNzaW9uLmR1cmF0aW9uID0gY29udGV4dC5kdXJhdGlvbjsKICB9IGVsc2UgewogICAgY29uc3QgZHVyYXRpb24gPSBzZXNzaW9uLnRpbWVzdGFtcCAtIHNlc3Npb24uc3RhcnRlZDsKICAgIHNlc3Npb24uZHVyYXRpb24gPSBkdXJhdGlvbiA+PSAwID8gZHVyYXRpb24gOiAwOwogIH0KICBpZiAoY29udGV4dC5yZWxlYXNlKSB7CiAgICBzZXNzaW9uLnJlbGVhc2UgPSBjb250ZXh0LnJlbGVhc2U7CiAgfQogIGlmIChjb250ZXh0LmVudmlyb25tZW50KSB7CiAgICBzZXNzaW9uLmVudmlyb25tZW50ID0gY29udGV4dC5lbnZpcm9ubWVudDsKICB9CiAgaWYgKCFzZXNzaW9uLmlwQWRkcmVzcyAmJiBjb250ZXh0LmlwQWRkcmVzcykgewogICAgc2Vzc2lvbi5pcEFkZHJlc3MgPSBjb250ZXh0LmlwQWRkcmVzczsKICB9CiAgaWYgKCFzZXNzaW9uLnVzZXJBZ2VudCAmJiBjb250ZXh0LnVzZXJBZ2VudCkgewogICAgc2Vzc2lvbi51c2VyQWdlbnQgPSBjb250ZXh0LnVzZXJBZ2VudDsKICB9CiAgaWYgKHR5cGVvZiBjb250ZXh0LmVycm9ycyA9PT0gJ251bWJlcicpIHsKICAgIHNlc3Npb24uZXJyb3JzID0gY29udGV4dC5lcnJvcnM7CiAgfQogIGlmIChjb250ZXh0LnN0YXR1cykgewogICAgc2Vzc2lvbi5zdGF0dXMgPSBjb250ZXh0LnN0YXR1czsKICB9Cn0KCi8qKgogKiBDbG9zZXMgYSBzZXNzaW9uIGJ5IHNldHRpbmcgaXRzIHN0YXR1cyBhbmQgdXBkYXRpbmcgdGhlIHNlc3Npb24gb2JqZWN0IHdpdGggaXQuCiAqIEludGVybmFsbHkgY2FsbHMgYHVwZGF0ZVNlc3Npb25gIHRvIHVwZGF0ZSB0aGUgcGFzc2VkIHNlc3Npb24gb2JqZWN0LgogKgogKiBOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBtdXRhdGVzIHRoZSBwYXNzZWQgc2Vzc2lvbiAoQHNlZSB1cGRhdGVTZXNzaW9uIGZvciBleHBsYW5hdGlvbikuCiAqCiAqIEBwYXJhbSBzZXNzaW9uIHRoZSBgU2Vzc2lvbmAgb2JqZWN0IHRvIGJlIGNsb3NlZAogKiBAcGFyYW0gc3RhdHVzIHRoZSBgU2Vzc2lvblN0YXR1c2Agd2l0aCB3aGljaCB0aGUgc2Vzc2lvbiB3YXMgY2xvc2VkLiBJZiB5b3UgZG9uJ3QgcGFzcyBhIHN0YXR1cywKICogICAgICAgICAgICAgICB0aGlzIGZ1bmN0aW9uIHdpbGwga2VlcCB0aGUgcHJldmlvdXNseSBzZXQgc3RhdHVzLCB1bmxlc3MgaXQgd2FzIGAnb2snYCBpbiB3aGljaCBjYXNlCiAqICAgICAgICAgICAgICAgaXQgaXMgY2hhbmdlZCB0byBgJ2V4aXRlZCdgLgogKi8KZnVuY3Rpb24gY2xvc2VTZXNzaW9uKHNlc3Npb24sIHN0YXR1cykgewogIGxldCBjb250ZXh0ID0ge307CiAgaWYgKHN0YXR1cykgewogICAgY29udGV4dCA9IHsgc3RhdHVzIH07CiAgfSBlbHNlIGlmIChzZXNzaW9uLnN0YXR1cyA9PT0gJ29rJykgewogICAgY29udGV4dCA9IHsgc3RhdHVzOiAnZXhpdGVkJyB9OwogIH0KCiAgdXBkYXRlU2Vzc2lvbihzZXNzaW9uLCBjb250ZXh0KTsKfQoKLyoqCiAqIFNlcmlhbGl6ZXMgYSBwYXNzZWQgc2Vzc2lvbiBvYmplY3QgdG8gYSBKU09OIG9iamVjdCB3aXRoIGEgc2xpZ2h0bHkgZGlmZmVyZW50IHN0cnVjdHVyZS4KICogVGhpcyBpcyBuZWNlc3NhcnkgYmVjYXVzZSB0aGUgU2VudHJ5IGJhY2tlbmQgcmVxdWlyZXMgYSBzbGlnaHRseSBkaWZmZXJlbnQgc2NoZW1hIG9mIGEgc2Vzc2lvbgogKiB0aGFuIHRoZSBvbmUgdGhlIEpTIFNES3MgdXNlIGludGVybmFsbHkuCiAqCiAqIEBwYXJhbSBzZXNzaW9uIHRoZSBzZXNzaW9uIHRvIGJlIGNvbnZlcnRlZAogKgogKiBAcmV0dXJucyBhIEpTT04gb2JqZWN0IG9mIHRoZSBwYXNzZWQgc2Vzc2lvbgogKi8KZnVuY3Rpb24gc2Vzc2lvblRvSlNPTihzZXNzaW9uKSB7CiAgcmV0dXJuIGRyb3BVbmRlZmluZWRLZXlzKHsKICAgIHNpZDogYCR7c2Vzc2lvbi5zaWR9YCwKICAgIGluaXQ6IHNlc3Npb24uaW5pdCwKICAgIC8vIE1ha2Ugc3VyZSB0aGF0IHNlYyBpcyBjb252ZXJ0ZWQgdG8gbXMgZm9yIGRhdGUgY29uc3RydWN0b3IKICAgIHN0YXJ0ZWQ6IG5ldyBEYXRlKHNlc3Npb24uc3RhcnRlZCAqIDEwMDApLnRvSVNPU3RyaW5nKCksCiAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKHNlc3Npb24udGltZXN0YW1wICogMTAwMCkudG9JU09TdHJpbmcoKSwKICAgIHN0YXR1czogc2Vzc2lvbi5zdGF0dXMsCiAgICBlcnJvcnM6IHNlc3Npb24uZXJyb3JzLAogICAgZGlkOiB0eXBlb2Ygc2Vzc2lvbi5kaWQgPT09ICdudW1iZXInIHx8IHR5cGVvZiBzZXNzaW9uLmRpZCA9PT0gJ3N0cmluZycgPyBgJHtzZXNzaW9uLmRpZH1gIDogdW5kZWZpbmVkLAogICAgZHVyYXRpb246IHNlc3Npb24uZHVyYXRpb24sCiAgICBhYm5vcm1hbF9tZWNoYW5pc206IHNlc3Npb24uYWJub3JtYWxfbWVjaGFuaXNtLAogICAgYXR0cnM6IHsKICAgICAgcmVsZWFzZTogc2Vzc2lvbi5yZWxlYXNlLAogICAgICBlbnZpcm9ubWVudDogc2Vzc2lvbi5lbnZpcm9ubWVudCwKICAgICAgaXBfYWRkcmVzczogc2Vzc2lvbi5pcEFkZHJlc3MsCiAgICAgIHVzZXJfYWdlbnQ6IHNlc3Npb24udXNlckFnZW50LAogICAgfSwKICB9KTsKfQoKLyoqCiAqIERlZmF1bHQgdmFsdWUgZm9yIG1heGltdW0gbnVtYmVyIG9mIGJyZWFkY3J1bWJzIGFkZGVkIHRvIGFuIGV2ZW50LgogKi8KY29uc3QgREVGQVVMVF9NQVhfQlJFQURDUlVNQlMgPSAxMDA7CgovKioKICogSG9sZHMgYWRkaXRpb25hbCBldmVudCBpbmZvcm1hdGlvbi4KICovCmNsYXNzIFNjb3BlICB7CiAgLyoqIEZsYWcgaWYgbm90aWZ5aW5nIGlzIGhhcHBlbmluZy4gKi8KCiAgLyoqIENhbGxiYWNrIGZvciBjbGllbnQgdG8gcmVjZWl2ZSBzY29wZSBjaGFuZ2VzLiAqLwoKICAvKiogQ2FsbGJhY2sgbGlzdCB0aGF0IHdpbGwgYmUgY2FsbGVkIGR1cmluZyBldmVudCBwcm9jZXNzaW5nLiAqLwoKICAvKiogQXJyYXkgb2YgYnJlYWRjcnVtYnMuICovCgogIC8qKiBVc2VyICovCgogIC8qKiBUYWdzICovCgogIC8qKiBFeHRyYSAqLwoKICAvKiogQ29udGV4dHMgKi8KCiAgLyoqIEF0dGFjaG1lbnRzICovCgogIC8qKiBQcm9wYWdhdGlvbiBDb250ZXh0IGZvciBkaXN0cmlidXRlZCB0cmFjaW5nICovCgogIC8qKgogICAqIEEgcGxhY2UgdG8gc3Rhc2ggZGF0YSB3aGljaCBpcyBuZWVkZWQgYXQgc29tZSBwb2ludCBpbiB0aGUgU0RLJ3MgZXZlbnQgcHJvY2Vzc2luZyBwaXBlbGluZSBidXQgd2hpY2ggc2hvdWxkbid0IGdldAogICAqIHNlbnQgdG8gU2VudHJ5CiAgICovCgogIC8qKiBGaW5nZXJwcmludCAqLwoKICAvKiogU2V2ZXJpdHkgKi8KCiAgLyoqCiAgICogVHJhbnNhY3Rpb24gTmFtZQogICAqCiAgICogSU1QT1JUQU5UOiBUaGUgdHJhbnNhY3Rpb24gbmFtZSBvbiB0aGUgc2NvcGUgaGFzIG5vdGhpbmcgdG8gZG8gd2l0aCByb290IHNwYW5zL3RyYW5zYWN0aW9uIG9iamVjdHMuCiAgICogSXQncyBwdXJwb3NlIGlzIHRvIGFzc2lnbiBhIHRyYW5zYWN0aW9uIHRvIHRoZSBzY29wZSB0aGF0J3MgYWRkZWQgdG8gbm9uLXRyYW5zYWN0aW9uIGV2ZW50cy4KICAgKi8KCiAgLyoqIFNwYW4gKi8KCiAgLyoqIFNlc3Npb24gKi8KCiAgLyoqIFJlcXVlc3QgTW9kZSBTZXNzaW9uIFN0YXR1cyAqLwoKICAvKiogVGhlIGNsaWVudCBvbiB0aGlzIHNjb3BlICovCgogIC8vIE5PVEU6IEFueSBmaWVsZCB3aGljaCBnZXRzIGFkZGVkIGhlcmUgc2hvdWxkIGdldCBhZGRlZCBub3Qgb25seSB0byB0aGUgY29uc3RydWN0b3IgYnV0IGFsc28gdG8gdGhlIGBjbG9uZWAgbWV0aG9kLgoKICAgY29uc3RydWN0b3IoKSB7CiAgICB0aGlzLl9ub3RpZnlpbmdMaXN0ZW5lcnMgPSBmYWxzZTsKICAgIHRoaXMuX3Njb3BlTGlzdGVuZXJzID0gW107CiAgICB0aGlzLl9ldmVudFByb2Nlc3NvcnMgPSBbXTsKICAgIHRoaXMuX2JyZWFkY3J1bWJzID0gW107CiAgICB0aGlzLl9hdHRhY2htZW50cyA9IFtdOwogICAgdGhpcy5fdXNlciA9IHt9OwogICAgdGhpcy5fdGFncyA9IHt9OwogICAgdGhpcy5fZXh0cmEgPSB7fTsKICAgIHRoaXMuX2NvbnRleHRzID0ge307CiAgICB0aGlzLl9zZGtQcm9jZXNzaW5nTWV0YWRhdGEgPSB7fTsKICAgIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCA9IGdlbmVyYXRlUHJvcGFnYXRpb25Db250ZXh0KCk7CiAgfQoKICAvKioKICAgKiBJbmhlcml0IHZhbHVlcyBmcm9tIHRoZSBwYXJlbnQgc2NvcGUuCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBzY29wZS5jbG9uZSgpYCBhbmQgYG5ldyBTY29wZSgpYCBpbnN0ZWFkLgogICAqLwogICBzdGF0aWMgY2xvbmUoc2NvcGUpIHsKICAgIHJldHVybiBzY29wZSA/IHNjb3BlLmNsb25lKCkgOiBuZXcgU2NvcGUoKTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNsb25lKCkgewogICAgY29uc3QgbmV3U2NvcGUgPSBuZXcgU2NvcGUoKTsKICAgIG5ld1Njb3BlLl9icmVhZGNydW1icyA9IFsuLi50aGlzLl9icmVhZGNydW1ic107CiAgICBuZXdTY29wZS5fdGFncyA9IHsgLi4udGhpcy5fdGFncyB9OwogICAgbmV3U2NvcGUuX2V4dHJhID0geyAuLi50aGlzLl9leHRyYSB9OwogICAgbmV3U2NvcGUuX2NvbnRleHRzID0geyAuLi50aGlzLl9jb250ZXh0cyB9OwogICAgbmV3U2NvcGUuX3VzZXIgPSB0aGlzLl91c2VyOwogICAgbmV3U2NvcGUuX2xldmVsID0gdGhpcy5fbGV2ZWw7CiAgICBuZXdTY29wZS5fc3BhbiA9IHRoaXMuX3NwYW47CiAgICBuZXdTY29wZS5fc2Vzc2lvbiA9IHRoaXMuX3Nlc3Npb247CiAgICBuZXdTY29wZS5fdHJhbnNhY3Rpb25OYW1lID0gdGhpcy5fdHJhbnNhY3Rpb25OYW1lOwogICAgbmV3U2NvcGUuX2ZpbmdlcnByaW50ID0gdGhpcy5fZmluZ2VycHJpbnQ7CiAgICBuZXdTY29wZS5fZXZlbnRQcm9jZXNzb3JzID0gWy4uLnRoaXMuX2V2ZW50UHJvY2Vzc29yc107CiAgICBuZXdTY29wZS5fcmVxdWVzdFNlc3Npb24gPSB0aGlzLl9yZXF1ZXN0U2Vzc2lvbjsKICAgIG5ld1Njb3BlLl9hdHRhY2htZW50cyA9IFsuLi50aGlzLl9hdHRhY2htZW50c107CiAgICBuZXdTY29wZS5fc2RrUHJvY2Vzc2luZ01ldGFkYXRhID0geyAuLi50aGlzLl9zZGtQcm9jZXNzaW5nTWV0YWRhdGEgfTsKICAgIG5ld1Njb3BlLl9wcm9wYWdhdGlvbkNvbnRleHQgPSB7IC4uLnRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCB9OwogICAgbmV3U2NvcGUuX2NsaWVudCA9IHRoaXMuX2NsaWVudDsKCiAgICByZXR1cm4gbmV3U2NvcGU7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRDbGllbnQoY2xpZW50KSB7CiAgICB0aGlzLl9jbGllbnQgPSBjbGllbnQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRDbGllbnQoKSB7CiAgICByZXR1cm4gdGhpcy5fY2xpZW50IDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGFkZFNjb3BlTGlzdGVuZXIoY2FsbGJhY2spIHsKICAgIHRoaXMuX3Njb3BlTGlzdGVuZXJzLnB1c2goY2FsbGJhY2spOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgYWRkRXZlbnRQcm9jZXNzb3IoY2FsbGJhY2spIHsKICAgIHRoaXMuX2V2ZW50UHJvY2Vzc29ycy5wdXNoKGNhbGxiYWNrKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0VXNlcih1c2VyKSB7CiAgICAvLyBJZiBudWxsIGlzIHBhc3NlZCB3ZSB3YW50IHRvIHVuc2V0IGV2ZXJ5dGhpbmcsIGJ1dCBzdGlsbCBkZWZpbmUga2V5cywKICAgIC8vIHNvIHRoYXQgbGF0ZXIgZG93biBpbiB0aGUgcGlwZWxpbmUgYW55IGV4aXN0aW5nIHZhbHVlcyBhcmUgY2xlYXJlZC4KICAgIHRoaXMuX3VzZXIgPSB1c2VyIHx8IHsKICAgICAgZW1haWw6IHVuZGVmaW5lZCwKICAgICAgaWQ6IHVuZGVmaW5lZCwKICAgICAgaXBfYWRkcmVzczogdW5kZWZpbmVkLAogICAgICB1c2VybmFtZTogdW5kZWZpbmVkLAogICAgfTsKCiAgICBpZiAodGhpcy5fc2Vzc2lvbikgewogICAgICB1cGRhdGVTZXNzaW9uKHRoaXMuX3Nlc3Npb24sIHsgdXNlciB9KTsKICAgIH0KCiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRVc2VyKCkgewogICAgcmV0dXJuIHRoaXMuX3VzZXI7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRSZXF1ZXN0U2Vzc2lvbigpIHsKICAgIHJldHVybiB0aGlzLl9yZXF1ZXN0U2Vzc2lvbjsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldFJlcXVlc3RTZXNzaW9uKHJlcXVlc3RTZXNzaW9uKSB7CiAgICB0aGlzLl9yZXF1ZXN0U2Vzc2lvbiA9IHJlcXVlc3RTZXNzaW9uOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRUYWdzKHRhZ3MpIHsKICAgIHRoaXMuX3RhZ3MgPSB7CiAgICAgIC4uLnRoaXMuX3RhZ3MsCiAgICAgIC4uLnRhZ3MsCiAgICB9OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0VGFnKGtleSwgdmFsdWUpIHsKICAgIHRoaXMuX3RhZ3MgPSB7IC4uLnRoaXMuX3RhZ3MsIFtrZXldOiB2YWx1ZSB9OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0RXh0cmFzKGV4dHJhcykgewogICAgdGhpcy5fZXh0cmEgPSB7CiAgICAgIC4uLnRoaXMuX2V4dHJhLAogICAgICAuLi5leHRyYXMsCiAgICB9OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0RXh0cmEoa2V5LCBleHRyYSkgewogICAgdGhpcy5fZXh0cmEgPSB7IC4uLnRoaXMuX2V4dHJhLCBba2V5XTogZXh0cmEgfTsKICAgIHRoaXMuX25vdGlmeVNjb3BlTGlzdGVuZXJzKCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldEZpbmdlcnByaW50KGZpbmdlcnByaW50KSB7CiAgICB0aGlzLl9maW5nZXJwcmludCA9IGZpbmdlcnByaW50OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0TGV2ZWwobGV2ZWwpIHsKICAgIHRoaXMuX2xldmVsID0gbGV2ZWw7CiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRUcmFuc2FjdGlvbk5hbWUobmFtZSkgewogICAgdGhpcy5fdHJhbnNhY3Rpb25OYW1lID0gbmFtZTsKICAgIHRoaXMuX25vdGlmeVNjb3BlTGlzdGVuZXJzKCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldENvbnRleHQoa2V5LCBjb250ZXh0KSB7CiAgICBpZiAoY29udGV4dCA9PT0gbnVsbCkgewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWR5bmFtaWMtZGVsZXRlCiAgICAgIGRlbGV0ZSB0aGlzLl9jb250ZXh0c1trZXldOwogICAgfSBlbHNlIHsKICAgICAgdGhpcy5fY29udGV4dHNba2V5XSA9IGNvbnRleHQ7CiAgICB9CgogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogU2V0cyB0aGUgU3BhbiBvbiB0aGUgc2NvcGUuCiAgICogQHBhcmFtIHNwYW4gU3BhbgogICAqIEBkZXByZWNhdGVkIEluc3RlYWQgb2Ygc2V0dGluZyBhIHNwYW4gb24gYSBzY29wZSwgdXNlIGBzdGFydFNwYW4oKWAvYHN0YXJ0U3Bhbk1hbnVhbCgpYCBpbnN0ZWFkLgogICAqLwogICBzZXRTcGFuKHNwYW4pIHsKICAgIHRoaXMuX3NwYW4gPSBzcGFuOwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgYFNwYW5gIGlmIHRoZXJlIGlzIG9uZS4KICAgKiBAZGVwcmVjYXRlZCBVc2UgYGdldEFjdGl2ZVNwYW4oKWAgaW5zdGVhZC4KICAgKi8KICAgZ2V0U3BhbigpIHsKICAgIHJldHVybiB0aGlzLl9zcGFuOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgYFRyYW5zYWN0aW9uYCBhdHRhY2hlZCB0byB0aGUgc2NvcGUgKGlmIHRoZXJlIGlzIG9uZSkuCiAgICogQGRlcHJlY2F0ZWQgWW91IHNob3VsZCBub3QgcmVseSBvbiB0aGUgdHJhbnNhY3Rpb24sIGJ1dCBqdXN0IHVzZSBgc3RhcnRTcGFuKClgIEFQSXMgaW5zdGVhZC4KICAgKi8KICAgZ2V0VHJhbnNhY3Rpb24oKSB7CiAgICAvLyBPZnRlbiwgdGhpcyBzcGFuIChpZiBpdCBleGlzdHMgYXQgYWxsKSB3aWxsIGJlIGEgdHJhbnNhY3Rpb24sIGJ1dCBpdCdzIG5vdCBndWFyYW50ZWVkIHRvIGJlLiBSZWdhcmRsZXNzLCBpdCB3aWxsCiAgICAvLyBoYXZlIGEgcG9pbnRlciB0byB0aGUgY3VycmVudGx5LWFjdGl2ZSB0cmFuc2FjdGlvbi4KICAgIGNvbnN0IHNwYW4gPSB0aGlzLl9zcGFuOwoKICAgIC8vIENhbm5vdCByZXBsYWNlIHdpdGggZ2V0Um9vdFNwYW4gYmVjYXVzZSBnZXRSb290U3BhbiByZXR1cm5zIGEgc3Bhbiwgbm90IGEgdHJhbnNhY3Rpb24KICAgIC8vIEFsc28sIHRoaXMgbWV0aG9kIHdpbGwgYmUgcmVtb3ZlZCBhbnl3YXkuCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGlmIChzcGFuICYmIChzcGFuICkudHJhbnNhY3Rpb24pIHsKICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgIHJldHVybiAoc3BhbiApLnRyYW5zYWN0aW9uOwogICAgfQogICAgcmV0dXJuIHVuZGVmaW5lZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldFNlc3Npb24oc2Vzc2lvbikgewogICAgaWYgKCFzZXNzaW9uKSB7CiAgICAgIGRlbGV0ZSB0aGlzLl9zZXNzaW9uOwogICAgfSBlbHNlIHsKICAgICAgdGhpcy5fc2Vzc2lvbiA9IHNlc3Npb247CiAgICB9CiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRTZXNzaW9uKCkgewogICAgcmV0dXJuIHRoaXMuX3Nlc3Npb247CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICB1cGRhdGUoY2FwdHVyZUNvbnRleHQpIHsKICAgIGlmICghY2FwdHVyZUNvbnRleHQpIHsKICAgICAgcmV0dXJuIHRoaXM7CiAgICB9CgogICAgY29uc3Qgc2NvcGVUb01lcmdlID0gdHlwZW9mIGNhcHR1cmVDb250ZXh0ID09PSAnZnVuY3Rpb24nID8gY2FwdHVyZUNvbnRleHQodGhpcykgOiBjYXB0dXJlQ29udGV4dDsKCiAgICBpZiAoc2NvcGVUb01lcmdlIGluc3RhbmNlb2YgU2NvcGUpIHsKICAgICAgY29uc3Qgc2NvcGVEYXRhID0gc2NvcGVUb01lcmdlLmdldFNjb3BlRGF0YSgpOwoKICAgICAgdGhpcy5fdGFncyA9IHsgLi4udGhpcy5fdGFncywgLi4uc2NvcGVEYXRhLnRhZ3MgfTsKICAgICAgdGhpcy5fZXh0cmEgPSB7IC4uLnRoaXMuX2V4dHJhLCAuLi5zY29wZURhdGEuZXh0cmEgfTsKICAgICAgdGhpcy5fY29udGV4dHMgPSB7IC4uLnRoaXMuX2NvbnRleHRzLCAuLi5zY29wZURhdGEuY29udGV4dHMgfTsKICAgICAgaWYgKHNjb3BlRGF0YS51c2VyICYmIE9iamVjdC5rZXlzKHNjb3BlRGF0YS51c2VyKS5sZW5ndGgpIHsKICAgICAgICB0aGlzLl91c2VyID0gc2NvcGVEYXRhLnVzZXI7CiAgICAgIH0KICAgICAgaWYgKHNjb3BlRGF0YS5sZXZlbCkgewogICAgICAgIHRoaXMuX2xldmVsID0gc2NvcGVEYXRhLmxldmVsOwogICAgICB9CiAgICAgIGlmIChzY29wZURhdGEuZmluZ2VycHJpbnQubGVuZ3RoKSB7CiAgICAgICAgdGhpcy5fZmluZ2VycHJpbnQgPSBzY29wZURhdGEuZmluZ2VycHJpbnQ7CiAgICAgIH0KICAgICAgaWYgKHNjb3BlVG9NZXJnZS5nZXRSZXF1ZXN0U2Vzc2lvbigpKSB7CiAgICAgICAgdGhpcy5fcmVxdWVzdFNlc3Npb24gPSBzY29wZVRvTWVyZ2UuZ2V0UmVxdWVzdFNlc3Npb24oKTsKICAgICAgfQogICAgICBpZiAoc2NvcGVEYXRhLnByb3BhZ2F0aW9uQ29udGV4dCkgewogICAgICAgIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCA9IHNjb3BlRGF0YS5wcm9wYWdhdGlvbkNvbnRleHQ7CiAgICAgIH0KICAgIH0gZWxzZSBpZiAoaXNQbGFpbk9iamVjdChzY29wZVRvTWVyZ2UpKSB7CiAgICAgIGNvbnN0IHNjb3BlQ29udGV4dCA9IGNhcHR1cmVDb250ZXh0IDsKICAgICAgdGhpcy5fdGFncyA9IHsgLi4udGhpcy5fdGFncywgLi4uc2NvcGVDb250ZXh0LnRhZ3MgfTsKICAgICAgdGhpcy5fZXh0cmEgPSB7IC4uLnRoaXMuX2V4dHJhLCAuLi5zY29wZUNvbnRleHQuZXh0cmEgfTsKICAgICAgdGhpcy5fY29udGV4dHMgPSB7IC4uLnRoaXMuX2NvbnRleHRzLCAuLi5zY29wZUNvbnRleHQuY29udGV4dHMgfTsKICAgICAgaWYgKHNjb3BlQ29udGV4dC51c2VyKSB7CiAgICAgICAgdGhpcy5fdXNlciA9IHNjb3BlQ29udGV4dC51c2VyOwogICAgICB9CiAgICAgIGlmIChzY29wZUNvbnRleHQubGV2ZWwpIHsKICAgICAgICB0aGlzLl9sZXZlbCA9IHNjb3BlQ29udGV4dC5sZXZlbDsKICAgICAgfQogICAgICBpZiAoc2NvcGVDb250ZXh0LmZpbmdlcnByaW50KSB7CiAgICAgICAgdGhpcy5fZmluZ2VycHJpbnQgPSBzY29wZUNvbnRleHQuZmluZ2VycHJpbnQ7CiAgICAgIH0KICAgICAgaWYgKHNjb3BlQ29udGV4dC5yZXF1ZXN0U2Vzc2lvbikgewogICAgICAgIHRoaXMuX3JlcXVlc3RTZXNzaW9uID0gc2NvcGVDb250ZXh0LnJlcXVlc3RTZXNzaW9uOwogICAgICB9CiAgICAgIGlmIChzY29wZUNvbnRleHQucHJvcGFnYXRpb25Db250ZXh0KSB7CiAgICAgICAgdGhpcy5fcHJvcGFnYXRpb25Db250ZXh0ID0gc2NvcGVDb250ZXh0LnByb3BhZ2F0aW9uQ29udGV4dDsKICAgICAgfQogICAgfQoKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgY2xlYXIoKSB7CiAgICAvLyBjbGllbnQgaXMgbm90IGNsZWFyZWQgaGVyZSBvbiBwdXJwb3NlIQogICAgdGhpcy5fYnJlYWRjcnVtYnMgPSBbXTsKICAgIHRoaXMuX3RhZ3MgPSB7fTsKICAgIHRoaXMuX2V4dHJhID0ge307CiAgICB0aGlzLl91c2VyID0ge307CiAgICB0aGlzLl9jb250ZXh0cyA9IHt9OwogICAgdGhpcy5fbGV2ZWwgPSB1bmRlZmluZWQ7CiAgICB0aGlzLl90cmFuc2FjdGlvbk5hbWUgPSB1bmRlZmluZWQ7CiAgICB0aGlzLl9maW5nZXJwcmludCA9IHVuZGVmaW5lZDsKICAgIHRoaXMuX3JlcXVlc3RTZXNzaW9uID0gdW5kZWZpbmVkOwogICAgdGhpcy5fc3BhbiA9IHVuZGVmaW5lZDsKICAgIHRoaXMuX3Nlc3Npb24gPSB1bmRlZmluZWQ7CiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgdGhpcy5fYXR0YWNobWVudHMgPSBbXTsKICAgIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCA9IGdlbmVyYXRlUHJvcGFnYXRpb25Db250ZXh0KCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGFkZEJyZWFkY3J1bWIoYnJlYWRjcnVtYiwgbWF4QnJlYWRjcnVtYnMpIHsKICAgIGNvbnN0IG1heENydW1icyA9IHR5cGVvZiBtYXhCcmVhZGNydW1icyA9PT0gJ251bWJlcicgPyBtYXhCcmVhZGNydW1icyA6IERFRkFVTFRfTUFYX0JSRUFEQ1JVTUJTOwoKICAgIC8vIE5vIGRhdGEgaGFzIGJlZW4gY2hhbmdlZCwgc28gZG9uJ3Qgbm90aWZ5IHNjb3BlIGxpc3RlbmVycwogICAgaWYgKG1heENydW1icyA8PSAwKSB7CiAgICAgIHJldHVybiB0aGlzOwogICAgfQoKICAgIGNvbnN0IG1lcmdlZEJyZWFkY3J1bWIgPSB7CiAgICAgIHRpbWVzdGFtcDogZGF0ZVRpbWVzdGFtcEluU2Vjb25kcygpLAogICAgICAuLi5icmVhZGNydW1iLAogICAgfTsKCiAgICBjb25zdCBicmVhZGNydW1icyA9IHRoaXMuX2JyZWFkY3J1bWJzOwogICAgYnJlYWRjcnVtYnMucHVzaChtZXJnZWRCcmVhZGNydW1iKTsKICAgIHRoaXMuX2JyZWFkY3J1bWJzID0gYnJlYWRjcnVtYnMubGVuZ3RoID4gbWF4Q3J1bWJzID8gYnJlYWRjcnVtYnMuc2xpY2UoLW1heENydW1icykgOiBicmVhZGNydW1iczsKCiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwoKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgZ2V0TGFzdEJyZWFkY3J1bWIoKSB7CiAgICByZXR1cm4gdGhpcy5fYnJlYWRjcnVtYnNbdGhpcy5fYnJlYWRjcnVtYnMubGVuZ3RoIC0gMV07CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBjbGVhckJyZWFkY3J1bWJzKCkgewogICAgdGhpcy5fYnJlYWRjcnVtYnMgPSBbXTsKICAgIHRoaXMuX25vdGlmeVNjb3BlTGlzdGVuZXJzKCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGFkZEF0dGFjaG1lbnQoYXR0YWNobWVudCkgewogICAgdGhpcy5fYXR0YWNobWVudHMucHVzaChhdHRhY2htZW50KTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYGdldFNjb3BlRGF0YSgpYCBpbnN0ZWFkLgogICAqLwogICBnZXRBdHRhY2htZW50cygpIHsKICAgIGNvbnN0IGRhdGEgPSB0aGlzLmdldFNjb3BlRGF0YSgpOwoKICAgIHJldHVybiBkYXRhLmF0dGFjaG1lbnRzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgY2xlYXJBdHRhY2htZW50cygpIHsKICAgIHRoaXMuX2F0dGFjaG1lbnRzID0gW107CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKiBAaW5oZXJpdERvYyAqLwogICBnZXRTY29wZURhdGEoKSB7CiAgICBjb25zdCB7CiAgICAgIF9icmVhZGNydW1icywKICAgICAgX2F0dGFjaG1lbnRzLAogICAgICBfY29udGV4dHMsCiAgICAgIF90YWdzLAogICAgICBfZXh0cmEsCiAgICAgIF91c2VyLAogICAgICBfbGV2ZWwsCiAgICAgIF9maW5nZXJwcmludCwKICAgICAgX2V2ZW50UHJvY2Vzc29ycywKICAgICAgX3Byb3BhZ2F0aW9uQ29udGV4dCwKICAgICAgX3Nka1Byb2Nlc3NpbmdNZXRhZGF0YSwKICAgICAgX3RyYW5zYWN0aW9uTmFtZSwKICAgICAgX3NwYW4sCiAgICB9ID0gdGhpczsKCiAgICByZXR1cm4gewogICAgICBicmVhZGNydW1iczogX2JyZWFkY3J1bWJzLAogICAgICBhdHRhY2htZW50czogX2F0dGFjaG1lbnRzLAogICAgICBjb250ZXh0czogX2NvbnRleHRzLAogICAgICB0YWdzOiBfdGFncywKICAgICAgZXh0cmE6IF9leHRyYSwKICAgICAgdXNlcjogX3VzZXIsCiAgICAgIGxldmVsOiBfbGV2ZWwsCiAgICAgIGZpbmdlcnByaW50OiBfZmluZ2VycHJpbnQgfHwgW10sCiAgICAgIGV2ZW50UHJvY2Vzc29yczogX2V2ZW50UHJvY2Vzc29ycywKICAgICAgcHJvcGFnYXRpb25Db250ZXh0OiBfcHJvcGFnYXRpb25Db250ZXh0LAogICAgICBzZGtQcm9jZXNzaW5nTWV0YWRhdGE6IF9zZGtQcm9jZXNzaW5nTWV0YWRhdGEsCiAgICAgIHRyYW5zYWN0aW9uTmFtZTogX3RyYW5zYWN0aW9uTmFtZSwKICAgICAgc3BhbjogX3NwYW4sCiAgICB9OwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKG5ld0RhdGEpIHsKICAgIHRoaXMuX3Nka1Byb2Nlc3NpbmdNZXRhZGF0YSA9IHsgLi4udGhpcy5fc2RrUHJvY2Vzc2luZ01ldGFkYXRhLCAuLi5uZXdEYXRhIH07CgogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRQcm9wYWdhdGlvbkNvbnRleHQoY29udGV4dCkgewogICAgdGhpcy5fcHJvcGFnYXRpb25Db250ZXh0ID0gY29udGV4dDsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgZ2V0UHJvcGFnYXRpb25Db250ZXh0KCkgewogICAgcmV0dXJuIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNhcHR1cmVFeGNlcHRpb24oZXhjZXB0aW9uLCBoaW50KSB7CiAgICBjb25zdCBldmVudElkID0gaGludCAmJiBoaW50LmV2ZW50X2lkID8gaGludC5ldmVudF9pZCA6IHV1aWQ0KCk7CgogICAgaWYgKCF0aGlzLl9jbGllbnQpIHsKICAgICAgbG9nZ2VyLndhcm4oJ05vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBleGNlcHRpb24hJyk7CiAgICAgIHJldHVybiBldmVudElkOwogICAgfQoKICAgIGNvbnN0IHN5bnRoZXRpY0V4Y2VwdGlvbiA9IG5ldyBFcnJvcignU2VudHJ5IHN5bnRoZXRpY0V4Y2VwdGlvbicpOwoKICAgIHRoaXMuX2NsaWVudC5jYXB0dXJlRXhjZXB0aW9uKAogICAgICBleGNlcHRpb24sCiAgICAgIHsKICAgICAgICBvcmlnaW5hbEV4Y2VwdGlvbjogZXhjZXB0aW9uLAogICAgICAgIHN5bnRoZXRpY0V4Y2VwdGlvbiwKICAgICAgICAuLi5oaW50LAogICAgICAgIGV2ZW50X2lkOiBldmVudElkLAogICAgICB9LAogICAgICB0aGlzLAogICAgKTsKCiAgICByZXR1cm4gZXZlbnRJZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNhcHR1cmVNZXNzYWdlKG1lc3NhZ2UsIGxldmVsLCBoaW50KSB7CiAgICBjb25zdCBldmVudElkID0gaGludCAmJiBoaW50LmV2ZW50X2lkID8gaGludC5ldmVudF9pZCA6IHV1aWQ0KCk7CgogICAgaWYgKCF0aGlzLl9jbGllbnQpIHsKICAgICAgbG9nZ2VyLndhcm4oJ05vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlIScpOwogICAgICByZXR1cm4gZXZlbnRJZDsKICAgIH0KCiAgICBjb25zdCBzeW50aGV0aWNFeGNlcHRpb24gPSBuZXcgRXJyb3IobWVzc2FnZSk7CgogICAgdGhpcy5fY2xpZW50LmNhcHR1cmVNZXNzYWdlKAogICAgICBtZXNzYWdlLAogICAgICBsZXZlbCwKICAgICAgewogICAgICAgIG9yaWdpbmFsRXhjZXB0aW9uOiBtZXNzYWdlLAogICAgICAgIHN5bnRoZXRpY0V4Y2VwdGlvbiwKICAgICAgICAuLi5oaW50LAogICAgICAgIGV2ZW50X2lkOiBldmVudElkLAogICAgICB9LAogICAgICB0aGlzLAogICAgKTsKCiAgICByZXR1cm4gZXZlbnRJZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNhcHR1cmVFdmVudChldmVudCwgaGludCkgewogICAgY29uc3QgZXZlbnRJZCA9IGhpbnQgJiYgaGludC5ldmVudF9pZCA/IGhpbnQuZXZlbnRfaWQgOiB1dWlkNCgpOwoKICAgIGlmICghdGhpcy5fY2xpZW50KSB7CiAgICAgIGxvZ2dlci53YXJuKCdObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhJyk7CiAgICAgIHJldHVybiBldmVudElkOwogICAgfQoKICAgIHRoaXMuX2NsaWVudC5jYXB0dXJlRXZlbnQoZXZlbnQsIHsgLi4uaGludCwgZXZlbnRfaWQ6IGV2ZW50SWQgfSwgdGhpcyk7CgogICAgcmV0dXJuIGV2ZW50SWQ7CiAgfQoKICAvKioKICAgKiBUaGlzIHdpbGwgYmUgY2FsbGVkIG9uIGV2ZXJ5IHNldCBjYWxsLgogICAqLwogICBfbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKSB7CiAgICAvLyBXZSBuZWVkIHRoaXMgY2hlY2sgZm9yIHRoaXMuX25vdGlmeWluZ0xpc3RlbmVycyB0byBiZSBhYmxlIHRvIHdvcmsgb24gc2NvcGUgZHVyaW5nIHVwZGF0ZXMKICAgIC8vIElmIHRoaXMgY2hlY2sgaXMgbm90IGhlcmUgd2UnbGwgcHJvZHVjZSBlbmRsZXNzIHJlY3Vyc2lvbiB3aGVuIHNvbWV0aGluZyBpcyBkb25lIHdpdGggdGhlIHNjb3BlCiAgICAvLyBkdXJpbmcgdGhlIGNhbGxiYWNrLgogICAgaWYgKCF0aGlzLl9ub3RpZnlpbmdMaXN0ZW5lcnMpIHsKICAgICAgdGhpcy5fbm90aWZ5aW5nTGlzdGVuZXJzID0gdHJ1ZTsKICAgICAgdGhpcy5fc2NvcGVMaXN0ZW5lcnMuZm9yRWFjaChjYWxsYmFjayA9PiB7CiAgICAgICAgY2FsbGJhY2sodGhpcyk7CiAgICAgIH0pOwogICAgICB0aGlzLl9ub3RpZnlpbmdMaXN0ZW5lcnMgPSBmYWxzZTsKICAgIH0KICB9Cn0KCmZ1bmN0aW9uIGdlbmVyYXRlUHJvcGFnYXRpb25Db250ZXh0KCkgewogIHJldHVybiB7CiAgICB0cmFjZUlkOiB1dWlkNCgpLAogICAgc3BhbklkOiB1dWlkNCgpLnN1YnN0cmluZygxNiksCiAgfTsKfQoKY29uc3QgU0RLX1ZFUlNJT04gPSAnOC4wLjAtYWxwaGEuMyc7CgovKioKICogQVBJIGNvbXBhdGliaWxpdHkgdmVyc2lvbiBvZiB0aGlzIGh1Yi4KICoKICogV0FSTklORzogVGhpcyBudW1iZXIgc2hvdWxkIG9ubHkgYmUgaW5jcmVhc2VkIHdoZW4gdGhlIGdsb2JhbCBpbnRlcmZhY2UKICogY2hhbmdlcyBhbmQgbmV3IG1ldGhvZHMgYXJlIGludHJvZHVjZWQuCiAqCiAqIEBoaWRkZW4KICovCmNvbnN0IEFQSV9WRVJTSU9OID0gcGFyc2VGbG9hdChTREtfVkVSU0lPTik7CgovKioKICogRGVmYXVsdCBtYXhpbXVtIG51bWJlciBvZiBicmVhZGNydW1icyBhZGRlZCB0byBhbiBldmVudC4gQ2FuIGJlIG92ZXJ3cml0dGVuCiAqIHdpdGgge0BsaW5rIE9wdGlvbnMubWF4QnJlYWRjcnVtYnN9LgogKi8KY29uc3QgREVGQVVMVF9CUkVBRENSVU1CUyQxID0gMTAwOwoKLyoqCiAqIEEgbGF5ZXIgaW4gdGhlIHByb2Nlc3Mgc3RhY2suCiAqIEBoaWRkZW4KICovCgovKioKICogQGluaGVyaXREb2MKICovCmNsYXNzIEh1YiAgewogIC8qKiBJcyBhIHtAbGluayBMYXllcn1bXSBjb250YWluaW5nIHRoZSBjbGllbnQgYW5kIHNjb3BlICovCgogIC8qKgogICAqIENyZWF0ZXMgYSBuZXcgaW5zdGFuY2Ugb2YgdGhlIGh1Yiwgd2lsbCBwdXNoIG9uZSB7QGxpbmsgTGF5ZXJ9IGludG8gdGhlCiAgICogaW50ZXJuYWwgc3RhY2sgb24gY3JlYXRpb24uCiAgICoKICAgKiBAcGFyYW0gY2xpZW50IGJvdW5kIHRvIHRoZSBodWIuCiAgICogQHBhcmFtIHNjb3BlIGJvdW5kIHRvIHRoZSBodWIuCiAgICogQHBhcmFtIHZlcnNpb24gbnVtYmVyLCBoaWdoZXIgbnVtYmVyIG1lYW5zIGhpZ2hlciBwcmlvcml0eS4KICAgKgogICAqIEBkZXByZWNhdGVkIEluc3RhbnRpYXRpb24gb2YgSHViIG9iamVjdHMgaXMgZGVwcmVjYXRlZCBhbmQgdGhlIGNvbnN0cnVjdG9yIHdpbGwgYmUgcmVtb3ZlZCBpbiB2ZXJzaW9uIDggb2YgdGhlIFNESy4KICAgKgogICAqIElmIHlvdSBhcmUgY3VycmVudGx5IHVzaW5nIHRoZSBIdWIgZm9yIG11bHRpLWNsaWVudCB1c2UgbGlrZSBzbzoKICAgKgogICAqIGBgYAogICAqIC8vIE9MRAogICAqIGNvbnN0IGh1YiA9IG5ldyBIdWIoKTsKICAgKiBodWIuYmluZENsaWVudChjbGllbnQpOwogICAqIG1ha2VNYWluKGh1YikKICAgKiBgYGAKICAgKgogICAqIGluc3RlYWQgaW5pdGlhbGl6ZSB0aGUgY2xpZW50IGFzIGZvbGxvd3M6CiAgICoKICAgKiBgYGAKICAgKiAvLyBORVcKICAgKiBTZW50cnkud2l0aElzb2xhdGlvblNjb3BlKCgpID0+IHsKICAgKiAgICBTZW50cnkuc2V0Q3VycmVudENsaWVudChjbGllbnQpOwogICAqICAgIGNsaWVudC5pbml0KCk7CiAgICogfSk7CiAgICogYGBgCiAgICoKICAgKiBJZiB5b3UgYXJlIHVzaW5nIHRoZSBIdWIgdG8gY2FwdHVyZSBldmVudHMgbGlrZSBzbzoKICAgKgogICAqIGBgYAogICAqIC8vIE9MRAogICAqIGNvbnN0IGNsaWVudCA9IG5ldyBDbGllbnQoKTsKICAgKiBjb25zdCBodWIgPSBuZXcgSHViKGNsaWVudCk7CiAgICogaHViLmNhcHR1cmVFeGNlcHRpb24oKQogICAqIGBgYAogICAqCiAgICogaW5zdGVhZCBjYXB0dXJlIGlzb2xhdGVkIGV2ZW50cyBhcyBmb2xsb3dzOgogICAqCiAgICogYGBgCiAgICogLy8gTkVXCiAgICogY29uc3QgY2xpZW50ID0gbmV3IENsaWVudCgpOwogICAqIGNvbnN0IHNjb3BlID0gbmV3IFNjb3BlKCk7CiAgICogc2NvcGUuc2V0Q2xpZW50KGNsaWVudCk7CiAgICogc2NvcGUuY2FwdHVyZUV4Y2VwdGlvbigpOwogICAqIGBgYAogICAqLwogICBjb25zdHJ1Y3RvcigKICAgIGNsaWVudCwKICAgIHNjb3BlLAogICAgaXNvbGF0aW9uU2NvcGUsCiAgICAgIF92ZXJzaW9uID0gQVBJX1ZFUlNJT04sCiAgKSB7dGhpcy5fdmVyc2lvbiA9IF92ZXJzaW9uOwogICAgbGV0IGFzc2lnbmVkU2NvcGU7CiAgICBpZiAoIXNjb3BlKSB7CiAgICAgIGFzc2lnbmVkU2NvcGUgPSBuZXcgU2NvcGUoKTsKICAgICAgYXNzaWduZWRTY29wZS5zZXRDbGllbnQoY2xpZW50KTsKICAgIH0gZWxzZSB7CiAgICAgIGFzc2lnbmVkU2NvcGUgPSBzY29wZTsKICAgIH0KCiAgICBsZXQgYXNzaWduZWRJc29sYXRpb25TY29wZTsKICAgIGlmICghaXNvbGF0aW9uU2NvcGUpIHsKICAgICAgYXNzaWduZWRJc29sYXRpb25TY29wZSA9IG5ldyBTY29wZSgpOwogICAgICBhc3NpZ25lZElzb2xhdGlvblNjb3BlLnNldENsaWVudChjbGllbnQpOwogICAgfSBlbHNlIHsKICAgICAgYXNzaWduZWRJc29sYXRpb25TY29wZSA9IGlzb2xhdGlvblNjb3BlOwogICAgfQoKICAgIHRoaXMuX3N0YWNrID0gW3sgc2NvcGU6IGFzc2lnbmVkU2NvcGUgfV07CgogICAgaWYgKGNsaWVudCkgewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgICAgdGhpcy5iaW5kQ2xpZW50KGNsaWVudCk7CiAgICB9CgogICAgdGhpcy5faXNvbGF0aW9uU2NvcGUgPSBhc3NpZ25lZElzb2xhdGlvblNjb3BlOwogIH0KCiAgLyoqCiAgICogQ2hlY2tzIGlmIHRoaXMgaHViJ3MgdmVyc2lvbiBpcyBvbGRlciB0aGFuIHRoZSBnaXZlbiB2ZXJzaW9uLgogICAqCiAgICogQHBhcmFtIHZlcnNpb24gQSB2ZXJzaW9uIG51bWJlciB0byBjb21wYXJlIHRvLgogICAqIEByZXR1cm4gVHJ1ZSBpZiB0aGUgZ2l2ZW4gdmVyc2lvbiBpcyBuZXdlcjsgb3RoZXJ3aXNlIGZhbHNlLgogICAqCiAgICogQGRlcHJlY2F0ZWQgVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjguCiAgICovCiAgIGlzT2xkZXJUaGFuKHZlcnNpb24pIHsKICAgIHJldHVybiB0aGlzLl92ZXJzaW9uIDwgdmVyc2lvbjsKICB9CgogIC8qKgogICAqIFRoaXMgYmluZHMgdGhlIGdpdmVuIGNsaWVudCB0byB0aGUgY3VycmVudCBzY29wZS4KICAgKiBAcGFyYW0gY2xpZW50IEFuIFNESyBjbGllbnQgKGNsaWVudCkgaW5zdGFuY2UuCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYGluaXRBbmRCaW5kKClgIGRpcmVjdGx5LCBvciBgc2V0Q3VycmVudENsaWVudCgpYCBhbmQvb3IgYGNsaWVudC5pbml0KClgIGluc3RlYWQuCiAgICovCiAgIGJpbmRDbGllbnQoY2xpZW50KSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IHRvcCA9IHRoaXMuZ2V0U3RhY2tUb3AoKTsKICAgIHRvcC5jbGllbnQgPSBjbGllbnQ7CiAgICB0b3Auc2NvcGUuc2V0Q2xpZW50KGNsaWVudCk7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGlmIChjbGllbnQgJiYgY2xpZW50LnNldHVwSW50ZWdyYXRpb25zKSB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgICBjbGllbnQuc2V0dXBJbnRlZ3JhdGlvbnMoKTsKICAgIH0KICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYHdpdGhTY29wZWAgaW5zdGVhZC4KICAgKi8KICAgcHVzaFNjb3BlKCkgewogICAgLy8gV2Ugd2FudCB0byBjbG9uZSB0aGUgY29udGVudCBvZiBwcmV2IHNjb3BlCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IHNjb3BlID0gdGhpcy5nZXRTY29wZSgpLmNsb25lKCk7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0U3RhY2soKS5wdXNoKHsKICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgIGNsaWVudDogdGhpcy5nZXRDbGllbnQoKSwKICAgICAgc2NvcGUsCiAgICB9KTsKICAgIHJldHVybiBzY29wZTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYHdpdGhTY29wZWAgaW5zdGVhZC4KICAgKi8KICAgcG9wU2NvcGUoKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGlmICh0aGlzLmdldFN0YWNrKCkubGVuZ3RoIDw9IDEpIHJldHVybiBmYWxzZTsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuICEhdGhpcy5nZXRTdGFjaygpLnBvcCgpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKgogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LndpdGhTY29wZSgpYCBpbnN0ZWFkLgogICAqLwogICB3aXRoU2NvcGUoY2FsbGJhY2spIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgY29uc3Qgc2NvcGUgPSB0aGlzLnB1c2hTY29wZSgpOwoKICAgIGxldCBtYXliZVByb21pc2VSZXN1bHQ7CiAgICB0cnkgewogICAgICBtYXliZVByb21pc2VSZXN1bHQgPSBjYWxsYmFjayhzY29wZSk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgICB0aGlzLnBvcFNjb3BlKCk7CiAgICAgIHRocm93IGU7CiAgICB9CgogICAgaWYgKGlzVGhlbmFibGUobWF5YmVQcm9taXNlUmVzdWx0KSkgewogICAgICAvLyBAdHMtZXhwZWN0LWVycm9yIC0gaXNUaGVuYWJsZSByZXR1cm5zIHRoZSB3cm9uZyB0eXBlCiAgICAgIHJldHVybiBtYXliZVByb21pc2VSZXN1bHQudGhlbigKICAgICAgICByZXMgPT4gewogICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgICAgICB0aGlzLnBvcFNjb3BlKCk7CiAgICAgICAgICByZXR1cm4gcmVzOwogICAgICAgIH0sCiAgICAgICAgZSA9PiB7CiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgICAgICAgIHRoaXMucG9wU2NvcGUoKTsKICAgICAgICAgIHRocm93IGU7CiAgICAgICAgfSwKICAgICAgKTsKICAgIH0KCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMucG9wU2NvcGUoKTsKICAgIHJldHVybiBtYXliZVByb21pc2VSZXN1bHQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuZ2V0Q2xpZW50KClgIGluc3RlYWQuCiAgICovCiAgIGdldENsaWVudCgpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnQgOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgc2NvcGUgb2YgdGhlIHRvcCBzdGFjay4KICAgKgogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LmdldEN1cnJlbnRTY29wZSgpYCBpbnN0ZWFkLgogICAqLwogICBnZXRTY29wZSgpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5zY29wZTsKICB9CgogIC8qKgogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LmdldElzb2xhdGlvblNjb3BlKClgIGluc3RlYWQuCiAgICovCiAgIGdldElzb2xhdGlvblNjb3BlKCkgewogICAgcmV0dXJuIHRoaXMuX2lzb2xhdGlvblNjb3BlOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgc2NvcGUgc3RhY2sgZm9yIGRvbWFpbnMgb3IgdGhlIHByb2Nlc3MuCiAgICogQGRlcHJlY2F0ZWQgVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjguCiAgICovCiAgIGdldFN0YWNrKCkgewogICAgcmV0dXJuIHRoaXMuX3N0YWNrOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgdG9wbW9zdCBzY29wZSBsYXllciBpbiB0aGUgb3JkZXIgZG9tYWluID4gbG9jYWwgPiBwcm9jZXNzLgogICAqIEBkZXByZWNhdGVkIFRoaXMgd2lsbCBiZSByZW1vdmVkIGluIHY4LgogICAqLwogICBnZXRTdGFja1RvcCgpIHsKICAgIHJldHVybiB0aGlzLl9zdGFja1t0aGlzLl9zdGFjay5sZW5ndGggLSAxXTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5jYXB0dXJlRXhjZXB0aW9uKClgIGluc3RlYWQuCiAgICovCiAgIGNhcHR1cmVFeGNlcHRpb24oZXhjZXB0aW9uLCBoaW50KSB7CiAgICBjb25zdCBldmVudElkID0gaGludCAmJiBoaW50LmV2ZW50X2lkID8gaGludC5ldmVudF9pZCA6IHV1aWQ0KCk7CiAgICBjb25zdCBzeW50aGV0aWNFeGNlcHRpb24gPSBuZXcgRXJyb3IoJ1NlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24nKTsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRTY29wZSgpLmNhcHR1cmVFeGNlcHRpb24oZXhjZXB0aW9uLCB7CiAgICAgIG9yaWdpbmFsRXhjZXB0aW9uOiBleGNlcHRpb24sCiAgICAgIHN5bnRoZXRpY0V4Y2VwdGlvbiwKICAgICAgLi4uaGludCwKICAgICAgZXZlbnRfaWQ6IGV2ZW50SWQsCiAgICB9KTsKCiAgICByZXR1cm4gZXZlbnRJZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgIGBTZW50cnkuY2FwdHVyZU1lc3NhZ2UoKWAgaW5zdGVhZC4KICAgKi8KICAgY2FwdHVyZU1lc3NhZ2UobWVzc2FnZSwgbGV2ZWwsIGhpbnQpIHsKICAgIGNvbnN0IGV2ZW50SWQgPSBoaW50ICYmIGhpbnQuZXZlbnRfaWQgPyBoaW50LmV2ZW50X2lkIDogdXVpZDQoKTsKICAgIGNvbnN0IHN5bnRoZXRpY0V4Y2VwdGlvbiA9IG5ldyBFcnJvcihtZXNzYWdlKTsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRTY29wZSgpLmNhcHR1cmVNZXNzYWdlKG1lc3NhZ2UsIGxldmVsLCB7CiAgICAgIG9yaWdpbmFsRXhjZXB0aW9uOiBtZXNzYWdlLAogICAgICBzeW50aGV0aWNFeGNlcHRpb24sCiAgICAgIC4uLmhpbnQsCiAgICAgIGV2ZW50X2lkOiBldmVudElkLAogICAgfSk7CgogICAgcmV0dXJuIGV2ZW50SWQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuY2FwdHVyZUV2ZW50KClgIGluc3RlYWQuCiAgICovCiAgIGNhcHR1cmVFdmVudChldmVudCwgaGludCkgewogICAgY29uc3QgZXZlbnRJZCA9IGhpbnQgJiYgaGludC5ldmVudF9pZCA/IGhpbnQuZXZlbnRfaWQgOiB1dWlkNCgpOwogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICB0aGlzLmdldFNjb3BlKCkuY2FwdHVyZUV2ZW50KGV2ZW50LCB7IC4uLmhpbnQsIGV2ZW50X2lkOiBldmVudElkIH0pOwogICAgcmV0dXJuIGV2ZW50SWQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuYWRkQnJlYWRjcnVtYigpYCBpbnN0ZWFkLgogICAqLwogICBhZGRCcmVhZGNydW1iKGJyZWFkY3J1bWIsIGhpbnQpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgY29uc3QgeyBjbGllbnQgfSA9IHRoaXMuZ2V0U3RhY2tUb3AoKTsKCiAgICBpZiAoIWNsaWVudCkgcmV0dXJuOwoKICAgIGNvbnN0IHsgYmVmb3JlQnJlYWRjcnVtYiA9IG51bGwsIG1heEJyZWFkY3J1bWJzID0gREVGQVVMVF9CUkVBRENSVU1CUyQxIH0gPQogICAgICAoY2xpZW50LmdldE9wdGlvbnMgJiYgY2xpZW50LmdldE9wdGlvbnMoKSkgfHwge307CgogICAgaWYgKG1heEJyZWFkY3J1bWJzIDw9IDApIHJldHVybjsKCiAgICBjb25zdCB0aW1lc3RhbXAgPSBkYXRlVGltZXN0YW1wSW5TZWNvbmRzKCk7CiAgICBjb25zdCBtZXJnZWRCcmVhZGNydW1iID0geyB0aW1lc3RhbXAsIC4uLmJyZWFkY3J1bWIgfTsKICAgIGNvbnN0IGZpbmFsQnJlYWRjcnVtYiA9IGJlZm9yZUJyZWFkY3J1bWIKICAgICAgPyAoY29uc29sZVNhbmRib3goKCkgPT4gYmVmb3JlQnJlYWRjcnVtYihtZXJnZWRCcmVhZGNydW1iLCBoaW50KSkgKQogICAgICA6IG1lcmdlZEJyZWFkY3J1bWI7CgogICAgaWYgKGZpbmFsQnJlYWRjcnVtYiA9PT0gbnVsbCkgcmV0dXJuOwoKICAgIGNsaWVudC5lbWl0KCdiZWZvcmVBZGRCcmVhZGNydW1iJywgZmluYWxCcmVhZGNydW1iLCBoaW50KTsKCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5hZGRCcmVhZGNydW1iKGZpbmFsQnJlYWRjcnVtYiwgbWF4QnJlYWRjcnVtYnMpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5zZXRVc2VyKClgIGluc3RlYWQuCiAgICovCiAgIHNldFVzZXIodXNlcikgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICB0aGlzLmdldElzb2xhdGlvblNjb3BlKCkuc2V0VXNlcih1c2VyKTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuc2V0VGFncygpYCBpbnN0ZWFkLgogICAqLwogICBzZXRUYWdzKHRhZ3MpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRJc29sYXRpb25TY29wZSgpLnNldFRhZ3ModGFncyk7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LnNldEV4dHJhcygpYCBpbnN0ZWFkLgogICAqLwogICBzZXRFeHRyYXMoZXh0cmFzKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5zZXRFeHRyYXMoZXh0cmFzKTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuc2V0VGFnKClgIGluc3RlYWQuCiAgICovCiAgIHNldFRhZyhrZXksIHZhbHVlKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5zZXRUYWcoa2V5LCB2YWx1ZSk7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LnNldEV4dHJhKClgIGluc3RlYWQuCiAgICovCiAgIHNldEV4dHJhKGtleSwgZXh0cmEpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRJc29sYXRpb25TY29wZSgpLnNldEV4dHJhKGtleSwgZXh0cmEpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5zZXRDb250ZXh0KClgIGluc3RlYWQuCiAgICovCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkKICAgc2V0Q29udGV4dChuYW1lLCBjb250ZXh0KSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5zZXRDb250ZXh0KG5hbWUsIGNvbnRleHQpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5nZXRDbGllbnQoKS5nZXRJbnRlZ3JhdGlvbkJ5TmFtZSgpYCBpbnN0ZWFkLgogICAqLwogICBnZXRJbnRlZ3JhdGlvbihpbnRlZ3JhdGlvbikgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICBjb25zdCBjbGllbnQgPSB0aGlzLmdldENsaWVudCgpOwogICAgaWYgKCFjbGllbnQpIHJldHVybiBudWxsOwogICAgdHJ5IHsKICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgIHJldHVybiBjbGllbnQuZ2V0SW50ZWdyYXRpb24oaW50ZWdyYXRpb24pOwogICAgfSBjYXRjaCAoX29PKSB7CiAgICAgIERFQlVHX0JVSUxEICYmIGxvZ2dlci53YXJuKGBDYW5ub3QgcmV0cmlldmUgaW50ZWdyYXRpb24gJHtpbnRlZ3JhdGlvbi5pZH0gZnJvbSB0aGUgY3VycmVudCBIdWJgKTsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIHRvcCBsZXZlbCBgY2FwdHVyZVNlc3Npb25gIGluc3RlYWQuCiAgICovCiAgIGNhcHR1cmVTZXNzaW9uKGVuZFNlc3Npb24gPSBmYWxzZSkgewogICAgLy8gYm90aCBzZW5kIHRoZSB1cGRhdGUgYW5kIHB1bGwgdGhlIHNlc3Npb24gZnJvbSB0aGUgc2NvcGUKICAgIGlmIChlbmRTZXNzaW9uKSB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgICByZXR1cm4gdGhpcy5lbmRTZXNzaW9uKCk7CiAgICB9CgogICAgLy8gb25seSBzZW5kIHRoZSB1cGRhdGUKICAgIHRoaXMuX3NlbmRTZXNzaW9uVXBkYXRlKCk7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqIEBkZXByZWNhdGVkIFVzZSB0b3AgbGV2ZWwgYGVuZFNlc3Npb25gIGluc3RlYWQuCiAgICovCiAgIGVuZFNlc3Npb24oKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IGxheWVyID0gdGhpcy5nZXRTdGFja1RvcCgpOwogICAgY29uc3Qgc2NvcGUgPSBsYXllci5zY29wZTsKICAgIGNvbnN0IHNlc3Npb24gPSBzY29wZS5nZXRTZXNzaW9uKCk7CiAgICBpZiAoc2Vzc2lvbikgewogICAgICBjbG9zZVNlc3Npb24oc2Vzc2lvbik7CiAgICB9CiAgICB0aGlzLl9zZW5kU2Vzc2lvblVwZGF0ZSgpOwoKICAgIC8vIHRoZSBzZXNzaW9uIGlzIG92ZXI7IHRha2UgaXQgb2ZmIG9mIHRoZSBzY29wZQogICAgc2NvcGUuc2V0U2Vzc2lvbigpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgdG9wIGxldmVsIGBzdGFydFNlc3Npb25gIGluc3RlYWQuCiAgICovCiAgIHN0YXJ0U2Vzc2lvbihjb250ZXh0KSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IHsgc2NvcGUsIGNsaWVudCB9ID0gdGhpcy5nZXRTdGFja1RvcCgpOwogICAgY29uc3QgeyByZWxlYXNlLCBlbnZpcm9ubWVudCA9IERFRkFVTFRfRU5WSVJPTk1FTlQgfSA9IChjbGllbnQgJiYgY2xpZW50LmdldE9wdGlvbnMoKSkgfHwge307CgogICAgLy8gV2lsbCBmZXRjaCB1c2VyQWdlbnQgaWYgY2FsbGVkIGZyb20gYnJvd3NlciBzZGsKICAgIGNvbnN0IHsgdXNlckFnZW50IH0gPSBHTE9CQUxfT0JKLm5hdmlnYXRvciB8fCB7fTsKCiAgICBjb25zdCBzZXNzaW9uID0gbWFrZVNlc3Npb24oewogICAgICByZWxlYXNlLAogICAgICBlbnZpcm9ubWVudCwKICAgICAgdXNlcjogc2NvcGUuZ2V0VXNlcigpLAogICAgICAuLi4odXNlckFnZW50ICYmIHsgdXNlckFnZW50IH0pLAogICAgICAuLi5jb250ZXh0LAogICAgfSk7CgogICAgLy8gRW5kIGV4aXN0aW5nIHNlc3Npb24gaWYgdGhlcmUncyBvbmUKICAgIGNvbnN0IGN1cnJlbnRTZXNzaW9uID0gc2NvcGUuZ2V0U2Vzc2lvbiAmJiBzY29wZS5nZXRTZXNzaW9uKCk7CiAgICBpZiAoY3VycmVudFNlc3Npb24gJiYgY3VycmVudFNlc3Npb24uc3RhdHVzID09PSAnb2snKSB7CiAgICAgIHVwZGF0ZVNlc3Npb24oY3VycmVudFNlc3Npb24sIHsgc3RhdHVzOiAnZXhpdGVkJyB9KTsKICAgIH0KICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5lbmRTZXNzaW9uKCk7CgogICAgLy8gQWZ0ZXJ3YXJkcyB3ZSBzZXQgdGhlIG5ldyBzZXNzaW9uIG9uIHRoZSBzY29wZQogICAgc2NvcGUuc2V0U2Vzc2lvbihzZXNzaW9uKTsKCiAgICByZXR1cm4gc2Vzc2lvbjsKICB9CgogIC8qKgogICAqIFJldHVybnMgaWYgZGVmYXVsdCBQSUkgc2hvdWxkIGJlIHNlbnQgdG8gU2VudHJ5IGFuZCBwcm9wYWdhdGVkIGluIG91cmdvaW5nIHJlcXVlc3RzCiAgICogd2hlbiBUcmFjaW5nIGlzIHVzZWQuCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgdG9wLWxldmVsIGBnZXRDbGllbnQoKS5nZXRPcHRpb25zKCkuc2VuZERlZmF1bHRQaWlgIGluc3RlYWQuIFRoaXMgZnVuY3Rpb24KICAgKiBvbmx5IHVubmVjZXNzYXJpbHkgaW5jcmVhc2VkIEFQSSBzdXJmYWNlIGJ1dCBvbmx5IHdyYXBwZWQgYWNjZXNzaW5nIHRoZSBvcHRpb24uCiAgICovCiAgIHNob3VsZFNlbmREZWZhdWx0UGlpKCkgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICBjb25zdCBjbGllbnQgPSB0aGlzLmdldENsaWVudCgpOwogICAgY29uc3Qgb3B0aW9ucyA9IGNsaWVudCAmJiBjbGllbnQuZ2V0T3B0aW9ucygpOwogICAgcmV0dXJuIEJvb2xlYW4ob3B0aW9ucyAmJiBvcHRpb25zLnNlbmREZWZhdWx0UGlpKTsKICB9CgogIC8qKgogICAqIFNlbmRzIHRoZSBjdXJyZW50IFNlc3Npb24gb24gdGhlIHNjb3BlCiAgICovCiAgIF9zZW5kU2Vzc2lvblVwZGF0ZSgpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgY29uc3QgeyBzY29wZSwgY2xpZW50IH0gPSB0aGlzLmdldFN0YWNrVG9wKCk7CgogICAgY29uc3Qgc2Vzc2lvbiA9IHNjb3BlLmdldFNlc3Npb24oKTsKICAgIGlmIChzZXNzaW9uICYmIGNsaWVudCAmJiBjbGllbnQuY2FwdHVyZVNlc3Npb24pIHsKICAgICAgY2xpZW50LmNhcHR1cmVTZXNzaW9uKHNlc3Npb24pOwogICAgfQogIH0KCiAgLyoqCiAgICogQ2FsbHMgZ2xvYmFsIGV4dGVuc2lvbiBtZXRob2QgYW5kIGJpbmRpbmcgY3VycmVudCBpbnN0YW5jZSB0byB0aGUgZnVuY3Rpb24gY2FsbAogICAqLwogIC8vIEB0cy1leHBlY3QtZXJyb3IgRnVuY3Rpb24gbGFja3MgZW5kaW5nIHJldHVybiBzdGF0ZW1lbnQgYW5kIHJldHVybiB0eXBlIGRvZXMgbm90IGluY2x1ZGUgJ3VuZGVmaW5lZCcuIHRzKDIzNjYpCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkKICAgX2NhbGxFeHRlbnNpb25NZXRob2QobWV0aG9kLCAuLi5hcmdzKSB7CiAgICBjb25zdCBjYXJyaWVyID0gZ2V0TWFpbkNhcnJpZXIoKTsKICAgIGNvbnN0IHNlbnRyeSA9IGdldFNlbnRyeUNhcnJpZXIoY2Fycmllcik7CiAgICBpZiAoc2VudHJ5LmV4dGVuc2lvbnMgJiYgdHlwZW9mIHNlbnRyeS5leHRlbnNpb25zW21ldGhvZF0gPT09ICdmdW5jdGlvbicpIHsKICAgICAgcmV0dXJuIHNlbnRyeS5leHRlbnNpb25zW21ldGhvZF0uYXBwbHkodGhpcywgYXJncyk7CiAgICB9CiAgICBERUJVR19CVUlMRCAmJiBsb2dnZXIud2FybihgRXh0ZW5zaW9uIG1ldGhvZCAke21ldGhvZH0gY291bGRuJ3QgYmUgZm91bmQsIGRvaW5nIG5vdGhpbmcuYCk7CiAgfQp9CgovKiogR2V0IHRoZSBkZWZhdWx0IGN1cnJlbnQgc2NvcGUuICovCmZ1bmN0aW9uIGdldERlZmF1bHRDdXJyZW50U2NvcGUoKSB7CiAgcmV0dXJuIGdldEdsb2JhbFNpbmdsZXRvbignZGVmYXVsdEN1cnJlbnRTY29wZScsICgpID0+IG5ldyBTY29wZSgpKTsKfQoKLyoqIEdldCB0aGUgZGVmYXVsdCBpc29sYXRpb24gc2NvcGUuICovCmZ1bmN0aW9uIGdldERlZmF1bHRJc29sYXRpb25TY29wZSgpIHsKICByZXR1cm4gZ2V0R2xvYmFsU2luZ2xldG9uKCdkZWZhdWx0SXNvbGF0aW9uU2NvcGUnLCAoKSA9PiBuZXcgU2NvcGUoKSk7Cn0KCi8qKgogKiBHZXQgdGhlIGdsb2JhbCBodWIuCiAqIFRoaXMgd2lsbCBiZSByZW1vdmVkIGR1cmluZyB0aGUgdjggY3ljbGUgYW5kIGlzIG9ubHkgaGVyZSB0byBtYWtlIG1pZ3JhdGlvbiBlYXNpZXIuCiAqLwpmdW5jdGlvbiBnZXRHbG9iYWxIdWIoKSB7CiAgY29uc3QgcmVnaXN0cnkgPSBnZXRNYWluQ2FycmllcigpOwogIGNvbnN0IHNlbnRyeSA9IGdldFNlbnRyeUNhcnJpZXIocmVnaXN0cnkpIDsKCiAgLy8gSWYgdGhlcmUncyBubyBodWIsIG9yIGl0cyBhbiBvbGQgQVBJLCBhc3NpZ24gYSBuZXcgb25lCiAgaWYgKHNlbnRyeS5odWIpIHsKICAgIHJldHVybiBzZW50cnkuaHViOwogIH0KCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgc2VudHJ5Lmh1YiA9IG5ldyBIdWIodW5kZWZpbmVkLCBnZXREZWZhdWx0Q3VycmVudFNjb3BlKCksIGdldERlZmF1bHRJc29sYXRpb25TY29wZSgpKTsKICByZXR1cm4gc2VudHJ5Lmh1YjsKfQoKLyoqCiAqIEdldCB0aGUgY3VycmVudCBhc3luYyBjb250ZXh0IHN0cmF0ZWd5LgogKiBJZiBub25lIGhhcyBiZWVuIHNldHVwLCB0aGUgZGVmYXVsdCB3aWxsIGJlIHVzZWQuCiAqLwpmdW5jdGlvbiBnZXRBc3luY0NvbnRleHRTdHJhdGVneShjYXJyaWVyKSB7CiAgY29uc3Qgc2VudHJ5ID0gZ2V0U2VudHJ5Q2FycmllcihjYXJyaWVyKTsKCiAgaWYgKHNlbnRyeS5hY3MpIHsKICAgIHJldHVybiBzZW50cnkuYWNzOwogIH0KCiAgLy8gT3RoZXJ3aXNlLCB1c2UgdGhlIGRlZmF1bHQgb25lCiAgcmV0dXJuIGdldEh1YlN0YWNrQXN5bmNDb250ZXh0U3RyYXRlZ3koKTsKfQoKZnVuY3Rpb24gd2l0aFNjb3BlJDEoY2FsbGJhY2spIHsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICByZXR1cm4gZ2V0R2xvYmFsSHViKCkud2l0aFNjb3BlKGNhbGxiYWNrKTsKfQoKZnVuY3Rpb24gd2l0aFNldFNjb3BlKHNjb3BlLCBjYWxsYmFjaykgewogIGNvbnN0IGh1YiA9IGdldEdsb2JhbEh1YigpIDsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICByZXR1cm4gaHViLndpdGhTY29wZSgoKSA9PiB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGh1Yi5nZXRTdGFja1RvcCgpLnNjb3BlID0gc2NvcGUgOwogICAgcmV0dXJuIGNhbGxiYWNrKHNjb3BlKTsKICB9KTsKfQoKZnVuY3Rpb24gd2l0aElzb2xhdGlvblNjb3BlJDEoY2FsbGJhY2spIHsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICByZXR1cm4gZ2V0R2xvYmFsSHViKCkud2l0aFNjb3BlKCgpID0+IHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuIGNhbGxiYWNrKGdldEdsb2JhbEh1YigpLmdldElzb2xhdGlvblNjb3BlKCkpOwogIH0pOwp9CgovKiBlc2xpbnQtZGlzYWJsZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbiAqLwpmdW5jdGlvbiBnZXRIdWJTdGFja0FzeW5jQ29udGV4dFN0cmF0ZWd5KCkgewogIHJldHVybiB7CiAgICBnZXRDdXJyZW50SHViOiBnZXRHbG9iYWxIdWIsCiAgICB3aXRoSXNvbGF0aW9uU2NvcGU6IHdpdGhJc29sYXRpb25TY29wZSQxLAogICAgd2l0aFNjb3BlOiB3aXRoU2NvcGUkMSwKICAgIHdpdGhTZXRTY29wZSwKICAgIHdpdGhTZXRJc29sYXRpb25TY29wZTogKF9pc29sYXRpb25TY29wZSwgY2FsbGJhY2spID0+IHsKICAgICAgcmV0dXJuIHdpdGhJc29sYXRpb25TY29wZSQxKGNhbGxiYWNrKTsKICAgIH0sCiAgICBnZXRDdXJyZW50U2NvcGU6ICgpID0+IGdldEdsb2JhbEh1YigpLmdldFNjb3BlKCksCiAgICBnZXRJc29sYXRpb25TY29wZTogKCkgPT4gZ2V0R2xvYmFsSHViKCkuZ2V0SXNvbGF0aW9uU2NvcGUoKSwKICB9Owp9Ci8qIGVzbGludC1lbmFibGUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24gKi8KCi8qKgogKiBHZXQgdGhlIGN1cnJlbnRseSBhY3RpdmUgc2NvcGUuCiAqLwpmdW5jdGlvbiBnZXRDdXJyZW50U2NvcGUoKSB7CiAgY29uc3QgY2FycmllciA9IGdldE1haW5DYXJyaWVyKCk7CiAgY29uc3QgYWNzID0gZ2V0QXN5bmNDb250ZXh0U3RyYXRlZ3koY2Fycmllcik7CiAgcmV0dXJuIGFjcy5nZXRDdXJyZW50U2NvcGUoKTsKfQoKLyoqCiAqIEdldCB0aGUgY3VycmVudGx5IGFjdGl2ZSBjbGllbnQuCiAqLwpmdW5jdGlvbiBnZXRDbGllbnQoKSB7CiAgcmV0dXJuIGdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpOwp9CgovKioKICogRmV0Y2hlcyB0aGUgbWV0cmljIHN1bW1hcnkgaWYgaXQgZXhpc3RzIGZvciB0aGUgcGFzc2VkIHNwYW4KICovCmZ1bmN0aW9uIGdldE1ldHJpY1N1bW1hcnlKc29uRm9yU3BhbihzcGFuKSB7CgogIHsKICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQp9CgovKioKICogVXNlIHRoaXMgYXR0cmlidXRlIHRvIHJlcHJlc2VudCB0aGUgb3BlcmF0aW9uIG9mIGEgc3Bhbi4KICovCmNvbnN0IFNFTUFOVElDX0FUVFJJQlVURV9TRU5UUllfT1AgPSAnc2VudHJ5Lm9wJzsKCi8qKgogKiBVc2UgdGhpcyBhdHRyaWJ1dGUgdG8gcmVwcmVzZW50IHRoZSBvcmlnaW4gb2YgYSBzcGFuLgogKi8KY29uc3QgU0VNQU5USUNfQVRUUklCVVRFX1NFTlRSWV9PUklHSU4gPSAnc2VudHJ5Lm9yaWdpbic7Cgpjb25zdCBTUEFOX1NUQVRVU19VTlNFVCA9IDA7CmNvbnN0IFNQQU5fU1RBVFVTX09LID0gMTsKCi8qKgogKiBDb252ZXJ0IGEgc3BhbiB0aW1lIGlucHV0IGludHAgYSB0aW1lc3RhbXAgaW4gc2Vjb25kcy4KICovCmZ1bmN0aW9uIHNwYW5UaW1lSW5wdXRUb1NlY29uZHMoaW5wdXQpIHsKICBpZiAodHlwZW9mIGlucHV0ID09PSAnbnVtYmVyJykgewogICAgcmV0dXJuIGVuc3VyZVRpbWVzdGFtcEluU2Vjb25kcyhpbnB1dCk7CiAgfQoKICBpZiAoQXJyYXkuaXNBcnJheShpbnB1dCkpIHsKICAgIC8vIFNlZSB7QGxpbmsgSHJUaW1lfSBmb3IgdGhlIGFycmF5LWJhc2VkIHRpbWUgZm9ybWF0CiAgICByZXR1cm4gaW5wdXRbMF0gKyBpbnB1dFsxXSAvIDFlOTsKICB9CgogIGlmIChpbnB1dCBpbnN0YW5jZW9mIERhdGUpIHsKICAgIHJldHVybiBlbnN1cmVUaW1lc3RhbXBJblNlY29uZHMoaW5wdXQuZ2V0VGltZSgpKTsKICB9CgogIHJldHVybiB0aW1lc3RhbXBJblNlY29uZHMoKTsKfQoKLyoqCiAqIENvbnZlcnRzIGEgdGltZXN0YW1wIHRvIHNlY29uZCwgaWYgaXQgd2FzIGluIG1pbGxpc2Vjb25kcywgb3Iga2VlcHMgaXQgYXMgc2Vjb25kLgogKi8KZnVuY3Rpb24gZW5zdXJlVGltZXN0YW1wSW5TZWNvbmRzKHRpbWVzdGFtcCkgewogIGNvbnN0IGlzTXMgPSB0aW1lc3RhbXAgPiA5OTk5OTk5OTk5OwogIHJldHVybiBpc01zID8gdGltZXN0YW1wIC8gMTAwMCA6IHRpbWVzdGFtcDsKfQoKLyoqCiAqIENvbnZlcnQgYSBzcGFuIHRvIGEgSlNPTiByZXByZXNlbnRhdGlvbi4KICogTm90ZSB0aGF0IGFsbCBmaWVsZHMgcmV0dXJuZWQgaGVyZSBhcmUgb3B0aW9uYWwgYW5kIG5lZWQgdG8gYmUgZ3VhcmRlZCBhZ2FpbnN0LgogKgogKiBOb3RlOiBCZWNhdXNlIG9mIHRoaXMsIHdlIGN1cnJlbnRseSBoYXZlIGEgY2lyY3VsYXIgdHlwZSBkZXBlbmRlbmN5ICh3aGljaCB3ZSBvcHRlZCBvdXQgb2YgaW4gcGFja2FnZS5qc29uKS4KICogVGhpcyBpcyBub3QgYXZvaWRhYmxlIGFzIHdlIG5lZWQgYHNwYW5Ub0pTT05gIGluIGBzcGFuVXRpbHMudHNgLCB3aGljaCBpbiB0dXJuIGlzIG5lZWRlZCBieSBgc3Bhbi50c2AgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5LgogKiBBbmQgYHNwYW5Ub0pTT05gIG5lZWRzIHRoZSBTcGFuIGNsYXNzIGZyb20gYHNwYW4udHNgIHRvIGNoZWNrIGhlcmUuCiAqLwpmdW5jdGlvbiBzcGFuVG9KU09OKHNwYW4pIHsKICBpZiAoc3BhbklzU2VudHJ5U3BhbihzcGFuKSkgewogICAgcmV0dXJuIHNwYW4uZ2V0U3BhbkpTT04oKTsKICB9CgogIHRyeSB7CiAgICBjb25zdCB7IHNwYW5JZDogc3Bhbl9pZCwgdHJhY2VJZDogdHJhY2VfaWQgfSA9IHNwYW4uc3BhbkNvbnRleHQoKTsKCiAgICAvLyBIYW5kbGUgYSBzcGFuIGZyb20gQG9wZW50ZWxlbWV0cnkvc2RrLWJhc2UtdHJhY2UncyBgU3BhbmAgY2xhc3MKICAgIGlmIChzcGFuSXNPcGVuVGVsZW1ldHJ5U2RrVHJhY2VCYXNlU3BhbihzcGFuKSkgewogICAgICBjb25zdCB7IGF0dHJpYnV0ZXMsIHN0YXJ0VGltZSwgbmFtZSwgZW5kVGltZSwgcGFyZW50U3BhbklkLCBzdGF0dXMgfSA9IHNwYW47CgogICAgICByZXR1cm4gZHJvcFVuZGVmaW5lZEtleXMoewogICAgICAgIHNwYW5faWQsCiAgICAgICAgdHJhY2VfaWQsCiAgICAgICAgZGF0YTogYXR0cmlidXRlcywKICAgICAgICBkZXNjcmlwdGlvbjogbmFtZSwKICAgICAgICBwYXJlbnRfc3Bhbl9pZDogcGFyZW50U3BhbklkLAogICAgICAgIHN0YXJ0X3RpbWVzdGFtcDogc3BhblRpbWVJbnB1dFRvU2Vjb25kcyhzdGFydFRpbWUpLAogICAgICAgIC8vIFRoaXMgaXMgWzAsMF0gYnkgZGVmYXVsdCBpbiBPVEVMLCBpbiB3aGljaCBjYXNlIHdlIHdhbnQgdG8gaW50ZXJwcmV0IHRoaXMgYXMgbm8gZW5kIHRpbWUKICAgICAgICB0aW1lc3RhbXA6IHNwYW5UaW1lSW5wdXRUb1NlY29uZHMoZW5kVGltZSkgfHwgdW5kZWZpbmVkLAogICAgICAgIHN0YXR1czogZ2V0U3RhdHVzTWVzc2FnZShzdGF0dXMpLAogICAgICAgIG9wOiBhdHRyaWJ1dGVzW1NFTUFOVElDX0FUVFJJQlVURV9TRU5UUllfT1BdLAogICAgICAgIG9yaWdpbjogYXR0cmlidXRlc1tTRU1BTlRJQ19BVFRSSUJVVEVfU0VOVFJZX09SSUdJTl0gLAogICAgICAgIF9tZXRyaWNzX3N1bW1hcnk6IGdldE1ldHJpY1N1bW1hcnlKc29uRm9yU3BhbigpLAogICAgICB9KTsKICAgIH0KCiAgICAvLyBGaW5hbGx5LCBhdCBsZWFzdCB3ZSBoYXZlIGBzcGFuQ29udGV4dCgpYC4uLi4KICAgIHJldHVybiB7CiAgICAgIHNwYW5faWQsCiAgICAgIHRyYWNlX2lkLAogICAgfTsKICB9IGNhdGNoIChlKSB7CiAgICByZXR1cm4ge307CiAgfQp9CgpmdW5jdGlvbiBzcGFuSXNPcGVuVGVsZW1ldHJ5U2RrVHJhY2VCYXNlU3BhbihzcGFuKSB7CiAgY29uc3QgY2FzdFNwYW4gPSBzcGFuIDsKICByZXR1cm4gISFjYXN0U3Bhbi5hdHRyaWJ1dGVzICYmICEhY2FzdFNwYW4uc3RhcnRUaW1lICYmICEhY2FzdFNwYW4ubmFtZSAmJiAhIWNhc3RTcGFuLmVuZFRpbWUgJiYgISFjYXN0U3Bhbi5zdGF0dXM7Cn0KCi8qKiBFeHBvcnRlZCBvbmx5IGZvciB0ZXN0cy4gKi8KCi8qKgogKiBTYWRseSwgZHVlIHRvIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2hlY2tzIHdlIGNhbm5vdCBhY3R1YWxseSBpbXBvcnQgdGhlIFNwYW4gY2xhc3MgaGVyZSBhbmQgY2hlY2sgZm9yIGluc3RhbmNlb2YuCiAqIDooIFNvIGluc3RlYWQgd2UgYXBwcm94aW1hdGUgdGhpcyBieSBjaGVja2luZyBpZiBpdCBoYXMgdGhlIGBnZXRTcGFuSlNPTmAgbWV0aG9kLgogKi8KZnVuY3Rpb24gc3BhbklzU2VudHJ5U3BhbihzcGFuKSB7CiAgcmV0dXJuIHR5cGVvZiAoc3BhbiApLmdldFNwYW5KU09OID09PSAnZnVuY3Rpb24nOwp9CgovKiogR2V0IHRoZSBzdGF0dXMgbWVzc2FnZSB0byB1c2UgZm9yIGEgSlNPTiByZXByZXNlbnRhdGlvbiBvZiBhIHNwYW4uICovCmZ1bmN0aW9uIGdldFN0YXR1c01lc3NhZ2Uoc3RhdHVzKSB7CiAgaWYgKCFzdGF0dXMgfHwgc3RhdHVzLmNvZGUgPT09IFNQQU5fU1RBVFVTX1VOU0VUKSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgaWYgKHN0YXR1cy5jb2RlID09PSBTUEFOX1NUQVRVU19PSykgewogICAgcmV0dXJuICdvayc7CiAgfQoKICByZXR1cm4gc3RhdHVzLm1lc3NhZ2UgfHwgJ3Vua25vd25fZXJyb3InOwp9CgovKioKICogQXBwbHkgU2RrSW5mbyAobmFtZSwgdmVyc2lvbiwgcGFja2FnZXMsIGludGVncmF0aW9ucykgdG8gdGhlIGNvcnJlc3BvbmRpbmcgZXZlbnQga2V5LgogKiBNZXJnZSB3aXRoIGV4aXN0aW5nIGRhdGEgaWYgYW55LgogKiovCmZ1bmN0aW9uIGVuaGFuY2VFdmVudFdpdGhTZGtJbmZvKGV2ZW50LCBzZGtJbmZvKSB7CiAgaWYgKCFzZGtJbmZvKSB7CiAgICByZXR1cm4gZXZlbnQ7CiAgfQogIGV2ZW50LnNkayA9IGV2ZW50LnNkayB8fCB7fTsKICBldmVudC5zZGsubmFtZSA9IGV2ZW50LnNkay5uYW1lIHx8IHNka0luZm8ubmFtZTsKICBldmVudC5zZGsudmVyc2lvbiA9IGV2ZW50LnNkay52ZXJzaW9uIHx8IHNka0luZm8udmVyc2lvbjsKICBldmVudC5zZGsuaW50ZWdyYXRpb25zID0gWy4uLihldmVudC5zZGsuaW50ZWdyYXRpb25zIHx8IFtdKSwgLi4uKHNka0luZm8uaW50ZWdyYXRpb25zIHx8IFtdKV07CiAgZXZlbnQuc2RrLnBhY2thZ2VzID0gWy4uLihldmVudC5zZGsucGFja2FnZXMgfHwgW10pLCAuLi4oc2RrSW5mby5wYWNrYWdlcyB8fCBbXSldOwogIHJldHVybiBldmVudDsKfQoKLyoqIENyZWF0ZXMgYW4gZW52ZWxvcGUgZnJvbSBhIFNlc3Npb24gKi8KZnVuY3Rpb24gY3JlYXRlU2Vzc2lvbkVudmVsb3BlKAogIHNlc3Npb24sCiAgZHNuLAogIG1ldGFkYXRhLAogIHR1bm5lbCwKKSB7CiAgY29uc3Qgc2RrSW5mbyA9IGdldFNka01ldGFkYXRhRm9yRW52ZWxvcGVIZWFkZXIobWV0YWRhdGEpOwogIGNvbnN0IGVudmVsb3BlSGVhZGVycyA9IHsKICAgIHNlbnRfYXQ6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSwKICAgIC4uLihzZGtJbmZvICYmIHsgc2RrOiBzZGtJbmZvIH0pLAogICAgLi4uKCEhdHVubmVsICYmIGRzbiAmJiB7IGRzbjogZHNuVG9TdHJpbmcoZHNuKSB9KSwKICB9OwoKICBjb25zdCBlbnZlbG9wZUl0ZW0gPQogICAgJ2FnZ3JlZ2F0ZXMnIGluIHNlc3Npb24gPyBbeyB0eXBlOiAnc2Vzc2lvbnMnIH0sIHNlc3Npb25dIDogW3sgdHlwZTogJ3Nlc3Npb24nIH0sIHNlc3Npb24udG9KU09OKCldOwoKICByZXR1cm4gY3JlYXRlRW52ZWxvcGUoZW52ZWxvcGVIZWFkZXJzLCBbZW52ZWxvcGVJdGVtXSk7Cn0KCi8qKgogKiBDcmVhdGUgYW4gRW52ZWxvcGUgZnJvbSBhbiBldmVudC4KICovCmZ1bmN0aW9uIGNyZWF0ZUV2ZW50RW52ZWxvcGUoCiAgZXZlbnQsCiAgZHNuLAogIG1ldGFkYXRhLAogIHR1bm5lbCwKKSB7CiAgY29uc3Qgc2RrSW5mbyA9IGdldFNka01ldGFkYXRhRm9yRW52ZWxvcGVIZWFkZXIobWV0YWRhdGEpOwoKICAvKgogICAgTm90ZTogRHVlIHRvIFRTLCBldmVudC50eXBlIG1heSBiZSBgcmVwbGF5X2V2ZW50YCwgdGhlb3JldGljYWxseS4KICAgIEluIHByYWN0aWNlLCB3ZSBuZXZlciBjYWxsIGBjcmVhdGVFdmVudEVudmVsb3BlYCB3aXRoIGByZXBsYXlfZXZlbnRgIHR5cGUsCiAgICBhbmQgd2UnZCBoYXZlIHRvIGFkanV0IGEgbG9vb3Qgb2YgdHlwZXMgdG8gbWFrZSB0aGlzIHdvcmsgcHJvcGVybHkuCiAgICBXZSB3YW50IHRvIGF2b2lkIGNhc3RpbmcgdGhpcyBhcm91bmQsIGFzIHRoYXQgY291bGQgbGVhZCB0byBidWdzIChlLmcuIHdoZW4gd2UgYWRkIGFub3RoZXIgdHlwZSkKICAgIFNvIHRoZSBzYWZlIGNob2ljZSBpcyB0byByZWFsbHkgZ3VhcmQgYWdhaW5zdCB0aGUgcmVwbGF5X2V2ZW50IHR5cGUgaGVyZS4KICAqLwogIGNvbnN0IGV2ZW50VHlwZSA9IGV2ZW50LnR5cGUgJiYgZXZlbnQudHlwZSAhPT0gJ3JlcGxheV9ldmVudCcgPyBldmVudC50eXBlIDogJ2V2ZW50JzsKCiAgZW5oYW5jZUV2ZW50V2l0aFNka0luZm8oZXZlbnQsIG1ldGFkYXRhICYmIG1ldGFkYXRhLnNkayk7CgogIGNvbnN0IGVudmVsb3BlSGVhZGVycyA9IGNyZWF0ZUV2ZW50RW52ZWxvcGVIZWFkZXJzKGV2ZW50LCBzZGtJbmZvLCB0dW5uZWwsIGRzbik7CgogIC8vIFByZXZlbnQgdGhpcyBkYXRhICh3aGljaCwgaWYgaXQgZXhpc3RzLCB3YXMgdXNlZCBpbiBlYXJsaWVyIHN0ZXBzIGluIHRoZSBwcm9jZXNzaW5nIHBpcGVsaW5lKSBmcm9tIGJlaW5nIHNlbnQgdG8KICAvLyBzZW50cnkuIChOb3RlOiBPdXIgdXNlIG9mIHRoaXMgcHJvcGVydHkgY29tZXMgYW5kIGdvZXMgd2l0aCB3aGF0ZXZlciB3ZSBtaWdodCBiZSBkZWJ1Z2dpbmcsIHdoYXRldmVyIGhhY2tzIHdlIG1heQogIC8vIGhhdmUgdGVtcG9yYXJpbHkgYWRkZWQsIGV0Yy4gRXZlbiBpZiB3ZSBkb24ndCBoYXBwZW4gdG8gYmUgdXNpbmcgaXQgYXQgc29tZSBwb2ludCBpbiB0aGUgZnV0dXJlLCBsZXQncyBub3QgZ2V0IHJpZAogIC8vIG9mIHRoaXMgYGRlbGV0ZWAsIGxlc3Qgd2UgbWlzcyBwdXR0aW5nIGl0IGJhY2sgaW4gdGhlIG5leHQgdGltZSB0aGUgcHJvcGVydHkgaXMgaW4gdXNlLikKICBkZWxldGUgZXZlbnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhOwoKICBjb25zdCBldmVudEl0ZW0gPSBbeyB0eXBlOiBldmVudFR5cGUgfSwgZXZlbnRdOwogIHJldHVybiBjcmVhdGVFbnZlbG9wZShlbnZlbG9wZUhlYWRlcnMsIFtldmVudEl0ZW1dKTsKfQoKY29uc3QgU0VOVFJZX0FQSV9WRVJTSU9OID0gJzcnOwoKLyoqIFJldHVybnMgdGhlIHByZWZpeCB0byBjb25zdHJ1Y3QgU2VudHJ5IGluZ2VzdGlvbiBBUEkgZW5kcG9pbnRzLiAqLwpmdW5jdGlvbiBnZXRCYXNlQXBpRW5kcG9pbnQoZHNuKSB7CiAgY29uc3QgcHJvdG9jb2wgPSBkc24ucHJvdG9jb2wgPyBgJHtkc24ucHJvdG9jb2x9OmAgOiAnJzsKICBjb25zdCBwb3J0ID0gZHNuLnBvcnQgPyBgOiR7ZHNuLnBvcnR9YCA6ICcnOwogIHJldHVybiBgJHtwcm90b2NvbH0vLyR7ZHNuLmhvc3R9JHtwb3J0fSR7ZHNuLnBhdGggPyBgLyR7ZHNuLnBhdGh9YCA6ICcnfS9hcGkvYDsKfQoKLyoqIFJldHVybnMgdGhlIGluZ2VzdCBBUEkgZW5kcG9pbnQgZm9yIHRhcmdldC4gKi8KZnVuY3Rpb24gX2dldEluZ2VzdEVuZHBvaW50KGRzbikgewogIHJldHVybiBgJHtnZXRCYXNlQXBpRW5kcG9pbnQoZHNuKX0ke2Rzbi5wcm9qZWN0SWR9L2VudmVsb3BlL2A7Cn0KCi8qKiBSZXR1cm5zIGEgVVJMLWVuY29kZWQgc3RyaW5nIHdpdGggYXV0aCBjb25maWcgc3VpdGFibGUgZm9yIGEgcXVlcnkgc3RyaW5nLiAqLwpmdW5jdGlvbiBfZW5jb2RlZEF1dGgoZHNuLCBzZGtJbmZvKSB7CiAgcmV0dXJuIHVybEVuY29kZSh7CiAgICAvLyBXZSBzZW5kIG9ubHkgdGhlIG1pbmltdW0gc2V0IG9mIHJlcXVpcmVkIGluZm9ybWF0aW9uLiBTZWUKICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9nZXRzZW50cnkvc2VudHJ5LWphdmFzY3JpcHQvaXNzdWVzLzI1NzIuCiAgICBzZW50cnlfa2V5OiBkc24ucHVibGljS2V5LAogICAgc2VudHJ5X3ZlcnNpb246IFNFTlRSWV9BUElfVkVSU0lPTiwKICAgIC4uLihzZGtJbmZvICYmIHsgc2VudHJ5X2NsaWVudDogYCR7c2RrSW5mby5uYW1lfS8ke3Nka0luZm8udmVyc2lvbn1gIH0pLAogIH0pOwp9CgovKioKICogUmV0dXJucyB0aGUgZW52ZWxvcGUgZW5kcG9pbnQgVVJMIHdpdGggYXV0aCBpbiB0aGUgcXVlcnkgc3RyaW5nLgogKgogKiBTZW5kaW5nIGF1dGggYXMgcGFydCBvZiB0aGUgcXVlcnkgc3RyaW5nIGFuZCBub3QgYXMgY3VzdG9tIEhUVFAgaGVhZGVycyBhdm9pZHMgQ09SUyBwcmVmbGlnaHQgcmVxdWVzdHMuCiAqLwpmdW5jdGlvbiBnZXRFbnZlbG9wZUVuZHBvaW50V2l0aFVybEVuY29kZWRBdXRoKAogIGRzbiwKICAvLyBUT0RPICh2OCk6IFJlbW92ZSBgdHVubmVsT3JPcHRpb25zYCBpbiBmYXZvciBvZiBgb3B0aW9uc2AsIGFuZCB1c2UgdGhlIHN1YnN0aXR1dGUgY29kZSBiZWxvdwogIC8vIG9wdGlvbnM6IENsaWVudE9wdGlvbnMgPSB7fSBhcyBDbGllbnRPcHRpb25zLAogIHR1bm5lbE9yT3B0aW9ucyA9IHt9ICwKKSB7CiAgLy8gVE9ETyAodjgpOiBVc2UgdGhpcyBjb2RlIGluc3RlYWQKICAvLyBjb25zdCB7IHR1bm5lbCwgX21ldGFkYXRhID0ge30gfSA9IG9wdGlvbnM7CiAgLy8gcmV0dXJuIHR1bm5lbCA/IHR1bm5lbCA6IGAke19nZXRJbmdlc3RFbmRwb2ludChkc24pfT8ke19lbmNvZGVkQXV0aChkc24sIF9tZXRhZGF0YS5zZGspfWA7CgogIGNvbnN0IHR1bm5lbCA9IHR5cGVvZiB0dW5uZWxPck9wdGlvbnMgPT09ICdzdHJpbmcnID8gdHVubmVsT3JPcHRpb25zIDogdHVubmVsT3JPcHRpb25zLnR1bm5lbDsKICBjb25zdCBzZGtJbmZvID0KICAgIHR5cGVvZiB0dW5uZWxPck9wdGlvbnMgPT09ICdzdHJpbmcnIHx8ICF0dW5uZWxPck9wdGlvbnMuX21ldGFkYXRhID8gdW5kZWZpbmVkIDogdHVubmVsT3JPcHRpb25zLl9tZXRhZGF0YS5zZGs7CgogIHJldHVybiB0dW5uZWwgPyB0dW5uZWwgOiBgJHtfZ2V0SW5nZXN0RW5kcG9pbnQoZHNuKX0/JHtfZW5jb2RlZEF1dGgoZHNuLCBzZGtJbmZvKX1gOwp9CgovKioKICogQ29udmVydCBhIG5ldyBpbnRlZ3JhdGlvbiBmdW5jdGlvbiB0byB0aGUgbGVnYWN5IGNsYXNzIHN5bnRheC4KICogSW4gdjgsIHdlIGNhbiByZW1vdmUgdGhpcyBhbmQgaW5zdGVhZCBleHBvcnQgdGhlIGludGVncmF0aW9uIGZ1bmN0aW9ucyBkaXJlY3RseS4KICoKICogQGRlcHJlY2F0ZWQgVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjghCiAqLwpmdW5jdGlvbiBjb252ZXJ0SW50ZWdyYXRpb25GblRvQ2xhc3MoCiAgbmFtZSwKICBmbiwKKSB7CiAgcmV0dXJuIE9iamVjdC5hc3NpZ24oCiAgICBmdW5jdGlvbiBDb252ZXJ0ZWRJbnRlZ3JhdGlvbiguLi5hcmdzKSB7CiAgICAgIHJldHVybiBmbiguLi5hcmdzKTsKICAgIH0sCiAgICB7IGlkOiBuYW1lIH0sCiAgKSA7Cn0KCi8qKgogKiBEZWZpbmUgYW4gaW50ZWdyYXRpb24gZnVuY3Rpb24gdGhhdCBjYW4gYmUgdXNlZCB0byBjcmVhdGUgYW4gaW50ZWdyYXRpb24gaW5zdGFuY2UuCiAqIE5vdGUgdGhhdCB0aGlzIGJ5IGRlc2lnbiBoaWRlcyB0aGUgaW1wbGVtZW50YXRpb24gZGV0YWlscyBvZiB0aGUgaW50ZWdyYXRpb24sIGFzIHRoZXkgYXJlIGNvbnNpZGVyZWQgaW50ZXJuYWwuCiAqLwpmdW5jdGlvbiBkZWZpbmVJbnRlZ3JhdGlvbihmbikgewogIHJldHVybiBmbjsKfQoKY29uc3QgREVGQVVMVF9UUkFOU1BPUlRfQlVGRkVSX1NJWkUgPSAzMDsKCi8qKgogKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGEgU2VudHJ5IGBUcmFuc3BvcnRgCiAqCiAqIEBwYXJhbSBvcHRpb25zCiAqIEBwYXJhbSBtYWtlUmVxdWVzdAogKi8KZnVuY3Rpb24gY3JlYXRlVHJhbnNwb3J0KAogIG9wdGlvbnMsCiAgbWFrZVJlcXVlc3QsCiAgYnVmZmVyID0gbWFrZVByb21pc2VCdWZmZXIoCiAgICBvcHRpb25zLmJ1ZmZlclNpemUgfHwgREVGQVVMVF9UUkFOU1BPUlRfQlVGRkVSX1NJWkUsCiAgKSwKKSB7CiAgbGV0IHJhdGVMaW1pdHMgPSB7fTsKICBjb25zdCBmbHVzaCA9ICh0aW1lb3V0KSA9PiBidWZmZXIuZHJhaW4odGltZW91dCk7CgogIGZ1bmN0aW9uIHNlbmQoZW52ZWxvcGUpIHsKICAgIGNvbnN0IGZpbHRlcmVkRW52ZWxvcGVJdGVtcyA9IFtdOwoKICAgIC8vIERyb3AgcmF0ZSBsaW1pdGVkIGl0ZW1zIGZyb20gZW52ZWxvcGUKICAgIGZvckVhY2hFbnZlbG9wZUl0ZW0oZW52ZWxvcGUsIChpdGVtLCB0eXBlKSA9PiB7CiAgICAgIGNvbnN0IGVudmVsb3BlSXRlbURhdGFDYXRlZ29yeSA9IGVudmVsb3BlSXRlbVR5cGVUb0RhdGFDYXRlZ29yeSh0eXBlKTsKICAgICAgaWYgKGlzUmF0ZUxpbWl0ZWQocmF0ZUxpbWl0cywgZW52ZWxvcGVJdGVtRGF0YUNhdGVnb3J5KSkgewogICAgICAgIGNvbnN0IGV2ZW50ID0gZ2V0RXZlbnRGb3JFbnZlbG9wZUl0ZW0oaXRlbSwgdHlwZSk7CiAgICAgICAgb3B0aW9ucy5yZWNvcmREcm9wcGVkRXZlbnQoJ3JhdGVsaW1pdF9iYWNrb2ZmJywgZW52ZWxvcGVJdGVtRGF0YUNhdGVnb3J5LCBldmVudCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZmlsdGVyZWRFbnZlbG9wZUl0ZW1zLnB1c2goaXRlbSk7CiAgICAgIH0KICAgIH0pOwoKICAgIC8vIFNraXAgc2VuZGluZyBpZiBlbnZlbG9wZSBpcyBlbXB0eSBhZnRlciBmaWx0ZXJpbmcgb3V0IHJhdGUgbGltaXRlZCBldmVudHMKICAgIGlmIChmaWx0ZXJlZEVudmVsb3BlSXRlbXMubGVuZ3RoID09PSAwKSB7CiAgICAgIHJldHVybiByZXNvbHZlZFN5bmNQcm9taXNlKHt9KTsKICAgIH0KCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQogICAgY29uc3QgZmlsdGVyZWRFbnZlbG9wZSA9IGNyZWF0ZUVudmVsb3BlKGVudmVsb3BlWzBdLCBmaWx0ZXJlZEVudmVsb3BlSXRlbXMgKTsKCiAgICAvLyBDcmVhdGVzIGNsaWVudCByZXBvcnQgZm9yIGVhY2ggaXRlbSBpbiBhbiBlbnZlbG9wZQogICAgY29uc3QgcmVjb3JkRW52ZWxvcGVMb3NzID0gKHJlYXNvbikgPT4gewogICAgICBmb3JFYWNoRW52ZWxvcGVJdGVtKGZpbHRlcmVkRW52ZWxvcGUsIChpdGVtLCB0eXBlKSA9PiB7CiAgICAgICAgY29uc3QgZXZlbnQgPSBnZXRFdmVudEZvckVudmVsb3BlSXRlbShpdGVtLCB0eXBlKTsKICAgICAgICBvcHRpb25zLnJlY29yZERyb3BwZWRFdmVudChyZWFzb24sIGVudmVsb3BlSXRlbVR5cGVUb0RhdGFDYXRlZ29yeSh0eXBlKSwgZXZlbnQpOwogICAgICB9KTsKICAgIH07CgogICAgY29uc3QgcmVxdWVzdFRhc2sgPSAoKSA9PgogICAgICBtYWtlUmVxdWVzdCh7IGJvZHk6IHNlcmlhbGl6ZUVudmVsb3BlKGZpbHRlcmVkRW52ZWxvcGUpIH0pLnRoZW4oCiAgICAgICAgcmVzcG9uc2UgPT4gewogICAgICAgICAgLy8gV2UgZG9uJ3Qgd2FudCB0byB0aHJvdyBvbiBOT0sgcmVzcG9uc2VzLCBidXQgd2Ugd2FudCB0byBhdCBsZWFzdCBsb2cgdGhlbQogICAgICAgICAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgIT09IHVuZGVmaW5lZCAmJiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA8IDIwMCB8fCByZXNwb25zZS5zdGF0dXNDb2RlID49IDMwMCkpIHsKICAgICAgICAgICAgREVCVUdfQlVJTEQgJiYgbG9nZ2VyLndhcm4oYFNlbnRyeSByZXNwb25kZWQgd2l0aCBzdGF0dXMgY29kZSAke3Jlc3BvbnNlLnN0YXR1c0NvZGV9IHRvIHNlbnQgZXZlbnQuYCk7CiAgICAgICAgICB9CgogICAgICAgICAgcmF0ZUxpbWl0cyA9IHVwZGF0ZVJhdGVMaW1pdHMocmF0ZUxpbWl0cywgcmVzcG9uc2UpOwogICAgICAgICAgcmV0dXJuIHJlc3BvbnNlOwogICAgICAgIH0sCiAgICAgICAgZXJyb3IgPT4gewogICAgICAgICAgcmVjb3JkRW52ZWxvcGVMb3NzKCduZXR3b3JrX2Vycm9yJyk7CiAgICAgICAgICB0aHJvdyBlcnJvcjsKICAgICAgICB9LAogICAgICApOwoKICAgIHJldHVybiBidWZmZXIuYWRkKHJlcXVlc3RUYXNrKS50aGVuKAogICAgICByZXN1bHQgPT4gcmVzdWx0LAogICAgICBlcnJvciA9PiB7CiAgICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgU2VudHJ5RXJyb3IpIHsKICAgICAgICAgIERFQlVHX0JVSUxEICYmIGxvZ2dlci5lcnJvcignU2tpcHBlZCBzZW5kaW5nIGV2ZW50IGJlY2F1c2UgYnVmZmVyIGlzIGZ1bGwuJyk7CiAgICAgICAgICByZWNvcmRFbnZlbG9wZUxvc3MoJ3F1ZXVlX292ZXJmbG93Jyk7CiAgICAgICAgICByZXR1cm4gcmVzb2x2ZWRTeW5jUHJvbWlzZSh7fSk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHRocm93IGVycm9yOwogICAgICAgIH0KICAgICAgfSwKICAgICk7CiAgfQoKICByZXR1cm4gewogICAgc2VuZCwKICAgIGZsdXNoLAogIH07Cn0KCmZ1bmN0aW9uIGdldEV2ZW50Rm9yRW52ZWxvcGVJdGVtKGl0ZW0sIHR5cGUpIHsKICBpZiAodHlwZSAhPT0gJ2V2ZW50JyAmJiB0eXBlICE9PSAndHJhbnNhY3Rpb24nKSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgcmV0dXJuIEFycmF5LmlzQXJyYXkoaXRlbSkgPyAoaXRlbSApWzFdIDogdW5kZWZpbmVkOwp9CgovKiogS2V5cyBhcmUgc291cmNlIGZpbGVuYW1lL3VybCwgdmFsdWVzIGFyZSBtZXRhZGF0YSBvYmplY3RzLiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQpjb25zdCBmaWxlbmFtZU1ldGFkYXRhTWFwID0gbmV3IE1hcCgpOwovKiogU2V0IG9mIHN0YWNrIHN0cmluZ3MgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiBwYXJzZWQuICovCmNvbnN0IHBhcnNlZFN0YWNrcyA9IG5ldyBTZXQoKTsKCmZ1bmN0aW9uIGVuc3VyZU1ldGFkYXRhU3RhY2tzQXJlUGFyc2VkKHBhcnNlcikgewogIGlmICghR0xPQkFMX09CSi5fc2VudHJ5TW9kdWxlTWV0YWRhdGEpIHsKICAgIHJldHVybjsKICB9CgogIGZvciAoY29uc3Qgc3RhY2sgb2YgT2JqZWN0LmtleXMoR0xPQkFMX09CSi5fc2VudHJ5TW9kdWxlTWV0YWRhdGEpKSB7CiAgICBjb25zdCBtZXRhZGF0YSA9IEdMT0JBTF9PQkouX3NlbnRyeU1vZHVsZU1ldGFkYXRhW3N0YWNrXTsKCiAgICBpZiAocGFyc2VkU3RhY2tzLmhhcyhzdGFjaykpIHsKICAgICAgY29udGludWU7CiAgICB9CgogICAgLy8gRW5zdXJlIHRoaXMgc3RhY2sgZG9lc24ndCBnZXQgcGFyc2VkIGFnYWluCiAgICBwYXJzZWRTdGFja3MuYWRkKHN0YWNrKTsKCiAgICBjb25zdCBmcmFtZXMgPSBwYXJzZXIoc3RhY2spOwoKICAgIC8vIEdvIHRocm91Z2ggdGhlIGZyYW1lcyBzdGFydGluZyBmcm9tIHRoZSB0b3Agb2YgdGhlIHN0YWNrIGFuZCBmaW5kIHRoZSBmaXJzdCBvbmUgd2l0aCBhIGZpbGVuYW1lCiAgICBmb3IgKGNvbnN0IGZyYW1lIG9mIGZyYW1lcy5yZXZlcnNlKCkpIHsKICAgICAgaWYgKGZyYW1lLmZpbGVuYW1lKSB7CiAgICAgICAgLy8gU2F2ZSB0aGUgbWV0YWRhdGEgZm9yIHRoaXMgZmlsZW5hbWUKICAgICAgICBmaWxlbmFtZU1ldGFkYXRhTWFwLnNldChmcmFtZS5maWxlbmFtZSwgbWV0YWRhdGEpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfQp9CgovKioKICogUmV0cmlldmUgbWV0YWRhdGEgZm9yIGEgc3BlY2lmaWMgSmF2YVNjcmlwdCBmaWxlIFVSTC4KICoKICogTWV0YWRhdGEgaXMgaW5qZWN0ZWQgYnkgdGhlIFNlbnRyeSBidW5kbGVyIHBsdWdpbnMgdXNpbmcgdGhlIGBfZXhwZXJpbWVudHMubW9kdWxlTWV0YWRhdGFgIGNvbmZpZyBvcHRpb24uCiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQpmdW5jdGlvbiBnZXRNZXRhZGF0YUZvclVybChwYXJzZXIsIGZpbGVuYW1lKSB7CiAgZW5zdXJlTWV0YWRhdGFTdGFja3NBcmVQYXJzZWQocGFyc2VyKTsKICByZXR1cm4gZmlsZW5hbWVNZXRhZGF0YU1hcC5nZXQoZmlsZW5hbWUpOwp9CgovKioKICogQWRkcyBtZXRhZGF0YSB0byBzdGFjayBmcmFtZXMuCiAqCiAqIE1ldGFkYXRhIGlzIGluamVjdGVkIGJ5IHRoZSBTZW50cnkgYnVuZGxlciBwbHVnaW5zIHVzaW5nIHRoZSBgX2V4cGVyaW1lbnRzLm1vZHVsZU1ldGFkYXRhYCBjb25maWcgb3B0aW9uLgogKi8KZnVuY3Rpb24gYWRkTWV0YWRhdGFUb1N0YWNrRnJhbWVzKHBhcnNlciwgZXZlbnQpIHsKICB0cnkgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb24KICAgIGV2ZW50LmV4Y2VwdGlvbi52YWx1ZXMuZm9yRWFjaChleGNlcHRpb24gPT4gewogICAgICBpZiAoIWV4Y2VwdGlvbi5zdGFja3RyYWNlKSB7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICBmb3IgKGNvbnN0IGZyYW1lIG9mIGV4Y2VwdGlvbi5zdGFja3RyYWNlLmZyYW1lcyB8fCBbXSkgewogICAgICAgIGlmICghZnJhbWUuZmlsZW5hbWUpIHsKICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KCiAgICAgICAgY29uc3QgbWV0YWRhdGEgPSBnZXRNZXRhZGF0YUZvclVybChwYXJzZXIsIGZyYW1lLmZpbGVuYW1lKTsKCiAgICAgICAgaWYgKG1ldGFkYXRhKSB7CiAgICAgICAgICBmcmFtZS5tb2R1bGVfbWV0YWRhdGEgPSBtZXRhZGF0YTsKICAgICAgICB9CiAgICAgIH0KICAgIH0pOwogIH0gY2F0Y2ggKF8pIHsKICAgIC8vIFRvIHNhdmUgYnVuZGxlIHNpemUgd2UncmUganVzdCB0cnkgY2F0Y2hpbmcgaGVyZSBpbnN0ZWFkIG9mIGNoZWNraW5nIGZvciB0aGUgZXhpc3RlbmNlIG9mIGFsbCB0aGUgZGlmZmVyZW50IG9iamVjdHMuCiAgfQp9CgovKioKICogU3RyaXBzIG1ldGFkYXRhIGZyb20gc3RhY2sgZnJhbWVzLgogKi8KZnVuY3Rpb24gc3RyaXBNZXRhZGF0YUZyb21TdGFja0ZyYW1lcyhldmVudCkgewogIHRyeSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW5vbi1udWxsLWFzc2VydGlvbgogICAgZXZlbnQuZXhjZXB0aW9uLnZhbHVlcy5mb3JFYWNoKGV4Y2VwdGlvbiA9PiB7CiAgICAgIGlmICghZXhjZXB0aW9uLnN0YWNrdHJhY2UpIHsKICAgICAgICByZXR1cm47CiAgICAgIH0KCiAgICAgIGZvciAoY29uc3QgZnJhbWUgb2YgZXhjZXB0aW9uLnN0YWNrdHJhY2UuZnJhbWVzIHx8IFtdKSB7CiAgICAgICAgZGVsZXRlIGZyYW1lLm1vZHVsZV9tZXRhZGF0YTsKICAgICAgfQogICAgfSk7CiAgfSBjYXRjaCAoXykgewogICAgLy8gVG8gc2F2ZSBidW5kbGUgc2l6ZSB3ZSdyZSBqdXN0IHRyeSBjYXRjaGluZyBoZXJlIGluc3RlYWQgb2YgY2hlY2tpbmcgZm9yIHRoZSBleGlzdGVuY2Ugb2YgYWxsIHRoZSBkaWZmZXJlbnQgb2JqZWN0cy4KICB9Cn0KCmNvbnN0IElOVEVHUkFUSU9OX05BTUUkYSA9ICdNb2R1bGVNZXRhZGF0YSc7Cgpjb25zdCBfbW9kdWxlTWV0YWRhdGFJbnRlZ3JhdGlvbiA9ICgoKSA9PiB7CiAgcmV0dXJuIHsKICAgIG5hbWU6IElOVEVHUkFUSU9OX05BTUUkYSwKICAgIHNldHVwKGNsaWVudCkgewogICAgICAvLyBXZSBuZWVkIHRvIHN0cmlwIG1ldGFkYXRhIGZyb20gc3RhY2sgZnJhbWVzIGJlZm9yZSBzZW5kaW5nIHRoZW0gdG8gU2VudHJ5IHNpbmNlIHRoZXNlIGFyZSBjbGllbnQgc2lkZSBvbmx5LgogICAgICBjbGllbnQub24oJ2JlZm9yZUVudmVsb3BlJywgZW52ZWxvcGUgPT4gewogICAgICAgIGZvckVhY2hFbnZlbG9wZUl0ZW0oZW52ZWxvcGUsIChpdGVtLCB0eXBlKSA9PiB7CiAgICAgICAgICBpZiAodHlwZSA9PT0gJ2V2ZW50JykgewogICAgICAgICAgICBjb25zdCBldmVudCA9IEFycmF5LmlzQXJyYXkoaXRlbSkgPyAoaXRlbSApWzFdIDogdW5kZWZpbmVkOwoKICAgICAgICAgICAgaWYgKGV2ZW50KSB7CiAgICAgICAgICAgICAgc3RyaXBNZXRhZGF0YUZyb21TdGFja0ZyYW1lcyhldmVudCk7CiAgICAgICAgICAgICAgaXRlbVsxXSA9IGV2ZW50OwogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgIH0pOwogICAgfSwKCiAgICBwcm9jZXNzRXZlbnQoZXZlbnQsIF9oaW50LCBjbGllbnQpIHsKICAgICAgY29uc3Qgc3RhY2tQYXJzZXIgPSBjbGllbnQuZ2V0T3B0aW9ucygpLnN0YWNrUGFyc2VyOwogICAgICBhZGRNZXRhZGF0YVRvU3RhY2tGcmFtZXMoc3RhY2tQYXJzZXIsIGV2ZW50KTsKICAgICAgcmV0dXJuIGV2ZW50OwogICAgfSwKICB9Owp9KSA7Cgpjb25zdCBtb2R1bGVNZXRhZGF0YUludGVncmF0aW9uID0gZGVmaW5lSW50ZWdyYXRpb24oX21vZHVsZU1ldGFkYXRhSW50ZWdyYXRpb24pOwoKLyoqCiAqIEFkZHMgbW9kdWxlIG1ldGFkYXRhIHRvIHN0YWNrIGZyYW1lcy4KICoKICogTWV0YWRhdGEgY2FuIGJlIGluamVjdGVkIGJ5IHRoZSBTZW50cnkgYnVuZGxlciBwbHVnaW5zIHVzaW5nIHRoZSBgX2V4cGVyaW1lbnRzLm1vZHVsZU1ldGFkYXRhYCBjb25maWcgb3B0aW9uLgogKgogKiBXaGVuIHRoaXMgaW50ZWdyYXRpb24gaXMgYWRkZWQsIHRoZSBtZXRhZGF0YSBwYXNzZWQgdG8gdGhlIGJ1bmRsZXIgcGx1Z2luIGlzIGFkZGVkIHRvIHRoZSBzdGFjayBmcmFtZXMgb2YgYWxsIGV2ZW50cwogKiB1bmRlciB0aGUgYG1vZHVsZV9tZXRhZGF0YWAgcHJvcGVydHkuIFRoaXMgY2FuIGJlIHVzZWQgdG8gaGVscCBpbiB0YWdnaW5nIG9yIHJvdXRpbmcgb2YgZXZlbnRzIGZyb20gZGlmZmVyZW50IHRlYW1zCiAqIG91ciBzb3VyY2VzCiAqCiAqIEBkZXByZWNhdGVkIFVzZSBgbW9kdWxlTWV0YWRhdGFJbnRlZ3JhdGlvbigpYCBpbnN0ZWFkLgogKi8KLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCmNvbnZlcnRJbnRlZ3JhdGlvbkZuVG9DbGFzcygKICBJTlRFR1JBVElPTl9OQU1FJGEsCiAgbW9kdWxlTWV0YWRhdGFJbnRlZ3JhdGlvbiwKKQoKOwoKY29uc3QgREVGQVVMVF9PUFRJT05TID0gewogIGluY2x1ZGU6IHsKICAgIGNvb2tpZXM6IHRydWUsCiAgICBkYXRhOiB0cnVlLAogICAgaGVhZGVyczogdHJ1ZSwKICAgIGlwOiBmYWxzZSwKICAgIHF1ZXJ5X3N0cmluZzogdHJ1ZSwKICAgIHVybDogdHJ1ZSwKICAgIHVzZXI6IHsKICAgICAgaWQ6IHRydWUsCiAgICAgIHVzZXJuYW1lOiB0cnVlLAogICAgICBlbWFpbDogdHJ1ZSwKICAgIH0sCiAgfSwKICB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZTogJ21ldGhvZFBhdGgnICwKfTsKCmNvbnN0IElOVEVHUkFUSU9OX05BTUUkOSA9ICdSZXF1ZXN0RGF0YSc7Cgpjb25zdCBfcmVxdWVzdERhdGFJbnRlZ3JhdGlvbiA9ICgob3B0aW9ucyA9IHt9KSA9PiB7CiAgY29uc3QgX29wdGlvbnMgPSB7CiAgICAuLi5ERUZBVUxUX09QVElPTlMsCiAgICAuLi5vcHRpb25zLAogICAgaW5jbHVkZTogewogICAgICAuLi5ERUZBVUxUX09QVElPTlMuaW5jbHVkZSwKICAgICAgLi4ub3B0aW9ucy5pbmNsdWRlLAogICAgICB1c2VyOgogICAgICAgIG9wdGlvbnMuaW5jbHVkZSAmJiB0eXBlb2Ygb3B0aW9ucy5pbmNsdWRlLnVzZXIgPT09ICdib29sZWFuJwogICAgICAgICAgPyBvcHRpb25zLmluY2x1ZGUudXNlcgogICAgICAgICAgOiB7CiAgICAgICAgICAgICAgLi4uREVGQVVMVF9PUFRJT05TLmluY2x1ZGUudXNlciwKICAgICAgICAgICAgICAvLyBVbmNsZWFyIHdoeSBUUyBzdGlsbCB0aGlua3MgYG9wdGlvbnMuaW5jbHVkZS51c2VyYCBjb3VsZCBiZSBhIGJvb2xlYW4gYXQgdGhpcyBwb2ludAogICAgICAgICAgICAgIC4uLigob3B0aW9ucy5pbmNsdWRlIHx8IHt9KS51c2VyICksCiAgICAgICAgICAgIH0sCiAgICB9LAogIH07CgogIHJldHVybiB7CiAgICBuYW1lOiBJTlRFR1JBVElPTl9OQU1FJDksCiAgICBwcm9jZXNzRXZlbnQoZXZlbnQsIF9oaW50LCBjbGllbnQpIHsKICAgICAgLy8gTm90ZTogSW4gdGhlIGxvbmcgcnVuLCBtb3N0IG9mIHRoZSBsb2dpYyBoZXJlIHNob3VsZCBwcm9iYWJseSBtb3ZlIGludG8gdGhlIHJlcXVlc3QgZGF0YSB1dGlsaXR5IGZ1bmN0aW9ucy4gRm9yCiAgICAgIC8vIHRoZSBtb21lbnQgaXQgbGl2ZXMgaGVyZSwgdGhvdWdoLCB1bnRpbCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0L2lzc3Vlcy81NzE4IGlzIGFkZHJlc3NlZC4KICAgICAgLy8gKFRMO0RSOiBUaG9zZSBmdW5jdGlvbnMgdG91Y2ggbWFueSBwYXJ0cyBvZiB0aGUgcmVwbyBpbiBtYW55IGRpZmZlcmVudCB3YXlzLCBhbmQgbmVlZCB0byBiZSBjbGVuZWQgdXAuIE9uY2UKICAgICAgLy8gdGhhdCdzIGhhcHBlbmVkLCBpdCB3aWxsIGJlIGVhc2llciB0byBhZGQgdGhpcyBsb2dpYyBpbiB3aXRob3V0IHdvcnJ5aW5nIGFib3V0IHVuZXhwZWN0ZWQgc2lkZSBlZmZlY3RzLikKICAgICAgY29uc3QgeyB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZSB9ID0gX29wdGlvbnM7CgogICAgICBjb25zdCB7IHNka1Byb2Nlc3NpbmdNZXRhZGF0YSA9IHt9IH0gPSBldmVudDsKICAgICAgY29uc3QgcmVxID0gc2RrUHJvY2Vzc2luZ01ldGFkYXRhLnJlcXVlc3Q7CgogICAgICBpZiAoIXJlcSkgewogICAgICAgIHJldHVybiBldmVudDsKICAgICAgfQoKICAgICAgY29uc3QgYWRkUmVxdWVzdERhdGFPcHRpb25zID0gY29udmVydFJlcURhdGFJbnRlZ3JhdGlvbk9wdHNUb0FkZFJlcURhdGFPcHRzKF9vcHRpb25zKTsKCiAgICAgIGNvbnN0IHByb2Nlc3NlZEV2ZW50ID0gYWRkUmVxdWVzdERhdGFUb0V2ZW50KGV2ZW50LCByZXEsIGFkZFJlcXVlc3REYXRhT3B0aW9ucyk7CgogICAgICAvLyBUcmFuc2FjdGlvbiBldmVudHMgYWxyZWFkeSBoYXZlIHRoZSByaWdodCBgdHJhbnNhY3Rpb25gIHZhbHVlCiAgICAgIGlmIChldmVudC50eXBlID09PSAndHJhbnNhY3Rpb24nIHx8IHRyYW5zYWN0aW9uTmFtaW5nU2NoZW1lID09PSAnaGFuZGxlcicpIHsKICAgICAgICByZXR1cm4gcHJvY2Vzc2VkRXZlbnQ7CiAgICAgIH0KCiAgICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgdXNlIHRoZSByZXF1ZXN0J3MgYXNzb2NpYXRlZCB0cmFuc2FjdGlvbiAoaWYgYW55KSB0byBvdmVyd3JpdGUgdGhlIGV2ZW50J3MgYHRyYW5zYWN0aW9uYAogICAgICAvLyB2YWx1ZSB3aXRoIGEgaGlnaC1xdWFsaXR5IG9uZQogICAgICBjb25zdCByZXFXaXRoVHJhbnNhY3Rpb24gPSByZXEgOwogICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHJlcVdpdGhUcmFuc2FjdGlvbi5fc2VudHJ5VHJhbnNhY3Rpb247CiAgICAgIGlmICh0cmFuc2FjdGlvbikgewogICAgICAgIGNvbnN0IG5hbWUgPSBzcGFuVG9KU09OKHRyYW5zYWN0aW9uKS5kZXNjcmlwdGlvbiB8fCAnJzsKCiAgICAgICAgLy8gVE9ETyAodjgpOiBSZW1vdmUgdGhlIG5leHRqcyBjaGVjayBhbmQganVzdCBiYXNlIGl0IG9uIGB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZWAgZm9yIGFsbCBTREtzLiAoV2UgaGF2ZSB0bwogICAgICAgIC8vIGtlZXAgaXQgdGhlIHdheSBpdCBpcyBmb3IgdGhlIG1vbWVudCwgYmVjYXVzZSBjaGFuZ2luZyB0aGUgbmFtZXMgb2YgdHJhbnNhY3Rpb25zIGluIFNlbnRyeSBoYXMgdGhlIHBvdGVudGlhbAogICAgICAgIC8vIHRvIGJyZWFrIHRoaW5ncyBsaWtlIGFsZXJ0IHJ1bGVzLikKICAgICAgICBjb25zdCBzaG91bGRJbmNsdWRlTWV0aG9kSW5UcmFuc2FjdGlvbk5hbWUgPQogICAgICAgICAgZ2V0U0RLTmFtZShjbGllbnQpID09PSAnc2VudHJ5LmphdmFzY3JpcHQubmV4dGpzJwogICAgICAgICAgICA/IG5hbWUuc3RhcnRzV2l0aCgnL2FwaScpCiAgICAgICAgICAgIDogdHJhbnNhY3Rpb25OYW1pbmdTY2hlbWUgIT09ICdwYXRoJzsKCiAgICAgICAgY29uc3QgW3RyYW5zYWN0aW9uVmFsdWVdID0gZXh0cmFjdFBhdGhGb3JUcmFuc2FjdGlvbihyZXEsIHsKICAgICAgICAgIHBhdGg6IHRydWUsCiAgICAgICAgICBtZXRob2Q6IHNob3VsZEluY2x1ZGVNZXRob2RJblRyYW5zYWN0aW9uTmFtZSwKICAgICAgICAgIGN1c3RvbVJvdXRlOiBuYW1lLAogICAgICAgIH0pOwoKICAgICAgICBwcm9jZXNzZWRFdmVudC50cmFuc2FjdGlvbiA9IHRyYW5zYWN0aW9uVmFsdWU7CiAgICAgIH0KCiAgICAgIHJldHVybiBwcm9jZXNzZWRFdmVudDsKICAgIH0sCiAgfTsKfSkgOwoKY29uc3QgcmVxdWVzdERhdGFJbnRlZ3JhdGlvbiA9IGRlZmluZUludGVncmF0aW9uKF9yZXF1ZXN0RGF0YUludGVncmF0aW9uKTsKCi8qKgogKiBBZGQgZGF0YSBhYm91dCBhIHJlcXVlc3QgdG8gYW4gZXZlbnQuIFByaW1hcmlseSBmb3IgdXNlIGluIE5vZGUtYmFzZWQgU0RLcywgYnV0IGluY2x1ZGVkIGluIGBAc2VudHJ5L2NvcmVgCiAqIHNvIGl0IGNhbiBiZSB1c2VkIGluIGNyb3NzLXBsYXRmb3JtIFNES3MgbGlrZSBgQHNlbnRyeS9uZXh0anNgLgogKiBAZGVwcmVjYXRlZCBVc2UgYHJlcXVlc3REYXRhSW50ZWdyYXRpb24oKWAgaW5zdGVhZC4KICovCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgpjb252ZXJ0SW50ZWdyYXRpb25GblRvQ2xhc3MoSU5URUdSQVRJT05fTkFNRSQ5LCByZXF1ZXN0RGF0YUludGVncmF0aW9uKQoKOwoKLyoqIENvbnZlcnQgdGhpcyBpbnRlZ3JhdGlvbidzIG9wdGlvbnMgdG8gbWF0Y2ggd2hhdCBgYWRkUmVxdWVzdERhdGFUb0V2ZW50YCBleHBlY3RzICovCi8qKiBUT0RPOiBDYW4gcG9zc2libHkgYmUgZGVsZXRlZCBvbmNlIGh0dHBzOi8vZ2l0aHViLmNvbS9nZXRzZW50cnkvc2VudHJ5LWphdmFzY3JpcHQvaXNzdWVzLzU3MTggaXMgZml4ZWQgKi8KZnVuY3Rpb24gY29udmVydFJlcURhdGFJbnRlZ3JhdGlvbk9wdHNUb0FkZFJlcURhdGFPcHRzKAogIGludGVncmF0aW9uT3B0aW9ucywKKSB7CiAgY29uc3QgewogICAgdHJhbnNhY3Rpb25OYW1pbmdTY2hlbWUsCiAgICBpbmNsdWRlOiB7IGlwLCB1c2VyLCAuLi5yZXF1ZXN0T3B0aW9ucyB9LAogIH0gPSBpbnRlZ3JhdGlvbk9wdGlvbnM7CgogIGNvbnN0IHJlcXVlc3RJbmNsdWRlS2V5cyA9IFsnbWV0aG9kJ107CiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocmVxdWVzdE9wdGlvbnMpKSB7CiAgICBpZiAodmFsdWUpIHsKICAgICAgcmVxdWVzdEluY2x1ZGVLZXlzLnB1c2goa2V5KTsKICAgIH0KICB9CgogIGxldCBhZGRSZXFEYXRhVXNlck9wdDsKICBpZiAodXNlciA9PT0gdW5kZWZpbmVkKSB7CiAgICBhZGRSZXFEYXRhVXNlck9wdCA9IHRydWU7CiAgfSBlbHNlIGlmICh0eXBlb2YgdXNlciA9PT0gJ2Jvb2xlYW4nKSB7CiAgICBhZGRSZXFEYXRhVXNlck9wdCA9IHVzZXI7CiAgfSBlbHNlIHsKICAgIGNvbnN0IHVzZXJJbmNsdWRlS2V5cyA9IFtdOwogICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXModXNlcikpIHsKICAgICAgaWYgKHZhbHVlKSB7CiAgICAgICAgdXNlckluY2x1ZGVLZXlzLnB1c2goa2V5KTsKICAgICAgfQogICAgfQogICAgYWRkUmVxRGF0YVVzZXJPcHQgPSB1c2VySW5jbHVkZUtleXM7CiAgfQoKICByZXR1cm4gewogICAgaW5jbHVkZTogewogICAgICBpcCwKICAgICAgdXNlcjogYWRkUmVxRGF0YVVzZXJPcHQsCiAgICAgIHJlcXVlc3Q6IHJlcXVlc3RJbmNsdWRlS2V5cy5sZW5ndGggIT09IDAgPyByZXF1ZXN0SW5jbHVkZUtleXMgOiB1bmRlZmluZWQsCiAgICAgIHRyYW5zYWN0aW9uOiB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZSwKICAgIH0sCiAgfTsKfQoKZnVuY3Rpb24gZ2V0U0RLTmFtZShjbGllbnQpIHsKICB0cnkgewogICAgLy8gRm9yIGEgbG9uZyBjaGFpbiBsaWtlIHRoaXMsIGl0J3MgZmV3ZXIgYnl0ZXMgdG8gY29tYmluZSBhIHRyeS1jYXRjaCB3aXRoIGFzc3VtaW5nIGV2ZXJ5dGhpbmcgaXMgdGhlcmUgdGhhbiB0bwogICAgLy8gd3JpdGUgb3V0IGEgbG9uZyBjaGFpbiBvZiBgYSAmJiBhLmIgJiYgYS5iLmMgJiYgLi4uYAogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb24KICAgIHJldHVybiBjbGllbnQuZ2V0T3B0aW9ucygpLl9tZXRhZGF0YS5zZGsubmFtZTsKICB9IGNhdGNoIChlcnIpIHsKICAgIC8vIEluIHRoZW9yeSB3ZSBzaG91bGQgbmV2ZXIgZ2V0IGhlcmUKICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQp9CgpsZXQgb3JpZ2luYWxGdW5jdGlvblRvU3RyaW5nOwoKY29uc3QgSU5URUdSQVRJT05fTkFNRSQ4ID0gJ0Z1bmN0aW9uVG9TdHJpbmcnOwoKY29uc3QgU0VUVVBfQ0xJRU5UUyA9IG5ldyBXZWFrTWFwKCk7Cgpjb25zdCBfZnVuY3Rpb25Ub1N0cmluZ0ludGVncmF0aW9uID0gKCgpID0+IHsKICByZXR1cm4gewogICAgbmFtZTogSU5URUdSQVRJT05fTkFNRSQ4LAogICAgc2V0dXBPbmNlKCkgewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L3VuYm91bmQtbWV0aG9kCiAgICAgIG9yaWdpbmFsRnVuY3Rpb25Ub1N0cmluZyA9IEZ1bmN0aW9uLnByb3RvdHlwZS50b1N0cmluZzsKCiAgICAgIC8vIGludHJpbnNpY3MgKGxpa2UgRnVuY3Rpb24ucHJvdG90eXBlKSBtaWdodCBiZSBpbW11dGFibGUgaW4gc29tZSBlbnZpcm9ubWVudHMKICAgICAgLy8gZS5nLiBOb2RlIHdpdGggLS1mcm96ZW4taW50cmluc2ljcywgWFMgKGFuIGVtYmVkZGVkIEphdmFTY3JpcHQgZW5naW5lKSBvciBTRVMgKGEgSmF2YVNjcmlwdCBwcm9wb3NhbCkKICAgICAgdHJ5IHsKICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQogICAgICAgIEZ1bmN0aW9uLnByb3RvdHlwZS50b1N0cmluZyA9IGZ1bmN0aW9uICggLi4uYXJncykgewogICAgICAgICAgY29uc3Qgb3JpZ2luYWxGdW5jdGlvbiA9IGdldE9yaWdpbmFsRnVuY3Rpb24odGhpcyk7CiAgICAgICAgICBjb25zdCBjb250ZXh0ID0KICAgICAgICAgICAgU0VUVVBfQ0xJRU5UUy5oYXMoZ2V0Q2xpZW50KCkgKSAmJiBvcmlnaW5hbEZ1bmN0aW9uICE9PSB1bmRlZmluZWQgPyBvcmlnaW5hbEZ1bmN0aW9uIDogdGhpczsKICAgICAgICAgIHJldHVybiBvcmlnaW5hbEZ1bmN0aW9uVG9TdHJpbmcuYXBwbHkoY29udGV4dCwgYXJncyk7CiAgICAgICAgfTsKICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIC8vIGlnbm9yZSBlcnJvcnMgaGVyZSwganVzdCBkb24ndCBwYXRjaCB0aGlzCiAgICAgIH0KICAgIH0sCiAgICBzZXR1cChjbGllbnQpIHsKICAgICAgU0VUVVBfQ0xJRU5UUy5zZXQoY2xpZW50LCB0cnVlKTsKICAgIH0sCiAgfTsKfSkgOwoKLyoqCiAqIFBhdGNoIHRvU3RyaW5nIGNhbGxzIHRvIHJldHVybiBwcm9wZXIgbmFtZSBmb3Igd3JhcHBlZCBmdW5jdGlvbnMuCiAqCiAqIGBgYGpzCiAqIFNlbnRyeS5pbml0KHsKICogICBpbnRlZ3JhdGlvbnM6IFsKICogICAgIGZ1bmN0aW9uVG9TdHJpbmdJbnRlZ3JhdGlvbigpLAogKiAgIF0sCiAqIH0pOwogKiBgYGAKICovCmNvbnN0IGZ1bmN0aW9uVG9TdHJpbmdJbnRlZ3JhdGlvbiA9IGRlZmluZUludGVncmF0aW9uKF9mdW5jdGlvblRvU3RyaW5nSW50ZWdyYXRpb24pOwoKLyoqCiAqIFBhdGNoIHRvU3RyaW5nIGNhbGxzIHRvIHJldHVybiBwcm9wZXIgbmFtZSBmb3Igd3JhcHBlZCBmdW5jdGlvbnMuCiAqCiAqIEBkZXByZWNhdGVkIFVzZSBgZnVuY3Rpb25Ub1N0cmluZ0ludGVncmF0aW9uKClgIGluc3RlYWQuCiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KY29udmVydEludGVncmF0aW9uRm5Ub0NsYXNzKAogIElOVEVHUkFUSU9OX05BTUUkOCwKICBmdW5jdGlvblRvU3RyaW5nSW50ZWdyYXRpb24sCikgOwoKZnVuY3Rpb24gX251bGxpc2hDb2FsZXNjZSQyKGxocywgcmhzRm4pIHsgaWYgKGxocyAhPSBudWxsKSB7IHJldHVybiBsaHM7IH0gZWxzZSB7IHJldHVybiByaHNGbigpOyB9IH0vKioKICogVGhpcyBjb2RlIHdhcyBvcmlnaW5hbGx5IGZvcmtlZCBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9Ub29UYWxsTmF0ZS9wcm94eS1hZ2VudHMvdHJlZS9iMTMzMjk1ZmQxNmY2NDc1NTc4YjZiMTViZDliNGUzM2VjYjBkMGI3CiAqIFdpdGggdGhlIGZvbGxvd2luZyBsaWNlbmNlOgogKgogKiAoVGhlIE1JVCBMaWNlbnNlKQogKgogKiBDb3B5cmlnaHQgKGMpIDIwMTMgTmF0aGFuIFJhamxpY2ggPG5hdGhhbkB0b290YWxsbmF0ZS5uZXQ+KgogKgogKiBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcKICogYSBjb3B5IG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlCiAqICdTb2Z0d2FyZScpLCB0byBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcKICogd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLAogKiBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Igc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8KICogcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvCiAqIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoqCiAqCiAqIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlCiAqIGluY2x1ZGVkIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLioKICoKICogVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEICdBUyBJUycsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsCiAqIEVYUFJFU1MgT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRgogKiBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuCiAqIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZCiAqIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsCiAqIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFCiAqIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFIFNPRlRXQVJFLgogKi8KCmNvbnN0IElOVEVSTkFMID0gU3ltYm9sKCdBZ2VudEJhc2VJbnRlcm5hbFN0YXRlJyk7CgpjbGFzcyBBZ2VudCBleHRlbmRzIGh0dHAuQWdlbnQgewoKICAvLyBTZXQgYnkgYGh0dHAuQWdlbnRgIC0gbWlzc2luZyBmcm9tIGBAdHlwZXMvbm9kZWAKCiAgY29uc3RydWN0b3Iob3B0cykgewogICAgc3VwZXIob3B0cyk7CiAgICB0aGlzW0lOVEVSTkFMXSA9IHt9OwogIH0KCiAgLyoqCiAgICogRGV0ZXJtaW5lIHdoZXRoZXIgdGhpcyBpcyBhbiBgaHR0cGAgb3IgYGh0dHBzYCByZXF1ZXN0LgogICAqLwogIGlzU2VjdXJlRW5kcG9pbnQob3B0aW9ucykgewogICAgaWYgKG9wdGlvbnMpIHsKICAgICAgLy8gRmlyc3QgY2hlY2sgdGhlIGBzZWN1cmVFbmRwb2ludGAgcHJvcGVydHkgZXhwbGljaXRseSwgc2luY2UgdGhpcwogICAgICAvLyBtZWFucyB0aGF0IGEgcGFyZW50IGBBZ2VudGAgaXMgInBhc3NpbmcgdGhyb3VnaCIgdG8gdGhpcyBpbnN0YW5jZS4KICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnksIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnNhZmUtbWVtYmVyLWFjY2VzcwogICAgICBpZiAodHlwZW9mIChvcHRpb25zICkuc2VjdXJlRW5kcG9pbnQgPT09ICdib29sZWFuJykgewogICAgICAgIHJldHVybiBvcHRpb25zLnNlY3VyZUVuZHBvaW50OwogICAgICB9CgogICAgICAvLyBJZiBubyBleHBsaWNpdCBgc2VjdXJlYCBlbmRwb2ludCwgY2hlY2sgaWYgYHByb3RvY29sYCBwcm9wZXJ0eSBpcwogICAgICAvLyBzZXQuIFRoaXMgd2lsbCB1c3VhbGx5IGJlIHRoZSBjYXNlIHNpbmNlIHVzaW5nIGEgZnVsbCBzdHJpbmcgVVJMCiAgICAgIC8vIG9yIGBVUkxgIGluc3RhbmNlIHNob3VsZCBiZSB0aGUgbW9zdCBjb21tb24gdXNhZ2UuCiAgICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5wcm90b2NvbCA9PT0gJ3N0cmluZycpIHsKICAgICAgICByZXR1cm4gb3B0aW9ucy5wcm90b2NvbCA9PT0gJ2h0dHBzOic7CiAgICAgIH0KICAgIH0KCiAgICAvLyBGaW5hbGx5LCBpZiBubyBgcHJvdG9jb2xgIHByb3BlcnR5IHdhcyBzZXQsIHRoZW4gZmFsbCBiYWNrIHRvCiAgICAvLyBjaGVja2luZyB0aGUgc3RhY2sgdHJhY2Ugb2YgdGhlIGN1cnJlbnQgY2FsbCBzdGFjaywgYW5kIHRyeSB0bwogICAgLy8gZGV0ZWN0IHRoZSAiaHR0cHMiIG1vZHVsZS4KICAgIGNvbnN0IHsgc3RhY2sgfSA9IG5ldyBFcnJvcigpOwogICAgaWYgKHR5cGVvZiBzdGFjayAhPT0gJ3N0cmluZycpIHJldHVybiBmYWxzZTsKICAgIHJldHVybiBzdGFjay5zcGxpdCgnXG4nKS5zb21lKGwgPT4gbC5pbmRleE9mKCcoaHR0cHMuanM6JykgIT09IC0xIHx8IGwuaW5kZXhPZignbm9kZTpodHRwczonKSAhPT0gLTEpOwogIH0KCiAgY3JlYXRlU29ja2V0KHJlcSwgb3B0aW9ucywgY2IpIHsKICAgIGNvbnN0IGNvbm5lY3RPcHRzID0gewogICAgICAuLi5vcHRpb25zLAogICAgICBzZWN1cmVFbmRwb2ludDogdGhpcy5pc1NlY3VyZUVuZHBvaW50KG9wdGlvbnMpLAogICAgfTsKICAgIFByb21pc2UucmVzb2x2ZSgpCiAgICAgIC50aGVuKCgpID0+IHRoaXMuY29ubmVjdChyZXEsIGNvbm5lY3RPcHRzKSkKICAgICAgLnRoZW4oc29ja2V0ID0+IHsKICAgICAgICBpZiAoc29ja2V0IGluc3RhbmNlb2YgaHR0cC5BZ2VudCkgewogICAgICAgICAgLy8gQHRzLWV4cGVjdC1lcnJvciBgYWRkUmVxdWVzdCgpYCBpc24ndCBkZWZpbmVkIGluIGBAdHlwZXMvbm9kZWAKICAgICAgICAgIHJldHVybiBzb2NrZXQuYWRkUmVxdWVzdChyZXEsIGNvbm5lY3RPcHRzKTsKICAgICAgICB9CiAgICAgICAgdGhpc1tJTlRFUk5BTF0uY3VycmVudFNvY2tldCA9IHNvY2tldDsKICAgICAgICAvLyBAdHMtZXhwZWN0LWVycm9yIGBjcmVhdGVTb2NrZXQoKWAgaXNuJ3QgZGVmaW5lZCBpbiBgQHR5cGVzL25vZGVgCiAgICAgICAgc3VwZXIuY3JlYXRlU29ja2V0KHJlcSwgb3B0aW9ucywgY2IpOwogICAgICB9LCBjYik7CiAgfQoKICBjcmVhdGVDb25uZWN0aW9uKCkgewogICAgY29uc3Qgc29ja2V0ID0gdGhpc1tJTlRFUk5BTF0uY3VycmVudFNvY2tldDsKICAgIHRoaXNbSU5URVJOQUxdLmN1cnJlbnRTb2NrZXQgPSB1bmRlZmluZWQ7CiAgICBpZiAoIXNvY2tldCkgewogICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHNvY2tldCB3YXMgcmV0dXJuZWQgaW4gdGhlIGBjb25uZWN0KClgIGZ1bmN0aW9uJyk7CiAgICB9CiAgICByZXR1cm4gc29ja2V0OwogIH0KCiAgZ2V0IGRlZmF1bHRQb3J0KCkgewogICAgcmV0dXJuIF9udWxsaXNoQ29hbGVzY2UkMih0aGlzW0lOVEVSTkFMXS5kZWZhdWx0UG9ydCwgKCkgPT4gKCAodGhpcy5wcm90b2NvbCA9PT0gJ2h0dHBzOicgPyA0NDMgOiA4MCkpKTsKICB9CgogIHNldCBkZWZhdWx0UG9ydCh2KSB7CiAgICBpZiAodGhpc1tJTlRFUk5BTF0pIHsKICAgICAgdGhpc1tJTlRFUk5BTF0uZGVmYXVsdFBvcnQgPSB2OwogICAgfQogIH0KCiAgZ2V0IHByb3RvY29sKCkgewogICAgcmV0dXJuIF9udWxsaXNoQ29hbGVzY2UkMih0aGlzW0lOVEVSTkFMXS5wcm90b2NvbCwgKCkgPT4gKCAodGhpcy5pc1NlY3VyZUVuZHBvaW50KCkgPyAnaHR0cHM6JyA6ICdodHRwOicpKSk7CiAgfQoKICBzZXQgcHJvdG9jb2wodikgewogICAgaWYgKHRoaXNbSU5URVJOQUxdKSB7CiAgICAgIHRoaXNbSU5URVJOQUxdLnByb3RvY29sID0gdjsKICAgIH0KICB9Cn0KCmZ1bmN0aW9uIGRlYnVnJDEoLi4uYXJncykgewogIGxvZ2dlci5sb2coJ1todHRwcy1wcm94eS1hZ2VudDpwYXJzZS1wcm94eS1yZXNwb25zZV0nLCAuLi5hcmdzKTsKfQoKZnVuY3Rpb24gcGFyc2VQcm94eVJlc3BvbnNlKHNvY2tldCkgewogIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAvLyB3ZSBuZWVkIHRvIGJ1ZmZlciBhbnkgSFRUUCB0cmFmZmljIHRoYXQgaGFwcGVucyB3aXRoIHRoZSBwcm94eSBiZWZvcmUgd2UgZ2V0CiAgICAvLyB0aGUgQ09OTkVDVCByZXNwb25zZSwgc28gdGhhdCBpZiB0aGUgcmVzcG9uc2UgaXMgYW55dGhpbmcgb3RoZXIgdGhhbiBhbiAiMjAwIgogICAgLy8gcmVzcG9uc2UgY29kZSwgdGhlbiB3ZSBjYW4gcmUtcGxheSB0aGUgImRhdGEiIGV2ZW50cyBvbiB0aGUgc29ja2V0IG9uY2UgdGhlCiAgICAvLyBIVFRQIHBhcnNlciBpcyBob29rZWQgdXAuLi4KICAgIGxldCBidWZmZXJzTGVuZ3RoID0gMDsKICAgIGNvbnN0IGJ1ZmZlcnMgPSBbXTsKCiAgICBmdW5jdGlvbiByZWFkKCkgewogICAgICBjb25zdCBiID0gc29ja2V0LnJlYWQoKTsKICAgICAgaWYgKGIpIG9uZGF0YShiKTsKICAgICAgZWxzZSBzb2NrZXQub25jZSgncmVhZGFibGUnLCByZWFkKTsKICAgIH0KCiAgICBmdW5jdGlvbiBjbGVhbnVwKCkgewogICAgICBzb2NrZXQucmVtb3ZlTGlzdGVuZXIoJ2VuZCcsIG9uZW5kKTsKICAgICAgc29ja2V0LnJlbW92ZUxpc3RlbmVyKCdlcnJvcicsIG9uZXJyb3IpOwogICAgICBzb2NrZXQucmVtb3ZlTGlzdGVuZXIoJ3JlYWRhYmxlJywgcmVhZCk7CiAgICB9CgogICAgZnVuY3Rpb24gb25lbmQoKSB7CiAgICAgIGNsZWFudXAoKTsKICAgICAgZGVidWckMSgnb25lbmQnKTsKICAgICAgcmVqZWN0KG5ldyBFcnJvcignUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UnKSk7CiAgICB9CgogICAgZnVuY3Rpb24gb25lcnJvcihlcnIpIHsKICAgICAgY2xlYW51cCgpOwogICAgICBkZWJ1ZyQxKCdvbmVycm9yICVvJywgZXJyKTsKICAgICAgcmVqZWN0KGVycik7CiAgICB9CgogICAgZnVuY3Rpb24gb25kYXRhKGIpIHsKICAgICAgYnVmZmVycy5wdXNoKGIpOwogICAgICBidWZmZXJzTGVuZ3RoICs9IGIubGVuZ3RoOwoKICAgICAgY29uc3QgYnVmZmVyZWQgPSBCdWZmZXIuY29uY2F0KGJ1ZmZlcnMsIGJ1ZmZlcnNMZW5ndGgpOwogICAgICBjb25zdCBlbmRPZkhlYWRlcnMgPSBidWZmZXJlZC5pbmRleE9mKCdcclxuXHJcbicpOwoKICAgICAgaWYgKGVuZE9mSGVhZGVycyA9PT0gLTEpIHsKICAgICAgICAvLyBrZWVwIGJ1ZmZlcmluZwogICAgICAgIGRlYnVnJDEoJ2hhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uJyk7CiAgICAgICAgcmVhZCgpOwogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgY29uc3QgaGVhZGVyUGFydHMgPSBidWZmZXJlZC5zbGljZSgwLCBlbmRPZkhlYWRlcnMpLnRvU3RyaW5nKCdhc2NpaScpLnNwbGl0KCdcclxuJyk7CiAgICAgIGNvbnN0IGZpcnN0TGluZSA9IGhlYWRlclBhcnRzLnNoaWZ0KCk7CiAgICAgIGlmICghZmlyc3RMaW5lKSB7CiAgICAgICAgc29ja2V0LmRlc3Ryb3koKTsKICAgICAgICByZXR1cm4gcmVqZWN0KG5ldyBFcnJvcignTm8gaGVhZGVyIHJlY2VpdmVkIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZScpKTsKICAgICAgfQogICAgICBjb25zdCBmaXJzdExpbmVQYXJ0cyA9IGZpcnN0TGluZS5zcGxpdCgnICcpOwogICAgICBjb25zdCBzdGF0dXNDb2RlID0gK2ZpcnN0TGluZVBhcnRzWzFdOwogICAgICBjb25zdCBzdGF0dXNUZXh0ID0gZmlyc3RMaW5lUGFydHMuc2xpY2UoMikuam9pbignICcpOwogICAgICBjb25zdCBoZWFkZXJzID0ge307CiAgICAgIGZvciAoY29uc3QgaGVhZGVyIG9mIGhlYWRlclBhcnRzKSB7CiAgICAgICAgaWYgKCFoZWFkZXIpIGNvbnRpbnVlOwogICAgICAgIGNvbnN0IGZpcnN0Q29sb24gPSBoZWFkZXIuaW5kZXhPZignOicpOwogICAgICAgIGlmIChmaXJzdENvbG9uID09PSAtMSkgewogICAgICAgICAgc29ja2V0LmRlc3Ryb3koKTsKICAgICAgICAgIHJldHVybiByZWplY3QobmV3IEVycm9yKGBJbnZhbGlkIGhlYWRlciBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2U6ICIke2hlYWRlcn0iYCkpOwogICAgICAgIH0KICAgICAgICBjb25zdCBrZXkgPSBoZWFkZXIuc2xpY2UoMCwgZmlyc3RDb2xvbikudG9Mb3dlckNhc2UoKTsKICAgICAgICBjb25zdCB2YWx1ZSA9IGhlYWRlci5zbGljZShmaXJzdENvbG9uICsgMSkudHJpbVN0YXJ0KCk7CiAgICAgICAgY29uc3QgY3VycmVudCA9IGhlYWRlcnNba2V5XTsKICAgICAgICBpZiAodHlwZW9mIGN1cnJlbnQgPT09ICdzdHJpbmcnKSB7CiAgICAgICAgICBoZWFkZXJzW2tleV0gPSBbY3VycmVudCwgdmFsdWVdOwogICAgICAgIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheShjdXJyZW50KSkgewogICAgICAgICAgY3VycmVudC5wdXNoKHZhbHVlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaGVhZGVyc1trZXldID0gdmFsdWU7CiAgICAgICAgfQogICAgICB9CiAgICAgIGRlYnVnJDEoJ2dvdCBwcm94eSBzZXJ2ZXIgcmVzcG9uc2U6ICVvICVvJywgZmlyc3RMaW5lLCBoZWFkZXJzKTsKICAgICAgY2xlYW51cCgpOwogICAgICByZXNvbHZlKHsKICAgICAgICBjb25uZWN0OiB7CiAgICAgICAgICBzdGF0dXNDb2RlLAogICAgICAgICAgc3RhdHVzVGV4dCwKICAgICAgICAgIGhlYWRlcnMsCiAgICAgICAgfSwKICAgICAgICBidWZmZXJlZCwKICAgICAgfSk7CiAgICB9CgogICAgc29ja2V0Lm9uKCdlcnJvcicsIG9uZXJyb3IpOwogICAgc29ja2V0Lm9uKCdlbmQnLCBvbmVuZCk7CgogICAgcmVhZCgpOwogIH0pOwp9CgpmdW5jdGlvbiBfbnVsbGlzaENvYWxlc2NlJDEobGhzLCByaHNGbikgeyBpZiAobGhzICE9IG51bGwpIHsgcmV0dXJuIGxoczsgfSBlbHNlIHsgcmV0dXJuIHJoc0ZuKCk7IH0gfSBmdW5jdGlvbiBfb3B0aW9uYWxDaGFpbiQxKG9wcykgeyBsZXQgbGFzdEFjY2Vzc0xIUyA9IHVuZGVmaW5lZDsgbGV0IHZhbHVlID0gb3BzWzBdOyBsZXQgaSA9IDE7IHdoaWxlIChpIDwgb3BzLmxlbmd0aCkgeyBjb25zdCBvcCA9IG9wc1tpXTsgY29uc3QgZm4gPSBvcHNbaSArIDFdOyBpICs9IDI7IGlmICgob3AgPT09ICdvcHRpb25hbEFjY2VzcycgfHwgb3AgPT09ICdvcHRpb25hbENhbGwnKSAmJiB2YWx1ZSA9PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gaWYgKG9wID09PSAnYWNjZXNzJyB8fCBvcCA9PT0gJ29wdGlvbmFsQWNjZXNzJykgeyBsYXN0QWNjZXNzTEhTID0gdmFsdWU7IHZhbHVlID0gZm4odmFsdWUpOyB9IGVsc2UgaWYgKG9wID09PSAnY2FsbCcgfHwgb3AgPT09ICdvcHRpb25hbENhbGwnKSB7IHZhbHVlID0gZm4oKC4uLmFyZ3MpID0+IHZhbHVlLmNhbGwobGFzdEFjY2Vzc0xIUywgLi4uYXJncykpOyBsYXN0QWNjZXNzTEhTID0gdW5kZWZpbmVkOyB9IH0gcmV0dXJuIHZhbHVlOyB9CgpmdW5jdGlvbiBkZWJ1ZyguLi5hcmdzKSB7CiAgbG9nZ2VyLmxvZygnW2h0dHBzLXByb3h5LWFnZW50XScsIC4uLmFyZ3MpOwp9CgovKioKICogVGhlIGBIdHRwc1Byb3h5QWdlbnRgIGltcGxlbWVudHMgYW4gSFRUUCBBZ2VudCBzdWJjbGFzcyB0aGF0IGNvbm5lY3RzIHRvCiAqIHRoZSBzcGVjaWZpZWQgIkhUVFAocykgcHJveHkgc2VydmVyIiBpbiBvcmRlciB0byBwcm94eSBIVFRQUyByZXF1ZXN0cy4KICoKICogT3V0Z29pbmcgSFRUUCByZXF1ZXN0cyBhcmUgZmlyc3QgdHVubmVsZWQgdGhyb3VnaCB0aGUgcHJveHkgc2VydmVyIHVzaW5nIHRoZQogKiBgQ09OTkVDVGAgSFRUUCByZXF1ZXN0IG1ldGhvZCB0byBlc3RhYmxpc2ggYSBjb25uZWN0aW9uIHRvIHRoZSBwcm94eSBzZXJ2ZXIsCiAqIGFuZCB0aGVuIHRoZSBwcm94eSBzZXJ2ZXIgY29ubmVjdHMgdG8gdGhlIGRlc3RpbmF0aW9uIHRhcmdldCBhbmQgaXNzdWVzIHRoZQogKiBIVFRQIHJlcXVlc3QgZnJvbSB0aGUgcHJveHkgc2VydmVyLgogKgogKiBgaHR0cHM6YCByZXF1ZXN0cyBoYXZlIHRoZWlyIHNvY2tldCBjb25uZWN0aW9uIHVwZ3JhZGVkIHRvIFRMUyBvbmNlCiAqIHRoZSBjb25uZWN0aW9uIHRvIHRoZSBwcm94eSBzZXJ2ZXIgaGFzIGJlZW4gZXN0YWJsaXNoZWQuCiAqLwpjbGFzcyBIdHRwc1Byb3h5QWdlbnQgZXh0ZW5kcyBBZ2VudCB7CiAgc3RhdGljIF9faW5pdFN0YXRpYygpIHt0aGlzLnByb3RvY29scyA9IFsnaHR0cCcsICdodHRwcyddOyB9CgogIGNvbnN0cnVjdG9yKHByb3h5LCBvcHRzKSB7CiAgICBzdXBlcihvcHRzKTsKICAgIHRoaXMub3B0aW9ucyA9IHt9OwogICAgdGhpcy5wcm94eSA9IHR5cGVvZiBwcm94eSA9PT0gJ3N0cmluZycgPyBuZXcgVVJMKHByb3h5KSA6IHByb3h5OwogICAgdGhpcy5wcm94eUhlYWRlcnMgPSBfbnVsbGlzaENvYWxlc2NlJDEoX29wdGlvbmFsQ2hhaW4kMShbb3B0cywgJ29wdGlvbmFsQWNjZXNzJywgXzIgPT4gXzIuaGVhZGVyc10pLCAoKSA9PiAoIHt9KSk7CiAgICBkZWJ1ZygnQ3JlYXRpbmcgbmV3IEh0dHBzUHJveHlBZ2VudCBpbnN0YW5jZTogJW8nLCB0aGlzLnByb3h5LmhyZWYpOwoKICAgIC8vIFRyaW0gb2ZmIHRoZSBicmFja2V0cyBmcm9tIElQdjYgYWRkcmVzc2VzCiAgICBjb25zdCBob3N0ID0gKHRoaXMucHJveHkuaG9zdG5hbWUgfHwgdGhpcy5wcm94eS5ob3N0KS5yZXBsYWNlKC9eXFt8XF0kL2csICcnKTsKICAgIGNvbnN0IHBvcnQgPSB0aGlzLnByb3h5LnBvcnQgPyBwYXJzZUludCh0aGlzLnByb3h5LnBvcnQsIDEwKSA6IHRoaXMucHJveHkucHJvdG9jb2wgPT09ICdodHRwczonID8gNDQzIDogODA7CiAgICB0aGlzLmNvbm5lY3RPcHRzID0gewogICAgICAvLyBBdHRlbXB0IHRvIG5lZ290aWF0ZSBodHRwLzEuMSBmb3IgcHJveHkgc2VydmVycyB0aGF0IHN1cHBvcnQgaHR0cC8yCiAgICAgIEFMUE5Qcm90b2NvbHM6IFsnaHR0cC8xLjEnXSwKICAgICAgLi4uKG9wdHMgPyBvbWl0KG9wdHMsICdoZWFkZXJzJykgOiBudWxsKSwKICAgICAgaG9zdCwKICAgICAgcG9ydCwKICAgIH07CiAgfQoKICAvKioKICAgKiBDYWxsZWQgd2hlbiB0aGUgbm9kZS1jb3JlIEhUVFAgY2xpZW50IGxpYnJhcnkgaXMgY3JlYXRpbmcgYQogICAqIG5ldyBIVFRQIHJlcXVlc3QuCiAgICovCiAgYXN5bmMgY29ubmVjdChyZXEsIG9wdHMpIHsKICAgIGNvbnN0IHsgcHJveHkgfSA9IHRoaXM7CgogICAgaWYgKCFvcHRzLmhvc3QpIHsKICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignTm8gImhvc3QiIHByb3ZpZGVkJyk7CiAgICB9CgogICAgLy8gQ3JlYXRlIGEgc29ja2V0IGNvbm5lY3Rpb24gdG8gdGhlIHByb3h5IHNlcnZlci4KICAgIGxldCBzb2NrZXQ7CiAgICBpZiAocHJveHkucHJvdG9jb2wgPT09ICdodHRwczonKSB7CiAgICAgIGRlYnVnKCdDcmVhdGluZyBgdGxzLlNvY2tldGA6ICVvJywgdGhpcy5jb25uZWN0T3B0cyk7CiAgICAgIGNvbnN0IHNlcnZlcm5hbWUgPSB0aGlzLmNvbm5lY3RPcHRzLnNlcnZlcm5hbWUgfHwgdGhpcy5jb25uZWN0T3B0cy5ob3N0OwogICAgICBzb2NrZXQgPSB0bHMuY29ubmVjdCh7CiAgICAgICAgLi4udGhpcy5jb25uZWN0T3B0cywKICAgICAgICBzZXJ2ZXJuYW1lOiBzZXJ2ZXJuYW1lICYmIG5ldC5pc0lQKHNlcnZlcm5hbWUpID8gdW5kZWZpbmVkIDogc2VydmVybmFtZSwKICAgICAgfSk7CiAgICB9IGVsc2UgewogICAgICBkZWJ1ZygnQ3JlYXRpbmcgYG5ldC5Tb2NrZXRgOiAlbycsIHRoaXMuY29ubmVjdE9wdHMpOwogICAgICBzb2NrZXQgPSBuZXQuY29ubmVjdCh0aGlzLmNvbm5lY3RPcHRzKTsKICAgIH0KCiAgICBjb25zdCBoZWFkZXJzID0KICAgICAgdHlwZW9mIHRoaXMucHJveHlIZWFkZXJzID09PSAnZnVuY3Rpb24nID8gdGhpcy5wcm94eUhlYWRlcnMoKSA6IHsgLi4udGhpcy5wcm94eUhlYWRlcnMgfTsKICAgIGNvbnN0IGhvc3QgPSBuZXQuaXNJUHY2KG9wdHMuaG9zdCkgPyBgWyR7b3B0cy5ob3N0fV1gIDogb3B0cy5ob3N0OwogICAgbGV0IHBheWxvYWQgPSBgQ09OTkVDVCAke2hvc3R9OiR7b3B0cy5wb3J0fSBIVFRQLzEuMVxyXG5gOwoKICAgIC8vIEluamVjdCB0aGUgYFByb3h5LUF1dGhvcml6YXRpb25gIGhlYWRlciBpZiBuZWNlc3NhcnkuCiAgICBpZiAocHJveHkudXNlcm5hbWUgfHwgcHJveHkucGFzc3dvcmQpIHsKICAgICAgY29uc3QgYXV0aCA9IGAke2RlY29kZVVSSUNvbXBvbmVudChwcm94eS51c2VybmFtZSl9OiR7ZGVjb2RlVVJJQ29tcG9uZW50KHByb3h5LnBhc3N3b3JkKX1gOwogICAgICBoZWFkZXJzWydQcm94eS1BdXRob3JpemF0aW9uJ10gPSBgQmFzaWMgJHtCdWZmZXIuZnJvbShhdXRoKS50b1N0cmluZygnYmFzZTY0Jyl9YDsKICAgIH0KCiAgICBoZWFkZXJzLkhvc3QgPSBgJHtob3N0fToke29wdHMucG9ydH1gOwoKICAgIGlmICghaGVhZGVyc1snUHJveHktQ29ubmVjdGlvbiddKSB7CiAgICAgIGhlYWRlcnNbJ1Byb3h5LUNvbm5lY3Rpb24nXSA9IHRoaXMua2VlcEFsaXZlID8gJ0tlZXAtQWxpdmUnIDogJ2Nsb3NlJzsKICAgIH0KICAgIGZvciAoY29uc3QgbmFtZSBvZiBPYmplY3Qua2V5cyhoZWFkZXJzKSkgewogICAgICBwYXlsb2FkICs9IGAke25hbWV9OiAke2hlYWRlcnNbbmFtZV19XHJcbmA7CiAgICB9CgogICAgY29uc3QgcHJveHlSZXNwb25zZVByb21pc2UgPSBwYXJzZVByb3h5UmVzcG9uc2Uoc29ja2V0KTsKCiAgICBzb2NrZXQud3JpdGUoYCR7cGF5bG9hZH1cclxuYCk7CgogICAgY29uc3QgeyBjb25uZWN0LCBidWZmZXJlZCB9ID0gYXdhaXQgcHJveHlSZXNwb25zZVByb21pc2U7CiAgICByZXEuZW1pdCgncHJveHlDb25uZWN0JywgY29ubmVjdCk7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2Jhbi10cy1jb21tZW50CiAgICAvLyBAdHMtaWdub3JlIE5vdCBFdmVudEVtaXR0ZXIgaW4gTm9kZSB0eXBlcwogICAgdGhpcy5lbWl0KCdwcm94eUNvbm5lY3QnLCBjb25uZWN0LCByZXEpOwoKICAgIGlmIChjb25uZWN0LnN0YXR1c0NvZGUgPT09IDIwMCkgewogICAgICByZXEub25jZSgnc29ja2V0JywgcmVzdW1lKTsKCiAgICAgIGlmIChvcHRzLnNlY3VyZUVuZHBvaW50KSB7CiAgICAgICAgLy8gVGhlIHByb3h5IGlzIGNvbm5lY3RpbmcgdG8gYSBUTFMgc2VydmVyLCBzbyB1cGdyYWRlCiAgICAgICAgLy8gdGhpcyBzb2NrZXQgY29ubmVjdGlvbiB0byBhIFRMUyBjb25uZWN0aW9uLgogICAgICAgIGRlYnVnKCdVcGdyYWRpbmcgc29ja2V0IGNvbm5lY3Rpb24gdG8gVExTJyk7CiAgICAgICAgY29uc3Qgc2VydmVybmFtZSA9IG9wdHMuc2VydmVybmFtZSB8fCBvcHRzLmhvc3Q7CiAgICAgICAgcmV0dXJuIHRscy5jb25uZWN0KHsKICAgICAgICAgIC4uLm9taXQob3B0cywgJ2hvc3QnLCAncGF0aCcsICdwb3J0JyksCiAgICAgICAgICBzb2NrZXQsCiAgICAgICAgICBzZXJ2ZXJuYW1lOiBuZXQuaXNJUChzZXJ2ZXJuYW1lKSA/IHVuZGVmaW5lZCA6IHNlcnZlcm5hbWUsCiAgICAgICAgfSk7CiAgICAgIH0KCiAgICAgIHJldHVybiBzb2NrZXQ7CiAgICB9CgogICAgLy8gU29tZSBvdGhlciBzdGF0dXMgY29kZSB0aGF0J3Mgbm90IDIwMC4uLiBuZWVkIHRvIHJlLXBsYXkgdGhlIEhUVFAKICAgIC8vIGhlYWRlciAiZGF0YSIgZXZlbnRzIG9udG8gdGhlIHNvY2tldCBvbmNlIHRoZSBIVFRQIG1hY2hpbmVyeSBpcwogICAgLy8gYXR0YWNoZWQgc28gdGhhdCB0aGUgbm9kZSBjb3JlIGBodHRwYCBjYW4gcGFyc2UgYW5kIGhhbmRsZSB0aGUKICAgIC8vIGVycm9yIHN0YXR1cyBjb2RlLgoKICAgIC8vIENsb3NlIHRoZSBvcmlnaW5hbCBzb2NrZXQsIGFuZCBhIG5ldyAiZmFrZSIgc29ja2V0IGlzIHJldHVybmVkCiAgICAvLyBpbnN0ZWFkLCBzbyB0aGF0IHRoZSBwcm94eSBkb2Vzbid0IGdldCB0aGUgSFRUUCByZXF1ZXN0CiAgICAvLyB3cml0dGVuIHRvIGl0ICh3aGljaCBtYXkgY29udGFpbiBgQXV0aG9yaXphdGlvbmAgaGVhZGVycyBvciBvdGhlcgogICAgLy8gc2Vuc2l0aXZlIGRhdGEpLgogICAgLy8KICAgIC8vIFNlZTogaHR0cHM6Ly9oYWNrZXJvbmUuY29tL3JlcG9ydHMvNTQxNTAyCiAgICBzb2NrZXQuZGVzdHJveSgpOwoKICAgIGNvbnN0IGZha2VTb2NrZXQgPSBuZXcgbmV0LlNvY2tldCh7IHdyaXRhYmxlOiBmYWxzZSB9KTsKICAgIGZha2VTb2NrZXQucmVhZGFibGUgPSB0cnVlOwoKICAgIC8vIE5lZWQgdG8gd2FpdCBmb3IgdGhlICJzb2NrZXQiIGV2ZW50IHRvIHJlLXBsYXkgdGhlICJkYXRhIiBldmVudHMuCiAgICByZXEub25jZSgnc29ja2V0JywgKHMpID0+IHsKICAgICAgZGVidWcoJ1JlcGxheWluZyBwcm94eSBidWZmZXIgZm9yIGZhaWxlZCByZXF1ZXN0Jyk7CiAgICAgIC8vIFJlcGxheSB0aGUgImJ1ZmZlcmVkIiBCdWZmZXIgb250byB0aGUgZmFrZSBgc29ja2V0YCwgc2luY2UgYXQKICAgICAgLy8gdGhpcyBwb2ludCB0aGUgSFRUUCBtb2R1bGUgbWFjaGluZXJ5IGhhcyBiZWVuIGhvb2tlZCB1cCBmb3IKICAgICAgLy8gdGhlIHVzZXIuCiAgICAgIHMucHVzaChidWZmZXJlZCk7CiAgICAgIHMucHVzaChudWxsKTsKICAgIH0pOwoKICAgIHJldHVybiBmYWtlU29ja2V0OwogIH0KfSBIdHRwc1Byb3h5QWdlbnQuX19pbml0U3RhdGljKCk7CgpmdW5jdGlvbiByZXN1bWUoc29ja2V0KSB7CiAgc29ja2V0LnJlc3VtZSgpOwp9CgpmdW5jdGlvbiBvbWl0KAogIG9iaiwKICAuLi5rZXlzCikKCiB7CiAgY29uc3QgcmV0ID0ge30KCjsKICBsZXQga2V5OwogIGZvciAoa2V5IGluIG9iaikgewogICAgaWYgKCFrZXlzLmluY2x1ZGVzKGtleSkpIHsKICAgICAgcmV0W2tleV0gPSBvYmpba2V5XTsKICAgIH0KICB9CiAgcmV0dXJuIHJldDsKfQoKZnVuY3Rpb24gX251bGxpc2hDb2FsZXNjZShsaHMsIHJoc0ZuKSB7IGlmIChsaHMgIT0gbnVsbCkgeyByZXR1cm4gbGhzOyB9IGVsc2UgeyByZXR1cm4gcmhzRm4oKTsgfSB9Ci8vIEVzdGltYXRlZCBtYXhpbXVtIHNpemUgZm9yIHJlYXNvbmFibGUgc3RhbmRhbG9uZSBldmVudApjb25zdCBHWklQX1RIUkVTSE9MRCA9IDEwMjQgKiAzMjsKCi8qKgogKiBHZXRzIGEgc3RyZWFtIGZyb20gYSBVaW50OEFycmF5IG9yIHN0cmluZwogKiBSZWFkYWJsZS5mcm9tIGlzIGlkZWFsIGJ1dCB3YXMgYWRkZWQgaW4gbm9kZS5qcyB2MTIuMy4wIGFuZCB2MTAuMTcuMAogKi8KZnVuY3Rpb24gc3RyZWFtRnJvbUJvZHkoYm9keSkgewogIHJldHVybiBuZXcgUmVhZGFibGUoewogICAgcmVhZCgpIHsKICAgICAgdGhpcy5wdXNoKGJvZHkpOwogICAgICB0aGlzLnB1c2gobnVsbCk7CiAgICB9LAogIH0pOwp9CgovKioKICogQ3JlYXRlcyBhIFRyYW5zcG9ydCB0aGF0IHVzZXMgbmF0aXZlIHRoZSBuYXRpdmUgJ2h0dHAnIGFuZCAnaHR0cHMnIG1vZHVsZXMgdG8gc2VuZCBldmVudHMgdG8gU2VudHJ5LgogKi8KZnVuY3Rpb24gbWFrZU5vZGVUcmFuc3BvcnQob3B0aW9ucykgewogIGxldCB1cmxTZWdtZW50czsKCiAgdHJ5IHsKICAgIHVybFNlZ21lbnRzID0gbmV3IFVSTChvcHRpb25zLnVybCk7CiAgfSBjYXRjaCAoZSkgewogICAgY29uc29sZVNhbmRib3goKCkgPT4gewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZQogICAgICBjb25zb2xlLndhcm4oCiAgICAgICAgJ1tAc2VudHJ5L25vZGVdOiBJbnZhbGlkIGRzbiBvciB0dW5uZWwgb3B0aW9uLCB3aWxsIG5vdCBzZW5kIGFueSBldmVudHMuIFRoZSB0dW5uZWwgb3B0aW9uIG11c3QgYmUgYSBmdWxsIFVSTCB3aGVuIHVzZWQuJywKICAgICAgKTsKICAgIH0pOwogICAgcmV0dXJuIGNyZWF0ZVRyYW5zcG9ydChvcHRpb25zLCAoKSA9PiBQcm9taXNlLnJlc29sdmUoe30pKTsKICB9CgogIGNvbnN0IGlzSHR0cHMgPSB1cmxTZWdtZW50cy5wcm90b2NvbCA9PT0gJ2h0dHBzOic7CgogIC8vIFByb3h5IHByaW9yaXRpemF0aW9uOiBodHRwID0+IGBvcHRpb25zLnByb3h5YCB8IGBwcm9jZXNzLmVudi5odHRwX3Byb3h5YAogIC8vIFByb3h5IHByaW9yaXRpemF0aW9uOiBodHRwcyA9PiBgb3B0aW9ucy5wcm94eWAgfCBgcHJvY2Vzcy5lbnYuaHR0cHNfcHJveHlgIHwgYHByb2Nlc3MuZW52Lmh0dHBfcHJveHlgCiAgY29uc3QgcHJveHkgPSBhcHBseU5vUHJveHlPcHRpb24oCiAgICB1cmxTZWdtZW50cywKICAgIG9wdGlvbnMucHJveHkgfHwgKGlzSHR0cHMgPyBwcm9jZXNzLmVudi5odHRwc19wcm94eSA6IHVuZGVmaW5lZCkgfHwgcHJvY2Vzcy5lbnYuaHR0cF9wcm94eSwKICApOwoKICBjb25zdCBuYXRpdmVIdHRwTW9kdWxlID0gaXNIdHRwcyA/IGh0dHBzIDogaHR0cCQxOwogIGNvbnN0IGtlZXBBbGl2ZSA9IG9wdGlvbnMua2VlcEFsaXZlID09PSB1bmRlZmluZWQgPyBmYWxzZSA6IG9wdGlvbnMua2VlcEFsaXZlOwoKICAvLyBUT0RPKHY3KTogRXZhbHVhdGUgaWYgd2UgY2FuIHNldCBrZWVwQWxpdmUgdG8gdHJ1ZS4gVGhpcyB3b3VsZCBpbnZvbHZlIHRlc3RpbmcgZm9yIG1lbW9yeSBsZWFrcyBpbiBvbGRlciBub2RlCiAgLy8gdmVyc2lvbnMoPj0gOCkgYXMgdGhleSBoYWQgbWVtb3J5IGxlYWtzIHdoZW4gdXNpbmcgaXQ6ICMyNTU1CiAgY29uc3QgYWdlbnQgPSBwcm94eQogICAgPyAobmV3IEh0dHBzUHJveHlBZ2VudChwcm94eSkgKQogICAgOiBuZXcgbmF0aXZlSHR0cE1vZHVsZS5BZ2VudCh7IGtlZXBBbGl2ZSwgbWF4U29ja2V0czogMzAsIHRpbWVvdXQ6IDIwMDAgfSk7CgogIGNvbnN0IHJlcXVlc3RFeGVjdXRvciA9IGNyZWF0ZVJlcXVlc3RFeGVjdXRvcihvcHRpb25zLCBfbnVsbGlzaENvYWxlc2NlKG9wdGlvbnMuaHR0cE1vZHVsZSwgKCkgPT4gKCBuYXRpdmVIdHRwTW9kdWxlKSksIGFnZW50KTsKICByZXR1cm4gY3JlYXRlVHJhbnNwb3J0KG9wdGlvbnMsIHJlcXVlc3RFeGVjdXRvcik7Cn0KCi8qKgogKiBIb25vcnMgdGhlIGBub19wcm94eWAgZW52IHZhcmlhYmxlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdG8gYWxsb3cgZm9yIGhvc3RzIGV4Y2x1c2lvbi4KICoKICogQHBhcmFtIHRyYW5zcG9ydFVybCBUaGUgVVJMIHRoZSB0cmFuc3BvcnQgaW50ZW5kcyB0byBzZW5kIGV2ZW50cyB0by4KICogQHBhcmFtIHByb3h5IFRoZSBjbGllbnQgY29uZmlndXJlZCBwcm94eS4KICogQHJldHVybnMgQSBwcm94eSB0aGUgdHJhbnNwb3J0IHNob3VsZCB1c2UuCiAqLwpmdW5jdGlvbiBhcHBseU5vUHJveHlPcHRpb24odHJhbnNwb3J0VXJsU2VnbWVudHMsIHByb3h5KSB7CiAgY29uc3QgeyBub19wcm94eSB9ID0gcHJvY2Vzcy5lbnY7CgogIGNvbnN0IHVybElzRXhlbXB0RnJvbVByb3h5ID0KICAgIG5vX3Byb3h5ICYmCiAgICBub19wcm94eQogICAgICAuc3BsaXQoJywnKQogICAgICAuc29tZSgKICAgICAgICBleGVtcHRpb24gPT4gdHJhbnNwb3J0VXJsU2VnbWVudHMuaG9zdC5lbmRzV2l0aChleGVtcHRpb24pIHx8IHRyYW5zcG9ydFVybFNlZ21lbnRzLmhvc3RuYW1lLmVuZHNXaXRoKGV4ZW1wdGlvbiksCiAgICAgICk7CgogIGlmICh1cmxJc0V4ZW1wdEZyb21Qcm94eSkgewogICAgcmV0dXJuIHVuZGVmaW5lZDsKICB9IGVsc2UgewogICAgcmV0dXJuIHByb3h5OwogIH0KfQoKLyoqCiAqIENyZWF0ZXMgYSBSZXF1ZXN0RXhlY3V0b3IgdG8gYmUgdXNlZCB3aXRoIGBjcmVhdGVUcmFuc3BvcnRgLgogKi8KZnVuY3Rpb24gY3JlYXRlUmVxdWVzdEV4ZWN1dG9yKAogIG9wdGlvbnMsCiAgaHR0cE1vZHVsZSwKICBhZ2VudCwKKSB7CiAgY29uc3QgeyBob3N0bmFtZSwgcGF0aG5hbWUsIHBvcnQsIHByb3RvY29sLCBzZWFyY2ggfSA9IG5ldyBVUkwob3B0aW9ucy51cmwpOwogIHJldHVybiBmdW5jdGlvbiBtYWtlUmVxdWVzdChyZXF1ZXN0KSB7CiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICBsZXQgYm9keSA9IHN0cmVhbUZyb21Cb2R5KHJlcXVlc3QuYm9keSk7CgogICAgICBjb25zdCBoZWFkZXJzID0geyAuLi5vcHRpb25zLmhlYWRlcnMgfTsKCiAgICAgIGlmIChyZXF1ZXN0LmJvZHkubGVuZ3RoID4gR1pJUF9USFJFU0hPTEQpIHsKICAgICAgICBoZWFkZXJzWydjb250ZW50LWVuY29kaW5nJ10gPSAnZ3ppcCc7CiAgICAgICAgYm9keSA9IGJvZHkucGlwZShjcmVhdGVHemlwKCkpOwogICAgICB9CgogICAgICBjb25zdCByZXEgPSBodHRwTW9kdWxlLnJlcXVlc3QoCiAgICAgICAgewogICAgICAgICAgbWV0aG9kOiAnUE9TVCcsCiAgICAgICAgICBhZ2VudCwKICAgICAgICAgIGhlYWRlcnMsCiAgICAgICAgICBob3N0bmFtZSwKICAgICAgICAgIHBhdGg6IGAke3BhdGhuYW1lfSR7c2VhcmNofWAsCiAgICAgICAgICBwb3J0LAogICAgICAgICAgcHJvdG9jb2wsCiAgICAgICAgICBjYTogb3B0aW9ucy5jYUNlcnRzLAogICAgICAgIH0sCiAgICAgICAgcmVzID0+IHsKICAgICAgICAgIHJlcy5vbignZGF0YScsICgpID0+IHsKICAgICAgICAgICAgLy8gRHJhaW4gc29ja2V0CiAgICAgICAgICB9KTsKCiAgICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHsKICAgICAgICAgICAgLy8gRHJhaW4gc29ja2V0CiAgICAgICAgICB9KTsKCiAgICAgICAgICByZXMuc2V0RW5jb2RpbmcoJ3V0ZjgnKTsKCiAgICAgICAgICAvLyAiS2V5LXZhbHVlIHBhaXJzIG9mIGhlYWRlciBuYW1lcyBhbmQgdmFsdWVzLiBIZWFkZXIgbmFtZXMgYXJlIGxvd2VyLWNhc2VkLiIKICAgICAgICAgIC8vIGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvaHR0cC5odG1sI2h0dHBfbWVzc2FnZV9oZWFkZXJzCiAgICAgICAgICBjb25zdCByZXRyeUFmdGVySGVhZGVyID0gX251bGxpc2hDb2FsZXNjZShyZXMuaGVhZGVyc1sncmV0cnktYWZ0ZXInXSwgKCkgPT4gKCBudWxsKSk7CiAgICAgICAgICBjb25zdCByYXRlTGltaXRzSGVhZGVyID0gX251bGxpc2hDb2FsZXNjZShyZXMuaGVhZGVyc1sneC1zZW50cnktcmF0ZS1saW1pdHMnXSwgKCkgPT4gKCBudWxsKSk7CgogICAgICAgICAgcmVzb2x2ZSh7CiAgICAgICAgICAgIHN0YXR1c0NvZGU6IHJlcy5zdGF0dXNDb2RlLAogICAgICAgICAgICBoZWFkZXJzOiB7CiAgICAgICAgICAgICAgJ3JldHJ5LWFmdGVyJzogcmV0cnlBZnRlckhlYWRlciwKICAgICAgICAgICAgICAneC1zZW50cnktcmF0ZS1saW1pdHMnOiBBcnJheS5pc0FycmF5KHJhdGVMaW1pdHNIZWFkZXIpID8gcmF0ZUxpbWl0c0hlYWRlclswXSA6IHJhdGVMaW1pdHNIZWFkZXIsCiAgICAgICAgICAgIH0sCiAgICAgICAgICB9KTsKICAgICAgICB9LAogICAgICApOwoKICAgICAgcmVxLm9uKCdlcnJvcicsIHJlamVjdCk7CiAgICAgIGJvZHkucGlwZShyZXEpOwogICAgfSk7CiAgfTsKfQoKLyoqIG5vcm1hbGl6ZXMgV2luZG93cyBwYXRocyAqLwpmdW5jdGlvbiBub3JtYWxpemVXaW5kb3dzUGF0aChwYXRoKSB7CiAgcmV0dXJuIHBhdGgKICAgIC5yZXBsYWNlKC9eW0EtWl06LywgJycpIC8vIHJlbW92ZSBXaW5kb3dzLXN0eWxlIHByZWZpeAogICAgLnJlcGxhY2UoL1xcL2csICcvJyk7IC8vIHJlcGxhY2UgYWxsIGBcYCBpbnN0YW5jZXMgd2l0aCBgL2AKfQoKLyoqIENyZWF0ZXMgYSBmdW5jdGlvbiB0aGF0IGdldHMgdGhlIG1vZHVsZSBuYW1lIGZyb20gYSBmaWxlbmFtZSAqLwpmdW5jdGlvbiBjcmVhdGVHZXRNb2R1bGVGcm9tRmlsZW5hbWUoCiAgYmFzZVBhdGggPSBwcm9jZXNzLmFyZ3ZbMV0gPyBkaXJuYW1lKHByb2Nlc3MuYXJndlsxXSkgOiBwcm9jZXNzLmN3ZCgpLAogIGlzV2luZG93cyA9IHNlcCA9PT0gJ1xcJywKKSB7CiAgY29uc3Qgbm9ybWFsaXplZEJhc2UgPSBpc1dpbmRvd3MgPyBub3JtYWxpemVXaW5kb3dzUGF0aChiYXNlUGF0aCkgOiBiYXNlUGF0aDsKCiAgcmV0dXJuIChmaWxlbmFtZSkgPT4gewogICAgaWYgKCFmaWxlbmFtZSkgewogICAgICByZXR1cm47CiAgICB9CgogICAgY29uc3Qgbm9ybWFsaXplZEZpbGVuYW1lID0gaXNXaW5kb3dzID8gbm9ybWFsaXplV2luZG93c1BhdGgoZmlsZW5hbWUpIDogZmlsZW5hbWU7CgogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHByZWZlci1jb25zdAogICAgbGV0IHsgZGlyLCBiYXNlOiBmaWxlLCBleHQgfSA9IHBvc2l4LnBhcnNlKG5vcm1hbGl6ZWRGaWxlbmFtZSk7CgogICAgaWYgKGV4dCA9PT0gJy5qcycgfHwgZXh0ID09PSAnLm1qcycgfHwgZXh0ID09PSAnLmNqcycpIHsKICAgICAgZmlsZSA9IGZpbGUuc2xpY2UoMCwgZXh0Lmxlbmd0aCAqIC0xKTsKICAgIH0KCiAgICBpZiAoIWRpcikgewogICAgICAvLyBObyBkaXJuYW1lIHdoYXRzb2V2ZXIKICAgICAgZGlyID0gJy4nOwogICAgfQoKICAgIGNvbnN0IG4gPSBkaXIubGFzdEluZGV4T2YoJy9ub2RlX21vZHVsZXMnKTsKICAgIGlmIChuID4gLTEpIHsKICAgICAgcmV0dXJuIGAke2Rpci5zbGljZShuICsgMTQpLnJlcGxhY2UoL1wvL2csICcuJyl9OiR7ZmlsZX1gOwogICAgfQoKICAgIC8vIExldCdzIHNlZSBpZiBpdCdzIGEgcGFydCBvZiB0aGUgbWFpbiBtb2R1bGUKICAgIC8vIFRvIGJlIGEgcGFydCBvZiBtYWluIG1vZHVsZSwgaXQgaGFzIHRvIHNoYXJlIHRoZSBzYW1lIGJhc2UKICAgIGlmIChkaXIuc3RhcnRzV2l0aChub3JtYWxpemVkQmFzZSkpIHsKICAgICAgbGV0IG1vZHVsZU5hbWUgPSBkaXIuc2xpY2Uobm9ybWFsaXplZEJhc2UubGVuZ3RoICsgMSkucmVwbGFjZSgvXC8vZywgJy4nKTsKCiAgICAgIGlmIChtb2R1bGVOYW1lKSB7CiAgICAgICAgbW9kdWxlTmFtZSArPSAnOic7CiAgICAgIH0KICAgICAgbW9kdWxlTmFtZSArPSBmaWxlOwoKICAgICAgcmV0dXJuIG1vZHVsZU5hbWU7CiAgICB9CgogICAgcmV0dXJuIGZpbGU7CiAgfTsKfQoKZnVuY3Rpb24gX29wdGlvbmFsQ2hhaW4ob3BzKSB7IGxldCBsYXN0QWNjZXNzTEhTID0gdW5kZWZpbmVkOyBsZXQgdmFsdWUgPSBvcHNbMF07IGxldCBpID0gMTsgd2hpbGUgKGkgPCBvcHMubGVuZ3RoKSB7IGNvbnN0IG9wID0gb3BzW2ldOyBjb25zdCBmbiA9IG9wc1tpICsgMV07IGkgKz0gMjsgaWYgKChvcCA9PT0gJ29wdGlvbmFsQWNjZXNzJyB8fCBvcCA9PT0gJ29wdGlvbmFsQ2FsbCcpICYmIHZhbHVlID09IG51bGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSBpZiAob3AgPT09ICdhY2Nlc3MnIHx8IG9wID09PSAnb3B0aW9uYWxBY2Nlc3MnKSB7IGxhc3RBY2Nlc3NMSFMgPSB2YWx1ZTsgdmFsdWUgPSBmbih2YWx1ZSk7IH0gZWxzZSBpZiAob3AgPT09ICdjYWxsJyB8fCBvcCA9PT0gJ29wdGlvbmFsQ2FsbCcpIHsgdmFsdWUgPSBmbigoLi4uYXJncykgPT4gdmFsdWUuY2FsbChsYXN0QWNjZXNzTEhTLCAuLi5hcmdzKSk7IGxhc3RBY2Nlc3NMSFMgPSB1bmRlZmluZWQ7IH0gfSByZXR1cm4gdmFsdWU7IH0KY29uc3Qgb3B0aW9ucyA9IHdvcmtlckRhdGE7CmxldCBzZXNzaW9uOwpsZXQgaGFzU2VudEFuckV2ZW50ID0gZmFsc2U7CgpmdW5jdGlvbiBsb2cobXNnKSB7CiAgaWYgKG9wdGlvbnMuZGVidWcpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlCiAgICBjb25zb2xlLmxvZyhgW0FOUiBXb3JrZXJdICR7bXNnfWApOwogIH0KfQoKY29uc3QgdXJsID0gZ2V0RW52ZWxvcGVFbmRwb2ludFdpdGhVcmxFbmNvZGVkQXV0aChvcHRpb25zLmRzbik7CmNvbnN0IHRyYW5zcG9ydCA9IG1ha2VOb2RlVHJhbnNwb3J0KHsKICB1cmwsCiAgcmVjb3JkRHJvcHBlZEV2ZW50OiAoKSA9PiB7CiAgICAvLwogIH0sCn0pOwoKYXN5bmMgZnVuY3Rpb24gc2VuZEFibm9ybWFsU2Vzc2lvbigpIHsKICAvLyBvZiB3ZSBoYXZlIGFuIGV4aXN0aW5nIHNlc3Npb24gcGFzc2VkIGZyb20gdGhlIG1haW4gdGhyZWFkLCBzZW5kIGl0IGFzIGFibm9ybWFsCiAgaWYgKHNlc3Npb24pIHsKICAgIGxvZygnU2VuZGluZyBhYm5vcm1hbCBzZXNzaW9uJyk7CiAgICB1cGRhdGVTZXNzaW9uKHNlc3Npb24sIHsgc3RhdHVzOiAnYWJub3JtYWwnLCBhYm5vcm1hbF9tZWNoYW5pc206ICdhbnJfZm9yZWdyb3VuZCcgfSk7CgogICAgY29uc3QgZW52ZWxvcGUgPSBjcmVhdGVTZXNzaW9uRW52ZWxvcGUoc2Vzc2lvbiwgb3B0aW9ucy5kc24sIG9wdGlvbnMuc2RrTWV0YWRhdGEpOwogICAgLy8gTG9nIHRoZSBlbnZlbG9wZSBzbyB0byBhaWQgaW4gdGVzdGluZwogICAgbG9nKEpTT04uc3RyaW5naWZ5KGVudmVsb3BlKSk7CgogICAgYXdhaXQgdHJhbnNwb3J0LnNlbmQoZW52ZWxvcGUpOwoKICAgIHRyeSB7CiAgICAgIC8vIE5vdGlmeSB0aGUgbWFpbiBwcm9jZXNzIHRoYXQgdGhlIHNlc3Npb24gaGFzIGVuZGVkIHNvIHRoZSBzZXNzaW9uIGNhbiBiZSBjbGVhcmVkIGZyb20gdGhlIHNjb3BlCiAgICAgIF9vcHRpb25hbENoYWluKFtwYXJlbnRQb3J0LCAnb3B0aW9uYWxBY2Nlc3MnLCBfMiA9PiBfMi5wb3N0TWVzc2FnZSwgJ2NhbGwnLCBfMyA9PiBfMygnc2Vzc2lvbi1lbmRlZCcpXSk7CiAgICB9IGNhdGNoIChfKSB7CiAgICAgIC8vIGlnbm9yZQogICAgfQogIH0KfQoKbG9nKCdTdGFydGVkJyk7CgpmdW5jdGlvbiBwcmVwYXJlU3RhY2tGcmFtZXMoc3RhY2tGcmFtZXMpIHsKICBpZiAoIXN0YWNrRnJhbWVzKSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgLy8gU3RyaXAgU2VudHJ5IGZyYW1lcyBhbmQgcmV2ZXJzZSB0aGUgc3RhY2sgZnJhbWVzIHNvIHRoZXkgYXJlIGluIHRoZSBjb3JyZWN0IG9yZGVyCiAgY29uc3Qgc3RyaXBwZWRGcmFtZXMgPSBzdHJpcFNlbnRyeUZyYW1lc0FuZFJldmVyc2Uoc3RhY2tGcmFtZXMpOwoKICAvLyBJZiB3ZSBoYXZlIGFuIGFwcCByb290IHBhdGgsIHJld3JpdGUgdGhlIGZpbGVuYW1lcyB0byBiZSByZWxhdGl2ZSB0byB0aGUgYXBwIHJvb3QKICBpZiAob3B0aW9ucy5hcHBSb290UGF0aCkgewogICAgZm9yIChjb25zdCBmcmFtZSBvZiBzdHJpcHBlZEZyYW1lcykgewogICAgICBpZiAoIWZyYW1lLmZpbGVuYW1lKSB7CiAgICAgICAgY29udGludWU7CiAgICAgIH0KCiAgICAgIGZyYW1lLmZpbGVuYW1lID0gbm9ybWFsaXplVXJsVG9CYXNlKGZyYW1lLmZpbGVuYW1lLCBvcHRpb25zLmFwcFJvb3RQYXRoKTsKICAgIH0KICB9CgogIHJldHVybiBzdHJpcHBlZEZyYW1lczsKfQoKYXN5bmMgZnVuY3Rpb24gc2VuZEFuckV2ZW50KGZyYW1lcywgdHJhY2VDb250ZXh0KSB7CiAgaWYgKGhhc1NlbnRBbnJFdmVudCkgewogICAgcmV0dXJuOwogIH0KCiAgaGFzU2VudEFuckV2ZW50ID0gdHJ1ZTsKCiAgYXdhaXQgc2VuZEFibm9ybWFsU2Vzc2lvbigpOwoKICBsb2coJ1NlbmRpbmcgZXZlbnQnKTsKCiAgY29uc3QgZXZlbnQgPSB7CiAgICBldmVudF9pZDogdXVpZDQoKSwKICAgIGNvbnRleHRzOiB7IC4uLm9wdGlvbnMuY29udGV4dHMsIHRyYWNlOiB0cmFjZUNvbnRleHQgfSwKICAgIHJlbGVhc2U6IG9wdGlvbnMucmVsZWFzZSwKICAgIGVudmlyb25tZW50OiBvcHRpb25zLmVudmlyb25tZW50LAogICAgZGlzdDogb3B0aW9ucy5kaXN0LAogICAgcGxhdGZvcm06ICdub2RlJywKICAgIGxldmVsOiAnZXJyb3InLAogICAgZXhjZXB0aW9uOiB7CiAgICAgIHZhbHVlczogWwogICAgICAgIHsKICAgICAgICAgIHR5cGU6ICdBcHBsaWNhdGlvbk5vdFJlc3BvbmRpbmcnLAogICAgICAgICAgdmFsdWU6IGBBcHBsaWNhdGlvbiBOb3QgUmVzcG9uZGluZyBmb3IgYXQgbGVhc3QgJHtvcHRpb25zLmFuclRocmVzaG9sZH0gbXNgLAogICAgICAgICAgc3RhY2t0cmFjZTogeyBmcmFtZXM6IHByZXBhcmVTdGFja0ZyYW1lcyhmcmFtZXMpIH0sCiAgICAgICAgICAvLyBUaGlzIGVuc3VyZXMgdGhlIFVJIGRvZXNuJ3Qgc2F5ICdDcmFzaGVkIGluJyBmb3IgdGhlIHN0YWNrIHRyYWNlCiAgICAgICAgICBtZWNoYW5pc206IHsgdHlwZTogJ0FOUicgfSwKICAgICAgICB9LAogICAgICBdLAogICAgfSwKICAgIHRhZ3M6IG9wdGlvbnMuc3RhdGljVGFncywKICB9OwoKICBjb25zdCBlbnZlbG9wZSA9IGNyZWF0ZUV2ZW50RW52ZWxvcGUoZXZlbnQsIG9wdGlvbnMuZHNuLCBvcHRpb25zLnNka01ldGFkYXRhKTsKICAvLyBMb2cgdGhlIGVudmVsb3BlIHNvIHRvIGFpZCBpbiB0ZXN0aW5nCiAgbG9nKEpTT04uc3RyaW5naWZ5KGVudmVsb3BlKSk7CgogIGF3YWl0IHRyYW5zcG9ydC5zZW5kKGVudmVsb3BlKTsKICBhd2FpdCB0cmFuc3BvcnQuZmx1c2goMjAwMCk7CgogIC8vIERlbGF5IGZvciA1IHNlY29uZHMgc28gdGhhdCBzdGRpbyBjYW4gZmx1c2ggaW4gdGhlIG1haW4gZXZlbnQgbG9vcCBldmVyIHJlc3RhcnRzLgogIC8vIFRoaXMgaXMgbWFpbmx5IGZvciB0aGUgYmVuZWZpdCBvZiBsb2dnaW5nL2RlYnVnZ2luZyBpc3N1ZXMuCiAgc2V0VGltZW91dCgoKSA9PiB7CiAgICBwcm9jZXNzLmV4aXQoMCk7CiAgfSwgNTAwMCk7Cn0KCmxldCBkZWJ1Z2dlclBhdXNlOwoKaWYgKG9wdGlvbnMuY2FwdHVyZVN0YWNrVHJhY2UpIHsKICBsb2coJ0Nvbm5lY3RpbmcgdG8gZGVidWdnZXInKTsKCiAgY29uc3Qgc2Vzc2lvbiA9IG5ldyBTZXNzaW9uKCk7CiAgc2Vzc2lvbi5jb25uZWN0VG9NYWluVGhyZWFkKCk7CgogIGxvZygnQ29ubmVjdGVkIHRvIGRlYnVnZ2VyJyk7CgogIC8vIENvbGxlY3Qgc2NyaXB0SWQgLT4gdXJsIG1hcCBzbyB3ZSBjYW4gbG9vayB1cCB0aGUgZmlsZW5hbWVzIGxhdGVyCiAgY29uc3Qgc2NyaXB0cyA9IG5ldyBNYXAoKTsKCiAgc2Vzc2lvbi5vbignRGVidWdnZXIuc2NyaXB0UGFyc2VkJywgZXZlbnQgPT4gewogICAgc2NyaXB0cy5zZXQoZXZlbnQucGFyYW1zLnNjcmlwdElkLCBldmVudC5wYXJhbXMudXJsKTsKICB9KTsKCiAgc2Vzc2lvbi5vbignRGVidWdnZXIucGF1c2VkJywgZXZlbnQgPT4gewogICAgaWYgKGV2ZW50LnBhcmFtcy5yZWFzb24gIT09ICdvdGhlcicpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHRyeSB7CiAgICAgIGxvZygnRGVidWdnZXIgcGF1c2VkJyk7CgogICAgICAvLyBjb3B5IHRoZSBmcmFtZXMKICAgICAgY29uc3QgY2FsbEZyYW1lcyA9IFsuLi5ldmVudC5wYXJhbXMuY2FsbEZyYW1lc107CgogICAgICBjb25zdCBnZXRNb2R1bGVOYW1lID0gb3B0aW9ucy5hcHBSb290UGF0aCA/IGNyZWF0ZUdldE1vZHVsZUZyb21GaWxlbmFtZShvcHRpb25zLmFwcFJvb3RQYXRoKSA6ICgpID0+IHVuZGVmaW5lZDsKICAgICAgY29uc3Qgc3RhY2tGcmFtZXMgPSBjYWxsRnJhbWVzLm1hcChmcmFtZSA9PgogICAgICAgIGNhbGxGcmFtZVRvU3RhY2tGcmFtZShmcmFtZSwgc2NyaXB0cy5nZXQoZnJhbWUubG9jYXRpb24uc2NyaXB0SWQpLCBnZXRNb2R1bGVOYW1lKSwKICAgICAgKTsKCiAgICAgIC8vIEV2YWx1YXRlIGEgc2NyaXB0IGluIHRoZSBjdXJyZW50bHkgcGF1c2VkIGNvbnRleHQKICAgICAgc2Vzc2lvbi5wb3N0KAogICAgICAgICdSdW50aW1lLmV2YWx1YXRlJywKICAgICAgICB7CiAgICAgICAgICAvLyBHcmFiIHRoZSB0cmFjZSBjb250ZXh0IGZyb20gdGhlIGN1cnJlbnQgc2NvcGUKICAgICAgICAgIGV4cHJlc3Npb246CiAgICAgICAgICAgICdjb25zdCBjdHggPSBfX1NFTlRSWV9fLmFjcz8uZ2V0Q3VycmVudFNjb3BlKCkuZ2V0UHJvcGFnYXRpb25Db250ZXh0KCkgfHwge307IGN0eC50cmFjZUlkICsgIi0iICsgY3R4LnNwYW5JZCArICItIiArIGN0eC5wYXJlbnRTcGFuSWQnLAogICAgICAgICAgLy8gRG9uJ3QgcmUtdHJpZ2dlciB0aGUgZGVidWdnZXIgaWYgdGhpcyBjYXVzZXMgYW4gZXJyb3IKICAgICAgICAgIHNpbGVudDogdHJ1ZSwKICAgICAgICB9LAogICAgICAgIChfLCBwYXJhbSkgPT4gewogICAgICAgICAgY29uc3QgdHJhY2VJZCA9IHBhcmFtICYmIHBhcmFtLnJlc3VsdCA/IChwYXJhbS5yZXN1bHQudmFsdWUgKSA6ICctLSc7CiAgICAgICAgICBjb25zdCBbdHJhY2VfaWQsIHNwYW5faWQsIHBhcmVudF9zcGFuX2lkXSA9IHRyYWNlSWQuc3BsaXQoJy0nKSA7CgogICAgICAgICAgc2Vzc2lvbi5wb3N0KCdEZWJ1Z2dlci5yZXN1bWUnKTsKICAgICAgICAgIHNlc3Npb24ucG9zdCgnRGVidWdnZXIuZGlzYWJsZScpOwoKICAgICAgICAgIGNvbnN0IGNvbnRleHQgPSBfb3B0aW9uYWxDaGFpbihbdHJhY2VfaWQsICdvcHRpb25hbEFjY2VzcycsIF80ID0+IF80Lmxlbmd0aF0pICYmIF9vcHRpb25hbENoYWluKFtzcGFuX2lkLCAnb3B0aW9uYWxBY2Nlc3MnLCBfNSA9PiBfNS5sZW5ndGhdKSA/IHsgdHJhY2VfaWQsIHNwYW5faWQsIHBhcmVudF9zcGFuX2lkIH0gOiB1bmRlZmluZWQ7CiAgICAgICAgICBzZW5kQW5yRXZlbnQoc3RhY2tGcmFtZXMsIGNvbnRleHQpLnRoZW4obnVsbCwgKCkgPT4gewogICAgICAgICAgICBsb2coJ1NlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4nKTsKICAgICAgICAgIH0pOwogICAgICAgIH0sCiAgICAgICk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHNlc3Npb24ucG9zdCgnRGVidWdnZXIucmVzdW1lJyk7CiAgICAgIHNlc3Npb24ucG9zdCgnRGVidWdnZXIuZGlzYWJsZScpOwogICAgICB0aHJvdyBlOwogICAgfQogIH0pOwoKICBkZWJ1Z2dlclBhdXNlID0gKCkgPT4gewogICAgdHJ5IHsKICAgICAgc2Vzc2lvbi5wb3N0KCdEZWJ1Z2dlci5lbmFibGUnLCAoKSA9PiB7CiAgICAgICAgc2Vzc2lvbi5wb3N0KCdEZWJ1Z2dlci5wYXVzZScpOwogICAgICB9KTsKICAgIH0gY2F0Y2ggKF8pIHsKICAgICAgLy8KICAgIH0KICB9Owp9CgpmdW5jdGlvbiBjcmVhdGVIclRpbWVyKCkgewogIC8vIFRPRE8gKHY4KTogV2UgY2FuIHVzZSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKSBhZnRlciB3ZSBkcm9wIG5vZGUgdjgKICBsZXQgbGFzdFBvbGwgPSBwcm9jZXNzLmhydGltZSgpOwoKICByZXR1cm4gewogICAgZ2V0VGltZU1zOiAoKSA9PiB7CiAgICAgIGNvbnN0IFtzZWNvbmRzLCBuYW5vU2Vjb25kc10gPSBwcm9jZXNzLmhydGltZShsYXN0UG9sbCk7CiAgICAgIHJldHVybiBNYXRoLmZsb29yKHNlY29uZHMgKiAxZTMgKyBuYW5vU2Vjb25kcyAvIDFlNik7CiAgICB9LAogICAgcmVzZXQ6ICgpID0+IHsKICAgICAgbGFzdFBvbGwgPSBwcm9jZXNzLmhydGltZSgpOwogICAgfSwKICB9Owp9CgpmdW5jdGlvbiB3YXRjaGRvZ1RpbWVvdXQoKSB7CiAgbG9nKCdXYXRjaGRvZyB0aW1lb3V0Jyk7CgogIGlmIChkZWJ1Z2dlclBhdXNlKSB7CiAgICBsb2coJ1BhdXNpbmcgZGVidWdnZXIgdG8gY2FwdHVyZSBzdGFjayB0cmFjZScpOwogICAgZGVidWdnZXJQYXVzZSgpOwogIH0gZWxzZSB7CiAgICBsb2coJ0NhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UnKTsKICAgIHNlbmRBbnJFdmVudCgpLnRoZW4obnVsbCwgKCkgPT4gewogICAgICBsb2coJ1NlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZCBvbiB3YXRjaGRvZyB0aW1lb3V0LicpOwogICAgfSk7CiAgfQp9Cgpjb25zdCB7IHBvbGwgfSA9IHdhdGNoZG9nVGltZXIoY3JlYXRlSHJUaW1lciwgb3B0aW9ucy5wb2xsSW50ZXJ2YWwsIG9wdGlvbnMuYW5yVGhyZXNob2xkLCB3YXRjaGRvZ1RpbWVvdXQpOwoKX29wdGlvbmFsQ2hhaW4oW3BhcmVudFBvcnQsICdvcHRpb25hbEFjY2VzcycsIF82ID0+IF82Lm9uLCAnY2FsbCcsIF83ID0+IF83KCdtZXNzYWdlJywgKG1zZykgPT4gewogIGlmIChtc2cuc2Vzc2lvbikgewogICAgc2Vzc2lvbiA9IG1ha2VTZXNzaW9uKG1zZy5zZXNzaW9uKTsKICB9CgogIHBvbGwoKTsKfSldKTs=';
|
|
1645
|
+
|
|
1646
|
+
function _optionalChain$4(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1647
|
+
const DEFAULT_INTERVAL = 50;
|
|
1648
|
+
const DEFAULT_HANG_THRESHOLD = 5000;
|
|
1649
|
+
|
|
1650
|
+
function log(message, ...args) {
|
|
1651
|
+
utils.logger.log(`[ANR] ${message}`, ...args);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
/**
|
|
1655
|
+
* Gets contexts by calling all event processors. This relies on being called after all integrations are setup
|
|
1656
|
+
*/
|
|
1657
|
+
async function getContexts(client) {
|
|
1658
|
+
let event = { message: 'ANR' };
|
|
1659
|
+
const eventHint = {};
|
|
1660
|
+
|
|
1661
|
+
for (const processor of client.getEventProcessors()) {
|
|
1662
|
+
if (event === null) break;
|
|
1663
|
+
event = await processor(event, eventHint);
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
return _optionalChain$4([event, 'optionalAccess', _2 => _2.contexts]) || {};
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
const INTEGRATION_NAME$1 = 'Anr';
|
|
1670
|
+
|
|
1671
|
+
const _anrIntegration = ((options = {}) => {
|
|
1672
|
+
return {
|
|
1673
|
+
name: INTEGRATION_NAME$1,
|
|
1674
|
+
setup(client) {
|
|
1675
|
+
if (NODE_MAJOR < 16 || (NODE_MAJOR === 16 && (NODE_VERSION.minor || 0) < 17)) {
|
|
1676
|
+
throw new Error('ANR detection requires Node 16.17.0 or later');
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// setImmediate is used to ensure that all other integrations have been setup
|
|
1680
|
+
setImmediate(() => _startWorker(client, options));
|
|
1681
|
+
},
|
|
1682
|
+
};
|
|
1683
|
+
}) ;
|
|
1684
|
+
|
|
1685
|
+
const anrIntegration = core.defineIntegration(_anrIntegration);
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* Starts the ANR worker thread
|
|
1689
|
+
*/
|
|
1690
|
+
async function _startWorker(client, _options) {
|
|
1691
|
+
const contexts = await getContexts(client);
|
|
1692
|
+
const dsn = client.getDsn();
|
|
1693
|
+
|
|
1694
|
+
if (!dsn) {
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// These will not be accurate if sent later from the worker thread
|
|
1699
|
+
_optionalChainDelete([contexts, 'access', _3 => _3.app, 'optionalAccess', _4 => delete _4.app_memory]);
|
|
1700
|
+
_optionalChainDelete([contexts, 'access', _5 => _5.device, 'optionalAccess', _6 => delete _6.free_memory]);
|
|
1701
|
+
|
|
1702
|
+
const initOptions = client.getOptions();
|
|
1703
|
+
|
|
1704
|
+
const sdkMetadata = client.getSdkMetadata() || {};
|
|
1705
|
+
if (sdkMetadata.sdk) {
|
|
1706
|
+
sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
const options = {
|
|
1710
|
+
debug: utils.logger.isEnabled(),
|
|
1711
|
+
dsn,
|
|
1712
|
+
environment: initOptions.environment || 'production',
|
|
1713
|
+
release: initOptions.release,
|
|
1714
|
+
dist: initOptions.dist,
|
|
1715
|
+
sdkMetadata,
|
|
1716
|
+
appRootPath: _options.appRootPath,
|
|
1717
|
+
pollInterval: _options.pollInterval || DEFAULT_INTERVAL,
|
|
1718
|
+
anrThreshold: _options.anrThreshold || DEFAULT_HANG_THRESHOLD,
|
|
1719
|
+
captureStackTrace: !!_options.captureStackTrace,
|
|
1720
|
+
staticTags: _options.staticTags || {},
|
|
1721
|
+
contexts,
|
|
1722
|
+
};
|
|
1723
|
+
|
|
1724
|
+
if (options.captureStackTrace) {
|
|
1725
|
+
if (!inspector.url()) {
|
|
1726
|
+
inspector.open(0);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const worker = new worker_threads.Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {
|
|
1731
|
+
workerData: options,
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
process.on('exit', () => {
|
|
1735
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1736
|
+
worker.terminate();
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
const timer = setInterval(() => {
|
|
1740
|
+
try {
|
|
1741
|
+
const currentSession = core.getCurrentScope().getSession();
|
|
1742
|
+
// We need to copy the session object and remove the toJSON method so it can be sent to the worker
|
|
1743
|
+
// serialized without making it a SerializedSession
|
|
1744
|
+
const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;
|
|
1745
|
+
// message the worker to tell it the main event loop is still running
|
|
1746
|
+
worker.postMessage({ session });
|
|
1747
|
+
} catch (_) {
|
|
1748
|
+
//
|
|
1749
|
+
}
|
|
1750
|
+
}, options.pollInterval);
|
|
1751
|
+
// Timer should not block exit
|
|
1752
|
+
timer.unref();
|
|
1753
|
+
|
|
1754
|
+
worker.on('message', (msg) => {
|
|
1755
|
+
if (msg === 'session-ended') {
|
|
1756
|
+
log('ANR event sent from ANR worker. Clearing session in this thread.');
|
|
1757
|
+
core.getCurrentScope().setSession(undefined);
|
|
1758
|
+
}
|
|
1759
|
+
});
|
|
1760
|
+
|
|
1761
|
+
worker.once('error', (err) => {
|
|
1762
|
+
clearInterval(timer);
|
|
1763
|
+
log('ANR worker error', err);
|
|
1764
|
+
});
|
|
1765
|
+
|
|
1766
|
+
worker.once('exit', (code) => {
|
|
1767
|
+
clearInterval(timer);
|
|
1768
|
+
log('ANR worker exit', code);
|
|
1769
|
+
});
|
|
1770
|
+
|
|
1771
|
+
// Ensure this thread can't block app exit
|
|
1772
|
+
worker.unref();
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
function _optionalChain$3(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1776
|
+
|
|
1777
|
+
const _expressIntegration = (() => {
|
|
1778
|
+
return {
|
|
1779
|
+
name: 'Express',
|
|
1780
|
+
setupOnce() {
|
|
1781
|
+
instrumentation.registerInstrumentations({
|
|
1782
|
+
instrumentations: [
|
|
1783
|
+
new instrumentationExpress.ExpressInstrumentation({
|
|
1784
|
+
requestHook(span) {
|
|
1785
|
+
addOriginToSpan(span, 'auto.http.otel.express');
|
|
1786
|
+
},
|
|
1787
|
+
}),
|
|
1788
|
+
],
|
|
1789
|
+
});
|
|
1790
|
+
},
|
|
1791
|
+
};
|
|
1792
|
+
}) ;
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* Express integration
|
|
1796
|
+
*
|
|
1797
|
+
* Capture tracing data for express.
|
|
1798
|
+
* In order to capture exceptions, you have to call `setupExpressErrorHandler(app)` before any other middleware and after all controllers.
|
|
1799
|
+
*/
|
|
1800
|
+
const expressIntegration = core.defineIntegration(_expressIntegration);
|
|
1801
|
+
|
|
1802
|
+
/**
|
|
1803
|
+
* An Express-compatible error handler.
|
|
1804
|
+
*/
|
|
1805
|
+
function expressErrorHandler(options
|
|
1806
|
+
|
|
1807
|
+
) {
|
|
1808
|
+
return function sentryErrorMiddleware(
|
|
1809
|
+
error,
|
|
1810
|
+
_req,
|
|
1811
|
+
res,
|
|
1812
|
+
next,
|
|
1813
|
+
) {
|
|
1814
|
+
const shouldHandleError = _optionalChain$3([options, 'optionalAccess', _ => _.shouldHandleError]) || defaultShouldHandleError;
|
|
35
1815
|
|
|
1816
|
+
if (shouldHandleError(error)) {
|
|
1817
|
+
const client = core.getClient();
|
|
1818
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
1819
|
+
// Check if the `SessionFlusher` is instantiated on the client to go into this branch that marks the
|
|
1820
|
+
// `requestSession.status` as `Crashed`, and this check is necessary because the `SessionFlusher` is only
|
|
1821
|
+
// instantiated when the the`requestHandler` middleware is initialised, which indicates that we should be
|
|
1822
|
+
// running in SessionAggregates mode
|
|
1823
|
+
const isSessionAggregatesMode = client['_sessionFlusher'] !== undefined;
|
|
1824
|
+
if (isSessionAggregatesMode) {
|
|
1825
|
+
const requestSession = core.getIsolationScope().getRequestSession();
|
|
1826
|
+
// If an error bubbles to the `errorHandler`, then this is an unhandled error, and should be reported as a
|
|
1827
|
+
// Crashed session. The `_requestSession.status` is checked to ensure that this error is happening within
|
|
1828
|
+
// the bounds of a request, and if so the status is updated
|
|
1829
|
+
if (requestSession && requestSession.status !== undefined) {
|
|
1830
|
+
requestSession.status = 'crashed';
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
36
1834
|
|
|
1835
|
+
const eventId = core.captureException(error, { mechanism: { type: 'middleware', handled: false } });
|
|
1836
|
+
(res ).sentry = eventId;
|
|
1837
|
+
next(error);
|
|
1838
|
+
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
next(error);
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
/**
|
|
1847
|
+
* Setup an error handler for Express.
|
|
1848
|
+
* The error handler must be before any other middleware and after all controllers.
|
|
1849
|
+
*/
|
|
1850
|
+
function setupExpressErrorHandler(app) {
|
|
1851
|
+
app.use(expressErrorHandler());
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
function getStatusCodeFromResponse(error) {
|
|
1855
|
+
const statusCode = error.status || error.statusCode || error.status_code || (error.output && error.output.statusCode);
|
|
1856
|
+
return statusCode ? parseInt(statusCode , 10) : 500;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
/** Returns true if response code is internal server error */
|
|
1860
|
+
function defaultShouldHandleError(error) {
|
|
1861
|
+
const status = getStatusCodeFromResponse(error);
|
|
1862
|
+
return status >= 500;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
const _fastifyIntegration = (() => {
|
|
1866
|
+
return {
|
|
1867
|
+
name: 'Fastify',
|
|
1868
|
+
setupOnce() {
|
|
1869
|
+
instrumentation.registerInstrumentations({
|
|
1870
|
+
instrumentations: [
|
|
1871
|
+
new instrumentationFastify.FastifyInstrumentation({
|
|
1872
|
+
requestHook(span) {
|
|
1873
|
+
addOriginToSpan(span, 'auto.http.otel.fastify');
|
|
1874
|
+
},
|
|
1875
|
+
}),
|
|
1876
|
+
],
|
|
1877
|
+
});
|
|
1878
|
+
},
|
|
1879
|
+
};
|
|
1880
|
+
}) ;
|
|
1881
|
+
|
|
1882
|
+
/**
|
|
1883
|
+
* Express integration
|
|
1884
|
+
*
|
|
1885
|
+
* Capture tracing data for fastify.
|
|
1886
|
+
*/
|
|
1887
|
+
const fastifyIntegration = core.defineIntegration(_fastifyIntegration);
|
|
1888
|
+
|
|
1889
|
+
// We inline the types we care about here
|
|
1890
|
+
|
|
1891
|
+
/**
|
|
1892
|
+
* Setup an error handler for Fastify.
|
|
1893
|
+
*/
|
|
1894
|
+
function setupFastifyErrorHandler(fastify) {
|
|
1895
|
+
const plugin = Object.assign(
|
|
1896
|
+
function (fastify, options, done) {
|
|
1897
|
+
fastify.addHook('onError', async (_request, _reply, error) => {
|
|
1898
|
+
core.captureException(error);
|
|
1899
|
+
});
|
|
1900
|
+
|
|
1901
|
+
done();
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
[Symbol.for('skip-override')]: true,
|
|
1905
|
+
[Symbol.for('fastify.display-name')]: 'sentry-fastify-error-handler',
|
|
1906
|
+
},
|
|
1907
|
+
);
|
|
1908
|
+
|
|
1909
|
+
fastify.register(plugin);
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
const _graphqlIntegration = (() => {
|
|
1913
|
+
return {
|
|
1914
|
+
name: 'Graphql',
|
|
1915
|
+
setupOnce() {
|
|
1916
|
+
instrumentation.registerInstrumentations({
|
|
1917
|
+
instrumentations: [
|
|
1918
|
+
new instrumentationGraphql.GraphQLInstrumentation({
|
|
1919
|
+
ignoreTrivialResolveSpans: true,
|
|
1920
|
+
responseHook(span) {
|
|
1921
|
+
addOriginToSpan(span, 'auto.graphql.otel.graphql');
|
|
1922
|
+
},
|
|
1923
|
+
}),
|
|
1924
|
+
],
|
|
1925
|
+
});
|
|
1926
|
+
},
|
|
1927
|
+
};
|
|
1928
|
+
}) ;
|
|
1929
|
+
|
|
1930
|
+
/**
|
|
1931
|
+
* GraphQL integration
|
|
1932
|
+
*
|
|
1933
|
+
* Capture tracing data for GraphQL.
|
|
1934
|
+
*/
|
|
1935
|
+
const graphqlIntegration = core.defineIntegration(_graphqlIntegration);
|
|
1936
|
+
|
|
1937
|
+
const _mongoIntegration = (() => {
|
|
1938
|
+
return {
|
|
1939
|
+
name: 'Mongo',
|
|
1940
|
+
setupOnce() {
|
|
1941
|
+
instrumentation.registerInstrumentations({
|
|
1942
|
+
instrumentations: [
|
|
1943
|
+
new instrumentationMongodb.MongoDBInstrumentation({
|
|
1944
|
+
responseHook(span) {
|
|
1945
|
+
addOriginToSpan(span, 'auto.db.otel.mongo');
|
|
1946
|
+
},
|
|
1947
|
+
}),
|
|
1948
|
+
],
|
|
1949
|
+
});
|
|
1950
|
+
},
|
|
1951
|
+
};
|
|
1952
|
+
}) ;
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* MongoDB integration
|
|
1956
|
+
*
|
|
1957
|
+
* Capture tracing data for MongoDB.
|
|
1958
|
+
*/
|
|
1959
|
+
const mongoIntegration = core.defineIntegration(_mongoIntegration);
|
|
1960
|
+
|
|
1961
|
+
const _mongooseIntegration = (() => {
|
|
1962
|
+
return {
|
|
1963
|
+
name: 'Mongoose',
|
|
1964
|
+
setupOnce() {
|
|
1965
|
+
instrumentation.registerInstrumentations({
|
|
1966
|
+
instrumentations: [
|
|
1967
|
+
new instrumentationMongoose.MongooseInstrumentation({
|
|
1968
|
+
responseHook(span) {
|
|
1969
|
+
addOriginToSpan(span, 'auto.db.otel.mongoose');
|
|
1970
|
+
},
|
|
1971
|
+
}),
|
|
1972
|
+
],
|
|
1973
|
+
});
|
|
1974
|
+
},
|
|
1975
|
+
};
|
|
1976
|
+
}) ;
|
|
1977
|
+
|
|
1978
|
+
/**
|
|
1979
|
+
* Mongoose integration
|
|
1980
|
+
*
|
|
1981
|
+
* Capture tracing data for Mongoose.
|
|
1982
|
+
*/
|
|
1983
|
+
const mongooseIntegration = core.defineIntegration(_mongooseIntegration);
|
|
1984
|
+
|
|
1985
|
+
const _mysqlIntegration = (() => {
|
|
1986
|
+
return {
|
|
1987
|
+
name: 'Mysql',
|
|
1988
|
+
setupOnce() {
|
|
1989
|
+
instrumentation.registerInstrumentations({
|
|
1990
|
+
instrumentations: [new instrumentationMysql.MySQLInstrumentation({})],
|
|
1991
|
+
});
|
|
1992
|
+
},
|
|
1993
|
+
};
|
|
1994
|
+
}) ;
|
|
1995
|
+
|
|
1996
|
+
/**
|
|
1997
|
+
* MySQL integration
|
|
1998
|
+
*
|
|
1999
|
+
* Capture tracing data for mysql.
|
|
2000
|
+
*/
|
|
2001
|
+
const mysqlIntegration = core.defineIntegration(_mysqlIntegration);
|
|
2002
|
+
|
|
2003
|
+
const _mysql2Integration = (() => {
|
|
2004
|
+
return {
|
|
2005
|
+
name: 'Mysql2',
|
|
2006
|
+
setupOnce() {
|
|
2007
|
+
instrumentation.registerInstrumentations({
|
|
2008
|
+
instrumentations: [
|
|
2009
|
+
new instrumentationMysql2.MySQL2Instrumentation({
|
|
2010
|
+
responseHook(span) {
|
|
2011
|
+
addOriginToSpan(span, 'auto.db.otel.mysql2');
|
|
2012
|
+
},
|
|
2013
|
+
}),
|
|
2014
|
+
],
|
|
2015
|
+
});
|
|
2016
|
+
},
|
|
2017
|
+
};
|
|
2018
|
+
}) ;
|
|
2019
|
+
|
|
2020
|
+
/**
|
|
2021
|
+
* MySQL2 integration
|
|
2022
|
+
*
|
|
2023
|
+
* Capture tracing data for mysql2
|
|
2024
|
+
*/
|
|
2025
|
+
const mysql2Integration = core.defineIntegration(_mysql2Integration);
|
|
2026
|
+
|
|
2027
|
+
const _nestIntegration = (() => {
|
|
2028
|
+
return {
|
|
2029
|
+
name: 'Nest',
|
|
2030
|
+
setupOnce() {
|
|
2031
|
+
instrumentation.registerInstrumentations({
|
|
2032
|
+
instrumentations: [new instrumentationNestjsCore.NestInstrumentation({})],
|
|
2033
|
+
});
|
|
2034
|
+
},
|
|
2035
|
+
};
|
|
2036
|
+
}) ;
|
|
2037
|
+
|
|
2038
|
+
/**
|
|
2039
|
+
* Nest framework integration
|
|
2040
|
+
*
|
|
2041
|
+
* Capture tracing data for nest.
|
|
2042
|
+
*/
|
|
2043
|
+
const nestIntegration = core.defineIntegration(_nestIntegration);
|
|
2044
|
+
|
|
2045
|
+
const _postgresIntegration = (() => {
|
|
2046
|
+
return {
|
|
2047
|
+
name: 'Postgres',
|
|
2048
|
+
setupOnce() {
|
|
2049
|
+
instrumentation.registerInstrumentations({
|
|
2050
|
+
instrumentations: [
|
|
2051
|
+
new instrumentationPg.PgInstrumentation({
|
|
2052
|
+
requireParentSpan: true,
|
|
2053
|
+
requestHook(span) {
|
|
2054
|
+
addOriginToSpan(span, 'auto.db.otel.postgres');
|
|
2055
|
+
},
|
|
2056
|
+
}),
|
|
2057
|
+
],
|
|
2058
|
+
});
|
|
2059
|
+
},
|
|
2060
|
+
};
|
|
2061
|
+
}) ;
|
|
2062
|
+
|
|
2063
|
+
/**
|
|
2064
|
+
* Postgres integration
|
|
2065
|
+
*
|
|
2066
|
+
* Capture tracing data for pg.
|
|
2067
|
+
*/
|
|
2068
|
+
const postgresIntegration = core.defineIntegration(_postgresIntegration);
|
|
2069
|
+
|
|
2070
|
+
const _prismaIntegration = (() => {
|
|
2071
|
+
return {
|
|
2072
|
+
name: 'Prisma',
|
|
2073
|
+
setupOnce() {
|
|
2074
|
+
instrumentation.registerInstrumentations({
|
|
2075
|
+
instrumentations: [
|
|
2076
|
+
// does not have a hook to adjust spans & add origin
|
|
2077
|
+
new prismaInstrumentation.PrismaInstrumentation({}),
|
|
2078
|
+
],
|
|
2079
|
+
});
|
|
2080
|
+
},
|
|
2081
|
+
};
|
|
2082
|
+
}) ;
|
|
2083
|
+
|
|
2084
|
+
/**
|
|
2085
|
+
* Prisma integration
|
|
2086
|
+
*
|
|
2087
|
+
* Capture tracing data for prisma.
|
|
2088
|
+
* Note: This requieres to set:
|
|
2089
|
+
* previewFeatures = ["tracing"]
|
|
2090
|
+
* For the prisma client.
|
|
2091
|
+
* See https://www.prisma.io/docs/concepts/components/prisma-client/opentelemetry-tracing for more details.
|
|
2092
|
+
*/
|
|
2093
|
+
const prismaIntegration = core.defineIntegration(_prismaIntegration);
|
|
2094
|
+
|
|
2095
|
+
const _hapiIntegration = (() => {
|
|
2096
|
+
return {
|
|
2097
|
+
name: 'Hapi',
|
|
2098
|
+
setupOnce() {
|
|
2099
|
+
instrumentation.registerInstrumentations({
|
|
2100
|
+
instrumentations: [new instrumentationHapi.HapiInstrumentation()],
|
|
2101
|
+
});
|
|
2102
|
+
},
|
|
2103
|
+
};
|
|
2104
|
+
}) ;
|
|
2105
|
+
|
|
2106
|
+
/**
|
|
2107
|
+
* Hapi integration
|
|
2108
|
+
*
|
|
2109
|
+
* Capture tracing data for Hapi.
|
|
2110
|
+
* If you also want to capture errors, you need to call `setupHapiErrorHandler(server)` after you set up your server.
|
|
2111
|
+
*/
|
|
2112
|
+
const hapiIntegration = core.defineIntegration(_hapiIntegration);
|
|
2113
|
+
|
|
2114
|
+
function isBoomObject(response) {
|
|
2115
|
+
return response && (response ).isBoom !== undefined;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
function isErrorEvent(event) {
|
|
2119
|
+
return event && (event ).error !== undefined;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
function sendErrorToSentry(errorData) {
|
|
2123
|
+
core.captureException(errorData, {
|
|
2124
|
+
mechanism: {
|
|
2125
|
+
type: 'hapi',
|
|
2126
|
+
handled: false,
|
|
2127
|
+
data: {
|
|
2128
|
+
function: 'hapiErrorPlugin',
|
|
2129
|
+
},
|
|
2130
|
+
},
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
const hapiErrorPlugin = {
|
|
2135
|
+
name: 'SentryHapiErrorPlugin',
|
|
2136
|
+
version: core.SDK_VERSION,
|
|
2137
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2138
|
+
register: async function (serverArg) {
|
|
2139
|
+
const server = serverArg ;
|
|
2140
|
+
|
|
2141
|
+
server.events.on('request', (request, event) => {
|
|
2142
|
+
const activeSpan = core.getActiveSpan();
|
|
2143
|
+
const rootSpan = activeSpan ? core.getRootSpan(activeSpan) : undefined;
|
|
2144
|
+
|
|
2145
|
+
if (request.response && isBoomObject(request.response)) {
|
|
2146
|
+
sendErrorToSentry(request.response);
|
|
2147
|
+
} else if (isErrorEvent(event)) {
|
|
2148
|
+
sendErrorToSentry(event.error);
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
if (rootSpan) {
|
|
2152
|
+
rootSpan.setStatus({ code: core.SPAN_STATUS_ERROR, message: 'internal_error' });
|
|
2153
|
+
rootSpan.end();
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
},
|
|
2157
|
+
};
|
|
2158
|
+
|
|
2159
|
+
/**
|
|
2160
|
+
* Add a Hapi plugin to capture errors to Sentry.
|
|
2161
|
+
*/
|
|
2162
|
+
async function setupHapiErrorHandler(server) {
|
|
2163
|
+
await server.register(hapiErrorPlugin);
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
const INTEGRATION_NAME = 'Spotlight';
|
|
2167
|
+
|
|
2168
|
+
const _spotlightIntegration = ((options = {}) => {
|
|
2169
|
+
const _options = {
|
|
2170
|
+
sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',
|
|
2171
|
+
};
|
|
2172
|
+
|
|
2173
|
+
return {
|
|
2174
|
+
name: INTEGRATION_NAME,
|
|
2175
|
+
setup(client) {
|
|
2176
|
+
if (typeof process === 'object' && process.env && process.env.NODE_ENV !== 'development') {
|
|
2177
|
+
utils.logger.warn("[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?");
|
|
2178
|
+
}
|
|
2179
|
+
connectToSpotlight(client, _options);
|
|
2180
|
+
},
|
|
2181
|
+
};
|
|
2182
|
+
}) ;
|
|
2183
|
+
|
|
2184
|
+
/**
|
|
2185
|
+
* Use this integration to send errors and transactions to Spotlight.
|
|
2186
|
+
*
|
|
2187
|
+
* Learn more about spotlight at https://spotlightjs.com
|
|
2188
|
+
*
|
|
2189
|
+
* Important: This integration only works with Node 18 or newer.
|
|
2190
|
+
*/
|
|
2191
|
+
const spotlightIntegration = core.defineIntegration(_spotlightIntegration);
|
|
2192
|
+
|
|
2193
|
+
function connectToSpotlight(client, options) {
|
|
2194
|
+
const spotlightUrl = parseSidecarUrl(options.sidecarUrl);
|
|
2195
|
+
if (!spotlightUrl) {
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
let failedRequests = 0;
|
|
2200
|
+
|
|
2201
|
+
client.on('beforeEnvelope', (envelope) => {
|
|
2202
|
+
if (failedRequests > 3) {
|
|
2203
|
+
utils.logger.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
const serializedEnvelope = utils.serializeEnvelope(envelope);
|
|
2208
|
+
|
|
2209
|
+
const request = getNativeHttpRequest();
|
|
2210
|
+
const req = request(
|
|
2211
|
+
{
|
|
2212
|
+
method: 'POST',
|
|
2213
|
+
path: spotlightUrl.pathname,
|
|
2214
|
+
hostname: spotlightUrl.hostname,
|
|
2215
|
+
port: spotlightUrl.port,
|
|
2216
|
+
headers: {
|
|
2217
|
+
'Content-Type': 'application/x-sentry-envelope',
|
|
2218
|
+
},
|
|
2219
|
+
},
|
|
2220
|
+
res => {
|
|
2221
|
+
res.on('data', () => {
|
|
2222
|
+
// Drain socket
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
res.on('end', () => {
|
|
2226
|
+
// Drain socket
|
|
2227
|
+
});
|
|
2228
|
+
res.setEncoding('utf8');
|
|
2229
|
+
},
|
|
2230
|
+
);
|
|
2231
|
+
|
|
2232
|
+
req.on('error', () => {
|
|
2233
|
+
failedRequests++;
|
|
2234
|
+
utils.logger.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');
|
|
2235
|
+
});
|
|
2236
|
+
req.write(serializedEnvelope);
|
|
2237
|
+
req.end();
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
function parseSidecarUrl(url) {
|
|
2242
|
+
try {
|
|
2243
|
+
return new URL(`${url}`);
|
|
2244
|
+
} catch (e) {
|
|
2245
|
+
utils.logger.warn(`[Spotlight] Invalid sidecar URL: ${url}`);
|
|
2246
|
+
return undefined;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/**
|
|
2251
|
+
* We want to get an unpatched http request implementation to avoid capturing our own calls.
|
|
2252
|
+
*/
|
|
2253
|
+
function getNativeHttpRequest() {
|
|
2254
|
+
const { request } = http;
|
|
2255
|
+
if (isWrapped(request)) {
|
|
2256
|
+
return request.__sentry_original__;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
return request;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
function isWrapped(impl) {
|
|
2263
|
+
return '__sentry_original__' in impl;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
const _koaIntegration = (() => {
|
|
2267
|
+
return {
|
|
2268
|
+
name: 'Koa',
|
|
2269
|
+
setupOnce() {
|
|
2270
|
+
instrumentation.registerInstrumentations({
|
|
2271
|
+
instrumentations: [new instrumentationKoa.KoaInstrumentation()],
|
|
2272
|
+
});
|
|
2273
|
+
},
|
|
2274
|
+
};
|
|
2275
|
+
}) ;
|
|
2276
|
+
|
|
2277
|
+
const koaIntegration = core.defineIntegration(_koaIntegration);
|
|
2278
|
+
|
|
2279
|
+
/**
|
|
2280
|
+
* With OTEL, all performance integrations will be added, as OTEL only initializes them when the patched package is actually required.
|
|
2281
|
+
*/
|
|
2282
|
+
function getAutoPerformanceIntegrations() {
|
|
2283
|
+
return [
|
|
2284
|
+
expressIntegration(),
|
|
2285
|
+
fastifyIntegration(),
|
|
2286
|
+
graphqlIntegration(),
|
|
2287
|
+
mongoIntegration(),
|
|
2288
|
+
mongooseIntegration(),
|
|
2289
|
+
mysqlIntegration(),
|
|
2290
|
+
mysql2Integration(),
|
|
2291
|
+
postgresIntegration(),
|
|
2292
|
+
prismaIntegration(),
|
|
2293
|
+
nestIntegration(),
|
|
2294
|
+
hapiIntegration(),
|
|
2295
|
+
koaIntegration(),
|
|
2296
|
+
];
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }/**
|
|
2300
|
+
* This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7
|
|
2301
|
+
* With the following licence:
|
|
2302
|
+
*
|
|
2303
|
+
* (The MIT License)
|
|
2304
|
+
*
|
|
2305
|
+
* Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*
|
|
2306
|
+
*
|
|
2307
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
|
2308
|
+
* a copy of this software and associated documentation files (the
|
|
2309
|
+
* 'Software'), to deal in the Software without restriction, including
|
|
2310
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
|
2311
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
2312
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
|
2313
|
+
* the following conditions:*
|
|
2314
|
+
*
|
|
2315
|
+
* The above copyright notice and this permission notice shall be
|
|
2316
|
+
* included in all copies or substantial portions of the Software.*
|
|
2317
|
+
*
|
|
2318
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
2319
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
2320
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
2321
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
2322
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
2323
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
2324
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
2325
|
+
*/
|
|
2326
|
+
|
|
2327
|
+
const INTERNAL = Symbol('AgentBaseInternalState');
|
|
2328
|
+
|
|
2329
|
+
class Agent extends http.Agent {
|
|
2330
|
+
|
|
2331
|
+
// Set by `http.Agent` - missing from `@types/node`
|
|
2332
|
+
|
|
2333
|
+
constructor(opts) {
|
|
2334
|
+
super(opts);
|
|
2335
|
+
this[INTERNAL] = {};
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
/**
|
|
2339
|
+
* Determine whether this is an `http` or `https` request.
|
|
2340
|
+
*/
|
|
2341
|
+
isSecureEndpoint(options) {
|
|
2342
|
+
if (options) {
|
|
2343
|
+
// First check the `secureEndpoint` property explicitly, since this
|
|
2344
|
+
// means that a parent `Agent` is "passing through" to this instance.
|
|
2345
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
2346
|
+
if (typeof (options ).secureEndpoint === 'boolean') {
|
|
2347
|
+
return options.secureEndpoint;
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
// If no explicit `secure` endpoint, check if `protocol` property is
|
|
2351
|
+
// set. This will usually be the case since using a full string URL
|
|
2352
|
+
// or `URL` instance should be the most common usage.
|
|
2353
|
+
if (typeof options.protocol === 'string') {
|
|
2354
|
+
return options.protocol === 'https:';
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// Finally, if no `protocol` property was set, then fall back to
|
|
2359
|
+
// checking the stack trace of the current call stack, and try to
|
|
2360
|
+
// detect the "https" module.
|
|
2361
|
+
const { stack } = new Error();
|
|
2362
|
+
if (typeof stack !== 'string') return false;
|
|
2363
|
+
return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
createSocket(req, options, cb) {
|
|
2367
|
+
const connectOpts = {
|
|
2368
|
+
...options,
|
|
2369
|
+
secureEndpoint: this.isSecureEndpoint(options),
|
|
2370
|
+
};
|
|
2371
|
+
Promise.resolve()
|
|
2372
|
+
.then(() => this.connect(req, connectOpts))
|
|
2373
|
+
.then(socket => {
|
|
2374
|
+
if (socket instanceof http.Agent) {
|
|
2375
|
+
// @ts-expect-error `addRequest()` isn't defined in `@types/node`
|
|
2376
|
+
return socket.addRequest(req, connectOpts);
|
|
2377
|
+
}
|
|
2378
|
+
this[INTERNAL].currentSocket = socket;
|
|
2379
|
+
// @ts-expect-error `createSocket()` isn't defined in `@types/node`
|
|
2380
|
+
super.createSocket(req, options, cb);
|
|
2381
|
+
}, cb);
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
createConnection() {
|
|
2385
|
+
const socket = this[INTERNAL].currentSocket;
|
|
2386
|
+
this[INTERNAL].currentSocket = undefined;
|
|
2387
|
+
if (!socket) {
|
|
2388
|
+
throw new Error('No socket was returned in the `connect()` function');
|
|
2389
|
+
}
|
|
2390
|
+
return socket;
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
get defaultPort() {
|
|
2394
|
+
return _nullishCoalesce$2(this[INTERNAL].defaultPort, () => ( (this.protocol === 'https:' ? 443 : 80)));
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
set defaultPort(v) {
|
|
2398
|
+
if (this[INTERNAL]) {
|
|
2399
|
+
this[INTERNAL].defaultPort = v;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
get protocol() {
|
|
2404
|
+
return _nullishCoalesce$2(this[INTERNAL].protocol, () => ( (this.isSecureEndpoint() ? 'https:' : 'http:')));
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
set protocol(v) {
|
|
2408
|
+
if (this[INTERNAL]) {
|
|
2409
|
+
this[INTERNAL].protocol = v;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
function debug$1(...args) {
|
|
2415
|
+
utils.logger.log('[https-proxy-agent:parse-proxy-response]', ...args);
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
function parseProxyResponse(socket) {
|
|
2419
|
+
return new Promise((resolve, reject) => {
|
|
2420
|
+
// we need to buffer any HTTP traffic that happens with the proxy before we get
|
|
2421
|
+
// the CONNECT response, so that if the response is anything other than an "200"
|
|
2422
|
+
// response code, then we can re-play the "data" events on the socket once the
|
|
2423
|
+
// HTTP parser is hooked up...
|
|
2424
|
+
let buffersLength = 0;
|
|
2425
|
+
const buffers = [];
|
|
2426
|
+
|
|
2427
|
+
function read() {
|
|
2428
|
+
const b = socket.read();
|
|
2429
|
+
if (b) ondata(b);
|
|
2430
|
+
else socket.once('readable', read);
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
function cleanup() {
|
|
2434
|
+
socket.removeListener('end', onend);
|
|
2435
|
+
socket.removeListener('error', onerror);
|
|
2436
|
+
socket.removeListener('readable', read);
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
function onend() {
|
|
2440
|
+
cleanup();
|
|
2441
|
+
debug$1('onend');
|
|
2442
|
+
reject(new Error('Proxy connection ended before receiving CONNECT response'));
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
function onerror(err) {
|
|
2446
|
+
cleanup();
|
|
2447
|
+
debug$1('onerror %o', err);
|
|
2448
|
+
reject(err);
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
function ondata(b) {
|
|
2452
|
+
buffers.push(b);
|
|
2453
|
+
buffersLength += b.length;
|
|
2454
|
+
|
|
2455
|
+
const buffered = Buffer.concat(buffers, buffersLength);
|
|
2456
|
+
const endOfHeaders = buffered.indexOf('\r\n\r\n');
|
|
2457
|
+
|
|
2458
|
+
if (endOfHeaders === -1) {
|
|
2459
|
+
// keep buffering
|
|
2460
|
+
debug$1('have not received end of HTTP headers yet...');
|
|
2461
|
+
read();
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
const headerParts = buffered.slice(0, endOfHeaders).toString('ascii').split('\r\n');
|
|
2466
|
+
const firstLine = headerParts.shift();
|
|
2467
|
+
if (!firstLine) {
|
|
2468
|
+
socket.destroy();
|
|
2469
|
+
return reject(new Error('No header received from proxy CONNECT response'));
|
|
2470
|
+
}
|
|
2471
|
+
const firstLineParts = firstLine.split(' ');
|
|
2472
|
+
const statusCode = +firstLineParts[1];
|
|
2473
|
+
const statusText = firstLineParts.slice(2).join(' ');
|
|
2474
|
+
const headers = {};
|
|
2475
|
+
for (const header of headerParts) {
|
|
2476
|
+
if (!header) continue;
|
|
2477
|
+
const firstColon = header.indexOf(':');
|
|
2478
|
+
if (firstColon === -1) {
|
|
2479
|
+
socket.destroy();
|
|
2480
|
+
return reject(new Error(`Invalid header from proxy CONNECT response: "${header}"`));
|
|
2481
|
+
}
|
|
2482
|
+
const key = header.slice(0, firstColon).toLowerCase();
|
|
2483
|
+
const value = header.slice(firstColon + 1).trimStart();
|
|
2484
|
+
const current = headers[key];
|
|
2485
|
+
if (typeof current === 'string') {
|
|
2486
|
+
headers[key] = [current, value];
|
|
2487
|
+
} else if (Array.isArray(current)) {
|
|
2488
|
+
current.push(value);
|
|
2489
|
+
} else {
|
|
2490
|
+
headers[key] = value;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
debug$1('got proxy server response: %o %o', firstLine, headers);
|
|
2494
|
+
cleanup();
|
|
2495
|
+
resolve({
|
|
2496
|
+
connect: {
|
|
2497
|
+
statusCode,
|
|
2498
|
+
statusText,
|
|
2499
|
+
headers,
|
|
2500
|
+
},
|
|
2501
|
+
buffered,
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
socket.on('error', onerror);
|
|
2506
|
+
socket.on('end', onend);
|
|
2507
|
+
|
|
2508
|
+
read();
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }function _optionalChain$2(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2513
|
+
|
|
2514
|
+
function debug(...args) {
|
|
2515
|
+
utils.logger.log('[https-proxy-agent]', ...args);
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
/**
|
|
2519
|
+
* The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to
|
|
2520
|
+
* the specified "HTTP(s) proxy server" in order to proxy HTTPS requests.
|
|
2521
|
+
*
|
|
2522
|
+
* Outgoing HTTP requests are first tunneled through the proxy server using the
|
|
2523
|
+
* `CONNECT` HTTP request method to establish a connection to the proxy server,
|
|
2524
|
+
* and then the proxy server connects to the destination target and issues the
|
|
2525
|
+
* HTTP request from the proxy server.
|
|
2526
|
+
*
|
|
2527
|
+
* `https:` requests have their socket connection upgraded to TLS once
|
|
2528
|
+
* the connection to the proxy server has been established.
|
|
2529
|
+
*/
|
|
2530
|
+
class HttpsProxyAgent extends Agent {
|
|
2531
|
+
static __initStatic() {this.protocols = ['http', 'https']; }
|
|
2532
|
+
|
|
2533
|
+
constructor(proxy, opts) {
|
|
2534
|
+
super(opts);
|
|
2535
|
+
this.options = {};
|
|
2536
|
+
this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy;
|
|
2537
|
+
this.proxyHeaders = _nullishCoalesce$1(_optionalChain$2([opts, 'optionalAccess', _2 => _2.headers]), () => ( {}));
|
|
2538
|
+
debug('Creating new HttpsProxyAgent instance: %o', this.proxy.href);
|
|
2539
|
+
|
|
2540
|
+
// Trim off the brackets from IPv6 addresses
|
|
2541
|
+
const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, '');
|
|
2542
|
+
const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80;
|
|
2543
|
+
this.connectOpts = {
|
|
2544
|
+
// Attempt to negotiate http/1.1 for proxy servers that support http/2
|
|
2545
|
+
ALPNProtocols: ['http/1.1'],
|
|
2546
|
+
...(opts ? omit(opts, 'headers') : null),
|
|
2547
|
+
host,
|
|
2548
|
+
port,
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
/**
|
|
2553
|
+
* Called when the node-core HTTP client library is creating a
|
|
2554
|
+
* new HTTP request.
|
|
2555
|
+
*/
|
|
2556
|
+
async connect(req, opts) {
|
|
2557
|
+
const { proxy } = this;
|
|
2558
|
+
|
|
2559
|
+
if (!opts.host) {
|
|
2560
|
+
throw new TypeError('No "host" provided');
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
// Create a socket connection to the proxy server.
|
|
2564
|
+
let socket;
|
|
2565
|
+
if (proxy.protocol === 'https:') {
|
|
2566
|
+
debug('Creating `tls.Socket`: %o', this.connectOpts);
|
|
2567
|
+
const servername = this.connectOpts.servername || this.connectOpts.host;
|
|
2568
|
+
socket = tls.connect({
|
|
2569
|
+
...this.connectOpts,
|
|
2570
|
+
servername: servername && net.isIP(servername) ? undefined : servername,
|
|
2571
|
+
});
|
|
2572
|
+
} else {
|
|
2573
|
+
debug('Creating `net.Socket`: %o', this.connectOpts);
|
|
2574
|
+
socket = net.connect(this.connectOpts);
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
const headers =
|
|
2578
|
+
typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders };
|
|
2579
|
+
const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host;
|
|
2580
|
+
let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r\n`;
|
|
2581
|
+
|
|
2582
|
+
// Inject the `Proxy-Authorization` header if necessary.
|
|
2583
|
+
if (proxy.username || proxy.password) {
|
|
2584
|
+
const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`;
|
|
2585
|
+
headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`;
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
headers.Host = `${host}:${opts.port}`;
|
|
2589
|
+
|
|
2590
|
+
if (!headers['Proxy-Connection']) {
|
|
2591
|
+
headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close';
|
|
2592
|
+
}
|
|
2593
|
+
for (const name of Object.keys(headers)) {
|
|
2594
|
+
payload += `${name}: ${headers[name]}\r\n`;
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
const proxyResponsePromise = parseProxyResponse(socket);
|
|
2598
|
+
|
|
2599
|
+
socket.write(`${payload}\r\n`);
|
|
2600
|
+
|
|
2601
|
+
const { connect, buffered } = await proxyResponsePromise;
|
|
2602
|
+
req.emit('proxyConnect', connect);
|
|
2603
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2604
|
+
// @ts-ignore Not EventEmitter in Node types
|
|
2605
|
+
this.emit('proxyConnect', connect, req);
|
|
2606
|
+
|
|
2607
|
+
if (connect.statusCode === 200) {
|
|
2608
|
+
req.once('socket', resume);
|
|
2609
|
+
|
|
2610
|
+
if (opts.secureEndpoint) {
|
|
2611
|
+
// The proxy is connecting to a TLS server, so upgrade
|
|
2612
|
+
// this socket connection to a TLS connection.
|
|
2613
|
+
debug('Upgrading socket connection to TLS');
|
|
2614
|
+
const servername = opts.servername || opts.host;
|
|
2615
|
+
return tls.connect({
|
|
2616
|
+
...omit(opts, 'host', 'path', 'port'),
|
|
2617
|
+
socket,
|
|
2618
|
+
servername: net.isIP(servername) ? undefined : servername,
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
return socket;
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
// Some other status code that's not 200... need to re-play the HTTP
|
|
2626
|
+
// header "data" events onto the socket once the HTTP machinery is
|
|
2627
|
+
// attached so that the node core `http` can parse and handle the
|
|
2628
|
+
// error status code.
|
|
2629
|
+
|
|
2630
|
+
// Close the original socket, and a new "fake" socket is returned
|
|
2631
|
+
// instead, so that the proxy doesn't get the HTTP request
|
|
2632
|
+
// written to it (which may contain `Authorization` headers or other
|
|
2633
|
+
// sensitive data).
|
|
2634
|
+
//
|
|
2635
|
+
// See: https://hackerone.com/reports/541502
|
|
2636
|
+
socket.destroy();
|
|
2637
|
+
|
|
2638
|
+
const fakeSocket = new net.Socket({ writable: false });
|
|
2639
|
+
fakeSocket.readable = true;
|
|
2640
|
+
|
|
2641
|
+
// Need to wait for the "socket" event to re-play the "data" events.
|
|
2642
|
+
req.once('socket', (s) => {
|
|
2643
|
+
debug('Replaying proxy buffer for failed request');
|
|
2644
|
+
// Replay the "buffered" Buffer onto the fake `socket`, since at
|
|
2645
|
+
// this point the HTTP module machinery has been hooked up for
|
|
2646
|
+
// the user.
|
|
2647
|
+
s.push(buffered);
|
|
2648
|
+
s.push(null);
|
|
2649
|
+
});
|
|
2650
|
+
|
|
2651
|
+
return fakeSocket;
|
|
2652
|
+
}
|
|
2653
|
+
}HttpsProxyAgent.__initStatic();
|
|
2654
|
+
|
|
2655
|
+
function resume(socket) {
|
|
2656
|
+
socket.resume();
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
function omit(
|
|
2660
|
+
obj,
|
|
2661
|
+
...keys
|
|
2662
|
+
)
|
|
2663
|
+
|
|
2664
|
+
{
|
|
2665
|
+
const ret = {}
|
|
2666
|
+
|
|
2667
|
+
;
|
|
2668
|
+
let key;
|
|
2669
|
+
for (key in obj) {
|
|
2670
|
+
if (!keys.includes(key)) {
|
|
2671
|
+
ret[key] = obj[key];
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
return ret;
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
// Estimated maximum size for reasonable standalone event
|
|
2678
|
+
const GZIP_THRESHOLD = 1024 * 32;
|
|
2679
|
+
|
|
2680
|
+
/**
|
|
2681
|
+
* Gets a stream from a Uint8Array or string
|
|
2682
|
+
* Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0
|
|
2683
|
+
*/
|
|
2684
|
+
function streamFromBody(body) {
|
|
2685
|
+
return new stream.Readable({
|
|
2686
|
+
read() {
|
|
2687
|
+
this.push(body);
|
|
2688
|
+
this.push(null);
|
|
2689
|
+
},
|
|
2690
|
+
});
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
/**
|
|
2694
|
+
* Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry.
|
|
2695
|
+
*/
|
|
2696
|
+
function makeNodeTransport(options) {
|
|
2697
|
+
let urlSegments;
|
|
2698
|
+
|
|
2699
|
+
try {
|
|
2700
|
+
urlSegments = new URL(options.url);
|
|
2701
|
+
} catch (e) {
|
|
2702
|
+
utils.consoleSandbox(() => {
|
|
2703
|
+
// eslint-disable-next-line no-console
|
|
2704
|
+
console.warn(
|
|
2705
|
+
'[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.',
|
|
2706
|
+
);
|
|
2707
|
+
});
|
|
2708
|
+
return core.createTransport(options, () => Promise.resolve({}));
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
const isHttps = urlSegments.protocol === 'https:';
|
|
2712
|
+
|
|
2713
|
+
// Proxy prioritization: http => `options.proxy` | `process.env.http_proxy`
|
|
2714
|
+
// Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy`
|
|
2715
|
+
const proxy = applyNoProxyOption(
|
|
2716
|
+
urlSegments,
|
|
2717
|
+
options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy,
|
|
2718
|
+
);
|
|
2719
|
+
|
|
2720
|
+
const nativeHttpModule = isHttps ? https : http$1;
|
|
2721
|
+
const keepAlive = options.keepAlive === undefined ? false : options.keepAlive;
|
|
2722
|
+
|
|
2723
|
+
// TODO(v7): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node
|
|
2724
|
+
// versions(>= 8) as they had memory leaks when using it: #2555
|
|
2725
|
+
const agent = proxy
|
|
2726
|
+
? (new HttpsProxyAgent(proxy) )
|
|
2727
|
+
: new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 });
|
|
2728
|
+
|
|
2729
|
+
const requestExecutor = createRequestExecutor(options, _nullishCoalesce(options.httpModule, () => ( nativeHttpModule)), agent);
|
|
2730
|
+
return core.createTransport(options, requestExecutor);
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
/**
|
|
2734
|
+
* Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion.
|
|
2735
|
+
*
|
|
2736
|
+
* @param transportUrl The URL the transport intends to send events to.
|
|
2737
|
+
* @param proxy The client configured proxy.
|
|
2738
|
+
* @returns A proxy the transport should use.
|
|
2739
|
+
*/
|
|
2740
|
+
function applyNoProxyOption(transportUrlSegments, proxy) {
|
|
2741
|
+
const { no_proxy } = process.env;
|
|
2742
|
+
|
|
2743
|
+
const urlIsExemptFromProxy =
|
|
2744
|
+
no_proxy &&
|
|
2745
|
+
no_proxy
|
|
2746
|
+
.split(',')
|
|
2747
|
+
.some(
|
|
2748
|
+
exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption),
|
|
2749
|
+
);
|
|
2750
|
+
|
|
2751
|
+
if (urlIsExemptFromProxy) {
|
|
2752
|
+
return undefined;
|
|
2753
|
+
} else {
|
|
2754
|
+
return proxy;
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
/**
|
|
2759
|
+
* Creates a RequestExecutor to be used with `createTransport`.
|
|
2760
|
+
*/
|
|
2761
|
+
function createRequestExecutor(
|
|
2762
|
+
options,
|
|
2763
|
+
httpModule,
|
|
2764
|
+
agent,
|
|
2765
|
+
) {
|
|
2766
|
+
const { hostname, pathname, port, protocol, search } = new URL(options.url);
|
|
2767
|
+
return function makeRequest(request) {
|
|
2768
|
+
return new Promise((resolve, reject) => {
|
|
2769
|
+
let body = streamFromBody(request.body);
|
|
2770
|
+
|
|
2771
|
+
const headers = { ...options.headers };
|
|
2772
|
+
|
|
2773
|
+
if (request.body.length > GZIP_THRESHOLD) {
|
|
2774
|
+
headers['content-encoding'] = 'gzip';
|
|
2775
|
+
body = body.pipe(zlib.createGzip());
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
const req = httpModule.request(
|
|
2779
|
+
{
|
|
2780
|
+
method: 'POST',
|
|
2781
|
+
agent,
|
|
2782
|
+
headers,
|
|
2783
|
+
hostname,
|
|
2784
|
+
path: `${pathname}${search}`,
|
|
2785
|
+
port,
|
|
2786
|
+
protocol,
|
|
2787
|
+
ca: options.caCerts,
|
|
2788
|
+
},
|
|
2789
|
+
res => {
|
|
2790
|
+
res.on('data', () => {
|
|
2791
|
+
// Drain socket
|
|
2792
|
+
});
|
|
2793
|
+
|
|
2794
|
+
res.on('end', () => {
|
|
2795
|
+
// Drain socket
|
|
2796
|
+
});
|
|
2797
|
+
|
|
2798
|
+
res.setEncoding('utf8');
|
|
2799
|
+
|
|
2800
|
+
// "Key-value pairs of header names and values. Header names are lower-cased."
|
|
2801
|
+
// https://nodejs.org/api/http.html#http_message_headers
|
|
2802
|
+
const retryAfterHeader = _nullishCoalesce(res.headers['retry-after'], () => ( null));
|
|
2803
|
+
const rateLimitsHeader = _nullishCoalesce(res.headers['x-sentry-rate-limits'], () => ( null));
|
|
2804
|
+
|
|
2805
|
+
resolve({
|
|
2806
|
+
statusCode: res.statusCode,
|
|
2807
|
+
headers: {
|
|
2808
|
+
'retry-after': retryAfterHeader,
|
|
2809
|
+
'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] : rateLimitsHeader,
|
|
2810
|
+
},
|
|
2811
|
+
});
|
|
2812
|
+
},
|
|
2813
|
+
);
|
|
2814
|
+
|
|
2815
|
+
req.on('error', reject);
|
|
2816
|
+
body.pipe(req);
|
|
2817
|
+
});
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
/** normalizes Windows paths */
|
|
2822
|
+
function normalizeWindowsPath(path) {
|
|
2823
|
+
return path
|
|
2824
|
+
.replace(/^[A-Z]:/, '') // remove Windows-style prefix
|
|
2825
|
+
.replace(/\\/g, '/'); // replace all `\` instances with `/`
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
/** Creates a function that gets the module name from a filename */
|
|
2829
|
+
function createGetModuleFromFilename(
|
|
2830
|
+
basePath = process.argv[1] ? utils.dirname(process.argv[1]) : process.cwd(),
|
|
2831
|
+
isWindows = path.sep === '\\',
|
|
2832
|
+
) {
|
|
2833
|
+
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
2834
|
+
|
|
2835
|
+
return (filename) => {
|
|
2836
|
+
if (!filename) {
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename;
|
|
2841
|
+
|
|
2842
|
+
// eslint-disable-next-line prefer-const
|
|
2843
|
+
let { dir, base: file, ext } = path.posix.parse(normalizedFilename);
|
|
2844
|
+
|
|
2845
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
2846
|
+
file = file.slice(0, ext.length * -1);
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
if (!dir) {
|
|
2850
|
+
// No dirname whatsoever
|
|
2851
|
+
dir = '.';
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
const n = dir.lastIndexOf('/node_modules');
|
|
2855
|
+
if (n > -1) {
|
|
2856
|
+
return `${dir.slice(n + 14).replace(/\//g, '.')}:${file}`;
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
// Let's see if it's a part of the main module
|
|
2860
|
+
// To be a part of main module, it has to share the same base
|
|
2861
|
+
if (dir.startsWith(normalizedBase)) {
|
|
2862
|
+
let moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, '.');
|
|
2863
|
+
|
|
2864
|
+
if (moduleName) {
|
|
2865
|
+
moduleName += ':';
|
|
2866
|
+
}
|
|
2867
|
+
moduleName += file;
|
|
2868
|
+
|
|
2869
|
+
return moduleName;
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
return file;
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
// PUBLIC APIS
|
|
2877
|
+
|
|
2878
|
+
|
|
2879
|
+
/** Get the currently active client. */
|
|
2880
|
+
function getClient() {
|
|
2881
|
+
const currentScope = core.getCurrentScope();
|
|
2882
|
+
|
|
2883
|
+
const client = currentScope.getClient();
|
|
2884
|
+
if (client) {
|
|
2885
|
+
return client ;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
// TODO otherwise ensure we use a noop client
|
|
2889
|
+
return {} ;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
/**
|
|
2893
|
+
* Returns a release dynamically from environment variables.
|
|
2894
|
+
*/
|
|
2895
|
+
function getSentryRelease(fallback) {
|
|
2896
|
+
// Always read first as Sentry takes this as precedence
|
|
2897
|
+
if (process.env.SENTRY_RELEASE) {
|
|
2898
|
+
return process.env.SENTRY_RELEASE;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
// This supports the variable that sentry-webpack-plugin injects
|
|
2902
|
+
if (utils.GLOBAL_OBJ.SENTRY_RELEASE && utils.GLOBAL_OBJ.SENTRY_RELEASE.id) {
|
|
2903
|
+
return utils.GLOBAL_OBJ.SENTRY_RELEASE.id;
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
return (
|
|
2907
|
+
// GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
|
|
2908
|
+
process.env.GITHUB_SHA ||
|
|
2909
|
+
// Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata
|
|
2910
|
+
process.env.COMMIT_REF ||
|
|
2911
|
+
// Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables
|
|
2912
|
+
process.env.VERCEL_GIT_COMMIT_SHA ||
|
|
2913
|
+
process.env.VERCEL_GITHUB_COMMIT_SHA ||
|
|
2914
|
+
process.env.VERCEL_GITLAB_COMMIT_SHA ||
|
|
2915
|
+
process.env.VERCEL_BITBUCKET_COMMIT_SHA ||
|
|
2916
|
+
// Zeit (now known as Vercel)
|
|
2917
|
+
process.env.ZEIT_GITHUB_COMMIT_SHA ||
|
|
2918
|
+
process.env.ZEIT_GITLAB_COMMIT_SHA ||
|
|
2919
|
+
process.env.ZEIT_BITBUCKET_COMMIT_SHA ||
|
|
2920
|
+
// Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables
|
|
2921
|
+
process.env.CF_PAGES_COMMIT_SHA ||
|
|
2922
|
+
fallback
|
|
2923
|
+
);
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
/** Node.js stack parser */
|
|
2927
|
+
const defaultStackParser = utils.createStackParser(utils.nodeStackLineParser(createGetModuleFromFilename()));
|
|
2928
|
+
|
|
2929
|
+
/**
|
|
2930
|
+
* This method is a noop and only here to ensure vite-plugin v0.6.0 (which is used by Sveltekit) still works,
|
|
2931
|
+
* as that uses this.
|
|
2932
|
+
*
|
|
2933
|
+
* @deprecated This will be removed before v8 is finalized.
|
|
2934
|
+
*/
|
|
2935
|
+
function makeMain(hub) {
|
|
2936
|
+
// noop
|
|
2937
|
+
return hub;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2941
|
+
/** A client for using Sentry with Node & OpenTelemetry. */
|
|
2942
|
+
class NodeClient extends core.ServerRuntimeClient {
|
|
2943
|
+
|
|
2944
|
+
constructor(options) {
|
|
2945
|
+
const clientOptions = {
|
|
2946
|
+
...options,
|
|
2947
|
+
platform: 'node',
|
|
2948
|
+
runtime: { name: 'node', version: global.process.version },
|
|
2949
|
+
serverName: options.serverName || global.process.env.SENTRY_NAME || os.hostname(),
|
|
2950
|
+
};
|
|
2951
|
+
|
|
2952
|
+
core.applySdkMetadata(clientOptions, 'node');
|
|
2953
|
+
|
|
2954
|
+
super(clientOptions);
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
/** Get the OTEL tracer. */
|
|
2958
|
+
get tracer() {
|
|
2959
|
+
if (this._tracer) {
|
|
2960
|
+
return this._tracer;
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
const name = '@sentry/node';
|
|
2964
|
+
const version = core.SDK_VERSION;
|
|
2965
|
+
const tracer = api.trace.getTracer(name, version);
|
|
2966
|
+
this._tracer = tracer;
|
|
2967
|
+
|
|
2968
|
+
return tracer;
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
/**
|
|
2972
|
+
* @inheritDoc
|
|
2973
|
+
*/
|
|
2974
|
+
async flush(timeout) {
|
|
2975
|
+
const provider = this.traceProvider;
|
|
2976
|
+
const spanProcessor = _optionalChain$1([provider, 'optionalAccess', _ => _.activeSpanProcessor]);
|
|
2977
|
+
|
|
2978
|
+
if (spanProcessor) {
|
|
2979
|
+
await spanProcessor.forceFlush();
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
return super.flush(timeout);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
/**
|
|
2987
|
+
* This is a custom ContextManager for OpenTelemetry, which extends the default AsyncLocalStorageContextManager.
|
|
2988
|
+
* It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Hub are always in sync.
|
|
2989
|
+
*
|
|
2990
|
+
* Note that we currently only support AsyncHooks with this,
|
|
2991
|
+
* but since this should work for Node 14+ anyhow that should be good enough.
|
|
2992
|
+
*/
|
|
2993
|
+
const SentryContextManager = opentelemetry.wrapContextManagerClass(contextAsyncHooks.AsyncLocalStorageContextManager);
|
|
2994
|
+
|
|
2995
|
+
/**
|
|
2996
|
+
* Initialize OpenTelemetry for Node.
|
|
2997
|
+
*/
|
|
2998
|
+
function initOtel() {
|
|
2999
|
+
const client = getClient();
|
|
3000
|
+
|
|
3001
|
+
if (!client) {
|
|
3002
|
+
DEBUG_BUILD &&
|
|
3003
|
+
utils.logger.warn(
|
|
3004
|
+
'No client available, skipping OpenTelemetry setup. This probably means that `Sentry.init()` was not called before `initOtel()`.',
|
|
3005
|
+
);
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
if (client.getOptions().debug) {
|
|
3010
|
+
const otelLogger = new Proxy(utils.logger , {
|
|
3011
|
+
get(target, prop, receiver) {
|
|
3012
|
+
const actualProp = prop === 'verbose' ? 'debug' : prop;
|
|
3013
|
+
return Reflect.get(target, actualProp, receiver);
|
|
3014
|
+
},
|
|
3015
|
+
});
|
|
3016
|
+
|
|
3017
|
+
api.diag.setLogger(otelLogger, api.DiagLogLevel.DEBUG);
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
opentelemetry.setupEventContextTrace(client);
|
|
3021
|
+
|
|
3022
|
+
const provider = setupOtel(client);
|
|
3023
|
+
client.traceProvider = provider;
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
/** Just exported for tests. */
|
|
3027
|
+
function setupOtel(client) {
|
|
3028
|
+
// Create and configure NodeTracerProvider
|
|
3029
|
+
const provider = new sdkTraceBase.BasicTracerProvider({
|
|
3030
|
+
sampler: new opentelemetry.SentrySampler(client),
|
|
3031
|
+
resource: new resources.Resource({
|
|
3032
|
+
[semanticConventions.SemanticResourceAttributes.SERVICE_NAME]: 'node',
|
|
3033
|
+
[semanticConventions.SemanticResourceAttributes.SERVICE_NAMESPACE]: 'sentry',
|
|
3034
|
+
[semanticConventions.SemanticResourceAttributes.SERVICE_VERSION]: core.SDK_VERSION,
|
|
3035
|
+
}),
|
|
3036
|
+
forceFlushTimeoutMillis: 500,
|
|
3037
|
+
});
|
|
3038
|
+
provider.addSpanProcessor(new opentelemetry.SentrySpanProcessor());
|
|
3039
|
+
|
|
3040
|
+
// Initialize the provider
|
|
3041
|
+
provider.register({
|
|
3042
|
+
propagator: new opentelemetry.SentryPropagator(),
|
|
3043
|
+
contextManager: new SentryContextManager(),
|
|
3044
|
+
});
|
|
3045
|
+
|
|
3046
|
+
return provider;
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
function getCjsOnlyIntegrations(isCjs = typeof require !== 'undefined') {
|
|
3050
|
+
return isCjs ? [modulesIntegration()] : [];
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
/** Get the default integrations for the Node Experimental SDK. */
|
|
3054
|
+
function getDefaultIntegrations(options) {
|
|
3055
|
+
return [
|
|
3056
|
+
// Common
|
|
3057
|
+
core.inboundFiltersIntegration(),
|
|
3058
|
+
core.functionToStringIntegration(),
|
|
3059
|
+
core.linkedErrorsIntegration(),
|
|
3060
|
+
core.requestDataIntegration(),
|
|
3061
|
+
// Native Wrappers
|
|
3062
|
+
consoleIntegration(),
|
|
3063
|
+
httpIntegration(),
|
|
3064
|
+
nativeNodeFetchIntegration(),
|
|
3065
|
+
// Global Handlers
|
|
3066
|
+
onUncaughtExceptionIntegration(),
|
|
3067
|
+
onUnhandledRejectionIntegration(),
|
|
3068
|
+
// Event Info
|
|
3069
|
+
contextLinesIntegration(),
|
|
3070
|
+
localVariablesIntegration(),
|
|
3071
|
+
nodeContextIntegration(),
|
|
3072
|
+
httpIntegration(),
|
|
3073
|
+
...getCjsOnlyIntegrations(),
|
|
3074
|
+
...(core.hasTracingEnabled(options) ? getAutoPerformanceIntegrations() : []),
|
|
3075
|
+
];
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
/**
|
|
3079
|
+
* Initialize Sentry for Node.
|
|
3080
|
+
*/
|
|
3081
|
+
function init(options = {}) {
|
|
3082
|
+
const clientOptions = getClientOptions(options);
|
|
3083
|
+
|
|
3084
|
+
if (clientOptions.debug === true) {
|
|
3085
|
+
if (DEBUG_BUILD) {
|
|
3086
|
+
utils.logger.enable();
|
|
3087
|
+
} else {
|
|
3088
|
+
// use `console.warn` rather than `logger.warn` since by non-debug bundles have all `logger.x` statements stripped
|
|
3089
|
+
utils.consoleSandbox(() => {
|
|
3090
|
+
// eslint-disable-next-line no-console
|
|
3091
|
+
console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
opentelemetry.setOpenTelemetryContextAsyncContextStrategy();
|
|
3097
|
+
|
|
3098
|
+
const scope = core.getCurrentScope();
|
|
3099
|
+
scope.update(options.initialScope);
|
|
3100
|
+
|
|
3101
|
+
const client = new NodeClient(clientOptions);
|
|
3102
|
+
// The client is on the current scope, from where it generally is inherited
|
|
3103
|
+
core.getCurrentScope().setClient(client);
|
|
3104
|
+
|
|
3105
|
+
if (isEnabled(client)) {
|
|
3106
|
+
client.init();
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
if (options.autoSessionTracking) {
|
|
3110
|
+
startSessionTracking();
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
updateScopeFromEnvVariables();
|
|
3114
|
+
|
|
3115
|
+
if (options.spotlight) {
|
|
3116
|
+
// force integrations to be setup even if no DSN was set
|
|
3117
|
+
// If they have already been added before, they will be ignored anyhow
|
|
3118
|
+
const integrations = client.getOptions().integrations;
|
|
3119
|
+
for (const integration of integrations) {
|
|
3120
|
+
client.addIntegration(integration);
|
|
3121
|
+
}
|
|
3122
|
+
client.addIntegration(
|
|
3123
|
+
spotlightIntegration({
|
|
3124
|
+
sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,
|
|
3125
|
+
}),
|
|
3126
|
+
);
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
// If users opt-out of this, they _have_ to set up OpenTelemetry themselves
|
|
3130
|
+
// There is no way to use this SDK without OpenTelemetry!
|
|
3131
|
+
if (!options.skipOpenTelemetrySetup) {
|
|
3132
|
+
initOtel();
|
|
3133
|
+
}
|
|
3134
|
+
|
|
3135
|
+
validateOpenTelemetrySetup();
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
function validateOpenTelemetrySetup() {
|
|
3139
|
+
if (!DEBUG_BUILD) {
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
const setup = opentelemetry.openTelemetrySetupCheck();
|
|
3144
|
+
|
|
3145
|
+
const required = ['SentrySpanProcessor', 'SentryContextManager', 'SentryPropagator'] ;
|
|
3146
|
+
for (const k of required) {
|
|
3147
|
+
if (!setup.includes(k)) {
|
|
3148
|
+
utils.logger.error(
|
|
3149
|
+
`You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`,
|
|
3150
|
+
);
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
if (!setup.includes('SentrySampler')) {
|
|
3155
|
+
utils.logger.warn(
|
|
3156
|
+
'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected.',
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
function getClientOptions(options) {
|
|
3162
|
+
if (options.defaultIntegrations === undefined) {
|
|
3163
|
+
options.defaultIntegrations = getDefaultIntegrations(options);
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
const release = getRelease(options.release);
|
|
3167
|
+
|
|
3168
|
+
const autoSessionTracking =
|
|
3169
|
+
typeof release !== 'string'
|
|
3170
|
+
? false
|
|
3171
|
+
: options.autoSessionTracking === undefined
|
|
3172
|
+
? true
|
|
3173
|
+
: options.autoSessionTracking;
|
|
3174
|
+
|
|
3175
|
+
const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);
|
|
3176
|
+
|
|
3177
|
+
const baseOptions = utils.dropUndefinedKeys({
|
|
3178
|
+
transport: makeNodeTransport,
|
|
3179
|
+
dsn: process.env.SENTRY_DSN,
|
|
3180
|
+
environment: process.env.SENTRY_ENVIRONMENT,
|
|
3181
|
+
});
|
|
3182
|
+
|
|
3183
|
+
const overwriteOptions = utils.dropUndefinedKeys({
|
|
3184
|
+
release,
|
|
3185
|
+
autoSessionTracking,
|
|
3186
|
+
tracesSampleRate,
|
|
3187
|
+
});
|
|
3188
|
+
|
|
3189
|
+
const clientOptions = {
|
|
3190
|
+
...baseOptions,
|
|
3191
|
+
...options,
|
|
3192
|
+
...overwriteOptions,
|
|
3193
|
+
stackParser: utils.stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
|
|
3194
|
+
integrations: core.getIntegrationsToSetup({
|
|
3195
|
+
defaultIntegrations: options.defaultIntegrations,
|
|
3196
|
+
integrations: options.integrations,
|
|
3197
|
+
}),
|
|
3198
|
+
};
|
|
3199
|
+
|
|
3200
|
+
return clientOptions;
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
function getRelease(release) {
|
|
3204
|
+
if (release !== undefined) {
|
|
3205
|
+
return release;
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
const detectedRelease = getSentryRelease();
|
|
3209
|
+
if (detectedRelease !== undefined) {
|
|
3210
|
+
return detectedRelease;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
return undefined;
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
function getTracesSampleRate(tracesSampleRate) {
|
|
3217
|
+
if (tracesSampleRate !== undefined) {
|
|
3218
|
+
return tracesSampleRate;
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;
|
|
3222
|
+
if (!sampleRateFromEnv) {
|
|
3223
|
+
return undefined;
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
const parsed = parseFloat(sampleRateFromEnv);
|
|
3227
|
+
return isFinite(parsed) ? parsed : undefined;
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
/**
|
|
3231
|
+
* Update scope and propagation context based on environmental variables.
|
|
3232
|
+
*
|
|
3233
|
+
* See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md
|
|
3234
|
+
* for more details.
|
|
3235
|
+
*/
|
|
3236
|
+
function updateScopeFromEnvVariables() {
|
|
3237
|
+
const sentryUseEnvironment = (process.env.SENTRY_USE_ENVIRONMENT || '').toLowerCase();
|
|
3238
|
+
if (!['false', 'n', 'no', 'off', '0'].includes(sentryUseEnvironment)) {
|
|
3239
|
+
const sentryTraceEnv = process.env.SENTRY_TRACE;
|
|
3240
|
+
const baggageEnv = process.env.SENTRY_BAGGAGE;
|
|
3241
|
+
const propagationContext = utils.propagationContextFromHeaders(sentryTraceEnv, baggageEnv);
|
|
3242
|
+
core.getCurrentScope().setPropagationContext(propagationContext);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
/**
|
|
3247
|
+
* Enable automatic Session Tracking for the node process.
|
|
3248
|
+
*/
|
|
3249
|
+
function startSessionTracking() {
|
|
3250
|
+
const client = core.getClient();
|
|
3251
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
3252
|
+
client.initSessionFlusher();
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
core.startSession();
|
|
3256
|
+
|
|
3257
|
+
// Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because
|
|
3258
|
+
// The 'beforeExit' event is not emitted for conditions causing explicit termination,
|
|
3259
|
+
// such as calling process.exit() or uncaught exceptions.
|
|
3260
|
+
// Ref: https://nodejs.org/api/process.html#process_event_beforeexit
|
|
3261
|
+
process.on('beforeExit', () => {
|
|
3262
|
+
const session = core.getIsolationScope().getSession();
|
|
3263
|
+
|
|
3264
|
+
// Only call endSession, if the Session exists on Scope and SessionStatus is not a
|
|
3265
|
+
// Terminal Status i.e. Exited or Crashed because
|
|
3266
|
+
// "When a session is moved away from ok it must not be updated anymore."
|
|
3267
|
+
// Ref: https://develop.sentry.dev/sdk/sessions/
|
|
3268
|
+
if (session && session.status !== 'ok') {
|
|
3269
|
+
core.endSession();
|
|
3270
|
+
}
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
function isEnabled(client) {
|
|
3275
|
+
return client.getOptions().enabled !== false && client.getTransport() !== undefined;
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
/**
|
|
3279
|
+
* This is for legacy reasons, and returns a proxy object instead of a hub to be used.
|
|
3280
|
+
* @deprecated Use the methods directly.
|
|
3281
|
+
*/
|
|
3282
|
+
function getCurrentHub() {
|
|
3283
|
+
return {
|
|
3284
|
+
isOlderThan(_version) {
|
|
3285
|
+
return false;
|
|
3286
|
+
},
|
|
3287
|
+
|
|
3288
|
+
bindClient(client) {
|
|
3289
|
+
const scope = core.getCurrentScope();
|
|
3290
|
+
scope.setClient(client);
|
|
3291
|
+
},
|
|
3292
|
+
|
|
3293
|
+
pushScope() {
|
|
3294
|
+
// TODO: This does not work and is actually deprecated
|
|
3295
|
+
return core.getCurrentScope();
|
|
3296
|
+
},
|
|
3297
|
+
|
|
3298
|
+
popScope() {
|
|
3299
|
+
// TODO: This does not work and is actually deprecated
|
|
3300
|
+
return false;
|
|
3301
|
+
},
|
|
3302
|
+
|
|
3303
|
+
withScope: core.withScope,
|
|
3304
|
+
getClient: () => getClient() ,
|
|
3305
|
+
getScope: core.getCurrentScope,
|
|
3306
|
+
getIsolationScope: core.getIsolationScope,
|
|
3307
|
+
captureException: (exception, hint) => {
|
|
3308
|
+
return core.getCurrentScope().captureException(exception, hint);
|
|
3309
|
+
},
|
|
3310
|
+
captureMessage: (message, level, hint) => {
|
|
3311
|
+
return core.getCurrentScope().captureMessage(message, level, hint);
|
|
3312
|
+
},
|
|
3313
|
+
captureEvent: core.captureEvent,
|
|
3314
|
+
addBreadcrumb: core.addBreadcrumb,
|
|
3315
|
+
setUser: core.setUser,
|
|
3316
|
+
setTags: core.setTags,
|
|
3317
|
+
setTag: core.setTag,
|
|
3318
|
+
setExtra: core.setExtra,
|
|
3319
|
+
setExtras: core.setExtras,
|
|
3320
|
+
setContext: core.setContext,
|
|
3321
|
+
|
|
3322
|
+
getIntegration(integration) {
|
|
3323
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3324
|
+
return getClient().getIntegration(integration);
|
|
3325
|
+
},
|
|
3326
|
+
|
|
3327
|
+
startSession: core.startSession,
|
|
3328
|
+
|
|
3329
|
+
endSession: core.endSession,
|
|
3330
|
+
|
|
3331
|
+
captureSession(endSession) {
|
|
3332
|
+
// both send the update and pull the session from the scope
|
|
3333
|
+
if (endSession) {
|
|
3334
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3335
|
+
return this.endSession();
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
// only send the update
|
|
3339
|
+
_sendSessionUpdate();
|
|
3340
|
+
},
|
|
3341
|
+
|
|
3342
|
+
shouldSendDefaultPii() {
|
|
3343
|
+
const client = getClient();
|
|
3344
|
+
const options = client.getOptions();
|
|
3345
|
+
return Boolean(options.sendDefaultPii);
|
|
3346
|
+
},
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
/**
|
|
3351
|
+
* Sends the current Session on the scope
|
|
3352
|
+
*/
|
|
3353
|
+
function _sendSessionUpdate() {
|
|
3354
|
+
const scope = core.getCurrentScope();
|
|
3355
|
+
const client = getClient();
|
|
3356
|
+
|
|
3357
|
+
const session = scope.getSession();
|
|
3358
|
+
if (session) {
|
|
3359
|
+
client.captureSession(session);
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
const replacements = [
|
|
3364
|
+
['january', '1'],
|
|
3365
|
+
['february', '2'],
|
|
3366
|
+
['march', '3'],
|
|
3367
|
+
['april', '4'],
|
|
3368
|
+
['may', '5'],
|
|
3369
|
+
['june', '6'],
|
|
3370
|
+
['july', '7'],
|
|
3371
|
+
['august', '8'],
|
|
3372
|
+
['september', '9'],
|
|
3373
|
+
['october', '10'],
|
|
3374
|
+
['november', '11'],
|
|
3375
|
+
['december', '12'],
|
|
3376
|
+
['jan', '1'],
|
|
3377
|
+
['feb', '2'],
|
|
3378
|
+
['mar', '3'],
|
|
3379
|
+
['apr', '4'],
|
|
3380
|
+
['may', '5'],
|
|
3381
|
+
['jun', '6'],
|
|
3382
|
+
['jul', '7'],
|
|
3383
|
+
['aug', '8'],
|
|
3384
|
+
['sep', '9'],
|
|
3385
|
+
['oct', '10'],
|
|
3386
|
+
['nov', '11'],
|
|
3387
|
+
['dec', '12'],
|
|
3388
|
+
['sunday', '0'],
|
|
3389
|
+
['monday', '1'],
|
|
3390
|
+
['tuesday', '2'],
|
|
3391
|
+
['wednesday', '3'],
|
|
3392
|
+
['thursday', '4'],
|
|
3393
|
+
['friday', '5'],
|
|
3394
|
+
['saturday', '6'],
|
|
3395
|
+
['sun', '0'],
|
|
3396
|
+
['mon', '1'],
|
|
3397
|
+
['tue', '2'],
|
|
3398
|
+
['wed', '3'],
|
|
3399
|
+
['thu', '4'],
|
|
3400
|
+
['fri', '5'],
|
|
3401
|
+
['sat', '6'],
|
|
3402
|
+
];
|
|
3403
|
+
|
|
3404
|
+
/**
|
|
3405
|
+
* Replaces names in cron expressions
|
|
3406
|
+
*/
|
|
3407
|
+
function replaceCronNames(cronExpression) {
|
|
3408
|
+
return replacements.reduce(
|
|
3409
|
+
// eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor
|
|
3410
|
+
(acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement),
|
|
3411
|
+
cronExpression,
|
|
3412
|
+
);
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
const ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string';
|
|
3416
|
+
|
|
3417
|
+
/**
|
|
3418
|
+
* Instruments the `cron` library to send a check-in event to Sentry for each job execution.
|
|
3419
|
+
*
|
|
3420
|
+
* ```ts
|
|
3421
|
+
* import * as Sentry from '@sentry/node';
|
|
3422
|
+
* import { CronJob } from 'cron';
|
|
3423
|
+
*
|
|
3424
|
+
* const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job');
|
|
3425
|
+
*
|
|
3426
|
+
* // use the constructor
|
|
3427
|
+
* const job = new CronJobWithCheckIn('* * * * *', () => {
|
|
3428
|
+
* console.log('You will see this message every minute');
|
|
3429
|
+
* });
|
|
3430
|
+
*
|
|
3431
|
+
* // or from
|
|
3432
|
+
* const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => {
|
|
3433
|
+
* console.log('You will see this message every minute');
|
|
3434
|
+
* });
|
|
3435
|
+
* ```
|
|
3436
|
+
*/
|
|
3437
|
+
function instrumentCron(lib, monitorSlug) {
|
|
3438
|
+
let jobScheduled = false;
|
|
3439
|
+
|
|
3440
|
+
return new Proxy(lib, {
|
|
3441
|
+
construct(target, args) {
|
|
3442
|
+
const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args;
|
|
3443
|
+
|
|
3444
|
+
if (typeof cronTime !== 'string') {
|
|
3445
|
+
throw new Error(ERROR_TEXT);
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
if (jobScheduled) {
|
|
3449
|
+
throw new Error(`A job named '${monitorSlug}' has already been scheduled`);
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
jobScheduled = true;
|
|
3453
|
+
|
|
3454
|
+
const cronString = replaceCronNames(cronTime);
|
|
3455
|
+
|
|
3456
|
+
function monitoredTick(context, onComplete) {
|
|
3457
|
+
return core.withMonitor(
|
|
3458
|
+
monitorSlug,
|
|
3459
|
+
() => {
|
|
3460
|
+
return onTick(context, onComplete);
|
|
3461
|
+
},
|
|
3462
|
+
{
|
|
3463
|
+
schedule: { type: 'crontab', value: cronString },
|
|
3464
|
+
...(timeZone ? { timeZone } : {}),
|
|
3465
|
+
},
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3468
|
+
|
|
3469
|
+
return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest);
|
|
3470
|
+
},
|
|
3471
|
+
get(target, prop) {
|
|
3472
|
+
if (prop === 'from') {
|
|
3473
|
+
return (param) => {
|
|
3474
|
+
const { cronTime, onTick, timeZone } = param;
|
|
3475
|
+
|
|
3476
|
+
if (typeof cronTime !== 'string') {
|
|
3477
|
+
throw new Error(ERROR_TEXT);
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
if (jobScheduled) {
|
|
3481
|
+
throw new Error(`A job named '${monitorSlug}' has already been scheduled`);
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
jobScheduled = true;
|
|
3485
|
+
|
|
3486
|
+
const cronString = replaceCronNames(cronTime);
|
|
3487
|
+
|
|
3488
|
+
param.onTick = (context, onComplete) => {
|
|
3489
|
+
return core.withMonitor(
|
|
3490
|
+
monitorSlug,
|
|
3491
|
+
() => {
|
|
3492
|
+
return onTick(context, onComplete);
|
|
3493
|
+
},
|
|
3494
|
+
{
|
|
3495
|
+
schedule: { type: 'crontab', value: cronString },
|
|
3496
|
+
...(timeZone ? { timeZone } : {}),
|
|
3497
|
+
},
|
|
3498
|
+
);
|
|
3499
|
+
};
|
|
3500
|
+
|
|
3501
|
+
return target.from(param);
|
|
3502
|
+
};
|
|
3503
|
+
} else {
|
|
3504
|
+
return target[prop];
|
|
3505
|
+
}
|
|
3506
|
+
},
|
|
3507
|
+
});
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
/**
|
|
3511
|
+
* Wraps the `node-cron` library with check-in monitoring.
|
|
3512
|
+
*
|
|
3513
|
+
* ```ts
|
|
3514
|
+
* import * as Sentry from "@sentry/node";
|
|
3515
|
+
* import cron from "node-cron";
|
|
3516
|
+
*
|
|
3517
|
+
* const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron);
|
|
3518
|
+
*
|
|
3519
|
+
* cronWithCheckIn.schedule(
|
|
3520
|
+
* "* * * * *",
|
|
3521
|
+
* () => {
|
|
3522
|
+
* console.log("running a task every minute");
|
|
3523
|
+
* },
|
|
3524
|
+
* { name: "my-cron-job" },
|
|
3525
|
+
* );
|
|
3526
|
+
* ```
|
|
3527
|
+
*/
|
|
3528
|
+
function instrumentNodeCron(lib) {
|
|
3529
|
+
return new Proxy(lib, {
|
|
3530
|
+
get(target, prop) {
|
|
3531
|
+
if (prop === 'schedule' && target.schedule) {
|
|
3532
|
+
// When 'get' is called for schedule, return a proxied version of the schedule function
|
|
3533
|
+
return new Proxy(target.schedule, {
|
|
3534
|
+
apply(target, thisArg, argArray) {
|
|
3535
|
+
const [expression, , options] = argArray;
|
|
3536
|
+
|
|
3537
|
+
if (!_optionalChain([options, 'optionalAccess', _ => _.name])) {
|
|
3538
|
+
throw new Error('Missing "name" for scheduled job. A name is required for Sentry check-in monitoring.');
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
return core.withMonitor(
|
|
3542
|
+
options.name,
|
|
3543
|
+
() => {
|
|
3544
|
+
return target.apply(thisArg, argArray);
|
|
3545
|
+
},
|
|
3546
|
+
{
|
|
3547
|
+
schedule: { type: 'crontab', value: replaceCronNames(expression) },
|
|
3548
|
+
timezone: _optionalChain([options, 'optionalAccess', _2 => _2.timezone]),
|
|
3549
|
+
},
|
|
3550
|
+
);
|
|
3551
|
+
},
|
|
3552
|
+
});
|
|
3553
|
+
} else {
|
|
3554
|
+
return target[prop];
|
|
3555
|
+
}
|
|
3556
|
+
},
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3560
|
+
/**
|
|
3561
|
+
* Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution.
|
|
3562
|
+
*
|
|
3563
|
+
* ```ts
|
|
3564
|
+
* import * as Sentry from '@sentry/node';
|
|
3565
|
+
* import * as schedule from 'node-schedule';
|
|
3566
|
+
*
|
|
3567
|
+
* const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule);
|
|
3568
|
+
*
|
|
3569
|
+
* const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => {
|
|
3570
|
+
* console.log('You will see this message every minute');
|
|
3571
|
+
* });
|
|
3572
|
+
* ```
|
|
3573
|
+
*/
|
|
3574
|
+
function instrumentNodeSchedule(lib) {
|
|
3575
|
+
return new Proxy(lib, {
|
|
3576
|
+
get(target, prop) {
|
|
3577
|
+
if (prop === 'scheduleJob') {
|
|
3578
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
3579
|
+
return new Proxy(target.scheduleJob, {
|
|
3580
|
+
apply(target, thisArg, argArray) {
|
|
3581
|
+
const [nameOrExpression, expressionOrCallback] = argArray;
|
|
3582
|
+
|
|
3583
|
+
if (typeof nameOrExpression !== 'string' || typeof expressionOrCallback !== 'string') {
|
|
3584
|
+
throw new Error(
|
|
3585
|
+
"Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string",
|
|
3586
|
+
);
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
const monitorSlug = nameOrExpression;
|
|
3590
|
+
const expression = expressionOrCallback;
|
|
3591
|
+
|
|
3592
|
+
return core.withMonitor(
|
|
3593
|
+
monitorSlug,
|
|
3594
|
+
() => {
|
|
3595
|
+
return target.apply(thisArg, argArray);
|
|
3596
|
+
},
|
|
3597
|
+
{
|
|
3598
|
+
schedule: { type: 'crontab', value: replaceCronNames(expression) },
|
|
3599
|
+
},
|
|
3600
|
+
);
|
|
3601
|
+
},
|
|
3602
|
+
});
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
return target[prop];
|
|
3606
|
+
},
|
|
3607
|
+
});
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
/** Methods to instrument cron libraries for Sentry check-ins */
|
|
3611
|
+
const cron = {
|
|
3612
|
+
instrumentCron,
|
|
3613
|
+
instrumentNodeCron,
|
|
3614
|
+
instrumentNodeSchedule,
|
|
3615
|
+
};
|
|
37
3616
|
|
|
38
|
-
exports.httpIntegration = http.httpIntegration;
|
|
39
|
-
exports.nativeNodeFetchIntegration = nodeFetch.nativeNodeFetchIntegration;
|
|
40
|
-
exports.consoleIntegration = console.consoleIntegration;
|
|
41
|
-
exports.nodeContextIntegration = context.nodeContextIntegration;
|
|
42
|
-
exports.contextLinesIntegration = contextlines.contextLinesIntegration;
|
|
43
|
-
exports.localVariablesIntegration = index.localVariablesIntegration;
|
|
44
|
-
exports.modulesIntegration = modules.modulesIntegration;
|
|
45
|
-
exports.onUncaughtExceptionIntegration = onuncaughtexception.onUncaughtExceptionIntegration;
|
|
46
|
-
exports.onUnhandledRejectionIntegration = onunhandledrejection.onUnhandledRejectionIntegration;
|
|
47
|
-
exports.anrIntegration = index$1.anrIntegration;
|
|
48
|
-
exports.expressErrorHandler = express.expressErrorHandler;
|
|
49
|
-
exports.expressIntegration = express.expressIntegration;
|
|
50
|
-
exports.setupExpressErrorHandler = express.setupExpressErrorHandler;
|
|
51
|
-
exports.fastifyIntegration = fastify.fastifyIntegration;
|
|
52
|
-
exports.graphqlIntegration = graphql.graphqlIntegration;
|
|
53
|
-
exports.mongoIntegration = mongo.mongoIntegration;
|
|
54
|
-
exports.mongooseIntegration = mongoose.mongooseIntegration;
|
|
55
|
-
exports.mysqlIntegration = mysql.mysqlIntegration;
|
|
56
|
-
exports.mysql2Integration = mysql2.mysql2Integration;
|
|
57
|
-
exports.nestIntegration = nest.nestIntegration;
|
|
58
|
-
exports.postgresIntegration = postgres.postgresIntegration;
|
|
59
|
-
exports.prismaIntegration = prisma.prismaIntegration;
|
|
60
|
-
exports.hapiIntegration = index$2.hapiIntegration;
|
|
61
|
-
exports.setupHapiErrorHandler = index$2.setupHapiErrorHandler;
|
|
62
|
-
exports.getDefaultIntegrations = init.getDefaultIntegrations;
|
|
63
|
-
exports.init = init.init;
|
|
64
|
-
exports.getAutoPerformanceIntegrations = index$3.getAutoPerformanceIntegrations;
|
|
65
|
-
exports.defaultStackParser = api.defaultStackParser;
|
|
66
|
-
exports.getClient = api.getClient;
|
|
67
|
-
exports.getSentryRelease = api.getSentryRelease;
|
|
68
|
-
exports.makeMain = api.makeMain;
|
|
69
|
-
exports.createGetModuleFromFilename = module$1.createGetModuleFromFilename;
|
|
70
|
-
exports.makeNodeTransport = http$1.makeNodeTransport;
|
|
71
|
-
exports.NodeClient = client.NodeClient;
|
|
72
|
-
exports.getCurrentHub = hub.getCurrentHub;
|
|
73
|
-
exports.cron = index$4.cron;
|
|
74
|
-
exports.getActiveSpan = opentelemetry.getActiveSpan;
|
|
75
|
-
exports.startInactiveSpan = opentelemetry.startInactiveSpan;
|
|
76
|
-
exports.startSpan = opentelemetry.startSpan;
|
|
77
|
-
exports.startSpanManual = opentelemetry.startSpanManual;
|
|
78
|
-
exports.withActiveSpan = opentelemetry.withActiveSpan;
|
|
79
|
-
exports.DEFAULT_USER_INCLUDES = utils.DEFAULT_USER_INCLUDES;
|
|
80
|
-
exports.addRequestDataToEvent = utils.addRequestDataToEvent;
|
|
81
|
-
exports.extractRequestData = utils.extractRequestData;
|
|
82
3617
|
exports.Hub = core.Hub;
|
|
83
3618
|
exports.SDK_VERSION = core.SDK_VERSION;
|
|
84
3619
|
exports.SEMANTIC_ATTRIBUTE_SENTRY_OP = core.SEMANTIC_ATTRIBUTE_SENTRY_OP;
|
|
@@ -88,26 +3623,32 @@ exports.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE = core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE
|
|
|
88
3623
|
exports.Scope = core.Scope;
|
|
89
3624
|
exports.addBreadcrumb = core.addBreadcrumb;
|
|
90
3625
|
exports.addEventProcessor = core.addEventProcessor;
|
|
3626
|
+
exports.addIntegration = core.addIntegration;
|
|
91
3627
|
exports.captureCheckIn = core.captureCheckIn;
|
|
92
3628
|
exports.captureConsoleIntegration = core.captureConsoleIntegration;
|
|
93
3629
|
exports.captureEvent = core.captureEvent;
|
|
94
3630
|
exports.captureException = core.captureException;
|
|
95
3631
|
exports.captureMessage = core.captureMessage;
|
|
3632
|
+
exports.captureSession = core.captureSession;
|
|
96
3633
|
exports.close = core.close;
|
|
97
|
-
exports.continueTrace = core.continueTrace;
|
|
98
3634
|
exports.createTransport = core.createTransport;
|
|
99
3635
|
exports.debugIntegration = core.debugIntegration;
|
|
100
3636
|
exports.dedupeIntegration = core.dedupeIntegration;
|
|
3637
|
+
exports.endSession = core.endSession;
|
|
101
3638
|
exports.extraErrorDataIntegration = core.extraErrorDataIntegration;
|
|
102
3639
|
exports.flush = core.flush;
|
|
103
3640
|
exports.functionToStringIntegration = core.functionToStringIntegration;
|
|
3641
|
+
exports.getActiveSpan = core.getActiveSpan;
|
|
104
3642
|
exports.getCurrentScope = core.getCurrentScope;
|
|
105
3643
|
exports.getGlobalScope = core.getGlobalScope;
|
|
106
3644
|
exports.getIsolationScope = core.getIsolationScope;
|
|
3645
|
+
exports.getRootSpan = core.getRootSpan;
|
|
3646
|
+
exports.getSpanDescendants = core.getSpanDescendants;
|
|
107
3647
|
exports.getSpanStatusFromHttpCode = core.getSpanStatusFromHttpCode;
|
|
108
3648
|
exports.inboundFiltersIntegration = core.inboundFiltersIntegration;
|
|
109
3649
|
exports.isInitialized = core.isInitialized;
|
|
110
3650
|
exports.linkedErrorsIntegration = core.linkedErrorsIntegration;
|
|
3651
|
+
exports.metrics = core.metricsDefault;
|
|
111
3652
|
exports.parameterize = core.parameterize;
|
|
112
3653
|
exports.requestDataIntegration = core.requestDataIntegration;
|
|
113
3654
|
exports.rewriteFramesIntegration = core.rewriteFramesIntegration;
|
|
@@ -121,7 +3662,54 @@ exports.setMeasurement = core.setMeasurement;
|
|
|
121
3662
|
exports.setTag = core.setTag;
|
|
122
3663
|
exports.setTags = core.setTags;
|
|
123
3664
|
exports.setUser = core.setUser;
|
|
3665
|
+
exports.startInactiveSpan = core.startInactiveSpan;
|
|
3666
|
+
exports.startSession = core.startSession;
|
|
3667
|
+
exports.startSpan = core.startSpan;
|
|
3668
|
+
exports.startSpanManual = core.startSpanManual;
|
|
3669
|
+
exports.withActiveSpan = core.withActiveSpan;
|
|
124
3670
|
exports.withIsolationScope = core.withIsolationScope;
|
|
125
3671
|
exports.withMonitor = core.withMonitor;
|
|
126
3672
|
exports.withScope = core.withScope;
|
|
3673
|
+
exports.continueTrace = opentelemetry.continueTrace;
|
|
3674
|
+
exports.DEFAULT_USER_INCLUDES = utils.DEFAULT_USER_INCLUDES;
|
|
3675
|
+
exports.addRequestDataToEvent = utils.addRequestDataToEvent;
|
|
3676
|
+
exports.extractRequestData = utils.extractRequestData;
|
|
3677
|
+
exports.NodeClient = NodeClient;
|
|
3678
|
+
exports.anrIntegration = anrIntegration;
|
|
3679
|
+
exports.consoleIntegration = consoleIntegration;
|
|
3680
|
+
exports.contextLinesIntegration = contextLinesIntegration;
|
|
3681
|
+
exports.createGetModuleFromFilename = createGetModuleFromFilename;
|
|
3682
|
+
exports.cron = cron;
|
|
3683
|
+
exports.defaultStackParser = defaultStackParser;
|
|
3684
|
+
exports.expressErrorHandler = expressErrorHandler;
|
|
3685
|
+
exports.expressIntegration = expressIntegration;
|
|
3686
|
+
exports.fastifyIntegration = fastifyIntegration;
|
|
3687
|
+
exports.getAutoPerformanceIntegrations = getAutoPerformanceIntegrations;
|
|
3688
|
+
exports.getClient = getClient;
|
|
3689
|
+
exports.getCurrentHub = getCurrentHub;
|
|
3690
|
+
exports.getDefaultIntegrations = getDefaultIntegrations;
|
|
3691
|
+
exports.getSentryRelease = getSentryRelease;
|
|
3692
|
+
exports.graphqlIntegration = graphqlIntegration;
|
|
3693
|
+
exports.hapiIntegration = hapiIntegration;
|
|
3694
|
+
exports.httpIntegration = httpIntegration;
|
|
3695
|
+
exports.init = init;
|
|
3696
|
+
exports.localVariablesIntegration = localVariablesIntegration;
|
|
3697
|
+
exports.makeMain = makeMain;
|
|
3698
|
+
exports.makeNodeTransport = makeNodeTransport;
|
|
3699
|
+
exports.modulesIntegration = modulesIntegration;
|
|
3700
|
+
exports.mongoIntegration = mongoIntegration;
|
|
3701
|
+
exports.mongooseIntegration = mongooseIntegration;
|
|
3702
|
+
exports.mysql2Integration = mysql2Integration;
|
|
3703
|
+
exports.mysqlIntegration = mysqlIntegration;
|
|
3704
|
+
exports.nativeNodeFetchIntegration = nativeNodeFetchIntegration;
|
|
3705
|
+
exports.nestIntegration = nestIntegration;
|
|
3706
|
+
exports.nodeContextIntegration = nodeContextIntegration;
|
|
3707
|
+
exports.onUncaughtExceptionIntegration = onUncaughtExceptionIntegration;
|
|
3708
|
+
exports.onUnhandledRejectionIntegration = onUnhandledRejectionIntegration;
|
|
3709
|
+
exports.postgresIntegration = postgresIntegration;
|
|
3710
|
+
exports.prismaIntegration = prismaIntegration;
|
|
3711
|
+
exports.setupExpressErrorHandler = setupExpressErrorHandler;
|
|
3712
|
+
exports.setupFastifyErrorHandler = setupFastifyErrorHandler;
|
|
3713
|
+
exports.setupHapiErrorHandler = setupHapiErrorHandler;
|
|
3714
|
+
exports.spotlightIntegration = spotlightIntegration;
|
|
127
3715
|
//# sourceMappingURL=index.js.map
|