123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- # #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- # <HTTPretty - HTTP client mock for Python>
- # Copyright (C) <2011-2018> Gabriel Falcao <gabriel@nacaolivre.org>
- #
- # Permission is hereby granted, free of charge, to any person
- # obtaining a copy of this software and associated documentation
- # files (the "Software"), to deal in the Software without
- # restriction, including without limitation the rights to use,
- # copy, modify, merge, publish, distribute, sublicense, and/or sell
- # copies of the Software, and to permit persons to whom the
- # Software is furnished to do so, subject to the following
- # conditions:
- #
- # The above copyright notice and this permission notice shall be
- # included in all copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- # OTHER DEALINGS IN THE SOFTWARE.
- from __future__ import unicode_literals
- import re
- from .compat import BaseClass
- from .utils import decode_utf8
- STATUSES = {
- 100: "Continue",
- 101: "Switching Protocols",
- 102: "Processing",
- 200: "OK",
- 201: "Created",
- 202: "Accepted",
- 203: "Non-Authoritative Information",
- 204: "No Content",
- 205: "Reset Content",
- 206: "Partial Content",
- 207: "Multi-Status",
- 208: "Already Reported",
- 226: "IM Used",
- 300: "Multiple Choices",
- 301: "Moved Permanently",
- 302: "Found",
- 303: "See Other",
- 304: "Not Modified",
- 305: "Use Proxy",
- 306: "Switch Proxy",
- 307: "Temporary Redirect",
- 308: "Permanent Redirect",
- 400: "Bad Request",
- 401: "Unauthorized",
- 402: "Payment Required",
- 403: "Forbidden",
- 404: "Not Found",
- 405: "Method Not Allowed",
- 406: "Not Acceptable",
- 407: "Proxy Authentication Required",
- 408: "Request a Timeout",
- 409: "Conflict",
- 410: "Gone",
- 411: "Length Required",
- 412: "Precondition Failed",
- 413: "Request Entity Too Large",
- 414: "Request-URI Too Long",
- 415: "Unsupported Media Type",
- 416: "Requested Range Not Satisfiable",
- 417: "Expectation Failed",
- 418: "I'm a teapot",
- 420: "Enhance Your Calm",
- 422: "Unprocessable Entity",
- 423: "Locked",
- 424: "Failed Dependency",
- 425: "Unordered Collection",
- 426: "Upgrade Required",
- 428: "Precondition Required",
- 429: "Too Many Requests",
- 431: "Request Header Fields Too Large",
- 444: "No Response",
- 449: "Retry With",
- 450: "Blocked by Windows Parental Controls",
- 451: "Unavailable For Legal Reasons",
- 494: "Request Header Too Large",
- 495: "Cert Error",
- 496: "No Cert",
- 497: "HTTP to HTTPS",
- 499: "Client Closed Request",
- 500: "Internal Server Error",
- 501: "Not Implemented",
- 502: "Bad Gateway",
- 503: "Service Unavailable",
- 504: "Gateway Timeout",
- 505: "HTTP Version Not Supported",
- 506: "Variant Also Negotiates",
- 507: "Insufficient Storage",
- 508: "Loop Detected",
- 509: "Bandwidth Limit Exceeded",
- 510: "Not Extended",
- 511: "Network Authentication Required",
- 598: "Network read timeout error",
- 599: "Network connect timeout error",
- }
- class HttpBaseClass(BaseClass):
- GET = 'GET'
- PUT = 'PUT'
- POST = 'POST'
- DELETE = 'DELETE'
- HEAD = 'HEAD'
- PATCH = 'PATCH'
- OPTIONS = 'OPTIONS'
- CONNECT = 'CONNECT'
- METHODS = (GET, PUT, POST, DELETE, HEAD, PATCH, OPTIONS, CONNECT)
- def parse_requestline(s):
- """
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5
- >>> parse_requestline('GET / HTTP/1.0')
- ('GET', '/', '1.0')
- >>> parse_requestline('post /testurl htTP/1.1')
- ('POST', '/testurl', '1.1')
- >>> parse_requestline('Im not a RequestLine')
- Traceback (most recent call last):
- ...
- ValueError: Not a Request-Line
- """
- methods = '|'.join(HttpBaseClass.METHODS)
- m = re.match(r'(' + methods + r')\s+(.*)\s+HTTP/(1.[0|1])', s, re.I)
- if m:
- return m.group(1).upper(), m.group(2), m.group(3)
- else:
- raise ValueError('Not a Request-Line')
- def last_requestline(sent_data):
- """
- Find the last line in sent_data that can be parsed with parse_requestline
- """
- for line in reversed(sent_data):
- try:
- parse_requestline(decode_utf8(line))
- except ValueError:
- pass
- else:
- return line
|