| 296 | }; |
| 297 | |
| 298 | static void |
| 299 | InjectMilestonesHeader(TSHttpTxn txn, TSMBuffer buffer, TSMLoc hdr) |
| 300 | { |
| 301 | // The set of milestones we can publish. Some milestones happen after |
| 302 | // this hook, so we skip those ... |
| 303 | static const milestone milestones[] = { |
| 304 | {TS_MILESTONE_UA_BEGIN, "UA-BEGIN" }, |
| 305 | {TS_MILESTONE_UA_FIRST_READ, "UA-FIRST-READ" }, |
| 306 | {TS_MILESTONE_UA_READ_HEADER_DONE, "UA-READ-HEADER-DONE" }, |
| 307 | {TS_MILESTONE_UA_BEGIN_WRITE, "UA-BEGIN-WRITE" }, |
| 308 | {TS_MILESTONE_UA_CLOSE, "UA-CLOSE" }, |
| 309 | {TS_MILESTONE_SERVER_FIRST_CONNECT, "SERVER-FIRST-CONNECT" }, |
| 310 | {TS_MILESTONE_SERVER_CONNECT, "SERVER-CONNECT" }, |
| 311 | {TS_MILESTONE_SERVER_CONNECT_END, "SERVER-CONNECT-END" }, |
| 312 | {TS_MILESTONE_SERVER_BEGIN_WRITE, "SERVER-BEGIN-WRITE" }, |
| 313 | {TS_MILESTONE_SERVER_FIRST_READ, "SERVER-FIRST-READ" }, |
| 314 | {TS_MILESTONE_SERVER_READ_HEADER_DONE, "SERVER-READ-HEADER-DONE"}, |
| 315 | {TS_MILESTONE_SERVER_CLOSE, "SERVER-CLOSE" }, |
| 316 | {TS_MILESTONE_CACHE_OPEN_READ_BEGIN, "CACHE-OPEN-READ-BEGIN" }, |
| 317 | {TS_MILESTONE_CACHE_OPEN_READ_END, "CACHE-OPEN-READ-END" }, |
| 318 | {TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN, "CACHE-OPEN-WRITE-BEGIN" }, |
| 319 | {TS_MILESTONE_CACHE_OPEN_WRITE_END, "CACHE-OPEN-WRITE-END" }, |
| 320 | {TS_MILESTONE_DNS_LOOKUP_BEGIN, "DNS-LOOKUP-BEGIN" }, |
| 321 | {TS_MILESTONE_DNS_LOOKUP_END, "DNS-LOOKUP-END" }, |
| 322 | // SM_START is deliberately excluded because as all the times are printed relative to it |
| 323 | // it would always be zero. |
| 324 | {TS_MILESTONE_SM_FINISH, "SM-FINISH" }, |
| 325 | {TS_MILESTONE_PLUGIN_ACTIVE, "PLUGIN-ACTIVE" }, |
| 326 | {TS_MILESTONE_PLUGIN_TOTAL, "PLUGIN-TOTAL" }, |
| 327 | }; |
| 328 | |
| 329 | TSMLoc dst = TS_NULL_MLOC; |
| 330 | TSHRTime epoch; |
| 331 | |
| 332 | // TS_MILESTONE_SM_START is stamped when the HTTP transaction is born. The slow |
| 333 | // log feature publishes the other times as seconds relative to this epoch. Let's |
| 334 | // do the same. |
| 335 | TSHttpTxnMilestoneGet(txn, TS_MILESTONE_SM_START, &epoch); |
| 336 | |
| 337 | // Create a new response header field. |
| 338 | dst = FindOrMakeHdrField(buffer, hdr, "X-Milestones", lengthof("X-Milestones")); |
| 339 | if (dst == TS_NULL_MLOC) { |
| 340 | goto done; |
| 341 | } |
| 342 | |
| 343 | for (unsigned i = 0; i < countof(milestones); ++i) { |
| 344 | TSHRTime time = 0; |
| 345 | char hdrval[64]; |
| 346 | |
| 347 | // If we got a milestone (it's in nanoseconds), convert it to seconds relative to |
| 348 | // the start of the transaction. We don't get milestone values for portions of the |
| 349 | // state machine the request doesn't traverse. |
| 350 | TSHttpTxnMilestoneGet(txn, milestones[i].mstype, &time); |
| 351 | if (time > 0) { |
| 352 | double elapsed = static_cast<double>(time - epoch) / 1000000000.0; |
| 353 | int len = snprintf(hdrval, sizeof(hdrval), "%s=%1.9lf", milestones[i].msname, elapsed); |
| 354 | |
| 355 | TSReleaseAssert(TSMimeHdrFieldValueStringInsert(buffer, hdr, dst, -1 /* idx */, hdrval, len) == TS_SUCCESS); |
no test coverage detected