From 8567061992be35ad457eeff571329c8c4711634c Mon Sep 17 00:00:00 2001 From: Olivier Albertini Date: Thu, 3 Oct 2019 17:43:09 -0400 Subject: [PATCH] feat(plugin-https): patch https requests (#379) * feat(plugin-https): patch https requests closes #375 add tests Signed-off-by: Olivier Albertini * docs(plugin-https): add jaeger image Signed-off-by: Olivier Albertini * fix: add mayurkale22 recommendations Signed-off-by: Olivier Albertini * fix: add markwolff recommendations Signed-off-by: Olivier Albertini * fix: file name utils * fix: add danielkhan and bg451 recommendations Signed-off-by: Olivier Albertini --- examples/https/README.md | 77 +++ examples/https/client.js | 42 ++ examples/https/images/jaeger-ui.png | Bin 0 -> 49369 bytes examples/https/images/zipkin-ui.png | Bin 0 -> 50422 bytes examples/https/package.json | 43 ++ examples/https/server-cert.pem | 11 + examples/https/server-key.pem | 15 + examples/https/server.js | 61 +++ examples/https/setup.js | 32 ++ packages/opentelemetry-plugin-http/README.md | 2 +- .../opentelemetry-plugin-http/src/http.ts | 30 +- .../test/functionals/http-enable.test.ts | 11 +- .../test/functionals/http-package.test.ts | 1 + .../test/integrations/http-enable.test.ts | 7 +- .../test/utils/assertSpan.ts | 4 +- .../test/utils/{Utils.ts => utils.ts} | 20 +- packages/opentelemetry-plugin-https/README.md | 37 +- .../opentelemetry-plugin-https/package.json | 34 +- .../opentelemetry-plugin-https/src/https.ts | 143 ++++++ .../opentelemetry-plugin-https/src/index.ts | 2 + .../opentelemetry-plugin-https/src/utils.ts | 23 + .../test/fixtures/google.json | 43 ++ .../test/fixtures/server-cert.pem | 11 + .../test/fixtures/server-key.pem | 15 + .../test/functionals/https-disable.test.ts | 95 ++++ .../test/functionals/https-enable.test.ts | 481 ++++++++++++++++++ .../test/functionals/https-package.test.ts | 139 +++++ .../test/integrations/https-enable.test.ts | 227 +++++++++ .../test/utils/DummyPropagation.ts | 36 ++ .../test/utils/assertSpan.ts | 99 ++++ .../test/utils/httpsRequest.ts | 74 +++ .../test/utils/utils.ts | 27 + 32 files changed, 1798 insertions(+), 44 deletions(-) create mode 100644 examples/https/README.md create mode 100644 examples/https/client.js create mode 100644 examples/https/images/jaeger-ui.png create mode 100644 examples/https/images/zipkin-ui.png create mode 100644 examples/https/package.json create mode 100644 examples/https/server-cert.pem create mode 100644 examples/https/server-key.pem create mode 100644 examples/https/server.js create mode 100644 examples/https/setup.js rename packages/opentelemetry-plugin-http/test/utils/{Utils.ts => utils.ts} (72%) create mode 100644 packages/opentelemetry-plugin-https/src/https.ts create mode 100644 packages/opentelemetry-plugin-https/src/utils.ts create mode 100644 packages/opentelemetry-plugin-https/test/fixtures/google.json create mode 100644 packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem create mode 100644 packages/opentelemetry-plugin-https/test/fixtures/server-key.pem create mode 100644 packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts create mode 100644 packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts create mode 100644 packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts create mode 100644 packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts create mode 100644 packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts create mode 100644 packages/opentelemetry-plugin-https/test/utils/assertSpan.ts create mode 100644 packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts create mode 100644 packages/opentelemetry-plugin-https/test/utils/utils.ts diff --git a/examples/https/README.md b/examples/https/README.md new file mode 100644 index 000000000..ac9b0e962 --- /dev/null +++ b/examples/https/README.md @@ -0,0 +1,77 @@ +# Overview + +OpenTelemetry HTTPS Instrumentation allows the user to automatically collect trace data and export them to the backend of choice (we can use Zipkin or Jaeger for this example), to give observability to distributed systems. + +This is a simple example that demonstrates tracing HTTPS request from client to server. The example +shows key aspects of tracing such as +- Root Span (on Client) +- Child Span (on Client) +- Child Span from a Remote Parent (on Server) +- SpanContext Propagation (from Client to Server) +- Span Events +- Span Attributes + +## Installation + +```sh +$ # from this directory +$ npm install +``` + +Setup [Zipkin Tracing](https://zipkin.io/pages/quickstart.html) +or +Setup [Jaeger Tracing](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one) + +## Run the Application + +### Zipkin + + - Run the server + + ```sh + $ # from this directory + $ npm run zipkin:server + ``` + + - Run the client + + ```sh + $ # from this directory + $ npm run zipkin:client + ``` + +#### Zipkin UI +`zipkin:server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`). +Go to Zipkin with your browser [http://localhost:9411/zipkin/traces/(your-trace-id)]() (e.g http://localhost:9411/zipkin/traces/4815c3d576d930189725f1f1d1bdfcc6) + +

+ +### Jaeger + + - Run the server + + ```sh + $ # from this directory + $ npm run jaeger:server + ``` + + - Run the client + + ```sh + $ # from this directory + $ npm run jaeger:client + ``` +#### Jaeger UI + +`jaeger:server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`). +Go to Jaeger with your browser [http://localhost:16686/trace/(your-trace-id)]() (e.g http://localhost:16686/trace/4815c3d576d930189725f1f1d1bdfcc6) + +

+ +## Useful links +- For more information on OpenTelemetry, visit: +- For more information on OpenTelemetry for Node.js, visit: + +## LICENSE + +Apache License 2.0 diff --git a/examples/https/client.js b/examples/https/client.js new file mode 100644 index 000000000..aef9966d3 --- /dev/null +++ b/examples/https/client.js @@ -0,0 +1,42 @@ +'use strict'; + +const opentelemetry = require('@opentelemetry/core'); +const config = require('./setup'); +/** + * The trace instance needs to be initialized first, if you want to enable + * automatic tracing for built-in plugins (HTTPs in this case). + */ +config.setupTracerAndExporters('https-client-service'); + +const https = require('https'); +const tracer = opentelemetry.getTracer(); + +/** A function which makes requests and handles response. */ +function makeRequest() { + // span corresponds to outgoing requests. Here, we have manually created + // the span, which is created to track work that happens outside of the + // request lifecycle entirely. + const span = tracer.startSpan('makeRequest'); + tracer.withSpan(span, () => { + https.get({ + host: 'localhost', + port: 443, + path: '/helloworld' + }, (response) => { + let body = []; + response.on('data', chunk => body.push(chunk)); + response.on('end', () => { + console.log(body.toString()); + span.end(); + }); + }); + }); + + // The process must live for at least the interval past any traces that + // must be exported, or some risk being lost if they are recorded after the + // last export. + console.log('Sleeping 5 seconds before shutdown to ensure all records are flushed.') + setTimeout(() => { console.log('Completed.'); }, 5000); +} + +makeRequest(); diff --git a/examples/https/images/jaeger-ui.png b/examples/https/images/jaeger-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..8b1d66a5f81fb67fe6454a62e9ca80b1f552795d GIT binary patch literal 49369 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%0Dni@tj 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 literal 0 HcmV?d00001 diff --git a/examples/https/package.json b/examples/https/package.json new file mode 100644 index 000000000..655bcf656 --- /dev/null +++ b/examples/https/package.json @@ -0,0 +1,43 @@ +{ + "name": "https-example", + "private": true, + "version": "0.1.0", + "description": "Example of HTTPs integration with OpenTelemetry", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "scripts": { + "zipkin:server": "cross-env EXPORTER=zipkin node ./server.js", + "zipkin:client": "cross-env EXPORTER=zipkin node ./client.js", + "jaeger:server": "cross-env EXPORTER=jaeger node ./server.js", + "jaeger:client": "cross-env EXPORTER=jaeger node ./client.js" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git" + }, + "keywords": [ + "opentelemetry", + "https", + "tracing" + ], + "engines": { + "node": ">=8" + }, + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/open-telemetry/opentelemetry-js/issues" + }, + "dependencies": { + "@opentelemetry/core": "^0.1.0", + "@opentelemetry/exporter-jaeger": "^0.1.0", + "@opentelemetry/exporter-zipkin": "^0.1.0", + "@opentelemetry/node-sdk": "^0.1.0", + "@opentelemetry/plugin-https": "^0.1.0", + "@opentelemetry/tracer-basic": "^0.1.0" + }, + "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", + "devDependencies": { + "cross-env": "^6.0.0" + } +} diff --git a/examples/https/server-cert.pem b/examples/https/server-cert.pem new file mode 100644 index 000000000..e2b79024d --- /dev/null +++ b/examples/https/server-cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqzCCARQCCQDLcUeJsLDL5jANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJD +QTELMAkGA1UECAwCUUMwHhcNMTkwOTI5MjIwMDI2WhcNMTkxMDI5MjIwMDI2WjAa +MQswCQYDVQQGEwJDQTELMAkGA1UECAwCUUMwgZ8wDQYJKoZIhvcNAQEBBQADgY0A +MIGJAoGBALhfi1dwIyC1Jha4N/j/VtlPPi+j+SZQGZqLNVVgzzGY7+cc3VkCySZD +yXh3Z+/ftp9DDKdHRutJQE0R4peSDussC/IQDJKzuKN/O9S6tnNlgUr5YZLRENxL +FSJIY5cIkty50IrEhlN5QeDJP8p4yrYq9J6M0yzyfdqIWI3CBqbzAgMBAAEwDQYJ +KoZIhvcNAQEFBQADgYEArnOeXmXXJTK39Ma25elHxlYUZiYOBu/truy5zmx4umyS +GyehAv+jRIanoCRWtOBnrjS5CY/6cC64aIVLMoqXEFIL7q/GD0wEM/DS8rN7KTcp +w+nIX98srYaAFeQZScPioS6WpXz5AjbTVhvAwkIm2/s6dOlX31+1zu6Zu6ASSuQ= +-----END CERTIFICATE----- diff --git a/examples/https/server-key.pem b/examples/https/server-key.pem new file mode 100644 index 000000000..405c5fa0d --- /dev/null +++ b/examples/https/server-key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC4X4tXcCMgtSYWuDf4/1bZTz4vo/kmUBmaizVVYM8xmO/nHN1Z +AskmQ8l4d2fv37afQwynR0brSUBNEeKXkg7rLAvyEAySs7ijfzvUurZzZYFK+WGS +0RDcSxUiSGOXCJLcudCKxIZTeUHgyT/KeMq2KvSejNMs8n3aiFiNwgam8wIDAQAB +AoGBAKBztcYQduGeBFm9VCjDvgc8KTg4kTlAeCfAglec+nOFTzJoMlGmVPuR/qFx ++OgOXtXW+goRw6w7gVQQ/os9tvCCp7awSC5UCfPejHh6bW2B0BF2lZJ6B9y+u5Fa +/p8oKoJGcC4eagVnDojuoYJHSqWBf7d7V/U54NpxwgBTsHAhAkEA8PJROgWzjMl2 +Gs5j8oBldEqzrC/d4K1uMEvCTb4RJ+t6jWq+Ug/vqvCfIcLfxHbOmTbOHTfhpv/d +NUf9eDyBGwJBAMPkZaHP5vPDd900MqypLVasollzxgPnMUg35EEQJLAbb/5xG3X9 +ZbaVDTRtLQYNFvDZLlTpRpCPxZCgrn9hJwkCQQDPEVChLrkpqxFm5CydAZ8vG+vh +dJmYNzPVKaZorYmM5yBBXJUHbU6pd3UqzJEGBJx0q9bi4V156bYvzhiVNlo1AkBu +1hbvFCwPtoRmg3c8nEhL50fApzHd2XzX6M/cRF8Nyah3ZdXsz6AyS2l6RV+ZMeTO +B4QghRDpEH/vUgsJhZXJAkB5GQZPJh6/kozc5+Ffc60ThN/58SX0KEFeKnWRlzfr +vfBXwcmaz1oNXN+kcWdLnKbr/tx+3UQ6weRRmeYX/hOi +-----END RSA PRIVATE KEY----- diff --git a/examples/https/server.js b/examples/https/server.js new file mode 100644 index 000000000..1d6d20886 --- /dev/null +++ b/examples/https/server.js @@ -0,0 +1,61 @@ +'use strict'; + +const fs = require('fs'); +const opentelemetry = require('@opentelemetry/core'); +const config = require('./setup'); +/** + * The trace instance needs to be initialized first, if you want to enable + * automatic tracing for built-in plugins (HTTPs in this case). + */ +config.setupTracerAndExporters('https-server-service'); + +const https = require('https'); +const tracer = opentelemetry.getTracer(); + +/** Starts a HTTPs server that receives requests on sample server port. */ +function startServer (port) { + const options = { + key: fs.readFileSync('./server-key.pem'), + cert: fs.readFileSync('./server-cert.pem') + }; + // Creates a server + const server = https.createServer(options, handleRequest); + // Starts the server + server.listen(port, err => { + if (err) { + throw err; + } + console.log(`Node HTTPs listening on ${port}`); + }); +} + +/** A function which handles requests and send response. */ +function handleRequest (request, response) { + const currentSpan = tracer.getCurrentSpan(); + // display traceid in the terminal + console.log(`traceid: ${currentSpan.context().traceId}`); + const span = tracer.startSpan('handleRequest', { + parent: currentSpan, + kind: 1, // server + attributes: { key:'value' } + }); + // Annotate our span to capture metadata about the operation + span.addEvent('invoking handleRequest'); + try { + let body = []; + request.on('error', err => console.log(err)); + request.on('data', chunk => body.push(chunk)); + request.on('end', () => { + // deliberately sleeping to mock some action. + setTimeout(() => { + span.end(); + response.end('Hello World!'); + }, 2000); + }); + } catch (err) { + console.log(err); + span.end(); + } +} + +startServer(443); diff --git a/examples/https/setup.js b/examples/https/setup.js new file mode 100644 index 000000000..f1622065b --- /dev/null +++ b/examples/https/setup.js @@ -0,0 +1,32 @@ +'use strict'; + +const opentelemetry = require('@opentelemetry/core'); +const { NodeTracer } = require('@opentelemetry/node-sdk'); +const { SimpleSpanProcessor } = require('@opentelemetry/tracer-basic'); +const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); +const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); +const EXPORTER = process.env.EXPORTER || ''; +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; +function setupTracerAndExporters(service) { + let exporter; + const tracer = new NodeTracer(); + + if (EXPORTER.toLowerCase().startsWith('z')) { + exporter = new ZipkinExporter({ + serviceName: service + }); + } else { + exporter = new JaegerExporter({ + serviceName: service, + // The default flush interval is 5 seconds. + flushInterval: 2000 + }); + } + + tracer.addSpanProcessor(new SimpleSpanProcessor(exporter)); + + // Initialize the OpenTelemetry APIs to use the BasicTracer bindings + opentelemetry.initGlobalTracer(tracer); +} + +exports.setupTracerAndExporters = setupTracerAndExporters; diff --git a/packages/opentelemetry-plugin-http/README.md b/packages/opentelemetry-plugin-http/README.md index 67496b3cf..fe6e68d89 100644 --- a/packages/opentelemetry-plugin-http/README.md +++ b/packages/opentelemetry-plugin-http/README.md @@ -17,7 +17,7 @@ npm install --save @opentelemetry/plugin-http ## Usage -OpenTelemetry HTTP Instrumentation allows the user to automatically collect trace data and export them to the backend of choice, to give observability to distributed systems. +OpenTelemetry HTTP Instrumentation allows the user to automatically collect trace data and export them to their backend of choice, to give observability to distributed systems. To load a specific plugin (HTTP in this case), specify it in the Node Tracer's configuration. ```js diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index a7eb4e9f5..d9d15294f 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -48,11 +48,13 @@ import * as utils from './utils'; * Http instrumentation plugin for Opentelemetry */ export class HttpPlugin extends BasePlugin { - static readonly component = 'http'; + readonly component: string; protected _config!: HttpPluginConfig; constructor(readonly moduleName: string, readonly version: string) { super(); + // For now component is equal to moduleName but it can change in the future. + this.component = this.moduleName; this._config = {}; } @@ -76,7 +78,7 @@ export class HttpPlugin extends BasePlugin { shimmer.wrap( this._moduleExports, 'get', - this._getPatchOutgoingGetFunction() + this._getPatchOutgoingGetFunction(request) ); } @@ -134,7 +136,12 @@ export class HttpPlugin extends BasePlugin { }; } - protected _getPatchOutgoingGetFunction() { + protected _getPatchOutgoingGetFunction( + clientRequest: ( + options: RequestOptions | string | URL, + ...args: HttpRequestArgs + ) => ClientRequest + ) { return (original: Func): Func => { // Re-implement http.get. This needs to be done (instead of using // getPatchOutgoingRequestFunction to patch it) because we need to @@ -148,10 +155,10 @@ export class HttpPlugin extends BasePlugin { // https://github.com/googleapis/cloud-trace-nodejs/blob/master/src/plugins/plugin-http.ts#L198 return function outgoingGetRequest< T extends RequestOptions | string | URL - >(options: T, ...args: HttpRequestArgs) { - const req = request(options, ...args); + >(options: T, ...args: HttpRequestArgs): ClientRequest { + const req = clientRequest(options, ...args); req.end(); - return req as ClientRequest; + return req; }; }; } @@ -180,7 +187,7 @@ export class HttpPlugin extends BasePlugin { [AttributeNames.HTTP_URL]: utils.getAbsoluteUrl( options, headers, - `${HttpPlugin.component}:` + `${this.component}:` ), [AttributeNames.HTTP_HOSTNAME]: host, [AttributeNames.HTTP_METHOD]: method, @@ -311,7 +318,7 @@ export class HttpPlugin extends BasePlugin { [AttributeNames.HTTP_URL]: utils.getAbsoluteUrl( requestUrl, headers, - `${HttpPlugin.component}:` + `${plugin.component}:` ), [AttributeNames.HTTP_HOSTNAME]: hostname, [AttributeNames.HTTP_METHOD]: method, @@ -433,7 +440,7 @@ export class HttpPlugin extends BasePlugin { private _startHttpSpan(name: string, options: SpanOptions) { return this._tracer .startSpan(name, options) - .setAttribute(AttributeNames.COMPONENT, HttpPlugin.component); + .setAttribute(AttributeNames.COMPONENT, this.component); } private _safeExecute< T extends (...args: unknown[]) => ReturnType, @@ -461,7 +468,4 @@ export class HttpPlugin extends BasePlugin { } } -export const plugin = new HttpPlugin( - HttpPlugin.component, - process.versions.node -); +export const plugin = new HttpPlugin('http', process.versions.node); diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts index 9bc9e80eb..6e30462f8 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts @@ -103,10 +103,9 @@ describe('HttpPlugin', () => { }, }; pluginWithBadOptions = new HttpPlugin( - HttpPlugin.component, + plugin.component, process.versions.node ); - pluginWithBadOptions.enable(http, tracer, tracer.logger, config); server = http.createServer((request, response) => { response.end('Test Server Response'); @@ -133,6 +132,7 @@ describe('HttpPlugin', () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assert.strictEqual(spans.length, 2); @@ -156,7 +156,6 @@ describe('HttpPlugin', () => { assert.strictEqual(spans.length, 0); }); }); - describe('with good plugin options', () => { beforeEach(() => { memoryExporter.reset(); @@ -195,7 +194,7 @@ describe('HttpPlugin', () => { it("should not patch if it's not a http module", () => { const httpNotPatched = new HttpPlugin( - HttpPlugin.component, + plugin.component, process.versions.node ).enable({} as Http, tracer, tracer.logger, {}); assert.strictEqual(Object.keys(httpNotPatched).length, 0); @@ -214,6 +213,7 @@ describe('HttpPlugin', () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assert.strictEqual(spans.length, 2); @@ -269,6 +269,7 @@ describe('HttpPlugin', () => { pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assertSpan(reqSpan, SpanKind.CLIENT, validations); @@ -294,6 +295,7 @@ describe('HttpPlugin', () => { pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); @@ -336,6 +338,7 @@ describe('HttpPlugin', () => { pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts index 1c7a9a8ed..3ae7ab999 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts @@ -108,6 +108,7 @@ describe('Packages', () => { pathname: urlparsed.pathname!, path: urlparsed.path, resHeaders, + component: plugin.component, }; assert.strictEqual(spans.length, 1); diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts index 206c62680..9c2095a15 100644 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts @@ -23,7 +23,7 @@ import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; import { httpRequest } from '../utils/httpRequest'; import * as url from 'url'; -import { Utils } from '../utils/Utils'; +import * as utils from '../utils/utils'; import { NodeTracer } from '@opentelemetry/node-sdk'; import { InMemorySpanExporter, @@ -48,7 +48,7 @@ describe('HttpPlugin Integration tests', () => { return; } - Utils.checkInternet(isConnected => { + utils.checkInternet(isConnected => { if (!isConnected) { this.skip(); // don't disturbe people @@ -105,6 +105,7 @@ describe('HttpPlugin Integration tests', () => { path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assert.strictEqual(spans.length, 1); @@ -123,6 +124,7 @@ describe('HttpPlugin Integration tests', () => { pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assert.strictEqual(spans.length, 1); @@ -149,6 +151,7 @@ describe('HttpPlugin Integration tests', () => { pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, + component: plugin.component, }; assert.strictEqual(spans.length, 1); diff --git a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts index 094da2468..4bd9374c1 100644 --- a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts @@ -22,7 +22,6 @@ import { AttributeNames } from '../../src/enums/AttributeNames'; import * as utils from '../../src/utils'; import { DummyPropagation } from './DummyPropagation'; import { ReadableSpan } from '@opentelemetry/tracer-basic'; -import { HttpPlugin } from '../../src/http'; export const assertSpan = ( span: ReadableSpan, @@ -36,6 +35,7 @@ export const assertSpan = ( reqHeaders?: http.OutgoingHttpHeaders; path?: string; forceStatus?: Status; + component: string; } ) => { assert.strictEqual(span.spanContext.traceId.length, 32); @@ -47,7 +47,7 @@ export const assertSpan = ( ); assert.strictEqual( span.attributes[AttributeNames.COMPONENT], - HttpPlugin.component + validations.component ); assert.strictEqual( span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], diff --git a/packages/opentelemetry-plugin-http/test/utils/Utils.ts b/packages/opentelemetry-plugin-http/test/utils/utils.ts similarity index 72% rename from packages/opentelemetry-plugin-http/test/utils/Utils.ts rename to packages/opentelemetry-plugin-http/test/utils/utils.ts index d52c36dda..57a75516a 100644 --- a/packages/opentelemetry-plugin-http/test/utils/Utils.ts +++ b/packages/opentelemetry-plugin-http/test/utils/utils.ts @@ -16,14 +16,12 @@ import * as dns from 'dns'; -export class Utils { - static checkInternet(cb: (isConnected: boolean) => void) { - dns.lookup('google.com', err => { - if (err && err.code === 'ENOTFOUND') { - cb(false); - } else { - cb(true); - } - }); - } -} +export const checkInternet = (cb: (isConnected: boolean) => void) => { + dns.lookup('google.com', err => { + if (err && err.code === 'ENOTFOUND') { + cb(false); + } else { + cb(true); + } + }); +}; diff --git a/packages/opentelemetry-plugin-https/README.md b/packages/opentelemetry-plugin-https/README.md index 7d8973bba..564f03936 100644 --- a/packages/opentelemetry-plugin-https/README.md +++ b/packages/opentelemetry-plugin-https/README.md @@ -17,12 +17,43 @@ npm install --save @opentelemetry/plugin-https ## Usage -```js -const opentelemetry = require('@opentelemetry/plugin-https'); +OpenTelemetry HTTPS Instrumentation allows the user to automatically collect trace data and export them to their backend of choice, to give observability to distributed systems. -// TODO: DEMONSTRATE API +To load a specific plugin (HTTPS in this case), specify it in the Node Tracer's configuration. +```js +const { NodeTracer } = require('@opentelemetry/node-sdk'); + +const tracer = new NodeTracer({ + plugins: { + https: { + enabled: true, + // You may use a package name or absolute path to the file. + path: '@opentelemetry/plugin-https', + // https plugin options + } + } +}); ``` +To load all the [supported plugins](https://github.com/open-telemetry/opentelemetry-js#plugins), use below approach. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules. +```js +const { NodeTracer } = require('@opentelemetry/node-sdk'); + +const tracer = new NodeTracer(); +``` + +See [examples/https](https://github.com/open-telemetry/opentelemetry-js/tree/master/examples/https) for a short example. + +### Https Plugin Options + +Https plugin has few options available to choose from. You can set the following: + +| Options | Type | Description | +| ------- | ---- | ----------- | +| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L52) | `HttpCustomAttributeFunction` | Function for adding custom attributes | +| [`ignoreIncomingPaths`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all incoming requests that match paths | +| [`ignoreOutgoingUrls`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all outgoing requests that match urls | + ## Useful links - For more information on OpenTelemetry, visit: - For more about OpenTelemetry JavaScript: diff --git a/packages/opentelemetry-plugin-https/package.json b/packages/opentelemetry-plugin-https/package.json index a7e6c6d02..d72b59d5f 100644 --- a/packages/opentelemetry-plugin-https/package.json +++ b/packages/opentelemetry-plugin-https/package.json @@ -2,7 +2,6 @@ "name": "@opentelemetry/plugin-https", "version": "0.1.0", "description": "OpenTelemetry https automatic instrumentation package.", - "private": true, "main": "build/src/index.js", "types": "build/src/index.d.ts", "repository": "open-telemetry/opentelemetry-js", @@ -38,22 +37,41 @@ "access": "public" }, "devDependencies": { + "@types/got": "^9.6.7", "@types/mocha": "^5.2.7", - "@types/node": "^12.6.9", - "codecov": "^3.5.0", + "@types/nock": "^11.1.0", + "@types/node": "^12.7.8", + "@types/request-promise-native": "^1.0.17", + "@types/semver": "^6.0.2", + "@types/shimmer": "^1.0.1", + "@types/sinon": "^7.0.13", + "@types/superagent": "^4.1.3", + "@opentelemetry/tracer-basic": "^0.1.0", + "@opentelemetry/node-sdk": "^0.1.0", + "@opentelemetry/scope-base": "^0.1.0", + "axios": "^0.19.0", + "got": "^9.6.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.7", + "superagent": "5.1.0", + "codecov": "^3.6.1", "gts": "^1.1.0", - "mocha": "^6.2.0", + "mocha": "^6.2.1", + "nock": "^11.3.5", "nyc": "^14.1.1", "rimraf": "^3.0.0", + "sinon": "^7.5.0", "tslint-microsoft-contrib": "^6.2.0", - "tslint-consistent-codestyle": "^1.15.1", + "tslint-consistent-codestyle": "^1.16.0", "ts-mocha": "^6.0.0", - "ts-node": "^8.3.0", + "ts-node": "^8.4.1", "typescript": "^3.6.3" }, "dependencies": { + "@opentelemetry/types": "^0.1.0", "@opentelemetry/core": "^0.1.0", - "@opentelemetry/node-sdk": "^0.1.0", - "@opentelemetry/types": "^0.1.0" + "@opentelemetry/plugin-http": "^0.1.0", + "semver": "^6.3.0", + "shimmer": "^1.2.1" } } diff --git a/packages/opentelemetry-plugin-https/src/https.ts b/packages/opentelemetry-plugin-https/src/https.ts new file mode 100644 index 000000000..2073ea722 --- /dev/null +++ b/packages/opentelemetry-plugin-https/src/https.ts @@ -0,0 +1,143 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { HttpPlugin, Func, HttpRequestArgs } from '@opentelemetry/plugin-http'; +import * as http from 'http'; +import * as https from 'https'; +import * as semver from 'semver'; +import * as shimmer from 'shimmer'; +import * as utils from './utils'; + +/** + * Https instrumentation plugin for Opentelemetry + */ +export class HttpsPlugin extends HttpPlugin { + /** Constructs a new HttpsPlugin instance. */ + constructor(readonly version: string) { + super('https', version); + } + + /** + * Patches HTTPS incoming and outcoming request functions. + */ + protected patch() { + this._logger.debug( + 'applying patch to %s@%s', + this.moduleName, + this.version + ); + + if ( + this._moduleExports && + this._moduleExports.Server && + this._moduleExports.Server.prototype + ) { + shimmer.wrap( + this._moduleExports.Server.prototype, + 'emit', + this._getPatchIncomingRequestFunction() + ); + } else { + this._logger.error( + 'Could not apply patch to %s.emit. Interface is not as expected.', + this.moduleName + ); + } + + shimmer.wrap( + this._moduleExports, + 'request', + this._getPatchHttpsOutgoingRequestFunction() + ); + + // In Node 8-12, http.get calls a private request method, therefore we patch it + // here too. + if (semver.satisfies(this.version, '>=8.0.0')) { + shimmer.wrap( + this._moduleExports, + 'get', + this._getPatchHttpsOutgoingGetFunction(https.request) + ); + } + + return this._moduleExports; + } + + /** Patches HTTPS outgoing requests */ + private _getPatchHttpsOutgoingRequestFunction() { + return (original: Func): Func => { + const plugin = this; + return function httpsOutgoingRequest( + options, + ...args: HttpRequestArgs + ): http.ClientRequest { + // Makes sure options will have default HTTPS parameters + if (typeof options === 'object') { + utils.setDefaultOptions(options); + } + return plugin._getPatchOutgoingRequestFunction()(original)( + options, + ...args + ); + }; + }; + } + + /** Patches HTTPS outgoing get requests */ + private _getPatchHttpsOutgoingGetFunction( + clientRequest: ( + options: http.RequestOptions | string | URL, + ...args: HttpRequestArgs + ) => http.ClientRequest + ) { + return (original: Func): Func => { + return function httpsOutgoingRequest( + options: https.RequestOptions | string, + ...args: HttpRequestArgs + ): http.ClientRequest { + const optionsType = typeof options; + // Makes sure options will have default HTTPS parameters + if (optionsType === 'object') { + utils.setDefaultOptions(options as https.RequestOptions); + } else if (typeof args[0] === 'object' && optionsType === 'string') { + utils.setDefaultOptions(args[0] as https.RequestOptions); + } + + return plugin._getPatchOutgoingGetFunction(clientRequest)(original)( + options, + ...args + ); + }; + }; + } + + /** Unpatches all HTTPS patched function. */ + protected unpatch(): void { + if ( + this._moduleExports && + this._moduleExports.Server && + this._moduleExports.Server.prototype + ) { + shimmer.unwrap(this._moduleExports.Server.prototype, 'emit'); + } + shimmer.unwrap(this._moduleExports, 'request'); + if (semver.satisfies(this.version, '>=8.0.0')) { + shimmer.unwrap(this._moduleExports, 'get'); + } + } +} + +export const plugin = new HttpsPlugin(process.versions.node); diff --git a/packages/opentelemetry-plugin-https/src/index.ts b/packages/opentelemetry-plugin-https/src/index.ts index ae225f6b5..19d7007d0 100644 --- a/packages/opentelemetry-plugin-https/src/index.ts +++ b/packages/opentelemetry-plugin-https/src/index.ts @@ -13,3 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +export * from './https'; diff --git a/packages/opentelemetry-plugin-https/src/utils.ts b/packages/opentelemetry-plugin-https/src/utils.ts new file mode 100644 index 000000000..eed5c63d3 --- /dev/null +++ b/packages/opentelemetry-plugin-https/src/utils.ts @@ -0,0 +1,23 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as https from 'https'; + +export const setDefaultOptions = (options: https.RequestOptions) => { + options.protocol = options.protocol || 'https:'; + options.port = options.port || 443; + options.agent = options.agent || https.globalAgent; +}; diff --git a/packages/opentelemetry-plugin-https/test/fixtures/google.json b/packages/opentelemetry-plugin-https/test/fixtures/google.json new file mode 100644 index 000000000..550bb764b --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/fixtures/google.json @@ -0,0 +1,43 @@ +[ + { + "scope": "https://www.google.com", + "method": "GET", + "path": "/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8", + "body": "", + "status": 200, + "response": "3c21646f63747970652068746d6c3e3c68746d6c206c616e673d22656e2d4341223e3c686561643e3c6d65746120636861727365743d225554462d38223e3c6d65746120636f6e74656e743d222f696d616765732f6272616e64696e672f676f6f676c65672f31782f676f6f676c65675f7374616e646172645f636f6c6f725f31323864702e706e6722206974656d70726f703d22696d616765223e3c7469746c653e6178696f73202d20476f6f676c65205365617263683c2f7469746c653e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220613d77696e646f772e706572666f726d616e63653b77696e646f772e73746172743d286e65772044617465292e67657454696d6528293b613a7b76617220623d77696e646f773b69662861297b76617220633d612e74696d696e673b69662863297b76617220643d632e6e617669676174696f6e53746172742c653d632e726573706f6e736553746172743b696628653e642626653c3d77696e646f772e7374617274297b77696e646f772e73746172743d653b622e777372743d652d643b627265616b20617d7d612e6e6f77262628622e777372743d4d6174682e666c6f6f7228612e6e6f77282929297d7d77696e646f772e676f6f676c653d77696e646f772e676f6f676c657c7c7b7d3b676f6f676c652e6166743d66756e6374696f6e2866297b662e7365744174747269627574652822646174612d696d6c222c2b6e65772044617465297d3b7d292e63616c6c2874686973293b2866756e6374696f6e28297b76617220633d5b5d2c653d303b77696e646f772e70696e673d66756e6374696f6e2862297b2d313d3d622e696e6465784f662822267a782229262628622b3d22267a783d222b286e65772044617465292e67657454696d652829293b76617220613d6e657720496d6167652c643d652b2b3b635b645d3d613b612e6f6e6572726f723d612e6f6e6c6f61643d612e6f6e61626f72743d66756e6374696f6e28297b64656c65746520635b645d7d3b612e7372633d627d3b7d292e63616c6c2874686973293b3c2f7363726970743e3c7374796c653e626f64797b6d617267696e3a30206175746f3b6d61782d77696474683a37333670783b70616464696e673a30203870787d617b636f6c6f723a233139363744323b746578742d6465636f726174696f6e3a6e6f6e653b7461702d686967686c696768742d636f6c6f723a7267626128302c302c302c2e31297d613a766973697465647b636f6c6f723a233442313141387d613a686f7665727b746578742d6465636f726174696f6e3a756e6465726c696e657d696d677b626f726465723a307d68746d6c7b666f6e742d66616d696c793a526f626f746f2c48656c7665746963614e6575652c417269616c2c73616e732d73657269663b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070783b746578742d73697a652d61646a7573743a313030253b636f6c6f723a233343343034333b776f72642d777261703a627265616b2d776f72643b6261636b67726f756e642d636f6c6f723a236666667d2e625273576e637b6261636b67726f756e642d636f6c6f723a236666663b626f726465722d746f703a31707820736f6c696420236530653065303b6865696768743a333970783b6f766572666c6f773a68696464656e7d2e4e365257567b6865696768743a353170783b6f766572666c6f772d7363726f6c6c696e673a746f7563683b6f766572666c6f772d783a6175746f3b6f766572666c6f772d793a68696464656e7d2e5576363771627b626f782d7061636b3a6a7573746966793b666f6e742d73697a653a313270783b6c696e652d6865696768743a333770783b6a7573746966792d636f6e74656e743a73706163652d6265747765656e3b6a7573746966792d636f6e74656e743a73706163652d6265747765656e7d2e55763637716220612c2e557636377162207370616e7b636f6c6f723a233735373537353b646973706c61793a626c6f636b3b666c65783a6e6f6e653b70616464696e673a3020313670783b746578742d616c69676e3a63656e7465723b746578742d7472616e73666f726d3a7570706572636173653b7d7370616e2e4f585875707b626f726465722d626f74746f6d3a32707820736f6c696420233432383566343b636f6c6f723a233432383566343b666f6e742d7765696768743a626f6c647d612e655a743878643a766973697465647b636f6c6f723a233735373537357d2e46456c6273667b626f726465722d6c6566743a31707820736f6c6964207267626128302c302c302c2e3132297d6865616465722061727469636c657b6f766572666c6f773a76697369626c657d2e5067373062667b6865696768743a333970783b646973706c61793a626f783b646973706c61793a666c65783b646973706c61793a666c65783b77696474683a313030257d2e4830505165637b706f736974696f6e3a72656c61746976653b666c65783a317d2e7362637b646973706c61793a666c65783b77696474683a313030257d2e50673730626620696e7075747b6d617267696e3a3270782034707820327078203870783b7d2e787b77696474683a323670783b636f6c6f723a233735373537353b666f6e743a323770782f3338707820617269616c2c2073616e732d73657269663b6c696e652d6865696768743a343070783b7d237164436c77627b666c65783a302030206175746f3b77696474683a333970783b6865696768743a333970783b626f726465722d626f74746f6d3a303b70616464696e673a303b626f726465722d746f702d72696768742d7261646975733a3870783b6261636b67726f756e642d636f6c6f723a233362373865373b626f726465723a31707820736f6c696420233333363764363b6261636b67726f756e642d696d6167653a75726c28646174613a696d6167652f6769663b6261736536342c52306c474f4464684a41416a41504948414f44722f6e436b2b4d505a2f466d5639367a4b2b2f372b2f354b352b6b714c39697741414141414a41416a41454144616e693633503477796b6d624b635152584473635141454d586d6d65614c51564c43756b7a79433039416a66654b37762f4d41616a41434c68504d564167776a73556345695a61387867415972567176324b783269777349414141426b6e664242414b7254453449634d796f743875723864617471496251664a646e41666f3257453642563035775849694a69676b414f773d3d293b7d2e73637b666f6e742d73697a653a3b706f736974696f6e3a6162736f6c7574653b746f703a333970783b6c6566743a303b72696768743a303b626f782d736861646f773a3070782032707820357078207267626128302c302c302c302e32293b7a2d696e6465783a323b6261636b67726f756e642d636f6c6f723a236666667d2e73633e6469767b70616464696e673a3130707820313070783b70616464696e672d6c6566743a313670783b70616464696e672d6c6566743a313470783b626f726465722d746f703a31707820736f6c696420234446453145357d2e7363737b6261636b67726f756e642d636f6c6f723a236635663566353b7d2e6e6f484978637b646973706c61793a626c6f636b3b666f6e742d73697a653a313670783b70616464696e673a3020302030203870783b666c65783a313b6865696768743a333570783b6f75746c696e653a6e6f6e653b626f726465723a6e6f6e653b77696474683a313030253b2d7765626b69742d7461702d686967686c696768742d636f6c6f723a7267626128302c302c302c30293b6f766572666c6f773a68696464656e3b7d2e73626320696e7075745b747970653d746578745d7b6261636b67726f756e643a6e6f6e657d2e736d6c202e634f6c3449647b646973706c61793a6e6f6e657d2e6c7b646973706c61793a6e6f6e657d2e736d6c206865616465727b6261636b67726f756e643a6e6f6e657d2e736d6c202e6c7b646973706c61793a626c6f636b3b70616464696e673a30203870787d2e736d6c202e6c7b6c65747465722d73706163696e673a2d3170783b746578742d616c69676e3a63656e7465723b626f726465722d7261646975733a3270782030203020303b666f6e743a323270782f33367078204675747572612c20417269616c2c2073616e732d73657269663b666f6e742d736d6f6f7468696e673a616e7469616c69617365647d2e627a316c42627b6261636b67726f756e643a236666663b626f726465722d7261646975733a38707820387078203020303b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3138293b6d617267696e2d746f703a313070787d2e4b50374c43627b626f726465722d7261646975733a30203020387078203870783b626f782d736861646f773a30203270782033707820726762612833322c2033332c2033362c20302e3138293b6d617267696e2d626f74746f6d3a313070783b6f766572666c6f773a68696464656e7d2e634f6c3449647b6c65747465722d73706163696e673a2d3170783b746578742d616c69676e3a63656e7465723b666f6e743a32327074204675747572612c20417269616c2c2073616e732d73657269663b70616464696e673a3130707820302035707820303b6865696768743a333770783b666f6e742d736d6f6f7468696e673a616e7469616c69617365647d2e634f6c344964207370616e7b646973706c61793a696e6c696e652d626c6f636b7d2e533539316a7b6865696768743a313030257d2e5636677756647b636f6c6f723a233432383546347d2e69576b7576647b636f6c6f723a234541343333357d2e63447251377b636f6c6f723a236662636330357d2e6e746c52397b636f6c6f723a233334413835337d2e744a334d79637b2d7765626b69742d7472616e73666f726d3a726f74617465282d3230646567293b706f736974696f6e3a72656c61746976653b6c6566743a2d3170783b646973706c61793a696e6c696e652d626c6f636b7d666f6f7465727b746578742d616c69676e3a63656e7465723b6d617267696e2d746f703a313870787d666f6f74657220612c666f6f74657220613a766973697465642c2e736d695562627b636f6c6f723a233566363336387d2e6b73545534637b6d617267696e3a3020313370787d236d436c6a6f627b6d617267696e2d746f703a333670787d236d436c6a6f623e6469767b6d617267696e3a323070787d3c2f7374796c653e3c2f686561643e3c626f6479206a736d6f64656c3d2220223e3c6865616465722069643d22686472223e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220633d3530303b2866756e6374696f6e28297b77696e646f772e73637265656e262677696e646f772e73637265656e2e77696474683c3d63262677696e646f772e73637265656e2e6865696768743c3d632626646f63756d656e742e676574456c656d656e7442794964282268647222292e636c6173734c6973742e6164642822736d6c22293b7d292e63616c6c2874686973293b7d2928293b3c2f7363726970743e3c64697620636c6173733d22634f6c344964223e3c6120687265663d222f3f73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673514f776743223e3c7370616e20636c6173733d22563667775664223e473c2f7370616e3e3c7370616e20636c6173733d2269576b757664223e6f3c2f7370616e3e3c7370616e20636c6173733d226344725137223e6f3c2f7370616e3e3c7370616e20636c6173733d22563667775664223e673c2f7370616e3e3c7370616e20636c6173733d226e746c5239223e6c3c2f7370616e3e3c7370616e20636c6173733d2269576b75766420744a334d7963223e653c2f7370616e3e3c2f613e3c2f6469763e3c64697620636c6173733d22627a316c4262223e3c666f726d20636c6173733d22506737306266222069643d227366223e3c6120636c6173733d226c2220687265663d222f3f6f75747075743d73656172636826616d703b69653d5554462d3826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735150416745223e3c7370616e20636c6173733d22563667775664223e473c2f7370616e3e3c7370616e20636c6173733d2269576b757664223e6f3c2f7370616e3e3c7370616e20636c6173733d226344725137223e6f3c2f7370616e3e3c7370616e20636c6173733d22563667775664223e673c2f7370616e3e3c7370616e20636c6173733d226e746c5239223e6c3c2f7370616e3e3c7370616e20636c6173733d2269576b75766420744a334d7963223e653c2f7370616e3e3c2f613e3c696e707574206e616d653d226965222076616c75653d2249534f2d383835392d312220747970653d2268696464656e223e3c64697620636c6173733d22483050516563223e3c64697620636c6173733d227362632065736263223e3c696e70757420636c6173733d226e6f48497863222076616c75653d226178696f7322206175746f6361706974616c697a653d226e6f6e6522206175746f636f6d706c6574653d226f666622206e616d653d227122207370656c6c636865636b3d2266616c73652220747970653d2274657874223e3c696e707574206e616d653d226f712220747970653d2268696464656e223e3c696e707574206e616d653d226171732220747970653d2268696464656e223e3c64697620636c6173733d2278223ed73c2f6469763e3c64697620636c6173733d227363223e3c2f6469763e3c2f6469763e3c2f6469763e3c627574746f6e2069643d227164436c77622220747970653d227375626d6974223e3c2f627574746f6e3e3c2f666f726d3e3c2f6469763e3c6e6f7363726970743e3c6d65746120636f6e74656e743d22303b75726c3d2f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b6762763d3126616d703b7365693d704d524f586250624472505339414f426d4b505942512220687474702d65717569763d2272656672657368223e3c7374796c653e7461626c652c6469762c7370616e2c707b646973706c61793a6e6f6e657d3c2f7374796c653e3c646976207374796c653d22646973706c61793a626c6f636b223e506c6561736520636c69636b203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b6762763d3126616d703b7365693d704d524f586250624472505339414f426d4b50594251223e686572653c2f613e20696620796f7520617265206e6f7420726564697265637465642077697468696e206120666577207365636f6e64732e3c2f6469763e3c2f6e6f7363726970743e3c2f6865616465723e3c6469762069643d226d61696e223e3c6469763e3c64697620636c6173733d224b50374c4362223e203c64697620636c6173733d22625273576e63223e203c64697620636c6173733d224e36525756223e203c64697620636c6173733d2250673730626620557636377162223e203c7370616e20636c6173733d224f58587570223e416c6c3c2f7370616e3e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d6e777326616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943436742223e4e6577733c2f613e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d76696426616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943536743223e566964656f733c2f613e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d6973636826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943696744223e496d616765733c2f613e2020203c6120687265663d22687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d6178696f7326616d703b756d3d3126616d703b69653d5554462d3826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943796745223e4d6170733c2f613e20203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d73686f7026616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554944436746223e53686f7070696e673c2f613e20203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d626b7326616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554944536747223e426f6f6b733c2f613e20202020203c64697620636c6173733d2246456c627366223e3c6120687265663d222f616476616e6365645f73656172636822207374796c653d2277686974652d73706163653a6e6f77726170222069643d2273742d746f67676c652220726f6c653d22627574746f6e223e53656172636820746f6f6c733c2f613e3c2f6469763e203c2f6469763e203c2f6469763e203c2f6469763e203c2f6469763e3c64697620636c6173733d22506737306266207745736a6264205a494e62626320787064204f396735636320755550476922207374796c653d22646973706c61793a6e6f6e65222069643d2273742d63617264223e3c7374796c653e2e7745736a62647b6261636b67726f756e642d636f6c6f723a236666663b6865696768743a343470783b77686974652d73706163653a6e6f777261707d2e636f505538637b6865696768743a363070783b6f766572666c6f772d7363726f6c6c696e673a746f7563683b6f766572666c6f772d783a6175746f3b6f766572666c6f772d793a68696464656e7d2e586a326175657b6865696768743a343470783b6f766572666c6f773a68696464656e7d2e526e4e477a657b6d617267696e3a3131707820313670787d2e7745736a6264206469762c2e7745736a626420612c2e7745736a6264206c697b6f75746c696e652d77696474683a303b6f75746c696e653a6e6f6e657d3c2f7374796c653e3c64697620636c6173733d22586a32617565223e3c64697620636c6173733d22636f50553863223e3c64697620636c6173733d22526e4e477a65223e3c7374796c653e2e5041394a357b646973706c61793a696e6c696e652d626c6f636b7d2e5258614f66647b646973706c61793a696e6c696e652d626c6f636b3b6865696768743a323270783b706f736974696f6e3a72656c61746976653b70616464696e672d746f703a303b70616464696e672d626f74746f6d3a303b70616464696e672d72696768743a313670783b70616464696e672d6c6566743a303b6c696e652d6865696768743a323270783b637572736f723a706f696e7465723b746578742d7472616e73666f726d3a7570706572636173653b666f6e742d73697a653a313270783b636f6c6f723a233735373537357d2e736131746f637b646973706c61793a6e6f6e653b706f736974696f6e3a6162736f6c7574653b6261636b67726f756e643a236666663b626f726465723a31707820736f6c696420236436643664363b626f782d736861646f773a302032707820347078207267626128302c302c302c302e33293b6d617267696e3a303b77686974652d73706163653a6e6f777261703b7a2d696e6465783a3130333b6c696e652d6865696768743a313770783b70616464696e672d746f703a3570783b70616464696e672d626f74746f6d3a3570783b70616464696e672d6c6566743a3070787d2e5041394a353a686f766572202e736131746f637b646973706c61793a626c6f636b7d2e6d475379386420613a6163746976652c2e5258614f66643a6163746976657b636f6c6f723a233432383566347d3c2f7374796c653e3c64697620636c6173733d225041394a35223e3c64697620636c6173733d225258614f66642220726f6c653d22627574746f6e2220746162696e6465783d2230223e3c7374796c653e2e54574d4f55637b646973706c61793a696e6c696e652d626c6f636b3b70616464696e672d72696768743a313470783b77686974652d73706163653a6e6f777261707d2e7651597547667b666f6e742d7765696768743a626f6c647d2e4f6d54497a667b626f726465722d636f6c6f723a23393039303930207472616e73706172656e743b626f726465722d7374796c653a736f6c69643b626f726465722d77696474683a347078203470782030203470783b77696474683a303b6865696768743a303b6d617267696e2d6c6566743a2d313070783b746f703a3530253b6d617267696e2d746f703a2d3270783b706f736974696f6e3a6162736f6c7574657d2e5258614f66643a616374697665202e4f6d54497a667b626f726465722d636f6c6f723a23343238356634207472616e73706172656e747d3c2f7374796c653e3c64697620636c6173733d2254574d4f5563223e416e792074696d653c2f6469763e3c7370616e20636c6173733d224f6d54497a66223e3c2f7370616e3e3c2f6469763e3c756c20636c6173733d22736131746f63206f7a61744d223e3c7374796c653e2e6f7a61744d7b666f6e742d73697a653a313270783b746578742d7472616e73666f726d3a7570706572636173657d2e6f7a61744d202e794e46736c2c2e6f7a61744d206c697b6c6973742d7374796c652d747970653a6e6f6e653b6c6973742d7374796c652d706f736974696f6e3a6f7574736964653b6c6973742d7374796c652d696d6167653a6e6f6e657d2e794e46736c2e536b556a34632c2e794e46736c20617b636f6c6f723a7267626128302c302c302c302e3534293b746578742d6465636f726174696f6e3a6e6f6e653b70616464696e673a36707820343470782036707820313470783b6c696e652d6865696768743a313770783b646973706c61793a626c6f636b7d2e536b556a34637b6261636b67726f756e642d696d6167653a75726c282f2f73736c2e677374617469632e636f6d2f75692f76312f6d656e752f636865636b6d61726b322e706e67293b6261636b67726f756e642d706f736974696f6e3a72696768742063656e7465723b6261636b67726f756e642d7265706561743a6e6f2d7265706561747d2e536b556a34633a6163746976657b6261636b67726f756e642d636f6c6f723a236635663566357d3c2f7374796c653e3c6c6920636c6173733d22794e46736c20536b556a3463223e416e792074696d653c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494477223e5061737420686f75723c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6426616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494541223e5061737420323420686f7572733c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a7726616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494551223e50617374207765656b3c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6d26616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494567223e50617374206d6f6e74683c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a7926616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494577223e5061737420796561723c2f613e3c2f6c693e3c2f756c3e3c2f6469763e3c64697620636c6173733d225041394a35223e3c64697620636c6173733d225258614f66642220726f6c653d22627574746f6e2220746162696e6465783d2230223e3c64697620636c6173733d2254574d4f5563223e416c6c20726573756c74733c2f6469763e3c7370616e20636c6173733d224f6d54497a66223e3c2f7370616e3e3c2f6469763e3c756c20636c6173733d22736131746f63206f7a61744d223e3c6c6920636c6173733d22794e46736c20536b556a3463223e416c6c20726573756c74733c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d6c693a3126616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494651223e566572626174696d3c2f613e3c2f6c693e3c2f756c3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220613d646f63756d656e742e676574456c656d656e7442794964282273742d746f67676c6522292c623d646f63756d656e742e676574456c656d656e7442794964282273742d6361726422293b612626622626612e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e2863297b622e7374796c652e646973706c61793d622e7374796c652e646973706c61793f22223a226e6f6e65223b632e70726576656e7444656661756c7428297d2c2131293b7d292e63616c6c2874686973293b3c2f7363726970743e3c2f6469763e3c2f6469763e3c7374796c653e2e5a494e6262637b6261636b67726f756e642d636f6c6f723a236666663b6d617267696e2d626f74746f6d3a313070783b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238293b626f726465722d7261646975733a3870787d2e75555047697b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070783b7d2e4f39673563633e2a3a66697273742d6368696c647b626f726465722d746f702d6c6566742d7261646975733a3870783b626f726465722d746f702d72696768742d7261646975733a3870787d2e4f39673563633e2a3a6c6173742d6368696c647b626f726465722d626f74746f6d2d6c6566742d7261646975733a3870783b626f726465722d626f74746f6d2d72696768742d7261646975733a3870787d2e726c37696c627b646973706c61793a626c6f636b3b636c6561723a626f74687d2e6c634a4631647b6d617267696e2d6c6566743a313670783b666c6f61743a72696768743b7d2e6b437259547b70616464696e673a31327078203136707820313270787d612e6664597371667b636f6c6f723a233442313141387d2e424e656177657b77686974652d73706163653a7072652d6c696e653b776f72642d777261703a627265616b2d776f72647d2e76766a774a627b636f6c6f723a233139363744323b666f6e742d73697a653a313670783b6c696e652d6865696768743a323070787d2e76766a774a6220613a766973697465647b636f6c6f723a233442313141387d2e76766a774a622e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e76766a774a622e48724764656220613a766973697465647b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e55506d69747b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e55506d69742e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e55506d69742e415037576e647b636f6c6f723a7267626128302c3130322c33332c31297d2e7835346774667b6865696768743a3170783b6261636b67726f756e642d636f6c6f723a236466653165353b6d617267696e3a3020313670787d2e4170354f53647b70616464696e672d626f74746f6d3a313270787d2e7333763972647b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e7333763972642e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e7333763972642e415037576e647b636f6c6f723a233230323132347d2e6d53783145657b70616464696e672d6c6566743a343870783b6d617267696e3a307d2e7639693631657b70616464696e672d626f74746f6d3a3870787d2e584c6c6f58657b636f6c6f723a233139363744323b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e584c6c6f586520613a766973697465647b636f6c6f723a233442313141387d2e584c6c6f58652e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e584c6c6f58652e48724764656220613a766973697465647b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e5a54763942627b646973706c61793a626c6f636b7d2e6465497643627b666f6e742d73697a653a313670783b6c696e652d6865696768743a323070783b666f6e742d7765696768743a3430307d2e6465497643622e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e6465497643622e415037576e647b636f6c6f723a233230323132347d2e4643557030637b666f6e742d7765696768743a626f6c647d2e58374e5456657b646973706c61793a7461626c653b77696474683a313030253b70616464696e672d72696768743a313670783b626f782d73697a696e673a626f726465722d626f787d2e74486d6651657b646973706c61793a7461626c652d63656c6c3b70616464696e673a313270782030203132707820313670787d2e554874726b7b77696474683a373270787d2e4842544d36647b77696474683a333070787d2e5853377947647b646973706c61793a7461626c652d63656c6c3b746578742d616c69676e3a63656e7465723b766572746963616c2d616c69676e3a6d6964646c653b70616464696e673a3132707820302031327078203870787d2e616d335142667b646973706c61793a7461626c653b766572746963616c2d616c69676e3a746f707d2e5862355652657b636f6c6f723a233139363744327d613a76697369746564202e5862355652657b636f6c6f723a233442313141387d2e5862355652652e74723064777b636f6c6f723a72676261283235352c3235352c3235352c31297d613a76697369746564202e5862355652652e74723064777b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e74416438447b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e74416438442e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e74416438442e415037576e647b636f6c6f723a233730373537417d2e614a79694f637b636f6c6f723a233030363632317d2e6f754a3943627b666c6f61743a6c6566743b70616464696e672d72696768743a313670787d2e526f434c6e657b636c6561723a626f74687d2e45594f736c647b646973706c61793a696e6c696e652d626c6f636b3b706f736974696f6e3a72656c61746976657d2e424669395a627b6f766572666c6f773a68696464656e3b706f736974696f6e3a72656c61746976657d2e53374a647a657b616c69676e2d6974656d733a63656e7465723b646973706c61793a666c65783b666c65782d646972656374696f6e3a636f6c756d6e3b6a7573746966792d636f6e74656e743a73706163652d61726f756e647d2e58646c7230647b6f766572666c6f772d783a6175746f3b2d7765626b69742d6f766572666c6f772d7363726f6c6c696e673a746f7563683b6d617267696e3a30202d3870783b70616464696e673a313670782030203136707820323470783b70616464696e672d746f703a3270783b6d617267696e2d746f703a2d3270783b7472616e73666f726d3a7472616e736c617465336428302c302c30297d2e6964673862657b646973706c61793a7461626c653b626f726465722d636f6c6c617073653a73657061726174653b626f726465722d73706163696e673a38707820303b6d617267696e3a30202d3870783b70616464696e672d72696768743a313670787d2e425647304e627b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a746f703b6261636b67726f756e642d636f6c6f723a236666663b626f726465722d7261646975733a3870783b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238293b6f766572666c6f773a68696464656e7d2e55796b5439647b646973706c61793a626c6f636b3b666c6f61743a72696768743b70616464696e672d6c6566743a313670787d2e6e59543751627b636c6561723a626f74687d2e736b566770627b646973706c61793a7461626c653b7461626c652d6c61796f75743a66697865643b77696474683a313030257d2e5647484d58647b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a6d6964646c653b6865696768743a353270783b746578742d616c69676e3a63656e7465727d2e4c70614472627b6d617267696e3a30206175746f203870783b646973706c61793a626c6f636b7d2e766253684f657b70616464696e672d746f703a307d2e4156736570667b70616464696e672d626f74746f6d3a3870787d2e4156736570662e753278314f647b70616464696e672d626f74746f6d3a307d2e787063202e6877632c2e787078202e6877787b646973706c61793a6e6f6e657d2e5275386964627b6d617267696e2d746f703a2d313670787d2e70756e657a7b666f6e742d7765696768743a3730303b6c65747465722d73706163696e673a302e373570783b746578742d7472616e73666f726d3a7570706572636173657d2e7779727758637b666f6e742d73697a653a313270783b6c696e652d6865696768743a313670787d2e7779727758632e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e7779727758632e415037576e647b636f6c6f723a233230323132347d2e6d4868796c667b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a6d6964646c657d2e575a35474a667b616c69676e2d6974656d733a63656e7465723b70616464696e673a3020323070783b6d696e2d77696474683a31313270787d2e714e394b65642c2e44586b354d657b6d617267696e3a30206175746f7d2e44586b354d657b6d617267696e2d626f74746f6d3a313270787d2e51693946647b6261636b67726f756e643a236666663b626f726465723a303b626f726465722d7261646975733a39393970783b646973706c61793a626c6f636b3b6865696768743a353670783b6a7573746966792d636f6e74656e743a63656e7465723b77696474683a353670783b7a2d696e6465783a307d2e51693946647b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238292c696e7365742030203020302030207267626128302c302c302c302e3130292c696e73657420302030203020302072676261283235352c3235352c3235352c302e3530297d2e51693946643a666f6375737b6f75746c696e653a6e6f6e657d2e5169394664202e685748754a7b646973706c61793a626c6f636b3b6d617267696e3a30206175746f7d2e6a69356a70667b746578742d616c69676e3a63656e7465727d68727b626f726465723a303b626f726465722d626f74746f6d3a31707820736f6c696420236466653165353b6d617267696e3a303b7d2e425579624b652c2e48736e4642667b6d617267696e2d6c6566743a313670787d2e425579624b652c2e6f4d3247417b6d617267696e2d72696768743a313670787d2e584f377268637b6d617267696e3a30202d313670787d2e6949576d34627b626f782d73697a696e673a626f726465722d626f783b6d696e2d6865696768743a343870787d2e664c745873637b70616464696e673a313470783b746578742d616c69676e3a63656e7465727d2e4c796d38577b77696474683a313470783b6865696768743a323070783b706f736974696f6e3a72656c61746976653b6d617267696e3a30206175746f7d2e4164665872627b6d617267696e2d6c6566743a2d313470783b766572746963616c2d616c69676e3a6d6964646c653b646973706c61793a696e6c696e652d626c6f636b7d2e4c796d3857206469767b706f736974696f6e3a6162736f6c7574653b626f726465722d6c6566743a37707820736f6c6964207472616e73706172656e743b626f726465722d72696768743a37707820736f6c6964207472616e73706172656e743b77696474683a303b6865696768743a303b6c6566743a307d2e4979596145647b746f703a3770783b626f726465722d746f703a37707820736f6c696420233735373537357d2e4543554851657b746f703a3470783b626f726465722d746f703a37707820736f6c696420236666667d2e4165515175627b626f74746f6d3a3770783b626f726465722d626f74746f6d3a37707820736f6c696420233735373537357d2e5943553765627b626f74746f6d3a3470783b626f726465722d626f74746f6d3a37707820736f6c696420236666667d2e4963783643647b6d617267696e3a30206175746f203870787d2e6d41646a51637b746578742d616c69676e3a72696768747d2e75456563337b666f6e742d73697a653a313270783b6c696e652d6865696768743a313670787d2e75456563332e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e75456563332e415037576e647b636f6c6f723a233730373537417d2e724c736879662c2e426d503574667b70616464696e672d746f703a313270783b70616464696e672d626f74746f6d3a313270787d2e773143334c652c2e426d503574662c2e47354e6242647b70616464696e672d6c6566743a313670783b70616464696e672d72696768743a313670783b7d2e47354e6242647b70616464696e672d626f74746f6d3a313270787d2e6e4d796d65667b646973706c61793a666c65787d2e473565466c667b666c65783a313b646973706c61793a626c6f636b7d2e6e4d796d6566207370616e7b746578742d616c69676e3a63656e7465727d3c2f7374796c653e3c6469763e3c212d2d53575f435f582d2d3e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4141656751494278414226616d703b7573673d414f76566177327466496430584346385649396a682d324f35555069223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f733c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e6178696f732e636f6d3c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d224170354f5364223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e536d6172742c20656666696369656e74206e65777320776f72746879206f6620796f75722074696d652c20617474656e74696f6e2c20616e642074727573742e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f706f6c697469637326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417741586f4543416351417726616d703b7573673d414f765661773139685044464b57637a653030627974527851476576223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e506f6c69746963733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f617574686f72732f6e6577736465736b26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a424177416e6f4543416351425126616d703b7573673d414f7656617730315a6b4f30626d50386e4370657152386257474959223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e53746f72696573206279204178696f733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6178696f732d64617368626f6172642d313535303631393334332d64653734306436612d656163312d343539392d383262642d3434303533343537376639322e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417741336f4543416351427726616d703b7573673d414f76566177305658706145566f434f444153557657784f61466652223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e4178696f732044617368626f6172643c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6e6577736c65747465727326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417742486f4543416351435126616d703b7573673d414f76566177316255574b54564d7a4944436d736a34536d614c4755223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e4e6577736c6574746572733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f776f726c6426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417742586f4543416351437726616d703b7573673d414f765661773246776f732d5f6e416d39666e4963486a754b325a53223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e576f726c643c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f627573696e65737326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a424177426e6f4543416351445126616d703b7573673d414f76566177324366426a4a724a72716e5052623241754d614e4953223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e427573696e6573733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e546f702073746f726965733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6e6577736c6574746572732f6178696f732d616d2d61653065626362352d616432352d346531632d623662642d3138633530393464656330312e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351714f63424d4164364241674145414926616d703b7573673d414f765661773148445a7242345437756b327137384256725676426f223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d2272514d516f6420586235565265223e4178696f7320414d202d2041756775737420392c20323031393c2f7370616e3e3c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e3c7370616e20636c6173733d2272514d516f6420614a79694f63223e4178696f733c2f7370616e3e20b72031206461792061676f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f636f756e74726965732d6d6f73742d7269736b2d77617465722d6372697369732d66343066343866392d623032652d346536622d393965642d3136346335636239353333352e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351714f63424d4168364241674145415526616d703b7573673d414f7656617733505243566f5a5532675a494b44336b62615072544e223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d2272514d516f6420586235565265223e54686520636f756e7472696573206d6f7374206174207269736b206f662061207761746572206372697369733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e3c7370616e20636c6173733d2272514d516f6420614a79694f63223e4178696f733c2f7370616e3e20b72031206461792061676f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c6469763e3c6120687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545617574686f7226616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673513646347743586f4543415951416726616d703b7573673d414f7656617733626475774e6f5934356137397a30464775566e566d223e3c64697620636c6173733d226b43725954223e3c64697620636c6173733d226f754a3943622053374a647a6522207374796c653d2277696474683a343070783b6865696768743a34307078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a343070783b6d61782d6865696768743a34307078222069643d2264696d675f312220646174612d64656665727265643d2231223e3c2f6469763e3c6469763e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f7320262331303030333b3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e54776974746572202623383235303b206178696f733c2f6469763e3c2f6469763e3c64697620636c6173733d22526f434c6e65223e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393838373636383733393733393634382533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c5177436e6f4543415951424126616d703b7573673d414f7656617733765932387a4a6374626a64305a365357505a344c63223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5477697474657220686173207265696e737461746564204d69746368204d63436f6e6e656c6c27732072652d656c656374696f6e2063616d706169676e206163636f756e74206166746572207468652070726f66696c65207761732073757370656e646564206561726c6965722074686973207765656b2e207777772e6178696f732e636f6d2f6d697463682d6d63636f6e2623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e313920686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393838333433333933313336363430322533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517743336f4543415951426726616d703b7573673d414f76566177336c4b50674566354d5351724b75784655584d614f41223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4e657720746563686e6f6c6f677920697320726573686170696e67206173736574206d616e6167656d656e7420666f722061206e65772067656e65726174696f6e206f6620776f726b6572732c20616e64206d6f7374206f662074686520696e64757374727920686173206e6f74206b65707420706163652e207777772e6178696f732e636f6d2f61737365742d6d616e61672623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393837373735323139323939353333332533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517744486f4543415951434126616d703b7573673d414f7656617732554b4158715044627577556a736d37626342675874223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5374657068656e20526f73732022667265616b6564206f75742220617420746865206261636b6c61736820746f20686973205472756d702066756e64726169736572206173205472756d70206173736f636961746573207065727375616465642068696d20746f20676f206168656164207769746820746865206576656e742061742068697320536f757468616d70746f6e206d616e73696f6e2e207777772e6178696f732e636f6d2f7374657068656e2d726f732623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393837323436373432383530373634392533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517744586f4543415951436726616d703b7573673d414f7656617733683854556374412d32397252656e584742497a7a5f223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4265796f6e6420726570616972696e6720616e6420696d70726f76696e6720726f61647320616e64207369646577616c6b732c20636974696573206861766520616e206f70706f7274756e69747920746f206275696c6420696e667261737472756374757265207468617420636f756c64206f70656e207570206e6577206d6f62696c697479206f7074696f6e7320616e6420696e637265617365206163636573736962696c6974792c207772697465732045787065727420566f6963657320636f6e7472696275746f722048656e727920436c6179706f6f6c2e207777772e6178696f732e636f6d2f616d642d6c6973612d73752623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393836373138323634333430343830322533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c5177446e6f4543415951444126616d703b7573673d414f765661773056326641356f4573505f6b723267467962676b5568223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e414d442043454f204c697361205375206469736375737365732077697468204178696f732068657220636f6d70616e79277320726573757267656e636520616e6420626174746c6573207769746820496e74656c207777772e6178696f732e636f6d2f616d642d6c6973612d73752623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323120686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f6769746875622e636f6d2f6178696f732f6178696f7326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4150656751494242414226616d703b7573673d414f7656617733486a474b6775516d6342745456445566344d665072223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e6178696f732f6178696f733a2050726f6d697365206261736564204854545020636c69656e7420666f72207468652062726f7773657220616e64202e2e2e202d204769744875623c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f6769746875622e636f6d202623383235303b206178696f73202623383235303b206178696f733c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e636f6e7374206178696f73203d207265717569726528276178696f7327293b202f2f204d616b652061207265717565737420666f72206120757365722077697468206120676976656e204944206178696f732e2067657428272f757365723f49443d31323334352729202e7468656e2866756e6374696f6e2028726573706f6e736529207b202f2f2068616e646c652073756363657373a02e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517757347745486f4543416b51416726616d703b7573673d414f7656617733565033564769536e46355355474c45653730387037223e3c696d6720636c6173733d2255796b5439642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a373270783b6d61782d6865696768743a37327078222069643d2264696d675f32332220646174612d64656665727265643d2231223e3c2f613e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e4178696f733c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e576562736974653c2f6469763e3c2f7370616e3e3c64697620636c6173733d226e5954375162223e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4178696f7320697320616e20416d65726963616e206e65777320616e6420696e666f726d6174696f6e207765627369746520666f756e64656420696e203230313620627920666f726d657220506f6c697469636f207374616666657273204a696d2056616e64654865692c204d696b6520416c6c656e2c20616e6420526f7920536368776172747a2e204974206f6666696369616c6c79206c61756e6368656420696e20323031372e2054686520736974652773206e616d65206973206261736564206f6e2074686520477265656b3a202623373934303b26233935383b26233935333b26233935393b26233936323b2c206d65616e696e672022776f72746879222e203c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f4178696f735f28776562736974652926616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516d684d77456e6f4543416b51427726616d703b7573673d414f76566177306a76706f32364a65425a684a7638496157384e7163223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e57696b6970656469613c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c64697620636c6173733d22766253684f65206b43725954223e3c64697620636c6173733d22415673657066223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e3e3c7370616e20636c6173733d22424e656177652073337639726420415037576e64223e4f776e65723c2f7370616e3e3c2f7370616e3e3a203c7370616e3e3c7370616e20636c6173733d22424e6561776520744164384420415037576e64223e4178696f73204d6564696120496e633c2f7370616e3e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d2241567365706620753278314f64223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e3e3c7370616e20636c6173733d22424e656177652073337639726420415037576e64223e4c61756e636865643c2f7370616e3e3c2f7370616e3e3a203c7370616e3e3c7370616e20636c6173733d22424e6561776520744164384420415037576e64223e323031373c2f7370616e3e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e50656f706c6520616c736f2073656172636820666f723c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22787063223e3c64697620636c6173733d22783534677466223e3c2f6469763e3c6469763e3c64697620636c6173733d226b43725954223e3c7370616e20636c6173733d2270756e657a223e3c64697620636c6173733d22424e656177652077797277586320415037576e64223e4c69626572616c206e65777320736f7572636573206c6973743c2f6469763e3c2f7370616e3e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d434e4e26616d703b737469636b3d483473494141414141414141414f4f5155654c517a3955335343394f4c7a4b537a4d6c4d5369314b7a4648495379307656696a4f4c79314b54693157794d6b734c6f6c6954617a497a43382d7851685866497152433851304d6f38334b726545636a4a79697772536b36434b6b67324b633644693859565a35686e707678676c6658435a333844437549695632646e504477435833376e476b414141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454149223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f332220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e434e4e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d506f6c697469636f26616d703b737469636b3d483473494141414141414141414f4f5155654c537a3955334d444b504e7971334e4a4c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794146536e6c3663586e534b45556b6e6c4a4f52573153516e6752566c4778516e414d566a795f4d4d7339495f38556f3659504c5f415957786b5773484148354f5a6b6c6d636e3541484749687547584141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945414d223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f352220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e506f6c697469636f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d48756666506f737426616d703b737469636b3d483473494141414141414141414f4f5155654c537a395533794d67744b6b68504d704c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794146536e6c3663586e534b45617a5479447a65714e7753796f45594131575562464363417857504c3877797a306a5f78536a7067387638426862475261776348715670615148357853554175376a463070634141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454151223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f372220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e48756666506f73743c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d4e505226616d703b737469636b3d483473494141414141414141414f4f5155654c517a3955335344596f7a6a47537a4d6c4d5369314b7a4648495379307656696a4f4c79314b54693157794d6b734c6f6c6954617a497a43382d785168576e463663586e534b6b5176454e444b504e79713368484979636f734b30704f67696b416d5173586a4337504d4d394a5f4d55723634444b5f675956784553757a583041514142514b584c32514141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454155223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f392220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4e6174696f6e616c205075626c69632052612e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d226d4868796c66223e3c64697620636c6173733d22575a35474a66223e3c6120636c6173733d22714e394b65642220687265663d222f7365617263683f69653d5554462d3826616d703b713d4c69626572616c2b6e6577732b736f75726365732b6c69737426616d703b737469636b3d483473494141414141414141414f4f514d5a4c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794b476671322d515870786564497152433851304d6f38334b726545636a4a79697772536b36434b6b67324b633644693859565a35686e707678676c6658435a3338444375496756747a514146695a305070774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517a4f30424d425a3642416749454159223e3c627574746f6e20636c6173733d2244586b354d65205169394664223e3c696d6720636c6173733d22685748754a2220616c743d224172726f7722207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a323470783b6d61782d6865696768743a32347078222069643d2264696d675f31312220646174612d64656665727265643d2231223e3c2f627574746f6e3e3c64697620636c6173733d22424e65617765206a69356a706620744164384420415037576e64223e4d6f726520726573756c74733c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c64697620636c6173733d22687763223e3c64697620636c6173733d226b43725954223e3c7370616e20636c6173733d2270756e657a223e3c64697620636c6173733d22424e656177652077797277586320415037576e64223e4c69626572616c20706f6c69746963616c2077656273697465733c2f6469763e3c2f7370616e3e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d536c6174652b4d6167617a696e6526616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d4449724e7259776b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a5762787875565730493547626c4642656c4a70786752426b456c7a4d334b636f70796f524c47795a55705656433253564a4b5354715562576c55626c7a796931484b423666564453794d69316a35676e4d5353314956664250544536737938314942452d4b766d72674141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454167223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31332220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e536c617465204d6167617a696e653c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d42757a7a4665656426616d703b737469636b3d483473494141414141414141414f4f5155654c537a3955334d44637279796e4b4e5a4c4b7955784b4c55724d55536a497a386b7379557747737370546b346f7a53314b4c6f3167544b7a4c7a69303878677455626d6363626c5674434f526d3552515870536163594f6345795a735847466c414a694b6c514365506b797051714b4e736b4b61556b486371324e436f334c766e464b4f5744302d6f4746735a467242784f7056565662716d704b5144564e44327173774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945416b223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31352220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e42757a7a466565643c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d4461696c792b4b6f7326616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d453675544b6b796b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a51626d6363626c5674434f526d3552515870536163597751595a6d5255625730416c7a4d334b636f70796f524a67473642736b3653556b6e516f32394b6f334c6a6b46364f554430367247316759463746797569526d356c5171654f635841774447505a396673774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945416f223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31372220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4461696c79204b6f733c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d54616c6b696e672b506f696e74732b4d656d6f26616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d456c4b4b556b336b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a51626d6363626c5674434f526d3552515870536163597751595a6d5255625730416c7a4d334b636f70796f524c47795a5570565641323244596f32394b6f334c6a6b46364f5544303672473167594637454b6879546d5a47666d705373453547666d6c5251722d4b626d35674d415637446f6d62304141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454173223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31392220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e54616c6b696e6720506f696e7473204d652e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d226d4868796c66223e3c64697620636c6173733d22575a35474a66223e3c6120636c6173733d22714e394b65642220687265663d222f7365617263683f69653d5554462d3826616d703b713d4c69626572616c2b706f6c69746963616c2b776562736974657326616d703b737469636b3d483473494141414141414141414f4f514d5a4c4b7955784b4c55724d55536a497a386b7379557747737370546b346f7a53314b4c6f3167544b7a4c7a6930387863756e6e366873596d6363626c5674434f526d3552515870536163594f6345795a735847466c414a63374f796e4b4a6371495278636d564b465a52746b7052536b67356c57787156473566385970547977576c314177766a496c5938386744456f63564e75514141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517a4f30424d425a3642416749454177223e3c627574746f6e20636c6173733d2244586b354d65205169394664223e3c696d6720636c6173733d22685748754a2220616c743d224172726f7722207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a323470783b6d61782d6865696768743a32347078222069643d2264696d675f32312220646174612d64656665727265643d2231223e3c2f627574746f6e3e3c64697620636c6173733d22424e65617765206a69356a706620744164384420415037576e64223e4d6f726520726573756c74733c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c68723e3c64697620636c6173733d226475662d68223e3c64697620636c6173733d22664c74587363206949576d346222206f6e636c69636b3d227870287468697329223e3c64697620636c6173733d224c796d3857223e3c64697620636c6173733d2241655151756220687763223e3c2f6469763e3c64697620636c6173733d2259435537656220687763223e3c2f6469763e3c64697620636c6173733d2249795961456420687778223e3c2f6469763e3c64697620636c6173733d2245435548516520687778223e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f4178696f735f28776562736974652926616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4159656751494252414226616d703b7573673d414f76566177333867573437455f39394a326a683746384149573439223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f7320287765627369746529202d2057696b6970656469613c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f656e2e77696b6970656469612e6f7267202623383235303b2077696b69202623383235303b204178696f735f2877656273697465293c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4178696f7320287374796c697a6564206173204158494f532920697320616e20416d65726963616e206e65777320616e6420696e666f726d6174696f6e207765627369746520666f756e64656420696e203230313620627920666f726d657220506f6c697469636f207374616666657273204a696d2056616e64654865692c204d696b6520416c6c656e2c20616e6420526f7920536368776172747a2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e66616365626f6f6b2e636f6d2f6178696f736e6577732f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a415a656751494178414226616d703b7573673d414f76566177306e6d4a4834444273432d317679714158357832346a223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f73202d20486f6d65207c2046616365626f6f6b3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e66616365626f6f6b2e636f6d202623383235303b202e2e2e202623383235303b204272616e64202623383235303b2057656273697465202623383235303b204e6577732026616d703b204d6564696120576562736974653c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5468657265206973206e6f207761792052657075626c6963616e732063616e206368616e6765206269727468207261746573206f7220637572622074686973207472656e64202623383231323b20616e642074686572652773206e6f7420612073696e676c652064656d6f67726170686963206d6567617472656e642074686174206661766f72732052657075626c6963616e732e206178696f732e636f6d2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e68626f2e636f6d2f6178696f7326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4161656751494168414226616d703b7573673d414f7656617730314959735a763273624653415f745778396f6a6850223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f73202d204f6666696369616c205765627369746520666f72207468652048424f20536572696573202d2048424f2e636f6d3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e68626f2e636f6d202623383235303b206178696f733c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4b6e6f776e20666f722064656c69766572696e67206e6577732c20636f7665726167652c20616e6420696e7369676874207769746820612064697374696e6374697665206272616e64206f6620736d61727420627265766974792c204178696f73206f6e2048424f2068656c707320766965776572732062657474657220756e6465727374616e642074686520626967207472656e6473a02e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e52656c617465642073656172636865733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6a7326616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454145223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73206a733c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6a73223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b68626f26616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454149223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732068626f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b68626f223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6d65616e696e6726616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a364241674245414d223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73206d65616e696e673c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6d65616e696e67223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76756526616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454151223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73207675653c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b767565223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76732b666574636826616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454155223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732076732066657463683c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76732b6665746368223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b72656163742b6e617469766526616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454159223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73207265616374206e61746976653c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b72656163742b6e6174697665223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b74762b73686f7726616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454163223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732074762073686f773c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b74762b73686f77223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b63646e26616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454167223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732063646e3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b63646e223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c666f6f7465723e203c6469763e20203c64697620636c6173733d225a494e62626320787064204f396735636320755550476920426d50357466223e3c64697620636c6173733d226e4d796d6566204d5578476264206c794c776c63223e3c6120636c6173733d226e424445316220473565466c662220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b65693d704d524f586250624472505339414f426d4b5059425126616d703b73746172743d313026616d703b73613d4e2220617269612d6c6162656c3d224e6578742070616765223e4e657874202667743b3c2f613e3c2f6469763e3c2f6469763e203c2f6469763e2020203c6469762069643d226d436c6a6f62223e3c6469763e3c6120687265663d222f75726c3f713d68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d2f536572766963654c6f67696e253346636f6e74696e7565253344687474703a2f2f7777772e676f6f676c652e636f6d2f73656172636825323533467125323533446178696f7325323532366f7125323533446178696f73253235323661717325323533446368726f6d652e302e36396935396c326a306c336a36396936302e3831316a306a372532353236736f75726365696425323533446368726f6d652532353236696525323533445554462d38253236686c253344656e26616d703b73613d5526616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517873384343475126616d703b7573673d414f765661773338654a695f306b32366743644b726d315057417979223e5369676e20696e3c2f613e3c2f6469763e3c6469763e3c6120636c6173733d226b73545534632220687265663d22687474703a2f2f7777772e676f6f676c652e636f6d2f707265666572656e6365733f686c3d656e2d434126616d703b66673d3126616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735135665543434755223e53657474696e67733c2f613e3c6120636c6173733d226b73545534632220687265663d222f2f7777772e676f6f676c652e636f6d2f696e746c2f656e5f63612f706f6c69636965732f707269766163792f3f66673d31223e507269766163793c2f613e3c6120636c6173733d226b73545534632220687265663d222f2f7777772e676f6f676c652e636f6d2f696e746c2f656e5f63612f706f6c69636965732f7465726d732f3f66673d31223e5465726d733c2f613e3c2f6469763e3c2f6469763e20203c2f666f6f7465723e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220686c3d27656e2d4341273b2866756e6374696f6e28297b76617220623d746869737c7c73656c662c643d2f5e5b5c772b2f5f2d5d2b5b3d5d7b302c327d242f2c653d6e756c6c3b76617220663d646f63756d656e742e717565727953656c6563746f7228222e6c22292c673d646f63756d656e742e717565727953656c6563746f72282223736622292c6b3d672e717565727953656c6563746f7228222e73626322292c6c3d672e717565727953656c6563746f7228225b747970653d746578745d22292c6d3d672e717565727953656c6563746f7228225b747970653d7375626d69745d22292c6e3d672e717565727953656c6563746f7228222e736322292c703d672e717565727953656c6563746f7228222e7822292c713d6c2e76616c75652c723d5b5d2c743d2d312c753d712c772c782c793b717c7c2870262628702e7374796c652e646973706c61793d226e6f6e6522292c7a28213129293b66756e6374696f6e207a2861297b6966286b2e636c6173734c6973742e636f6e7461696e732822657362632229297b76617220633d6b2e636c6173734c6973742e636f6e7461696e732822636873626322292c683d6b2e636c6173734c6973742e636f6e7461696e73282272746c73626322293b612626286e2e7374796c652e646973706c61793d22626c6f636b222c633f28672e7374796c652e626f726465725261646975733d2232307078203230707820302030222c6e2e7374796c652e626f72646572426f74746f6d3d2231707820736f6c69642023444645314535222c6d2e7374796c652e626f726465725261646975733d683f2232307078203020302030223a223020323070782030203022293a6b2e7374796c652e626f726465725261646975733d683f22302038707820302030223a2238707820302030203022293b617c7c286e2e7374796c652e646973706c61793d226e6f6e65222c633f28672e7374796c652e626f726465725261646975733d2232307078222c6e2e7374796c652e626f72646572426f74746f6d3d226e6f6e65222c6d2e7374796c652e626f726465725261646975733d683f2232307078203020302032307078223a223020323070782032307078203022293a6b2e7374796c652e626f726465725261646975733d683f223020387078203870782030223a22387078203020302038707822297d7d66756e6374696f6e204128297b672e717565727953656c6563746f7228225b6e616d653d6f715d22292e76616c75653d753b672e717565727953656c6563746f7228225b6e616d653d6171735d22292e76616c75653d22686569726c6f6f6d2d7372702e222b28303c3d743f743a2222292b222e222b28303c722e6c656e6774683f22306c222b722e6c656e6774683a2222297d0a66756e6374696f6e204328297b773d6e756c6c3b69662878297b76617220613d222f636f6d706c6574652f7365617263683f636c69656e743d686569726c6f6f6d2d73727026686c3d222b686c2b22266a736f6e3d742663616c6c6261636b3d685326713d222b656e636f6465555249436f6d706f6e656e742878293b22756e646566696e656422213d3d747970656f6620647326266473262628612b3d222664733d222b6473293b76617220633d646f63756d656e742e637265617465456c656d656e74282273637269707422293b632e7372633d613b6966286e756c6c3d3d3d6529613a7b613d622e646f63756d656e743b69662828613d612e717565727953656c6563746f722626612e717565727953656c6563746f7228227363726970745b6e6f6e63655d222929262628613d612e6e6f6e63657c7c612e67657441747472696275746528226e6f6e63652229292626642e74657374286129297b653d613b627265616b20617d653d22227d28613d65292626632e73657441747472696275746528226e6f6e6365222c61293b646f63756d656e742e626f64792e617070656e644368696c642863293b783d6e756c6c3b773d73657454696d656f757428432c353030297d7d0a66756e6374696f6e204428297b666f72283b6e2e66697273744368696c643b296e2e72656d6f76654368696c64286e2e66697273744368696c64293b723d5b5d3b743d2d313b7a282131297d66756e6374696f6e204528297b76617220613d6e2e717565727953656c6563746f7228222e73637322293b61262628612e636c6173734e616d653d2222293b303c3d743f28613d6e2e6368696c644e6f6465735b745d2c612e636c6173734e616d653d22736373222c713d612e74657874436f6e74656e74293a713d753b6c2e76616c75653d717d6c2e6164644576656e744c697374656e65722822666f637573222c66756e6374696f6e28297b66262628662e7374796c652e646973706c61793d226e6f6e6522297d2c2131293b6c2e6164644576656e744c697374656e65722822626c7572222c66756e6374696f6e28297b4428293b66262628662e7374796c652e646973706c61793d2222297d2c2131293b6c2e6164644576656e744c697374656e657228226b65797570222c66756e6374696f6e2861297b713d6c2e76616c75653b793d21313b31333d3d612e77686963683f4128293a32373d3d612e77686963683f284428292c66262628662e7374796c652e646973706c61793d2222292c713d752c6c2e76616c75653d71293a34303d3d612e77686963683f28742b2b2c743e3d722e6c656e677468262628743d2d31292c452829293a33383d3d612e77686963683f28742d2d2c2d313e74262628743d722e6c656e6774682d31292c452829293a28613d71293f2870262628702e7374796c652e646973706c61793d2222292c783d612c777c7c4328292c753d61293a2870262628702e7374796c652e646973706c61793d226e6f6e6522292c7a282131292c4428292c753d22222c793d2130297d2c2131293b6d2e6164644576656e744c697374656e65722822636c69636b222c412c2131293b702e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e28297b6c2e76616c75653d22223b702e7374796c652e646973706c61793d226e6f6e65223b7a282131297d2c2131293b6b2e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e28297b6c2e666f63757328297d2c2131293b77696e646f772e68533d66756e6374696f6e2861297b6966282179297b4428293b303d3d615b315d2e6c656e67746826267a282131293b666f722876617220633d303b633c615b315d2e6c656e6774683b632b2b297b76617220683d615b315d5b635d5b305d2c763d646f63756d656e742e637265617465456c656d656e74282264697622293b762e696e6e657248544d4c3d683b762e6164644576656e744c697374656e657228226d6f757365646f776e222c66756e6374696f6e2842297b422e70726576656e7444656661756c7428293b72657475726e21317d2c2131293b683d682e7265706c616365282f3c5c2f3f623e2f672c2222293b762e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e2842297b72657475726e2066756e6374696f6e28297b743d423b4128293b4528293b4428293b672e7375626d697428297d7d2863292c2131293b6e2e617070656e644368696c642876293b7a282130293b722e707573682868297d7d7d3b7d292e63616c6c2874686973293b7d2928293b2866756e6374696f6e28297b66756e6374696f6e20622861297b666f7228613d612e7461726765747c7c612e737263456c656d656e743b612626224122213d612e6e6f64654e616d653b29613d612e706172656e74456c656d656e743b61262628612e687265667c7c2222292e6d61746368282f5c2f7365617263682e2a5b3f265d74626d3d697363682f29262628612e687265662b3d22266269773d222b646f63756d656e742e646f63756d656e74456c656d656e742e636c69656e7457696474682c612e687265662b3d22266269683d222b646f63756d656e742e646f63756d656e74456c656d656e742e636c69656e74486569676874297d646f63756d656e742e6164644576656e744c697374656e65722822636c69636b222c622c2131293b646f63756d656e742e6164644576656e744c697374656e65722822746f7563685374617274222c622c2131293b7d292e63616c6c2874686973293b3c2f7363726970743e3c2f6469763e3c212d2d206363746c636d2035206363746c636d202d2d3e3c746578746172656120636c6173733d2263736922206e616d653d2263736922207374796c653d22646973706c61793a6e6f6e65223e3c2f74657874617265613e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220653d27704d524f586250624472505339414f426d4b50594251273b76617220736e3d27776562273b2866756e6374696f6e28297b76617220713d66756e6374696f6e2861297b76617220633d77696e646f772c643d646f63756d656e743b69662821617c7c226e6f6e65223d3d612e7374796c652e646973706c61792972657475726e20303b696628642e64656661756c74566965772626642e64656661756c74566965772e676574436f6d70757465645374796c65297b76617220623d642e64656661756c74566965772e676574436f6d70757465645374796c652861293b696628622626282268696464656e223d3d622e7669736962696c6974797c7c22307078223d3d622e686569676874262622307078223d3d622e7769647468292972657475726e20307d69662821612e676574426f756e64696e67436c69656e74526563742972657475726e20313b76617220663d612e676574426f756e64696e67436c69656e745265637428293b613d662e6c6566742b632e70616765584f66667365743b623d662e746f702b632e70616765594f66667365743b766172206b3d662e77696474683b663d662e6865696768743b72657475726e20303e3d662626303e3d6b3f303a303e622b663f323a623e3d28632e696e6e65724865696768747c7c642e646f63756d656e74456c656d656e742e636c69656e74486569676874293f333a303e612b6b7c7c613e3d28632e696e6e657257696474687c7c642e646f63756d656e74456c656d656e742e636c69656e745769647468293f343a317d3b76617220723d652c763d736e3b66756e6374696f6e207728612c632c64297b613d222f67656e5f3230343f617479703d63736926733d222b28767c7c2277656222292b2226743d222b612b2822266c6974653d312665693d222b722b2226636f6e6e3d222b2877696e646f772e6e6176696761746f72262677696e646f772e6e6176696761746f722e636f6e6e656374696f6e3f77696e646f772e6e6176696761746f722e636f6e6e656374696f6e2e747970653a2d31292b63293b633d222672743d223b666f7228766172206220696e206429612b3d632b622b222e222b645b625d2c633d222c223b72657475726e20617d66756e6374696f6e20782861297b613d7b7072743a617d3b77696e646f772e77737274262628612e777372743d77696e646f772e77737274293b72657475726e20617d66756e6374696f6e20792861297b77696e646f772e70696e673f77696e646f772e70696e672861293a286e657720496d616765292e7372633d617d0a2866756e6374696f6e28297b666f722876617220613d2b6e657720446174652d77696e646f772e73746172742c633d782861292c643d302c623d302c663d302c6b3d646f63756d656e742e676574456c656d656e747342795461674e616d652822696d6722292c6d3d2226696d6e3d222b6b2e6c656e6774682b22266269773d222b77696e646f772e696e6e657257696474682b22266269683d222b77696e646f772e696e6e65724865696768742c413d66756e6374696f6e28682c7a297b682e6f6e6c6f61643d66756e6374696f6e28297b623d2b6e657720446174652d77696e646f772e73746172743b7a26262b2b6e3d3d66262628643d622c742829293b682e6f6e6c6f61643d6e756c6c7d7d2c743d66756e6374696f6e28297b6d2b3d2226696d613d222b663b632e6166743d643b7928772822616674222c6d2c6329297d2c6e3d302c423d302c673d766f696420303b673d6b5b422b2b5d3b297b766172206c3d313d3d712867293b6c26262b2b663b76617220753d672e636f6d706c657465262621672e6765744174747269627574652822646174612d646566657272656422292c703d7526264e756d62657228672e6765744174747269627574652822646174612d696d6c2229297c7c303b752626703f286c26262b2b6e2c70262628673d702d77696e646f772e73746172742c6c262628643d4d6174682e6d617828642c6729292c623d4d6174682e6d617828622c672929293a4128672c6c297d647c7c28643d61293b627c7c28623d64293b6e3d3d6626267428293b77696e646f772e6164644576656e744c697374656e657228226c6f6164222c66756e6374696f6e28297b77696e646f772e73657454696d656f75742866756e6374696f6e28297b632e6f6c3d2b6e657720446174652d77696e646f772e73746172743b632e696d6c3d623b76617220683d77696e646f772e706572666f726d616e6365262677696e646f772e706572666f726d616e63652e74696d696e673b68262628632e727173743d682e726573706f6e7365456e642d682e7265717565737453746172742c632e727370743d682e726573706f6e7365456e642d682e726573706f6e73655374617274293b7928772822616c6c222c6d2c6329297d2c30297d2c2131297d2928293b7d292e63616c6c2874686973293b7d2928293b3c2f7363726970743e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e66756e6374696f6e205f736574496d6167657353726328652c63297b66756e6374696f6e20662861297b612e6f6e6572726f723d66756e6374696f6e28297b612e7374796c652e646973706c61793d226e6f6e65227d3b612e7372633d637d666f722876617220673d302c623d766f696420303b623d655b672b2b5d3b297b76617220643d646f63756d656e742e676574456c656d656e74427949642862293b643f662864293a2877696e646f772e676f6f676c653d77696e646f772e676f6f676c657c7c7b7d2c676f6f676c652e6969723d676f6f676c652e6969727c7c7b7d2c676f6f676c652e6969725b625d3d63297d7d3b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f6a7065673b6261736536342c2f396a2f34414151536b5a4a5267414241514141415141424141442f3277434541416b474277674842676b494277674b43676b4c445259504451774d445273554652415749423069496941644878386b4b4451734a4359784a7838664c5430744d5455334f6a6f364979732f52443834517a51354f6a634243676f4b4451774e47673850476a636c487955334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e2f2f4141424549414367414b414d4249674143455145444551482f7841415a4141414441514542414141414141414141414141414141414267634243414c2f784141734541414241774d434241554541774141414141414141414241674d454141555242694548456a4642453147426b61456959634852464256782f3851414751454141674d424141414141414141414141414141414141514944424155412f385141485245414177454141674d424141414141414141414141414141454341784a4242424d7842662f61414177444151414345514d5241443841754e655843416e4a3644725735706631746450362b794f6870584b2b2b5044622b3265703971664f48644b56325236364c4b4862364632343858744c57366139456b6d64346a4b696c5254487944397763394b66597237556d4d3149595746744f6f43304c4232556b6a4949394b3566313559333151303374686e4d6474774d504c386c455a542b765565645666675271495858536f746a7a67564a7468434d5a33384d377039747836553232667274794a342b767479566c506f7242306f71496e4d5053705a7275354766656a486279576f333044486452362f67565272784a6369323139356c70546a6752394345444a4a376256504e4c574b5a4c76726239776a504962625558564b6451527a4b37664e58764334787931726f797630656438635a58305a4470526d586f682b7879414f61537965645248527737672b687837564265476c346530667841615a6d6b7474716456436d4950626647663841516f44357271554441785541343161496e6a564b62725a59456951334f547a752f7741644255554f4a786b37644d6a487a564f716455365a705a776f68536a6f45644b4b576548647a6e584c53634a64316a5078357a4b664265513867704a4b647562667a4739464b4f4d7441414859555555414734464742525252434742355555555678782f2f32515c7833645c783364273b76617220693d5b2764696d675f31275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414945414141424943414d414141446d6d575965414141416f6c424d5645556949694c2f2f2f38414141415a47526b644852337537753751304e41534568494b6d654156465256675947414f4467352b666e3757317462382f507a643364314c533073784d5447506a342b656e703675727134714b69705a57566d30744c544477384e766232382f507a2f303950546a342b4f38764c783364336547686f596a484255556572416545414137676138416d657133312f496a46674d586135704570654d416b64375336506f61585952666b376f4f6a63346453474d476e2b6f564141416a445141664f6b77684c6a636b4141416256485a4d6b39507941414143636b6c455156526f6765325732334c694d41794748646b4a635135415349434568435464412b774a324c624c2b372f61536e4b67334f4f5a6e646e526431484c78575039746e384a6c42494551524145515241455152442b41347a57326a794d48476a3661397a6e4d3044306662314e30356e784b7142496b6d53706c55356f744e4d2f6d7655475a355149564c614e366e49444c6d74716d374c4d647542527761665051524345566b474634787a504452454743326944494d633855474a414c48597036656e647450497034637458334447624b656877334550615577616c777943495145456533476c5342634e747376636d3458434d762b47474853697a6f353033674b6d4441517772674a7254686537675331764d6359676958756a4c43324d63662f394247326f464b787a7a4d7544444f77554e35616f4c6f2f6f35793654374b5145324e4b536542507938785045766c395135674e78677a4b5341336d434c486a5441463751636e414b6f4275767246635a54484d66734144532b556650626730384b5345374231773137537235685439617235554e3150696e6766496b764c37387a50697157576e4d33475374594f3463517475645046704d5238384b54684665386773766261454e33566e594350346854554842644f67556b726f5a37626379564879652b6f51744f72316a30624331584456514a44362f51756c54634c6b704949616c797168612b732b6435666345724f492f4b724e45414c62416670764d354252326e785a57574c5a4c73686d67414d4d6e48367a7a4a4f39727739416344324f4b6550643345766e2b73526d35417051565930726c4436685768776c7277706542775242396552347a342f6b4f36434e634673395170534e6c355965344d6d5042547446585a6565764c65415778693236746f48646d6d4538393052547452316465676448686255493934326d7747313075787750486d6973647a613563515554544e354f355351766168507870706e4c73646a3671635478667239663361574c364c4d75474e5956416b634a3551316c67557932365254335947532f4457716a7262652b704934334966574a6e6941733577726e4c6f6748774e387273746b3754377858724a623867434949674349496743494c77442f6b4c6b327368546d377038716741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3233275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416231424d5645584d4141442f2f2f2f4b4141444c4141442b2b2f763435655879304e44514e7a666f72713754516b4c72744c54696b5a484d4a53584b44772f34346548484141446f7036663939766235367572383866483031396676784d54676834664f49694c61626d37636433666b6d4a6a78793876575631666e6f4b445453456a55546b3759596d4c4e4768726666332f514d444475766231676d3047614141414758306c455156526f6762576234614b714b68434662636a4b4c44484e637165563758722f5a37774774484d424b70347238394f5154324341575441464732584868635757595855706e337661373571345341363249734e325349713432584861503875344374385642467a5a31564a36382f50675243774941736149732f4b6154634e6c61636d346546395538506a5a484261424d6b714e316d31755442622b4b30507265446d68645a63316f2b373762585852706864346a446a51354376385562676969783033336d387236414e7548695a4f764c452f4f65475746374a5830414d383952656e327346356c67333176573844486b365733756751387a46653367785659414b76773539487a566a37546d507451324131396e6e384d6779386a725950674e6c75654c686265367947654b45447277754d4866716a48484b636376534441626a614f78546e6d333765316f585841526f443369354b6e477654684c4665337145304b6e67766e58385039394c3442376a55696a4d57586174566358706f79317a762f452f3043756965726c61766e346436486b704c506e4e7269794e4f74304a3953486f4734726e50627a51586f4470524c592f6c34715758787736682b727442464d38756b637165525256396e48362b377255395734444c573763384f3364724c634364714c44796374776736753576477a4b4279647053616162325876416e74724d3273514967443858444d4a466c617a4c47734470333637794c702b6b39716f2f79363646544c58753261735633524d537a2b4261565969535037592f616a722f64473156754f574e734a346862614f4c5446705a63756b5834567644614375695734612b6665516856306e74364c392f393041372b7531507942707234612b6c5563464b7133674d6933494c452b68762b6a64676645476f5577456738496a454e30472b3470596b4958416d497245414f6a304d4c4a5a44747865386c64756f556f4278505a3241673136495170366b5265765544412f3661426d5272345767704e44454b3359464d5241727577486235464835547779676253326f2f55506252424744417a4b6b52554f494f4446672b445569524b464a446b667345494a585467476f65342f354457336467384b696d41646b394d2b70557a39794172446c4d416759557638746b734b507732423359397047786c6734436c593973594a506d6f5475513751496c44394f6a47314175463276636974324241656e37345168512b6367524e3732584f394459674d654163726e4157423745787478413556304a646d7271455269516d41596f48336a75452f676a436d4b3038657352474f78465a495842307237774347534e384a73664b4e6b632f41454445744e673159337631444c72436368494e43666d38477a704436693233514d63644644734561695544473746753556486f4654417141537057666f444b744652595a5261654151714266794c3673596a554a3263354f673346342f4134476d4a55742f507641476c6b74485554656b524b4a58526f734374654f5552794e6269425467325a48655051435658387a31757852642f514b566b674d42714f506561462f694a55753851382b424a78377a41674552732b656f2f444a77644b4b505565793978426941396f454b706276712f3650384464316563427161366d526e347a4c4842596d7145634877316377767871456b706d5733506f664d73774d5664587a34586d72715a47336a456f4e73537063344d314134564c65706d6271424e795352506a304474356b5a47716462626e4c6d416d674b5736755a70385a765a674158347a554e737862596c6454626745734931715752513363774d58427a4252355477683256325a714332664d717a31462b6a69544d434678685a53415673584f504f436453556a4e694b4e336f5435775271536b61637752386172596c7a416e55464c4657785436436d5a4f5235757a5931484941702b4d4967634d6b4d4a625049344a6b445542755a51534436694f304d33674749303273456d454f594c34562f6a74757a425a68324c483571517a414d314f3970684d6948726467473542335438794847674f676a37436165645a74744177375a4b44416a773866674c4856326f4f59335574313074754c3567526e653030683138333168667544696851366852616b65674b69413154334e3766504d4278447661526775715436416d674a4764654d46695063303671723336524734534347456b3165393667782b4b6c43635347493269415834395248426b506c4c612f6f486f4c7a4777767966737757493236343862792f454d3345496b4a783731314c4e784257734a6c335746694157495855477a786e6a49764249554479477653595073554f494a5042693632505961536f314a3637726b336c6b79347a304673304f6d4f4245396e77767a572f6b6c554b75726d74776f6f34416c31712b45612f7335664463464c6f427732506a4f67397871316f54373953547449634b5746374179797069644645654a4c3157706555443239664b335a37304d6b334a554b507550784d74556d58314a776e4c596b5941454e6853495a536875676b5933654c5836334c5455305a356f566335624e594d476d6b587266506679576e4746374d6f6d38537a547772567158754839615231386d6c4150512b696177574e767339752b5351672f78336757525777586b47786d414a5561553239466f356c62516f56366335546556674474724b6b4d5864355a5434467950686754716d777a5a446a55435136794a56483535354644657a566b7a76397275416d46796e48356c467035434c5a652f566d397878474a3756477565484f71577565666e62694669522f76443672367a694d7a6c45364957462b6357783257696f7165386266377833613856746a55586c7847547845786e64362f366d674e5349656c476c334f4437704c5262626271746a6b76334466783765365a7176754978327a3374357262522f54667748444f747262744e7643415541414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f33275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416456424d5645584e4742332f2f2f2f4a4141444d414144373850444d44685856556c4c4d4642726e73612f37382f505556316a76794d62504953583032747276797372464141446a6b35585150554c4d42512f787a39444d41416a36362b765351304c2b2b76726e724b7a32354f5471754c6661654866585a4754737672375a6348445961576e6669597664666f48504d54446a6f364854536b765350446e6a6d70765a704856754141414461306c455156526f67653258323361714d42424177345159735642705141533543496a2b2f79636553494a415346307439727a4e6672484e5a546245535449536769414967694149676941496769414973676b6d597141514338614e446a3567616541324c4d4f48475749646e43625876413772706a6f5145504f78784f395a7a423461664b342b5448547a776769304c664a4742743852594c6f314f6a6b4b4c3768387a5a5277433370536d466169692f71474c49597957484d5a65353947547545615a57636433413175766c59366337496948683845396b5044783079343834615747414a6e545342554c7832466774646e5938776c595376685949692f46626f7668653563534c6c6c56456774517363723645736832495673496153352b58707a59526e6d6564366b6d57342b4a2f53566b454a6d695a5574684841596661653036594f48355578344261423938736230324f37564d4f386758676e4a555350484a7570766d43397058476a6635533644437771515a3072495366794d4b4b4251442b2f434b2b4759467141656a7131377756566871696e704f6656444b61526b546e794d354e413966555049495657724449766748496f6d4a72526343416e7a737a484d5a69487235462b6e75316a47376e644b502b70734e677048657a594c395174326c4b7942306c6b3133527731636276514d535a4f634f7173684379525231314e7477726a5175367473583042726464434168394b7446564935596f477134746a6a4c312b696c784f3844634c5a614b6e31685674413475513357574f4a5275467a4a636e5832564c476461644c4549563646787346616f63574f304a4f6162772f6c346f4f696b6b7470775277354833763453326e486b6c394137764c656d5864556b50746955563356386b5457354e6d7353574e46444c6262483561465062596d2f64467235745736674a356561545268326c67572f37456d6c704f646f365479334a357150744d4877366865314c48413456737731554e66444f625347664f4c4f7436544448664962453053665464754644686d6a57526e6c314c5a766952463265795273584d4c2f4c532f78636d5562654d706134792f654c56526e2b654b6645494e4449494e357559525351317054514734466e4f674856785849774c364b456876326969464a35344e7734544c70447049756f422b744c4f646f586370422f716e48756e5432466e2b32587068552f466e4b69612b5567684b4d4d666b777535366b517a737048474b625065746f564d714f5663474950507859534e6c586e575271476a314c566a665a5350306a55446a4b46743138492b2b71764e4f4e2b49335276342b2b3474345345482b755459394c454b3245354658656d4d41586a42356b7058505953656a63435a4a5536376937425365616d6d333355414e4f39435858304f534f7149536d48542f45554476316c4e77714e3371485568766f6a6b2b2f746e614a39432f72484b764375714b7271756d75424c6737634958466e304434564271597838742f6e453571394b735239642b324446776d647651706e61702f5a6a7665335563475a72654241454152424541524245415242454154354466384152776f2b4948597561487741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f35275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414555414141424643414d414141417255397362414141415946424d564555414141442f2f2f2b4b696f724f7a733553556c4c613274716e70366672362b7648783866382f507a7a382f4e61576c712b7672342b506a373239765a7763484376723639396658316b5a47524d544579676f4b41774d44415044772b31746257616d706f6749434156465255714b6971546b354e455245527061576b354f546d4d703246774141414239456c455156525968613257366136434d4243465757565241554655334f3737762b55464539716d6338704d577336764575424c4d3265324b4e354455534c53536631775171386a6d5470467959522f494f574b4d7670446e73554b4b57372b6c4c4f3653756f506958704675516451486f7079436143552b317255424667304e53736c44376a4b50686131697649496f4f6a3862774d6f2b31696b382f2f7044376d70713452594e4f6a6d456b424a464157336f3965677a3333716b67704c2f4957554d74626e613879712b6f4f55334b436b504b57476b4c6b2b39455042553742466333336f454e55383551417072554535386843485258654430764951522f366e4275584151796f4969577144346d39526246427946754c492f3936676a41304c69612b51636a556f47513978744b695451526d366736334b707277677054446a516a5353752b447633473857585777497a7639736d2f4b774b5868456a2b30734e3657304b572f3374323652477666702f792b5351443558365730497a6e3947695530706653696454656c384b4b51366662616f4737486f374548356b43727947644666472b49316f742b37574554794837636f527353696e76306c79596c4963486d4c79505770696f6d6c43495972586c476e2f766a54373447484f4b624932672b587336542f347847394474526663415555624e487170456e636b474f4c576f764e4a47366f674a447a6d672f4c67324334596f745550357a5049386b774b707a2f4b71435262502f424c55706c7139436934365a46432b584f513271592f35504b2b5569306f6d4b4c50757239334745465659547a58776430336c7745467545527259654e7a434b38464f6946456d775956425675555471676f686256514d696b5034674769555766444d6859746951646974632f7555735465446a676a616741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f37275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141417146424d56455541414141795a737a2f4d77372f2f2f2f304e42466246776f70544a497a614e424f546b374c793873725973744f6564497156616f735a644258614a442f4141442f4c77436d57452f4e4b51757071616e2f61463956664e4c4d31753854574d6b2b506a372f7a4d725330744b4868346663334e79337437664377734a3565586d546b355072362b76464b51314b45676767505855794d6a4b656e7035746257332f3239556348427a2b764c622f66572f2f6548442b7070372b366555724b7973524552466a5932502f5755784663744259574668634477426c5555395759486b6636744c75414141427a456c455156526f67653359363036444d42694134627036574f64685536596955416271594e4e4e6e4b4c652f35324a5543627041637570662f7a65784d515136324f686443424345415242454152424550516675314a302f3944686c7a36654b554d6a525964484863446a4532564467516571414151515141414e67444e6a344351506a615a7955674358646c365366657453516f676678633341367a794556334f704b49414f7a725057464f2f7a466d746463484a614445485a31354e736b674a496967472b673674353478596758756e4d6b474235555173776d324e37454339616750685a4f4b6e36494259587a392b6765464a72514e393141387572584d6357494e37775531534374447751684d6f70616f4262585a442b486c6d57496d6b437672427a796938624657685644746c4d444e4d47344a62396c573141564f34422f454b74417a64647741524c447634427a727541355172674c2b4a77494c7335516d4f677a30596241796d41413131446334754762545838396a333466656962416c3032324455466c7338334f304f677863593672326241674133464166656a76594c59693232453176624f33582f67383364687a324232317a6d4f56336d6b6b5479323951787943536430594a434b337042674b486b4d48684b6b306c654c57764474726b67586a464c693565737a3942782b67366b4633322f79304778614e4e4945732f737753654d343371574a696c4f384839376d36623979537a66764a6d4152674141434347414e474a47663150746e3732435461762b62714f716a43336975444632712b687933372b74435759647051424145515241455152436b33546552454559374c6f71374d6741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f39275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414267414141415941674d414141436447645672414141414446424d5645564d615846436866524368665243686654307443505a4141414141335253546c4d4167464a456b47784e414141414c306c455156523441575041447867647742543342544446394155697568644336574e4b2f762f2f2f792b55676772436c53413037455756676c6d4546774141356559534578654377696741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3131272c2764696d675f3231275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414649414141425343414d4141414477386e4f70414141415946424d5645582f2f2f3933476b6c3247556a6d3074794b4e324a39496c43494e46393548557a31376648372b5071424b46584f714c7a392b2f796556337672322b547a36652f446c61336276383678655a6152516d75565358436a59594c77342b7135686143454c6c716f615972486e62505875636d2b6a71657262343751726344697939636a4536764241414144756b6c455156525968653259323961794b6853474253564555544655464866336635632f696943536c562b4e64624a4737306b6a6841636d7a41306142442f39394e4e502f325052754e434b36616345767948764d7130752f7778597a6a744c2f34513371425746487848374b72562f73746b6777614c506b48534b3049344531667731556847686939544d6235416b5662766d496d4753666f636b556f31316b62426c38566449496c7331314558654a416d2b51524c5772754d63354b526439464d6b59516e306b5a76546634714d4d7742385a504157535568634e49304b56584953713339486b727958517356715656565a5051705a357551724a4733536a69634951794f4d576a354f78546262784a53475349384475474e61386755796c686b43436752324c662b5362467058576c52346b5832474e3758506b666d59484841574331717865484a546e5478556a354f6e794c7a475a3043396277767a7a38696965307063686b6e365a79526c79424b6833696667626750506734622f44526c794f7835463958325154497856757739454d696a71534f6d4754464d626166467a4a4758324a4b4d68584632636b714c5039715633684f616855726d5a44394551626a7048787257317348644370686e4e544a446e76717450723130394e33596a6565686c323245372b38673330564f3257377a775937326b6432774f6f6677724d754e61776774706153782f6853526e534a4a764b72795a552f5157535949317543376d53784b4f3731665a424568634b68516b7a7675686a717758506b655767537151394356534f5754597933764745777a322b486d4f6c4d486d5975644930737970364b70494a553177544574506b56536f7177456536436d53354f6c59335235676235426b564568594e536449476772755a2b424c794870424a76306a4d7062384c4c764261386a566367385a6938514871765769396e595243556269752f7141344a4547634d493756724b337271365273493739674c7a74524c6a517370475644626b53506152626b566c78524d616473385132593331596248483748726b3630534e79746f7545714376644634494c4d533431306a4f63375847337051416a2b52355a726b6a76654d686f7265364f784743346b6a62555879794f546d544c78455032337964376c64793267533679794179796e59394957796865707542742b3836525a71425a774f367572354771674236527074755755586269644873616b4b72726a6f544a736d47483437467543626d7a7a4a6a5a63465449336b50434b6a54544131415848704b4b2f5972484a33336d744f68724e2b794e6e5935466e4d3135735742687443376b344a64393469776e45326d355a48576e446577466e745237473235357661547a684433576e6959376844684b45504c7a4d4759615365394f493453334a554f63565568716738523042623677324459756462747531352f6745626d376b53396b64376b6a767176613063455a4d706a504c342b524d49652b567066566375636d2b684a4a792b7168546b436339633439793768584d54713332794e53767849413078694f4c58424e676a67532b584c412b72304249476263734243746d663641624d52646139674d436b672f526e627263464b4a6d5377426444656162474352307654554a3237626a5a796d63424c7256355436727371454a6c44623778437171756459582f7a596f74346634394f58783865656139645050776e39394e4e502f34332b41627851504c6a33385835594141414141456c46546b5375516d4343273b76617220693d5b2764696d675f3133275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d41414144785067523541414141636c424d564558754d794c2f2f2f2f74494144744a513334747250754d522f74496762306a346a326f3537744b5250744667442b2b666a326e706a796533502b39765838337433754c5272764f536a765244627550433334757258765545583936756e316c354c734141443731644c3372367636794d587861562f30683444367a63727858564c796347667857457a76537a2f35774c3378596c6a796447355a6e6968724141414745456c455156526f67653261375a616a4b424347465270554e49716d51324b4d332b6e37763855315569674b5a6e706d31396b357537352f2b675331486f47694b4d70326e454f4844683036644f6a51763667414c555578333564583970384c3362324f426a7343306164724b446b6873682f7777775336626848735272514433527a395a714162375558634173624f546f4f71674c6e3030534a577848496e563158413634324e776a3041377a754e71514a475750346d5349436a4d6b4a6677765051347247424f774531684231754e744c76414231306c773039612f315271627156583866664433727944586e3461546236747069314366545a616677626e6c555873657837644c50456970795646732b7a64644563306b51324e45674334786d5977477a2f493844726252783168487a3557374267583241566a614e2b682f36355462414e74437a646e7765754644486e4454434d6c6454394869766e786841612b2b3843512f48466e4733676241592f775061647a52594a675957564d46757773674772376e58724779435234686d597a6c2b4c46526f4a79313134304c715432346530542f6d506530696f4d70317070686c346e6476674e65766448496f7a2f7947517759703148357070656f4c474b31756a6c6b42526a6371564779546f5230436b2f4e4c546f69362f774b7757477a776a654163656d436e5a6532445167756c65343545415a6c56737067784770474641544735663734416b5662366f6d326146624179377a645450694b57457931454e7337644156494844364b5a5a424d507a5a5863594b3942526f394b73675651445472366f6d38594e4e50706245326a74495947352b57706764496936494e2f4577356f7636716b507a38446a716a65384751692b4e3832683237547937776c65686176667765434c594c725148515a47774256763036485a53386445483538564c2b7a4f7371733534764a4e594a4f6f4f6546674f694663785a64686c4e574b767543356352736f45716b7048417547495a44305a34596f493272664967524233684d323355574a6f437338654d326d7876514e304642457356725a636546485062695336794f734273454e703775465535754e626d745a484674414d517957476a6c646354614e37614b5a322f62446e7743477a79457444657251754843697a68354155592b75695a3969325236576731762b54614435644a686348556937672f4f6e33736d71485265394252682b47386766336c4c6c5634766f354e4345645634685242774c6b5565315843444749362b6e5347733258732b3268634878537348797451686d2b4a786c35344252767658496f4f452b55373938487470617849634f2f5235685a74567535536a734a315a39375558633269333271696e386a34476e2f3177502b334b3574625237526573705457544c6e555775437349483264446a685639614f57617150346b6a31443261356e6c6d36343074594b52746d73636c6d4f494430594d47665664743351515364726d4c4d55754e63382f5279774d42505658696c586d4534764d686b53544c5a2f582b56376f64716261415161706e4d364b634377536f3062504871734d6a634a6e667858317472536873413347397974632b564a366a4b6b655437596161774e634447314f385067484c7542303854515079334b4a4b6362504352324144756b6e36746f7152464570567a6164366944746b61387255654a536654684375454f6f6563535932344d617832374c7748787a4279546c76686b51764b36586c49636d666177623353307253467037395241434d723145552b644d78704c6564453231412f46536a474c7a794e5a724b5939516471544e384f446a6e717a44453543485a37626745436f516f5259796f3038377a65366e2b4138784f70537334326f717067314f684241373150674d67684176326b4a597153786e62426954796b466a53715a6f6c5061566863676146616963384857394e71414c434b366f536743564b32594279524d504f5355466348756d6a6d79786446456864534e4534326d4732416849755454586d6d453666456536544d706943574a4d4533615137684e6f464f63623165516c30626e4c614c51467a64635a2f69526c4c445a52547358486c7441624367662f446e45524c704448584e696a68573841795851484278766541314e38774b394157304673447764463973795a734151627962426d577035556171506a6e7a66704b6c36336e554d36365a2b594e4675425541574c4255687a4f766d4934524b36757249415949734a6c323073314949477053725449784d645868524c716f6b4936426b7979424b7276536647626861383773504b612b3030744b33595a69336845726e4d337270556c7a737258755a726f6b5959674469556c7978526167535144582b6a54327a4234394f5a4559546a577464566d474a654d3451437a573132356e327747336c366258486546636c5a6f32793673477a425664632b77754a616c583732736a5846782b6b5467697274586576667831394235745433705879324743375a553037376a30386f314e4e5a4a6739713845472f7368354731696d6b48456d34533551524e33306230563845326f4765766d6d376b4e4d52592f5155555866426c5659454c5332784a4d61724f2b726c794145497063753151424c57465a7252345476394c77476d70495957665973334e704a4a3769376279524e4c566f7a4c446f54684b7934387153664c4362357a4638356932555a4550562f7072693255326c7a346d3156324b4e33474f537472742b587a7753686b6f4d6a4e706a7448347458482b527847753653676948547030364e436851332b492f674a2f6948506855784b5a387741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f3135275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141413156424d5645582f2f2f2f6a6641446a6567442b2f2f33696467442b2f6672686367447267514438367450366a774436376433392f5066376c41447468674431696744326a67443335395037394f763738655437307148706a525866625144716b437a3133735432343833686677446d6b555838324c50366f4450316c532f766d5437726b68623030375478794a767477492f737549626967436636776f58346e682f76304b726c6a546a6f6744583236642f77785a37686642726e706d3369677833747670546c6e316e6c70574c7172336a767a4c54706c32546e6a457a7670337a757771586f754a6278313862696e573770716f44706c46726b63786e707359336b6479626d6d555030723176347a4a4c367533583272453331707a6e796641416c4e7748774141414e4f6b6c455156526f67653161435865627568494749555143735931494d4574365578616e45474e6a77456e644e4c6b70625872372f332f536d7846657744464f2b3972653838343731546c3144466f2b7a66624e534b346b2f576c2f6d6954706a764c76417159732f6e634259325a6e36722b45705a704f6b6b526c3551572f46555a426d366e635361496f4468304c704d75383548666953524a33677a444b484175526c314f4657776c3166694f674730616861786e71536a47347362793965372f696269616276774e4b6b525365784a4659652f7268357537526c5054567a6430484b6244693334496f57566b5563496c7a2b4d72763339383833457553385948666378377965577239636a676c695630442f6b342f6d737370574f2b653636725264446d426c4e37785834786e785948424a5555314832372b2f6d67595169442b734a713647495768593651706c33346c366269527a706533793655424b452f4c6577752b54766e7138655a6d4b5a416a79357a4d6a562b495a325a63637039752f6e34414b62677133542f64664870554662363865586f537174526a3157585672304a554a434d7955484f3371395855584b71414b443038725a61415051554f554666674e30346b42617871614f46486d3272736b614d6942655953564b644d5665582b34656239453677364258633137724558465875376c494b466c4c487352385341565254446955732f54564f2f7142795541324d503133534d3230635142722f6533393138584973396658783457454a77726a37646656436c49414d6d2f333545574a6f48706363596f35544370383238644747756a6349742f75456a35782f76707841504b78302b5631504f7265586a337a667751706f7570616b687157456d56665148614e55745a45706b797168334d523566654d7932575435763071756c532b7239394f6e75426f4d4e325852353933674c2b6c773933433978543462705a4658783643564759627666475278475261684d364b514954554f465a7568426d524a477834474b4b6f5552717252386646677041516362547a393875734e34554131754a6d57614531414c4a64544f72474c6d66422b694a594e3065597955714268476f306f6556446c6a4261416c316e513152564c6a397838665079355269397930544366326852474954414253546f73713043635439377438315a68545670677730417a6e5256484d4936665256557a735757796f7a764c6d41373551514c59555a654d414e674555496f4e654743506a4b6a41744c756d754b6166485357367a47576353414942546778454a7754586b306855305855337373614d75507a6f636542706b57306d75365251455147515a4a5a756c5253783242794670756645386d666d39524f3434727237656a6d4c6f7742636c6f374973316b46513667665971382f424f4a6c6a38506550364a4f4b75386852697a434b73627859424d337975684e557151632b586c5330364b4e564d70756b666855366131413342654667475a764b6e6b647444424130494d696432677755646676707653475a3551785641426133535a3370584b774d6476426e464e2b4467744f616c54324151686a302f334673416c63795849616d6c646941345559467247485861466770385364317348726931687832497850687a346e516f384b64796f4d6c7945597a6146453250317a4a4351386a73676544714a2f46384a336d6d623774567430596c4a64487770526d4465487470417a5870537864514671436e5668426e446236625a7948356e355a5a566e655536334738364b2b386968722f47514738685549703570516b6f574e2f536f3070616952696b5179557970304d733461492b6978543344487370444c5330736f347a6a475645425a654241526d77484c627979775142724a636c7330576d656754726541674335637a6c4f663179674c545350554a444968452f71464c55304b384952326367706d374768394448566d56736f32556d39513037576e676a337a79704855424879504641587a4f5a71496f5249555351304b4559516757516f6875423936536b624a41566f3158644f4569712f784b46555059567255474754394151717264456b76684d6f4c6e734c6e584e6a4f4c46435473435566597643414b416f75394c4a6142616468784b766e433064764a716d7863417268414c5278426b595758457053384c7859435169424f67495330344b674c6d6b6148366d415935496e69643539742b594b614f6b3852426f4e476d334b746c66456933672b526a6f68794b62365975794471424e427a55614a5157446e432f306730726f5a637a7333793636755a6449347449416c525741347775666f4c425231764b71363155526f452b5253515a57576a7a7970566a614d6b6550584b444f344b6f776f366567625a524f71417852505a737a50664d51726d7a4a61444c55574535446172674e3834503745456d6f67496c526579516e6f536c62534b617a4b4f49497a5546586e744b454b4975646774424c4e704b7933696145497a6b396e4651686b2b5a6a52462f435938316678444e77536a77377241644a6f4261456f484b3851626d68594a684237737a3048765a546c4652786169472b7046534835556573317a635777304d6442377744754c487749725654486f45344b344473354c7a4a584646475a54443343434a59676754716e4e483431772f4b7342713752493538654f65594144356432444869574c33684863456770504e2b7462524763784450564f614842712f717357424942305537314e44326f6a653138437a626b354f766f4545514e365263316d3077457363774e55436b4162707054654634644748754c38594b526770656f564f36422f6a6331683246425357426961644275657472435130677952796e4e4259526c376b4a55554c6f7450335655425756374b755a7a34413861524d4b546e5a6d2f586439504a396977376c336e625253315a4774713230484f49714d3532467649526f51754e6b3463436b3244545a554f6e7544305764794d4356697838656c4e496f4d3932757969456c63456b726e6d377162794578555a556b2b77325359576c4e746a537967327838436d324153615752533132475735325542696232714f6c684249595153344558705355596e6c2f72794b36367277476b716c644f343258474241766f72567459534f344e3563654b496f594c4d696c367661677856537652454c2f73563270625941642b6f4476302f6e5a73556735775563366d48466764494a3868415269574d3246304b61516f556237634555396851466872736f2f43696569486f6e72584d303455374851496178514e7a774b476c68676d4e414a7471716a417450424f71454d5978556a6d6e57744c3159646c755559516d6c43474e6a483072773962364a434b2b7432786f5162736865776e5a55744b6141587550626a72546d584463555a346e41593432553873534459544e33742f756b71442f58496c74646c6339466d712f334c644c594267386a57325433744b35434b4a69686d55465531716d6f4d325235374a6c626c30326174586b6c6c4e5559414136656d36556b4d362f6e4f5661487773316d6564346b764265485954316c45435237784771345765566a535261466a754d45436251677159786d70795a6b4b747155675836727a6e564c767832316f7444304638464c796a596639555a6174524f7a687557416a39554146416175726b50524e747659533958424138453865627972354257706d70464f30424c6d69663439366b4f37624f6d47367934495a4f716362354b6c6f613831596b4174587252337937757370455232687947673673694f33797a6f77514943527751353549597143397a3167716f494f5967513072706a656e454d4d7964647644537a6a755a4a7330724a2b6b5167693978414356535969626d656f70666f664939746d62715153725848756f5630504339543175564d4d516e4f476e4a64786c41494d4b5152747569627265427070447435612f4365746b2f534f314f497542512b554279706c51713276397669754157372f747763457072513337773869756441475541365732356e79694f41614d645a366b4d5431774e6b632f4b43343246777a43497847344f564f3468302f446f6768634e313572696d446f514441524c45425a366e6f486e56676668744e33644771705475696167663878717347576764384937694651684d427a6a47354b2f39504d4342712b7439382f6366384646435173756675423958387a3054436f55647538576b6e754346677a72346a7173575657516a7236745463757a32792f2b357133456a6878716b5342685a6b306254614e3476346b39656a484e303745714a684950765774702f32763676726c4e62737a4754637367776534332f334c72484d562b2b2b71565837582f612f3262375152736648373435632f534d4f6a382f747a70394f727a5a6352322f684d645737776b386e6a514c4e6c39664c4b75494b514a5076317750627072686e702b6230764230654e6d5a64546b38506233657a7462666e5a362b32394b47496e71666d306a344446387644346877636746543849767a35757a30644c6462342f6b4d73553750686c3836347939507a3971414d4f6c646936663432646e5a6c57416e3638335a326274447565766b4c2b6941762f71625578693841377947523967687648767542355432416457764d45316f2f424b6d667a356754365542564b527a4242687541666b376741494a34632f587a6a365041307268734f6c57727358307778494b6c594a4570322b2b624731344d6b4c5a54416c322f4f616b50523441523063415964377050336945523145504d6e38624d4f784d504c7343306237412b70304566446b364b71454651434d41636c476a42354e514737436c6767336735664273314e454d7642683241643932317232472f6e4f7830654868484e5143484c595338516251484936473139327747413362586a6f616451477848397a736e2b466f644c67304f666c724e477741523375414977513865547361646c537a447a6a73416972577851676d5771505273443663764c65417346426270594f52414c7a385042792b365155456c513648585543706867484f35546363645a4339476b4246414c596c58414e2b67666544397333554869444d3367504541654831743948686f4e67434b732b48415a2f4434556872543330464550783041455a4d766f314750655551417236786f756774614c316c5a584d4e57434c416c355a7958675530726f624469355042384f74687641625130595a6f3552616c624143726b7776776d7462345142747062634442384f31652b523343694f543532376c307544574173487a582f5465416f54556544713561485945323048592f34756a6a7757425051736b5a444c546e685051567443642f44515a6a52394d47304671324d7545524152336c737a6277576c346a414c63624f41536f587730474638355833704e685432444b57502f7935545073712b3443347630386c36344249646831584c3471496337346c6a67393578774641532f575579396167466f4443446d66447251575356322b4b7145596374316236514b67686b426d48364144674e4865616c304a3932585259613766653237635347694f4e61304e364347674969574a4b512b3063696645433556714c77417438496a443139685349356c32434642754a4e5249634b567072534e6b6f476d76417372616f502f5132512b6f43554161786c547a646e6544416446494731445458746751414c556a67424e74432b6931415455425346695a554533626364424c77497344456d72392f77477141376a7a4c484d394b556b632f434670393139453967466c4d745a6a614e4675376d7541736a5a6541354979324f775741655653614e4c305a46707570516a7765726b4c364e68346f3746547245586b3434414541546e2b6173593278333246343732744c616a4838676c4a6577454a47627455396d537641796a334137707251476e426d6d7668426c434b6d4f77316c305a4741524a733577633273376658765a492b5933622b516b4a343767384c39784e4d77533971525a6d3975394177596e6755706c4f634d41793363617a44512b74734155384a666f54687a6f5947767579394e75415a39445a6654566971355848773246774a39303374613072727336667a5545632f7a7038542b702f32702f312f742f38412b724a44466c344139755541414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3137275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416231424d5645575a4141442f2f2f2b574141435a45684c7a354f535341414474323976713164585772713743686f61765656586e304e432f666e372b2b2f763538764b705330754e414143694f546e66774d446275626d5a4367717153456a323675713965586e52704b53474141446b794d6969516b4b36635847684e44544a6c4a537457566d644b69716149534764485232775947433161576b305454574c4141414376306c455156526f6765325759584f6a4942434764654e714c4e56456a4d6247746b6d622f502f665747424230574454752f4e6d626d37322f59514c374f4d43757842464c42614c78574b7857437757693856697356697366307962425230696d4a73414944544a39335a59374848434e416d7150554c547a6d785a2f626f787944642f5576734f6f7a63784759386859426d486c654d755945324f326b6c5654477744454d36547765304b77446a756c5074714f756e7369466a2f45664170334b465763427068584476484d4c5758693843796c764a6b42376c6d6a7057553076374f53526c623636626f34564c4c4f7445687044546e772f4a793353316f5746624c4c53774278517675477a744f4e63316135684168496e6b734b745838644f76566f65725a622f562f584d6c794a4d2b5971625a384a64767a486b4d42456a446252324158554a30736175626143566a677a767549552f32424769695177685947434f5a487a3065374a6148776667324975563331616744754f7a4a56777a2b30384c7761454b357545776367374f6959644b5a667539726962774450433043374f3855594964684e31716b495a6933376878466d716954495759537471545233533270505462735a675743585761556943724f2f6a34415267436d524870427331476d4250514b364c5978504f414b6a4379574f536b57546e506c6a6f4f5036774e46716765393533746b7a47736533385a53717543516470452f5535366634694659425469534e4a77654547316d504a6b4d6b7267394d71617734594252524b6c494e754d4c6177474a3767436b514b52564e5257763159566746574769565a646f3177385867674e434d4662746243316a6365715871416a6834475a63557831586f56774d3264374e486f4574464e64643872674c6366514e307165694b30393848326c5455536668546f436f6a376a355556364f37783754563359657a797733785252394f6f63786a4b756f455656507339625230487872506653704552754e4b49635357526a6171535365777949536f76542b2b6e49526f615843717468636f46585553536945536370514963566f737037506e556f6f42612b4c4e39743830543242547354334d58306a46556f687a59425943706a3751383675416b626b5664524a4f6763464831426f52556970576438446c434a736b3955585076706c56657341334d5a695478747939535772324749552f4a666a794a754a4d49665053684f453734476d4a78324b78574377576938566973566773466f7646597630332b67492b6453615a504562466d6741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f3139275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220653d27704d524f586250624472505339414f426d4b50594251273b2866756e6374696f6e28297b76617220613d652c623d77696e646f772e706572666f726d616e6365262677696e646f772e706572666f726d616e63652e6e617669676174696f6e3b622626323d3d622e74797065262677696e646f772e70696e6728222f67656e5f3230343f63743d6261636b627574746f6e2665693d222b61293b7d292e63616c6c2874686973293b7d2928293b2866756e6374696f6e28297b2866756e6374696f6e28297b676f6f676c652e637363743d7b7d3b676f6f676c652e637363742e70733d27414f765661773144704144365477704f5055674e344e6f72467065555c7832367573745c78336431353635353239363336323834373838273b7d2928293b7d2928293b2866756e6374696f6e28297b2866756e6374696f6e28297b676f6f676c652e637363742e72643d747275653b7d2928293b7d2928293b2866756e6374696f6e28297b77696e646f772e78703d66756e6374696f6e2861297b66756e6374696f6e206528682c662c67297b72657475726e227870222b282278223d3d663f2263223a227822292b677d666f722876617220623d2f5c62787028787c6329285c643f295c622f3b613b297b76617220633d612e636c6173734e616d652c643d632e6d617463682862293b69662864297b612e636c6173734e616d653d632e7265706c61636528622c65293b6966282263223d3d645b315d29666f7228613d612e676574456c656d656e747342795461674e616d652822696d6722292c623d303b623c612e6c656e6774683b2b2b6229696628633d615b625d2c643d632e6765744174747269627574652822646174612d6c6c222929632e7372633d642c632e72656d6f76654174747269627574652822646174612d6c6c22293b627265616b7d613d612e706172656e74456c656d656e747d7d3b7d2928293b676f6f676c652e647274792626676f6f676c652e6472747928293b3c2f7363726970743e3c2f626f64793e3c2f68746d6c3e", + "rawHeaders": [ + "Content-Type", + "text/html; charset=ISO-8859-1", + "Date", + "Sat, 10 Aug 2019 01:21:31 GMT", + "Expires", + "-1", + "Cache-Control", + "private, max-age=0", + "P3P", + "CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"", + "Server", + "gws", + "X-XSS-Protection", + "0", + "X-Frame-Options", + "SAMEORIGIN", + "Set-Cookie", + "1P_JAR=2019-08-10-01; expires=Mon, 09-Sep-2019 01:21:31 GMT; path=/; domain=.google.com", + "Set-Cookie", + "CGIC=IiFhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L3BsYWluLCAqLyo; expires=Thu, 06-Feb-2020 01:21:31 GMT; path=/complete/search; domain=.google.com; HttpOnly", + "Set-Cookie", + "CGIC=IiFhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L3BsYWluLCAqLyo; expires=Thu, 06-Feb-2020 01:21:31 GMT; path=/search; domain=.google.com; HttpOnly", + "Set-Cookie", + "NID=188=vTMutucOBO-Yl5bpVtVnzkN1voOukQ24RkD0wuuzeNL_BDPMEB90MqBF06HFaILh_fs-PO8JGLhIjkSb3nxl9Rzf8L7CxJtk_yJF0aEgi2znY0rMT_dQr6_5tYfVNKU9u0d2BoXOVOWHEN3ZzaD7q6yRUb44yH3vjL0kue6Ki0s; expires=Sun, 09-Feb-2020 01:21:31 GMT; path=/; domain=.google.com; HttpOnly", + "Accept-Ranges", + "none", + "Vary", + "Accept-Encoding", + "Connection", + "close" + ], + "responseIsBinary": true + } +] diff --git a/packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem b/packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem new file mode 100644 index 000000000..e2b79024d --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqzCCARQCCQDLcUeJsLDL5jANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJD +QTELMAkGA1UECAwCUUMwHhcNMTkwOTI5MjIwMDI2WhcNMTkxMDI5MjIwMDI2WjAa +MQswCQYDVQQGEwJDQTELMAkGA1UECAwCUUMwgZ8wDQYJKoZIhvcNAQEBBQADgY0A +MIGJAoGBALhfi1dwIyC1Jha4N/j/VtlPPi+j+SZQGZqLNVVgzzGY7+cc3VkCySZD +yXh3Z+/ftp9DDKdHRutJQE0R4peSDussC/IQDJKzuKN/O9S6tnNlgUr5YZLRENxL +FSJIY5cIkty50IrEhlN5QeDJP8p4yrYq9J6M0yzyfdqIWI3CBqbzAgMBAAEwDQYJ +KoZIhvcNAQEFBQADgYEArnOeXmXXJTK39Ma25elHxlYUZiYOBu/truy5zmx4umyS +GyehAv+jRIanoCRWtOBnrjS5CY/6cC64aIVLMoqXEFIL7q/GD0wEM/DS8rN7KTcp +w+nIX98srYaAFeQZScPioS6WpXz5AjbTVhvAwkIm2/s6dOlX31+1zu6Zu6ASSuQ= +-----END CERTIFICATE----- diff --git a/packages/opentelemetry-plugin-https/test/fixtures/server-key.pem b/packages/opentelemetry-plugin-https/test/fixtures/server-key.pem new file mode 100644 index 000000000..405c5fa0d --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/fixtures/server-key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC4X4tXcCMgtSYWuDf4/1bZTz4vo/kmUBmaizVVYM8xmO/nHN1Z +AskmQ8l4d2fv37afQwynR0brSUBNEeKXkg7rLAvyEAySs7ijfzvUurZzZYFK+WGS +0RDcSxUiSGOXCJLcudCKxIZTeUHgyT/KeMq2KvSejNMs8n3aiFiNwgam8wIDAQAB +AoGBAKBztcYQduGeBFm9VCjDvgc8KTg4kTlAeCfAglec+nOFTzJoMlGmVPuR/qFx ++OgOXtXW+goRw6w7gVQQ/os9tvCCp7awSC5UCfPejHh6bW2B0BF2lZJ6B9y+u5Fa +/p8oKoJGcC4eagVnDojuoYJHSqWBf7d7V/U54NpxwgBTsHAhAkEA8PJROgWzjMl2 +Gs5j8oBldEqzrC/d4K1uMEvCTb4RJ+t6jWq+Ug/vqvCfIcLfxHbOmTbOHTfhpv/d +NUf9eDyBGwJBAMPkZaHP5vPDd900MqypLVasollzxgPnMUg35EEQJLAbb/5xG3X9 +ZbaVDTRtLQYNFvDZLlTpRpCPxZCgrn9hJwkCQQDPEVChLrkpqxFm5CydAZ8vG+vh +dJmYNzPVKaZorYmM5yBBXJUHbU6pd3UqzJEGBJx0q9bi4V156bYvzhiVNlo1AkBu +1hbvFCwPtoRmg3c8nEhL50fApzHd2XzX6M/cRF8Nyah3ZdXsz6AyS2l6RV+ZMeTO +B4QghRDpEH/vUgsJhZXJAkB5GQZPJh6/kozc5+Ffc60ThN/58SX0KEFeKnWRlzfr +vfBXwcmaz1oNXN+kcWdLnKbr/tx+3UQ6weRRmeYX/hOi +-----END RSA PRIVATE KEY----- diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts new file mode 100644 index 000000000..8190ba8d9 --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts @@ -0,0 +1,95 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracer } from '@opentelemetry/node-sdk'; +import { Http } from '@opentelemetry/plugin-http'; +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as https from 'https'; +import { AddressInfo } from 'net'; +import * as nock from 'nock'; +import * as sinon from 'sinon'; +import { plugin } from '../../src/https'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpsRequest } from '../utils/httpsRequest'; + +describe('HttpsPlugin', () => { + let server: https.Server; + let serverPort = 0; + + describe('disable()', () => { + const httpTextFormat = new DummyPropagation(); + const logger = new NoopLogger(); + const tracer = new NodeTracer({ + logger, + httpTextFormat, + }); + before(() => { + nock.cleanAll(); + nock.enableNetConnect(); + + plugin.enable((https as unknown) as Http, tracer, tracer.logger); + // Ensure that https module is patched. + assert.strictEqual(https.Server.prototype.emit.__wrapped, true); + server = https.createServer( + { + key: fs.readFileSync('test/fixtures/server-key.pem'), + cert: fs.readFileSync('test/fixtures/server-cert.pem'), + }, + (request, response) => { + response.end('Test Server Response'); + } + ); + + server.listen(serverPort); + server.once('listening', () => { + serverPort = (server.address() as AddressInfo).port; + }); + }); + + beforeEach(() => { + tracer.startSpan = sinon.spy(); + tracer.withSpan = sinon.spy(); + }); + + afterEach(() => { + sinon.restore(); + }); + + after(() => { + server.close(); + }); + describe('unpatch()', () => { + it('should not call tracer methods for creating span', async () => { + plugin.disable(); + const testPath = '/incoming/unpatch/'; + + const options = { host: 'localhost', path: testPath, port: serverPort }; + + await httpsRequest.get(options).then(result => { + assert.strictEqual( + (tracer.startSpan as sinon.SinonSpy).called, + false + ); + + assert.strictEqual(https.Server.prototype.emit.__wrapped, undefined); + assert.strictEqual((tracer.withSpan as sinon.SinonSpy).called, false); + }); + }); + }); + }); +}); diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts new file mode 100644 index 000000000..ce0f2b797 --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts @@ -0,0 +1,481 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracer-basic'; +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracer } from '@opentelemetry/node-sdk'; +import { + Http, + HttpPluginConfig, + OT_REQUEST_HEADER, +} from '@opentelemetry/plugin-http'; +import { CanonicalCode, Span as ISpan, SpanKind } from '@opentelemetry/types'; +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as http from 'http'; +import * as https from 'https'; +import * as nock from 'nock'; +import { HttpsPlugin, plugin } from '../../src/https'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpsRequest } from '../utils/httpsRequest'; + +let server: https.Server; +const serverPort = 32345; +const protocol = 'https'; +const hostname = 'localhost'; +const pathname = '/test'; +const memoryExporter = new InMemorySpanExporter(); + +function doNock( + hostname: string, + path: string, + httpCode: number, + respBody: string, + times?: number +) { + const i = times || 1; + nock(`${protocol}://${hostname}`) + .get(path) + .times(i) + .reply(httpCode, respBody); +} + +export const customAttributeFunction = (span: ISpan): void => { + span.setAttribute('span kind', SpanKind.CLIENT); +}; + +describe('HttpsPlugin', () => { + it('should return a plugin', () => { + assert.ok(plugin instanceof HttpsPlugin); + }); + + it('should match version', () => { + assert.strictEqual(process.versions.node, plugin.version); + }); + + it('moduleName should be https', () => { + assert.strictEqual('https', plugin.moduleName); + }); + + describe('enable()', () => { + const httpTextFormat = new DummyPropagation(); + const logger = new NoopLogger(); + const tracer = new NodeTracer({ + logger, + httpTextFormat, + }); + tracer.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); + beforeEach(() => { + memoryExporter.reset(); + }); + + before(() => { + const config: HttpPluginConfig = { + ignoreIncomingPaths: [ + `/ignored/string`, + /\/ignored\/regexp$/i, + (url: string) => url.endsWith(`/ignored/function`), + ], + ignoreOutgoingUrls: [ + `${protocol}://${hostname}:${serverPort}/ignored/string`, + /\/ignored\/regexp$/i, + (url: string) => url.endsWith(`/ignored/function`), + ], + applyCustomAttributesOnSpan: customAttributeFunction, + }; + plugin.enable((https as unknown) as Http, tracer, tracer.logger, config); + server = https.createServer( + { + key: fs.readFileSync('test/fixtures/server-key.pem'), + cert: fs.readFileSync('test/fixtures/server-cert.pem'), + }, + (request, response) => { + response.end('Test Server Response'); + } + ); + + server.listen(serverPort); + }); + + after(() => { + server.close(); + plugin.disable(); + }); + + it('https module should be patched', () => { + assert.strictEqual(https.Server.prototype.emit.__wrapped, true); + }); + + it("should not patch if it's not a http module", () => { + const httpNotPatched = new HttpsPlugin(process.versions.node).enable( + {} as Http, + tracer, + tracer.logger, + {} + ); + assert.strictEqual(Object.keys(httpNotPatched).length, 0); + }); + + it('should generate valid spans (client side and server side)', async () => { + const result = await httpsRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + const spans = memoryExporter.getFinishedSpans(); + const [incomingSpan, outgoingSpan] = spans; + const validations = { + hostname, + httpStatusCode: result.statusCode!, + httpMethod: result.method!, + pathname, + resHeaders: result.resHeaders, + reqHeaders: result.reqHeaders, + component: plugin.component, + }; + + assert.strictEqual(spans.length, 2); + assertSpan(incomingSpan, SpanKind.SERVER, validations); + assertSpan(outgoingSpan, SpanKind.CLIENT, validations); + }); + + it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => { + const testPath = '/outgoing/do-not-trace'; + doNock(hostname, testPath, 200, 'Ok'); + + const options = { + host: hostname, + path: testPath, + headers: { [OT_REQUEST_HEADER]: 1 }, + }; + + const result = await httpsRequest.get(options); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(result.data, 'Ok'); + assert.strictEqual(spans.length, 0); + }); + + const httpErrorCodes = [400, 401, 403, 404, 429, 501, 503, 504, 500, 505]; + + for (let i = 0; i < httpErrorCodes.length; i++) { + it(`should test span for GET requests with https error ${httpErrorCodes[i]}`, async () => { + const testPath = '/outgoing/rootSpan/1'; + + doNock( + hostname, + testPath, + httpErrorCodes[i], + httpErrorCodes[i].toString() + ); + + const isReset = memoryExporter.getFinishedSpans().length === 0; + assert.ok(isReset); + + const result = await httpsRequest.get( + `${protocol}://${hostname}${testPath}` + ); + const spans = memoryExporter.getFinishedSpans(); + const reqSpan = spans[0]; + + assert.strictEqual(result.data, httpErrorCodes[i].toString()); + assert.strictEqual(spans.length, 1); + + const validations = { + hostname, + httpStatusCode: result.statusCode!, + httpMethod: 'GET', + pathname: testPath, + resHeaders: result.resHeaders, + reqHeaders: result.reqHeaders, + component: plugin.component, + }; + + assertSpan(reqSpan, SpanKind.CLIENT, validations); + }); + } + + it('should create a child span for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 200, 'Ok'); + const name = 'TestRootSpan'; + const span = tracer.startSpan(name); + return tracer.withSpan(span, async () => { + const result = await httpsRequest.get( + `${protocol}://${hostname}${testPath}` + ); + span.end(); + const spans = memoryExporter.getFinishedSpans(); + const [reqSpan, localSpan] = spans; + const validations = { + hostname, + httpStatusCode: result.statusCode!, + httpMethod: 'GET', + pathname: testPath, + resHeaders: result.resHeaders, + reqHeaders: result.reqHeaders, + component: plugin.component, + }; + + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.strictEqual(spans.length, 2); + assert.ok(reqSpan.name.indexOf(testPath) >= 0); + assert.strictEqual( + localSpan.spanContext.traceId, + reqSpan.spanContext.traceId + ); + assertSpan(reqSpan, SpanKind.CLIENT, validations); + assert.notStrictEqual( + localSpan.spanContext.spanId, + reqSpan.spanContext.spanId + ); + }); + }); + + for (let i = 0; i < httpErrorCodes.length; i++) { + it(`should test child spans for GET requests with https error ${httpErrorCodes[i]}`, async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock( + hostname, + testPath, + httpErrorCodes[i], + httpErrorCodes[i].toString() + ); + const name = 'TestRootSpan'; + const span = tracer.startSpan(name); + return tracer.withSpan(span, async () => { + const result = await httpsRequest.get( + `${protocol}://${hostname}${testPath}` + ); + span.end(); + const spans = memoryExporter.getFinishedSpans(); + const [reqSpan, localSpan] = spans; + const validations = { + hostname, + httpStatusCode: result.statusCode!, + httpMethod: 'GET', + pathname: testPath, + resHeaders: result.resHeaders, + reqHeaders: result.reqHeaders, + component: plugin.component, + }; + + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.strictEqual(spans.length, 2); + assert.ok(reqSpan.name.indexOf(testPath) >= 0); + assert.strictEqual( + localSpan.spanContext.traceId, + reqSpan.spanContext.traceId + ); + assertSpan(reqSpan, SpanKind.CLIENT, validations); + assert.notStrictEqual( + localSpan.spanContext.spanId, + reqSpan.spanContext.spanId + ); + }); + }); + } + + it('should create multiple child spans for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs'; + const num = 5; + doNock(hostname, testPath, 200, 'Ok', num); + const name = 'TestRootSpan'; + const span = tracer.startSpan(name); + await tracer.withSpan(span, async () => { + for (let i = 0; i < num; i++) { + await httpsRequest.get(`${protocol}://${hostname}${testPath}`); + const spans = memoryExporter.getFinishedSpans(); + assert.ok(spans[i].name.indexOf(testPath) >= 0); + assert.strictEqual( + span.context().traceId, + spans[i].spanContext.traceId + ); + } + span.end(); + const spans = memoryExporter.getFinishedSpans(); + // 5 child spans ended + 1 span (root) + assert.strictEqual(spans.length, 6); + }); + }); + + for (const ignored of ['string', 'function', 'regexp']) { + it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { + const testPath = `/ignored/${ignored}`; + + await httpsRequest.get( + `${protocol}://${hostname}:${serverPort}${testPath}` + ); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + }); + } + + for (const arg of ['string', '', {}, new Date()]) { + it(`should be tracable and not throw exception in https plugin when passing the following argument ${JSON.stringify( + arg + )}`, async () => { + try { + await httpsRequest.get(arg); + } catch (error) { + // https request has been made + // nock throw + assert.ok(error.message.startsWith('Nock: No match for request')); + } + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + }); + } + + for (const arg of [true, 1, false, 0]) { + it(`should not throw exception in https plugin when passing the following argument ${JSON.stringify( + arg + )}`, async () => { + try { + // @ts-ignore + await httpsRequest.get(arg); + } catch (error) { + // https request has been made + // nock throw + assert.ok( + error.stack.indexOf('/node_modules/nock/lib/intercept.js') > 0 + ); + } + const spans = memoryExporter.getFinishedSpans(); + // for this arg with don't provide trace. We pass arg to original method (https.get) + assert.strictEqual(spans.length, 0); + }); + } + + it('should have 1 ended span when request throw on bad "options" object', () => { + nock.cleanAll(); + nock.enableNetConnect(); + try { + https.request({ protocol: 'telnet' }); + assert.fail(); + } catch (error) { + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + } + }); + + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); + + const promiseRequest = new Promise((resolve, reject) => { + const req = https.request( + `${protocol}://${hostname}${testPath}`, + (resp: http.IncomingMessage) => { + let data = ''; + resp.on('data', chunk => { + data += chunk; + }); + resp.on('end', () => { + reject(new Error(data)); + }); + } + ); + return req.end(); + }); + + try { + await promiseRequest; + assert.fail(); + } catch (error) { + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + } + }); + + it('should have 1 ended span when request is aborted', async () => { + nock(`${protocol}://my.server.com`) + .get('/') + .socketDelay(50) + .reply(200, ''); + + const promiseRequest = new Promise((resolve, reject) => { + const req = https.request( + `${protocol}://my.server.com`, + (resp: http.IncomingMessage) => { + let data = ''; + resp.on('data', chunk => { + data += chunk; + }); + resp.on('end', () => { + resolve(data); + }); + } + ); + req.setTimeout(10, () => { + req.abort(); + reject('timeout'); + }); + return req.end(); + }); + + try { + await promiseRequest; + assert.fail(); + } catch (error) { + const spans = memoryExporter.getFinishedSpans(); + const [span] = spans; + assert.strictEqual(spans.length, 1); + assert.strictEqual(span.status.code, CanonicalCode.ABORTED); + assert.ok(Object.keys(span.attributes).length > 7); + } + }); + + it('should have 1 ended span when request is aborted after receiving response', async () => { + nock(`${protocol}://my.server.com`) + .get('/') + .delay({ + body: 50, + }) + .replyWithFile(200, `${process.cwd()}/package.json`); + + const promiseRequest = new Promise((resolve, reject) => { + const req = https.request( + `${protocol}://my.server.com`, + (resp: http.IncomingMessage) => { + let data = ''; + resp.on('data', chunk => { + req.abort(); + data += chunk; + }); + resp.on('end', () => { + resolve(data); + }); + } + ); + + return req.end(); + }); + + try { + await promiseRequest; + assert.fail(); + } catch (error) { + const spans = memoryExporter.getFinishedSpans(); + const [span] = spans; + assert.strictEqual(spans.length, 1); + assert.strictEqual(span.status.code, CanonicalCode.ABORTED); + assert.ok(Object.keys(span.attributes).length > 7); + } + }); + }); +}); diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts new file mode 100644 index 000000000..dfa0ad555 --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts @@ -0,0 +1,139 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NoopLogger } from '@opentelemetry/core'; +import { SpanKind, Span } from '@opentelemetry/types'; +import * as assert from 'assert'; +import * as https from 'https'; +import * as http from 'http'; +import * as nock from 'nock'; +import { plugin } from '../../src/https'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import * as url from 'url'; +import axios, { AxiosResponse } from 'axios'; +import * as superagent from 'superagent'; +import * as got from 'got'; +import * as request from 'request-promise-native'; +import * as path from 'path'; +import { NodeTracer } from '@opentelemetry/node-sdk'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracer-basic'; +import { Http } from '@opentelemetry/plugin-http'; + +const memoryExporter = new InMemorySpanExporter(); + +export const customAttributeFunction = (span: Span): void => { + span.setAttribute('span kind', SpanKind.CLIENT); +}; + +describe('Packages', () => { + describe('get', () => { + const httpTextFormat = new DummyPropagation(); + const logger = new NoopLogger(); + + const tracer = new NodeTracer({ + logger, + httpTextFormat, + }); + tracer.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); + beforeEach(() => { + memoryExporter.reset(); + }); + + before(() => { + plugin.enable((https as unknown) as Http, tracer, tracer.logger); + }); + + after(() => { + // back to normal + nock.cleanAll(); + nock.enableNetConnect(); + }); + + let resHeaders: http.IncomingHttpHeaders; + [ + { name: 'axios', httpPackage: axios }, //keep first + { name: 'superagent', httpPackage: superagent }, + { name: 'got', httpPackage: { get: (url: string) => got(url) } }, + { + name: 'request', + httpPackage: { get: (url: string) => request(url) }, + }, + ].forEach(({ name, httpPackage }) => { + it(`should create a span for GET requests and add propagation headers by using ${name} package`, async () => { + if (process.versions.node.startsWith('12') && name === 'got') { + // got complains with nock and node version 12+ + // > RequestError: The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type function + // so let's make a real call + nock.cleanAll(); + nock.enableNetConnect(); + } else { + nock.load(path.join(__dirname, '../', '/fixtures/google.json')); + } + + const urlparsed = url.parse( + name === 'got' && process.versions.node.startsWith('12') + ? // there is an issue with got 9.6 version and node 12 when redirecting so url above will not work + // https://github.com/nock/nock/pull/1551 + // https://github.com/sindresorhus/got/commit/bf1aa5492ae2bc78cbbec6b7d764906fb156e6c2#diff-707a4781d57c42085155dcb27edb9ccbR258 + // TODO: check if this is still the case when new version + 'https://www.google.com' + : `https://www.google.com/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8` + ); + const result = await httpPackage.get(urlparsed.href!); + if (!resHeaders) { + const res = result as AxiosResponse<{}>; + resHeaders = res.headers; + } + const spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + const validations = { + hostname: urlparsed.hostname!, + httpStatusCode: 200, + httpMethod: 'GET', + pathname: urlparsed.pathname!, + path: urlparsed.path, + resHeaders, + component: plugin.component, + }; + + assert.strictEqual(spans.length, 1); + assert.ok(span.name.indexOf(`GET ${urlparsed.pathname}`) >= 0); + + switch (name) { + case 'axios': + assert.ok( + result.request._headers[DummyPropagation.TRACE_CONTEXT_KEY] + ); + assert.ok( + result.request._headers[DummyPropagation.SPAN_CONTEXT_KEY] + ); + break; + case 'got': + case 'superagent': + break; + default: + break; + } + assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); + assertSpan(span, SpanKind.CLIENT, validations); + }); + }); + }); +}); diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts new file mode 100644 index 000000000..9bf2b4a04 --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts @@ -0,0 +1,227 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracer-basic'; +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracer } from '@opentelemetry/node-sdk'; +import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http'; +import { Span, SpanKind } from '@opentelemetry/types'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as https from 'https'; +import * as url from 'url'; +import { plugin } from '../../src/https'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpsRequest } from '../utils/httpsRequest'; +import * as utils from '../utils/utils'; + +const serverPort = 42345; +const hostname = 'localhost'; +const memoryExporter = new InMemorySpanExporter(); + +export const customAttributeFunction = (span: Span): void => { + span.setAttribute('span kind', SpanKind.CLIENT); +}; + +describe('HttpsPlugin Integration tests', () => { + describe('enable()', () => { + before(function(done) { + // mandatory + if (process.env.CI) { + done(); + return; + } + + utils.checkInternet(isConnected => { + if (!isConnected) { + this.skip(); + // don't disturbe people + } + done(); + }); + }); + + const httpTextFormat = new DummyPropagation(); + const logger = new NoopLogger(); + const tracer = new NodeTracer({ + logger, + httpTextFormat, + }); + tracer.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); + beforeEach(() => { + memoryExporter.reset(); + }); + + before(() => { + const ignoreConfig = [ + `https://${hostname}:${serverPort}/ignored/string`, + /\/ignored\/regexp$/i, + (url: string) => url.endsWith(`/ignored/function`), + ]; + const config: HttpPluginConfig = { + ignoreIncomingPaths: ignoreConfig, + ignoreOutgoingUrls: ignoreConfig, + applyCustomAttributesOnSpan: customAttributeFunction, + }; + try { + plugin.disable(); + } catch (e) {} + plugin.enable((https as unknown) as Http, tracer, tracer.logger, config); + }); + + after(() => { + plugin.disable(); + }); + + it('should create a rootSpan for GET requests and add propagation headers', async () => { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + const result = await httpsRequest.get(`https://google.fr/?query=test`); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + const validations = { + hostname: 'google.fr', + httpStatusCode: result.statusCode!, + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', + resHeaders: result.resHeaders, + reqHeaders: result.reqHeaders, + component: plugin.component, + }; + + assert.strictEqual(spans.length, 1); + assert.ok(span.name.indexOf('GET /') >= 0); + assertSpan(span, SpanKind.CLIENT, validations); + }); + + it('custom attributes should show up on client spans', async () => { + const result = await httpsRequest.get(`https://google.fr/`); + const spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + const validations = { + hostname: 'google.fr', + httpStatusCode: result.statusCode!, + httpMethod: 'GET', + pathname: '/', + resHeaders: result.resHeaders, + reqHeaders: result.reqHeaders, + component: plugin.component, + }; + + assert.strictEqual(spans.length, 1); + assert.ok(span.name.indexOf('GET /') >= 0); + assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); + assertSpan(span, SpanKind.CLIENT, validations); + }); + + it('should create a span for GET requests and add propagation headers with Expect headers', async () => { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + const options = Object.assign( + { headers: { Expect: '100-continue' } }, + url.parse('https://google.fr/') + ); + + const result = await httpsRequest.get(options); + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + const validations = { + hostname: 'google.fr', + httpStatusCode: 301, + httpMethod: 'GET', + pathname: '/', + resHeaders: result.resHeaders, + reqHeaders: result.reqHeaders, + component: plugin.component, + }; + + assert.strictEqual(spans.length, 1); + assert.ok(span.name.indexOf('GET /') >= 0); + + try { + assertSpan(span, SpanKind.CLIENT, validations); + } catch (error) { + // temporary redirect is also correct + validations.httpStatusCode = 307; + assertSpan(span, SpanKind.CLIENT, validations); + } + }); + for (const headers of [ + { Expect: '100-continue', 'user-agent': 'https-plugin-test' }, + { 'user-agent': 'https-plugin-test' }, + ]) { + it(`should create a span for GET requests and add propagation when using the following signature: https.get(url, options, callback) and following headers: ${JSON.stringify( + headers + )}`, done => { + let validations: { + hostname: string; + httpStatusCode: number; + httpMethod: string; + pathname: string; + reqHeaders: http.OutgoingHttpHeaders; + resHeaders: http.IncomingHttpHeaders; + }; + let data = ''; + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + const options = { headers }; + const req = https.get( + 'https://google.fr/', + options, + (resp: http.IncomingMessage) => { + const res = (resp as unknown) as http.IncomingMessage & { + req: http.IncomingMessage; + }; + + resp.on('data', chunk => { + data += chunk; + }); + resp.on('end', () => { + validations = { + hostname: 'google.fr', + httpStatusCode: 301, + httpMethod: 'GET', + pathname: '/', + resHeaders: resp.headers, + /* tslint:disable:no-any */ + reqHeaders: (res.req as any).getHeaders + ? (res.req as any).getHeaders() + : (res.req as any)._headers, + /* tslint:enable:no-any */ + }; + }); + } + ); + req.on('close', () => { + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + assert.ok(spans[0].name.indexOf('GET /') >= 0); + assert.ok(data); + assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); + assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); + done(); + }); + }); + } + }); +}); diff --git a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts new file mode 100644 index 000000000..2c9cbd238 --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts @@ -0,0 +1,36 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { SpanContext, HttpTextFormat } from '@opentelemetry/types'; +import * as http from 'http'; + +export class DummyPropagation implements HttpTextFormat { + static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; + static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; + extract(format: string, carrier: http.OutgoingHttpHeaders): SpanContext { + return { + traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, + spanId: DummyPropagation.SPAN_CONTEXT_KEY, + }; + } + inject( + spanContext: SpanContext, + format: string, + headers: { [custom: string]: string } + ): void { + headers[DummyPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; + headers[DummyPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; + } +} diff --git a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts new file mode 100644 index 000000000..d9ddfd385 --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts @@ -0,0 +1,99 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SpanKind } from '@opentelemetry/types'; +import { hrTimeToNanoseconds } from '@opentelemetry/core'; +import * as assert from 'assert'; +import * as http from 'http'; +import { DummyPropagation } from './DummyPropagation'; +import { ReadableSpan } from '@opentelemetry/tracer-basic'; +import { + AttributeNames, + parseResponseStatus, +} from '@opentelemetry/plugin-http'; + +export const assertSpan = ( + span: ReadableSpan, + kind: SpanKind, + validations: { + httpStatusCode: number; + httpMethod: string; + resHeaders: http.IncomingHttpHeaders; + hostname: string; + pathname: string; + reqHeaders?: http.OutgoingHttpHeaders; + path?: string; + component: string; + } +) => { + assert.strictEqual(span.spanContext.traceId.length, 32); + assert.strictEqual(span.spanContext.spanId.length, 16); + assert.strictEqual(span.kind, kind); + assert.strictEqual( + span.name, + `${validations.httpMethod} ${validations.pathname}` + ); + assert.strictEqual( + span.attributes[AttributeNames.COMPONENT], + validations.component + ); + assert.strictEqual( + span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], + span.status.message + ); + assert.strictEqual( + span.attributes[AttributeNames.HTTP_HOSTNAME], + validations.hostname + ); + assert.strictEqual( + span.attributes[AttributeNames.HTTP_METHOD], + validations.httpMethod + ); + assert.strictEqual( + span.attributes[AttributeNames.HTTP_PATH], + validations.path || validations.pathname + ); + assert.strictEqual( + span.attributes[AttributeNames.HTTP_STATUS_CODE], + validations.httpStatusCode + ); + assert.ok(span.endTime); + assert.strictEqual(span.links.length, 0); + assert.strictEqual(span.events.length, 0); + assert.deepStrictEqual( + span.status, + parseResponseStatus(validations.httpStatusCode) + ); + + assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); + + if (validations.reqHeaders) { + const userAgent = validations.reqHeaders['user-agent']; + if (userAgent) { + assert.strictEqual( + span.attributes[AttributeNames.HTTP_USER_AGENT], + userAgent + ); + } + } + + if (span.kind === SpanKind.SERVER) { + assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); + } else if (validations.reqHeaders) { + assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); + assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); + } +}; diff --git a/packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts b/packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts new file mode 100644 index 000000000..c5436be76 --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts @@ -0,0 +1,74 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as http from 'http'; +import * as https from 'https'; +import { RequestOptions } from 'https'; +import * as url from 'url'; + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +export const httpsRequest = { + get: ( + options: string | RequestOptions + ): Promise<{ + data: string; + statusCode: number | undefined; + resHeaders: http.IncomingHttpHeaders; + reqHeaders: http.OutgoingHttpHeaders; + method: string | undefined; + }> => { + const _options = + typeof options === 'string' + ? Object.assign(url.parse(options), { + headers: { + 'user-agent': 'https-plugin-test', + }, + }) + : options; + return new Promise((resolve, reject) => { + const req = https.get(_options, (resp: http.IncomingMessage) => { + const res = (resp as unknown) as http.IncomingMessage & { + req: http.IncomingMessage; + }; + let data = ''; + resp.on('data', chunk => { + data += chunk; + }); + resp.on('end', () => { + resolve({ + data, + statusCode: res.statusCode, + /* tslint:disable:no-any */ + reqHeaders: (res.req as any).getHeaders + ? (res.req as any).getHeaders() + : (res.req as any)._headers, + /* tslint:enable:no-any */ + resHeaders: res.headers, + method: res.req.method, + }); + }); + resp.on('error', err => { + reject(err); + }); + }); + req.on('error', err => { + reject(err); + }); + return req; + }); + }, +}; diff --git a/packages/opentelemetry-plugin-https/test/utils/utils.ts b/packages/opentelemetry-plugin-https/test/utils/utils.ts new file mode 100644 index 000000000..57a75516a --- /dev/null +++ b/packages/opentelemetry-plugin-https/test/utils/utils.ts @@ -0,0 +1,27 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as dns from 'dns'; + +export const checkInternet = (cb: (isConnected: boolean) => void) => { + dns.lookup('google.com', err => { + if (err && err.code === 'ENOTFOUND') { + cb(false); + } else { + cb(true); + } + }); +};