diff --git a/docs/public/yarn.lock b/docs/public/yarn.lock
index 7c7afbb..ebb25bc 100644
--- a/docs/public/yarn.lock
+++ b/docs/public/yarn.lock
@@ -1510,9 +1510,9 @@
to-fast-properties "^2.0.0"
"@braintree/sanitize-url@^6.0.0":
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz#fe364f025ba74f6de6c837a84ef44bdb1d61e68f"
- integrity sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w==
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz#6110f918d273fe2af8ea1c4398a88774bb9fc12f"
+ integrity sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==
"@colors/colors@1.5.0":
version "1.5.0"
@@ -1805,7 +1805,7 @@
"@docusaurus/theme-search-algolia" "2.4.0"
"@docusaurus/types" "2.4.0"
-"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
+"@docusaurus/react-loadable@5.5.2":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
@@ -2384,11 +2384,6 @@
dependencies:
defer-to-connect "^1.0.1"
-"@trysound/sax@0.2.0":
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
- integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
-
"@tsconfig/docusaurus@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@tsconfig/docusaurus/-/docusaurus-1.0.2.tgz#400ef146d3e7da2f78bf9749c943d123dce5fd97"
@@ -5831,9 +5826,9 @@ htmlparser2@^8.0.1:
entities "^4.4.0"
http-cache-semantics@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
- integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
+ integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
http-deceiver@^1.2.7:
version "1.2.7"
@@ -6416,20 +6411,13 @@ json-stable-stringify-without-jsonify@^1.0.1:
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
json5@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
- integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
+ integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
dependencies:
minimist "^1.2.0"
-json5@^2.1.2:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
- integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
- dependencies:
- minimist "^1.2.5"
-
-json5@^2.2.2:
+json5@^2.1.2, json5@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@@ -6561,9 +6549,9 @@ loader-runner@^4.2.0:
integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==
loader-utils@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
- integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
+ integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
@@ -6940,9 +6928,9 @@ minimatch@^3.0.5, minimatch@^3.1.1:
brace-expansion "^1.1.7"
minimist@^1.2.0, minimist@^1.2.5:
- version "1.2.5"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
- integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
ms@2.0.0:
version "2.0.0"
@@ -6967,10 +6955,10 @@ multicast-dns@^7.2.5:
dns-packet "^5.2.2"
thunky "^1.0.2"
-nanoid@^3.3.6:
- version "3.3.6"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
- integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
+nanoid@^3.3.16:
+ version "3.3.16"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.16.tgz#a04d8ec4b1f10009d2d533947aefe4293737816c"
+ integrity sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==
natural-compare@^1.4.0:
version "1.4.0"
@@ -7427,6 +7415,11 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+picocolors@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
@@ -7766,13 +7759,13 @@ postcss-zindex@^5.1.0:
integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==
postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.17, postcss@^8.4.19:
- version "8.4.23"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
- integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
+ version "8.5.24"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.24.tgz#01d8b032451e1b9ec41ae66eaf02843f42a720d2"
+ integrity sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw==
dependencies:
- nanoid "^3.3.6"
- picocolors "^1.0.0"
- source-map-js "^1.0.2"
+ nanoid "^3.3.16"
+ picocolors "^1.1.1"
+ source-map-js "^1.2.1"
prelude-ls@^1.2.1:
version "1.2.1"
@@ -8044,6 +8037,14 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
dependencies:
"@babel/runtime" "^7.10.3"
+"react-loadable@npm:@docusaurus/react-loadable@5.5.2":
+ version "5.5.2"
+ resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
+ integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
+ dependencies:
+ "@types/react" "*"
+ prop-types "^15.6.2"
+
react-router-config@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988"
@@ -8478,6 +8479,11 @@ sax@^1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+sax@^1.5.0:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.1.tgz#4c23cf608c0b693ab54b4b5888e92cfe977b9843"
+ integrity sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==
+
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
@@ -8560,26 +8566,19 @@ semver-diff@^3.1.1:
semver "^6.3.0"
"semver@2 || 3 || 4 || 5", semver@^5.4.1:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
- integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+ version "5.7.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
+ integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
- integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+ version "6.3.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.2.1, semver@^7.3.2, semver@^7.3.4:
- version "7.3.5"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
- integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
- dependencies:
- lru-cache "^6.0.0"
-
-semver@^7.3.7, semver@^7.3.8:
- version "7.5.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0"
- integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==
+semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8:
+ version "7.5.4"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+ integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
@@ -8770,10 +8769,10 @@ sort-css-media-queries@2.1.0:
resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz#7c85e06f79826baabb232f5560e9745d7a78c4ce"
integrity sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==
-source-map-js@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
- integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+source-map-js@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+ integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
source-map-support@~0.5.20:
version "0.5.21"
@@ -9062,16 +9061,16 @@ svg-parser@^2.0.4:
integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==
svgo@^2.7.0, svgo@^2.8.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
- integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
+ version "2.8.3"
+ resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.3.tgz#8de2fa579f3f7429dec3fb86471005cf46fb483a"
+ integrity sha512-5EZD0pafXX6PphdwOGCiVLDSaV1xyuQao2blHajHLsPxr07q4mmEjdtXEWgG07ae2mIz8Ex2CDXNCTiXhy3Khw==
dependencies:
- "@trysound/sax" "0.2.0"
commander "^7.2.0"
css-select "^4.1.3"
css-tree "^1.1.3"
csso "^4.2.0"
picocolors "^1.0.0"
+ sax "^1.5.0"
stable "^0.1.8"
table@^6.0.9:
@@ -9264,9 +9263,9 @@ typescript@^4.2.4:
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
ua-parser-js@^0.7.18:
- version "0.7.28"
- resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31"
- integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==
+ version "0.7.35"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.35.tgz#8bda4827be4f0b1dda91699a29499575a1f1d307"
+ integrity sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==
unbox-primitive@^1.0.1:
version "1.0.1"
@@ -9793,9 +9792,9 @@ wildcard@^2.0.0:
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
word-wrap@^1.2.3:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
- integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f"
+ integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==
wrap-ansi@^7.0.0:
version "7.0.0"
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index e52ed21..6378603 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -32,7 +32,7 @@
-
+
diff --git a/src/Wemogy.Core.Tests/DynamicProxies/AsyncInterceptorExtensionsTests.cs b/src/Wemogy.Core.Tests/DynamicProxies/AsyncInterceptorExtensionsTests.cs
new file mode 100644
index 0000000..7daa8b3
--- /dev/null
+++ b/src/Wemogy.Core.Tests/DynamicProxies/AsyncInterceptorExtensionsTests.cs
@@ -0,0 +1,33 @@
+using System;
+using FluentAssertions;
+using Polly.Contrib.WaitAndRetry;
+using Wemogy.Core.DynamicProxies;
+using Wemogy.Core.DynamicProxies.Extensions;
+using Wemogy.Core.Errors.Exceptions;
+using Wemogy.Core.Tests.DynamicProxies.TestData;
+using Xunit;
+
+namespace Wemogy.Core.Tests.DynamicProxies;
+
+public class AsyncInterceptorExtensionsTests
+{
+ [Fact]
+ public void Wrap_ShouldReuseTheProxyTypeAcrossCalls()
+ {
+ // Arrange
+ var retryProxy = new RetryProxy(
+ Backoff.ExponentialBackoff(TimeSpan.FromMilliseconds(10), 1));
+
+ // Act
+ var first = retryProxy.Wrap(new FlakyService(0, () => new Exception()));
+ var second = retryProxy.Wrap(new FlakyService(0, () => new Exception()));
+ var untyped = retryProxy.Wrap(typeof(IFlakyService), new FlakyService(0, () => new Exception()));
+
+ // Assert
+ // A new ProxyGenerator per call emits a new dynamic assembly with a new proxy type every time.
+ // Those types can never be unloaded, so a wrapper created per request (e.g. a scoped
+ // repository) grows the process until it runs out of memory.
+ second.GetType().Should().BeSameAs(first.GetType());
+ untyped.GetType().Should().BeSameAs(first.GetType());
+ }
+}
diff --git a/src/Wemogy.Core.Tests/Wemogy.Core.Tests.csproj b/src/Wemogy.Core.Tests/Wemogy.Core.Tests.csproj
index 960fc69..2a82f12 100644
--- a/src/Wemogy.Core.Tests/Wemogy.Core.Tests.csproj
+++ b/src/Wemogy.Core.Tests/Wemogy.Core.Tests.csproj
@@ -1,6 +1,7 @@
net8.0;net10.0
+ Exe
@@ -11,7 +12,7 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/Wemogy.Core/DynamicProxies/Extensions/AsyncInterceptorExtensions.cs b/src/Wemogy.Core/DynamicProxies/Extensions/AsyncInterceptorExtensions.cs
index 003e288..59f924b 100644
--- a/src/Wemogy.Core/DynamicProxies/Extensions/AsyncInterceptorExtensions.cs
+++ b/src/Wemogy.Core/DynamicProxies/Extensions/AsyncInterceptorExtensions.cs
@@ -5,14 +5,20 @@ namespace Wemogy.Core.DynamicProxies.Extensions
{
public static class AsyncInterceptorExtensions
{
+ ///
+ /// Shared on purpose. Every ProxyGenerator owns its own module scope, so a new instance per call
+ /// emits a new dynamic assembly with a new proxy type each time — types that can never be
+ /// unloaded. Wrapping per request (a scoped repository, for example) then leaks memory until
+ /// the process is killed. One generator caches the proxy type per interface and is thread-safe.
+ ///
+ private static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator();
+
public static TInterface Wrap(
this IAsyncInterceptor proxy,
object target)
where TInterface : class
{
- var proxyGenerator = new ProxyGenerator();
-
- var wrappedImplementation = (TInterface)proxyGenerator.CreateInterfaceProxyWithTarget(
+ var wrappedImplementation = (TInterface)ProxyGenerator.CreateInterfaceProxyWithTarget(
typeof(TInterface),
target,
proxy.ToInterceptor());
@@ -25,9 +31,7 @@ public static object Wrap(
Type interfaceType,
object target)
{
- var proxyGenerator = new ProxyGenerator();
-
- var wrappedImplementation = proxyGenerator.CreateInterfaceProxyWithTarget(
+ var wrappedImplementation = ProxyGenerator.CreateInterfaceProxyWithTarget(
interfaceType,
target,
proxy.ToInterceptor());
diff --git a/src/Wemogy.Core/Helpers/RandomGenerator.cs b/src/Wemogy.Core/Helpers/RandomGenerator.cs
index 20bacf7..d972e83 100644
--- a/src/Wemogy.Core/Helpers/RandomGenerator.cs
+++ b/src/Wemogy.Core/Helpers/RandomGenerator.cs
@@ -1,46 +1,52 @@
+using System;
using System.Security.Cryptography;
namespace Wemogy.Core.Helpers
{
public class RandomGenerator
{
- public static string GetUniqueToken(int length, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_")
+ public static string GenerateRandomToken(int length, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_")
{
- using (var crypto = RandomNumberGenerator.Create())
- {
- byte[] data = new byte[length];
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(length, 0, nameof(length));
+ ArgumentException.ThrowIfNullOrWhiteSpace(chars, nameof(chars));
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(chars.Length, byte.MaxValue + 1, nameof(chars));
- // Maximum random number that can be used without introducing a bias
- int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);
+ // Maximum random byte value usable without introducing modulo bias
+ int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);
- crypto.GetBytes(data);
+ Span data = length <= 256 ? stackalloc byte[length] : new byte[length];
+ RandomNumberGenerator.Fill(data);
- char[] result = new char[length];
+ Span result = length <= 256 ? stackalloc char[length] : new char[length];
+ Span retryBuffer = stackalloc byte[1];
- for (int i = 0; i < length; i++)
+ for (int i = 0; i < length; i++)
+ {
+ byte value = data[i];
+ while (value > maxRandom)
{
- byte value = data[i];
-
- while (value > maxRandom)
- {
- // If chars.Length isn't a power of 2 then there is a bias if we simply use the modulus operator. The first characters of chars will be more probable than the last ones.
- // buffer used if we encounter an unusable random byte. We will regenerate it in this buffer
- var buffer = new byte[1];
-
- crypto.GetBytes(buffer);
- value = buffer[0];
- }
-
- result[i] = chars[value % chars.Length];
+ // If chars.Length isn't a power of 2, using modulus directly would bias
+ // the first characters of chars over the last ones. Rejection sampling
+ // (regenerating out-of-range bytes) avoids that bias.
+ RandomNumberGenerator.Fill(retryBuffer);
+ value = retryBuffer[0];
}
- return new string(result);
+ result[i] = chars[value % chars.Length];
}
+
+ return new string(result);
+ }
+
+ [Obsolete("Use GenerateRandomToken instead. This method does not guarantee uniqueness.")]
+ public static string GetUniqueToken(int length, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_")
+ {
+ return GenerateRandomToken(length, chars);
}
public static string GenerateRandomPassword(int length)
{
- return GetUniqueToken(length);
+ return GenerateRandomToken(length);
}
}
}