├── README.md ├── .github └── workflows │ └── publish.yml ├── UNLICENSE.md ├── thttp.py └── gen.py /README.md: -------------------------------------------------------------------------------- 1 | Just a list of HTTP Status Codes with links to the relevant RFC and MDN pages. 2 | 3 | Published here: https://brntn.me/blog/http-status-codes/ 4 | 5 | This content, including my manipulation of it, is public domain. -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish post to Mataroa 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Set up Python 3.10 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: "3.10" 19 | 20 | - name: Generate and publish post 21 | run: | 22 | python gen.py 23 | env: 24 | MICROPUB_API_KEY: ${{ secrets.MICROPUB_API_KEY }} 25 | -------------------------------------------------------------------------------- /UNLICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /thttp.py: -------------------------------------------------------------------------------- 1 | """ 2 | UNLICENSED 3 | This is free and unencumbered software released into the public domain. 4 | 5 | https://github.com/sesh/thttp 6 | """ 7 | 8 | import gzip 9 | import ssl 10 | import json as json_lib 11 | 12 | from base64 import b64encode 13 | from collections import namedtuple 14 | 15 | from http.cookiejar import CookieJar 16 | from urllib.error import HTTPError, URLError 17 | from urllib.parse import urlencode 18 | from urllib.request import ( 19 | Request, 20 | build_opener, 21 | HTTPRedirectHandler, 22 | HTTPSHandler, 23 | HTTPCookieProcessor, 24 | ) 25 | 26 | 27 | Response = namedtuple("Response", "request content json status url headers cookiejar") 28 | 29 | 30 | class NoRedirect(HTTPRedirectHandler): 31 | def redirect_request(self, req, fp, code, msg, headers, newurl): 32 | return None 33 | 34 | 35 | def request( 36 | url, 37 | params={}, 38 | json=None, 39 | data=None, 40 | headers={}, 41 | method="GET", 42 | verify=True, 43 | redirect=True, 44 | cookiejar=None, 45 | basic_auth=None, 46 | timeout=None, 47 | ): 48 | """ 49 | Returns a (named)tuple with the following properties: 50 | - request 51 | - content 52 | - json (dict; or None) 53 | - headers (dict; all lowercase keys) 54 | - https://stackoverflow.com/questions/5258977/are-http-headers-case-sensitive 55 | - status 56 | - url (final url, after any redirects) 57 | - cookiejar 58 | """ 59 | method = method.upper() 60 | headers = {k.lower(): v for k, v in headers.items()} # lowecase headers 61 | 62 | if params: 63 | url += "?" + urlencode(params) # build URL from params 64 | if json and data: 65 | raise Exception("Cannot provide both json and data parameters") 66 | if method not in ["POST", "PATCH", "PUT"] and (json or data): 67 | raise Exception( 68 | "Request method must POST, PATCH or PUT if json or data is provided" 69 | ) 70 | if not timeout: 71 | timeout = 60 72 | 73 | if json: # if we have json, stringify and put it in our data variable 74 | headers["content-type"] = "application/json" 75 | data = json_lib.dumps(json).encode("utf-8") 76 | elif data: 77 | data = urlencode(data).encode() 78 | 79 | if basic_auth and len(basic_auth) == 2 and "authorization" not in headers: 80 | username, password = basic_auth 81 | headers[ 82 | "authorization" 83 | ] = f'Basic {b64encode(f"{username}:{password}".encode()).decode("ascii")}' 84 | 85 | if not cookiejar: 86 | cookiejar = CookieJar() 87 | 88 | ctx = ssl.create_default_context() 89 | if not verify: # ignore ssl errors 90 | ctx.check_hostname = False 91 | ctx.verify_mode = ssl.CERT_NONE 92 | 93 | handlers = [] 94 | handlers.append(HTTPSHandler(context=ctx)) 95 | handlers.append(HTTPCookieProcessor(cookiejar=cookiejar)) 96 | 97 | if not redirect: 98 | no_redirect = NoRedirect() 99 | handlers.append(no_redirect) 100 | 101 | opener = build_opener(*handlers) 102 | req = Request(url, data=data, headers=headers, method=method) 103 | 104 | try: 105 | with opener.open(req, timeout=timeout) as resp: 106 | status, content, resp_url = (resp.getcode(), resp.read(), resp.geturl()) 107 | headers = {k.lower(): v for k, v in list(resp.info().items())} 108 | 109 | if "gzip" in headers.get("content-encoding", ""): 110 | content = gzip.decompress(content) 111 | 112 | json = ( 113 | json_lib.loads(content) 114 | if "application/json" in headers.get("content-type", "").lower() 115 | and content 116 | else None 117 | ) 118 | except HTTPError as e: 119 | status, content, resp_url = (e.code, e.read(), e.geturl()) 120 | headers = {k.lower(): v for k, v in list(e.headers.items())} 121 | 122 | if "gzip" in headers.get("content-encoding", ""): 123 | content = gzip.decompress(content) 124 | 125 | json = ( 126 | json_lib.loads(content) 127 | if "application/json" in headers.get("content-type", "").lower() and content 128 | else None 129 | ) 130 | 131 | return Response(req, content, json, status, resp_url, headers, cookiejar) 132 | 133 | 134 | import unittest 135 | 136 | 137 | class RequestTestCase(unittest.TestCase): 138 | def test_cannot_provide_json_and_data(self): 139 | with self.assertRaises(Exception): 140 | request( 141 | "https://httpbingo.org/post", 142 | json={"name": "Brenton"}, 143 | data="This is some form data", 144 | ) 145 | 146 | def test_should_fail_if_json_or_data_and_not_p_method(self): 147 | with self.assertRaises(Exception): 148 | request("https://httpbingo.org/post", json={"name": "Brenton"}) 149 | 150 | with self.assertRaises(Exception): 151 | request( 152 | "https://httpbingo.org/post", json={"name": "Brenton"}, method="HEAD" 153 | ) 154 | 155 | def test_should_set_content_type_for_json_request(self): 156 | response = request( 157 | "https://httpbingo.org/post", json={"name": "Brenton"}, method="POST" 158 | ) 159 | self.assertEqual(response.request.headers["Content-type"], "application/json") 160 | 161 | def test_should_work(self): 162 | response = request("https://httpbingo.org/get") 163 | self.assertEqual(response.status, 200) 164 | 165 | def test_should_create_url_from_params(self): 166 | response = request( 167 | "https://httpbingo.org/get", 168 | params={"name": "brenton", "library": "tiny-request"}, 169 | ) 170 | self.assertEqual( 171 | response.url, "https://httpbingo.org/get?name=brenton&library=tiny-request" 172 | ) 173 | 174 | def test_should_return_headers(self): 175 | response = request( 176 | "https://httpbingo.org/response-headers", params={"Test-Header": "value"} 177 | ) 178 | self.assertEqual(response.headers["test-header"], "value") 179 | 180 | def test_should_populate_json(self): 181 | response = request("https://httpbingo.org/json") 182 | self.assertTrue("slideshow" in response.json) 183 | 184 | def test_should_return_response_for_404(self): 185 | response = request("https://httpbingo.org/404") 186 | self.assertEqual(response.status, 404) 187 | self.assertTrue("text/plain" in response.headers["content-type"]) 188 | 189 | def test_should_fail_with_bad_ssl(self): 190 | with self.assertRaises(URLError): 191 | response = request("https://expired.badssl.com/") 192 | 193 | def test_should_load_bad_ssl_with_verify_false(self): 194 | response = request("https://expired.badssl.com/", verify=False) 195 | self.assertEqual(response.status, 200) 196 | 197 | def test_should_form_encode_non_json_post_requests(self): 198 | response = request( 199 | "https://httpbingo.org/post", data={"name": "test-user"}, method="POST" 200 | ) 201 | self.assertEqual(response.json["form"]["name"], ["test-user"]) 202 | 203 | def test_should_follow_redirect(self): 204 | response = request( 205 | "https://httpbingo.org/redirect-to", 206 | params={"url": "https://duckduckgo.com/"}, 207 | ) 208 | self.assertEqual(response.url, "https://duckduckgo.com/") 209 | self.assertEqual(response.status, 200) 210 | 211 | def test_should_not_follow_redirect_if_redirect_false(self): 212 | response = request( 213 | "https://httpbingo.org/redirect-to", 214 | params={"url": "https://duckduckgo.com/"}, 215 | redirect=False, 216 | ) 217 | self.assertEqual(response.status, 302) 218 | 219 | def test_cookies(self): 220 | response = request( 221 | "https://httpbingo.org/cookies/set", 222 | params={"cookie": "test"}, 223 | redirect=False, 224 | ) 225 | response = request( 226 | "https://httpbingo.org/cookies", cookiejar=response.cookiejar 227 | ) 228 | self.assertEqual(response.json["cookie"], "test") 229 | 230 | def test_basic_auth(self): 231 | response = request( 232 | "http://httpbingo.org/basic-auth/user/passwd", basic_auth=("user", "passwd") 233 | ) 234 | self.assertEqual(response.json["authorized"], True) 235 | 236 | def test_should_handle_gzip(self): 237 | response = request( 238 | "http://httpbingo.org/gzip", headers={"Accept-Encoding": "gzip"} 239 | ) 240 | self.assertEqual(response.json["gzipped"], True) 241 | 242 | def test_should_handle_gzip_error(self): 243 | response = request( 244 | "http://httpbingo.org/status/418", headers={"Accept-Encoding": "gzip"} 245 | ) 246 | self.assertEqual(response.content, b"I'm a teapot!") 247 | 248 | def test_should_timeout(self): 249 | import socket 250 | 251 | with self.assertRaises((TimeoutError, socket.timeout)): 252 | response = request("http://httpbingo.org/delay/3", timeout=1) 253 | 254 | def test_should_handle_head_requests(self): 255 | response = request("http://httpbingo.org/head", method="HEAD") 256 | self.assertTrue(response.content == b"") 257 | -------------------------------------------------------------------------------- /gen.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from thttp import request 4 | 5 | API_KEY = os.environ.get("MICROPUB_API_KEY") 6 | 7 | STATUS_CODES = { 8 | "100": { 9 | "reason": "Continue", 10 | "links": { 11 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.2.1", 12 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100", 13 | }, 14 | }, 15 | "101": { 16 | "reason": "Switching Protocols", 17 | "links": { 18 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.2.2", 19 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101", 20 | }, 21 | }, 22 | "103": { 23 | "reason": "Early Hints", 24 | "links": { 25 | "rfc": "https://datatracker.ietf.org/doc/html/rfc8297", 26 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103", 27 | }, 28 | }, 29 | "200": { 30 | "reason": "OK", 31 | "links": { 32 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.1", 33 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200", 34 | }, 35 | }, 36 | "201": { 37 | "reason": "Created", 38 | "links": { 39 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.2", 40 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201", 41 | }, 42 | }, 43 | "202": { 44 | "reason": "Accepted", 45 | "links": { 46 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.3", 47 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202", 48 | }, 49 | }, 50 | "203": { 51 | "reason": "Non-Authoritative Information", 52 | "links": { 53 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.4", 54 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/203", 55 | }, 56 | }, 57 | "204": { 58 | "reason": "No Content", 59 | "links": { 60 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.5", 61 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204", 62 | }, 63 | }, 64 | "205": { 65 | "reason": "Reset Content", 66 | "links": { 67 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.6", 68 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/205", 69 | }, 70 | }, 71 | "206": { 72 | "reason": "Partial Content", 73 | "links": { 74 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7233#section-4.1", 75 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206", 76 | }, 77 | }, 78 | "300": { 79 | "reason": "Multiple Choices", 80 | "links": { 81 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.4.1", 82 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/300", 83 | }, 84 | }, 85 | "301": { 86 | "reason": "Moved Permanently", 87 | "links": { 88 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.4.2", 89 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301", 90 | }, 91 | }, 92 | "302": { 93 | "reason": "Found", 94 | "links": { 95 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.4.3", 96 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302", 97 | }, 98 | }, 99 | "303": { 100 | "reason": "See Other", 101 | "links": { 102 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.4.4", 103 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303", 104 | }, 105 | }, 106 | "304": { 107 | "reason": "Not Modified", 108 | "links": { 109 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7232#section-4.1", 110 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304", 111 | }, 112 | }, 113 | "307": { 114 | "reason": "Temporary Redirect", 115 | "links": { 116 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.4.7", 117 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307", 118 | }, 119 | }, 120 | "308": { 121 | "reason": "Permanent Redirect", 122 | "links": { 123 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7538", 124 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308", 125 | }, 126 | }, 127 | "400": { 128 | "reason": "Bad Request", 129 | "links": { 130 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1", 131 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400", 132 | }, 133 | }, 134 | "401": { 135 | "reason": "Unauthorized", 136 | "links": { 137 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7235#section-3.1", 138 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401", 139 | }, 140 | }, 141 | "402": { 142 | "reason": "Payment Required", 143 | "links": { 144 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.2", 145 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402", 146 | }, 147 | }, 148 | "403": { 149 | "reason": "Forbidden", 150 | "links": { 151 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.3", 152 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403", 153 | }, 154 | }, 155 | "404": { 156 | "reason": "Not Found", 157 | "links": { 158 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4", 159 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404", 160 | }, 161 | }, 162 | "405": { 163 | "reason": "Method Not Allowed", 164 | "links": { 165 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.5", 166 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405", 167 | }, 168 | }, 169 | "406": { 170 | "reason": "Not Acceptable", 171 | "links": { 172 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.6", 173 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406", 174 | }, 175 | }, 176 | "407": { 177 | "reason": "Proxy Authentication Required", 178 | "links": { 179 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7235#section-3.2", 180 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407", 181 | }, 182 | }, 183 | "408": { 184 | "reason": "Request Timeout", 185 | "links": { 186 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.7", 187 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408", 188 | }, 189 | }, 190 | "409": { 191 | "reason": "Conflict", 192 | "links": { 193 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.8", 194 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409", 195 | }, 196 | }, 197 | "410": { 198 | "reason": "Gone", 199 | "links": { 200 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.9", 201 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410", 202 | }, 203 | }, 204 | "411": { 205 | "reason": "Length Required", 206 | "links": { 207 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.10", 208 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/411", 209 | }, 210 | }, 211 | "412": { 212 | "reason": "Precondition Failed", 213 | "links": { 214 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7232#section-4.2", 215 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412", 216 | }, 217 | }, 218 | "413": { 219 | "reason": "Payload Too Large", 220 | "links": { 221 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.11", 222 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413", 223 | }, 224 | }, 225 | "414": { 226 | "reason": "URI Too Long", 227 | "links": { 228 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.12", 229 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414", 230 | }, 231 | }, 232 | "415": { 233 | "reason": "Unsupported Media Type", 234 | "links": { 235 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.13", 236 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415", 237 | }, 238 | }, 239 | "416": { 240 | "reason": "Range Not Satisfiable", 241 | "links": { 242 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7233#section-4.4", 243 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416", 244 | }, 245 | }, 246 | "417": { 247 | "reason": "Expectation Failed", 248 | "links": { 249 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.14", 250 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/417", 251 | }, 252 | }, 253 | "418": { 254 | "reason": "I'm a teapot", 255 | "links": { 256 | "rfc": "https://datatracker.ietf.org/doc/html/rfc2324", 257 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418", 258 | }, 259 | }, 260 | "422": { 261 | "reason": "Unprocessable Entity", 262 | "links": { 263 | "rfc": "https://datatracker.ietf.org/doc/html/rfc4918#section-11.2", 264 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422", 265 | }, 266 | }, 267 | "425": { 268 | "reason": "Too Early", 269 | "links": { 270 | "rfc": "https://datatracker.ietf.org/doc/html/rfc8470#section-5.2", 271 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/425", 272 | }, 273 | }, 274 | "426": { 275 | "reason": "Upgrade Required", 276 | "links": { 277 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.15", 278 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426", 279 | }, 280 | }, 281 | "428": { 282 | "reason": "Precondition Required", 283 | "links": { 284 | "rfc": "https://datatracker.ietf.org/doc/html/rfc6585#section-3", 285 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/428", 286 | }, 287 | }, 288 | "429": { 289 | "reason": "Too Many Requests", 290 | "links": { 291 | "rfc": "https://datatracker.ietf.org/doc/html/rfc6585#section-4", 292 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429", 293 | }, 294 | }, 295 | "431": { 296 | "reason": "Request Header Fields Too Large", 297 | "links": { 298 | "rfc": "https://datatracker.ietf.org/doc/html/rfc6585#section-5", 299 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431", 300 | }, 301 | }, 302 | "451": { 303 | "reason": "Unavailable For Legal Reasons", 304 | "links": { 305 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7725#section-3", 306 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/451", 307 | }, 308 | }, 309 | "500": { 310 | "reason": "Internal Server Error", 311 | "links": { 312 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1", 313 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500", 314 | }, 315 | }, 316 | "501": { 317 | "reason": "Not Implemented", 318 | "links": { 319 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.2", 320 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501", 321 | }, 322 | }, 323 | "502": { 324 | "reason": "Bad Gateway", 325 | "links": { 326 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.3", 327 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502", 328 | }, 329 | }, 330 | "503": { 331 | "reason": "Service Unavailable", 332 | "links": { 333 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.4", 334 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503", 335 | }, 336 | }, 337 | "504": { 338 | "reason": "Gateway Timeout", 339 | "links": { 340 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.5", 341 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504", 342 | }, 343 | }, 344 | "505": { 345 | "reason": "HTTP Version Not Supported", 346 | "links": { 347 | "rfc": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.6", 348 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/505", 349 | }, 350 | }, 351 | "506": { 352 | "reason": "Variant Also Negotiates", 353 | "links": { 354 | "rfc": "https://datatracker.ietf.org/doc/html/rfc2295#section-8.1", 355 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/506", 356 | }, 357 | }, 358 | "507": { 359 | "reason": "Insufficient Storage", 360 | "links": { 361 | "rfc": "https://datatracker.ietf.org/doc/html/rfc4918#section-11.5", 362 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/507", 363 | }, 364 | }, 365 | "508": { 366 | "reason": "Loop Detected", 367 | "links": { 368 | "rfc": "https://datatracker.ietf.org/doc/html/rfc5842#section-7.2", 369 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508", 370 | }, 371 | }, 372 | "510": { 373 | "reason": "Not Extended", 374 | "links": { 375 | "rfc": "https://datatracker.ietf.org/doc/html/rfc2774#section-7", 376 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/510", 377 | }, 378 | }, 379 | "511": { 380 | "reason": "Network Authentication Required", 381 | "links": { 382 | "rfc": "https://datatracker.ietf.org/doc/html/rfc6585#section-6", 383 | "mdn": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/511", 384 | }, 385 | }, 386 | } 387 | 388 | md = "" 389 | prev = "" 390 | 391 | for code in sorted(STATUS_CODES.keys()): 392 | if prev and prev[0] != code[0]: 393 | md += "\n\n\n" 394 | prev = code[0] 395 | 396 | status_code = STATUS_CODES[code] 397 | 398 | links_md = ", ".join( 399 | [f"[{name}]({url})" for name, url in status_code["links"].items()] 400 | ) 401 | md += f"- {code} {status_code['reason']} ({links_md})\n" 402 | 403 | md += """\n\n 404 | This post is generated and posted here by a Github action on the [sesh/post-httpstatuscodes](https://github.com/sesh/post-httpstatuscodes) repository. 405 | Pull requests more than welcome to add new status codes or links. 406 | 407 | Heavily inspired by httpstatuses.com being redirected to WebFX instead of showing a completely ad-free list of HTTP Status Codes. 408 | 409 | Content is released into the public domain with the [Unlicense](https://unlicense.org/). 410 | """ 411 | 412 | # publish 413 | if not API_KEY: 414 | sys.stderr.write("Missing Mataroa API Key") 415 | sys.exit(1) 416 | else: 417 | response = request( 418 | "https://brntn.me/micropub/", 419 | json={ 420 | "action": "update", 421 | "url": "https://brntn.me/blog/http-status-codes/", 422 | "replace": { 423 | "content": [md] 424 | } 425 | }, 426 | headers={"Authorization": f"Bearer {API_KEY}"}, 427 | method="POST", 428 | ) 429 | print(response) 430 | sys.exit(response.status != 200) 431 | --------------------------------------------------------------------------------