|
| 1 | +namespace Http11Probe.Cli.Reporting; |
| 2 | + |
| 3 | +internal static class DocsUrlMap |
| 4 | +{ |
| 5 | + private const string BaseUrl = "https://mda2av.github.io/Http11Probe/docs/"; |
| 6 | + |
| 7 | + private static readonly Dictionary<string, string> ComplianceSlugs = new(StringComparer.OrdinalIgnoreCase) |
| 8 | + { |
| 9 | + // body |
| 10 | + ["COMP-CHUNKED-BODY"] = "body/chunked-body", |
| 11 | + ["COMP-CHUNKED-EMPTY"] = "body/chunked-empty", |
| 12 | + ["COMP-CHUNKED-EXTENSION"] = "body/chunked-extension", |
| 13 | + ["COMP-CHUNKED-HEX-UPPERCASE"] = "body/chunked-hex-uppercase", |
| 14 | + ["COMP-CHUNKED-MULTI"] = "body/chunked-multi", |
| 15 | + ["COMP-CHUNKED-NO-FINAL"] = "body/chunked-no-final", |
| 16 | + ["COMP-CHUNKED-TRAILER-VALID"] = "body/chunked-trailer-valid", |
| 17 | + ["COMP-GET-WITH-CL-BODY"] = "body/get-with-cl-body", |
| 18 | + ["COMP-POST-CL-BODY"] = "body/post-cl-body", |
| 19 | + ["COMP-POST-CL-UNDERSEND"] = "body/post-cl-undersend", |
| 20 | + ["COMP-POST-CL-ZERO"] = "body/post-cl-zero", |
| 21 | + ["COMP-POST-NO-CL-NO-TE"] = "body/post-no-cl-no-te", |
| 22 | + |
| 23 | + // content-length |
| 24 | + ["RFC9112-6.1-CL-NON-NUMERIC"] = "content-length/cl-non-numeric", |
| 25 | + ["RFC9112-6.1-CL-PLUS-SIGN"] = "content-length/cl-plus-sign", |
| 26 | + |
| 27 | + // headers |
| 28 | + ["COMP-CONNECTION-CLOSE"] = "headers/connection-close", |
| 29 | + ["RFC9112-5-EMPTY-HEADER-NAME"] = "headers/empty-header-name", |
| 30 | + ["COMP-EXPECT-UNKNOWN"] = "headers/expect-unknown", |
| 31 | + ["RFC9112-5-HEADER-NO-COLON"] = "headers/header-no-colon", |
| 32 | + ["COMP-HTTP10-DEFAULT-CLOSE"] = "headers/http10-default-close", |
| 33 | + ["RFC9112-5-INVALID-HEADER-NAME"] = "headers/invalid-header-name", |
| 34 | + ["RFC9112-5.1-OBS-FOLD"] = "headers/obs-fold", |
| 35 | + ["RFC9110-5.6.2-SP-BEFORE-COLON"] = "headers/sp-before-colon", |
| 36 | + ["COMP-WHITESPACE-BEFORE-HEADERS"] = "headers/whitespace-before-headers", |
| 37 | + |
| 38 | + // host-header |
| 39 | + ["RFC9110-5.4-DUPLICATE-HOST"] = "host-header/duplicate-host", |
| 40 | + ["COMP-DUPLICATE-HOST-SAME"] = "host-header/duplicate-host-same", |
| 41 | + ["COMP-HOST-EMPTY-VALUE"] = "host-header/host-empty-value", |
| 42 | + ["COMP-HOST-WITH-PATH"] = "host-header/host-with-path", |
| 43 | + ["COMP-HOST-WITH-USERINFO"] = "host-header/host-with-userinfo", |
| 44 | + ["COMP-HTTP10-NO-HOST"] = "host-header/http10-no-host", |
| 45 | + ["RFC9112-7.1-MISSING-HOST"] = "host-header/missing-host", |
| 46 | + |
| 47 | + // line-endings |
| 48 | + ["RFC9112-2.2-BARE-LF-HEADER"] = "line-endings/bare-lf-header", |
| 49 | + ["RFC9112-2.2-BARE-LF-REQUEST-LINE"] = "line-endings/bare-lf-request-line", |
| 50 | + ["RFC9112-3-CR-ONLY-LINE-ENDING"] = "line-endings/cr-only-line-ending", |
| 51 | + ["COMP-LEADING-CRLF"] = "line-endings/leading-crlf", |
| 52 | + |
| 53 | + // request-line |
| 54 | + ["COMP-ABSOLUTE-FORM"] = "request-line/absolute-form", |
| 55 | + ["COMP-ASTERISK-WITH-GET"] = "request-line/asterisk-with-get", |
| 56 | + ["COMP-CONNECT-EMPTY-PORT"] = "request-line/connect-empty-port", |
| 57 | + ["COMP-CONNECT-ORIGIN-FORM"] = "request-line/connect-origin-form", |
| 58 | + ["RFC9112-3.2-FRAGMENT-IN-TARGET"] = "request-line/fragment-in-target", |
| 59 | + ["RFC9112-2.3-HTTP09-REQUEST"] = "request-line/http09-request", |
| 60 | + ["COMP-HTTP12-VERSION"] = "request-line/http12-version", |
| 61 | + ["RFC9112-2.3-INVALID-VERSION"] = "request-line/invalid-version", |
| 62 | + ["COMP-METHOD-CASE"] = "request-line/method-case", |
| 63 | + ["COMP-METHOD-CONNECT"] = "request-line/method-connect", |
| 64 | + ["COMP-METHOD-CONNECT-NO-PORT"] = "request-line/method-connect-no-port", |
| 65 | + ["COMP-METHOD-TRACE"] = "request-line/method-trace", |
| 66 | + ["RFC9112-3-MISSING-TARGET"] = "request-line/missing-target", |
| 67 | + ["RFC9112-3-MULTI-SP-REQUEST-LINE"] = "request-line/multi-sp-request-line", |
| 68 | + ["COMP-OPTIONS-STAR"] = "request-line/options-star", |
| 69 | + ["COMP-REQUEST-LINE-TAB"] = "request-line/request-line-tab", |
| 70 | + ["COMP-TRACE-WITH-BODY"] = "request-line/trace-with-body", |
| 71 | + ["COMP-UNKNOWN-TE-501"] = "request-line/unknown-te-501", |
| 72 | + ["COMP-VERSION-LEADING-ZEROS"] = "request-line/version-leading-zeros", |
| 73 | + ["COMP-VERSION-MISSING-MINOR"] = "request-line/version-missing-minor", |
| 74 | + ["COMP-VERSION-WHITESPACE"] = "request-line/version-whitespace", |
| 75 | + |
| 76 | + // upgrade |
| 77 | + ["COMP-UPGRADE-INVALID-VER"] = "upgrade/upgrade-invalid-ver", |
| 78 | + ["COMP-UPGRADE-MISSING-CONN"] = "upgrade/upgrade-missing-conn", |
| 79 | + ["COMP-UPGRADE-POST"] = "upgrade/upgrade-post", |
| 80 | + ["COMP-UPGRADE-UNKNOWN"] = "upgrade/upgrade-unknown", |
| 81 | + }; |
| 82 | + |
| 83 | + // Special cases where the doc filename doesn't match the ID suffix |
| 84 | + private static readonly Dictionary<string, string> SpecialSlugs = new(StringComparer.OrdinalIgnoreCase) |
| 85 | + { |
| 86 | + ["MAL-CHUNK-EXT-64K"] = "malformed-input/chunk-extension-long", |
| 87 | + ["SMUG-TRANSFER_ENCODING"] = "smuggling/transfer-encoding-underscore", |
| 88 | + }; |
| 89 | + |
| 90 | + public static string? GetUrl(string testId) |
| 91 | + { |
| 92 | + if (SpecialSlugs.TryGetValue(testId, out var special)) |
| 93 | + return BaseUrl + special; |
| 94 | + |
| 95 | + if (ComplianceSlugs.TryGetValue(testId, out var slug)) |
| 96 | + return BaseUrl + slug; |
| 97 | + |
| 98 | + // SMUG-* → smuggling/{suffix} |
| 99 | + if (testId.StartsWith("SMUG-", StringComparison.OrdinalIgnoreCase)) |
| 100 | + { |
| 101 | + var suffix = testId[5..].ToLowerInvariant(); |
| 102 | + return BaseUrl + "smuggling/" + suffix; |
| 103 | + } |
| 104 | + |
| 105 | + // MAL-* → malformed-input/{suffix} |
| 106 | + if (testId.StartsWith("MAL-", StringComparison.OrdinalIgnoreCase)) |
| 107 | + { |
| 108 | + var suffix = testId[4..].ToLowerInvariant(); |
| 109 | + return BaseUrl + "malformed-input/" + suffix; |
| 110 | + } |
| 111 | + |
| 112 | + return null; |
| 113 | + } |
| 114 | +} |
0 commit comments