[{"data":1,"prerenderedAt":4471},["ShallowReactive",2],{"navigation":3,"/actix-web/keys":240,"/actix-web/keys-surround":4466},[4,34,66,87,106,145,166,176,204],{"title":5,"path":6,"stem":7,"children":8,"icon":32,"defaultOpen":33},"Getting Started","/getting-started","1.getting-started/1.index",[9,12,17,22,27],{"title":10,"path":6,"stem":7,"icon":11},"Introduction","i-lucide-info",{"title":13,"path":14,"stem":15,"icon":16},"Installation","/getting-started/installation","1.getting-started/2.installation","i-lucide-download",{"title":18,"path":19,"stem":20,"icon":21},"Quickstart (Local)","/getting-started/quickstart-local","1.getting-started/3.quickstart-local","i-lucide-zap",{"title":23,"path":24,"stem":25,"icon":26},"Quickstart (Redis)","/getting-started/quickstart-redis","1.getting-started/4.quickstart-redis","i-lucide-database",{"title":28,"path":29,"stem":30,"icon":31},"Quickstart (Hybrid)","/getting-started/quickstart-hybrid","1.getting-started/5.quickstart-hybrid","i-lucide-arrow-left-right","i-lucide-rocket",true,{"title":35,"icon":36,"defaultOpen":33,"path":37,"stem":38,"children":39,"page":65},"Concepts","i-lucide-lightbulb","/concepts","2.concepts",[40,45,50,55,60],{"title":41,"path":42,"stem":43,"icon":44},"Keys","/concepts/keys","2.concepts/1.keys","i-lucide-key-round",{"title":46,"path":47,"stem":48,"icon":49},"Rate Limits","/concepts/rate-limits","2.concepts/2.rate-limits","i-lucide-badge-percent",{"title":51,"path":52,"stem":53,"icon":54},"Sliding Windows","/concepts/sliding-windows","2.concepts/3.sliding-windows","i-lucide-timer-reset",{"title":56,"path":57,"stem":58,"icon":59},"Decisions","/concepts/decisions","2.concepts/4.decisions","i-lucide-check-check",{"title":61,"path":62,"stem":63,"icon":64},"Configuration Types","/concepts/configuration-types","2.concepts/5.configuration-types","i-lucide-box",false,{"title":67,"icon":68,"defaultOpen":33,"path":69,"stem":70,"children":71,"page":65},"Strategies","i-lucide-git-branch","/strategies","3.strategies",[72,77,82],{"title":73,"path":74,"stem":75,"icon":76},"Absolute","/strategies/absolute","3.strategies/1.absolute","i-lucide-shield",{"title":78,"path":79,"stem":80,"icon":81},"Suppressed","/strategies/suppressed","3.strategies/2.suppressed","i-lucide-activity",{"title":83,"path":84,"stem":85,"icon":86},"How Suppression Works","/strategies/how-suppression-works","3.strategies/3.how-suppression-works","i-lucide-function-square",{"title":88,"icon":89,"defaultOpen":33,"path":90,"stem":91,"children":92,"page":65},"Providers","i-lucide-layers","/providers","4.providers",[93,98,102],{"title":94,"path":95,"stem":96,"icon":97},"Local","/providers/local","4.providers/1.local","i-lucide-cpu",{"title":99,"path":100,"stem":101,"icon":26},"Redis","/providers/redis","4.providers/2.redis",{"title":103,"path":104,"stem":105,"icon":31},"Hybrid","/providers/hybrid","4.providers/3.hybrid",{"title":107,"path":108,"stem":109,"children":110,"icon":76,"defaultOpen":65},"Actix Web","/actix-web","5.actix-web/1.index",[111,113,118,122,127,131,136,140],{"title":112,"path":108,"stem":109,"icon":76},"Overview",{"title":114,"path":115,"stem":116,"icon":117},"Client IPs","/actix-web/client-ips","5.actix-web/2.client-ips","i-lucide-network",{"title":119,"path":120,"stem":121,"icon":44},"Keys and custom extractors","/actix-web/keys","5.actix-web/3.keys",{"title":123,"path":124,"stem":125,"icon":126},"Limits","/actix-web/limits","5.actix-web/4.limits","i-lucide-gauge",{"title":128,"path":129,"stem":130,"icon":26},"Backends","/actix-web/backends","5.actix-web/5.backends",{"title":132,"path":133,"stem":134,"icon":135},"Responses","/actix-web/responses","5.actix-web/6.responses","i-lucide-reply",{"title":137,"path":138,"stem":139,"icon":81},"Observability","/actix-web/observability","5.actix-web/7.observability",{"title":141,"path":142,"stem":143,"icon":144},"Performance","/actix-web/performance","5.actix-web/8.performance","i-lucide-timer",{"title":146,"icon":147,"defaultOpen":65,"path":148,"stem":149,"children":150,"page":65},"Guides","i-lucide-map","/guides","5.guides",[151,156,161],{"title":152,"path":153,"stem":154,"icon":155},"Configuration","/guides/configuration","5.guides/1.configuration","i-lucide-sliders-horizontal",{"title":157,"path":158,"stem":159,"icon":160},"Cleanup","/guides/cleanup","5.guides/2.cleanup","i-lucide-trash-2",{"title":162,"path":163,"stem":164,"icon":165},"Troubleshooting","/guides/troubleshooting","5.guides/4.troubleshooting","i-lucide-wrench",{"title":167,"icon":168,"defaultOpen":65,"path":169,"stem":170,"children":171,"page":65},"Reference","i-lucide-file-text","/reference","6.reference",[172],{"title":173,"path":174,"stem":175,"icon":168},"API Reference Guide","/reference/api","6.reference/1.api",{"title":177,"icon":126,"defaultOpen":65,"path":178,"stem":179,"children":180,"page":65},"Benchmarks","/benchmarks","7.benchmarks",[181,186,190],{"title":182,"path":183,"stem":184,"icon":185},"Benchmark Concepts","/benchmarks/benchmark-concepts","7.benchmarks/0.benchmark-concepts","i-lucide-book-open",{"title":187,"path":188,"stem":189,"icon":126},"Benchmarking & Load Testing","/benchmarks/benchmarking-load-testing","7.benchmarks/1.benchmarking-load-testing",{"title":191,"path":192,"stem":193,"children":194,"icon":203},"Benchmark Results","/benchmarks/benchmark-results","7.benchmarks/2.benchmark-results",[195,199],{"title":196,"path":197,"stem":198,"icon":97},"Local Benchmark Comparison","/benchmarks/benchmark-results/local-benchmark-comparison","7.benchmarks/2.benchmark-results/1.local-benchmark-comparison",{"title":200,"path":201,"stem":202,"icon":26},"Redis + Hybrid Benchmark Comparison","/benchmarks/benchmark-results/redis-benchmark-comparison","7.benchmarks/2.benchmark-results/2.redis-benchmark-comparison","i-lucide-chart-no-axes-combined",{"title":205,"path":206,"stem":207,"children":208,"icon":210,"defaultOpen":65},"Changelog","/changelog","8.changelog/0.index",[209,211,216,220,224,228,232,236],{"title":205,"path":206,"stem":207,"icon":210},"i-lucide-scroll",{"title":212,"path":213,"stem":214,"icon":215},"v2.2.1","/changelog/v2-2-1","8.changelog/1.v2-2-1","i-lucide-tag",{"title":217,"path":218,"stem":219,"icon":215},"v2.2.0","/changelog/v2-2-0","8.changelog/2.v2-2-0",{"title":221,"path":222,"stem":223,"icon":215},"v2.1.0","/changelog/v2-1-0","8.changelog/3.v2-1-0",{"title":225,"path":226,"stem":227,"icon":215},"v2.0.0","/changelog/v2-0-0","8.changelog/4.v2-0-0",{"title":229,"path":230,"stem":231,"icon":215},"v1.1.0","/changelog/v1-1-0","8.changelog/5.v1-1-0",{"title":233,"path":234,"stem":235,"icon":215},"v1.0.1","/changelog/v1-0-1","8.changelog/6.v1-0-1",{"title":237,"path":238,"stem":239,"icon":215},"v1.0.0","/changelog/v1-0-0","8.changelog/7.v1-0-0",{"id":241,"title":119,"authors":242,"badge":242,"body":243,"date":242,"description":4460,"extension":4461,"links":242,"meta":4462,"navigation":4463,"path":120,"seo":4464,"stem":121,"__hash__":4465},"docs/5.actix-web/3.keys.md",null,{"type":244,"value":245,"toc":4440},"minimark",[246,250,339,346,595,611,622,628,633,797,803,808,995,998,1002,1012,1017,1020,1357,1361,1372,1763,1767,2015,2025,2029,2037,2600,2607,2621,2759,2762,3203,3219,3626,3631,4034,4038,4074,4078,4085,4137,4339,4361,4365,4375,4436],[247,248,249],"p",{},"The extractor decides who a request counts against. Each distinct key gets its own bucket. The\nkey is combined with the limiter's namespace before it is stored, so limiters never share\nbuckets by accident.",[251,252,253,266],"table",{},[254,255,256],"thead",{},[257,258,259,263],"tr",{},[260,261,262],"th",{},"Extractor",[260,264,265],{},"Keys by",[267,268,269,289,299,309,319,329],"tbody",{},[257,270,271,282],{},[272,273,274,278,279],"td",{},[275,276,277],"code",{},"PeerIp",", ",[275,280,281],{},"RealIp",[272,283,284,285,288],{},"Client IP; see ",[286,287,114],"a",{"href":115},".",[257,290,291,296],{},[272,292,293],{},[275,294,295],{},"Header",[272,297,298],{},"A request header, raw or hashed.",[257,300,301,306],{},[272,302,303],{},[275,304,305],{},"Global",[272,307,308],{},"Nothing: one bucket for every request.",[257,310,311,316],{},[272,312,313],{},[275,314,315],{},"Composite",[272,317,318],{},"Two extractors joined, such as tenant and IP.",[257,320,321,326],{},[272,322,323],{},[275,324,325],{},"extractor_fn(..)",[272,327,328],{},"A closure you write.",[257,330,331,336],{},[272,332,333],{},[275,334,335],{},"impl KeyExtractor",[272,337,338],{},"A type you write, without allocating per request.",[340,341,343,344],"h2",{"id":342},"api-keys-and-other-headers-header","API keys and other headers: ",[275,345,295],{},[347,348,353],"pre",{"className":349,"code":350,"language":351,"meta":352,"style":352},"language-rust shiki shiki-themes material-theme-lighter github-light github-dark monokai","use actix_trypema::{Local, TrypemaLimiter, extract::Header};\nuse trypema::{RateLimit, WindowSize};\n\nlet limiter = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(60))?)\n    .namespace(\"apikey\")\n    // hashed(): the raw key never appears in limiter state, Redis or logs.\n    .extractor(Header::new(\"x-api-key\")?.hashed())\n    .rate(RateLimit::per_minute_or_panic(1_000.0))\n    .build()?;\n","rust","",[275,354,355,396,418,424,481,504,511,548,579],{"__ignoreMap":352},[356,357,360,364,368,372,376,378,381,384,386,389,391,393],"span",{"class":358,"line":359},"line",1,[356,361,363],{"class":362},"shWJe","use",[356,365,367],{"class":366},"sKvfc"," actix_trypema",[356,369,371],{"class":370},"sGXK2","::",[356,373,375],{"class":374},"swvn1","{",[356,377,94],{"class":366},[356,379,380],{"class":374},",",[356,382,383],{"class":366}," TrypemaLimiter",[356,385,380],{"class":374},[356,387,388],{"class":366}," extract",[356,390,371],{"class":370},[356,392,295],{"class":366},[356,394,395],{"class":374},"};\n",[356,397,399,401,404,406,408,411,413,416],{"class":358,"line":398},2,[356,400,363],{"class":362},[356,402,403],{"class":366}," trypema",[356,405,371],{"class":370},[356,407,375],{"class":374},[356,409,410],{"class":366},"RateLimit",[356,412,380],{"class":374},[356,414,415],{"class":366}," WindowSize",[356,417,395],{"class":374},[356,419,421],{"class":358,"line":420},3,[356,422,423],{"emptyLinePlaceholder":33},"\n",[356,425,427,431,435,438,440,442,446,449,451,453,456,458,461,463,466,468,472,475,478],{"class":358,"line":426},4,[356,428,430],{"class":429},"srJo8","let",[356,432,434],{"class":433},"ss--_"," limiter ",[356,436,437],{"class":370},"=",[356,439,383],{"class":366},[356,441,371],{"class":370},[356,443,445],{"class":444},"sD0ED","builder",[356,447,448],{"class":374},"(",[356,450,94],{"class":366},[356,452,371],{"class":370},[356,454,455],{"class":444},"new",[356,457,448],{"class":374},[356,459,460],{"class":366},"WindowSize",[356,462,371],{"class":370},[356,464,465],{"class":444},"seconds_or_panic",[356,467,448],{"class":374},[356,469,471],{"class":470},"sYThS","60",[356,473,474],{"class":374},"))",[356,476,477],{"class":370},"?",[356,479,480],{"class":374},")\n",[356,482,484,487,490,492,496,500,502],{"class":358,"line":483},5,[356,485,486],{"class":370},"    .",[356,488,489],{"class":444},"namespace",[356,491,448],{"class":374},[356,493,495],{"class":494},"siCPE","\"",[356,497,499],{"class":498},"sLACW","apikey",[356,501,495],{"class":494},[356,503,480],{"class":374},[356,505,507],{"class":358,"line":506},6,[356,508,510],{"class":509},"ss7Ak","    // hashed(): the raw key never appears in limiter state, Redis or logs.\n",[356,512,514,516,519,521,523,525,527,529,531,534,536,539,542,545],{"class":358,"line":513},7,[356,515,486],{"class":370},[356,517,518],{"class":444},"extractor",[356,520,448],{"class":374},[356,522,295],{"class":366},[356,524,371],{"class":370},[356,526,455],{"class":444},[356,528,448],{"class":374},[356,530,495],{"class":494},[356,532,533],{"class":498},"x-api-key",[356,535,495],{"class":494},[356,537,538],{"class":374},")",[356,540,541],{"class":370},"?.",[356,543,544],{"class":444},"hashed",[356,546,547],{"class":374},"())\n",[356,549,551,553,556,558,560,562,565,567,570,573,576],{"class":358,"line":550},8,[356,552,486],{"class":370},[356,554,555],{"class":444},"rate",[356,557,448],{"class":374},[356,559,410],{"class":366},[356,561,371],{"class":370},[356,563,564],{"class":444},"per_minute_or_panic",[356,566,448],{"class":374},[356,568,569],{"class":470},"1_000",[356,571,288],{"class":572},"sMTiH",[356,574,575],{"class":470},"0",[356,577,578],{"class":374},"))\n",[356,580,582,584,587,590,592],{"class":358,"line":581},9,[356,583,486],{"class":370},[356,585,586],{"class":444},"build",[356,588,589],{"class":374},"()",[356,591,477],{"class":370},[356,593,594],{"class":374},";\n",[596,597,599],"callout",{"icon":598},"i-lucide-triangle-alert",[247,600,601,602,606,607,288],{},"Key only on values an earlier middleware has ",[603,604,605],"strong",{},"authenticated",". A raw header is chosen by the\nclient, and every new value gets a fresh, full bucket. Wrap this limiter inside your\nauthentication middleware, or validate the value in a ",[286,608,610],{"href":609},"#closure-extractors","custom extractor",[247,612,613,614,617,618,621],{},"A missing header is a key error (",[275,615,616],{},"400"," by default). Values longer than 1024 bytes are rejected,\nor truncated before hashing with ",[275,619,620],{},"hashed()",". Header names must be lowercase.",[340,623,625,626],{"id":624},"one-bucket-for-everything-global","One bucket for everything: ",[275,627,305],{},[247,629,630,632],{},[275,631,305],{}," puts every request in one bucket. Use it to protect something with a fixed total\ncapacity, such as a slow downstream API:",[347,634,636],{"className":349,"code":635,"language":351,"meta":352,"style":352},"use actix_trypema::{Local, TrypemaLimiter, extract::Global};\nuse trypema::{RateLimit, WindowSize};\n\nlet limiter = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(1))?)\n    .namespace(\"reports\")\n    .extractor(Global)\n    .rate(RateLimit::per_second_or_panic(20.0)) // 20 requests per second in total\n    .build()?;\n",[275,637,638,664,682,686,727,744,756,785],{"__ignoreMap":352},[356,639,640,642,644,646,648,650,652,654,656,658,660,662],{"class":358,"line":359},[356,641,363],{"class":362},[356,643,367],{"class":366},[356,645,371],{"class":370},[356,647,375],{"class":374},[356,649,94],{"class":366},[356,651,380],{"class":374},[356,653,383],{"class":366},[356,655,380],{"class":374},[356,657,388],{"class":366},[356,659,371],{"class":370},[356,661,305],{"class":366},[356,663,395],{"class":374},[356,665,666,668,670,672,674,676,678,680],{"class":358,"line":398},[356,667,363],{"class":362},[356,669,403],{"class":366},[356,671,371],{"class":370},[356,673,375],{"class":374},[356,675,410],{"class":366},[356,677,380],{"class":374},[356,679,415],{"class":366},[356,681,395],{"class":374},[356,683,684],{"class":358,"line":420},[356,685,423],{"emptyLinePlaceholder":33},[356,687,688,690,692,694,696,698,700,702,704,706,708,710,712,714,716,718,721,723,725],{"class":358,"line":426},[356,689,430],{"class":429},[356,691,434],{"class":433},[356,693,437],{"class":370},[356,695,383],{"class":366},[356,697,371],{"class":370},[356,699,445],{"class":444},[356,701,448],{"class":374},[356,703,94],{"class":366},[356,705,371],{"class":370},[356,707,455],{"class":444},[356,709,448],{"class":374},[356,711,460],{"class":366},[356,713,371],{"class":370},[356,715,465],{"class":444},[356,717,448],{"class":374},[356,719,720],{"class":470},"1",[356,722,474],{"class":374},[356,724,477],{"class":370},[356,726,480],{"class":374},[356,728,729,731,733,735,737,740,742],{"class":358,"line":483},[356,730,486],{"class":370},[356,732,489],{"class":444},[356,734,448],{"class":374},[356,736,495],{"class":494},[356,738,739],{"class":498},"reports",[356,741,495],{"class":494},[356,743,480],{"class":374},[356,745,746,748,750,752,754],{"class":358,"line":506},[356,747,486],{"class":370},[356,749,518],{"class":444},[356,751,448],{"class":374},[356,753,305],{"class":366},[356,755,480],{"class":374},[356,757,758,760,762,764,766,768,771,773,776,778,780,782],{"class":358,"line":513},[356,759,486],{"class":370},[356,761,555],{"class":444},[356,763,448],{"class":374},[356,765,410],{"class":366},[356,767,371],{"class":370},[356,769,770],{"class":444},"per_second_or_panic",[356,772,448],{"class":374},[356,774,775],{"class":470},"20",[356,777,288],{"class":572},[356,779,575],{"class":470},[356,781,474],{"class":374},[356,783,784],{"class":509}," // 20 requests per second in total\n",[356,786,787,789,791,793,795],{"class":358,"line":550},[356,788,486],{"class":370},[356,790,586],{"class":444},[356,792,589],{"class":374},[356,794,477],{"class":370},[356,796,594],{"class":374},[340,798,800,801],{"id":799},"two-keys-at-once-composite","Two keys at once: ",[275,802,315],{},[247,804,805,807],{},[275,806,315],{}," joins two extractors into one key, for example a per-IP limit inside each tenant:",[347,809,811],{"className":349,"code":810,"language":351,"meta":352,"style":352},"use actix_trypema::extract::{Composite, Header, PeerIp};\n\nlet per_tenant_ip = Composite::new(Header::new(\"x-tenant-id\")?, PeerIp::default());\n\n// Nest for three parts: tenant, user and IP.\nlet per_tenant_user_ip = Composite::new(\n    Header::new(\"x-tenant-id\")?,\n    Composite::new(Header::new(\"x-user-id\")?.hashed(), PeerIp::default()),\n);\n",[275,812,813,842,846,895,899,904,922,946,990],{"__ignoreMap":352},[356,814,815,817,819,821,824,826,828,830,832,835,837,840],{"class":358,"line":359},[356,816,363],{"class":362},[356,818,367],{"class":366},[356,820,371],{"class":370},[356,822,823],{"class":366},"extract",[356,825,371],{"class":370},[356,827,375],{"class":374},[356,829,315],{"class":366},[356,831,380],{"class":374},[356,833,834],{"class":366}," Header",[356,836,380],{"class":374},[356,838,839],{"class":366}," PeerIp",[356,841,395],{"class":374},[356,843,844],{"class":358,"line":398},[356,845,423],{"emptyLinePlaceholder":33},[356,847,848,850,853,855,858,860,862,864,866,868,870,872,874,877,879,881,883,885,887,889,892],{"class":358,"line":420},[356,849,430],{"class":429},[356,851,852],{"class":433}," per_tenant_ip ",[356,854,437],{"class":370},[356,856,857],{"class":366}," Composite",[356,859,371],{"class":370},[356,861,455],{"class":444},[356,863,448],{"class":374},[356,865,295],{"class":366},[356,867,371],{"class":370},[356,869,455],{"class":444},[356,871,448],{"class":374},[356,873,495],{"class":494},[356,875,876],{"class":498},"x-tenant-id",[356,878,495],{"class":494},[356,880,538],{"class":374},[356,882,477],{"class":370},[356,884,380],{"class":374},[356,886,839],{"class":366},[356,888,371],{"class":370},[356,890,891],{"class":444},"default",[356,893,894],{"class":374},"());\n",[356,896,897],{"class":358,"line":426},[356,898,423],{"emptyLinePlaceholder":33},[356,900,901],{"class":358,"line":483},[356,902,903],{"class":509},"// Nest for three parts: tenant, user and IP.\n",[356,905,906,908,911,913,915,917,919],{"class":358,"line":506},[356,907,430],{"class":429},[356,909,910],{"class":433}," per_tenant_user_ip ",[356,912,437],{"class":370},[356,914,857],{"class":366},[356,916,371],{"class":370},[356,918,455],{"class":444},[356,920,921],{"class":374},"(\n",[356,923,924,927,929,931,933,935,937,939,941,943],{"class":358,"line":513},[356,925,926],{"class":366},"    Header",[356,928,371],{"class":370},[356,930,455],{"class":444},[356,932,448],{"class":374},[356,934,495],{"class":494},[356,936,876],{"class":498},[356,938,495],{"class":494},[356,940,538],{"class":374},[356,942,477],{"class":370},[356,944,945],{"class":374},",\n",[356,947,948,951,953,955,957,959,961,963,965,967,970,972,974,976,978,981,983,985,987],{"class":358,"line":550},[356,949,950],{"class":366},"    Composite",[356,952,371],{"class":370},[356,954,455],{"class":444},[356,956,448],{"class":374},[356,958,295],{"class":366},[356,960,371],{"class":370},[356,962,455],{"class":444},[356,964,448],{"class":374},[356,966,495],{"class":494},[356,968,969],{"class":498},"x-user-id",[356,971,495],{"class":494},[356,973,538],{"class":374},[356,975,541],{"class":370},[356,977,544],{"class":444},[356,979,980],{"class":374},"(),",[356,982,839],{"class":366},[356,984,371],{"class":370},[356,986,891],{"class":444},[356,988,989],{"class":374},"()),\n",[356,991,992],{"class":358,"line":581},[356,993,994],{"class":374},");\n",[247,996,997],{},"The parts are joined so that different pairs never produce the same key.",[340,999,1001],{"id":1000},"closure-extractors","Closure extractors",[247,1003,1004,1007,1008,1011],{},[275,1005,1006],{},"extractor_fn"," keys requests with a closure that returns ",[275,1009,1010],{},"Result\u003CString, KeyError>",". It can read\nanything on the request: headers, the path, cookies, query strings, or values earlier\nmiddleware stored in the request extensions.",[1013,1014,1016],"h3",{"id":1015},"the-authenticated-user","The authenticated user",[247,1018,1019],{},"Authentication middleware usually stores the user in the request extensions. Key on it, and\nwrap the limiter inside that middleware so the user is there:",[347,1021,1023],{"className":349,"code":1022,"language":351,"meta":352,"style":352},"use actix_trypema::{KeyError, Local, TrypemaLimiter};\nuse actix_web::HttpMessage;\nuse trypema::{RateLimit, WindowSize};\n\n/// Inserted by your authentication middleware.\n#[derive(Clone)]\nstruct UserId(u64);\n\nlet per_user = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(60))?)\n    .namespace(\"user\")\n    .extractor_fn(|req| {\n        req.extensions()\n            .get::\u003CUserId>()\n            .map(|user| user.0.to_string())\n            .ok_or(KeyError::InvalidValue {\n                reason: \"request is not authenticated\",\n            })\n    })\n    .rate(RateLimit::per_minute_or_panic(300.0))\n    .build()?;\n",[275,1024,1025,1049,1063,1081,1085,1090,1106,1121,1125,1166,1184,1204,1218,1238,1268,1287,1306,1312,1318,1344],{"__ignoreMap":352},[356,1026,1027,1029,1031,1033,1035,1038,1040,1043,1045,1047],{"class":358,"line":359},[356,1028,363],{"class":362},[356,1030,367],{"class":366},[356,1032,371],{"class":370},[356,1034,375],{"class":374},[356,1036,1037],{"class":366},"KeyError",[356,1039,380],{"class":374},[356,1041,1042],{"class":366}," Local",[356,1044,380],{"class":374},[356,1046,383],{"class":366},[356,1048,395],{"class":374},[356,1050,1051,1053,1056,1058,1061],{"class":358,"line":398},[356,1052,363],{"class":362},[356,1054,1055],{"class":366}," actix_web",[356,1057,371],{"class":370},[356,1059,1060],{"class":366},"HttpMessage",[356,1062,594],{"class":374},[356,1064,1065,1067,1069,1071,1073,1075,1077,1079],{"class":358,"line":420},[356,1066,363],{"class":362},[356,1068,403],{"class":366},[356,1070,371],{"class":370},[356,1072,375],{"class":374},[356,1074,410],{"class":366},[356,1076,380],{"class":374},[356,1078,415],{"class":366},[356,1080,395],{"class":374},[356,1082,1083],{"class":358,"line":426},[356,1084,423],{"emptyLinePlaceholder":33},[356,1086,1087],{"class":358,"line":483},[356,1088,1089],{"class":509},"/// Inserted by your authentication middleware.\n",[356,1091,1092,1095,1098,1100,1103],{"class":358,"line":506},[356,1093,1094],{"class":374},"#[",[356,1096,1097],{"class":433},"derive",[356,1099,448],{"class":374},[356,1101,1102],{"class":366},"Clone",[356,1104,1105],{"class":374},")]\n",[356,1107,1108,1111,1114,1116,1119],{"class":358,"line":513},[356,1109,1110],{"class":429},"struct",[356,1112,1113],{"class":366}," UserId",[356,1115,448],{"class":374},[356,1117,1118],{"class":366},"u64",[356,1120,994],{"class":374},[356,1122,1123],{"class":358,"line":550},[356,1124,423],{"emptyLinePlaceholder":33},[356,1126,1127,1129,1132,1134,1136,1138,1140,1142,1144,1146,1148,1150,1152,1154,1156,1158,1160,1162,1164],{"class":358,"line":581},[356,1128,430],{"class":429},[356,1130,1131],{"class":433}," per_user ",[356,1133,437],{"class":370},[356,1135,383],{"class":366},[356,1137,371],{"class":370},[356,1139,445],{"class":444},[356,1141,448],{"class":374},[356,1143,94],{"class":366},[356,1145,371],{"class":370},[356,1147,455],{"class":444},[356,1149,448],{"class":374},[356,1151,460],{"class":366},[356,1153,371],{"class":370},[356,1155,465],{"class":444},[356,1157,448],{"class":374},[356,1159,471],{"class":470},[356,1161,474],{"class":374},[356,1163,477],{"class":370},[356,1165,480],{"class":374},[356,1167,1169,1171,1173,1175,1177,1180,1182],{"class":358,"line":1168},10,[356,1170,486],{"class":370},[356,1172,489],{"class":444},[356,1174,448],{"class":374},[356,1176,495],{"class":494},[356,1178,1179],{"class":498},"user",[356,1181,495],{"class":494},[356,1183,480],{"class":374},[356,1185,1187,1189,1191,1193,1196,1199,1201],{"class":358,"line":1186},11,[356,1188,486],{"class":370},[356,1190,1006],{"class":444},[356,1192,448],{"class":374},[356,1194,1195],{"class":370},"|",[356,1197,1198],{"class":433},"req",[356,1200,1195],{"class":370},[356,1202,1203],{"class":374}," {\n",[356,1205,1207,1210,1212,1215],{"class":358,"line":1206},12,[356,1208,1209],{"class":433},"        req",[356,1211,288],{"class":370},[356,1213,1214],{"class":444},"extensions",[356,1216,1217],{"class":374},"()\n",[356,1219,1221,1224,1227,1229,1232,1235],{"class":358,"line":1220},13,[356,1222,1223],{"class":370},"            .",[356,1225,1226],{"class":444},"get",[356,1228,371],{"class":370},[356,1230,1231],{"class":374},"\u003C",[356,1233,1234],{"class":366},"UserId",[356,1236,1237],{"class":374},">()\n",[356,1239,1241,1243,1246,1248,1250,1252,1254,1257,1259,1261,1263,1266],{"class":358,"line":1240},14,[356,1242,1223],{"class":370},[356,1244,1245],{"class":444},"map",[356,1247,448],{"class":374},[356,1249,1195],{"class":370},[356,1251,1179],{"class":433},[356,1253,1195],{"class":370},[356,1255,1256],{"class":433}," user",[356,1258,288],{"class":370},[356,1260,575],{"class":470},[356,1262,288],{"class":572},[356,1264,1265],{"class":444},"to_string",[356,1267,547],{"class":374},[356,1269,1271,1273,1276,1278,1280,1282,1285],{"class":358,"line":1270},15,[356,1272,1223],{"class":370},[356,1274,1275],{"class":444},"ok_or",[356,1277,448],{"class":374},[356,1279,1037],{"class":366},[356,1281,371],{"class":370},[356,1283,1284],{"class":366},"InvalidValue",[356,1286,1203],{"class":374},[356,1288,1290,1293,1296,1299,1302,1304],{"class":358,"line":1289},16,[356,1291,1292],{"class":433},"                reason",[356,1294,1295],{"class":370},":",[356,1297,1298],{"class":494}," \"",[356,1300,1301],{"class":498},"request is not authenticated",[356,1303,495],{"class":494},[356,1305,945],{"class":374},[356,1307,1309],{"class":358,"line":1308},17,[356,1310,1311],{"class":374},"            })\n",[356,1313,1315],{"class":358,"line":1314},18,[356,1316,1317],{"class":374},"    })\n",[356,1319,1321,1323,1325,1327,1329,1331,1333,1335,1338,1340,1342],{"class":358,"line":1320},19,[356,1322,486],{"class":370},[356,1324,555],{"class":444},[356,1326,448],{"class":374},[356,1328,410],{"class":366},[356,1330,371],{"class":370},[356,1332,564],{"class":444},[356,1334,448],{"class":374},[356,1336,1337],{"class":470},"300",[356,1339,288],{"class":572},[356,1341,575],{"class":470},[356,1343,578],{"class":374},[356,1345,1347,1349,1351,1353,1355],{"class":358,"line":1346},20,[356,1348,486],{"class":370},[356,1350,586],{"class":444},[356,1352,589],{"class":374},[356,1354,477],{"class":370},[356,1356,594],{"class":374},[1013,1358,1360],{"id":1359},"a-path-parameter","A path parameter",[247,1362,1363,1364,1367,1368,1371],{},"Path parameters are available when the limiter wraps the scope or resource that declares them.\nOn ",[275,1365,1366],{},"App::wrap"," the request has not been routed yet, and ",[275,1369,1370],{},"match_info()"," is empty.",[347,1373,1375],{"className":349,"code":1374,"language":351,"meta":352,"style":352},"use actix_trypema::{KeyError, Local, TrypemaLimiter};\nuse actix_web::{App, web};\nuse trypema::{RateLimit, WindowSize};\n\nlet per_tenant = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(60))?)\n    .namespace(\"tenant\")\n    .extractor_fn(|req| {\n        req.match_info()\n            .get(\"tenant\")\n            .map(str::to_string)\n            .ok_or(KeyError::InvalidValue {\n                reason: \"missing tenant\",\n            })\n    })\n    .rate(RateLimit::per_minute_or_panic(1_000.0))\n    .build()?;\n\nlet app = App::new().service(\n    web::scope(\"/tenants/{tenant}\")\n        .wrap(per_tenant)\n        .route(\"/orders\", web::get().to(|| async { \"orders\" })),\n);\n",[275,1376,1377,1399,1420,1438,1442,1483,1500,1516,1527,1543,1560,1576,1591,1595,1599,1623,1635,1639,1664,1690,1705,1758],{"__ignoreMap":352},[356,1378,1379,1381,1383,1385,1387,1389,1391,1393,1395,1397],{"class":358,"line":359},[356,1380,363],{"class":362},[356,1382,367],{"class":366},[356,1384,371],{"class":370},[356,1386,375],{"class":374},[356,1388,1037],{"class":366},[356,1390,380],{"class":374},[356,1392,1042],{"class":366},[356,1394,380],{"class":374},[356,1396,383],{"class":366},[356,1398,395],{"class":374},[356,1400,1401,1403,1405,1407,1409,1412,1414,1418],{"class":358,"line":398},[356,1402,363],{"class":362},[356,1404,1055],{"class":366},[356,1406,371],{"class":370},[356,1408,375],{"class":374},[356,1410,1411],{"class":366},"App",[356,1413,380],{"class":374},[356,1415,1417],{"class":1416},"s6Ffr"," web",[356,1419,395],{"class":374},[356,1421,1422,1424,1426,1428,1430,1432,1434,1436],{"class":358,"line":420},[356,1423,363],{"class":362},[356,1425,403],{"class":366},[356,1427,371],{"class":370},[356,1429,375],{"class":374},[356,1431,410],{"class":366},[356,1433,380],{"class":374},[356,1435,415],{"class":366},[356,1437,395],{"class":374},[356,1439,1440],{"class":358,"line":426},[356,1441,423],{"emptyLinePlaceholder":33},[356,1443,1444,1446,1449,1451,1453,1455,1457,1459,1461,1463,1465,1467,1469,1471,1473,1475,1477,1479,1481],{"class":358,"line":483},[356,1445,430],{"class":429},[356,1447,1448],{"class":433}," per_tenant ",[356,1450,437],{"class":370},[356,1452,383],{"class":366},[356,1454,371],{"class":370},[356,1456,445],{"class":444},[356,1458,448],{"class":374},[356,1460,94],{"class":366},[356,1462,371],{"class":370},[356,1464,455],{"class":444},[356,1466,448],{"class":374},[356,1468,460],{"class":366},[356,1470,371],{"class":370},[356,1472,465],{"class":444},[356,1474,448],{"class":374},[356,1476,471],{"class":470},[356,1478,474],{"class":374},[356,1480,477],{"class":370},[356,1482,480],{"class":374},[356,1484,1485,1487,1489,1491,1493,1496,1498],{"class":358,"line":506},[356,1486,486],{"class":370},[356,1488,489],{"class":444},[356,1490,448],{"class":374},[356,1492,495],{"class":494},[356,1494,1495],{"class":498},"tenant",[356,1497,495],{"class":494},[356,1499,480],{"class":374},[356,1501,1502,1504,1506,1508,1510,1512,1514],{"class":358,"line":513},[356,1503,486],{"class":370},[356,1505,1006],{"class":444},[356,1507,448],{"class":374},[356,1509,1195],{"class":370},[356,1511,1198],{"class":433},[356,1513,1195],{"class":370},[356,1515,1203],{"class":374},[356,1517,1518,1520,1522,1525],{"class":358,"line":550},[356,1519,1209],{"class":433},[356,1521,288],{"class":370},[356,1523,1524],{"class":444},"match_info",[356,1526,1217],{"class":374},[356,1528,1529,1531,1533,1535,1537,1539,1541],{"class":358,"line":581},[356,1530,1223],{"class":370},[356,1532,1226],{"class":444},[356,1534,448],{"class":374},[356,1536,495],{"class":494},[356,1538,1495],{"class":498},[356,1540,495],{"class":494},[356,1542,480],{"class":374},[356,1544,1545,1547,1549,1551,1554,1556,1558],{"class":358,"line":1168},[356,1546,1223],{"class":370},[356,1548,1245],{"class":444},[356,1550,448],{"class":374},[356,1552,1553],{"class":366},"str",[356,1555,371],{"class":370},[356,1557,1265],{"class":433},[356,1559,480],{"class":374},[356,1561,1562,1564,1566,1568,1570,1572,1574],{"class":358,"line":1186},[356,1563,1223],{"class":370},[356,1565,1275],{"class":444},[356,1567,448],{"class":374},[356,1569,1037],{"class":366},[356,1571,371],{"class":370},[356,1573,1284],{"class":366},[356,1575,1203],{"class":374},[356,1577,1578,1580,1582,1584,1587,1589],{"class":358,"line":1206},[356,1579,1292],{"class":433},[356,1581,1295],{"class":370},[356,1583,1298],{"class":494},[356,1585,1586],{"class":498},"missing tenant",[356,1588,495],{"class":494},[356,1590,945],{"class":374},[356,1592,1593],{"class":358,"line":1220},[356,1594,1311],{"class":374},[356,1596,1597],{"class":358,"line":1240},[356,1598,1317],{"class":374},[356,1600,1601,1603,1605,1607,1609,1611,1613,1615,1617,1619,1621],{"class":358,"line":1270},[356,1602,486],{"class":370},[356,1604,555],{"class":444},[356,1606,448],{"class":374},[356,1608,410],{"class":366},[356,1610,371],{"class":370},[356,1612,564],{"class":444},[356,1614,448],{"class":374},[356,1616,569],{"class":470},[356,1618,288],{"class":572},[356,1620,575],{"class":470},[356,1622,578],{"class":374},[356,1624,1625,1627,1629,1631,1633],{"class":358,"line":1289},[356,1626,486],{"class":370},[356,1628,586],{"class":444},[356,1630,589],{"class":374},[356,1632,477],{"class":370},[356,1634,594],{"class":374},[356,1636,1637],{"class":358,"line":1308},[356,1638,423],{"emptyLinePlaceholder":33},[356,1640,1641,1643,1646,1648,1651,1653,1655,1657,1659,1662],{"class":358,"line":1314},[356,1642,430],{"class":429},[356,1644,1645],{"class":433}," app ",[356,1647,437],{"class":370},[356,1649,1650],{"class":366}," App",[356,1652,371],{"class":370},[356,1654,455],{"class":444},[356,1656,589],{"class":374},[356,1658,288],{"class":370},[356,1660,1661],{"class":444},"service",[356,1663,921],{"class":374},[356,1665,1666,1669,1671,1674,1676,1678,1681,1683,1685,1688],{"class":358,"line":1320},[356,1667,1668],{"class":366},"    web",[356,1670,371],{"class":370},[356,1672,1673],{"class":444},"scope",[356,1675,448],{"class":374},[356,1677,495],{"class":494},[356,1679,1680],{"class":498},"/tenants/",[356,1682,375],{"class":494},[356,1684,1495],{"class":498},[356,1686,1687],{"class":494},"}\"",[356,1689,480],{"class":374},[356,1691,1692,1695,1698,1700,1703],{"class":358,"line":1346},[356,1693,1694],{"class":370},"        .",[356,1696,1697],{"class":444},"wrap",[356,1699,448],{"class":374},[356,1701,1702],{"class":433},"per_tenant",[356,1704,480],{"class":374},[356,1706,1708,1710,1713,1715,1717,1720,1722,1724,1726,1728,1730,1732,1734,1737,1739,1742,1745,1748,1750,1753,1755],{"class":358,"line":1707},21,[356,1709,1694],{"class":370},[356,1711,1712],{"class":444},"route",[356,1714,448],{"class":374},[356,1716,495],{"class":494},[356,1718,1719],{"class":498},"/orders",[356,1721,495],{"class":494},[356,1723,380],{"class":374},[356,1725,1417],{"class":366},[356,1727,371],{"class":370},[356,1729,1226],{"class":444},[356,1731,589],{"class":374},[356,1733,288],{"class":370},[356,1735,1736],{"class":444},"to",[356,1738,448],{"class":374},[356,1740,1741],{"class":370},"||",[356,1743,1744],{"class":362}," async",[356,1746,1747],{"class":374}," {",[356,1749,1298],{"class":494},[356,1751,1752],{"class":498},"orders",[356,1754,495],{"class":494},[356,1756,1757],{"class":374}," })),\n",[356,1759,1761],{"class":358,"line":1760},22,[356,1762,994],{"class":374},[1013,1764,1766],{"id":1765},"a-cookie","A cookie",[347,1768,1770],{"className":349,"code":1769,"language":351,"meta":352,"style":352},"use actix_trypema::{KeyError, Local, TrypemaLimiter};\nuse trypema::{RateLimit, WindowSize};\n\nlet per_session = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(60))?)\n    .namespace(\"session\")\n    .extractor_fn(|req| {\n        req.cookie(\"session\")\n            .map(|cookie| cookie.value().to_string())\n            .ok_or(KeyError::InvalidValue {\n                reason: \"missing session cookie\",\n            })\n    })\n    .rate(RateLimit::per_minute_or_panic(120.0))\n    .build()?;\n",[275,1771,1772,1794,1812,1816,1857,1874,1890,1909,1939,1955,1970,1974,1978,2003],{"__ignoreMap":352},[356,1773,1774,1776,1778,1780,1782,1784,1786,1788,1790,1792],{"class":358,"line":359},[356,1775,363],{"class":362},[356,1777,367],{"class":366},[356,1779,371],{"class":370},[356,1781,375],{"class":374},[356,1783,1037],{"class":366},[356,1785,380],{"class":374},[356,1787,1042],{"class":366},[356,1789,380],{"class":374},[356,1791,383],{"class":366},[356,1793,395],{"class":374},[356,1795,1796,1798,1800,1802,1804,1806,1808,1810],{"class":358,"line":398},[356,1797,363],{"class":362},[356,1799,403],{"class":366},[356,1801,371],{"class":370},[356,1803,375],{"class":374},[356,1805,410],{"class":366},[356,1807,380],{"class":374},[356,1809,415],{"class":366},[356,1811,395],{"class":374},[356,1813,1814],{"class":358,"line":420},[356,1815,423],{"emptyLinePlaceholder":33},[356,1817,1818,1820,1823,1825,1827,1829,1831,1833,1835,1837,1839,1841,1843,1845,1847,1849,1851,1853,1855],{"class":358,"line":426},[356,1819,430],{"class":429},[356,1821,1822],{"class":433}," per_session ",[356,1824,437],{"class":370},[356,1826,383],{"class":366},[356,1828,371],{"class":370},[356,1830,445],{"class":444},[356,1832,448],{"class":374},[356,1834,94],{"class":366},[356,1836,371],{"class":370},[356,1838,455],{"class":444},[356,1840,448],{"class":374},[356,1842,460],{"class":366},[356,1844,371],{"class":370},[356,1846,465],{"class":444},[356,1848,448],{"class":374},[356,1850,471],{"class":470},[356,1852,474],{"class":374},[356,1854,477],{"class":370},[356,1856,480],{"class":374},[356,1858,1859,1861,1863,1865,1867,1870,1872],{"class":358,"line":483},[356,1860,486],{"class":370},[356,1862,489],{"class":444},[356,1864,448],{"class":374},[356,1866,495],{"class":494},[356,1868,1869],{"class":498},"session",[356,1871,495],{"class":494},[356,1873,480],{"class":374},[356,1875,1876,1878,1880,1882,1884,1886,1888],{"class":358,"line":506},[356,1877,486],{"class":370},[356,1879,1006],{"class":444},[356,1881,448],{"class":374},[356,1883,1195],{"class":370},[356,1885,1198],{"class":433},[356,1887,1195],{"class":370},[356,1889,1203],{"class":374},[356,1891,1892,1894,1896,1899,1901,1903,1905,1907],{"class":358,"line":513},[356,1893,1209],{"class":433},[356,1895,288],{"class":370},[356,1897,1898],{"class":444},"cookie",[356,1900,448],{"class":374},[356,1902,495],{"class":494},[356,1904,1869],{"class":498},[356,1906,495],{"class":494},[356,1908,480],{"class":374},[356,1910,1911,1913,1915,1917,1919,1921,1923,1926,1928,1931,1933,1935,1937],{"class":358,"line":550},[356,1912,1223],{"class":370},[356,1914,1245],{"class":444},[356,1916,448],{"class":374},[356,1918,1195],{"class":370},[356,1920,1898],{"class":433},[356,1922,1195],{"class":370},[356,1924,1925],{"class":433}," cookie",[356,1927,288],{"class":370},[356,1929,1930],{"class":444},"value",[356,1932,589],{"class":374},[356,1934,288],{"class":370},[356,1936,1265],{"class":444},[356,1938,547],{"class":374},[356,1940,1941,1943,1945,1947,1949,1951,1953],{"class":358,"line":581},[356,1942,1223],{"class":370},[356,1944,1275],{"class":444},[356,1946,448],{"class":374},[356,1948,1037],{"class":366},[356,1950,371],{"class":370},[356,1952,1284],{"class":366},[356,1954,1203],{"class":374},[356,1956,1957,1959,1961,1963,1966,1968],{"class":358,"line":1168},[356,1958,1292],{"class":433},[356,1960,1295],{"class":370},[356,1962,1298],{"class":494},[356,1964,1965],{"class":498},"missing session cookie",[356,1967,495],{"class":494},[356,1969,945],{"class":374},[356,1971,1972],{"class":358,"line":1186},[356,1973,1311],{"class":374},[356,1975,1976],{"class":358,"line":1206},[356,1977,1317],{"class":374},[356,1979,1980,1982,1984,1986,1988,1990,1992,1994,1997,1999,2001],{"class":358,"line":1220},[356,1981,486],{"class":370},[356,1983,555],{"class":444},[356,1985,448],{"class":374},[356,1987,410],{"class":366},[356,1989,371],{"class":370},[356,1991,564],{"class":444},[356,1993,448],{"class":374},[356,1995,1996],{"class":470},"120",[356,1998,288],{"class":572},[356,2000,575],{"class":470},[356,2002,578],{"class":374},[356,2004,2005,2007,2009,2011,2013],{"class":358,"line":1240},[356,2006,486],{"class":370},[356,2008,586],{"class":444},[356,2010,589],{"class":374},[356,2012,477],{"class":370},[356,2014,594],{"class":374},[247,2016,2017,2020,2021,2024],{},[275,2018,2019],{},"ServiceRequest::cookie"," needs actix-web's default ",[275,2022,2023],{},"cookies"," feature.",[1013,2026,2028],{"id":2027},"validating-before-keying","Validating before keying",[247,2030,2031,2032,2036],{},"A closure can also reject values it doesn't recognize, so unknown keys never get a bucket. This\none accepts only known API keys and puts the plan into the key (see\n",[286,2033,2035],{"href":2034},"/actix-web/limits#plan-tiers","plan tiers","):",[347,2038,2040],{"className":349,"code":2039,"language":351,"meta":352,"style":352},"use actix_trypema::{KeyError, Local, TrypemaLimiter};\nuse trypema::{RateLimit, WindowSize};\n\n/// Stand-in for a real key store: API key, account, plan.\nconst API_KEYS: [(&str, &str, &str); 2] = [\n    (\"basic-key\", \"account-1\", \"basic\"),\n    (\"pro-key\", \"account-2\", \"pro\"),\n];\n\nlet limiter = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(60))?)\n    .namespace(\"apikey\")\n    .extractor_fn(|req| {\n        let presented = req\n            .headers()\n            .get(\"x-api-key\")\n            .and_then(|value| value.to_str().ok())\n            .ok_or(KeyError::InvalidValue {\n                reason: \"missing api key\",\n            })?;\n\n        let (_, account, plan) = API_KEYS\n            .iter()\n            .find(|(key, _, _)| *key == presented)\n            .ok_or(KeyError::InvalidValue {\n                reason: \"unknown api key\",\n            })?;\n\n        Ok(format!(\"{plan}:{account}\"))\n    })\n    .rate(RateLimit::per_minute_or_panic(60.0))\n    .build()?;\n",[275,2041,2042,2064,2082,2086,2091,2138,2171,2202,2207,2211,2251,2267,2283,2296,2305,2321,2353,2369,2384,2393,2397,2424,2433,2477,2494,2510,2519,2524,2557,2562,2587],{"__ignoreMap":352},[356,2043,2044,2046,2048,2050,2052,2054,2056,2058,2060,2062],{"class":358,"line":359},[356,2045,363],{"class":362},[356,2047,367],{"class":366},[356,2049,371],{"class":370},[356,2051,375],{"class":374},[356,2053,1037],{"class":366},[356,2055,380],{"class":374},[356,2057,1042],{"class":366},[356,2059,380],{"class":374},[356,2061,383],{"class":366},[356,2063,395],{"class":374},[356,2065,2066,2068,2070,2072,2074,2076,2078,2080],{"class":358,"line":398},[356,2067,363],{"class":362},[356,2069,403],{"class":366},[356,2071,371],{"class":370},[356,2073,375],{"class":374},[356,2075,410],{"class":366},[356,2077,380],{"class":374},[356,2079,415],{"class":366},[356,2081,395],{"class":374},[356,2083,2084],{"class":358,"line":420},[356,2085,423],{"emptyLinePlaceholder":33},[356,2087,2088],{"class":358,"line":426},[356,2089,2090],{"class":509},"/// Stand-in for a real key store: API key, account, plan.\n",[356,2092,2093,2096,2100,2102,2105,2108,2110,2112,2115,2117,2119,2121,2123,2126,2129,2132,2135],{"class":358,"line":483},[356,2094,2095],{"class":429},"const",[356,2097,2099],{"class":2098},"sQeA1"," API_KEYS",[356,2101,1295],{"class":370},[356,2103,2104],{"class":374}," [(",[356,2106,2107],{"class":370},"&",[356,2109,1553],{"class":366},[356,2111,380],{"class":374},[356,2113,2114],{"class":370}," &",[356,2116,1553],{"class":366},[356,2118,380],{"class":374},[356,2120,2114],{"class":370},[356,2122,1553],{"class":366},[356,2124,2125],{"class":374},");",[356,2127,2128],{"class":470}," 2",[356,2130,2131],{"class":374},"]",[356,2133,2134],{"class":370}," =",[356,2136,2137],{"class":374}," [\n",[356,2139,2140,2143,2145,2148,2150,2152,2154,2157,2159,2161,2163,2166,2168],{"class":358,"line":506},[356,2141,2142],{"class":374},"    (",[356,2144,495],{"class":494},[356,2146,2147],{"class":498},"basic-key",[356,2149,495],{"class":494},[356,2151,380],{"class":374},[356,2153,1298],{"class":494},[356,2155,2156],{"class":498},"account-1",[356,2158,495],{"class":494},[356,2160,380],{"class":374},[356,2162,1298],{"class":494},[356,2164,2165],{"class":498},"basic",[356,2167,495],{"class":494},[356,2169,2170],{"class":374},"),\n",[356,2172,2173,2175,2177,2180,2182,2184,2186,2189,2191,2193,2195,2198,2200],{"class":358,"line":513},[356,2174,2142],{"class":374},[356,2176,495],{"class":494},[356,2178,2179],{"class":498},"pro-key",[356,2181,495],{"class":494},[356,2183,380],{"class":374},[356,2185,1298],{"class":494},[356,2187,2188],{"class":498},"account-2",[356,2190,495],{"class":494},[356,2192,380],{"class":374},[356,2194,1298],{"class":494},[356,2196,2197],{"class":498},"pro",[356,2199,495],{"class":494},[356,2201,2170],{"class":374},[356,2203,2204],{"class":358,"line":550},[356,2205,2206],{"class":374},"];\n",[356,2208,2209],{"class":358,"line":581},[356,2210,423],{"emptyLinePlaceholder":33},[356,2212,2213,2215,2217,2219,2221,2223,2225,2227,2229,2231,2233,2235,2237,2239,2241,2243,2245,2247,2249],{"class":358,"line":1168},[356,2214,430],{"class":429},[356,2216,434],{"class":433},[356,2218,437],{"class":370},[356,2220,383],{"class":366},[356,2222,371],{"class":370},[356,2224,445],{"class":444},[356,2226,448],{"class":374},[356,2228,94],{"class":366},[356,2230,371],{"class":370},[356,2232,455],{"class":444},[356,2234,448],{"class":374},[356,2236,460],{"class":366},[356,2238,371],{"class":370},[356,2240,465],{"class":444},[356,2242,448],{"class":374},[356,2244,471],{"class":470},[356,2246,474],{"class":374},[356,2248,477],{"class":370},[356,2250,480],{"class":374},[356,2252,2253,2255,2257,2259,2261,2263,2265],{"class":358,"line":1186},[356,2254,486],{"class":370},[356,2256,489],{"class":444},[356,2258,448],{"class":374},[356,2260,495],{"class":494},[356,2262,499],{"class":498},[356,2264,495],{"class":494},[356,2266,480],{"class":374},[356,2268,2269,2271,2273,2275,2277,2279,2281],{"class":358,"line":1206},[356,2270,486],{"class":370},[356,2272,1006],{"class":444},[356,2274,448],{"class":374},[356,2276,1195],{"class":370},[356,2278,1198],{"class":433},[356,2280,1195],{"class":370},[356,2282,1203],{"class":374},[356,2284,2285,2288,2291,2293],{"class":358,"line":1220},[356,2286,2287],{"class":429},"        let",[356,2289,2290],{"class":433}," presented ",[356,2292,437],{"class":370},[356,2294,2295],{"class":433}," req\n",[356,2297,2298,2300,2303],{"class":358,"line":1240},[356,2299,1223],{"class":370},[356,2301,2302],{"class":444},"headers",[356,2304,1217],{"class":374},[356,2306,2307,2309,2311,2313,2315,2317,2319],{"class":358,"line":1270},[356,2308,1223],{"class":370},[356,2310,1226],{"class":444},[356,2312,448],{"class":374},[356,2314,495],{"class":494},[356,2316,533],{"class":498},[356,2318,495],{"class":494},[356,2320,480],{"class":374},[356,2322,2323,2325,2328,2330,2332,2334,2336,2339,2341,2344,2346,2348,2351],{"class":358,"line":1289},[356,2324,1223],{"class":370},[356,2326,2327],{"class":444},"and_then",[356,2329,448],{"class":374},[356,2331,1195],{"class":370},[356,2333,1930],{"class":433},[356,2335,1195],{"class":370},[356,2337,2338],{"class":433}," value",[356,2340,288],{"class":370},[356,2342,2343],{"class":444},"to_str",[356,2345,589],{"class":374},[356,2347,288],{"class":370},[356,2349,2350],{"class":444},"ok",[356,2352,547],{"class":374},[356,2354,2355,2357,2359,2361,2363,2365,2367],{"class":358,"line":1308},[356,2356,1223],{"class":370},[356,2358,1275],{"class":444},[356,2360,448],{"class":374},[356,2362,1037],{"class":366},[356,2364,371],{"class":370},[356,2366,1284],{"class":366},[356,2368,1203],{"class":374},[356,2370,2371,2373,2375,2377,2380,2382],{"class":358,"line":1314},[356,2372,1292],{"class":433},[356,2374,1295],{"class":370},[356,2376,1298],{"class":494},[356,2378,2379],{"class":498},"missing api key",[356,2381,495],{"class":494},[356,2383,945],{"class":374},[356,2385,2386,2389,2391],{"class":358,"line":1320},[356,2387,2388],{"class":374},"            })",[356,2390,477],{"class":370},[356,2392,594],{"class":374},[356,2394,2395],{"class":358,"line":1346},[356,2396,423],{"emptyLinePlaceholder":33},[356,2398,2399,2401,2404,2407,2409,2412,2414,2417,2419,2421],{"class":358,"line":1707},[356,2400,2287],{"class":429},[356,2402,2403],{"class":374}," (",[356,2405,2406],{"class":433},"_",[356,2408,380],{"class":374},[356,2410,2411],{"class":433}," account",[356,2413,380],{"class":374},[356,2415,2416],{"class":433}," plan",[356,2418,538],{"class":374},[356,2420,2134],{"class":370},[356,2422,2423],{"class":2098}," API_KEYS\n",[356,2425,2426,2428,2431],{"class":358,"line":1760},[356,2427,1223],{"class":370},[356,2429,2430],{"class":444},"iter",[356,2432,1217],{"class":374},[356,2434,2436,2438,2441,2443,2445,2447,2450,2452,2455,2457,2459,2461,2463,2466,2469,2472,2475],{"class":358,"line":2435},23,[356,2437,1223],{"class":370},[356,2439,2440],{"class":444},"find",[356,2442,448],{"class":374},[356,2444,1195],{"class":370},[356,2446,448],{"class":374},[356,2448,2449],{"class":433},"key",[356,2451,380],{"class":374},[356,2453,2454],{"class":433}," _",[356,2456,380],{"class":374},[356,2458,2454],{"class":433},[356,2460,538],{"class":374},[356,2462,1195],{"class":370},[356,2464,2465],{"class":370}," *",[356,2467,2468],{"class":433},"key ",[356,2470,2471],{"class":370},"==",[356,2473,2474],{"class":433}," presented",[356,2476,480],{"class":374},[356,2478,2480,2482,2484,2486,2488,2490,2492],{"class":358,"line":2479},24,[356,2481,1223],{"class":370},[356,2483,1275],{"class":444},[356,2485,448],{"class":374},[356,2487,1037],{"class":366},[356,2489,371],{"class":370},[356,2491,1284],{"class":366},[356,2493,1203],{"class":374},[356,2495,2497,2499,2501,2503,2506,2508],{"class":358,"line":2496},25,[356,2498,1292],{"class":433},[356,2500,1295],{"class":370},[356,2502,1298],{"class":494},[356,2504,2505],{"class":498},"unknown api key",[356,2507,495],{"class":494},[356,2509,945],{"class":374},[356,2511,2513,2515,2517],{"class":358,"line":2512},26,[356,2514,2388],{"class":374},[356,2516,477],{"class":370},[356,2518,594],{"class":374},[356,2520,2522],{"class":358,"line":2521},27,[356,2523,423],{"emptyLinePlaceholder":33},[356,2525,2527,2530,2532,2535,2537,2540,2543,2546,2548,2550,2553,2555],{"class":358,"line":2526},28,[356,2528,2529],{"class":366},"        Ok",[356,2531,448],{"class":374},[356,2533,2534],{"class":444},"format!",[356,2536,448],{"class":374},[356,2538,2539],{"class":494},"\"{",[356,2541,2542],{"class":498},"plan",[356,2544,2545],{"class":494},"}",[356,2547,1295],{"class":498},[356,2549,375],{"class":494},[356,2551,2552],{"class":498},"account",[356,2554,1687],{"class":494},[356,2556,578],{"class":374},[356,2558,2560],{"class":358,"line":2559},29,[356,2561,1317],{"class":374},[356,2563,2565,2567,2569,2571,2573,2575,2577,2579,2581,2583,2585],{"class":358,"line":2564},30,[356,2566,486],{"class":370},[356,2568,555],{"class":444},[356,2570,448],{"class":374},[356,2572,410],{"class":366},[356,2574,371],{"class":370},[356,2576,564],{"class":444},[356,2578,448],{"class":374},[356,2580,471],{"class":470},[356,2582,288],{"class":572},[356,2584,575],{"class":470},[356,2586,578],{"class":374},[356,2588,2590,2592,2594,2596,2598],{"class":358,"line":2589},31,[356,2591,486],{"class":370},[356,2593,586],{"class":444},[356,2595,589],{"class":374},[356,2597,477],{"class":370},[356,2599,594],{"class":374},[340,2601,2603,2604],{"id":2602},"implementing-keyextractor","Implementing ",[275,2605,2606],{},"KeyExtractor",[247,2608,2609,2610,2613,2614,2616,2617,2620],{},"A closure allocates a ",[275,2611,2612],{},"String"," on every request. For hot paths, or an extractor you reuse\nacross limiters, implement ",[275,2615,2606],{}," instead. It either borrows the key from the request or\nwrites it into the provided ",[275,2618,2619],{},"KeyBuf",", a 256-byte buffer that allocates nothing for short keys:",[347,2622,2624],{"className":349,"code":2623,"language":351,"meta":352,"style":352},"pub trait KeyExtractor: Send + Sync + 'static {\n    fn extract\u003C'a>(\n        &self,\n        req: &'a ServiceRequest,\n        buf: &'a mut KeyBuf,\n    ) -> Result\u003C&'a str, KeyError>;\n}\n",[275,2625,2626,2658,2673,2684,2702,2724,2754],{"__ignoreMap":352},[356,2627,2628,2631,2634,2637,2639,2642,2645,2648,2650,2653,2656],{"class":358,"line":359},[356,2629,2630],{"class":362},"pub",[356,2632,2633],{"class":429}," trait",[356,2635,2636],{"class":366}," KeyExtractor",[356,2638,1295],{"class":370},[356,2640,2641],{"class":366}," Send",[356,2643,2644],{"class":370}," +",[356,2646,2647],{"class":366}," Sync",[356,2649,2644],{"class":370},[356,2651,2652],{"class":374}," '",[356,2654,2655],{"class":366},"static",[356,2657,1203],{"class":374},[356,2659,2660,2663,2665,2668,2670],{"class":358,"line":398},[356,2661,2662],{"class":362},"    fn",[356,2664,388],{"class":444},[356,2666,2667],{"class":374},"\u003C'",[356,2669,286],{"class":366},[356,2671,2672],{"class":374},">(\n",[356,2674,2675,2678,2682],{"class":358,"line":420},[356,2676,2677],{"class":370},"        &",[356,2679,2681],{"class":2680},"sSsL9","self",[356,2683,945],{"class":374},[356,2685,2686,2688,2690,2692,2695,2697,2700],{"class":358,"line":426},[356,2687,1209],{"class":433},[356,2689,1295],{"class":370},[356,2691,2114],{"class":370},[356,2693,2694],{"class":374},"'",[356,2696,286],{"class":366},[356,2698,2699],{"class":366}," ServiceRequest",[356,2701,945],{"class":374},[356,2703,2704,2707,2709,2711,2713,2715,2719,2722],{"class":358,"line":483},[356,2705,2706],{"class":433},"        buf",[356,2708,1295],{"class":370},[356,2710,2114],{"class":370},[356,2712,2694],{"class":374},[356,2714,286],{"class":366},[356,2716,2718],{"class":2717},"sTNss"," mut",[356,2720,2721],{"class":366}," KeyBuf",[356,2723,945],{"class":374},[356,2725,2726,2729,2732,2735,2737,2739,2741,2743,2746,2748,2751],{"class":358,"line":506},[356,2727,2728],{"class":374},"    )",[356,2730,2731],{"class":370}," ->",[356,2733,2734],{"class":366}," Result",[356,2736,1231],{"class":374},[356,2738,2107],{"class":370},[356,2740,2694],{"class":374},[356,2742,286],{"class":366},[356,2744,2745],{"class":366}," str",[356,2747,380],{"class":374},[356,2749,2750],{"class":366}," KeyError",[356,2752,2753],{"class":374},">;\n",[356,2755,2756],{"class":358,"line":513},[356,2757,2758],{"class":374},"}\n",[247,2760,2761],{},"Borrowing a value straight from the request:",[347,2763,2765],{"className":349,"code":2764,"language":351,"meta":352,"style":352},"use actix_trypema::{KeyError, extract::{KeyBuf, KeyExtractor}};\nuse actix_web::dev::ServiceRequest;\n\n/// Keys by the `x-tenant-id` header, accepting only short alphanumeric ids.\nstruct TenantId;\n\nimpl KeyExtractor for TenantId {\n    fn extract\u003C'a>(\n        &self,\n        req: &'a ServiceRequest,\n        _buf: &'a mut KeyBuf,\n    ) -> Result\u003C&'a str, KeyError> {\n        let tenant = req\n            .headers()\n            .get(\"x-tenant-id\")\n            .and_then(|value| value.to_str().ok())\n            .ok_or(KeyError::InvalidValue {\n                reason: \"missing tenant id\",\n            })?;\n\n        let is_valid = !tenant.is_empty()\n            && tenant.len() \u003C= 32\n            && tenant.bytes().all(|byte| byte.is_ascii_alphanumeric());\n\n        if !is_valid {\n            return Err(KeyError::InvalidValue {\n                reason: \"invalid tenant id\",\n            });\n        }\n\n        Ok(tenant)\n    }\n}\n",[275,2766,2767,2796,2814,2818,2823,2832,2836,2851,2863,2871,2887,2906,2933,2944,2952,2968,2996,3012,3027,3035,3039,3060,3081,3118,3122,3135,3153,3168,3173,3178,3182,3192,3198],{"__ignoreMap":352},[356,2768,2769,2771,2773,2775,2777,2779,2781,2783,2785,2787,2789,2791,2793],{"class":358,"line":359},[356,2770,363],{"class":362},[356,2772,367],{"class":366},[356,2774,371],{"class":370},[356,2776,375],{"class":374},[356,2778,1037],{"class":366},[356,2780,380],{"class":374},[356,2782,388],{"class":366},[356,2784,371],{"class":370},[356,2786,375],{"class":374},[356,2788,2619],{"class":366},[356,2790,380],{"class":374},[356,2792,2636],{"class":366},[356,2794,2795],{"class":374},"}};\n",[356,2797,2798,2800,2802,2804,2807,2809,2812],{"class":358,"line":398},[356,2799,363],{"class":362},[356,2801,1055],{"class":366},[356,2803,371],{"class":370},[356,2805,2806],{"class":366},"dev",[356,2808,371],{"class":370},[356,2810,2811],{"class":366},"ServiceRequest",[356,2813,594],{"class":374},[356,2815,2816],{"class":358,"line":420},[356,2817,423],{"emptyLinePlaceholder":33},[356,2819,2820],{"class":358,"line":426},[356,2821,2822],{"class":509},"/// Keys by the `x-tenant-id` header, accepting only short alphanumeric ids.\n",[356,2824,2825,2827,2830],{"class":358,"line":483},[356,2826,1110],{"class":429},[356,2828,2829],{"class":366}," TenantId",[356,2831,594],{"class":374},[356,2833,2834],{"class":358,"line":506},[356,2835,423],{"emptyLinePlaceholder":33},[356,2837,2838,2841,2843,2847,2849],{"class":358,"line":513},[356,2839,2840],{"class":362},"impl",[356,2842,2636],{"class":366},[356,2844,2846],{"class":2845},"sRxSC"," for",[356,2848,2829],{"class":366},[356,2850,1203],{"class":374},[356,2852,2853,2855,2857,2859,2861],{"class":358,"line":550},[356,2854,2662],{"class":362},[356,2856,388],{"class":444},[356,2858,2667],{"class":374},[356,2860,286],{"class":366},[356,2862,2672],{"class":374},[356,2864,2865,2867,2869],{"class":358,"line":581},[356,2866,2677],{"class":370},[356,2868,2681],{"class":2680},[356,2870,945],{"class":374},[356,2872,2873,2875,2877,2879,2881,2883,2885],{"class":358,"line":1168},[356,2874,1209],{"class":433},[356,2876,1295],{"class":370},[356,2878,2114],{"class":370},[356,2880,2694],{"class":374},[356,2882,286],{"class":366},[356,2884,2699],{"class":366},[356,2886,945],{"class":374},[356,2888,2889,2892,2894,2896,2898,2900,2902,2904],{"class":358,"line":1186},[356,2890,2891],{"class":433},"        _buf",[356,2893,1295],{"class":370},[356,2895,2114],{"class":370},[356,2897,2694],{"class":374},[356,2899,286],{"class":366},[356,2901,2718],{"class":2717},[356,2903,2721],{"class":366},[356,2905,945],{"class":374},[356,2907,2908,2910,2912,2914,2916,2918,2920,2922,2924,2926,2928,2931],{"class":358,"line":1206},[356,2909,2728],{"class":374},[356,2911,2731],{"class":370},[356,2913,2734],{"class":366},[356,2915,1231],{"class":374},[356,2917,2107],{"class":370},[356,2919,2694],{"class":374},[356,2921,286],{"class":366},[356,2923,2745],{"class":366},[356,2925,380],{"class":374},[356,2927,2750],{"class":366},[356,2929,2930],{"class":374},">",[356,2932,1203],{"class":374},[356,2934,2935,2937,2940,2942],{"class":358,"line":1220},[356,2936,2287],{"class":429},[356,2938,2939],{"class":433}," tenant ",[356,2941,437],{"class":370},[356,2943,2295],{"class":433},[356,2945,2946,2948,2950],{"class":358,"line":1240},[356,2947,1223],{"class":370},[356,2949,2302],{"class":444},[356,2951,1217],{"class":374},[356,2953,2954,2956,2958,2960,2962,2964,2966],{"class":358,"line":1270},[356,2955,1223],{"class":370},[356,2957,1226],{"class":444},[356,2959,448],{"class":374},[356,2961,495],{"class":494},[356,2963,876],{"class":498},[356,2965,495],{"class":494},[356,2967,480],{"class":374},[356,2969,2970,2972,2974,2976,2978,2980,2982,2984,2986,2988,2990,2992,2994],{"class":358,"line":1289},[356,2971,1223],{"class":370},[356,2973,2327],{"class":444},[356,2975,448],{"class":374},[356,2977,1195],{"class":370},[356,2979,1930],{"class":433},[356,2981,1195],{"class":370},[356,2983,2338],{"class":433},[356,2985,288],{"class":370},[356,2987,2343],{"class":444},[356,2989,589],{"class":374},[356,2991,288],{"class":370},[356,2993,2350],{"class":444},[356,2995,547],{"class":374},[356,2997,2998,3000,3002,3004,3006,3008,3010],{"class":358,"line":1308},[356,2999,1223],{"class":370},[356,3001,1275],{"class":444},[356,3003,448],{"class":374},[356,3005,1037],{"class":366},[356,3007,371],{"class":370},[356,3009,1284],{"class":366},[356,3011,1203],{"class":374},[356,3013,3014,3016,3018,3020,3023,3025],{"class":358,"line":1314},[356,3015,1292],{"class":433},[356,3017,1295],{"class":370},[356,3019,1298],{"class":494},[356,3021,3022],{"class":498},"missing tenant id",[356,3024,495],{"class":494},[356,3026,945],{"class":374},[356,3028,3029,3031,3033],{"class":358,"line":1320},[356,3030,2388],{"class":374},[356,3032,477],{"class":370},[356,3034,594],{"class":374},[356,3036,3037],{"class":358,"line":1346},[356,3038,423],{"emptyLinePlaceholder":33},[356,3040,3041,3043,3046,3048,3051,3053,3055,3058],{"class":358,"line":1707},[356,3042,2287],{"class":429},[356,3044,3045],{"class":433}," is_valid ",[356,3047,437],{"class":370},[356,3049,3050],{"class":370}," !",[356,3052,1495],{"class":433},[356,3054,288],{"class":370},[356,3056,3057],{"class":444},"is_empty",[356,3059,1217],{"class":374},[356,3061,3062,3065,3068,3070,3073,3075,3078],{"class":358,"line":1760},[356,3063,3064],{"class":370},"            &&",[356,3066,3067],{"class":433}," tenant",[356,3069,288],{"class":370},[356,3071,3072],{"class":444},"len",[356,3074,589],{"class":374},[356,3076,3077],{"class":370}," \u003C=",[356,3079,3080],{"class":470}," 32\n",[356,3082,3083,3085,3087,3089,3092,3094,3096,3099,3101,3103,3106,3108,3111,3113,3116],{"class":358,"line":2435},[356,3084,3064],{"class":370},[356,3086,3067],{"class":433},[356,3088,288],{"class":370},[356,3090,3091],{"class":444},"bytes",[356,3093,589],{"class":374},[356,3095,288],{"class":370},[356,3097,3098],{"class":444},"all",[356,3100,448],{"class":374},[356,3102,1195],{"class":370},[356,3104,3105],{"class":433},"byte",[356,3107,1195],{"class":370},[356,3109,3110],{"class":433}," byte",[356,3112,288],{"class":370},[356,3114,3115],{"class":444},"is_ascii_alphanumeric",[356,3117,894],{"class":374},[356,3119,3120],{"class":358,"line":2479},[356,3121,423],{"emptyLinePlaceholder":33},[356,3123,3124,3127,3129,3132],{"class":358,"line":2496},[356,3125,3126],{"class":2845},"        if",[356,3128,3050],{"class":370},[356,3130,3131],{"class":433},"is_valid ",[356,3133,3134],{"class":374},"{\n",[356,3136,3137,3140,3143,3145,3147,3149,3151],{"class":358,"line":2512},[356,3138,3139],{"class":2845},"            return",[356,3141,3142],{"class":366}," Err",[356,3144,448],{"class":374},[356,3146,1037],{"class":366},[356,3148,371],{"class":370},[356,3150,1284],{"class":366},[356,3152,1203],{"class":374},[356,3154,3155,3157,3159,3161,3164,3166],{"class":358,"line":2521},[356,3156,1292],{"class":433},[356,3158,1295],{"class":370},[356,3160,1298],{"class":494},[356,3162,3163],{"class":498},"invalid tenant id",[356,3165,495],{"class":494},[356,3167,945],{"class":374},[356,3169,3170],{"class":358,"line":2526},[356,3171,3172],{"class":374},"            });\n",[356,3174,3175],{"class":358,"line":2559},[356,3176,3177],{"class":374},"        }\n",[356,3179,3180],{"class":358,"line":2564},[356,3181,423],{"emptyLinePlaceholder":33},[356,3183,3184,3186,3188,3190],{"class":358,"line":2589},[356,3185,2529],{"class":366},[356,3187,448],{"class":374},[356,3189,1495],{"class":433},[356,3191,480],{"class":374},[356,3193,3195],{"class":358,"line":3194},32,[356,3196,3197],{"class":374},"    }\n",[356,3199,3201],{"class":358,"line":3200},33,[356,3202,2758],{"class":374},[247,3204,3205,3206,3208,3209,3212,3213,3216,3217,1295],{},"Building the key in the buffer, here from a user stored by authentication middleware.\n",[275,3207,2619],{}," implements ",[275,3210,3211],{},"std::fmt::Write",", so ",[275,3214,3215],{},"write!"," works without an intermediate ",[275,3218,2612],{},[347,3220,3222],{"className":349,"code":3221,"language":351,"meta":352,"style":352},"use std::fmt::Write as _;\n\nuse actix_trypema::{KeyError, extract::{KeyBuf, KeyExtractor}};\nuse actix_web::{HttpMessage, dev::ServiceRequest};\n\n#[derive(Clone)]\nstruct User {\n    org: u64,\n    id: u64,\n}\n\n/// Keys by `{org}.{user}` for authenticated users.\nstruct OrgUser;\n\nimpl KeyExtractor for OrgUser {\n    fn extract\u003C'a>(\n        &self,\n        req: &'a ServiceRequest,\n        buf: &'a mut KeyBuf,\n    ) -> Result\u003C&'a str, KeyError> {\n        let extensions = req.extensions();\n        let user = extensions.get::\u003CUser>().ok_or(KeyError::InvalidValue {\n            reason: \"request is not authenticated\",\n        })?;\n\n        let _ = write!(buf, \"{}.{}\", user.org, user.id);\n        Ok(buf.as_str())\n    }\n}\n",[275,3223,3224,3248,3252,3280,3303,3307,3319,3328,3340,3351,3355,3359,3364,3373,3377,3389,3401,3409,3425,3443,3469,3488,3528,3543,3552,3556,3603,3618,3622],{"__ignoreMap":352},[356,3225,3226,3228,3231,3233,3236,3238,3241,3244,3246],{"class":358,"line":359},[356,3227,363],{"class":362},[356,3229,3230],{"class":366}," std",[356,3232,371],{"class":370},[356,3234,3235],{"class":366},"fmt",[356,3237,371],{"class":370},[356,3239,3240],{"class":366},"Write",[356,3242,3243],{"class":362}," as",[356,3245,2454],{"class":1416},[356,3247,594],{"class":374},[356,3249,3250],{"class":358,"line":398},[356,3251,423],{"emptyLinePlaceholder":33},[356,3253,3254,3256,3258,3260,3262,3264,3266,3268,3270,3272,3274,3276,3278],{"class":358,"line":420},[356,3255,363],{"class":362},[356,3257,367],{"class":366},[356,3259,371],{"class":370},[356,3261,375],{"class":374},[356,3263,1037],{"class":366},[356,3265,380],{"class":374},[356,3267,388],{"class":366},[356,3269,371],{"class":370},[356,3271,375],{"class":374},[356,3273,2619],{"class":366},[356,3275,380],{"class":374},[356,3277,2636],{"class":366},[356,3279,2795],{"class":374},[356,3281,3282,3284,3286,3288,3290,3292,3294,3297,3299,3301],{"class":358,"line":426},[356,3283,363],{"class":362},[356,3285,1055],{"class":366},[356,3287,371],{"class":370},[356,3289,375],{"class":374},[356,3291,1060],{"class":366},[356,3293,380],{"class":374},[356,3295,3296],{"class":366}," dev",[356,3298,371],{"class":370},[356,3300,2811],{"class":366},[356,3302,395],{"class":374},[356,3304,3305],{"class":358,"line":483},[356,3306,423],{"emptyLinePlaceholder":33},[356,3308,3309,3311,3313,3315,3317],{"class":358,"line":506},[356,3310,1094],{"class":374},[356,3312,1097],{"class":433},[356,3314,448],{"class":374},[356,3316,1102],{"class":366},[356,3318,1105],{"class":374},[356,3320,3321,3323,3326],{"class":358,"line":513},[356,3322,1110],{"class":429},[356,3324,3325],{"class":366}," User",[356,3327,1203],{"class":374},[356,3329,3330,3333,3335,3338],{"class":358,"line":550},[356,3331,3332],{"class":433},"    org",[356,3334,1295],{"class":370},[356,3336,3337],{"class":366}," u64",[356,3339,945],{"class":374},[356,3341,3342,3345,3347,3349],{"class":358,"line":581},[356,3343,3344],{"class":433},"    id",[356,3346,1295],{"class":370},[356,3348,3337],{"class":366},[356,3350,945],{"class":374},[356,3352,3353],{"class":358,"line":1168},[356,3354,2758],{"class":374},[356,3356,3357],{"class":358,"line":1186},[356,3358,423],{"emptyLinePlaceholder":33},[356,3360,3361],{"class":358,"line":1206},[356,3362,3363],{"class":509},"/// Keys by `{org}.{user}` for authenticated users.\n",[356,3365,3366,3368,3371],{"class":358,"line":1220},[356,3367,1110],{"class":429},[356,3369,3370],{"class":366}," OrgUser",[356,3372,594],{"class":374},[356,3374,3375],{"class":358,"line":1240},[356,3376,423],{"emptyLinePlaceholder":33},[356,3378,3379,3381,3383,3385,3387],{"class":358,"line":1270},[356,3380,2840],{"class":362},[356,3382,2636],{"class":366},[356,3384,2846],{"class":2845},[356,3386,3370],{"class":366},[356,3388,1203],{"class":374},[356,3390,3391,3393,3395,3397,3399],{"class":358,"line":1289},[356,3392,2662],{"class":362},[356,3394,388],{"class":444},[356,3396,2667],{"class":374},[356,3398,286],{"class":366},[356,3400,2672],{"class":374},[356,3402,3403,3405,3407],{"class":358,"line":1308},[356,3404,2677],{"class":370},[356,3406,2681],{"class":2680},[356,3408,945],{"class":374},[356,3410,3411,3413,3415,3417,3419,3421,3423],{"class":358,"line":1314},[356,3412,1209],{"class":433},[356,3414,1295],{"class":370},[356,3416,2114],{"class":370},[356,3418,2694],{"class":374},[356,3420,286],{"class":366},[356,3422,2699],{"class":366},[356,3424,945],{"class":374},[356,3426,3427,3429,3431,3433,3435,3437,3439,3441],{"class":358,"line":1320},[356,3428,2706],{"class":433},[356,3430,1295],{"class":370},[356,3432,2114],{"class":370},[356,3434,2694],{"class":374},[356,3436,286],{"class":366},[356,3438,2718],{"class":2717},[356,3440,2721],{"class":366},[356,3442,945],{"class":374},[356,3444,3445,3447,3449,3451,3453,3455,3457,3459,3461,3463,3465,3467],{"class":358,"line":1346},[356,3446,2728],{"class":374},[356,3448,2731],{"class":370},[356,3450,2734],{"class":366},[356,3452,1231],{"class":374},[356,3454,2107],{"class":370},[356,3456,2694],{"class":374},[356,3458,286],{"class":366},[356,3460,2745],{"class":366},[356,3462,380],{"class":374},[356,3464,2750],{"class":366},[356,3466,2930],{"class":374},[356,3468,1203],{"class":374},[356,3470,3471,3473,3476,3478,3481,3483,3485],{"class":358,"line":1707},[356,3472,2287],{"class":429},[356,3474,3475],{"class":433}," extensions ",[356,3477,437],{"class":370},[356,3479,3480],{"class":433}," req",[356,3482,288],{"class":370},[356,3484,1214],{"class":444},[356,3486,3487],{"class":374},"();\n",[356,3489,3490,3492,3495,3497,3500,3502,3504,3506,3508,3511,3514,3516,3518,3520,3522,3524,3526],{"class":358,"line":1760},[356,3491,2287],{"class":429},[356,3493,3494],{"class":433}," user ",[356,3496,437],{"class":370},[356,3498,3499],{"class":433}," extensions",[356,3501,288],{"class":370},[356,3503,1226],{"class":444},[356,3505,371],{"class":370},[356,3507,1231],{"class":374},[356,3509,3510],{"class":366},"User",[356,3512,3513],{"class":374},">()",[356,3515,288],{"class":370},[356,3517,1275],{"class":444},[356,3519,448],{"class":374},[356,3521,1037],{"class":366},[356,3523,371],{"class":370},[356,3525,1284],{"class":366},[356,3527,1203],{"class":374},[356,3529,3530,3533,3535,3537,3539,3541],{"class":358,"line":2435},[356,3531,3532],{"class":433},"            reason",[356,3534,1295],{"class":370},[356,3536,1298],{"class":494},[356,3538,1301],{"class":498},[356,3540,495],{"class":494},[356,3542,945],{"class":374},[356,3544,3545,3548,3550],{"class":358,"line":2479},[356,3546,3547],{"class":374},"        })",[356,3549,477],{"class":370},[356,3551,594],{"class":374},[356,3553,3554],{"class":358,"line":2496},[356,3555,423],{"emptyLinePlaceholder":33},[356,3557,3558,3560,3563,3565,3568,3570,3573,3575,3578,3580,3583,3585,3587,3589,3592,3594,3596,3598,3601],{"class":358,"line":2512},[356,3559,2287],{"class":429},[356,3561,3562],{"class":433}," _ ",[356,3564,437],{"class":370},[356,3566,3567],{"class":444}," write!",[356,3569,448],{"class":374},[356,3571,3572],{"class":433},"buf",[356,3574,380],{"class":374},[356,3576,3577],{"class":494}," \"{}",[356,3579,288],{"class":498},[356,3581,3582],{"class":494},"{}\"",[356,3584,380],{"class":374},[356,3586,1256],{"class":433},[356,3588,288],{"class":370},[356,3590,3591],{"class":433},"org",[356,3593,380],{"class":374},[356,3595,1256],{"class":433},[356,3597,288],{"class":370},[356,3599,3600],{"class":433},"id",[356,3602,994],{"class":374},[356,3604,3605,3607,3609,3611,3613,3616],{"class":358,"line":2521},[356,3606,2529],{"class":366},[356,3608,448],{"class":374},[356,3610,3572],{"class":433},[356,3612,288],{"class":370},[356,3614,3615],{"class":444},"as_str",[356,3617,547],{"class":374},[356,3619,3620],{"class":358,"line":2526},[356,3621,3197],{"class":374},[356,3623,3624],{"class":358,"line":2559},[356,3625,2758],{"class":374},[247,3627,3628,3629,1295],{},"Use your extractor like any built-in one, alone or inside ",[275,3630,315],{},[347,3632,3634],{"className":349,"code":3633,"language":351,"meta":352,"style":352},"use actix_trypema::{Local, TrypemaLimiter, KeyError, extract::{Composite, KeyBuf, KeyExtractor, PeerIp}};\nuse actix_web::dev::ServiceRequest;\nuse trypema::{RateLimit, WindowSize};\n\nstruct TenantId;\n\nimpl KeyExtractor for TenantId {\n    fn extract\u003C'a>(&self, req: &'a ServiceRequest, _buf: &'a mut KeyBuf) -> Result\u003C&'a str, KeyError> {\n        req.headers()\n            .get(\"x-tenant-id\")\n            .and_then(|value| value.to_str().ok())\n            .ok_or(KeyError::InvalidValue { reason: \"missing tenant id\" })\n    }\n}\n\nlet limiter = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(60))?)\n    .namespace(\"tenant-ip\")\n    .extractor(Composite::new(TenantId, PeerIp::default()))\n    .rate(RateLimit::per_minute_or_panic(100.0))\n    .build()?;\n",[275,3635,3636,3680,3696,3714,3718,3726,3730,3742,3814,3824,3840,3868,3898,3902,3906,3910,3950,3967,3997,4022],{"__ignoreMap":352},[356,3637,3638,3640,3642,3644,3646,3648,3650,3652,3654,3656,3658,3660,3662,3664,3666,3668,3670,3672,3674,3676,3678],{"class":358,"line":359},[356,3639,363],{"class":362},[356,3641,367],{"class":366},[356,3643,371],{"class":370},[356,3645,375],{"class":374},[356,3647,94],{"class":366},[356,3649,380],{"class":374},[356,3651,383],{"class":366},[356,3653,380],{"class":374},[356,3655,2750],{"class":366},[356,3657,380],{"class":374},[356,3659,388],{"class":366},[356,3661,371],{"class":370},[356,3663,375],{"class":374},[356,3665,315],{"class":366},[356,3667,380],{"class":374},[356,3669,2721],{"class":366},[356,3671,380],{"class":374},[356,3673,2636],{"class":366},[356,3675,380],{"class":374},[356,3677,839],{"class":366},[356,3679,2795],{"class":374},[356,3681,3682,3684,3686,3688,3690,3692,3694],{"class":358,"line":398},[356,3683,363],{"class":362},[356,3685,1055],{"class":366},[356,3687,371],{"class":370},[356,3689,2806],{"class":366},[356,3691,371],{"class":370},[356,3693,2811],{"class":366},[356,3695,594],{"class":374},[356,3697,3698,3700,3702,3704,3706,3708,3710,3712],{"class":358,"line":420},[356,3699,363],{"class":362},[356,3701,403],{"class":366},[356,3703,371],{"class":370},[356,3705,375],{"class":374},[356,3707,410],{"class":366},[356,3709,380],{"class":374},[356,3711,415],{"class":366},[356,3713,395],{"class":374},[356,3715,3716],{"class":358,"line":426},[356,3717,423],{"emptyLinePlaceholder":33},[356,3719,3720,3722,3724],{"class":358,"line":483},[356,3721,1110],{"class":429},[356,3723,2829],{"class":366},[356,3725,594],{"class":374},[356,3727,3728],{"class":358,"line":506},[356,3729,423],{"emptyLinePlaceholder":33},[356,3731,3732,3734,3736,3738,3740],{"class":358,"line":513},[356,3733,2840],{"class":362},[356,3735,2636],{"class":366},[356,3737,2846],{"class":2845},[356,3739,2829],{"class":366},[356,3741,1203],{"class":374},[356,3743,3744,3746,3748,3750,3752,3755,3757,3759,3761,3763,3765,3767,3769,3771,3773,3775,3778,3780,3782,3784,3786,3788,3790,3792,3794,3796,3798,3800,3802,3804,3806,3808,3810,3812],{"class":358,"line":550},[356,3745,2662],{"class":362},[356,3747,388],{"class":444},[356,3749,2667],{"class":374},[356,3751,286],{"class":366},[356,3753,3754],{"class":374},">(",[356,3756,2107],{"class":370},[356,3758,2681],{"class":2680},[356,3760,380],{"class":374},[356,3762,3480],{"class":433},[356,3764,1295],{"class":370},[356,3766,2114],{"class":370},[356,3768,2694],{"class":374},[356,3770,286],{"class":366},[356,3772,2699],{"class":366},[356,3774,380],{"class":374},[356,3776,3777],{"class":433}," _buf",[356,3779,1295],{"class":370},[356,3781,2114],{"class":370},[356,3783,2694],{"class":374},[356,3785,286],{"class":366},[356,3787,2718],{"class":2717},[356,3789,2721],{"class":366},[356,3791,538],{"class":374},[356,3793,2731],{"class":370},[356,3795,2734],{"class":366},[356,3797,1231],{"class":374},[356,3799,2107],{"class":370},[356,3801,2694],{"class":374},[356,3803,286],{"class":366},[356,3805,2745],{"class":366},[356,3807,380],{"class":374},[356,3809,2750],{"class":366},[356,3811,2930],{"class":374},[356,3813,1203],{"class":374},[356,3815,3816,3818,3820,3822],{"class":358,"line":581},[356,3817,1209],{"class":433},[356,3819,288],{"class":370},[356,3821,2302],{"class":444},[356,3823,1217],{"class":374},[356,3825,3826,3828,3830,3832,3834,3836,3838],{"class":358,"line":1168},[356,3827,1223],{"class":370},[356,3829,1226],{"class":444},[356,3831,448],{"class":374},[356,3833,495],{"class":494},[356,3835,876],{"class":498},[356,3837,495],{"class":494},[356,3839,480],{"class":374},[356,3841,3842,3844,3846,3848,3850,3852,3854,3856,3858,3860,3862,3864,3866],{"class":358,"line":1186},[356,3843,1223],{"class":370},[356,3845,2327],{"class":444},[356,3847,448],{"class":374},[356,3849,1195],{"class":370},[356,3851,1930],{"class":433},[356,3853,1195],{"class":370},[356,3855,2338],{"class":433},[356,3857,288],{"class":370},[356,3859,2343],{"class":444},[356,3861,589],{"class":374},[356,3863,288],{"class":370},[356,3865,2350],{"class":444},[356,3867,547],{"class":374},[356,3869,3870,3872,3874,3876,3878,3880,3882,3884,3887,3889,3891,3893,3895],{"class":358,"line":1206},[356,3871,1223],{"class":370},[356,3873,1275],{"class":444},[356,3875,448],{"class":374},[356,3877,1037],{"class":366},[356,3879,371],{"class":370},[356,3881,1284],{"class":366},[356,3883,1747],{"class":374},[356,3885,3886],{"class":433}," reason",[356,3888,1295],{"class":370},[356,3890,1298],{"class":494},[356,3892,3022],{"class":498},[356,3894,495],{"class":494},[356,3896,3897],{"class":374}," })\n",[356,3899,3900],{"class":358,"line":1220},[356,3901,3197],{"class":374},[356,3903,3904],{"class":358,"line":1240},[356,3905,2758],{"class":374},[356,3907,3908],{"class":358,"line":1270},[356,3909,423],{"emptyLinePlaceholder":33},[356,3911,3912,3914,3916,3918,3920,3922,3924,3926,3928,3930,3932,3934,3936,3938,3940,3942,3944,3946,3948],{"class":358,"line":1289},[356,3913,430],{"class":429},[356,3915,434],{"class":433},[356,3917,437],{"class":370},[356,3919,383],{"class":366},[356,3921,371],{"class":370},[356,3923,445],{"class":444},[356,3925,448],{"class":374},[356,3927,94],{"class":366},[356,3929,371],{"class":370},[356,3931,455],{"class":444},[356,3933,448],{"class":374},[356,3935,460],{"class":366},[356,3937,371],{"class":370},[356,3939,465],{"class":444},[356,3941,448],{"class":374},[356,3943,471],{"class":470},[356,3945,474],{"class":374},[356,3947,477],{"class":370},[356,3949,480],{"class":374},[356,3951,3952,3954,3956,3958,3960,3963,3965],{"class":358,"line":1308},[356,3953,486],{"class":370},[356,3955,489],{"class":444},[356,3957,448],{"class":374},[356,3959,495],{"class":494},[356,3961,3962],{"class":498},"tenant-ip",[356,3964,495],{"class":494},[356,3966,480],{"class":374},[356,3968,3969,3971,3973,3975,3977,3979,3981,3983,3986,3988,3990,3992,3994],{"class":358,"line":1314},[356,3970,486],{"class":370},[356,3972,518],{"class":444},[356,3974,448],{"class":374},[356,3976,315],{"class":366},[356,3978,371],{"class":370},[356,3980,455],{"class":444},[356,3982,448],{"class":374},[356,3984,3985],{"class":366},"TenantId",[356,3987,380],{"class":374},[356,3989,839],{"class":366},[356,3991,371],{"class":370},[356,3993,891],{"class":444},[356,3995,3996],{"class":374},"()))\n",[356,3998,3999,4001,4003,4005,4007,4009,4011,4013,4016,4018,4020],{"class":358,"line":1320},[356,4000,486],{"class":370},[356,4002,555],{"class":444},[356,4004,448],{"class":374},[356,4006,410],{"class":366},[356,4008,371],{"class":370},[356,4010,564],{"class":444},[356,4012,448],{"class":374},[356,4014,4015],{"class":470},"100",[356,4017,288],{"class":572},[356,4019,575],{"class":470},[356,4021,578],{"class":374},[356,4023,4024,4026,4028,4030,4032],{"class":358,"line":1346},[356,4025,486],{"class":370},[356,4027,586],{"class":444},[356,4029,589],{"class":374},[356,4031,477],{"class":370},[356,4033,594],{"class":374},[1013,4035,4037],{"id":4036},"what-makes-a-good-key","What makes a good key",[4039,4040,4041,4048,4054,4068],"ul",{},[4042,4043,4044,4047],"li",{},[603,4045,4046],{},"Stable per client."," The same client must produce the same key on every request.",[4042,4049,4050,4053],{},[603,4051,4052],{},"Bounded."," Every distinct key holds state for a window. Never key on raw client-chosen values\nwithout validating them.",[4042,4055,4056,4059,4060,4063,4064,4067],{},[603,4057,4058],{},"No secrets."," Keys are stored in Redis and appear in ",[275,4061,4062],{},"RejectionInfo",". Hash credentials with\n",[275,4065,4066],{},"Header::hashed()",", or key on an account id instead.",[4042,4069,4070,4073],{},[603,4071,4072],{},"Any length."," Long keys are stored as a hash automatically, so size isn't a concern.",[340,4075,4077],{"id":4076},"key-errors","Key errors",[247,4079,4080,4081,4084],{},"When an extractor fails, ",[275,4082,4083],{},"on_key_error"," decides what happens:",[251,4086,4087,4097],{},[254,4088,4089],{},[257,4090,4091,4094],{},[260,4092,4093],{},"Policy",[260,4095,4096],{},"Effect",[267,4098,4099,4117,4127],{},[257,4100,4101,4107],{},[272,4102,4103,4106],{},[275,4104,4105],{},"KeyErrorPolicy::Reject"," (default)",[272,4108,4109,4112,4113,4116],{},[275,4110,4111],{},"400 Bad Request"," with the body ",[275,4114,4115],{},"invalid rate limiting key",". The body never echoes the request.",[257,4118,4119,4124],{},[272,4120,4121],{},[275,4122,4123],{},"KeyErrorPolicy::Bypass",[272,4125,4126],{},"Forward the request without counting it.",[257,4128,4129,4134],{},[272,4130,4131],{},[275,4132,4133],{},"KeyErrorPolicy::UseGlobalKey",[272,4135,4136],{},"Count the request in one shared bucket for the namespace.",[347,4138,4140],{"className":349,"code":4139,"language":351,"meta":352,"style":352},"use actix_trypema::{KeyErrorPolicy, Local, TrypemaLimiter, extract::Header};\nuse trypema::{RateLimit, WindowSize};\n\n// Anonymous requests (no API key) share one small bucket instead of being rejected.\nlet limiter = TrypemaLimiter::builder(Local::new(WindowSize::seconds_or_panic(60))?)\n    .namespace(\"apikey\")\n    .extractor(Header::new(\"x-api-key\")?.hashed())\n    .rate(RateLimit::per_minute_or_panic(100.0))\n    .on_key_error(KeyErrorPolicy::UseGlobalKey)\n    .build()?;\n",[275,4141,4142,4173,4191,4195,4200,4240,4256,4286,4310,4327],{"__ignoreMap":352},[356,4143,4144,4146,4148,4150,4152,4155,4157,4159,4161,4163,4165,4167,4169,4171],{"class":358,"line":359},[356,4145,363],{"class":362},[356,4147,367],{"class":366},[356,4149,371],{"class":370},[356,4151,375],{"class":374},[356,4153,4154],{"class":366},"KeyErrorPolicy",[356,4156,380],{"class":374},[356,4158,1042],{"class":366},[356,4160,380],{"class":374},[356,4162,383],{"class":366},[356,4164,380],{"class":374},[356,4166,388],{"class":366},[356,4168,371],{"class":370},[356,4170,295],{"class":366},[356,4172,395],{"class":374},[356,4174,4175,4177,4179,4181,4183,4185,4187,4189],{"class":358,"line":398},[356,4176,363],{"class":362},[356,4178,403],{"class":366},[356,4180,371],{"class":370},[356,4182,375],{"class":374},[356,4184,410],{"class":366},[356,4186,380],{"class":374},[356,4188,415],{"class":366},[356,4190,395],{"class":374},[356,4192,4193],{"class":358,"line":420},[356,4194,423],{"emptyLinePlaceholder":33},[356,4196,4197],{"class":358,"line":426},[356,4198,4199],{"class":509},"// Anonymous requests (no API key) share one small bucket instead of being rejected.\n",[356,4201,4202,4204,4206,4208,4210,4212,4214,4216,4218,4220,4222,4224,4226,4228,4230,4232,4234,4236,4238],{"class":358,"line":483},[356,4203,430],{"class":429},[356,4205,434],{"class":433},[356,4207,437],{"class":370},[356,4209,383],{"class":366},[356,4211,371],{"class":370},[356,4213,445],{"class":444},[356,4215,448],{"class":374},[356,4217,94],{"class":366},[356,4219,371],{"class":370},[356,4221,455],{"class":444},[356,4223,448],{"class":374},[356,4225,460],{"class":366},[356,4227,371],{"class":370},[356,4229,465],{"class":444},[356,4231,448],{"class":374},[356,4233,471],{"class":470},[356,4235,474],{"class":374},[356,4237,477],{"class":370},[356,4239,480],{"class":374},[356,4241,4242,4244,4246,4248,4250,4252,4254],{"class":358,"line":506},[356,4243,486],{"class":370},[356,4245,489],{"class":444},[356,4247,448],{"class":374},[356,4249,495],{"class":494},[356,4251,499],{"class":498},[356,4253,495],{"class":494},[356,4255,480],{"class":374},[356,4257,4258,4260,4262,4264,4266,4268,4270,4272,4274,4276,4278,4280,4282,4284],{"class":358,"line":513},[356,4259,486],{"class":370},[356,4261,518],{"class":444},[356,4263,448],{"class":374},[356,4265,295],{"class":366},[356,4267,371],{"class":370},[356,4269,455],{"class":444},[356,4271,448],{"class":374},[356,4273,495],{"class":494},[356,4275,533],{"class":498},[356,4277,495],{"class":494},[356,4279,538],{"class":374},[356,4281,541],{"class":370},[356,4283,544],{"class":444},[356,4285,547],{"class":374},[356,4287,4288,4290,4292,4294,4296,4298,4300,4302,4304,4306,4308],{"class":358,"line":550},[356,4289,486],{"class":370},[356,4291,555],{"class":444},[356,4293,448],{"class":374},[356,4295,410],{"class":366},[356,4297,371],{"class":370},[356,4299,564],{"class":444},[356,4301,448],{"class":374},[356,4303,4015],{"class":470},[356,4305,288],{"class":572},[356,4307,575],{"class":470},[356,4309,578],{"class":374},[356,4311,4312,4314,4316,4318,4320,4322,4325],{"class":358,"line":581},[356,4313,486],{"class":370},[356,4315,4083],{"class":444},[356,4317,448],{"class":374},[356,4319,4154],{"class":366},[356,4321,371],{"class":370},[356,4323,4324],{"class":366},"UseGlobalKey",[356,4326,480],{"class":374},[356,4328,4329,4331,4333,4335,4337],{"class":358,"line":1168},[356,4330,486],{"class":370},[356,4332,586],{"class":444},[356,4334,589],{"class":374},[356,4336,477],{"class":370},[356,4338,594],{"class":374},[247,4340,4341,4343,4344,278,4347,945,4350,4353,4354,4357,4358,4360],{},[275,4342,1037],{}," has four variants: ",[275,4345,4346],{},"MissingPeerAddr",[275,4348,4349],{},"MissingHeader { name }",[275,4351,4352],{},"MalformedForwardedChain"," and ",[275,4355,4356],{},"InvalidValue { reason }",". Custom extractors usually return\n",[275,4359,1284],{}," with a short static reason.",[340,4362,4364],{"id":4363},"the-stored-key","The stored key",[247,4366,4367,4370,4371,288],{},[275,4368,4369],{},"derived_key(namespace, key)"," returns exactly what a limiter stores for a key. Use it to inspect\nor reset one client; see ",[286,4372,4374],{"href":4373},"/actix-web/observability#managing-keys","Managing keys",[347,4376,4378],{"className":349,"code":4377,"language":351,"meta":352,"style":352},"use actix_trypema::derived_key;\n\nassert_eq!(derived_key(\"ip\", \"203.0.113.7\"), \"ip_r_203.0.113.7\");\n",[275,4379,4380,4393,4397],{"__ignoreMap":352},[356,4381,4382,4384,4386,4388,4391],{"class":358,"line":359},[356,4383,363],{"class":362},[356,4385,367],{"class":366},[356,4387,371],{"class":370},[356,4389,4390],{"class":1416},"derived_key",[356,4392,594],{"class":374},[356,4394,4395],{"class":358,"line":398},[356,4396,423],{"emptyLinePlaceholder":33},[356,4398,4399,4402,4404,4406,4408,4410,4413,4415,4417,4419,4422,4424,4427,4429,4432,4434],{"class":358,"line":420},[356,4400,4401],{"class":444},"assert_eq!",[356,4403,448],{"class":374},[356,4405,4390],{"class":444},[356,4407,448],{"class":374},[356,4409,495],{"class":494},[356,4411,4412],{"class":498},"ip",[356,4414,495],{"class":494},[356,4416,380],{"class":374},[356,4418,1298],{"class":494},[356,4420,4421],{"class":498},"203.0.113.7",[356,4423,495],{"class":494},[356,4425,4426],{"class":374},"),",[356,4428,1298],{"class":494},[356,4430,4431],{"class":498},"ip_r_203.0.113.7",[356,4433,495],{"class":494},[356,4435,994],{"class":374},[4437,4438,4439],"style",{},"html pre.shiki code .shWJe, html code.shiki .shWJe{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sKvfc, html code.shiki .sKvfc{--shiki-light:#E2931D;--shiki-light-text-decoration:inherit;--shiki-default:#6F42C1;--shiki-default-text-decoration:inherit;--shiki-dark:#B392F0;--shiki-dark-text-decoration:inherit;--shiki-sepia:#A6E22E;--shiki-sepia-text-decoration:underline}html pre.shiki code .sGXK2, html code.shiki .sGXK2{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .swvn1, html code.shiki .swvn1{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .srJo8, html code.shiki .srJo8{--shiki-light:#9C3EDA;--shiki-light-font-style:inherit;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .ss--_, html code.shiki .ss--_{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .sD0ED, html code.shiki .sD0ED{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .sYThS, html code.shiki .sYThS{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .siCPE, html code.shiki .siCPE{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .ss7Ak, html code.shiki .ss7Ak{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit;--shiki-sepia:#88846F;--shiki-sepia-font-style:inherit}html pre.shiki code .sMTiH, html code.shiki .sMTiH{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s6Ffr, html code.shiki .s6Ffr{--shiki-light:#E2931D;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .sQeA1, html code.shiki .sQeA1{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .sSsL9, html code.shiki .sSsL9{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#FD971F}html pre.shiki code .sTNss, html code.shiki .sTNss{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sRxSC, html code.shiki .sRxSC{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#F92672;--shiki-sepia-font-style:inherit}",{"title":352,"searchDepth":359,"depth":398,"links":4441},[4442,4444,4446,4448,4454,4458,4459],{"id":342,"depth":398,"text":4443},"API keys and other headers: Header",{"id":624,"depth":398,"text":4445},"One bucket for everything: Global",{"id":799,"depth":398,"text":4447},"Two keys at once: Composite",{"id":1000,"depth":398,"text":1001,"children":4449},[4450,4451,4452,4453],{"id":1015,"depth":420,"text":1016},{"id":1359,"depth":420,"text":1360},{"id":1765,"depth":420,"text":1766},{"id":2027,"depth":420,"text":2028},{"id":2602,"depth":398,"text":4455,"children":4456},"Implementing KeyExtractor",[4457],{"id":4036,"depth":420,"text":4037},{"id":4076,"depth":398,"text":4077},{"id":4363,"depth":398,"text":4364},"Limit by API key, tenant, user or anything else in the request, with the built-in extractors or your own.","md",{},{"icon":44},{"title":119,"description":4460},"VuAvBrrwq4NAd88i9SBWlHNTuDt7pw1YGmaeZQWTQb4",[4467,4469],{"title":114,"path":115,"stem":116,"description":4468,"icon":117,"children":-1},"Key limits by client IP, directly or behind load balancers, proxies and CDNs, without letting clients spoof their address.",{"title":123,"path":124,"stem":125,"description":4470,"icon":126,"children":-1},"Set rates and windows, give plans different limits, charge expensive requests more, shed load gradually, and stack several limiters.",1791663192236]