The Art of Reading RFC
What Is an RFC?
RFC stands for "Request for Comments." Despite the name, modern RFCs are not drafts — they are the published technical standards that define how the Internet works. Every email protocol (RFC 5321, RFC 5322), every web standard (HTTP in RFC 9110, URLs in RFC 3986, cookies in RFC 6265), and most data format specifications (JSON in RFC 8259, JWT in RFC 7519) are defined in RFCs published by the Internet Engineering Task Force (IETF).
An RFC is the authoritative definition of what is valid, what is invalid, and what is implementation-defined. When a developer says "that email address is invalid," and you want to know if they're right, the RFC is your oracle.
The Structure of an RFC
RFCs follow a standard structure you will encounter repeatedly:
Abstract — a short summary of what the RFC does. Read this first to confirm it covers what you're looking for.
Introduction — motivation, scope, and key terms. Often contains the most readable explanation of the problem.
Terminology — defines terms used throughout. The RFC may redefine common words. "MUST," "SHOULD," "MAY," "MUST NOT," and "SHALL NOT" have specific technical meanings (from RFC 2119) — not just emphasis.
Core specification sections — the formal rules. These use ABNF grammar (Augmented Backus-Naur Form) to define valid syntax precisely. These are dense but unambiguous.
Examples — concrete instances of valid and invalid inputs. Gold for test case design.
Security considerations — common attack vectors, vulnerabilities, and implementation warnings. Often ignored by developers; priceless for testers.
IANA considerations and appendices — often skippable unless you need to verify registry values.
RFC 2119 Keywords Are Not Decoration
Every RFC that references RFC 2119 uses these keywords with precise meanings:
- MUST / REQUIRED / SHALL — This is a hard requirement. Any implementation that violates it is non-conformant. A "MUST" clause is a test case.
- MUST NOT / SHALL NOT — This is a hard prohibition. Anything this applies to is invalid input.
- SHOULD / RECOMMENDED — This is a strong preference that may be violated with good reason. Violations are acceptable but should be documented. A "SHOULD" clause is a potential edge case.
- SHOULD NOT / NOT RECOMMENDED — Allowed with justification, but discouraged.
- MAY / OPTIONAL — Purely discretionary. This is where implementations diverge most.
When you see "implementations MUST reject messages containing..." in an RFC, that sentence is a test case. Write it down.
Reading ABNF Grammar
ABNF is the formal language used to define valid syntax in most RFCs. It looks like this (from RFC 5322):
local-part = dot-atom / quoted-string / obs-local-part
dot-atom = [CFWS] dot-atom-text [CFWS]
dot-atom-text = 1*atext *("." 1*atext)
atext = ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" /
"*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" /
"`" / "{" / "|" / "}" / "~"
You don't need to parse this like a computer. Read it as: "a local-part is either a dot-atom, a quoted-string, or an obs-local-part. A dot-atom-text is one or more atext characters, optionally followed by dot-separated groups of one or more atext characters."
The key insight for testing: the ABNF tells you exactly what characters are valid and where. atext above explicitly includes +, !, #, $, %, &, etc. Any validator that rejects these in a local part is non-conformant. You now have specific test cases from the grammar.
Extracting Test Cases from RFC Language
The most efficient approach is to search for specific patterns:
-
Search for "MUST" — every occurrence is either a requirement (input MUST be valid under condition X) or a constraint (value MUST NOT exceed Y). Both are test cases.
-
Search for "examples" — most RFCs provide examples of valid and invalid values. Copy them directly into your test cases.
-
Search for "obsolete" or "obs-"** — ABNF sections prefixed with "obs-" define obsolete syntax. Per RFC 5322, parsers MUST accept obsolete syntax even though generators MUST NOT produce it. This is a rich source of edge cases: the RFC says validators must accept things your developers probably didn't know were valid.
-
Search for "implementations" or "implementations SHOULD" — these sentences describe known divergence between the spec and common practice.
-
Read the Security Considerations section — it explicitly documents attack vectors. These are test cases for security-focused testing.
Common Gaps Between RFC and Implementation
After reading an RFC, expect these classes of divergence:
Over-restrictive validation — the implementation rejects inputs the RFC says are valid. This is the most common gap. The developer implemented a simplified mental model of the spec. Examples: rejecting + in email local parts (RFC allows it), rejecting path-relative URLs (RFC 3986 allows them), rejecting cookies with = in their value (RFC 6265 allows it).
Under-restrictive validation — the implementation accepts inputs the RFC says are invalid. Often happens because validation is missing entirely for edge cases. Examples: accepting email addresses with two consecutive dots, accepting URLs with spaces in the path.
Obsolete syntax not handled — the RFC requires accepting an obsolete form for backwards compatibility, but the implementation was written using only the current spec and never tested the obsolete forms.
Case handling — many RFC tokens are case-insensitive (HTTP method names, email domain part, MIME type names). Implementations sometimes make them case-sensitive accidentally, or vice versa.
Length limits — RFCs specify maximum lengths for tokens. Implementations often don't validate these, or validate them with the wrong units (bytes vs. characters).
How to Use RFC as a Test Oracle
Once you've read the relevant sections:
- List the valid forms — write at least one test case for each valid form you found in the grammar and examples.
- List the invalid forms — write test cases that represent things the RFC explicitly prohibits.
- List the edge cases — obsolete forms, optional sections, CFWS (comments/folding whitespace), internationalized versions.
- Compare to the implementation — submit each test case and record what happens.
- Document divergences — note every place the implementation differs from the spec, whether it rejects valid inputs or accepts invalid ones.
This approach transforms the RFC from a reference document into a systematic test suite generator.
RFCs Are Occasionally Wrong
A note of intellectual honesty: RFCs can contain errors, ambiguities, and sections that are interpreted differently by different implementors. RFC 5321 and RFC 5322 together define email, but they have known inconsistencies. RFC 3986 (URI) has sections where the intended interpretation is genuinely ambiguous.
When you find a case where "the RFC says X but every major implementation does Y," document both. The practical oracle for user-facing behavior may be "what does Gmail/Outlook/etc. do" rather than "what does the RFC say." Both data points matter.
The RFC is the best starting point for test case generation — not the final word on production behavior.