├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── assets └── demo.gif ├── requirements.txt ├── setup.py └── statcode ├── __init__.py ├── __main__.py ├── code_descriptions.yml ├── copyright_description.yml ├── header_descriptions.yml └── statcode.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | .idea 4 | *.egg-info/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jonathan Shobrook 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include statcode/* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # statcode 2 | 3 | `statcode` is like `man` but for HTTP status codes. If you're a web developer, you probably spend some time looking at response codes (usually errors) and then Googling what they mean. But with `statcode`, you can simply run `$ statcode [status_code]` and get a quick explanation of your HTTP response without leaving the terminal. 4 | 5 | ![demo](assets/demo.gif) 6 | 7 | ## Installation 8 | 9 | >Requires Python 3.0 or higher 10 | 11 | `statcode` works on MacOS, Linux, and Windows (if you use Cygwin). You can install it with pip: 12 | 13 | ```bash 14 | $ pip install statcode 15 | ``` 16 | 17 | Or if you're running Arch, you can install [`statcode`](https://aur.archlinux.org/packages/statcode/) from the AUR: 18 | 19 | ```bash 20 | $ aurman -S statcode 21 | ``` 22 | 23 | ## Contributing 24 | 25 | This is a pretty small project (something I put together on a plane ride), but with enough help it could turn into a go-to manual for everything HTTP-related. For example, it should be possible to look up different [request headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers), e.g. running `$ statcode cache-control` and getting "Specifies directives for caching mechanisms in both requests and responses." If you'd like to help make this happen, feel free to fork the repo and contribute. 26 | 27 | If you've discovered a bug or have a feature request, create an [issue](https://github.com/shobrook/statcode/issues/new) and I'll take care of it! 28 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shobrook/statcode/15172e8240cd81b1671aa88fd6bee03c29361d21/assets/demo.gif -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyyaml 2 | urwid 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup 3 | except ImportError: 4 | from distutils.core import setup 5 | from codecs import open 6 | 7 | setup( 8 | name="statcode", 9 | description="Like man pages, but for HTTP status codes", 10 | version="v2.0.0", 11 | install_requires=["pyyaml", "urwid"], 12 | packages=["statcode"], 13 | entry_points={"console_scripts": ["statcode = statcode.statcode:main"]}, 14 | include_package_data=True, 15 | python_requires=">=3", 16 | url="https://github.com/shobrook/statcode", 17 | author="shobrook", 18 | author_email="shobrookj@gmail.com", 19 | license="MIT" 20 | ) 21 | -------------------------------------------------------------------------------- /statcode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shobrook/statcode/15172e8240cd81b1671aa88fd6bee03c29361d21/statcode/__init__.py -------------------------------------------------------------------------------- /statcode/__main__.py: -------------------------------------------------------------------------------- 1 | import statcode.statcode 2 | 3 | if __name__ == "__main__": 4 | statcode.statcode.main() 5 | -------------------------------------------------------------------------------- /statcode/code_descriptions.yml: -------------------------------------------------------------------------------- 1 | # Informational Responses 2 | 3 | 100: 4 | message: "Continue" 5 | category: "Informational Responses" 6 | description: "This interim response indicates that everything so far is OK \ 7 | and that the client should continue with the request or ignore \ 8 | it if it is already finished." 9 | 10 | 101: 11 | message: "Switching Protocol" 12 | category: "Informational Responses" 13 | description: "This code is sent in response to an Upgrade request header by \ 14 | the client, and indicates the protocol the server is switching to." 15 | 16 | 102: 17 | message: "Processing (WebDAV)" 18 | category: "Informational Responses" 19 | description: "This code indicates that the server has received and is \ 20 | processing the request, but no response is available yet." 21 | 22 | 103: 23 | message: "Early Hints / Checkpoint" 24 | description: "Early Hints 25 | This code indicates that the server is likely to send a final response with the\ 26 | header fields included in that very informational response. It is intended to\ 27 | minimize the perceived latency taking advantage of the Link header field to\ 28 | speculatively preload or prefetch resources. 29 | 30 | Checkpoint 31 | This code is the same as the 308 status code except for the fact that it is sent\ 32 | as a provisional response rather than a final response.\ 33 | In other words, zero or more 103 responses can be sent in response to a request\ 34 | that is still being processed, after which a final response code must be sent." 35 | 36 | # Successful Responses 37 | 38 | 200: 39 | message: "OK" 40 | category: "Successful Responses" 41 | description: "The request has succeeded. The meaning of a success varies \ 42 | depending on the HTTP method: 43 | GET: The resource has been fetched and is transmitted in the \ 44 | message body. 45 | HEAD: The entity headers are in the message body. 46 | PUT or POST: The resource describing the result of the action is \ 47 | transmitted in the message body. 48 | TRACE: The message body contains the request message as received \ 49 | by the server" 50 | 51 | 201: 52 | message: "Created" 53 | category: "Successful Responses" 54 | description: "The request has succeeded and a new resource has been created as \ 55 | a result of it. This is typically the response sent after a POST \ 56 | request, or after some PUT requests." 57 | 58 | 202: 59 | message: "Accepted" 60 | category: "Successful Responses" 61 | description: "The request has been received but not yet acted upon. It is \ 62 | non-committal, meaning that there is no way in HTTP to later send \ 63 | an asynchronous response indicating the outcome of processing the \ 64 | request. It is intended for cases where another process or server \ 65 | handles the request, or for batch processing." 66 | 67 | 203: 68 | message: "Non-Authoritative Information" 69 | category: "Successful Responses" 70 | description: "This response code means returned meta-information set is not \ 71 | exact set as available from the origin server, but collected from \ 72 | a local or a third party copy. Except this condition, 200 OK \ 73 | response should be preferred instead of this response." 74 | 75 | 204: 76 | message: "No Content" 77 | category: "Successful Responses" 78 | description: "There is no content to send for this request, but the headers \ 79 | may be useful. The user-agent may update its cached headers for \ 80 | this resource with the new ones." 81 | 82 | 205: 83 | message: "Reset Content" 84 | category: "Successful Responses" 85 | description: "This response code is sent after accomplishing request to tell \ 86 | user agent reset document view which sent this request." 87 | 88 | 206: 89 | message: "Partial Content" 90 | category: "Successful Responses" 91 | description: "This response code is used because of range header sent by the \ 92 | client to separate download into multiple streams." 93 | 94 | 207: 95 | message: "Multi-Status (WebDav)" 96 | category: "Successful Responses" 97 | description: "A Multi-Status response conveys information about multiple \ 98 | resources in situations where multiple status codes might be \ 99 | appropriate." 100 | 101 | 208: 102 | message: "Multi-Status (WebDav)" 103 | category: "Successful Responses" 104 | description: "Used inside a DAV: propstat response element to avoid enumerating \ 105 | the internal members of multiple bindings to the same collection \ 106 | repeatedly." 107 | 108 | 226: 109 | message: "IM Used (HTTP Delta encoding)" 110 | category: "Successful Responses" 111 | description: "The server has fulfilled a GET request for the resource, and the \ 112 | response is a representation of the result of one or more \ 113 | instance-manipulations applied to the current instance." 114 | 115 | # Redirection Messages 116 | 117 | 300: 118 | message: "Multiple Choice" 119 | category: "Redirection Messages" 120 | description: "The request has more than one possible response. The user-agent \ 121 | or user should choose one of them. There is no standardized way \ 122 | of choosing one of the responses." 123 | 124 | 301: 125 | message: "Moved Permanently" 126 | category: "Redirection Messages" 127 | description: "This response code means that the URI of the requested resource \ 128 | has been changed. Probably, the new URI would be given in the \ 129 | response." 130 | 131 | 302: 132 | message: "Found" 133 | category: "Redirection Messages" 134 | description: "This response code means that the URI of requested resource has \ 135 | been changed temporarily. New changes in the URI might be made \ 136 | in the future. Therefore, this same URI should be used by the \ 137 | client in future requests." 138 | 139 | 303: 140 | message: "See Other" 141 | category: "Redirection Messages" 142 | description: "The server sent this response to direct the client to get the \ 143 | requested resource at another URI with a GET request." 144 | 145 | 304: 146 | message: "Not Modified" 147 | category: "Redirection Messages" 148 | description: "This is used for caching purposes. It tells the client that the \ 149 | response has not been modified, so the client can continue to \ 150 | use the same cached version of the response." 151 | 152 | 305: 153 | message: "Use Proxy" 154 | category: "Redirection Messages" 155 | description: "Was defined in a previous version of the HTTP specification to \ 156 | indicate that a requested response must be accessed by a proxy. \ 157 | It has been deprecated due to security concerns regarding in-band \ 158 | configuration of a proxy." 159 | 160 | 306: 161 | message: "Unused" 162 | category: "Redirection Messages" 163 | description: "This response code is no longer used, it is just reserved \ 164 | currently. It was used in a previous version of the HTTP 1.1 \ 165 | specification." 166 | 167 | 307: 168 | message: "Temporary Redirect" 169 | category: "Redirection Messages" 170 | description: "The server sends this response to direct the client to get the \ 171 | requested resource at another URI with same method that was used \ 172 | in the prior request. This has the same semantics as the 302 Found \ 173 | HTTP response code, with the exception that the user agent must \ 174 | not change the HTTP method used: If a POST was used in the first \ 175 | request, a POST must be used in the second request." 176 | 177 | 308: 178 | message: "Permanent Redirect" 179 | category: "Redirection Messages" 180 | description: "This means that the resource is now permanently located at another \ 181 | URI, specified by the Location: HTTP Response header. This has \ 182 | the same semantics as the 301 Moved Permanently HTTP response \ 183 | code, with the exception that the user agent must not change the \ 184 | HTTP method used: If a POST was used in the first request, a POST \ 185 | must be used in the second request." 186 | 187 | # Client Error Responses 188 | 189 | 400: 190 | message: "Bad Request" 191 | category: "Client Error Responses" 192 | description: "This response means that server could not understand the request \ 193 | due to invalid syntax." 194 | 195 | 401: 196 | message: "Unauthorized" 197 | category: "Client Error Responses" 198 | description: "Although the HTTP standard specifies 'unauthorized', semantically \ 199 | this response means 'unauthenticated'. That is, the client must \ 200 | authenticate itself to get the requested response." 201 | 202 | 402: 203 | message: "Payment Required" 204 | category: "Client Error Responses" 205 | description: "This response code is reserved for future use. Initial aim for \ 206 | creating this code was using it for digital payment systems \ 207 | however this is not used currently." 208 | 209 | 403: 210 | message: "Forbidden" 211 | category: "Client Error Responses" 212 | description: "The client does not have access rights to the content, i.e. they \ 213 | are unauthorized, so server is rejecting to give proper response. \ 214 | Unlike 401, the client's identity is known to the server." 215 | 216 | 404: 217 | message: "Not Found" 218 | category: "Client Error Responses" 219 | description: "The server can not find requested resource. In the browser, this \ 220 | means the URL is not recognized. In an API, this can also mean \ 221 | that the endpoint is valid but the resource itself does not exist. \ 222 | Servers may also send this response instead of 403 to hide the \ 223 | existence of a resource from an unauthorized client. This response \ 224 | code is probably the most famous one due to its frequent occurence \ 225 | on the web." 226 | 227 | 405: 228 | message: "Method Not Allowed" 229 | category: "Client Error Responses" 230 | description: "The request method is known by the server but has been disabled \ 231 | and cannot be used. For example, an API may forbid DELETE-ing a \ 232 | resource. The two mandatory methods, GET and HEAD, must never be \ 233 | disabled and should not return this error code." 234 | 235 | 406: 236 | message: "Not Acceptable" 237 | category: "Client Error Responses" 238 | description: "This response is sent when the web server, after performing \ 239 | server-driven content negotiation, doesn't find any content \ 240 | following the criteria given by the user agent." 241 | 242 | 407: 243 | message: "Proxy Authentication Required" 244 | category: "Client Error Responses" 245 | description: "This is similar to 401 but authentication is needed to be done \ 246 | by a proxy." 247 | 248 | 408: 249 | message: "Request Timeout" 250 | category: "Client Error Responses" 251 | description: "This response is sent on an idle connection by some servers, \ 252 | even without any previous request by the client. It means that \ 253 | the server would like to shut down this unused connection. This \ 254 | response is used much more since some browsers, like Chrome, \ 255 | Firefox 27+, or IE9, use HTTP pre-connection mechanisms to speed \ 256 | up surfing. Also note that some servers merely shut down the \ 257 | connection without sending this message." 258 | 259 | 409: 260 | message: "Conflict" 261 | category: "Client Error Responses" 262 | description: "This response is sent when a request conflicts with the current \ 263 | state of the server." 264 | 265 | 410: 266 | message: "Gone" 267 | category: "Client Error Responses" 268 | description: "This response would be sent when the requested content has been \ 269 | permanently deleted from server, with no forwarding address. \ 270 | Clients are expected to remove their caches and links to the \ 271 | resource. The HTTP specification intends this status code to be \ 272 | used for 'limited-time, promotional services'. APIs should not \ 273 | feel compelled to indicate resources that have been deleted with \ 274 | this status code." 275 | 276 | 411: 277 | message: "Length Required" 278 | category: "Client Error Responses" 279 | description: "Server rejected the request because the Content-Length header \ 280 | field is not defined and the server requires it." 281 | 282 | 412: 283 | message: "Precondition Failed" 284 | category: "Client Error Responses" 285 | description: "The client has indicated preconditions in its headers which the \ 286 | server does not meet." 287 | 288 | 413: 289 | message: "Payload Too Large" 290 | category: "Client Error Responses" 291 | description: "Request entity is larger than limits defined by server; the server \ 292 | might close the connection or return an Retry-After header field." 293 | 294 | 414: 295 | message: "URI Too Long" 296 | category: "Client Error Responses" 297 | description: "Request entity is larger than limits defined by server; the server \ 298 | might close the connection or return an Retry-After header field." 299 | 300 | 415: 301 | message: "Unsupported Media Type" 302 | category: "Client Error Responses" 303 | description: "The media format of the requested data is not supported by the \ 304 | server, so the server is rejecting the request." 305 | 306 | 416: 307 | message: "Requested Range Not Satisfiable" 308 | category: "Client Error Responses" 309 | description: "The range specified by the Range header field in the request \ 310 | can't be fulfilled; it's possible that the range is outside the \ 311 | size of the target URI's data." 312 | 313 | 417: 314 | message: "Expectation Failed" 315 | category: "Client Error Responses" 316 | description: "This response code means the expectation indicated by the Expect \ 317 | request header field can't be met by the server." 318 | 319 | 418: 320 | message: "I'm a teapot" 321 | category: "Client Error Responses" 322 | description: "The server refuses the attempt to brew coffee with a teapot." 323 | 324 | 421: 325 | message: "Misdirected Request" 326 | category: "Client Error Responses" 327 | description: "The request was directed at a server that is not able to produce \ 328 | a response. This can be sent by a server that is not configured \ 329 | to produce responses for the combination of scheme and authority \ 330 | that are included in the request URI." 331 | 332 | 422: 333 | message: "Unprocessable Entity (WebDAV)" 334 | category: "Client Error Responses" 335 | description: "The request was well-formed but was unable to be followed due to \ 336 | semantic errors." 337 | 338 | 423: 339 | message: "Locked (WebDAV)" 340 | category: "Client Error Responses" 341 | description: "The resource that is being accessed is locked." 342 | 343 | 424: 344 | message: "Failed Dependency (WebDAV)" 345 | category: "Client Error Responses" 346 | description: "The request failed due to failure of a previous request." 347 | 348 | 426: 349 | message: "Upgrade Required" 350 | category: "Client Error Responses" 351 | description: "The server refuses to perform the request using the current \ 352 | protocol but might be willing to do so after the client upgrades \ 353 | to a different protocol. The server sends an Upgrade header in a \ 354 | 426 response to indicate the required protocol(s)." 355 | 356 | 428: 357 | message: "Precondition Required" 358 | category: "Client Error Responses" 359 | description: "The origin server requires the request to be conditional. Intended \ 360 | to prevent the 'lost update' problem, where a client GETs a resource's \ 361 | state, modifies it, and PUTs it back to the server, when meanwhile a \ 362 | third party has modified the state on the server, leading to a \ 363 | conflict." 364 | 365 | 429: 366 | message: "Too Many Requests" 367 | category: "Client Error Responses" 368 | description: "The user has sent too many requests in a given amount of time \ 369 | ('rate limiting')." 370 | 371 | 431: 372 | message: "Request Header Fields Too Large" 373 | category: "Client Error Responses" 374 | description: "The server is unwilling to process the request because its header \ 375 | fields are too large. The request MAY be resubmitted after reducing \ 376 | the size of the request header fields." 377 | 378 | 451: 379 | message: "Unavailable For Legal Reasons" 380 | category: "Client Error Responses" 381 | description: "The user requests an illegal resource, such as a web page censored \ 382 | by a government." 383 | 384 | # Server Error Responses 385 | 386 | 500: 387 | message: "Internal Server Error" 388 | category: "Server Error Responses" 389 | description: "The server has encountered a situation it doesn't know how to \ 390 | handle." 391 | 392 | 501: 393 | message: "Not Implemented" 394 | category: "Server Error Responses" 395 | description: "The request method is not supported by the server and cannot be \ 396 | handled. The only methods that servers are required to support \ 397 | (and therefore that must not return this code) are GET and HEAD." 398 | 399 | 502: 400 | message: "Bad Gateway" 401 | category: "Server Error Responses" 402 | description: "This error response means that the server, while working as a \ 403 | gateway to get a response needed to handle the request, got an \ 404 | invalid response." 405 | 406 | 503: 407 | message: "Service Unavailable" 408 | category: "Server Error Responses" 409 | description: "The server is not ready to handle the request. Common causes are \ 410 | a server that is down for maintenance or that is overloaded. Note \ 411 | that together with this response, a user-friendly page explaining \ 412 | the problem should be sent. This responses should be used for \ 413 | temporary conditions and the Retry-After: HTTP header should, \ 414 | if possible, contain the estimated time before the recovery of \ 415 | the service. The webmaster must also take care about the \ 416 | caching-related headers that are sent along with this response, \ 417 | as these temporary condition responses should usually not be \ 418 | cached." 419 | 420 | 504: 421 | message: "Gateway Timeout" 422 | category: "Server Error Responses" 423 | description: "This error response is given when the server is acting as a \ 424 | gateway and cannot get a response in time." 425 | 426 | 505: 427 | message: "HTTP Version Not Supported" 428 | category: "Server Error Responses" 429 | description: "The HTTP version used in the request is not supported by the server." 430 | 431 | 506: 432 | message: "Variant Also Negotiates" 433 | category: "Server Error Responses" 434 | description: "The server has an internal configuration error: transparent \ 435 | content negotiation for the request results in a circular reference." 436 | 437 | 507: 438 | message: "Insufficient Storage" 439 | category: "Server Error Responses" 440 | description: "The server has an internal configuration error: the chosen \ 441 | variant resource is configured to engage in transparent content \ 442 | negotiation itself, and is therefore not a proper end point in \ 443 | the negotiation process." 444 | 445 | 508: 446 | message: "Loop Detected (WebDAV)" 447 | category: "Server Error Responses" 448 | description: "The server detected an infinite loop while processing the request." 449 | 450 | 510: 451 | message: "Not Extended" 452 | category: "Server Error Responses" 453 | description: "Further extensions to the request are required for the server to \ 454 | fulfill it." 455 | 456 | 511: 457 | message: "Network Authentication Required" 458 | category: "Server Error Responses" 459 | description: "The 511 status code indicates that the client needs to authenticate \ 460 | to gain network access." 461 | -------------------------------------------------------------------------------- /statcode/copyright_description.yml: -------------------------------------------------------------------------------- 1 | statuscode: "Copyright © CC BY-SA Mozilla Contributors " 2 | headers: "Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. \ 3 | Wikipedia® is a registered trademark of the Wikimedia Foundation, Inc., a non-profit organization." -------------------------------------------------------------------------------- /statcode/header_descriptions.yml: -------------------------------------------------------------------------------- 1 | Accept-Charset: 2 | message: "Accept-Charset: utf-8" 3 | category: "Client Request" 4 | description: "Character sets that are acceptable by the client." 5 | 6 | A-IM: 7 | message: "A-IM: feed" 8 | category: "Client Request" 9 | description: "Acceptable instance-manipulations for the request. See RFC 3229" 10 | 11 | Accept: 12 | message: "Accept: text/html" 13 | category: "Client Request" 14 | description: "Media type(s) that is(/are) acceptable for the response." 15 | 16 | Accept-Encoding: 17 | message: "Accept-Encoding: gzip, deflate" 18 | category: "Client Request" 19 | description: "List of acceptable encodings. See HTTP compression." 20 | 21 | Accept-Language: 22 | message: "Accept-Language: en-US" 23 | category: "Client Request" 24 | description: "List of acceptable human languages for response." 25 | 26 | Accept-Datetime: 27 | message: "Accept-Datetime: Thu, 31 May 2007 20:35:00 GMT" 28 | category: "Client Request" 29 | description: " Acceptable version in time." 30 | 31 | Access-Control-Request-Method: 32 | message: "Access-Control-Request-Method: GET" 33 | category: "Client Request" 34 | description: "Initiates a request for cross-origin resource sharing with Origin." 35 | 36 | Access-Control-Request-Headers: 37 | message: "Access-Control-Request-Method: GET" 38 | category: "Client Request" 39 | description: "Initiates a request for cross-origin resource sharing with Origin." 40 | 41 | Authorization: 42 | message: "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" 43 | category: "Client Request" 44 | description: "Authentication credentials for HTTP authentication." 45 | 46 | Cache-Control: 47 | message: "Client= Cache-Control: no-cache 48 | Server= Cache-Control: max-age=3600" 49 | category: "Client Request, Server Response" 50 | description: "For client: Used to specify directives that must be obeyed by all caching mechanisms \ 51 | along the request-response chain. 52 | For Server: Tells all caching mechanisms from server to client whether they may cache this object. It is measured in seconds" 53 | 54 | Connection: 55 | message: "Connection: keep-alive 56 | Connection: Upgrade" 57 | category: "Client Request, Server Response" 58 | description: "Control options for the current connection and list of hop-by-hop request fields.\ 59 | Must not be used with HTTP/2." 60 | Content-Length: 61 | message: "Content-Length: 348" 62 | category: "Client Request, Server Response" 63 | description: "The length of the request body in octets (8-bit bytes)." 64 | 65 | Content-MD5: 66 | message: "Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==" 67 | category: "Client Request, Server Response" 68 | description: "A Base64-encoded binary MD5 sum of the content of the request body." 69 | 70 | Content-Type: 71 | message: "Content-Type: application/x-www-form-urlencoded" 72 | category: "Client Request, Server Response" 73 | description: "Client Request: The Media type of the body of the request (used with POST and PUT requests).\ 74 | Server Response: The Media type of the body of the response (always sent, eg text/html)." 75 | 76 | Cookie: 77 | message: "Cookie: $Version=1; Skin=new;" 78 | category: "Client Request" 79 | description: "An HTTP cookie previously sent by the server with Set-Cookie." 80 | 81 | Date: 82 | message: "Date: Tue, 15 Nov 1994 08:12:31 GMT" 83 | category: "Client Request, Server Response" 84 | description: "The date and time that the message was originated (in \"HTTP-date\" format as defined by RFC 7231)" 85 | 86 | Expect: 87 | message: "Expect: 100-continue" 88 | category: "Client Request" 89 | description: "Indicates that particular server behaviors are required by the client." 90 | 91 | Forwarded: 92 | message: "Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43 Forwarded: for=192.0.2.43, for=198.51.100.17" 93 | category: "Client Request" 94 | description: "Disclose original information of a client connecting to a web server through an HTTP proxy." 95 | 96 | From: 97 | message: "From: user@example.com" 98 | category: "Client Request" 99 | description: "The email address of the user making the request." 100 | 101 | Host: 102 | message: "Host: en.wikipedia.org:8080 103 | Host: en.wikipedia.org" 104 | category: "Client Request" 105 | description: "The domain name of the server (for virtual hosting), and the TCP port number on which the server is listening. \ 106 | The port number may be omitted if the port is the standard port for the service requested. 107 | Mandatory since HTTP/1.1. If the request is generated directly in HTTP/2, it should not be used." 108 | 109 | If-Match: 110 | message: "If-Match: \"737060cd8c284d8af7ad3082f209582d\"" 111 | category: "Client Request" 112 | description: "Only perform the action if the client supplied entity matches the same entity on the server. 113 | This is mainly for methods like PUT to only update a resource if it has not been modified since the user last updated it." 114 | 115 | If-Modified-Since: 116 | message: "If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT" 117 | category: "Client Request" 118 | description: "Allows a 304 Not Modified to be returned if content is unchanged." 119 | 120 | If-None-Match: 121 | message: "If-None-Match: \"737060cd8c284d8af7ad3082f209582d\"" 122 | category: "Client Request" 123 | description: "Allows a 304 Not Modified to be returned if content is unchanged." 124 | 125 | If-Range: 126 | message: "If-Range: \"737060cd8c284d8af7ad3082f209582d\"" 127 | category: "Client Request" 128 | description: "If the entity is unchanged, send me the part(s) that I am missing; otherwise, send me the entire new entity." 129 | 130 | If-Unmodified-Since: 131 | message: "If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT" 132 | category: "Client Request" 133 | description: "Only send the response if the entity has not been modified since a specific time." 134 | 135 | Max-Forwards: 136 | message: "Max-Forwards: 10" 137 | category: "Client Request" 138 | description: "Limit the number of times the message can be forwarded through proxies or gateways." 139 | 140 | Origin: 141 | message: "Origin: http://www.example-social-network.com" 142 | category: "Client Request" 143 | description: "Initiates a request for cross-origin resource sharing (asks server for Access-Control-* response fields)." 144 | 145 | Pragma: 146 | message: "Pragma: no-cache" 147 | category: "Client Request, Server Response" 148 | description: "Implementation-specific fields that may have various effects anywhere along the request-response chain." 149 | 150 | Proxy-Authorization: 151 | message: "Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" 152 | category: "Client Request" 153 | description: "Authorization credentials for connecting to a proxy." 154 | 155 | Range: 156 | message: "Range: bytes=500-999" 157 | category: "Client Request" 158 | description: "Request only part of an entity. Bytes are numbered from 0. " 159 | 160 | Referer: 161 | message: "Referer: http://www.example.com/" 162 | category: "Client Request" 163 | description: "This is the address of the previous web page from which a link to the currently requested page was followed. 164 | (The word \"referrer\" has been misspelled in the RFC as well as in most implementations to the point that it has become \ 165 | standard usage and is considered correct terminology)" 166 | 167 | TE: 168 | message: "TE: trailers, deflate" 169 | category: "Client Request" 170 | description: "The transfer encodings the user agent is willing to accept: the same values as for the response header \ 171 | field Transfer-Encoding can be used, plus the \"trailers\" value (related to the \"chunked\" transfer method) to \ 172 | notify the server it expects to receive additional fields in the trailer after the last, zero-sized, chunk. 173 | Only trailers is supported in HTTP/2." 174 | 175 | User-Agent: 176 | message: "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/12.0" 177 | category: "Client Request" 178 | description: "The user agent string of the user agent." 179 | 180 | Upgrade: 181 | message: "Upgrade: h2c, HTTPS/1.3, IRC/6.9, RTA/x11, websocket" 182 | category: "Client Request, Server Response" 183 | description: "Ask the server/client (respectively) to upgrade to another protocol. 184 | Must not be used in HTTP/2." 185 | 186 | Via: 187 | message: "Via: 1.0 fred, 1.1 example.com (Apache/1.1)" 188 | category: "Client Request, Server Response" 189 | description: "Informs the server/client (respectively) of proxies through which the request was sent." 190 | 191 | Warning: 192 | message: "Warning: 199 Miscellaneous warning" 193 | category: "Client Request, Server Response" 194 | description: "A general warning about possible problems with the entity body." 195 | 196 | Access-Control-Allow-Origin: 197 | message: "Access-Control-Allow-Origin: *" 198 | category: "Server Response" 199 | description: "Specifying which web sites can participate in cross-origin resource sharing" 200 | 201 | Access-Control-Allow-Credentials: 202 | message: "Access-Control-Allow-Origin: *" 203 | category: "Server Response" 204 | description: "Specifying which web sites can participate in cross-origin resource sharing" 205 | 206 | Access-Control-Expose-Headers: 207 | message: "Access-Control-Allow-Origin: *" 208 | category: "Server Response" 209 | description: "Specifying which web sites can participate in cross-origin resource sharing" 210 | 211 | Access-Control-Max-Age: 212 | message: "Access-Control-Allow-Origin: *" 213 | category: "Server Response" 214 | description: "Specifying which web sites can participate in cross-origin resource sharing" 215 | 216 | Access-Control-Allow-Methods: 217 | message: "Access-Control-Allow-Origin: *" 218 | category: "Server Response" 219 | description: "Specifying which web sites can participate in cross-origin resource sharing" 220 | 221 | Access-Control-Allow-Headers: 222 | message: "Access-Control-Allow-Origin: *" 223 | category: "Server Response" 224 | description: "Specifying which web sites can participate in cross-origin resource sharing" 225 | 226 | Accept-Patch: 227 | message: "Accept-Patch: text/example;charset=utf-8" 228 | category: "Server Response" 229 | description: "Specifies which patch document formats this server supports." 230 | 231 | Accept-Ranges: 232 | message: "Accept-Ranges: bytes" 233 | category: "Server Response" 234 | description: "What partial content range types this server supports via byte serving." 235 | 236 | Age: 237 | message: "Age: 12" 238 | category: "Server Response" 239 | description: "The age the object has been in a proxy cache in seconds." 240 | 241 | Allow: 242 | message: "Allow: GET, HEAD" 243 | category: "Server Response" 244 | description: "Valid methods for a specified resource. To be used for a 405 Method not allowed." 245 | 246 | Alt-Svc: 247 | message: "Alt-Svc: http/1.1=\"http2.example.com:8001\"; ma=7200" 248 | category: "Server Response" 249 | description: "A server uses \"Alt-Svc\" header (meaning Alternative Services) to indicate that its resources can also \ 250 | be accessed at a different network location (host or port) or using a different protocol. 251 | When using HTTP/2, servers should instead send an ALTSVC frame." 252 | 253 | Content-Disposition: 254 | message: "Content-Disposition: attachment; filename=\"fname.ext\"" 255 | category: "Server Response" 256 | description: "An opportunity to raise a \"File Download\" dialogue box for a known MIME type with binary format \ 257 | or suggest a filename for dynamic content. Quotes are necessary with special characters." 258 | 259 | Content-Language: 260 | message: "Content-Language: it" 261 | category: "Server Response" 262 | description: "The natural language or languages of the intended audience for the enclosed content" 263 | 264 | Content-Location: 265 | message: "Content-Location: /index.htm" 266 | category: "Server Response" 267 | description: "An alternate location for the returned data." 268 | 269 | Content-Range: 270 | message: "Content-Range: bytes 21010-47021/47022" 271 | category: "Server Response" 272 | description: "Where in a full body message this partial message belongs." 273 | 274 | Delta-Base: 275 | message: "Delta-Base: \"abc\"" 276 | category: "Server Response" 277 | description: "Specifies the delta-encoding entity tag of the response." 278 | 279 | ETag: 280 | message: "ETag: \"737060cd8c284d8af7ad3082f209582d\"" 281 | category: "Server Response" 282 | description: "An identifier for a specific version of a resource, often a message digest." 283 | 284 | Expires: 285 | message: "Expires: Thu, 01 Dec 1994 16:00:00 GMT" 286 | category: "Server Response" 287 | description: "Gives the date/time after which the response is considered stale (in \"HTTP-date\" format as defined by RFC 7231)." 288 | 289 | IM: 290 | message: "IM: feed" 291 | category: "Server Response" 292 | description: "Instance-manipulations applied to the response." 293 | 294 | Last-Modified: 295 | message: "Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT" 296 | category: "Server Response" 297 | description: "The last modified date for the requested object (in \"HTTP-date\" format as defined by RFC 7231)." 298 | 299 | Link: 300 | message: "Link: ; rel=\"alternate\"" 301 | category: "Server Response" 302 | description: "Used to express a typed relationship with another resource, where the relation type is defined by RFC 5988." 303 | 304 | Location: 305 | message: "Location: /pub/WWW/People.html" 306 | category: "Server Response" 307 | description: "Used in redirection, or when a new resource has been created. Can be absolute or relative path \ 308 | (if absolute, http:// protocol must be specified)." 309 | 310 | P3P: 311 | message: "P3P: CP=\"This is not a P3P policy! See https://en.wikipedia.org/wiki/Special:CentralAutoLogin/P3P for more info.\"" 312 | category: "Server Response" 313 | description: "This field is supposed to set P3P policy, in the form of P3P:CP=\"your_compact_policy\". 314 | However, P3P did not take off,[45] most browsers have never fully implemented it, a lot of websites set this field \ 315 | with fake policy text, that was enough to fool browsers the existence of P3P policy and grant permissions for third party cookies." 316 | 317 | Proxy-Authenticate: 318 | message: "Proxy-Authenticate: Basic" 319 | category: "Server Response" 320 | description: "Request authentication to access the proxy." 321 | 322 | Public-Key-Pins: 323 | message: "Public-Key-Pins: max-age=2592000; pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\";" 324 | category: "Server Response" 325 | description: "HTTP Public Key Pinning, announces hash of website's authentic TLS certificate." 326 | 327 | Retry-After: 328 | message: "Retry-After: 120; Retry-After: Fri, 07 Nov 2014 23:59:59 GMT" 329 | category: "Server Response" 330 | description: "If an entity is temporarily unavailable, this instructs the client to try again later. Value could be a specified period of time (in seconds) or a HTTP-date." 331 | 332 | Server: 333 | message: "Server: Apache/2.4.1 (Unix)" 334 | category: "Server Response" 335 | description: "A name for the server.\ 336 | Usually provides information about the OS and HTTP Server implementation used, mirroring User Agent" 337 | 338 | Set-Cookie: 339 | message: "Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1" 340 | category: "Server Response" 341 | description: "Sets an HTTP Cookie on the client." 342 | 343 | Strict-Transport-Security: 344 | message: "Strict-Transport-Security: max-age=16070400; includeSubDomains" 345 | category: "Server Response" 346 | description: "A HSTS Policy informing the HTTP client how long to cache the HTTPS only policy and whether this applies to subdomains." 347 | 348 | Trailer: 349 | message: "Trailer: Max-Forwards" 350 | category: "Server Response" 351 | description: "The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded with chunked transfer coding." 352 | 353 | Transfer-Encoding: 354 | message: "Transfer-Encoding: chunked" 355 | category: "Server Response" 356 | description: "The form of encoding used to safely transfer the entity to the user. Currently defined methods are:\ 357 | chunked, compress, deflate, gzip, identity. 358 | Must not be used with HTTP/2." 359 | 360 | Tk: 361 | message: "Tk: ?" 362 | category: "Server Response" 363 | description: "Tracking Status header, value suggested to be sent in response to a DNT(do-not-track), possible values: 364 | \"!\" — under construction 365 | \"?\" — dynamic 366 | \"G\" — gateway to multiple parties 367 | \"N\" — not tracking 368 | \"T\" — tracking 369 | \"C\" — tracking with consent 370 | \"P\" — tracking only if consented 371 | \"D\" — disregarding DNT 372 | \"U\" — updated." 373 | 374 | Vary: 375 | message: "Vary: * ; Vary: Accept-Language" 376 | category: "Server Response" 377 | description: "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." 378 | 379 | WWW-Authenticate: 380 | message: "WWW-Authenticate: Basic" 381 | category: "Server Response" 382 | description: "Indicates the authentication scheme that should be used to access the requested entity." 383 | 384 | X-Frame-Options: 385 | message: "X-Frame-Options: deny" 386 | category: "Server Response" 387 | description: "Clickjacking protection: deny - no rendering within a frame, sameorigin - no rendering if origin mismatch, allow-from - allow from specified location, allowall - non-standard, allow from any location." 388 | 389 | Refresh: 390 | message: "Refresh: 5; url=http://www.example.com/pub/statcode/fun_times.html" 391 | category: "Server Response" 392 | description: "Used in redirection, or when a new resource has been created. This refresh redirects after 5 seconds. Header extension introduced by Netscape and supported by most web browsers. NOT STANDARD." 393 | 394 | Status: 395 | message: "Status: 200 OK" 396 | category: "Server Response" 397 | description: "CGI header field specifying the status of the HTTP response. Normal HTTP responses use a separate \"Status-Line\" instead, defined by RFC 7230. NOT STANDARD." 398 | 399 | X-XSS-Protection: 400 | message: "X-XSS-Protection: 1; mode=block" 401 | category: "Server Response" 402 | description: "Cross-site scripting (XSS) filter. NOT STANDARD." 403 | 404 | X-UA-Compatible: 405 | message: "X-UA-Compatible: IE=EmulateIE7 ; X-UA-Compatible: IE=edge ; X-UA-Compatible: Chrome=1" 406 | category: "Server Response" 407 | description: "Recommends the preferred rendering engine (often a backward-compatibility mode) to use to display the content. Also used to activate Chrome Frame in Internet Explorer. NOT STANDARD." 408 | 409 | X-Powered-By: 410 | message: "X-Powered-By: PHP/5.4.0" 411 | category: "Server Response" 412 | description: "Specifies the technology (e.g. ASP.NET, PHP, JBoss) supporting the web application (version details are often in X-Runtime, X-Version, or X-AspNet-Version). NOT STANDARD." 413 | 414 | X-Content-Type-Options: 415 | message: "X-Content-Type-Options: nosniff" 416 | category: "Server Response" 417 | description: "The only defined value, \"nosniff\", prevents Internet Explorer from MIME-sniffing a response away from the declared content-type. This also applies to Google Chrome, when downloading extensions. NOT STANDARD." 418 | 419 | X-Requested-With: 420 | message: "X-Requested-With: XMLHttpRequest" 421 | category: "Client Request" 422 | description: "Mainly used to identify Ajax requests. Most JavaScript frameworks send this field with value of XMLHttpRequest. NOT STANDARD." 423 | 424 | DNT: 425 | message: "DNT: 1 (Do Not Track Enabled) ; DNT: 0 (Do Not Track Disabled)" 426 | category: "Client Request" 427 | description: "Requests a web application to disable their tracking of a user. 428 | This is Mozilla's version of the X-Do-Not-Track header field (since Firefox 4.0 Beta 11). \ 429 | Safari and IE9 also have support for this field. 430 | On March 7, 2011, a draft proposal was submitted to IETF. The W3C Tracking Protection Working Group is producing a specification." -------------------------------------------------------------------------------- /statcode/statcode.py: -------------------------------------------------------------------------------- 1 | ######### 2 | # GLOBALS 3 | ######### 4 | 5 | 6 | import os 7 | import sys 8 | import shutil 9 | 10 | import yaml 11 | import urwid 12 | from urwid.widget import BOX, FLOW, FIXED 13 | 14 | 15 | ######### 16 | # HELPERS 17 | ######### 18 | 19 | 20 | CURR_DIR = os.path.dirname(os.path.realpath(__file__)) 21 | is_not_dumb = os.getenv("TERM", "dumb").lower() != "dumb" 22 | 23 | # Scroll actions 24 | SCROLL_LINE_UP = "line up" 25 | SCROLL_LINE_DOWN = "line down" 26 | SCROLL_PAGE_UP = "page up" 27 | SCROLL_PAGE_DOWN = "page down" 28 | SCROLL_TO_TOP = "to top" 29 | SCROLL_TO_END = "to end" 30 | 31 | # ASCII color codes 32 | YELLOW = '\033[33m' if is_not_dumb else '' 33 | RED = "\033[31m" if is_not_dumb else '' 34 | BOLD = '\033[1m' if is_not_dumb else '' 35 | UNDERLINE = '\033[4m' if is_not_dumb else '' 36 | END = "\033[0m" if is_not_dumb else '' 37 | 38 | class Scrollable(urwid.WidgetDecoration): 39 | # TODO: Fix scrolling behavior (works with up/down keys, not with cursor) <--- Now works with mouse though 40 | 41 | def sizing(self): 42 | return frozenset([BOX]) 43 | 44 | def selectable(self): 45 | return True 46 | 47 | def __init__(self, widget): 48 | """ 49 | Box widget (wrapper) that makes a fixed or flow widget vertically scrollable. 50 | """ 51 | 52 | self._trim_top = 0 53 | self._scroll_action = None 54 | self._forward_keypress = None 55 | self._old_cursor_coords = None 56 | self._rows_max_cached = 0 57 | self.__super.__init__(widget) 58 | 59 | def render(self, size, focus=False): 60 | maxcol, maxrow = size 61 | 62 | # Render complete original widget 63 | ow = self._original_widget 64 | ow_size = self._get_original_widget_size(size) 65 | canv = urwid.CompositeCanvas(ow.render(ow_size, focus)) 66 | canv_cols, canv_rows = canv.cols(), canv.rows() 67 | 68 | if canv_cols <= maxcol: 69 | pad_width = maxcol - canv_cols 70 | if pad_width > 0: # Canvas is narrower than available horizontal space 71 | canv.pad_trim_left_right(0, pad_width) 72 | 73 | if canv_rows <= maxrow: 74 | fill_height = maxrow - canv_rows 75 | if fill_height > 0: # Canvas is lower than available vertical space 76 | canv.pad_trim_top_bottom(0, fill_height) 77 | 78 | if canv_cols <= maxcol and canv_rows <= maxrow: # Canvas is small enough to fit without trimming 79 | return canv 80 | 81 | self._adjust_trim_top(canv, size) 82 | 83 | # Trim canvas if necessary 84 | trim_top = self._trim_top 85 | trim_end = canv_rows - maxrow - trim_top 86 | trim_right = canv_cols - maxcol 87 | if trim_top > 0: 88 | canv.trim(trim_top) 89 | if trim_end > 0: 90 | canv.trim_end(trim_end) 91 | if trim_right > 0: 92 | canv.pad_trim_left_right(0, -trim_right) 93 | 94 | # Disable cursor display if cursor is outside of visible canvas parts 95 | if canv.cursor is not None: 96 | curscol, cursrow = canv.cursor 97 | if cursrow >= maxrow or cursrow < 0: 98 | canv.cursor = None 99 | 100 | # Let keypress() know if original_widget should get keys 101 | self._forward_keypress = bool(canv.cursor) 102 | 103 | return canv 104 | 105 | def mouse_event(self, size, event, button, col, row, focus): 106 | if 'press' in event.split(' '): 107 | if button in (4,5): 108 | self.keypress(size, SCROLL_PAGE_DOWN if button == 5 else SCROLL_PAGE_UP) 109 | 110 | def keypress(self, size, key): 111 | if self._forward_keypress: 112 | ow = self._original_widget 113 | ow_size = self._get_original_widget_size(size) 114 | 115 | # Remember previous cursor position if possible 116 | if hasattr(ow, "get_cursor_coords"): 117 | self._old_cursor_coords = ow.get_cursor_coords(ow_size) 118 | 119 | key = ow.keypress(ow_size, key) 120 | if key is None: 121 | return None 122 | 123 | # Handle up/down, page up/down, etc 124 | command_map = self._command_map 125 | if command_map[key] == urwid.CURSOR_UP: 126 | self._scroll_action = SCROLL_LINE_UP 127 | elif command_map[key] == urwid.CURSOR_DOWN: 128 | self._scroll_action = SCROLL_LINE_DOWN 129 | elif command_map[key] == urwid.CURSOR_PAGE_UP: 130 | self._scroll_action = SCROLL_PAGE_UP 131 | elif command_map[key] == urwid.CURSOR_PAGE_DOWN: 132 | self._scroll_action = SCROLL_PAGE_DOWN 133 | elif command_map[key] == urwid.CURSOR_MAX_LEFT: # "home" 134 | self._scroll_action = SCROLL_TO_TOP 135 | elif command_map[key] == urwid.CURSOR_MAX_RIGHT: # "end" 136 | self._scroll_action = SCROLL_TO_END 137 | else: 138 | return key 139 | 140 | self._invalidate() 141 | 142 | def mouse_event(self, size, event, button, col, row, focus): 143 | ow = self._original_widget 144 | if hasattr(ow, "mouse_event"): 145 | ow_size = self._get_original_widget_size(size) 146 | row += self._trim_top 147 | return ow.mouse_event(ow_size, event, button, col, row, focus) 148 | else: 149 | return False 150 | 151 | def _adjust_trim_top(self, canv, size): 152 | """ 153 | Adjust self._trim_top according to self._scroll_action 154 | """ 155 | 156 | action = self._scroll_action 157 | self._scroll_action = None 158 | 159 | maxcol, maxrow = size 160 | trim_top = self._trim_top 161 | canv_rows = canv.rows() 162 | 163 | if trim_top < 0: 164 | # Negative trim_top values use bottom of canvas as reference 165 | trim_top = canv_rows - maxrow + trim_top + 1 166 | 167 | if canv_rows <= maxrow: 168 | self._trim_top = 0 # Reset scroll position 169 | return 170 | 171 | def ensure_bounds(new_trim_top): 172 | return max(0, min(canv_rows - maxrow, new_trim_top)) 173 | 174 | if action == SCROLL_LINE_UP: 175 | self._trim_top = ensure_bounds(trim_top - 1) 176 | elif action == SCROLL_LINE_DOWN: 177 | self._trim_top = ensure_bounds(trim_top + 1) 178 | elif action == SCROLL_PAGE_UP: 179 | self._trim_top = ensure_bounds(trim_top - maxrow + 1) 180 | elif action == SCROLL_PAGE_DOWN: 181 | self._trim_top = ensure_bounds(trim_top + maxrow - 1) 182 | elif action == SCROLL_TO_TOP: 183 | self._trim_top = 0 184 | elif action == SCROLL_TO_END: 185 | self._trim_top = canv_rows - maxrow 186 | else: 187 | self._trim_top = ensure_bounds(trim_top) 188 | 189 | if self._old_cursor_coords is not None and self._old_cursor_coords != canv.cursor: 190 | self._old_cursor_coords = None 191 | curscol, cursrow = canv.cursor 192 | if cursrow < self._trim_top: 193 | self._trim_top = cursrow 194 | elif cursrow >= self._trim_top + maxrow: 195 | self._trim_top = max(0, cursrow - maxrow + 1) 196 | 197 | def _get_original_widget_size(self, size): 198 | ow = self._original_widget 199 | sizing = ow.sizing() 200 | if FIXED in sizing: 201 | return () 202 | elif FLOW in sizing: 203 | return size[0], 204 | 205 | def get_scrollpos(self, size=None, focus=False): 206 | return self._trim_top 207 | 208 | def set_scrollpos(self, position): 209 | self._trim_top = int(position) 210 | self._invalidate() 211 | 212 | def rows_max(self, size=None, focus=False): 213 | if size is not None: 214 | ow = self._original_widget 215 | ow_size = self._get_original_widget_size(size) 216 | sizing = ow.sizing() 217 | if FIXED in sizing: 218 | self._rows_max_cached = ow.pack(ow_size, focus)[1] 219 | elif FLOW in sizing: 220 | self._rows_max_cached = ow.rows(ow_size, focus) 221 | else: 222 | raise RuntimeError("Not a flow/box widget: %r" % self._original_widget) 223 | return self._rows_max_cached 224 | 225 | 226 | class App(object): 227 | def __init__(self, content): 228 | self._palette = [ 229 | ("menu", "black", "light cyan", "standout"), 230 | ("title", "default,bold", "default", "bold") 231 | ] 232 | 233 | menu = urwid.Text([u'\n', ("menu", u" Q "), ("light gray", u" Quit")]) # TODO: Make like man pages (vim input) 234 | layout = urwid.Frame(body=content, footer=menu) 235 | 236 | main_loop = urwid.MainLoop(layout, self._palette, unhandled_input=App._handle_input, handle_mouse=True) 237 | main_loop.run() 238 | 239 | @staticmethod 240 | def _handle_input(inp): 241 | if inp in ('q', 'Q'): 242 | raise urwid.ExitMainLoop() 243 | 244 | def output_without_ui(content): 245 | size = shutil.get_terminal_size() 246 | canvas = content.render(size) 247 | text = ("\n".join(text.decode("utf-8") for text in canvas.text)).rstrip() 248 | print(text) 249 | 250 | def generate_content(status_code): 251 | try: 252 | code_descriptions, num, status_code = get_yaml_dictionary(status_code) 253 | content = code_descriptions[status_code] 254 | pile = urwid.Pile([ 255 | urwid.Text("STATCODE: The Manual for HTTP Status Codes and Headers\n", align="center"), 256 | urwid.Text(("title", "STATUS MESSAGE" if num else "HEADER INFO")), 257 | urwid.Padding( 258 | urwid.Text(''.join([str(status_code), ": " if num else ", Example= ", content["message"], '\n'])), 259 | left=5), 260 | urwid.Text(("title", "CATEGORY")), 261 | urwid.Padding(urwid.Text(''.join([content["category"], '\n'])), left=5), 262 | urwid.Text(("title", "DESCRIPTION")), 263 | urwid.Padding(urwid.Text(''.join([content["description"], '\n'])), left=5), 264 | urwid.Text(("title", "COPYRIGHT")), 265 | urwid.Padding(urwid.Text(''.join([__load_file_data(num), '\n'])), left=5), 266 | ]) 267 | padding = urwid.Padding(Scrollable(pile), left=1, right=1) 268 | 269 | return padding 270 | except KeyError: # None is used to print "not recognized", so KeyError. Other errors have nothing to do with it 271 | return None 272 | 273 | def __load_file_data(num): 274 | copyleft = yaml.safe_load(open('/'.join([CURR_DIR, "copyright_description.yml"]), 'r')) 275 | if num: 276 | return copyleft['statuscode'] 277 | else: 278 | return copyleft['headers'] 279 | 280 | def get_yaml_dictionary(status_code): 281 | try: 282 | status_code = int(status_code) 283 | num = True 284 | filename = "code_descriptions.yml" 285 | except (TypeError, ValueError): 286 | num = False 287 | filename = "header_descriptions.yml" 288 | try: 289 | code_descriptions = yaml.safe_load( 290 | open('/'.join([CURR_DIR, filename]), 'r')) 291 | except yaml.constructor.ConstructorError: 292 | print("Invalid file. Only support valid json and yaml files.") 293 | sys.exit(1) 294 | 295 | return code_descriptions, num, status_code 296 | 297 | def print_help(): 298 | print(''.join([BOLD, "statcode v1.0.0 – Made by @shobrook", END, '\n'])) 299 | print("Like man pages, but for HTTP status codes.\n") 300 | print(''.join([UNDERLINE, "Usage:", END, " $ statcode ", YELLOW, "status_code", END])) 301 | print(''.join([BOLD, "-h, --help:", END, " prints this help"])) 302 | print(''.join([BOLD, "-a,-l, --all,--list statucode", END, " prints all codes in compact version"])) 303 | print(''.join([BOLD, "-a,-l, --all,--list headers", END, " prints all headers in compact version"])) 304 | print(''.join([BOLD, "-n, --no-ui", END, " force output without UI"])) 305 | 306 | def print_all(status_code): 307 | if status_code == "statuscode": 308 | code_descriptions, num, status_code = get_yaml_dictionary(200) 309 | else: 310 | code_descriptions, num, status_code = get_yaml_dictionary("Accept") 311 | del status_code 312 | for k, v in code_descriptions.items(): 313 | print(''.join([RED, str(k), ':', END, " ", v["message"] if num else ""])) 314 | 315 | 316 | ###### 317 | # MAIN 318 | ###### 319 | 320 | 321 | def main(): 322 | if len(sys.argv) == 1 or sys.argv[1].lower() in ("-h", "--help"): 323 | print_help() 324 | elif sys.argv[1].lower() in ("-a", "-l", "--all", "--list"): 325 | try: 326 | status_code = sys.argv[2] 327 | if status_code not in ("statuscode", "headers"): 328 | print(''.join([BOLD, "Wrong parameter for this usage, see help", END])) 329 | return 330 | print_all(status_code) 331 | except IndexError: 332 | print_help() 333 | else: 334 | status_code = sys.argv[1] 335 | without_ui = len(sys.argv) > 2 and sys.argv[2].lower() in ("-n", "--no-ui") 336 | content = generate_content(status_code) 337 | 338 | if content: 339 | if without_ui or not is_not_dumb: 340 | output_without_ui(content) 341 | else: 342 | try: 343 | App(content) # Opens interface 344 | except NameError: 345 | output_without_ui(content) 346 | else: 347 | print(''.join([RED, "Sorry, statcode doesn't recognize: ", status_code, END])) 348 | 349 | return 350 | --------------------------------------------------------------------------------