Skip to content
This repository has been archived by the owner on Oct 3, 2023. It is now read-only.

Commit

Permalink
Propagation/B3: Enforce strictNullChecks and noUnusedLocals (#421)
Browse files Browse the repository at this point in the history
* Propagation/B3: Enforce strictNullChecks and noUnusedLocals

* x-x3-parentspanid -> x-b3-parentspanid

* Add check for an array and falsey values

1. Remove unwanted check on getter.
2. Add check for an array or falsey value
3. Make sentence readable

* Add validators and return null when if there is no incoming traceID/spanID
  • Loading branch information
mayurkale22 committed Mar 15, 2019
1 parent 95607d0 commit 6e666d3
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 41 deletions.
68 changes: 37 additions & 31 deletions packages/opencensus-propagation-b3/src/b3-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,16 @@
*/

import {HeaderGetter, HeaderSetter, Propagation, SpanContext} from '@opencensus/core';

import * as crypto from 'crypto';
import * as uuid from 'uuid';
import {isValidSpanId, isValidTraceId} from './validators';


const X_B3_TRACE_ID = 'x-b3-traceid';
const X_B3_SPAN_ID = 'x-b3-spanid';
const X_B3_PARENT_SPAN_ID = 'x-x3-parentspanid';
const X_B3_SAMPLED = 'x-b3-sampled';

const SAMPLED_VALUE = 0x1;
const NOT_SAMPLED_VALUE = 0x0;

export const X_B3_TRACE_ID = 'x-b3-traceid';
export const X_B3_SPAN_ID = 'x-b3-spanid';
export const X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid';
export const X_B3_SAMPLED = 'x-b3-sampled';
export const SAMPLED_VALUE = 0x1;
export const NOT_SAMPLED_VALUE = 0x0;

/** Propagates span context through B3 Format propagation. */
export class B3Format implements Propagation {
Expand All @@ -36,19 +33,17 @@ export class B3Format implements Propagation {
* in the headers, null is returned.
* @param getter
*/
extract(getter: HeaderGetter): SpanContext {
if (getter) {
let opt = getter.getHeader(X_B3_SAMPLED);
if (opt instanceof Array) {
opt = opt[0];
}
const spanContext = {
traceId: getter.getHeader(X_B3_TRACE_ID),
spanId: getter.getHeader(X_B3_SPAN_ID),
options: isNaN(Number(opt)) ? undefined : Number(opt)
} as SpanContext;
extract(getter: HeaderGetter): SpanContext|null {
const traceId = this.parseHeader(getter.getHeader(X_B3_TRACE_ID));
const spanId = this.parseHeader(getter.getHeader(X_B3_SPAN_ID));
const opt = this.parseHeader(getter.getHeader(X_B3_SAMPLED));

return spanContext;
if (traceId && spanId) {
return {
traceId,
spanId,
options: isNaN(Number(opt)) ? NOT_SAMPLED_VALUE : Number(opt)
};
}
return null;
}
Expand All @@ -59,14 +54,17 @@ export class B3Format implements Propagation {
* @param spanContext
*/
inject(setter: HeaderSetter, spanContext: SpanContext): void {
if (setter) {
setter.setHeader(X_B3_TRACE_ID, spanContext.traceId || 'undefined');
setter.setHeader(X_B3_SPAN_ID, spanContext.spanId || 'undefined');
if (spanContext && (spanContext.options & SAMPLED_VALUE) !== 0) {
setter.setHeader(X_B3_SAMPLED, `${SAMPLED_VALUE}`);
} else {
setter.setHeader(X_B3_SAMPLED, `${NOT_SAMPLED_VALUE}`);
}
if (!spanContext || !isValidTraceId(spanContext.traceId) ||
!isValidSpanId(spanContext.spanId)) {
return;
}

setter.setHeader(X_B3_TRACE_ID, spanContext.traceId);
setter.setHeader(X_B3_SPAN_ID, spanContext.spanId);
if (((spanContext.options || NOT_SAMPLED_VALUE) & SAMPLED_VALUE) !== 0) {
setter.setHeader(X_B3_SAMPLED, `${SAMPLED_VALUE}`);
} else {
setter.setHeader(X_B3_SAMPLED, `${NOT_SAMPLED_VALUE}`);
}
}

Expand All @@ -78,6 +76,14 @@ export class B3Format implements Propagation {
traceId: uuid.v4().split('-').join(''),
spanId: crypto.randomBytes(8).toString('hex'),
options: SAMPLED_VALUE
} as SpanContext;
};
}

/** Converts a headers type to a string. */
private parseHeader(str: string|string[]|undefined): string|undefined {
if (Array.isArray(str)) {
return str[0];
}
return str;
}
}
72 changes: 72 additions & 0 deletions packages/opencensus-propagation-b3/src/validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright 2019, OpenCensus 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
*
* 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.
*/

type ValidationFn = (value: string) => boolean;

/**
* Determines if the given hex string is truely a hex value. False if value is
* null.
* @param value
*/
const isHex: ValidationFn = (value: string): boolean => {
return typeof value === 'string' && /^[0-9A-F]*$/i.test(value);
};

/**
* Determines if the given hex string is all zeros. False if value is null.
* @param value
*/
const isNotAllZeros: ValidationFn = (value: string): boolean => {
return typeof value === 'string' && !/^[0]*$/i.test(value);
};

/**
* Determines if the given hex string is of the given length. False if value is
* null.
* @param value
*/
const isLength = (length: number): ValidationFn => {
return (value: string): boolean => {
return typeof value === 'string' && value.length === length;
};
};

/**
* Compose a set of validation functions into a single validation call.
*/
const compose = (...fns: ValidationFn[]): ValidationFn => {
return (value: string) => {
return fns.reduce((isValid, fn) => isValid && fn(value), true);
};
};

/**
* Determines if the given traceId is valid based on section 2.2.2.1 of the
* Trace Context spec.
*/
export const isValidTraceId = compose(isHex, isNotAllZeros, isLength(32));

/**
* Determines if the given spanId is valid based on section 2.2.2.2 of the Trace
* Context spec.
*/
export const isValidSpanId = compose(isHex, isNotAllZeros, isLength(16));

/**
* Determines if the given option is valid based on section 2.2.3 of the Trace
* Context spec.
*/
export const isValidOption = compose(isHex, isLength(2));
84 changes: 75 additions & 9 deletions packages/opencensus-propagation-b3/test/test-b3-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,9 @@

import {HeaderGetter, HeaderSetter} from '@opencensus/core';
import * as assert from 'assert';
import * as uuid from 'uuid';

import {B3Format} from '../src/';

const X_B3_TRACE_ID = 'x-b3-traceid';
const X_B3_SPAN_ID = 'x-b3-spanid';
const X_B3_PARENT_SPAN_ID = 'x-x3-parentspanid';
const X_B3_SAMPLED = 'x-b3-sampled';

const SAMPLED_VALUE = 0x1;
const NOT_SAMPLED_VALUE = 0x0;
import {B3Format, NOT_SAMPLED_VALUE, SAMPLED_VALUE, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID} from '../src/';

const b3Format = new B3Format();

Expand All @@ -49,6 +42,55 @@ describe('B3Propagation', () => {

assert.deepEqual(b3Format.extract(getter), spanContext);
});

it('should return null when options and spanId are undefined', () => {
const traceId = uuid.v4().split('-').join('');
// tslint:disable-next-line
const headers = {} as any;
headers[X_B3_TRACE_ID] = traceId;
headers[X_B3_SPAN_ID] = undefined;

const getter: HeaderGetter = {
getHeader(name: string) {
return headers[name];
}
};

assert.deepEqual(b3Format.extract(getter), null);
});

it('should return null when traceId is undefined', () => {
// tslint:disable-next-line
const headers = {} as any;
headers[X_B3_TRACE_ID] = undefined;
headers[X_B3_SPAN_ID] = undefined;

const getter: HeaderGetter = {
getHeader(name: string) {
return headers[name];
}
};

assert.deepEqual(b3Format.extract(getter), null);
});

it('should extract data from an array', () => {
const spanContext = b3Format.generate();
// tslint:disable-next-line
const headers = {} as any;
headers[X_B3_TRACE_ID] =
[spanContext.traceId, uuid.v4().split('-').join('')];
headers[X_B3_SPAN_ID] = [spanContext.spanId];
headers[X_B3_SAMPLED] = [SAMPLED_VALUE];

const getter: HeaderGetter = {
getHeader(name: string) {
return headers[name];
}
};

assert.deepEqual(b3Format.extract(getter), spanContext);
});
});

describe('inject', () => {
Expand All @@ -71,6 +113,30 @@ describe('B3Propagation', () => {
b3Format.inject(setter, spanContext);
assert.deepEqual(b3Format.extract(getter), spanContext);
});

it('should not inject empty spancontext', () => {
const emptySpanContext = {
traceId: '',
spanId: '',
options: NOT_SAMPLED_VALUE,
};
// disable-next-line to disable no-any check
// tslint:disable-next-line
const headers = {} as any;
const setter: HeaderSetter = {
setHeader(name: string, value: string) {
headers[name] = value;
}
};
const getter: HeaderGetter = {
getHeader(name: string) {
return headers[name];
}
};

b3Format.inject(setter, emptySpanContext);
assert.deepEqual(b3Format.extract(getter), null);
});
});


Expand Down
3 changes: 2 additions & 1 deletion packages/opencensus-propagation-b3/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"pretty": true,
"module": "commonjs",
"target": "es6",
"strictNullChecks": false
"strictNullChecks": true,
"noUnusedLocals": true
},
"include": [
"src/**/*.ts",
Expand Down

0 comments on commit 6e666d3

Please sign in to comment.