From a2fce25e6121758927af1d25d600a0f55ad17deb Mon Sep 17 00:00:00 2001 From: thislight Date: Thu, 3 Oct 2024 14:29:33 +0800 Subject: [PATCH 1/4] upgrade hexo --- package.json | 10 +- pnpm-lock.yaml | 366 ++++++++++++++++++++++++++++--------------------- 2 files changed, 214 insertions(+), 162 deletions(-) diff --git a/package.json b/package.json index dba76ad..e41d39b 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,11 @@ "new": "hexo new" }, "hexo": { - "version": "7.2.0" + "version": "7.3.0" }, "dependencies": { - "axios": "^1.7.2", - "hexo": "^7.2.0", + "axios": "^1.7.7", + "hexo": "^7.3.0", "hexo-feed": "^1.1.2", "hexo-generator-archive": "^2.0.0", "hexo-generator-category": "^2.0.0", @@ -29,7 +29,7 @@ "rate-limiter-flexible": "^2.4.2" }, "devDependencies": { - "wrangler": "^3.58.0" + "wrangler": "^3.79.0" }, - "packageManager": "pnpm@9.4.0+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a" + "packageManager": "pnpm@9.12.0+sha512.4abf725084d7bcbafbd728bfc7bee61f2f791f977fd87542b3579dcb23504d170d46337945e4c66485cd12d588a0c0e570ed9c477e7ccdd8507cf05f3f92eaca" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3013a49..5de792d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: .: dependencies: axios: - specifier: ^1.7.2 - version: 1.7.2 + specifier: ^1.7.7 + version: 1.7.7 hexo: - specifier: ^7.2.0 - version: 7.2.0(chokidar@3.6.0) + specifier: ^7.3.0 + version: 7.3.0(chokidar@3.6.0) hexo-feed: specifier: ^1.1.2 - version: 1.1.2(chalk@4.1.2)(hexo@7.2.0(chokidar@3.6.0)) + version: 1.1.2(chalk@4.1.2)(hexo@7.3.0(chokidar@3.6.0)) hexo-generator-archive: specifier: ^2.0.0 version: 2.0.0 @@ -49,48 +49,52 @@ importers: version: 2.4.2 devDependencies: wrangler: - specifier: ^3.58.0 - version: 3.58.0 + specifier: ^3.79.0 + version: 3.79.0 packages: '@adobe/css-tools@4.3.3': resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} - '@cloudflare/kv-asset-handler@0.3.2': - resolution: {integrity: sha512-EeEjMobfuJrwoctj7FA1y1KEbM0+Q1xSjobIEyie9k4haVEBB7vkDvsasw1pM3rO39mL2akxIAzLMUAtrMHZhA==} + '@cloudflare/kv-asset-handler@0.3.4': + resolution: {integrity: sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==} engines: {node: '>=16.13'} - '@cloudflare/workerd-darwin-64@1.20240524.0': - resolution: {integrity: sha512-ATaXjefbTsrv4mpn4Fdua114RRDXcX5Ky+Mv+f4JTUllgalmqC4CYMN4jxRz9IpJU/fNMN8IEfvUyuJBAcl9Iw==} + '@cloudflare/workerd-darwin-64@1.20240925.0': + resolution: {integrity: sha512-KdLnSXuzB65CbqZPm+qYzk+zkQ1tUNPaaRGYVd/jPYAxwwtfTUQdQ+ahDPwVVs2tmQELKy7ZjQjf2apqSWUfjw==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20240524.0': - resolution: {integrity: sha512-wnbsZI4CS0QPCd+wnBHQ40C28A/2Qo4ESi1YhE2735G3UNcc876MWksZhsubd+XH0XPIra6eNFqyw6wRMpQOXA==} + '@cloudflare/workerd-darwin-arm64@1.20240925.0': + resolution: {integrity: sha512-MiQ6uUmCXjsXgWNV+Ock2tp2/tYqNJGzjuaH6jFioeRF+//mz7Tv7J7EczOL4zq+TH8QFOh0/PUsLyazIWVGng==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20240524.0': - resolution: {integrity: sha512-E8mj+HPBryKwaJAiNsYzXtVjKCL0KvUBZbtxJxlWM4mLSQhT+uwGT3nydb/hFY59rZnQgZslw0oqEWht5TEYiQ==} + '@cloudflare/workerd-linux-64@1.20240925.0': + resolution: {integrity: sha512-Rjix8jsJMfsInmq3Hm3fmiRQ+rwzuWRPV1pg/OWhMSfNP7Qp2RCU+RGkhgeR9Z5eNAje0Sn2BMrFq4RvF9/yRA==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20240524.0': - resolution: {integrity: sha512-/Fr1W671t2triNCDCBWdStxngnbUfZunZ/2e4kaMLzJDJLYDtYdmvOUCBDzUD4ssqmIMbn9RCQQ0U+CLEoqBqw==} + '@cloudflare/workerd-linux-arm64@1.20240925.0': + resolution: {integrity: sha512-VYIPeMHQRtbwQoIjUwS/zULlywPxyDvo46XkTpIW5MScEChfqHvAYviQ7TzYGx6Q+gmZmN+DUB2KOMx+MEpCxA==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20240524.0': - resolution: {integrity: sha512-G+ThDEx57g9mAEKqhWnHaaJgpeGYtyhkmwM/BDpLqPks/rAY5YEfZbY4YL1pNk1kkcZDXGrwIsY8xe9Apf5JdA==} + '@cloudflare/workerd-windows-64@1.20240925.0': + resolution: {integrity: sha512-C8peGvaU5R51bIySi1VbyfRgwNSSRknqoFSnSbSBI3uTN3THTB3UnmRKy7GXJDmyjgXuT9Pcs1IgaWNubLtNtw==} engines: {node: '>=16'} cpu: [x64] os: [win32] + '@cloudflare/workers-shared@0.5.4': + resolution: {integrity: sha512-PNL/0TjKRdUHa1kwgVdqUNJVZ9ez4kacsi8omz+gv859EvJmsVuGiMAClY2YfJnC9LVKhKCcjqmFgKNXG9/IXA==} + engines: {node: '>=16.7.0'} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -245,8 +249,8 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -258,8 +262,8 @@ packages: '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - '@types/node@20.14.0': - resolution: {integrity: sha512-5cHBxFGJx6L4s56Bubp4fglrEpmyJypsqI6RgzMfBHWUJQGWAAi8cWcgetEbZXHYXo9C2Fa4EEds/uSyS4cxmA==} + '@types/node@22.7.4': + resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} a-sync-waterfall@1.0.1: resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==} @@ -279,12 +283,12 @@ packages: acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true @@ -316,14 +320,14 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.7.2: - resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -440,8 +444,8 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -456,6 +460,9 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -483,8 +490,8 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.1.5: - resolution: {integrity: sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==} + dompurify@3.1.7: + resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==} domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} @@ -501,6 +508,10 @@ packages: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -560,8 +571,8 @@ packages: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} - follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -683,13 +694,13 @@ packages: resolution: {integrity: sha512-YvGngXijE2muEh5L/VI4Fmjqb+/yAkmY+VuyhWVoRwQu1X7bmWodsfYRXX7CUYhi5LqsvH8FAe/yBW1+f6ZX4Q==} engines: {node: '>=14'} - hexo@7.2.0: - resolution: {integrity: sha512-RYIzl7jfG0i2jH/k5IZg4C1anyHfmKHNUsBKIn9LU0V3iQ0WQrwuOLFDJwaZDenqmzHYJhAVCGAkrBDfF/IlVg==} + hexo@7.3.0: + resolution: {integrity: sha512-dOe8mzBKrvjubW5oBmyhcnQDpC+M2xmAMLae5K+o+SkHxyvAhShkS2VQZoTsOLIJKY6xilv7dzCjCvE7ol/NHQ==} engines: {node: '>=14'} hasBin: true - highlight.js@11.9.0: - resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==} + highlight.js@11.10.0: + resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==} engines: {node: '>=12.0.0'} html-encoding-sniffer@3.0.0: @@ -726,8 +737,9 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} @@ -760,8 +772,8 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jake@10.9.1: - resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==} + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} engines: {node: '>=10'} hasBin: true @@ -801,14 +813,18 @@ packages: micro-memoize@4.1.2: resolution: {integrity: sha512-+HzcV2H+rbSJzApgkj0NdTakkC+bnyeiUxgT6/m7mjcz1CmM22KYFKp+EVj1sWe4UYcnriJr5uqHQD/gMHLD+g==} - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.53.0: + resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -832,8 +848,8 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - miniflare@3.20240524.1: - resolution: {integrity: sha512-5d3pRxvd5pT7lX1SsBH9+AjXuyHJnChSNOnYhubfi7pxMek4ZfULwhnUmNUp1R7b2xKuzqdFDZa0fsZuUoFxlw==} + miniflare@3.20240925.0: + resolution: {integrity: sha512-2LmQbKHf0n6ertUKhT+Iltixi53giqDH7P71+wCir3OnGyXIODqYwOECx1mSDNhYThpxM2dav8UdPn6SQiMoXw==} engines: {node: '>=16.13'} hasBin: true @@ -863,9 +879,6 @@ packages: ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -908,8 +921,11 @@ packages: chokidar: optional: true - nwsapi@2.2.10: - resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} + nwsapi@2.2.13: + resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==} + + ohash@1.1.4: + resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} on-finished@2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} @@ -951,11 +967,14 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-to-regexp@6.2.2: - resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -1011,8 +1030,8 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true - rfdc@1.3.1: - resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} rollup-plugin-inject@3.0.2: resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} @@ -1044,12 +1063,12 @@ packages: resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} engines: {node: '>=10'} - send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} - serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} setprototypeof@1.2.0: @@ -1149,16 +1168,22 @@ packages: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} engines: {node: '>=14.0'} + unenv-nightly@2.0.0-20240919-125358-9a64854: + resolution: {integrity: sha512-XjsgUTrTHR7iw+k/SRTNjh6EQgwpC9voygnoCJo5kh4hKqsSDHUW84MhL9EsHTNfLctvVBHaSw8e2k3R2fKXsQ==} + universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -1210,17 +1235,17 @@ packages: engines: {node: '>= 8'} hasBin: true - workerd@1.20240524.0: - resolution: {integrity: sha512-LWLe5D8PVHBcqturmBbwgI71r7YPpIMYZoVEH6S4G35EqIJ55cb0n3FipoSyraoIfpcCxCFxX1K6WsRHbP3pFA==} + workerd@1.20240925.0: + resolution: {integrity: sha512-/Jj6+yLwfieZGEt3Kx4+5MoufuC3g/8iFaIh4MPBNGJOGYmdSKXvgCqz09m2+tVCYnysRfbq2zcbVxJRBfOCqQ==} engines: {node: '>=16'} hasBin: true - wrangler@3.58.0: - resolution: {integrity: sha512-h9gWER7LXLnmHABDNP1p3aqXtchlvSBN8Dp22ZurnkxaLMZ3L3H1Ze1ftiFSs0VRWv0BUnz7AWIUqZmzuBY4Nw==} + wrangler@3.79.0: + resolution: {integrity: sha512-29wzQWc5qNKtD3bSyAEX11j/U5IIk8xy2/ZJ4wljJlS5ppff8qGqI+LtlLmqjqTSeQqFLb87xRkms0YFUgNLXg==} engines: {node: '>=16.17.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20240524.0 + '@cloudflare/workers-types': ^4.20240925.0 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -1228,8 +1253,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -1260,25 +1285,30 @@ snapshots: '@adobe/css-tools@4.3.3': {} - '@cloudflare/kv-asset-handler@0.3.2': + '@cloudflare/kv-asset-handler@0.3.4': dependencies: mime: 3.0.0 - '@cloudflare/workerd-darwin-64@1.20240524.0': + '@cloudflare/workerd-darwin-64@1.20240925.0': optional: true - '@cloudflare/workerd-darwin-arm64@1.20240524.0': + '@cloudflare/workerd-darwin-arm64@1.20240925.0': optional: true - '@cloudflare/workerd-linux-64@1.20240524.0': + '@cloudflare/workerd-linux-64@1.20240925.0': optional: true - '@cloudflare/workerd-linux-arm64@1.20240524.0': + '@cloudflare/workerd-linux-arm64@1.20240925.0': optional: true - '@cloudflare/workerd-windows-64@1.20240524.0': + '@cloudflare/workerd-windows-64@1.20240925.0': optional: true + '@cloudflare/workers-shared@0.5.4': + dependencies: + mime: 3.0.0 + zod: 3.23.8 + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -1363,22 +1393,22 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@tootallnate/once@2.0.0': {} '@types/node-forge@1.3.11': dependencies: - '@types/node': 20.14.0 + '@types/node': 22.7.4 - '@types/node@20.14.0': + '@types/node@22.7.4': dependencies: - undici-types: 5.26.5 + undici-types: 6.19.8 a-sync-waterfall@1.0.1: {} @@ -1393,16 +1423,18 @@ snapshots: acorn-globals@7.0.1: dependencies: - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.4 - acorn-walk@8.3.2: {} + acorn-walk@8.3.4: + dependencies: + acorn: 8.12.1 - acorn@8.11.3: {} + acorn@8.12.1: {} agent-base@6.0.2: dependencies: - debug: 4.3.5 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -1427,13 +1459,13 @@ snapshots: asap@2.0.6: {} - async@3.2.5: {} + async@3.2.6: {} asynckit@0.4.0: {} - axios@1.7.2: + axios@1.7.7: dependencies: - follow-redirects: 1.15.6 + follow-redirects: 1.15.9 form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -1469,12 +1501,12 @@ snapshots: camel-case@4.1.2: dependencies: pascal-case: 3.1.2 - tslib: 2.6.2 + tslib: 2.7.0 capnp-ts@0.7.0: dependencies: - debug: 4.3.5 - tslib: 2.6.2 + debug: 4.3.7 + tslib: 2.7.0 transitivePeerDependencies: - supports-color @@ -1511,7 +1543,7 @@ snapshots: compressible@2.0.18: dependencies: - mime-db: 1.52.0 + mime-db: 1.53.0 compression@1.7.4: dependencies: @@ -1566,14 +1598,16 @@ snapshots: dependencies: ms: 2.0.0 - debug@4.3.5: + debug@4.3.7: dependencies: - ms: 2.1.2 + ms: 2.1.3 decimal.js@10.4.3: {} deepmerge@4.3.1: {} + defu@6.1.4: {} + delayed-stream@1.0.0: {} depd@2.0.0: {} @@ -1596,7 +1630,7 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.1.5: {} + dompurify@3.1.7: {} domutils@3.1.0: dependencies: @@ -1608,10 +1642,12 @@ snapshots: ejs@3.1.10: dependencies: - jake: 10.9.1 + jake: 10.9.2 encodeurl@1.0.2: {} + encodeurl@2.0.0: {} + entities@4.5.0: {} esbuild@0.17.19: @@ -1685,7 +1721,7 @@ snapshots: transitivePeerDependencies: - supports-color - follow-redirects@1.15.6: {} + follow-redirects@1.15.9: {} form-data@4.0.0: dependencies: @@ -1739,14 +1775,14 @@ snapshots: hexo-log: 4.1.0 hexo-util: 3.3.0 minimist: 1.2.8 - picocolors: 1.0.1 + picocolors: 1.1.0 resolve: 1.22.8 tildify: 2.0.0 - hexo-feed@1.1.2(chalk@4.1.2)(hexo@7.2.0(chokidar@3.6.0)): + hexo-feed@1.1.2(chalk@4.1.2)(hexo@7.3.0(chokidar@3.6.0)): dependencies: chalk: 4.1.2 - hexo: 7.2.0(chokidar@3.6.0) + hexo: 7.3.0(chokidar@3.6.0) hexo-front-matter@4.2.1: dependencies: @@ -1781,7 +1817,7 @@ snapshots: hexo-log@4.1.0: dependencies: - picocolors: 1.0.1 + picocolors: 1.1.0 hexo-pagination@3.0.0: {} @@ -1791,7 +1827,7 @@ snapshots: hexo-renderer-marked@6.3.0: dependencies: - dompurify: 3.1.5 + dompurify: 3.1.7 hexo-util: 3.3.0 jsdom: 20.0.3 marked: 4.3.0 @@ -1817,7 +1853,7 @@ snapshots: mime: 2.6.0 morgan: 1.10.0 open: 7.4.2 - serve-static: 1.15.0 + serve-static: 1.16.2 transitivePeerDependencies: - supports-color @@ -1828,12 +1864,12 @@ snapshots: camel-case: 4.1.2 cross-spawn: 7.0.3 deepmerge: 4.3.1 - highlight.js: 11.9.0 + highlight.js: 11.10.0 htmlparser2: 9.1.0 prismjs: 1.29.0 strip-indent: 3.0.0 - hexo@7.2.0(chokidar@3.6.0): + hexo@7.3.0(chokidar@3.6.0): dependencies: abbrev: 2.0.0 archy: 1.0.0 @@ -1846,12 +1882,12 @@ snapshots: hexo-util: 3.3.0 js-yaml: 4.1.0 js-yaml-js-types: 1.0.1(js-yaml@4.1.0) - micromatch: 4.0.7 + micromatch: 4.0.8 moize: 6.1.6 moment: 2.30.1 moment-timezone: 0.5.45 nunjucks: 3.2.4(chokidar@3.6.0) - picocolors: 1.0.1 + picocolors: 1.1.0 pretty-hrtime: 1.0.3 resolve: 1.22.8 strip-ansi: 6.0.1 @@ -1862,7 +1898,7 @@ snapshots: transitivePeerDependencies: - chokidar - highlight.js@11.9.0: {} + highlight.js@11.10.0: {} html-encoding-sniffer@3.0.0: dependencies: @@ -1887,14 +1923,14 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.7 transitivePeerDependencies: - supports-color https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -1913,7 +1949,7 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-core-module@2.13.1: + is-core-module@2.15.1: dependencies: hasown: 2.0.2 @@ -1937,9 +1973,9 @@ snapshots: isexe@2.0.0: {} - jake@10.9.1: + jake@10.9.2: dependencies: - async: 3.2.5 + async: 3.2.6 chalk: 4.1.2 filelist: 1.0.4 minimatch: 3.1.2 @@ -1956,7 +1992,7 @@ snapshots: jsdom@20.0.3: dependencies: abab: 2.0.6 - acorn: 8.11.3 + acorn: 8.12.1 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0 @@ -1969,7 +2005,7 @@ snapshots: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.10 + nwsapi: 2.2.13 parse5: 7.1.2 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -1979,7 +2015,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.17.0 + ws: 8.18.0 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -1990,7 +2026,7 @@ snapshots: lower-case@2.0.2: dependencies: - tslib: 2.6.2 + tslib: 2.7.0 magic-string@0.25.9: dependencies: @@ -2000,13 +2036,15 @@ snapshots: micro-memoize@4.1.2: {} - micromatch@4.0.7: + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 mime-db@1.52.0: {} + mime-db@1.53.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 @@ -2019,18 +2057,18 @@ snapshots: min-indent@1.0.1: {} - miniflare@3.20240524.1: + miniflare@3.20240925.0: dependencies: '@cspotcode/source-map-support': 0.8.1 - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.4 capnp-ts: 0.7.0 exit-hook: 2.2.1 glob-to-regexp: 0.4.1 stoppable: 1.1.0 undici: 5.28.4 - workerd: 1.20240524.0 - ws: 8.17.0 + workerd: 1.20240925.0 + ws: 8.18.0 youch: 3.3.3 zod: 3.23.8 transitivePeerDependencies: @@ -2071,8 +2109,6 @@ snapshots: ms@2.0.0: {} - ms@2.1.2: {} - ms@2.1.3: {} mustache@4.2.0: {} @@ -2088,7 +2124,7 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.6.2 + tslib: 2.7.0 node-forge@1.3.1: {} @@ -2102,7 +2138,9 @@ snapshots: optionalDependencies: chokidar: 3.6.0 - nwsapi@2.2.10: {} + nwsapi@2.2.13: {} + + ohash@1.1.4: {} on-finished@2.3.0: dependencies: @@ -2132,7 +2170,7 @@ snapshots: pascal-case@3.1.2: dependencies: no-case: 3.0.4 - tslib: 2.6.2 + tslib: 2.7.0 path-is-absolute@1.0.1: {} @@ -2140,9 +2178,11 @@ snapshots: path-parse@1.0.7: {} - path-to-regexp@6.2.2: {} + path-to-regexp@6.3.0: {} - picocolors@1.0.1: {} + pathe@1.1.2: {} + + picocolors@1.1.0: {} picomatch@2.3.1: {} @@ -2180,11 +2220,11 @@ snapshots: resolve@1.22.8: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - rfdc@1.3.1: {} + rfdc@1.4.1: {} rollup-plugin-inject@3.0.2: dependencies: @@ -2217,7 +2257,7 @@ snapshots: '@types/node-forge': 1.3.11 node-forge: 1.3.1 - send@0.18.0: + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -2235,12 +2275,12 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.15.0: + serve-static@1.16.2: dependencies: - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0 + send: 0.19.0 transitivePeerDependencies: - supports-color @@ -2286,7 +2326,7 @@ snapshots: stylus@0.62.0: dependencies: '@adobe/css-tools': 4.3.3 - debug: 4.3.5 + debug: 4.3.7 glob: 7.2.3 sax: 1.3.0 source-map: 0.7.4 @@ -2328,14 +2368,23 @@ snapshots: dependencies: punycode: 2.3.1 - tslib@2.6.2: {} + tslib@2.7.0: {} - undici-types@5.26.5: {} + ufo@1.5.4: {} + + undici-types@6.19.8: {} undici@5.28.4: dependencies: '@fastify/busboy': 2.1.1 + unenv-nightly@2.0.0-20240919-125358-9a64854: + dependencies: + defu: 6.1.4 + ohash: 1.1.4 + pathe: 1.1.2 + ufo: 1.5.4 + universalify@0.2.0: {} unpipe@1.0.0: {} @@ -2363,7 +2412,7 @@ snapshots: hexo-log: 4.1.0 is-plain-object: 5.0.0 jsonparse: 1.3.1 - rfdc: 1.3.1 + rfdc: 1.4.1 through2: 4.0.2 webidl-conversions@7.0.0: {} @@ -2383,29 +2432,32 @@ snapshots: dependencies: isexe: 2.0.0 - workerd@1.20240524.0: + workerd@1.20240925.0: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20240524.0 - '@cloudflare/workerd-darwin-arm64': 1.20240524.0 - '@cloudflare/workerd-linux-64': 1.20240524.0 - '@cloudflare/workerd-linux-arm64': 1.20240524.0 - '@cloudflare/workerd-windows-64': 1.20240524.0 + '@cloudflare/workerd-darwin-64': 1.20240925.0 + '@cloudflare/workerd-darwin-arm64': 1.20240925.0 + '@cloudflare/workerd-linux-64': 1.20240925.0 + '@cloudflare/workerd-linux-arm64': 1.20240925.0 + '@cloudflare/workerd-windows-64': 1.20240925.0 - wrangler@3.58.0: + wrangler@3.79.0: dependencies: - '@cloudflare/kv-asset-handler': 0.3.2 + '@cloudflare/kv-asset-handler': 0.3.4 + '@cloudflare/workers-shared': 0.5.4 '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) blake3-wasm: 2.1.5 chokidar: 3.6.0 esbuild: 0.17.19 - miniflare: 3.20240524.1 + miniflare: 3.20240925.0 nanoid: 3.3.7 - path-to-regexp: 6.2.2 + path-to-regexp: 6.3.0 resolve: 1.22.8 resolve.exports: 2.0.2 selfsigned: 2.4.1 source-map: 0.6.1 + unenv: unenv-nightly@2.0.0-20240919-125358-9a64854 + workerd: 1.20240925.0 xxhash-wasm: 1.0.2 optionalDependencies: fsevents: 2.3.3 @@ -2416,7 +2468,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.17.0: {} + ws@8.18.0: {} xml-name-validator@4.0.0: {} From 05ca30e1efea0eafc3951b59689cd18455f1dc0c Mon Sep 17 00:00:00 2001 From: thislight Date: Thu, 3 Oct 2024 15:00:34 +0800 Subject: [PATCH 2/4] added draft http101 --- source/_drafts/http101.md | 980 ++++++++++++++++++ source/_drafts/http101/devtools-in-menu.png | 3 + .../http101/devtools-inspect-request.png | 3 + source/_drafts/http101/devtools.png | 3 + 4 files changed, 989 insertions(+) create mode 100644 source/_drafts/http101.md create mode 100644 source/_drafts/http101/devtools-in-menu.png create mode 100644 source/_drafts/http101/devtools-inspect-request.png create mode 100644 source/_drafts/http101/devtools.png diff --git a/source/_drafts/http101.md b/source/_drafts/http101.md new file mode 100644 index 0000000..5cd6294 --- /dev/null +++ b/source/_drafts/http101.md @@ -0,0 +1,980 @@ +--- +title: 超文本传输协议(HTTP)快速入门 +date: 2023-03-21 +tags: + - 网络 +--- + +- 请求一个页面 +- 用词约定 +- 从零开始的HTTP服务器生涯 +- GET和POST +- 常见HTTP状态码和头简介 +- 从HTTP/1.1到HTTP/3 +- 参考资料和扩展阅读 +- FAQ + + + +## 请求一个页面 + +这篇文章会用到[Curl](https://curl.dev/),Curl是一个用于在命令行中访问URI(Uniform Resource Indicator,统一资源标志符,我们常说的“网址”)的工具。我们用它作为例子,看看一个HTTP客户端如何从服务器获取网页。 + +在大多数Linux发行版中,这个工具都是默认安装的,你可以在终端模拟器中尝试`curl --version`: + +```` +$ curl --version +curl 7.85.0 (x86_64-redhat-linux-gnu) libcurl/7.85.0 OpenSSL/3.0.8 zlib/1.2.12 brotli/1.0.9 libidn2/2.3.4 libpsl/0.21.1 (+libidn2/2.3.3) libssh/0.10.4/openssl/zlib nghttp2/1.51.0 +Release-Date: 2022-08-31 +Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp +Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets +```` + +如果你的系统已经安装了Curl,就会出现和上面类似的输出,而不是一条“找不到”的报错。 + +如果你使用Windows,[Windows 10和Windows 11已经预装Curl](https://curl.se/windows/microsoft.html)。但是在PowerShell的默认环境下,使用`curl`不会使用curl,你需要用`curl.exe`来代替接下来所有命令中的`curl`。(提示:如果你的命令窗口标题有PowerShell字样,说明你可能正在使用PowerShell) + +你可以在命令行窗口中尝试`curl --version`: + +```` +> curl.exe --version +curl 7.79.1 (Windows) libcurl/7.79.1 Schannel +Release-Date: 2021-09-22 +Protocols: dict file ftp ftps http https imap imaps pop3 pop3s smtp smtps telnet tftp +Features: AsynchDNS HSTS IPv6 Kerberos Largefile NTLM SPNEGO SSL SSPI UnixSockets +```` + +如果你正在使用没有预装Curl的Windows,你可以在 [curl.se/windows/](https://curl.se/windows/) 下载。 + +确认Curl可以使用,我们来试试访问`http://example.com` + +```` +$ curl --http1.1 http://example.com -v +* Trying 93.184.216.34:80... +* Trying 2606:2800:220:1:248:1893:25c8:1946:80... +* Immediate connect fail for 2606:2800:220:1:248:1893:25c8:1946: 网络不可达 +* Connected to example.com (93.184.216.34) port 80 (#0) +> GET / HTTP/1.1 +> Host: example.com +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< Age: 106060 +< Cache-Control: max-age=604800 +< Content-Type: text/html; charset=UTF-8 +< Date: Sat, 18 Mar 2023 06:39:37 GMT +< Etag: "3147526947+ident" +< Expires: Sat, 25 Mar 2023 06:39:37 GMT +< Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT +< Server: ECS (sab/5707) +< Vary: Accept-Encoding +< X-Cache: HIT +< Content-Length: 1256 +< + + + + Example Domain + + + + + + + + +
+

Example Domain

+

This domain is for use in illustrative examples in documents. You may use this + domain in literature without prior coordination or asking for permission.

+

More information...

+
+ + +* Connection #0 to host example.com left intact +```` + +在我们使用curl时,我们加上了两个额外参数,一个是`--http1.1`,一个是`-v`。`--http1.1`告诉Curl,我们要求它使用1.1版本的HTTP,否则Curl可能会自动选择其它版本的HTTP;`-v`则让Curl展示更详细一点的信息,包括连接服务器和HTTP请求。 + +让我们来关注``之前以`<`或`>`开头的行: + +```` +> GET / HTTP/1.1 +> Host: example.com +> User-Agent: curl/7.85.0 +> Accept: */* +> + +< HTTP/1.1 200 OK +< Age: 106060 +< Cache-Control: max-age=604800 +< Content-Type: text/html; charset=UTF-8 +< Date: Sat, 18 Mar 2023 06:39:37 GMT +< Etag: "3147526947+ident" +< Expires: Sat, 25 Mar 2023 06:39:37 GMT +< Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT +< Server: ECS (sab/5707) +< Vary: Accept-Encoding +< X-Cache: HIT +< Content-Length: 1256 +< +```` + +在这里,`>`的意思是发送到服务器,`<`是从服务器接收。我们向服务器发送的是“请求”(Request),而服务器向我们返回“响应”(Response),一次请求-响应是一次“事务”(Transcation)。Curl执行我们对于`example.com`的请求,首先向服务器发送了以下信息: + +```` +GET / HTTP/1.1 +^^^ ~~~~~~~~~~~~ 请求方法 (Request Method) + ^ ~~~~~~~~~~ 路径(Path) + ^^^^^^^^ ~ 协议(Protocol) +```` + +这一行可以很容易用大白话说明:“使用HTTP/1.1协议获取(GET)路径‘/’”。 + +接下来这一段通常被叫作“HTTP头”: + +```` +Host: example.com +User-Agent: curl/7.85.0 +Accept: */* +```` + +每一行意味着一个键值对,用`Key: Value`的格式。`Host: example.com`告诉服务器我们想请求的域名是`example.com`,其它两个头我们留到稍后再了解。 +这些“HTTP头”跟第一行加起来组成了“HTTP请求头部”的数据。但是还没完,这里还有一个空行: + +```` + +```` + +它标志着“HTTP请求头部”的结束。 + +另外,还有一个要注意的地方:HTTP的换行是CRLF格式,也就是大多数编程语言中的`\r\n`字符串转义,这个稍后我们自己编写HTTP服务器的时候才会变得重要。 + +我们接着来看看服务器发回给我们的数据,它跟请求有两点不同,第一个是: + +```` +HTTP/1.1 200 OK +^^^^^^^^ ~~~~~~~~~~ 协议 + ^^^ ~~~~~~ 状态码(Status Code) + ^^ ~~~ 状态信息 +```` + +通常,状态码和状态信息一一对应,或者说,状态信息只是在解释状态码。在这里,`200`可以说是“成功”的意思。 + +第二个是,它在空行之后,即”HTTP响应头部“结束之后,带上了我们请求的页面`/`: + +```` + + +... +```` + +如果你的电脑上有[telnet](https://en.wikipedia.org/wiki/Telnet),你可以亲手发送一个HTTP请求!执行`telnet example.com 80`来连接到服务器的80端口。 + +```` +$ telnet example.com 80 +Trying 93.184.216.34... +Connected to example.com. +Escape character is '^]'. +```` + +输入以下内容。就是上面HTTP请求的简化版,HTTP头只留下`Host`。 + +```` +GET / HTTP/1.1 +Host: example.com + +```` + +别忘了空行!不必担心换行格式,telnet默认发送CRLF形式的换行,这跟HTTP的要求是一样的。 + +## 用词约定 + +### 套接字和Socket + +Socket是一种对于网络逻辑接口的抽象,本文使用Socket一词。在有些资料中这个词被翻译成“套接字”。 + +### HTTP头部、头 + +为了尊重使用习惯,本文中的HTTP头和HTTP头部所指的内容是不一样的。为了方便理解,假设有这样的HTTP请求: + +```` +GET /path/to/page HTTP/1.1 +Host: example.com +User-Agent: sample-client/1 + +```` + +HTTP头部指的是结束标志(空行)以及之前所有部分,而HTTP头指的是第一行之后、结束标志之前的键值对部分。 + +### TCP + +本文中的TCP是Transmission Control Protocol的缩写,中文翻译是“传输控制协议”。 + +## 从零开始的HTTP服务器生涯 + +我们已经了解HTTP请求和响应的结构,从这里开始,我们将使用Python来编写一个简单的HTTP服务器。如果你不会Python,可以看看[Python文档网站上的教程](https://docs.python.org/zh-cn/3/tutorial/index.html)。我们只会使用许多编程语言都具备的概念和特性,用其它编程语言实现应该也不会有太大障碍。 + +另外提一句,所有代码都在Fedora 37上使用Python 3.11运行。 + +你可能没有从零开始写HTTP服务器的经验,让我来为我们将要完成的代码划分几个部分: + +1. 监听网络端口,等待连接 +2. 从连接读取HTTP请求 +3. 生成HTTP响应并返回 + +````python +def main(): + pass + +if __name__ == "__main__": + main() +```` + +### 等待客户端连接 + +HTTP运行在TCP之上,我们需要打开一个TCP端口,等待客户端创建TCP连接。 + +我们将使用Python标准库里的[socket](https://docs.python.org/zh-cn/3/library/socket.html),这个模块应该已经随着你的Python安装了。没用过Socket API也没关系,我将对我们要进行的操作做一个简单介绍。要在一个端口上等待客户端连接,我们大概要进行以下工作: + +1. 创建一个Socket(使用socket库里的`socket`类),配置使用TCP(HTTP在TCP上传输) +2. 把一个端口bind到这个Socket上(使用Socket的`bind`方法) +3. 将这个Socket设置为监听(使用Socket的`listen`方法) + +完成这些工作后,我们就可以使用Socket的`accept`方法等待一个连接。 + +在main里添加代码之后: + +````python +from socket import socket, AF_INET, SOCK_STREAM # 导入我们需要的值 + +def main(): + # SOCK_STREAM配合AF_INET就是在IPv4上使用TCP的意思,HTTP在TCP上传输 + with socket(AF_INET, SOCK_STREAM) as server_port: # with会在运行离开这块代码之后关闭这个Socket + server_port.bind(("127.0.0.1", 8989)) # 将127.0.0.1:8989这个地址绑定到这个Socket上 + server_port.listen() # 设置监听 + conn, addr = server_port.accept() # conn是相应连接的Socket, addr是地址 +```` + +要实验我们的代码是否有效,我们可以编写一个供telnet使用的Echo服务器。Echo服务器,回声服务器,顾名思义就是一个原样输出收到内容的服务器。以下是包含了这个Echo服务器逻辑的文件内容。 + +````python +from socket import socket, AF_INET, SOCK_STREAM + +def main(): + with socket(AF_INET, SOCK_STREAM) as server_port: + server_port.bind(("127.0.0.1", 8989)) + server_port.listen() + conn, addr = server_port.accept() + with conn: # 运行离开这个with代码块时,with会帮我们关闭这个Socket + print(f"Accepted {addr}") + conn.settimeout(8) + while True: + data = conn.recv(4096) # 接收最多4096 bytes的数据 + if data != b"\r\n": # 如果是一个空行(只包含CRLF换行),就退出循环 + conn.send(data) + else: + break + +if __name__ == "__main__": + main() +```` + +假设这个Python脚本的名字叫作`server.py`,我们执行`python server.py`(因为需要Python 3,有些机器可能要使用`python3`代替`python`)。 + +```` +python server.py + +```` + +现在我们可以使用`telnet localhost 8989`连接到我们的Echo服务器。输入一些内容、换行,看看服务器返回的内容。最后用空行退出。 + +```` +$ telnet localhost 8989 +Trying ::1... +telnet: connect to address ::1: Connection refused +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. +Hello! +Hello! + +Connection closed by foreign host. +```` + +### 读取HTTP请求 + +先让我们来总结一下我们之前了解到的内容:一次HTTP事务包括请求和响应,请求和响应发送的数据叫作HTTP信息(Message)。 + +一个信息包括两部分:头部和主体,一个空行代表头部结束。HTTP请求和响应的头部只有第一行的格式不同,第一行之后都是由键值对组成的HTTP头。 + +让我们新添加两个函数,分别用于读取HTTP请求和处理HTTP请求: + +````python +def read_http_request(conn): + pass + +def handle_http_request(conn): + pass +```` + +> HTTP在TCP连接上传输,TCP提供面向字节流传输,意思是:在同一个Socket上,无论你如何发送数据,TCP都将它们视为同一串数据,不保证它们分开到达目标(你可以试试搜索“TCP粘包问题”,这是一个存在又不存在的问题)。当然,TCP保证数据收到的顺序和发送的顺序一致。这也可以帮助你理解HTTP为何要如此设计。 + +为了简化代码,我们这里将使用比较简单的方法读取HTTP请求,并将其处理成三个返回值:请求方法、路径、头。请求方法和路径是字符串,头是一个Python字典(`dict`),保存HTTP头键值对。跟Python字典类似功能的东西,在其它编程语言中可能更习惯叫"Map"。 + +读取HTTP请求时,我们先原样读取出整个头部,存为字符串再解析。读取头部的函数命名为`read_http_request_header_string`。 + +````python +def read_http_request_header_string(conn: socket): + buffer = bytearray() + while True: + buffer.extend(conn.recv(4096)) + header_length = buffer.find(b"\r\n\r\n") # 两个连在一起的CRLF,第二个CRLF就代表空行 + if header_length != -1: + return buffer[:header_length] +```` + +这样我们就可以在`read_http_request`里使用它了,你可以先试着写一写这个`read_http_request`再看完整代码。 + +逻辑很简单,将读到的字符串按照`\r\n`分开,再分别处理第一行和剩余的行。需要注意的是,我们的`read_http_request_header_string`返回`bytes`(可以简单理解为一块内存),你需要用`decode`方法将其转换为字符串,该方法需要指定一个编码,你可以使用`"ascii"`。 + +````python +def read_http_request(conn: socket): + s = read_http_request_header_string(conn).decode("ascii") + lines = s.split("\r\n") # 按CRLF切开 + # 解析第一行 + fstline = lines[0] + method, path, protocol = fstline.split(" ") # 按空格切开第一行 + if protocol != "HTTP/1.1": + raise RuntimeError("unknown protocol", protocol) + method = method.lower() # 把方法转换成小写 + # 解析头 + header = {} + if len(lines) > 1: + for line in lines[1:]: + key, value = line.split(": ") + header[key] = value + return method, path, header +```` + +### 生成HTTP响应头部 + +相对于读取请求,生成响应要简单多了,只需要按照格式拼装信息: + +````python +STATUS_MESSAGES = { + 200: "OK", + 400: "Bad Request", + 404: "Not Found", + 500: "Server Error", +} + +def build_http_response_header(status_code, headers): + lines = [ + f"HTTP/1.1 {status_code} {STATUS_MESSAGES[status_code]}" + ] + for key, value in headers: + lines.append(f"{key}: {value}") + lines.append("") # 别忘了加空行 + lines.append("") + return '\r\n'.join(lines).encode('ascii') +```` + +请注意,这里为了增加空行,使用两次`lines.append("")`增加了两个空字符串。因为`'\r\n'.join`只在两个字符串中间增加分隔符`"\r\n"`。 + +举个例子:我们有`"A"`、`"B"`两个字符串,每个字符串一行。如果我们不增加空字符串,`'\r\n'.join`只会在A和B之间插入一个换行,结果是`"A\r\nB"`。 + +如果我们只在列表末尾增加一个空字符串,那么最后就只会是 + +```` +"A\r\nB\r\n" + ^ 新增加的空字符串 +```` + +,只是在B和增加的空字符串中间增加了一个换行;如果需要单独的空行,还需要一个额外空字符串,在第一个空字符串和第二个空字符串之间再插入一个换行,才会变为我们需要的`"A\r\nB\r\n\r\n"`。 + +### 处理HTTP请求 + +接下来我们正式在`handle_http_request`中处理HTTP请求。首先我们读取HTTP请求,如果请求出错,我们返回400 Bad Request。 + +````python +def handle_http_request(conn: socket): + try: + method, path, headers = read_http_request(conn) + except Exception as e: + conn.send(build_http_response_header(400, [])) + print(f"- - 400 {STATUS_MESSAGES[400]}") + raise e # 把错误重新抛出,方便你看错误堆栈 +```` + +然后我们检查方法是否是`get`,路径是否是`/`或者`/index.html`,满足条件的话我们就返回200 OK和一段HTML内容: + +````python + if method == "get" and (path == "/" or path == "/index.html"): + headers = [ + ("Charset", "UTF-8"), + ("Content-Length",str(len(DEFAULT_PAGE_HTML))), + ("Connection", "close"), + ] + conn.send(build_http_response_header(200, headers)) + conn.send(DEFAULT_PAGE_HTML) + print(f"{method.upper()} {path} 200 {STATUS_MESSAGES[200]}") +```` + +你发现我们在这里设置了三个头,一个是`Charset`,我们将它设置为`UTF-8`,这是提示客户端,我们的内容使用UTF-8编码。我们的`DEFAULT_PAGE_HTML`定义如下: + +````python +DEFAULT_PAGE_HTML = """ + + + + Default Page + + +

Hello World!

+ +""".encode("utf-8") +```` + +这里我们使用`.encode("utf-8")`将字符串转换为`bytes`。关于`decode`、`encode`方法和编解码可以阅读Python文档相应页面。 + +另一个头是`Content-Length`,这个头指定了HTTP主体(Body)的长度,也就是我们返回HTML的长度,接收端会根据这个长度读取主体内容。长度必须是十进制的字节数量。你可以看到我们在这里给了`DEFAULT_PAGE_HTML`的长度。 + +最后一个是`Connection: close`,这样设置意味着:无论是客户端还是服务器都可以在响应完成之后关闭相应连接。设置这个头是因为我们的服务器在响应HTTP请求之后就会自动关闭连接,但是HTTP/1.1默认不会关闭。 + +最后,如果不满足条件,返回404 Not Found,意味着这个页面没有找到。 + +````python + else: + conn.send(build_http_response_header(404, [])) + print(f"{method.upper()} {path} 404 {STATUS_MESSAGES[404]}") +```` + +最后只要在我们的main函数使用这个`handle_http_request`就好了。 + +````python +def main(): + with socket(AF_INET, SOCK_STREAM) as server_port: + server_port.bind(("127.0.0.1", 8989)) + server_port.listen() + conn, addr = server_port.accept() + with conn: + conn.settimeout(8) + handle_http_request(conn) +```` + +使用`python server.py`启动之后,使用`curl http://localhost:8989 -v`来看看效果。 + +```` +$ curl http://localhost:8989 -v +* Trying 127.0.0.1:8989... +* Connected to localhost (127.0.0.1) port 8989 (#0) +> GET / HTTP/1.1 +> Host: localhost:8989 +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< Charset: UTF-8 +< Content-Length: 175 +< Connection: close +< + + + + + Default Page + + +

Hello World!

+ +* Closing connection 0 +⏎ +```` + +我们的代码处理了一个新连接就退出了,如果要让它持续处理新连接,只要从`accept`方法开始放在死循环里就行: + +````python +def main(): + with socket(AF_INET, SOCK_STREAM) as server_port: + server_port.bind(("127.0.0.1", 8989)) + server_port.listen() + while True: + conn, addr = server_port.accept() + with conn: + conn.settimeout(8) + handle_http_request(conn) +```` + +[完整代码请见Gist](https://gist.github.com/thislight/b0aaa23f247d62bb6d78cc650732b214) + +### 使用浏览器开发者工具 + +浏览器开发者工具是调试你Web程序的利器!它就内置在你的浏览器里,包含多个有用的工具,快捷键通常是F12。接下来,我将演示用Firefox的开发者工具查看我们的服务器响应信息。 + +先打开一个新标签页。你有两种方法打开开发者工具:一种是在浏览器菜单里点击“更多工具”, 点击“Web开发者工具”。 + +![“更多工具”里存在“Web开发者工具”这一项](devtools-in-menu.png) + +另一种打开方法是按键盘上的F12。 + +打开后,你就会看到开发者工具,选择“网络(Network)”页面。 + +![“Web开发者工具”显示在浏览器窗口内部的右边,选择了“网络”页面](devtools.png) + +接下来,在地址栏中输入我们服务器的地址`http://localhost:8989`、确认访问,就可以在这个页面下看到浏览器产生的请求。点击单个项目可以展开详细信息。 + +![“网络”页面的列表出现了浏览器产生的请求,显示了访问主页请求的详细信息](devtools-inspect-request.png) + +## GET和POST + +我们的服务器应该能够根据用户的输入进行不同的操作,不然为什么不直接提供一个HTML就好了呢?正好,HTTP给我们提供了一个工具:路径里面可以携带一个叫查询(Query)的部分,`?`之后就是我们的查询,查询里面可以直接携带URI未保留字符,这些字符在URI中没有特殊意义。比如在`/?something`中,`something`就是我们的查询。 + +> 查询其实是属于URI的一部分。我们在这里说查询是在“路径”中,这里的“路径”指的是前面我们所提到HTTP请求格式中的“路径”。 + +但是有一些字符不能直接放进URI里,因为它们是URI的保留字符,比如`@`,这时候就需要转义(Escape)这些字符。我们在这使用的编码叫[URL encoding(URL编码,也可以叫percent encoding,百分号编码)](https://en.wikipedia.org/wiki/URL_encoding)。 + +比如说我们要在路径里携带`example@example.com`,比如`/?email=example@example.com`,我们必须将其编码成`/?email=example%40example.com`。 + +虽然查询里面可以携带任何允许的字符,但是使用类似上面的格式仍然是比较常用的做法。也就是`key=val`代表键key的值是val,在上面就是`email`的值是`example@example.com`。如果要携带多对键值,可以用`&`从中间区分,比如`email=example%40example.com&nuke=1`,就有`email`和`nuke`两对键值。 + +接下来,我们一起改写上面的服务器,让它能够接受查询字符串,比如说在访问`/?name=HTTP`的时候可以返回"Hello HTTP!"。 + +### 在GET请求中接受用户输入 + +我们要修改以下部分: + +- 修改访问路径匹配。我们之前使用`path == "/"`来确认正在访问的路径,这样的话`/?name=HTTP`就不能访问到`/`了。 +- 处理查询里的键值对。我们需要从请求中的路径取得访问路径和查询,将查询里的键值对解析成我们需要的数据结构,在这里是字典。 +- 让`DEFAULT_HTML_PAGE`的"World"可以自定义。我们在这里使用Python的`str.format`方法。 + +首先,我们需要从请求中的路径取得访问路径和查询: + +````python +def read_path(path: str): # 返回访问路径和查询 + parts = path.split("?", maxsplit=1) + if len(parts) > 1: + return parts[0], parts[1] + else: + return parts[0], "" + +def handle_http_request(conn: socket): + # ... + onlypath, query = read_path(path) + if onlypath == "/" and method == "get": + # ... +```` + +然后,我们需要将查询里的键值对解析成字典: + +````python +def parse_query(q: str): + pairs_str = q.split("&") + pairs = {} + for s in pairs_str: + if s: # 确保不是空字符串 + k, v = s.split("=") + pairs[unquote_plus(k)] = unquote_plus(v) + return pairs +```` + +在这里,我们使用了`urllib.parse`的`unquote_plus`函数,你需要在文件顶部引入它: + +````python +from urllib.parse import unquote_plus +```` + +我们使用这个函数将URL编码的字符串转换为普通字符串。 + +接下来,我们要让`DEFAULT_HTML_PAGE`可以接受自定义名字。因为`bytes`对象没有`format`方法,我们得去掉`.encode`,改在自定义名字之后完成。 + +````python +DEFAULT_PAGE_HTML = """ + + + + Default Page + + +

Hello {name}!

+ +""" +```` + +当我们使用`format`方法时,就可以替换掉`{name}`的内容,生成实际要发送给客户端的页面。这时`DEFAULT_PAGE_HTML`就被叫作“模板”,生成这个页面的过程叫“渲染”。 + +````python + if onlypath == "/" and method == "get": + query_dict = parse_query(query) # 解析查询键值对 + # 渲染页面 + content = DEFAULT_PAGE_HTML.format( + # 检查键值对中有没有键name,有并且值不为空的话就以其值替换{name},否则用World替换。 + name=(query_dict["name"] if query_dict.get("name") else "World") + ).encode("utf-8") + headers = [ + ("Charset", "UTF-8"), + ("Content-Length",str(len(content))), # 主体长度等于实际内容长度 + ("Connection", "close"), + ] + conn.send(build_http_response_header(200, headers)) + conn.send(content) # 发送渲染出来的页面 + # ... +```` + +启动你的服务器。用`curl http://localhost:8989?name=HTTP -v`试试。 + +```` +$ curl http://localhost:8989?name=HTTP -v +* Trying 127.0.0.1:8989... +* Connected to localhost (127.0.0.1) port 8989 (#0) +> GET /?name=HTTP HTTP/1.1 +> Host: localhost:8989 +> User-Agent: curl/7.85.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< Charset: UTF-8 +< Content-Length: 174 +< Connection: close +< + + + + + Default Page + + +

Hello HTTP!

+ +* Closing connection 0 +⏎ +```` + +你也可以用浏览器看看。修改一下`name`的值,看看给出什么结果。如果出现bug,尝试自己修一修。 + +修改一下模板,就可以直接在网页里面使用这个参数了。 + +````python +DEFAULT_PAGE_HTML = """ + + + + Default Page + + +

Hello {name}!

+
+ + +
+ +""" +```` + +HTML不在本文范围,敬请参阅MDN Web Docs相关页面。 + +[完整代码请见Gist](https://gist.github.com/thislight/d32a565609ee1f2379fc9b3af1d87012) + +### HTTP POST + +目前为止,我们都在使用HTTP的GET方法。另一个常用的HTTP方法是POST,为什么我们需要它呢? + +- HTTP请求可以跟响应一样携带主体。GET请求不可以携带主体,但POST请求可以。放在路径里的数据经过编码后体积可能大幅增加;主体里的数据格式并没有规定,可以不用编码。 +- 理论上HTTP请求头部的路径可以无限长,但是客户端或者服务器可能会限制路径最大长度。 +- 在浏览器中,路径会被记录在浏览历史里,放在主体里的数据一般不会被记录。 + +如果我们使用HTML的form元素进行请求,POST请求主体使用的格式与我们之前查询键值对的格式相同。键是form元素内input元素name属性的值。 + +> 绝大部分情况下,你应该用GET方法展示数据、POST方法只用来记录数据。在刷新POST方法返回的页面时,浏览器需要重新提交请求,并会询问用户是否要这样做(因为这样做很可能会导致不需要的副作用)。大部分情况下,这不是用户想体验的麻烦。 +> 如果你使用HTML的form直接处理POST提交,你可以在处理POST方法完成后返回[HTTP 303 See Other状态码](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303)并在`Location`头中指定重新请求的地址,让浏览器用GET方法重新请求并展示相应页面。 +> 我们接下来就是采用类似方法。 + +让我们来改写我们之前的服务器,让它可以接受POST方法的`default_name`参数,并将它设置为默认名字(当没有name参数时使用的名字)。 + +先添加一个全局变量: + +````python +global default_name +default_name = "World" +```` + +这是个比较大的改写,首先我们要先改写读取HTTP请求的部分,让它支持读取主体。然后我们要使用一个全局变量保存我们对默认名字的修改。 + +````python +def read_http_request_header_string(conn: socket): + buffer = bytearray() + while True: + buffer.extend(conn.recv(4096)) + header_length = buffer.find(b"\r\n\r\n") + if header_length != -1: + return buffer[:header_length], buffer[header_length+4:] + +def read_http_request(conn: socket): + s, rest = read_http_request_header_string(conn) + s = s.decode("ascii") + lines = s.split("\r\n") + # 解析第一行 + fstline = lines[0] + method, path, protocol = fstline.split(" ") + if protocol != "HTTP/1.1": + raise RuntimeError("unknown protocol", protocol) + method = method.lower() + # 解析头 + header = {} + if len(lines) > 1: + for line in lines[1:]: + key, value = line.split(": ") + header[key] = value + return method, path, header, rest + +def read_http_request_body(conn: socket, length: int, buffer: bytearray): + while len(buffer) < length: + buffer.extend(conn.recv(4096)) + return buffer[:length] +```` + +我们在原先`read_http_request`函数的基础上增加`read_http_request_body`,并且让`read_http_request`返回多读的数据(在`\r\n\r\n`之后的数据)。在`handle_http_request`,我们这样读取主体: + +````python +def handle_http_request(conn: socket): + global default_name + try: + method, path, headers, rest = read_http_request(conn) + if "Content-Length" in headers: + body = read_http_request_body(conn, int(headers["Content-Length"]), rest) + # 我们在这只支持Content-Length,不支持Transfer-Encoding: chunked + else: + body = bytearray() + except Exception as e: + conn.send(build_http_response_header(400, [])) + print(f"- - 400 {STATUS_MESSAGES[400]}") + raise e + # ... +```` + +接下来我们要改动检查路径和方法的代码: + +````python + onlypath, query = read_path(path) + if onlypath == "/": + if method == "get": + query_dict = parse_query(query) + # 渲染页面 + content = DEFAULT_PAGE_HTML.format( + name=(query_dict["name"] if query_dict.get("name") else default_name) + # 这里引用全局变量default_name的值,而不是硬编码"World" + ).encode("utf-8") + headers = [ + ("Charset", "UTF-8"), + ("Content-Length",str(len(content))), + ("Connection", "close"), + ] + conn.send(build_http_response_header(200, headers)) + conn.send(content) + print(f"{method.upper()} {path} 200 {STATUS_MESSAGES[200]}") + return # 响应完成,直接返回 + elif method == "post": # 处理POST请求 + form_data_s = body.decode("utf-8") + form_dict = parse_query(form_data_s) + if form_dict.get("default_name"): + default_name = form_dict["default_name"] + conn.send(build_http_response_header(303, [ + ("Connection", "close"), + ("Location", "."), + ])) # 让浏览器重新用GET请求并展示当前页面,Location是“.” + print(f"{method.upper()} {path} 303 {STATUS_MESSAGES[303]}") + return # 响应完成,直接返回 + conn.send(build_http_response_header(404, [])) # 没有匹配的路径或者方法,返回404 + print(f"{method.upper()} {path} 404 {STATUS_MESSAGES[404]}") +```` + +最后,改动一下模板,这样我们就可以直接使用这个参数了: + +````python +DEFAULT_PAGE_HTML = """ + + + + Default Page + + +

Hello {name}!

+
+ + +
+

Default Name

+
+ + +
+ +""" +```` + +[完整代码请见Gist](https://gist.github.com/thislight/b69161000e5ea2904057a49f16b5ac8b) + +### 再进一步 + +我们的代码尚不完善,你可以试试按以下方向改进。 + +#### 处理并发连接 + +当前的代码只能依照顺序一个一个处理连接,你使用多线程或者Python的asyncio让我们的服务器能够并发处理连接。 + +参考资料: + +- [Python文档:threading模块](https://docs.python.org/zh-cn/3/library/threading.html#module-threading) +- [Python文档:asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html#module-asyncio) + +#### 连接复用 + +当前代码在回应请求之后就直接关闭连接,你可以支持HTTP/1.1式的连接复用来提高连接使用效率。 + +参考资料: + +- [MDN Web Docs:HTTP Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive) +- [MDB Web Docs:HTTP Connection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection) + +#### 增强兼容性 + +我们目前的代码作了一些假设,你可以改进代码以提高对不同客户端的兼容性。 + +参考资料: + +- [MDN Web Docs: HTTP Transfer-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding) + +#### 包装和抽象 + +你可以将HTTP服务器包装起来,甚至进一步包装成可以使用的框架,为用户提供可用的API。例如: + +````python +from mymodule import MyServer + +def handle_index(request): + return request.ok(template="index.html") + +if __name__ == "__main__": + server = MyServer({ + "/": handle_index + }) + server.run() +```` + +你还可以试试阅读[Tornado的代码](https://github.com/tornadoweb/tornado),这是一个Python异步Web框架。 + +#### 支持Cookie + +HTTP是一个无状态协议:不同的响应-请求之间没有联系。HTTP Cookie是一项在不同响应-请求之间保留数据的技术。设计一个需要使用Cookie的功能,并在我们的HTTP服务器中实现它。 + +参考资料: + +- [MDN Web Docs:Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) + +## 常见HTTP状态码和头简介 + +我们已经明白了HTTP请求-响应的大致框架,接下来我们要往里面补充一些内容。这里简单列出一些常见HTTP状态码和HTTP头。 + +要进一步了解HTTP的详细信息,可以访问[MDN Web Docs上的HTTP页面](https://developer.mozilla.org/en-US/docs/Web/HTTP)。 + +### 常见HTTP状态码 + +HTTP状态码按信息类别组织: + +- `1XX` 继续 +- `2XX` 成功 +- `3XX` 跳转 +- `4XX` 客户端错误 +- `5XX` 服务器错误 + +比较常见的状态码: + +- `200 OK` 请求成功 +- `204 No Content` 请求成功,但是没有响应主体 +- `303 See Other` 要求客户端以GET请求访问`Location`头中的地址,这个跳转是暂时的 +- `304 Not Modified` 资源没有改变,这个跟条件请求(Conditional Requests)和HTTP缓存相关。 +- `400 Bad Request` 请求有错误 +- `401 Unauthorized` 未验证的请求 +- `403 Forbidden` 请求被服务器拒绝 +- `404 Not Found` 找不到资源 +- `500 Internal Server Error` 内部服务器错误 +- `504 Gateway Timeout` 网关超时 + +### `Content-Length`和`Transfer-Encoding: chunked` + +我们在前面已经了解到`Content-Length`的值是主体长度,接收者可以利用这个主体长度读取主体。HTTP同样也提供了在主体长度不确定时的传输方法,要使用这个传输方法,将头`Transfer-Encoding`的值设置为`chunked`。指定这个传输方法时不再需要设置`Content-Length`。 + +顾名思义,这种方法是“按块传输”,主体可以包含一个或多个块。每个块以十六进制的块大小开头,加上`\r\n`,再加上块内容。发送结束时,发送一个大小为0的块来标识发送已结束。 + +### `Host` + +这个头用于给服务器提供访问的域名。 所有请求都应该带上这个头。 + +### `User-Agent` + +代理用户访问网站的客户端就叫做"User Agent",用户代理。这个头可以给服务器提供客户端信息,但是正在被逐步淘汰,取而代之的是Client Hint标准。 + +### `Accept` + +客户端可以接受的数据类型。 + +### `Accept-Encoding` + +客户端可以接受的数据编码,通常是压缩编码。 + +### `Accept-Language` + +客户端偏好的语言。 + +### `Location` + +在一些状态码中标识下一个目标的位置。 + +## 从HTTP/1.1到HTTP/3 + +本文之前内容主要基于HTTP/1.1。如今,HTTP已经改进出HTTP/2和HTTP/3。这些新版本协议更加高效,更加适合我们当下的使用场景。虽然HTTP/2和HTTP/3与HTTP/1.1相比变化很大,但是基本概念并没有什么变动。 + +敬请阅读:[Web.dev: Introduction to HTTP/2](https://web.dev/performance-http2/)。 + +敬请阅读:[Couldflare: What is HTTP/3?](https://www.cloudflare.com/learning/performance/what-is-http3/)。 + +## 参考资料和扩展阅读 + +- [MDN Web Docs: HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) +- [Python Docs: socket](https://docs.python.org/zh-cn/3/library/socket.html) +- [Wikipedia: URL encoding](https://en.wikipedia.org/wiki/URL_encoding) +- [Wikipedia: Telnet](https://en.wikipedia.org/wiki/Telnet) + +### HTTP有关的内容太多了,我记不住 + +你不需要记住全部内容,只要大概看看知道能这么做就可以了。人类记忆过程中,忘记是一个正常阶段,不清楚的地方直接查资料就可以了。理论物理学家也不需要记得光在真空中传播的速度。 diff --git a/source/_drafts/http101/devtools-in-menu.png b/source/_drafts/http101/devtools-in-menu.png new file mode 100644 index 0000000..58ac692 --- /dev/null +++ b/source/_drafts/http101/devtools-in-menu.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3bb307721b1745c82ce498001996bf6892461b61aed1de19b8f9e191c93325f +size 96806 diff --git a/source/_drafts/http101/devtools-inspect-request.png b/source/_drafts/http101/devtools-inspect-request.png new file mode 100644 index 0000000..20a3803 --- /dev/null +++ b/source/_drafts/http101/devtools-inspect-request.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2dbf027f8b7c6a88f48d2b5e1ebf58f012e5ad78bf9a9da57a91aa70b7a5827 +size 311125 diff --git a/source/_drafts/http101/devtools.png b/source/_drafts/http101/devtools.png new file mode 100644 index 0000000..818065e --- /dev/null +++ b/source/_drafts/http101/devtools.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2905e16453a345594f7648daedae09c73b0864c2052089dc091dd697c2f996ad +size 115091 From 3825011bbf07b2d4789ceff6e6b8b1cd9cef5fe9 Mon Sep 17 00:00:00 2001 From: thislight Date: Thu, 3 Oct 2024 15:04:50 +0800 Subject: [PATCH 3/4] upgrade buck --- themes/hexo-theme-buck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/hexo-theme-buck b/themes/hexo-theme-buck index 3534fd0..b38efd3 160000 --- a/themes/hexo-theme-buck +++ b/themes/hexo-theme-buck @@ -1 +1 @@ -Subproject commit 3534fd07c303322d6e8dfc805457553990db6753 +Subproject commit b38efd3f7db191477c6e1c3034ed82dcdeeba84a From 2c2ea539b193a50923f8c9c528693122a7b05e1c Mon Sep 17 00:00:00 2001 From: thislight Date: Fri, 4 Oct 2024 14:09:28 +0800 Subject: [PATCH 4/4] added http101 --- source/{_drafts => _posts}/http101.md | 157 +++++------------- .../http101/devtools-in-menu.png | 0 .../http101/devtools-inspect-request.png | 0 .../{_drafts => _posts}/http101/devtools.png | 0 4 files changed, 44 insertions(+), 113 deletions(-) rename source/{_drafts => _posts}/http101.md (86%) rename source/{_drafts => _posts}/http101/devtools-in-menu.png (100%) rename source/{_drafts => _posts}/http101/devtools-inspect-request.png (100%) rename source/{_drafts => _posts}/http101/devtools.png (100%) diff --git a/source/_drafts/http101.md b/source/_posts/http101.md similarity index 86% rename from source/_drafts/http101.md rename to source/_posts/http101.md index 5cd6294..d865725 100644 --- a/source/_drafts/http101.md +++ b/source/_posts/http101.md @@ -1,18 +1,17 @@ --- title: 超文本传输协议(HTTP)快速入门 -date: 2023-03-21 tags: - - 网络 + - 网络协议 + - HTTP +date: 2023-03-21 00:00:00 --- -- 请求一个页面 -- 用词约定 -- 从零开始的HTTP服务器生涯 -- GET和POST -- 常见HTTP状态码和头简介 -- 从HTTP/1.1到HTTP/3 -- 参考资料和扩展阅读 -- FAQ + +- [请求一个页面](#请求一个页面) +- [用词约定](#用词约定) +- [从零开始的HTTP服务器生涯](#从零开始的HTTP服务器生涯) +- [GET和POST](#GET和POST) +- [参考资料和扩展阅读](#参考资料和扩展阅读) @@ -243,7 +242,7 @@ HTTP头部指的是结束标志(空行)以及之前所有部分,而HTTP头 我们已经了解HTTP请求和响应的结构,从这里开始,我们将使用Python来编写一个简单的HTTP服务器。如果你不会Python,可以看看[Python文档网站上的教程](https://docs.python.org/zh-cn/3/tutorial/index.html)。我们只会使用许多编程语言都具备的概念和特性,用其它编程语言实现应该也不会有太大障碍。 -另外提一句,所有代码都在Fedora 37上使用Python 3.11运行。 +另外提一句,所有代码都在Fedora 37上使用Python 3.11测试,但是程序并没有使用特殊的特性,在其它平台和Python上多半也能正常使用。 你可能没有从零开始写HTTP服务器的经验,让我来为我们将要完成的代码划分几个部分: @@ -715,7 +714,7 @@ HTML不在本文范围,敬请参阅MDN Web Docs相关页面。 如果我们使用HTML的form元素进行请求,POST请求主体使用的格式与我们之前查询键值对的格式相同。键是form元素内input元素name属性的值。 -> 绝大部分情况下,你应该用GET方法展示数据、POST方法只用来记录数据。在刷新POST方法返回的页面时,浏览器需要重新提交请求,并会询问用户是否要这样做(因为这样做很可能会导致不需要的副作用)。大部分情况下,这不是用户想体验的麻烦。 +> 绝大部分情况下,你应该用GET方法展示数据、POST方法只用来记录数据。在刷新POST方法返回的页面时,浏览器需要重新提交请求,并会询问用户是否要这样做(因为这样做可能会导致不需要的副作用)。大部分情况下,这不是用户想体验的麻烦。 > 如果你使用HTML的form直接处理POST提交,你可以在处理POST方法完成后返回[HTTP 303 See Other状态码](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303)并在`Location`头中指定重新请求的地址,让浏览器用GET方法重新请求并展示相应页面。 > 我们接下来就是采用类似方法。 @@ -848,125 +847,56 @@ DEFAULT_PAGE_HTML = """ 我们的代码尚不完善,你可以试试按以下方向改进。 -#### 处理并发连接 +- 处理并发连接 -当前的代码只能依照顺序一个一个处理连接,你使用多线程或者Python的asyncio让我们的服务器能够并发处理连接。 + 当前的代码只能依照顺序一个一个处理连接,你使用多线程或者Python的asyncio让我们的服务器能够并发处理连接。 -参考资料: + 参考资料: -- [Python文档:threading模块](https://docs.python.org/zh-cn/3/library/threading.html#module-threading) -- [Python文档:asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html#module-asyncio) + - [Python文档:threading模块](https://docs.python.org/zh-cn/3/library/threading.html#module-threading) + - [Python文档:asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html#module-asyncio) -#### 连接复用 +- 连接复用 -当前代码在回应请求之后就直接关闭连接,你可以支持HTTP/1.1式的连接复用来提高连接使用效率。 + 当前代码在回应请求之后就直接关闭连接,你可以支持HTTP/1.1式的连接复用来提高连接使用效率。 -参考资料: + 参考资料: -- [MDN Web Docs:HTTP Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive) -- [MDB Web Docs:HTTP Connection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection) + - [MDN Web Docs:HTTP Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive) + - [MDB Web Docs:HTTP Connection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection) -#### 增强兼容性 +- 增强兼容性 -我们目前的代码作了一些假设,你可以改进代码以提高对不同客户端的兼容性。 + 我们目前的代码作了一些假设,你可以改进代码以提高对不同客户端的兼容性。 -参考资料: + - 支持[HTTP Transfer-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding),我们的代码中没有支持这种传输方式 -- [MDN Web Docs: HTTP Transfer-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding) +- 包装和抽象 -#### 包装和抽象 + 你可以将HTTP服务器包装起来,甚至进一步包装成可以使用的框架,为用户提供可用的API。例如: -你可以将HTTP服务器包装起来,甚至进一步包装成可以使用的框架,为用户提供可用的API。例如: + ````python + from mymodule import MyServer -````python -from mymodule import MyServer + def handle_index(request): + return request.ok(template="index.html") -def handle_index(request): - return request.ok(template="index.html") + if __name__ == "__main__": + server = MyServer({ + "/": handle_index + }) + server.run() + ```` -if __name__ == "__main__": - server = MyServer({ - "/": handle_index - }) - server.run() -```` + 你还可以试试阅读[Tornado的代码](https://github.com/tornadoweb/tornado),这是一个Python异步Web框架。 -你还可以试试阅读[Tornado的代码](https://github.com/tornadoweb/tornado),这是一个Python异步Web框架。 +- 支持Cookie -#### 支持Cookie + HTTP是一个无状态协议:不同的响应-请求之间没有联系。HTTP Cookie是一项在不同响应-请求之间保留数据的技术。设计一个需要保留数据的功能,并在我们的HTTP服务器中实现它。 -HTTP是一个无状态协议:不同的响应-请求之间没有联系。HTTP Cookie是一项在不同响应-请求之间保留数据的技术。设计一个需要使用Cookie的功能,并在我们的HTTP服务器中实现它。 + 参考资料: -参考资料: - -- [MDN Web Docs:Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) - -## 常见HTTP状态码和头简介 - -我们已经明白了HTTP请求-响应的大致框架,接下来我们要往里面补充一些内容。这里简单列出一些常见HTTP状态码和HTTP头。 - -要进一步了解HTTP的详细信息,可以访问[MDN Web Docs上的HTTP页面](https://developer.mozilla.org/en-US/docs/Web/HTTP)。 - -### 常见HTTP状态码 - -HTTP状态码按信息类别组织: - -- `1XX` 继续 -- `2XX` 成功 -- `3XX` 跳转 -- `4XX` 客户端错误 -- `5XX` 服务器错误 - -比较常见的状态码: - -- `200 OK` 请求成功 -- `204 No Content` 请求成功,但是没有响应主体 -- `303 See Other` 要求客户端以GET请求访问`Location`头中的地址,这个跳转是暂时的 -- `304 Not Modified` 资源没有改变,这个跟条件请求(Conditional Requests)和HTTP缓存相关。 -- `400 Bad Request` 请求有错误 -- `401 Unauthorized` 未验证的请求 -- `403 Forbidden` 请求被服务器拒绝 -- `404 Not Found` 找不到资源 -- `500 Internal Server Error` 内部服务器错误 -- `504 Gateway Timeout` 网关超时 - -### `Content-Length`和`Transfer-Encoding: chunked` - -我们在前面已经了解到`Content-Length`的值是主体长度,接收者可以利用这个主体长度读取主体。HTTP同样也提供了在主体长度不确定时的传输方法,要使用这个传输方法,将头`Transfer-Encoding`的值设置为`chunked`。指定这个传输方法时不再需要设置`Content-Length`。 - -顾名思义,这种方法是“按块传输”,主体可以包含一个或多个块。每个块以十六进制的块大小开头,加上`\r\n`,再加上块内容。发送结束时,发送一个大小为0的块来标识发送已结束。 - -### `Host` - -这个头用于给服务器提供访问的域名。 所有请求都应该带上这个头。 - -### `User-Agent` - -代理用户访问网站的客户端就叫做"User Agent",用户代理。这个头可以给服务器提供客户端信息,但是正在被逐步淘汰,取而代之的是Client Hint标准。 - -### `Accept` - -客户端可以接受的数据类型。 - -### `Accept-Encoding` - -客户端可以接受的数据编码,通常是压缩编码。 - -### `Accept-Language` - -客户端偏好的语言。 - -### `Location` - -在一些状态码中标识下一个目标的位置。 - -## 从HTTP/1.1到HTTP/3 - -本文之前内容主要基于HTTP/1.1。如今,HTTP已经改进出HTTP/2和HTTP/3。这些新版本协议更加高效,更加适合我们当下的使用场景。虽然HTTP/2和HTTP/3与HTTP/1.1相比变化很大,但是基本概念并没有什么变动。 - -敬请阅读:[Web.dev: Introduction to HTTP/2](https://web.dev/performance-http2/)。 - -敬请阅读:[Couldflare: What is HTTP/3?](https://www.cloudflare.com/learning/performance/what-is-http3/)。 + - [MDN Web Docs:Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) ## 参考资料和扩展阅读 @@ -975,6 +905,7 @@ HTTP状态码按信息类别组织: - [Wikipedia: URL encoding](https://en.wikipedia.org/wiki/URL_encoding) - [Wikipedia: Telnet](https://en.wikipedia.org/wiki/Telnet) -### HTTP有关的内容太多了,我记不住 +本文内容主要基于HTTP/1.1。如今,HTTP已经改进出HTTP/2和HTTP/3。这些新版本协议更加高效,更加适合我们当下的使用场景。虽然HTTP/2和HTTP/3与HTTP/1.1相比变化很大,但是基本概念并没有什么变动。 -你不需要记住全部内容,只要大概看看知道能这么做就可以了。人类记忆过程中,忘记是一个正常阶段,不清楚的地方直接查资料就可以了。理论物理学家也不需要记得光在真空中传播的速度。 +- [Web.dev: Introduction to HTTP/2](https://web.dev/performance-http2/) +- [Couldflare: What is HTTP/3?](https://www.cloudflare.com/learning/performance/what-is-http3/) diff --git a/source/_drafts/http101/devtools-in-menu.png b/source/_posts/http101/devtools-in-menu.png similarity index 100% rename from source/_drafts/http101/devtools-in-menu.png rename to source/_posts/http101/devtools-in-menu.png diff --git a/source/_drafts/http101/devtools-inspect-request.png b/source/_posts/http101/devtools-inspect-request.png similarity index 100% rename from source/_drafts/http101/devtools-inspect-request.png rename to source/_posts/http101/devtools-inspect-request.png diff --git a/source/_drafts/http101/devtools.png b/source/_posts/http101/devtools.png similarity index 100% rename from source/_drafts/http101/devtools.png rename to source/_posts/http101/devtools.png