@@ -42,6 +42,16 @@ function getSpanName(options: ClientRequestArgs|url.URL) {
4242 return options . hostname || options . host || 'localhost' ;
4343}
4444
45+ /**
46+ * Returns whether the Expect header is on the given options object.
47+ * @param options Options for http.request.
48+ */
49+ function hasExpectHeader ( options : ClientRequestArgs | url . URL ) : boolean {
50+ return ! ! (
51+ ( options as ClientRequestArgs ) . headers &&
52+ ( options as ClientRequestArgs ) . headers ! . Expect ) ;
53+ }
54+
4555function extractUrl (
4656 options : ClientRequestArgs | url . URL , fallbackProtocol : string ) {
4757 let path ;
@@ -111,6 +121,24 @@ function makeRequestTrace(
111121 span . addLabel ( api . labels . HTTP_METHOD_LABEL_KEY , method ) ;
112122 span . addLabel ( api . labels . HTTP_URL_LABEL_KEY , uri ) ;
113123
124+ // If outgoing request headers contain the "Expect" header, the returned
125+ // ClientRequest will throw an error if any new headers are added. For this
126+ // reason, only in this scenario, we opt to clone the options object to
127+ // inject the trace context header instead of using ClientRequest#setHeader.
128+ // (We don't do this generally because cloning the options object is an
129+ // expensive operation.)
130+ let traceHeaderPreinjected = false ;
131+ if ( hasExpectHeader ( options ) ) {
132+ traceHeaderPreinjected = true ;
133+ // "Clone" the options object -- but don't deep-clone anything except for
134+ // headers.
135+ options = Object . assign ( { } , options ) as ClientRequestArgs ;
136+ options . headers = Object . assign ( { } , options . headers ) ;
137+ // Inject the trace context header.
138+ options . headers [ api . constants . TRACE_CONTEXT_HEADER_NAME ] =
139+ span . getTraceContext ( ) ;
140+ }
141+
114142 const req = request ( options , ( res ) => {
115143 api . wrapEmitter ( res ) ;
116144 let numBytes = 0 ;
@@ -144,13 +172,17 @@ function makeRequestTrace(
144172 }
145173 } ) ;
146174 api . wrapEmitter ( req ) ;
147- req . setHeader (
148- api . constants . TRACE_CONTEXT_HEADER_NAME , span . getTraceContext ( ) ) ;
149175 req . on ( 'error' , error => {
150176 span . addLabel ( api . labels . ERROR_DETAILS_NAME , error . name ) ;
151177 span . addLabel ( api . labels . ERROR_DETAILS_MESSAGE , error . message ) ;
152178 span . endSpan ( ) ;
153179 } ) ;
180+ // Inject the trace context header, but only if it wasn't already injected
181+ // earlier.
182+ if ( ! traceHeaderPreinjected ) {
183+ req . setHeader (
184+ api . constants . TRACE_CONTEXT_HEADER_NAME , span . getTraceContext ( ) ) ;
185+ }
154186 return req ;
155187 } ;
156188}
0 commit comments