| 30 | import { ignoreHttpMethodCheck } from '../config/AgentConfig'; |
| 31 | |
| 32 | class HttpPlugin implements SwPlugin { |
| 33 | readonly module = 'http'; |
| 34 | readonly versions = '*'; |
| 35 | readonly isBuiltIn = true; |
| 36 | |
| 37 | install(): void { |
| 38 | const http = require('http'); |
| 39 | const https = require('https'); |
| 40 | |
| 41 | this.interceptClientRequest(http, 'http'); |
| 42 | this.interceptServerRequest(http, 'http'); |
| 43 | this.interceptClientRequest(https, 'https'); |
| 44 | this.interceptServerRequest(https, 'https'); |
| 45 | } |
| 46 | |
| 47 | private interceptClientRequest(module: any, protocol: string) { |
| 48 | const _request = module.request; // BUG! this doesn't work with "import {request} from http", but haven't found an alternative yet |
| 49 | |
| 50 | module.request = function () { |
| 51 | const url: URL | string | RequestOptions = arguments[0]; |
| 52 | |
| 53 | const { host, pathname } = |
| 54 | url instanceof URL |
| 55 | ? url |
| 56 | : typeof url === 'string' |
| 57 | ? new URL(url) // TODO: this may throw invalid URL |
| 58 | : { |
| 59 | host: (url.host || url.hostname || 'unknown') + ':' + (url.port || 80), |
| 60 | pathname: url.path || '/', |
| 61 | }; |
| 62 | |
| 63 | const operation = pathname.replace(/\?.*$/g, ''); |
| 64 | const method = arguments[url instanceof URL || typeof url === 'string' ? 1 : 0]?.method || 'GET'; |
| 65 | const span = ignoreHttpMethodCheck(method) |
| 66 | ? DummySpan.create() |
| 67 | : ContextManager.current.newExitSpan(operation, Component.HTTP); |
| 68 | |
| 69 | if (span.depth) |
| 70 | // if we inherited from a higher level plugin then do nothing, higher level should do all the work and we don't duplicate here |
| 71 | return _request.apply(this, arguments); |
| 72 | |
| 73 | span.start(); |
| 74 | |
| 75 | try { |
| 76 | span.component = Component.HTTP; |
| 77 | span.layer = SpanLayer.HTTP; |
| 78 | span.peer = host; |
| 79 | |
| 80 | span.tag(Tag.httpURL(protocol + '://' + host + pathname)); |
| 81 | span.tag(Tag.httpMethod(method)); |
| 82 | |
| 83 | const copyStatusAndWrapEmit = (res: any) => { |
| 84 | span.tag(Tag.httpStatusCode(res.statusCode)); |
| 85 | |
| 86 | if (res.statusCode && res.statusCode >= 400) span.errored = true; |
| 87 | |
| 88 | if (res.statusMessage) span.tag(Tag.httpStatusMsg(res.statusMessage)); |
| 89 |
nothing calls this directly
no outgoing calls
no test coverage detected