diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/LICENSE b/propagators/opentelemetry-propagator-grpc-census-binary/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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
+
+ http://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.
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/README.md b/propagators/opentelemetry-propagator-grpc-census-binary/README.md
new file mode 100644
index 0000000000..9ac3becb1c
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/README.md
@@ -0,0 +1,56 @@
+# OpenTelemetry Propagator gRPC Census
+[![Gitter chat][gitter-image]][gitter-url]
+[![NPM Published Version][npm-img]][npm-url]
+[![dependencies][dependencies-image]][dependencies-url]
+[![devDependencies][devDependencies-image]][devDependencies-url]
+[![Apache License][license-image]][license-image]
+
+OpenTelemetry gRPC Census propagator provides gRPC header propagation for systems that use the OpenCensus 'grpc-trace-bin' binary header format. This allows for context propagation when either:
+* incoming gRPC calls come from services already instrumented using OpenCensus
+* outgoing gRPC calls go to services already instrumented using OpenCensus
+
+This propagator works in conjunction with the OpenTelemetry [gRPC plugin](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-grpc).
+
+
+Example of usage:
+```javascript
+const { NodeTracerProvider } = require('@opentelemetry/node');
+const { GrpcCensusPropagator } = require("@opentelemetry/propagator-grpc-census-binary");
+
+const provider = new NodeTracerProvider({
+ plugins: {
+ grpc: {
+ enabled: true,
+ path: '@opentelemetry/plugin-grpc',
+ }
+ }
+});
+
+// Register GrpcCensusPropagator so we can propagate content using
+// the 'grpc-trace-bin' header in our incoming/outgoing gRPC calls.
+provider.register({
+ propagator: new GrpcCensusPropagator()
+});
+```
+## Implementation Details
+See [binary-format.ts](https://github.com/census-instrumentation/opencensus-node/blob/master/packages/opencensus-propagation-binaryformat/src/binary-format.ts) for equivalent encoding/decoding of the format in OpenCensus. Note: the author of the OpenCensus binary format, [~mayurkale22](https://github.com/mayurkale22), also created BinaryTraceContext.ts in [opentelemetry-core](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-core) but that was subsequently removed as part of PR [#804](https://github.com/open-telemetry/opentelemetry-js/pull/804). The implementation of GrpcCensusPropagator in _this_ module uses a version of BinaryTraceContext.ts inspired by Mayur's previous work (with minor modifications e.g. there is no longer a BinaryFormat interface to implement).
+
+## Useful links
+- For more information on OpenTelemetry, visit:
+- For more about OpenTelemetry JavaScript:
+- For help or feedback on this project, join us on [gitter][gitter-url]
+
+## License
+
+Apache 2.0 - See [LICENSE][license-url] for more information.
+
+[gitter-image]: https://badges.gitter.im/open-telemetry/opentelemetry-js.svg
+[gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+[license-url]: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/master/LICENSE
+[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat
+[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib/status.svg?path=propagators/opentelemetry-propagator-grpc-census-binary
+[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib?path=propagators%2Fopentelemetry-propagator-grpc-census-binary
+[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib/dev-status.svg?path=propagators/opentelemetry-propagator-grpc-census-binary
+[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib?path=propagators%2Fopentelemetry-propagator-grpc-census-binary&type=dev
+[npm-url]: https://www.npmjs.com/package/@opentelemetry/propagator-grpc-census-binary
+[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fpropagator-grpc-census-binary.svg
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/package.json b/propagators/opentelemetry-propagator-grpc-census-binary/package.json
new file mode 100644
index 0000000000..1de45d546d
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "@opentelemetry/propagator-grpc-census-binary",
+ "version": "0.8.0",
+ "description": "OpenTelemetry gRPC Census propagator provides a context propagator for OpenTelemetry that can use the gRPC binary header: 'grpc-trace-bin' for interoperability with OpenCensus",
+ "main": "build/src/index.js",
+ "types": "build/src/index.d.ts",
+ "repository": "open-telemetry/opentelemetry-js-contrib",
+ "scripts": {
+ "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'",
+ "tdd": "npm run tdd:node",
+ "tdd:node": "npm run test -- --watch-extensions ts --watch",
+ "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
+ "clean": "rimraf build/*",
+ "lint": "gts check",
+ "lint:fix": "gts fix",
+ "precompile": "tsc --version",
+ "compile": "npm run version:update && tsc -p .",
+ "prepare": "npm run compile",
+ "version:update": "node ../../scripts/version-update.js",
+ "watch": "tsc -w"
+ },
+ "keywords": [
+ "opentelemetry",
+ "nodejs",
+ "tracing",
+ "grpc",
+ "opencensus"
+ ],
+ "author": "OpenTelemetry Authors",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ },
+ "files": [
+ "build/src/**/*.js",
+ "build/src/**/*.d.ts",
+ "doc",
+ "LICENSE",
+ "README.md"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "@types/mocha": "^7.0.0",
+ "@types/node": "^12.6.8",
+ "codecov": "^3.6.1",
+ "grpc": "^1.24.2",
+ "gts": "^1.1.0",
+ "mocha": "^6.1.0",
+ "nyc": "^15.0.0",
+ "rimraf": "^3.0.0",
+ "ts-loader": "^6.0.4",
+ "ts-mocha": "^6.0.0",
+ "ts-node": "^8.6.2",
+ "tslint-consistent-codestyle": "^1.16.0",
+ "tslint-microsoft-contrib": "^6.2.0",
+ "typescript": "3.7.2"
+ },
+ "dependencies": {
+ "@opentelemetry/api": "^0.8.3",
+ "@opentelemetry/core": "^0.8.3"
+ }
+}
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/src/BinaryTraceContext.ts b/propagators/opentelemetry-propagator-grpc-census-binary/src/BinaryTraceContext.ts
new file mode 100644
index 0000000000..1e57bd91dc
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/src/BinaryTraceContext.ts
@@ -0,0 +1,141 @@
+/*!
+ * Copyright 2020, 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, TraceFlags } from '@opentelemetry/api';
+
+const VERSION_ID = 0;
+const TRACE_ID_FIELD_ID = 0;
+const SPAN_ID_FIELD_ID = 1;
+const TRACE_OPTION_FIELD_ID = 2;
+
+// Sizes are number of bytes.
+const ID_SIZE = 1;
+const TRACE_ID_SIZE = 16;
+const SPAN_ID_SIZE = 8;
+const TRACE_OPTION_SIZE = 1;
+
+const VERSION_ID_OFFSET = 0;
+const TRACE_ID_FIELD_ID_OFFSET = VERSION_ID_OFFSET + ID_SIZE;
+const TRACE_ID_OFFSET = TRACE_ID_FIELD_ID_OFFSET + ID_SIZE;
+const SPAN_ID_FIELD_ID_OFFSET = TRACE_ID_OFFSET + TRACE_ID_SIZE;
+const SPAN_ID_OFFSET = SPAN_ID_FIELD_ID_OFFSET + ID_SIZE;
+const TRACE_OPTION_FIELD_ID_OFFSET = SPAN_ID_OFFSET + SPAN_ID_SIZE;
+const TRACE_OPTIONS_OFFSET = TRACE_OPTION_FIELD_ID_OFFSET + ID_SIZE;
+
+const FORMAT_LENGTH =
+ 4 * ID_SIZE + TRACE_ID_SIZE + SPAN_ID_SIZE + TRACE_OPTION_SIZE;
+
+/**
+ * Used to encode SpanContext to binary (toBytes) or decode SpanContext
+ * from binary (fromBytes). Derived from a previous implementation in
+ * opentelemetry-core (see README.md). Supports the main
+ * GrpcCensusPropagator class.
+ */
+export class BinaryTraceContext {
+ /**
+ * Converts context to the binary format used by OpenCensus.
+ *
+ * @param spanContext - The SpanContext to encode
+ * @returns Encoded context as a Uint8Array
+ */
+ static toBytes(spanContext: SpanContext): Uint8Array {
+ /**
+ * 0 1 2
+ * 0 1 2345678901234567 8 90123456 7 8
+ * -------------------------------------
+ * | | | | | | | |
+ * -------------------------------------
+ * ^ ^ ^ ^ ^ ^ ^
+ * | | | | | | `-- options value
+ * | | | | | `---- options field ID (2)
+ * | | | | `---------- spanID value
+ * | | | `--------------- spanID field ID (1)
+ * | | `--------------------------- traceID value
+ * | `---------------------------------- traceID field ID (0)
+ * `------------------------------------ version (0)
+ */
+ const traceId = spanContext.traceId;
+ const spanId = spanContext.spanId;
+ const buf = new Uint8Array(FORMAT_LENGTH);
+ let j = TRACE_ID_OFFSET;
+ for (let i = TRACE_ID_OFFSET; i < SPAN_ID_FIELD_ID_OFFSET; i++) {
+ // tslint:disable-next-line:ban Needed to parse hexadecimal.
+ buf[j++] = parseInt(traceId.substr((i - TRACE_ID_OFFSET) * 2, 2), 16);
+ }
+ buf[j++] = SPAN_ID_FIELD_ID;
+ for (let i = SPAN_ID_OFFSET; i < TRACE_OPTION_FIELD_ID_OFFSET; i++) {
+ // tslint:disable-next-line:ban Needed to parse hexadecimal.
+ buf[j++] = parseInt(spanId.substr((i - SPAN_ID_OFFSET) * 2, 2), 16);
+ }
+ buf[j++] = TRACE_OPTION_FIELD_ID;
+ buf[j++] = Number(spanContext.traceFlags) || TraceFlags.NONE;
+ return buf;
+ }
+
+ /**
+ * Converts the binary format used by OpenCensus to a SpanContext
+ *
+ * @param buf - Encoded context as a Uint8Array
+ * @returns Decoded context or null if the incoming binary is invalid
+ */
+ static fromBytes(buf: Uint8Array): SpanContext | null {
+ const result: SpanContext = {
+ traceId: '',
+ spanId: '',
+ traceFlags: TraceFlags.NONE,
+ };
+ // Length must be 29.
+ if (buf.length !== FORMAT_LENGTH) return null;
+ // Check version and field numbers.
+ if (
+ buf[VERSION_ID_OFFSET] !== VERSION_ID ||
+ buf[TRACE_ID_FIELD_ID_OFFSET] !== TRACE_ID_FIELD_ID ||
+ buf[SPAN_ID_FIELD_ID_OFFSET] !== SPAN_ID_FIELD_ID ||
+ buf[TRACE_OPTION_FIELD_ID_OFFSET] !== TRACE_OPTION_FIELD_ID
+ ) {
+ return null;
+ }
+
+ result.isRemote = true;
+
+ // See toBytes for byte offsets.
+ result.traceId = toHex(buf.slice(TRACE_ID_OFFSET, SPAN_ID_FIELD_ID_OFFSET));
+ result.spanId = toHex(
+ buf.slice(SPAN_ID_OFFSET, TRACE_OPTION_FIELD_ID_OFFSET)
+ );
+ result.traceFlags = buf[TRACE_OPTIONS_OFFSET];
+ return result;
+ }
+}
+
+/**
+ * Convert a binary array to a Hex representation
+ *
+ * @param buff - binary array to be converted
+ * @returns Hex representation
+ */
+function toHex(buff: Uint8Array) {
+ let out = '';
+ for (let i = 0; i < buff.length; ++i) {
+ const n = buff[i];
+ if (n < 16) {
+ out += '0' + n.toString(16);
+ } else {
+ out += n.toString(16);
+ }
+ }
+ return out;
+}
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/src/GrpcCensusPropagator.ts b/propagators/opentelemetry-propagator-grpc-census-binary/src/GrpcCensusPropagator.ts
new file mode 100644
index 0000000000..12b5fdcdc4
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/src/GrpcCensusPropagator.ts
@@ -0,0 +1,140 @@
+/*!
+ * Copyright 2020, 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 {
+ Context,
+ GetterFunction,
+ HttpTextPropagator,
+ SetterFunction,
+} from '@opentelemetry/api';
+import {
+ getParentSpanContext,
+ setExtractedSpanContext,
+} from '@opentelemetry/core';
+import { BinaryTraceContext } from './BinaryTraceContext';
+
+/** The metadata key under which span context is stored as a binary value. */
+export const GRPC_TRACE_KEY = 'grpc-trace-bin';
+
+const VALID_TRACEID_REGEX = /^[0-9a-f]{32}$/i;
+const VALID_SPANID_REGEX = /^[0-9a-f]{16}$/i;
+const INVALID_ID_REGEX = /^0+$/i;
+
+/**
+ * Check whether a traceId is valid
+ * @param traceId - traceId to check
+ * @returns true if valid
+ */
+function isValidTraceId(traceId: string): boolean {
+ return VALID_TRACEID_REGEX.test(traceId) && !INVALID_ID_REGEX.test(traceId);
+}
+
+/**
+ * Check whether a spanId is valid
+ * @param spanId - spanId to check
+ * @returns true if valid
+ */
+function isValidSpanId(spanId: string): boolean {
+ return VALID_SPANID_REGEX.test(spanId) && !INVALID_ID_REGEX.test(spanId);
+}
+
+/**
+ * Propagator for the grpc-trace-bin header used by OpenCensus for
+ * gRPC. Acts as a bridge between the HttpTextPropagator interface and
+ * the binary encoding/decoding that happens in the supporting
+ * BinaryTraceContext class.
+ */
+export class GrpcCensusPropagator implements HttpTextPropagator {
+ /**
+ * Injects trace propagation context into the carrier after encoding
+ * in binary format
+ *
+ * @param context - Context to be injected
+ * @param carrier - Carrier in which to inject (for gRPC this will
+ * be a grpc.Metadata object)
+ * @param setter - setter function that sets the correct key in
+ * the carrier
+ */
+ inject(context: Context, carrier: unknown, setter: SetterFunction) {
+ const spanContext = getParentSpanContext(context);
+ if (!spanContext) return;
+
+ if (
+ isValidTraceId(spanContext.traceId) &&
+ isValidSpanId(spanContext.spanId)
+ ) {
+ // We set the header only if there is an existing sampling decision.
+ // Otherwise we will omit it => Absent.
+ if (spanContext.traceFlags !== undefined) {
+ const encodedArray = BinaryTraceContext.toBytes(spanContext);
+ const encodedContext = Buffer.from(encodedArray.buffer);
+
+ if (carrier && encodedContext) {
+ // Set the gRPC header (carrier will be of type grpc.Metadata)
+ setter(carrier, GRPC_TRACE_KEY, encodedContext);
+ }
+ }
+ }
+ }
+
+ /**
+ * Extracts trace propagation context from the carrier and decodes
+ * from the binary format
+ *
+ * @param context - context to set extracted span context on
+ * @param carrier - Carrier from which to extract (for gRPC this will
+ * be a grpc.Metadata object)
+ * @param getter - getter function that gets value(s) for the correct
+ * key in the carrier
+ * @returns Extracted context if successful, otherwise the input context
+ */
+ extract(context: Context, carrier: unknown, getter: GetterFunction): Context {
+ if (carrier) {
+ // Get the gRPC header (carrier will be of type grpc.Metadata and
+ // getter actually returns an Array so we use the zero-th element if
+ // it exists)
+ const values = getter(carrier, GRPC_TRACE_KEY) as Array;
+ const metadataValue = values.length > 0 ? values[0] : null;
+
+ if (!metadataValue) {
+ // No metadata, return empty context
+ return context;
+ } else {
+ const decodedContext = BinaryTraceContext.fromBytes(metadataValue);
+
+ if (decodedContext) {
+ const traceId = decodedContext.traceId;
+ const spanId = decodedContext.spanId;
+
+ if (isValidTraceId(traceId) && isValidSpanId(spanId)) {
+ return setExtractedSpanContext(context, {
+ traceId,
+ spanId,
+ isRemote: true,
+ traceFlags: decodedContext.traceFlags,
+ });
+ }
+ return context;
+ } else {
+ // Failed to deserialize Span Context, return empty context
+ return context;
+ }
+ }
+ }
+
+ return context;
+ }
+}
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/src/index.ts b/propagators/opentelemetry-propagator-grpc-census-binary/src/index.ts
new file mode 100644
index 0000000000..1975ea0e2f
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/src/index.ts
@@ -0,0 +1,17 @@
+/*!
+ * Copyright 2020, 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.
+ */
+
+export * from './GrpcCensusPropagator';
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/src/version.ts b/propagators/opentelemetry-propagator-grpc-census-binary/src/version.ts
new file mode 100644
index 0000000000..f30ba9baca
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/src/version.ts
@@ -0,0 +1,18 @@
+/*!
+ * 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.
+ */
+
+// this is autogenerated file, see scripts/version-update.js
+export const VERSION = '0.8.0';
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/test/BinaryTraceContext.test.ts b/propagators/opentelemetry-propagator-grpc-census-binary/test/BinaryTraceContext.test.ts
new file mode 100644
index 0000000000..fd2f8d7abd
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/test/BinaryTraceContext.test.ts
@@ -0,0 +1,147 @@
+/*!
+ * Copyright 2020, 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 assert from 'assert';
+import { BinaryTraceContext } from '../src/BinaryTraceContext';
+import { SpanContext, TraceFlags } from '@opentelemetry/api';
+
+describe('BinaryTraceContext', () => {
+ const commonTraceId = 'd4cda95b652f4a0b92b449d5929fda1b';
+ const commonSpanId = '75e8ed491aec7eca';
+
+ const testCases: Array<{
+ structured: SpanContext | null;
+ binary: Uint8Array;
+ description: string;
+ }> = [
+ {
+ structured: {
+ traceId: commonTraceId,
+ spanId: commonSpanId,
+ traceFlags: TraceFlags.SAMPLED,
+ },
+ binary: new Uint8Array([
+ 0,
+ 0,
+ 212,
+ 205,
+ 169,
+ 91,
+ 101,
+ 47,
+ 74,
+ 11,
+ 146,
+ 180,
+ 73,
+ 213,
+ 146,
+ 159,
+ 218,
+ 27,
+ 1,
+ 117,
+ 232,
+ 237,
+ 73,
+ 26,
+ 236,
+ 126,
+ 202,
+ 2,
+ 1,
+ ]),
+ description: 'span context with 64-bit span ID',
+ },
+ {
+ structured: {
+ traceId: commonTraceId,
+ spanId: commonSpanId,
+ traceFlags: TraceFlags.NONE,
+ },
+ binary: new Uint8Array([
+ 0,
+ 0,
+ 212,
+ 205,
+ 169,
+ 91,
+ 101,
+ 47,
+ 74,
+ 11,
+ 146,
+ 180,
+ 73,
+ 213,
+ 146,
+ 159,
+ 218,
+ 27,
+ 1,
+ 117,
+ 232,
+ 237,
+ 73,
+ 26,
+ 236,
+ 126,
+ 202,
+ 2,
+ 0,
+ ]),
+ description: 'span context with no traceFlags',
+ },
+ {
+ structured: null,
+ binary: new Uint8Array([0, 0]),
+ description: 'incomplete binary span context (by returning null)',
+ },
+ {
+ structured: null,
+ binary: new Uint8Array(29),
+ description: 'bad binary span context (by returning null)',
+ },
+ ];
+
+ describe('.toBytes()', () => {
+ testCases.forEach(
+ testCase =>
+ testCase.structured &&
+ it(`should serialize ${testCase.description}`, () => {
+ assert.deepStrictEqual(
+ BinaryTraceContext.toBytes(testCase.structured!),
+ testCase.binary
+ );
+ })
+ );
+ });
+
+ describe('.fromBytes()', () => {
+ testCases.forEach(testCase =>
+ it(`should deserialize ${testCase.description}`, () => {
+ assert.deepStrictEqual(
+ BinaryTraceContext.fromBytes(testCase.binary),
+ testCase.structured &&
+ Object.assign(
+ { isRemote: true, traceFlags: TraceFlags.NONE },
+ testCase.structured
+ )
+ );
+ })
+ );
+ });
+});
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/test/GrpcCensusPropagator.test.ts b/propagators/opentelemetry-propagator-grpc-census-binary/test/GrpcCensusPropagator.test.ts
new file mode 100644
index 0000000000..ebe9fd3b2e
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/test/GrpcCensusPropagator.test.ts
@@ -0,0 +1,285 @@
+/*!
+ * Copyright 2020, 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 assert from 'assert';
+import { SpanContext, TraceFlags } from '@opentelemetry/api';
+import { Context } from '@opentelemetry/context-base';
+import {
+ getExtractedSpanContext,
+ setExtractedSpanContext,
+} from '@opentelemetry/core';
+import { Metadata, MetadataValue } from 'grpc';
+import {
+ GRPC_TRACE_KEY,
+ GrpcCensusPropagator,
+} from '../src/GrpcCensusPropagator';
+
+describe('GrpcCensusPropagator', () => {
+ const censusPropagator = new GrpcCensusPropagator();
+ let metadata = new Metadata();
+
+ beforeEach(() => {
+ metadata.remove(GRPC_TRACE_KEY);
+ });
+
+ describe('.inject()', () => {
+ it('should set grpc-trace-bin header correctly for sampled span', () => {
+ const spanContext: SpanContext = {
+ traceId: 'd4cda95b652f4a1592b449d5929fda1b',
+ spanId: '6e0c63257de34c92',
+ traceFlags: TraceFlags.SAMPLED,
+ };
+ censusPropagator.inject(
+ setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext),
+ metadata,
+ (metadata, k, v) => metadata.set(k, v as MetadataValue)
+ );
+
+ const value = metadata.getMap()[GRPC_TRACE_KEY] as Buffer;
+
+ const binaryExpected = `0000${spanContext.traceId}01${
+ spanContext.spanId
+ }02${'01'}`;
+
+ assert.deepStrictEqual(value.toString('hex'), binaryExpected);
+ });
+
+ it('should set grpc-trace-bin header correctly for unsampled span', () => {
+ const spanContext: SpanContext = {
+ traceId: 'd4cda95b652f4a1592b449d5929fda1b',
+ spanId: '6e0c63257de34c92',
+ traceFlags: TraceFlags.NONE,
+ };
+ censusPropagator.inject(
+ setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext),
+ metadata,
+ (metadata, k, v) => metadata.set(k, v as MetadataValue)
+ );
+
+ const value = metadata.getMap()[GRPC_TRACE_KEY] as Buffer;
+
+ const binaryExpected = `0000${spanContext.traceId}01${
+ spanContext.spanId
+ }02${'00'}`;
+
+ assert.deepStrictEqual(value.toString('hex'), binaryExpected);
+ });
+
+ it('should not inject empty spancontext', () => {
+ const emptySpanContext = {
+ traceId: '',
+ spanId: '',
+ traceFlags: TraceFlags.NONE,
+ };
+ censusPropagator.inject(
+ setExtractedSpanContext(Context.ROOT_CONTEXT, emptySpanContext),
+ metadata,
+ (metadata, k, v) => metadata.set(k, v as MetadataValue)
+ );
+
+ const value = metadata.getMap()[GRPC_TRACE_KEY] as Buffer;
+ assert.deepStrictEqual(value, undefined);
+ });
+
+ it('should not inject when context has no parent', () => {
+ censusPropagator.inject(
+ Context.ROOT_CONTEXT,
+ metadata,
+ (metadata, k, v) => metadata.set(k, v as MetadataValue)
+ );
+
+ const value = metadata.getMap()[GRPC_TRACE_KEY] as Buffer;
+ assert.deepStrictEqual(value, undefined);
+ });
+
+ it('should not try to inject without valid TraceFlags', () => {
+ const spanContext = {
+ traceId: 'd4cda95b652f4a1592b449d5929fda1b',
+ spanId: '6e0c63257de34c92',
+ traceFlags: undefined,
+ };
+ censusPropagator.inject(
+ // cast to any so that undefined traceFlags can be used for coverage
+ setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext as any),
+ metadata,
+ (metadata, k, v) => metadata.set(k, v as MetadataValue)
+ );
+
+ const value = metadata.getMap()[GRPC_TRACE_KEY] as Buffer;
+ assert.deepStrictEqual(value, undefined);
+ });
+
+ it('should not try to inject without carrier', () => {
+ const emptySpanContext = {
+ traceId: 'd4cda95b652f4a1592b449d5929fda1b',
+ spanId: '6e0c63257de34c92',
+ traceFlags: TraceFlags.SAMPLED,
+ };
+ censusPropagator.inject(
+ setExtractedSpanContext(Context.ROOT_CONTEXT, emptySpanContext),
+ null,
+ (metadata, k, v) => metadata.set(k, v as MetadataValue)
+ );
+
+ const value = metadata.getMap()[GRPC_TRACE_KEY] as Buffer;
+ assert.deepStrictEqual(value, undefined);
+ });
+ });
+
+ describe('.extract()', () => {
+ it('should extract context of a unsampled span from carrier', () => {
+ let encodedArray = getUnsampledSpanEncodedArray();
+ let encoded = Buffer.from(encodedArray.buffer);
+ metadata.set(GRPC_TRACE_KEY, encoded);
+
+ const extractedSpanContext = getExtractedSpanContext(
+ censusPropagator.extract(
+ Context.ROOT_CONTEXT,
+ metadata,
+ (carrier, key) => carrier.get(key)
+ )
+ );
+
+ assert.deepStrictEqual(extractedSpanContext, {
+ spanId: '75e8ed491aec7eca',
+ traceId: 'd4cda95b652f4a0b92b449d5929fda1b',
+ isRemote: true,
+ traceFlags: TraceFlags.NONE,
+ });
+ });
+
+ it('should extract context of a sampled span from carrier', () => {
+ let encodedArray = getUnsampledSpanEncodedArray();
+
+ // switch last byte to sampled
+ encodedArray[encodedArray.length - 1] = 1;
+ let encoded = Buffer.from(encodedArray.buffer);
+ metadata.set(GRPC_TRACE_KEY, encoded);
+
+ const extractedSpanContext = getExtractedSpanContext(
+ censusPropagator.extract(
+ Context.ROOT_CONTEXT,
+ metadata,
+ (carrier, key) => carrier.get(key)
+ )
+ );
+
+ assert.deepStrictEqual(extractedSpanContext, {
+ spanId: '75e8ed491aec7eca',
+ traceId: 'd4cda95b652f4a0b92b449d5929fda1b',
+ isRemote: true,
+ traceFlags: TraceFlags.SAMPLED,
+ });
+ });
+
+ it('should return undefined when header is not set', () => {
+ const extractedSpanContext = getExtractedSpanContext(
+ censusPropagator.extract(
+ Context.ROOT_CONTEXT,
+ metadata,
+ (carrier, key) => carrier.get(key)
+ )
+ );
+ assert.deepStrictEqual(extractedSpanContext, undefined);
+ });
+
+ it('should return undefined for invalid field IDs', () => {
+ // zero out all 29 bytes - this will fail due to lack of valid
+ // field IDs for spanId and options (bytes 18 and 27)
+ let encodedArray = new Uint8Array(29);
+
+ let encoded = Buffer.from(encodedArray.buffer);
+ metadata.set(GRPC_TRACE_KEY, encoded);
+
+ const extractedSpanContext = getExtractedSpanContext(
+ censusPropagator.extract(
+ Context.ROOT_CONTEXT,
+ metadata,
+ (carrier, key) => carrier.get(key)
+ )
+ );
+
+ assert.deepStrictEqual(extractedSpanContext, undefined);
+ });
+
+ it('should return undefined for invalid trace or span ids', () => {
+ // this should give coverage for the flow where either
+ // isValidTraceId or isValidSpanId fails
+
+ // zero out all 29 bytes except for the spanId field ID and
+ // the options field IF
+ let encodedArray = new Uint8Array(29);
+ encodedArray[18] = 1;
+ encodedArray[27] = 2;
+
+ let encoded = Buffer.from(encodedArray.buffer);
+ metadata.set(GRPC_TRACE_KEY, encoded);
+
+ const extractedSpanContext = getExtractedSpanContext(
+ censusPropagator.extract(
+ Context.ROOT_CONTEXT,
+ metadata,
+ (carrier, key) => carrier.get(key)
+ )
+ );
+
+ assert.deepStrictEqual(extractedSpanContext, undefined);
+ });
+
+ it('should return undefined when carrier is null', () => {
+ const extractedSpanContext = getExtractedSpanContext(
+ censusPropagator.extract(Context.ROOT_CONTEXT, null, (carrier, key) =>
+ carrier.get(key)
+ )
+ );
+ assert.deepStrictEqual(extractedSpanContext, undefined);
+ });
+ });
+});
+
+function getUnsampledSpanEncodedArray() {
+ return new Uint8Array([
+ 0,
+ 0,
+ 212,
+ 205,
+ 169,
+ 91,
+ 101,
+ 47,
+ 74,
+ 11,
+ 146,
+ 180,
+ 73,
+ 213,
+ 146,
+ 159,
+ 218,
+ 27,
+ 1,
+ 117,
+ 232,
+ 237,
+ 73,
+ 26,
+ 236,
+ 126,
+ 202,
+ 2,
+ 0,
+ ]);
+}
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/tsconfig.json b/propagators/opentelemetry-propagator-grpc-census-binary/tsconfig.json
new file mode 100644
index 0000000000..4078877ce6
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../../tsconfig.base",
+ "compilerOptions": {
+ "rootDir": ".",
+ "outDir": "build"
+ },
+ "include": [
+ "src/**/*.ts",
+ "test/**/*.ts"
+ ]
+}
diff --git a/propagators/opentelemetry-propagator-grpc-census-binary/tslint.json b/propagators/opentelemetry-propagator-grpc-census-binary/tslint.json
new file mode 100644
index 0000000000..0710b135d0
--- /dev/null
+++ b/propagators/opentelemetry-propagator-grpc-census-binary/tslint.json
@@ -0,0 +1,4 @@
+{
+ "rulesDirectory": ["node_modules/tslint-microsoft-contrib"],
+ "extends": ["../../tslint.base.js", "./node_modules/tslint-consistent-codestyle"]
+}