Apache Traffic Server (ATS) is a high-performance HTTP/HTTPS caching proxy server written in C++20. It processes large-scale web traffic using an event-driven, multi-threaded architecture with a sophisticated plugin system.
Key Facts:
Some of these rules are enforced automatically by CI (via clang-format and clang-tidy); others are recommended conventions that will not themselves cause CI failures but should still be followed in code reviews. Formatting Rules:
if (condition) { // code here }
Type *ptr; // Correct Type &ref; // Correct Type* ptr; // Wrong
void function() { int x = 5; std::string name = "test"; // Empty line before first code statement process_data(x, name); }
if (x > 0) { // Correct foo(); } if (x > 0) // Wrong - missing braces foo();
// Correct - declarations grouped together void function() { int count = 0; std::string name = "test"; auto *buffer = new_buffer(); // Empty line before first code statement process_data(count, name, buffer); } // Wrong - declarations scattered void function() { int count = 0; process_count(count); std::string name = "test"; // Don't scatter declarations }
Naming Conventions:
CamelCase → HttpSM, NetVConnection, CacheProcessorsnake_case → handle_request(), server_port, cache_keyUPPER_CASE → HTTP_STATUS_OK, MAX_BUFFER_SIZEsnake_case with no prefix → connection_count, buffer_sizeC++20 Patterns (Use These):
// GOOD - Modern C++20 auto buffer = std::make_unique<MIOBuffer>(size); for (const auto &entry : container) { if (auto *ptr = entry.get(); ptr != nullptr) { process(ptr); } } // AVOID - Legacy C-style MIOBuffer *buffer = (MIOBuffer*)malloc(sizeof(MIOBuffer)); for (int i = 0; i < container.size(); i++) { process(container[i]); }
Memory Management:
std::unique_ptr, std::shared_ptr)new/delete when possibleats_malloc()/ats_free() for large allocations (not malloc)IOBuffer for network data (zero-copy design)delete this (e.g., continuation-based code)What NOT to Use:
malloc/free for large allocations (use ats_malloc), or prefer heaps or stack allocationsMinimal comments philosophy:
// BAD - stating the obvious // Increment the counter counter++; // GOOD - explaining why // Skip the first element since it's always the sentinel value counter++; // BAD - describing what // Loop through all connections and close them for (auto &conn : connections) { conn.close(); } // GOOD - explaining why (if not obvious) // Must close connections before destroying the acceptor to avoid use-after-free for (auto &conn : connections) { conn.close(); }
When to add comments:
When NOT to add comments:
New source and test files must start with Apache License 2.0 header (.cc, .h, .py, and other code files):
/** @file * * Brief description of file * * @section license License * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */
ATS uses Continuation-based asynchronous programming:
// Continuation is the core callback pattern class MyContinuation : public Continuation { int handle_event(int event, void *data) { switch (event) { case EVENT_SUCCESS: // Handle success return EVENT_DONE; case EVENT_ERROR: // Handle error return EVENT_ERROR; } return EVENT_CONT; } };
Key Rules:
Continuation callbacksEVENT_DONE, EVENT_CONT, or EVENT_ERROR from handlersEThread::schedule() for deferred workThe HttpSM class orchestrates HTTP request processing:
// HttpSM is the central state machine class HttpSM : public Continuation { // Processes requests through various states // Hook into appropriate stage via plugin hooks // Access transaction state via HttpTxn };
Common hooks:
TS_HTTP_READ_REQUEST_HDR_HOOK - After reading client requestTS_HTTP_SEND_REQUEST_HDR_HOOK - Before sending to originTS_HTTP_READ_RESPONSE_HDR_HOOK - After reading origin responseTS_HTTP_SEND_RESPONSE_HDR_HOOK - Before sending to clientRule: Don't create threads. Use the event system or existing thread pools.
// At file scope static DbgCtl dbg_ctl{"http_sm"}; // In code (preferred) Dbg(dbg_ctl, "Processing request for URL: %s", url); // Alternative (less common) DbgPrint(dbg_ctl, "Processing request for URL: %s", url);
Note: Use Dbg() for new code. DbgPrint() exists but is rarely used (~60 vs ~3400 uses).
trafficserver/
├── src/
│ ├── iocore/ # I/O subsystem
│ │ ├── eventsystem/ # Event engine (Continuation.h is core)
│ │ ├── cache/ # Cache implementation
│ │ ├── net/ # Network I/O, TLS, QUIC
│ │ └── dns/ # DNS resolution
│ ├── proxy/ # HTTP proxy logic
│ │ ├── http/ # HTTP/1.1 (HttpSM.cc is central)
│ │ ├── http2/ # HTTP/2
│ │ ├── http3/ # HTTP/3
│ │ ├── hdrs/ # Header parsing
│ │ └── logging/ # Logging
│ ├── tscore/ # Core utilities
│ ├── tsutil/ # Utilities (metrics, debugging)
│ └── api/ # Plugin API implementation
│
├── include/
│ ├── ts/ # Public plugin API (ts.h)
│ ├── tscpp/ # C++ plugin API
│ └── iocore/ # Internal headers
│
├── plugins/ # Stable plugins
│ ├── header_rewrite/ # Header manipulation (see HRW.instructions.md)
│ └── experimental/ # Experimental plugins
│
└── tools/
└── hrw4u/ # Header Rewrite DSL compiler
include/iocore/eventsystem/Continuation.h - Core async patternsrc/proxy/http/HttpSM.cc - HTTP state machine (most important)src/iocore/cache/Cache.cc - Cache implementationinclude/ts/ts.h - Plugin API (most stable interface)include/tscore/ink_memory.h - Memory allocation functionsBefore writing new code, look for similar existing code:
example/plugins/ for simple patternsplugins/ for production patternsplugins/experimental/ for newer approachesPattern discovery:
include/ts/ts.h for plugin API patternstests/gold_tests/ for usage examplesTypical file structure for a plugin:
plugins/my_plugin/ ├── my_plugin.cc # Main plugin logic ├── handler.cc # Request/response handlers ├── handler.h # Handler interface ├── config.cc # Configuration parsing └── CMakeLists.txt # Build configuration
Typical class structure:
Continuation for async operationshandle_event() for event processingGeneral structure for async operations:
Action*)handle_event()EVENT_DONE when completeAlways async, never blocking:
schedule_in() or schedule_at()Recoverable errors:
Unrecoverable errors:
ink_release_assert() for conditions that should never happenWhen adding new functionality:
tests/gold_tests/ (autest)Test.ATSReplayTest() with replay.yaml format (Proxy Verifier)src/records/RecordsConfig.cc:{RECT_CONFIG, "proxy.config.my_feature.enabled", RECD_INT, "0", RECU_RESTART_TS, RR_NULL, RECC_INT, nullptr, RECA_NULL}
int enabled = 0; REC_ReadConfigInteger(enabled, "proxy.config.my_feature.enabled");
❌ Blocking in event threads:
// WRONG - blocks event thread sleep(5); blocking_network_call();
✅ Use async operations:
// CORRECT - schedules continuation eventProcessor.schedule_in(this, HRTIME_SECONDS(5));
❌ Manual memory management:
// WRONG auto *obj = new MyObject(); // ... might leak if exception thrown delete obj;
✅ Use RAII:
// CORRECT auto obj = std::make_unique<MyObject>(); // Automatically cleaned up
❌ Creating threads:
// WRONG std::thread t([](){ do_work(); });
✅ Use event system:
// CORRECT eventProcessor.schedule_imm(continuation, ET_CALL);
include/ts/ts.hinclude/iocore/eventsystem/src/proxy/http/HttpSM.ccdoc/developer-guide/