Ejemplo n.º 1
0
    def parse_cookies(self):
        from http.cookies import SimpleCookie, CookieError

        if not self._headers_history:
            self._parse_headers_raw()

        # Get cookies from endpoint
        cookies = []
        for header in chain(*self._headers_history):
            if len(header) > 2:
                continue

            key, value = header[0], header[1]

            if key.lower().startswith("set-cookie"):

                try:
                    cookie = SimpleCookie()
                    cookie.load(value)
                    cookies.extend(list(cookie.values()))

                    # update cookie jar
                    for morsel in list(cookie.values()):
                        if isinstance(self._cookies_jar, CookieJar):
                            self._cookies_jar.set_cookie(morsel_to_cookie(morsel))
                except CookieError as e:
                    logger.warn(e)
        self._cookies = dict([(cookie.key, cookie.value) for cookie in cookies])
        return self._cookies
Ejemplo n.º 2
0
    def parse_cookies(self):
        from http.cookies import SimpleCookie, CookieError

        if not self._headers_history:
            self._parse_headers_raw()

        # Get cookies from endpoint
        cookies = []
        for header in chain(*self._headers_history):
            if len(header) > 2:
                continue

            key, value = header[0], header[1]

            if key.lower().startswith("set-cookie"):

                try:
                    cookie = SimpleCookie()
                    cookie.load(value)
                    cookies.extend(list(cookie.values()))

                    # update cookie jar
                    for morsel in list(cookie.values()):
                        if isinstance(self._cookies_jar, CookieJar):
                            self._cookies_jar.set_cookie(
                                morsel_to_cookie(morsel))
                except CookieError as e:
                    #logger.warn(e)
                    pass
        self._cookies = dict([(cookie.key, cookie.value)
                              for cookie in cookies])
        return self._cookies
Ejemplo n.º 3
0
 def request(self, url, callback):
     cookie_text = 'uuid_n_v=v1; uuid=9EB2A080B96A11EA9F28E30FF5FFF73CB5154A84C7A94D1DAB10BB8C2D31FEB8; _csrf=448d5750ef63e51bf723695e9008d2c176352dad7c0fc460f422f517baa014ba; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1593367820; _lxsdk_cuid=172fc1f7724c8-098b7ae06bfb5b-39647b09-1fa400-172fc1f7724c8; _lxsdk=9EB2A080B96A11EA9F28E30FF5FFF73CB5154A84C7A94D1DAB10BB8C2D31FEB8; mojo-uuid=005c478c1b1c76a729dcde705c8ea14b; mojo-session-id={"id":"2749c8c2bea2ee39e46c57deda270ee5","time":1593387106486}; mojo-trace-id=6; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1593387742; __mta=147693064.1593367823108.1593387730201.1593387742702.13; _lxsdk_s=172fd4449b1-687-c0b-19b%7C%7C12'
     cookie = SimpleCookie(cookie_text)
     cookie_dict = {cookie.key: cookie.value for cookie in cookie.values()}
     request = scrapy.Request(url=url, callback=callback)
     request.cookies = cookie_dict
     return request
Ejemplo n.º 4
0
def test(raw_cookie):
    from http.cookies import SimpleCookie
    pcookie = SimpleCookie()
    pcookie.load(raw_cookie)
    cookie = {}
    for p in pcookie.values():
        cookie[p.key] = p.coded_value
    #import pdb; pdb.set_trace()
    url = 'https://radar.statcan.gc.ca/api/'
    data = {
        "variables": {},
        "query":
        '''{ studies(orderBy: CREATED_AT_DESC) {
                      nodes {
                        ...StudyData
                      }
                    }
                 }
                fragment StudyData on Study {
                  id
                  name
                  description
                  type
                  createdBy
                  createdAt
                  updatedAt
                  sourceRepositoryId
                }
            '''
    }
    r = requests.post(url=url, data=data, timeout=10, cookies=cookie)
    print(r.text, r.status_code)
Ejemplo n.º 5
0
    def _get_cookies(self, *args, **kwargs):
        u'''
        Override method in superclass to ensure HttpOnly is set appropriately.
        '''
        super_cookies = super(CkanAuthTktCookiePlugin, self). \
            _get_cookies(*args, **kwargs)

        cookies = []
        for k, v in super_cookies:
            cookie = SimpleCookie(str(v))
            morsel = list(cookie.values())[0]
            # SameSite was only added on Python 3.8
            morsel._reserved['samesite'] = 'SameSite'
            # Keep old case as it's the one used in tests, it should make no
            # difference in the browser
            morsel._reserved['httponly'] = 'HttpOnly'
            morsel._reserved['secure'] = 'Secure'

            if self.httponly:
                cookie[self.cookie_name]['HttpOnly'] = True

            if self.samesite == 'none':
                cookie[self.cookie_name]['SameSite'] = 'None'
            elif self.samesite == 'strict':
                cookie[self.cookie_name]['SameSite'] = 'Strict'
            else:
                cookie[self.cookie_name]['SameSite'] = 'Lax'

            cookies.append((k, cookie.output().replace('Set-Cookie: ', '')))

        return cookies
Ejemplo n.º 6
0
def cookies_action(cli_args):
    if cli_args.csv:
        output_writer = csv.writer(cli_args.output)

    try:
        jar = getattr(browser_cookie3, cli_args.browser)()
    except browser_cookie3.BrowserCookieError:
        die('Could not extract cookies from %s!' % cli_args.browser)

    if cli_args.url is not None:
        resolver = CookieResolver(jar)

        cookie = resolver(cli_args.url)

        if cookie is not None:

            if cli_args.csv:
                output_writer.writerow(MORSEL_CSV_HEADER)

                parsed = SimpleCookie(cookie)

                for morsel in parsed.values():
                    output_writer.writerow(format_morsel_for_csv(morsel))
            else:
                print(cookie, file=cli_args.output)
        else:
            die('Could not find relevant cookie for %s in %s!' % (cli_args.url, cli_args.browser))
    else:
        if cli_args.csv:
            output_writer.writerow(COOKIE_CSV_HEADER)

            for cookie in jar:
                output_writer.writerow(format_cookie_for_csv(cookie))
        else:
            write_jar_as_text_mozilla(jar, cli_args.output)
Ejemplo n.º 7
0
 def COOKIES(self):
     if self._COOKIES is None:
         raw_dict = SimpleCookie(self._environ.get('HTTP_COOKIE', ''))
         self._COOKIES = {}
         for cookie in raw_dict.values():
             self._COOKIES[cookie.key] = cookie.value
     return self._COOKIES
Ejemplo n.º 8
0
        def wrapped_start_response(_status, _response_headers, _exc_info=None):

            # This mess is needed for CORS - Cross Origin Resource Sharing
            # Preflight isn't getting called the way we're using it apparently. Might can get rid of * in Allow-Headers

            # Not to be confused with the game engine! :)
            origin = environ.get("HTTP_ORIGIN")

            if origin:
                _response_headers.extend([
                    ("Access-Control-Allow-Origin", origin),
                    ("Access-Control-Allow-Credentials", "true"),
                    ("Access-Control-Allow-Methods", "GET,POST"),
                    ("Access-Control-Allow-Headers", "Content-Type")
                ])

            _sid = self.factory.save(environ["wsgi.session"])

            _cookies = SimpleCookie()
            _cookies["session_id"] = _sid
            _cookie = _cookies["session_id"]
            _cookie["path"] = cookie_path
            _cookie["httponly"] = 1

            _response_headers.extend(("set-cookie", morsel.OutputString())
                                     for morsel in _cookies.values())

            return start_response(_status, _response_headers, _exc_info)
Ejemplo n.º 9
0
async def test_set_cookies(tab, recordingServer):
    """ Make sure cookies are set properly and only affect the domain they were
    set for """

    logger = Logger()

    url, reqs = recordingServer

    cookies = []
    c = Morsel()
    c.set('foo', 'bar', '')
    c['domain'] = 'localhost'
    cookies.append(c)
    c = Morsel()
    c.set('buz', 'beef', '')
    c['domain'] = 'nonexistent.example'

    settings = ControllerSettings(idleTimeout=1, timeout=60, cookies=cookies)
    controller = SinglePageController(url=url,
                                      logger=logger,
                                      service=Process(),
                                      behavior=[],
                                      settings=settings)
    await asyncio.wait_for(controller.run(), settings.timeout * 2)

    assert len(reqs) == 1
    req = reqs[0]
    reqCookies = SimpleCookie(req.headers['cookie'])
    assert len(reqCookies) == 1
    c = next(iter(reqCookies.values()))
    assert c.key == cookies[0].key
    assert c.value == cookies[0].value
Ejemplo n.º 10
0
    def load_account_from_cookies(self):

        # Get any cookies from the request
        cookie_data = self.headers.get('Cookie')

        account = None
        if cookie_data is not None:

            # Parse the cookie data
            parsed_cookie = SimpleCookie(cookie_data)

            cookie = {}

            # Look through cookies for a session
            for c in parsed_cookie.values():

                if c.key == "session":

                    # Check if the session is set and try to load an account from it
                    if c.coded_value != "":
                        account = load_account_from_session(c.coded_value)

                    break

        return account
Ejemplo n.º 11
0
def requests_1_sde(one_input):
    try:
        ipt_data = str(one_input)
    except UnicodeDecodeError:
        sys.exit(0)

    C = SimpleCookie(ipt_data)
    for morsel in C.values():
        cookie = morsel_to_cookie(morsel)
        print(cookie)

    ipt_data2 = "{}={}; path=/; domain=.test.com".format(ipt_data, ipt_data)
    C = SimpleCookie(ipt_data2)
    for morsel in C.values():
        cookie = morsel_to_cookie(morsel)
        print(cookie)
Ejemplo n.º 12
0
class Response(object):
    default_status_code = STATUS.OK

    def __init__(self, content="", status_code=None, content_type="text/html", status_message=None, **kwargs):
        self.content = content
        self.encoding = kwargs.get("encoding", DEFAULT_ENCODING)
        if status_code is None:
            status_code = self.default_status_code
        self.status_code = status_code
        self.status_message = status_message
        self.headers = {}
        self.headers["Content-Type"] = content_type
        self.cookies = SimpleCookie()

    def __iter__(self):
        """WSGI Iterates response content."""
        value = self.content
        if not hasattr(value, "__iter__") or isinstance(value, (bytes, str)):
            value = [value]

        for chunk in value:
            # Don't encode when already bytes or Content-Encoding set
            if not isinstance(chunk, bytes):
                chunk = chunk.encode(self.encoding)
            yield chunk

    def build_headers(self):
        """
        Return the list of headers as two-tuples
        """
        if not "Content-Type" in self.headers:
            content_type = self.content_type
            if self.encoding != DEFAULT_ENCODING:
                content_type += "; charset=%s" % self.encoding
            self.headers["Content-Type"] = content_type

        headers = list(self.headers.items())
        # Append cookies
        headers += [("Set-Cookie", cookie.OutputString()) for cookie in self.cookies.values()]
        return headers

    def add_cookie(self, key, value, **attrs):
        """
        Finer control over cookies.  Allow specifying an Morsel arguments.
        """
        if attrs:
            c = Morsel()
            c.set(key, value, **attrs)
            self.cookies[key] = c
        else:
            self.cookies[key] = value

    @property
    def status(self):
        """Allow custom status messages"""
        message = self.status_message
        if message is None:
            message = STATUS[self.status_code]
        return "%s %s" % (self.status_code, message)
def identity(req, resp):
    identity = int(time.time())

    cookie = SimpleCookie()
    cookie['identity'] = 'USER: {}'.format(identity)

    for set_cookie in cookie.values():
        resp.headers.add_header('Set-Cookie', set_cookie.OutputString())
    return b'Go back to <a href="/">index</a> to check your identity'
Ejemplo n.º 14
0
 def request(self, url, callback, err_callback):
     cookie_text = 'uuid_n_v=v1; uuid=9EB2A080B96A11EA9F28E30FF5FFF73CB5154A84C7A94D1DAB10BB8C2D31FEB8; _csrf=448d5750ef63e51bf723695e9008d2c176352dad7c0fc460f422f517baa014ba; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1593367820; _lxsdk_cuid=172fc1f7724c8-098b7ae06bfb5b-39647b09-1fa400-172fc1f7724c8; _lxsdk=9EB2A080B96A11EA9F28E30FF5FFF73CB5154A84C7A94D1DAB10BB8C2D31FEB8; mojo-uuid=005c478c1b1c76a729dcde705c8ea14b; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1593500815; __mta=147693064.1593367823108.1593500797746.1593500814850.16; _lxsdk_s=173065bd698-1d5-a44-de2%7C%7C1'
     cookie = SimpleCookie(cookie_text)
     cookie_dict = {cookie.key: cookie.value for cookie in cookie.values()}
     request = scrapy.Request(url=url,
                              callback=callback,
                              errback=err_callback)
     request.cookies = cookie_dict
     return request
Ejemplo n.º 15
0
def cookie (s):
    """ argparse: Cookie """
    c = SimpleCookie (s)
    # for some reason the constructor does not raise an exception if the cookie
    # supplied is invalid. It’ll simply be empty.
    if len (c) != 1:
        raise argparse.ArgumentTypeError ('Invalid cookie')
    # we want a single Morsel
    return next (iter (c.values ()))
Ejemplo n.º 16
0
def get_me(cookies: SimpleCookie):
    request_cookies = {c.key: c.value for c in cookies.values()}
    resp = requests.get(API_URL + "/users/me", cookies=request_cookies)
    if not resp.ok:
        if resp.status_code == http.HTTPStatus.UNAUTHORIZED:
            raise tornado.web.HTTPError(http.HTTPStatus.UNAUTHORIZED)
        raise tornado.web.HTTPError()
    resp.raise_for_status()
    user = resp.json().get('user')
    return user
Ejemplo n.º 17
0
 def getCookies(self):
     '''
     从字符串中格式化出字典形式的Cookies
     '''
     items = self.data
     for item in items:
         if 'cookie' in item or 'Cookie' in item:
             cookies = SimpleCookie(item[7:])
             return {i.key: i.value for i in cookies.values()}
     return {}
Ejemplo n.º 18
0
 def _set_cookies(self, appid, headers):
     if appid not in self.cookies:
         self._init_cookies(appid)
     for sc in headers.get_list("Set-Cookie"):
         c = SimpleCookie(sc)
         for morsel in c.values():
             if morsel.key not in ['data_bizuin', 'slave_user', 'bizuin']:
                 if morsel.value and morsel.value != 'EXPIRED':
                     self.cookies[appid][morsel.key] = morsel.value
                 else:
                     self.cookies[appid].pop(morsel.key, None)
Ejemplo n.º 19
0
 def _set_cookies(self, appid, headers):
     if appid not in self.cookies:
         self._init_cookies(appid)
     for sc in headers.get_list("Set-Cookie"):
         c = SimpleCookie(sc)
         for morsel in c.values():
             if morsel.key not in ['data_bizuin', 'slave_user', 'bizuin']:
                 if morsel.value and morsel.value != 'EXPIRED':
                     self.cookies[appid][morsel.key] = morsel.value
                 else:
                     self.cookies[appid].pop(morsel.key, None)
    def _handle_cookies(self, response):
        # type: (httplib.HTTPResponse) -> None
        """
		Parse cookies from |HTTP| response and store for next request.

		:param httplib.HTTPResponse: The |HTTP| response.
		"""
        # FIXME: this cookie handling doesn't respect path, domain and expiry
        cookies = SimpleCookie()
        cookies.load(response.getheader('set-cookie', ''))
        self.cookies.update(
            dict((cookie.key, cookie.value) for cookie in cookies.values()))
Ejemplo n.º 21
0
class Response:
    """
    Describes an HTTP response. Currently very simple since the actual body
    of the request is handled separately.
    """
    def __init__(self):
        """
        Create a new Response defaulting to HTML content and "200 OK" status
        """
        self.status = "200 OK"
        self.headers = HeaderDict({"content-type": "text/html; charset=UTF-8"})
        self.cookies = SimpleCookie()

    def set_content_type(self, type_):
        """
        Sets the Content-Type header
        """
        self.headers["content-type"] = type_

    def get_content_type(self):
        return self.headers.get("content-type", None)

    def send_redirect(self, url):
        """
        Send an HTTP redirect response to (target `url`)
        """
        if "\n" in url or "\r" in url:
            raise webob.exc.HTTPInternalServerError(
                "Invalid redirect URL encountered.")
        raise webob.exc.HTTPFound(location=url,
                                  headers=self.wsgi_headeritems())

    def wsgi_headeritems(self):
        """
        Return headers in format appropriate for WSGI `start_response`
        """
        result = self.headers.headeritems()
        # Add cookie to header
        for crumb in self.cookies.values():
            header, value = str(crumb).split(': ', 1)
            result.append((header, value))
        return result

    def wsgi_status(self):
        """
        Return status line in format appropriate for WSGI `start_response`
        """
        if isinstance(self.status, int):
            exception = webob.exc.status_map.get(self.status)
            return "%d %s" % (exception.code, exception.title)
        else:
            return self.status
Ejemplo n.º 22
0
def parse_cookie_str(cookie_str: str) -> Dict:
    sc = SimpleCookie()
    sc.load(cookie_str)
    cd = {}
    for morsal in sc.values():
        cd["name"] = morsal.key
        cd["value"] = morsal.value
        for k, v in morsal.items():
            if k in SkippedValue:
                continue
            if v:
                cd[k if k != "httponly" else "httpOnly"] = v
    return cd
Ejemplo n.º 23
0
 def clean_cookie(self):
     """Only clean the cookie from headers and return self."""
     if not self.is_cookie_necessary:
         return self
     headers = self.request.get('headers', {})
     cookies = SimpleCookie(headers['Cookie'])
     for k, v in cookies.items():
         new_cookie = '; '.join(
             [i.OutputString() for i in cookies.values() if i != v])
         new_request = deepcopy(self.request)
         new_request['headers']['Cookie'] = new_cookie
         self._add_task('Cookie', k, new_request)
     return self
Ejemplo n.º 24
0
class HttpResponse:
    def __init__(self, body, *, status=200, content_type='text/html'):
        if isinstance(body, str):
            body = body.encode('utf-8')
        self._body = body
        self._status = int(status)
        self._cookies = SimpleCookie()
        self._headers = [
            ('Content-Encoding', 'UTF-8'),
            ('Content-Type', content_type),
            ('Content-Length', str(len(body))),
        ]
        self._content_type = content_type

    def __call__(self, start_response):
        status = status_line(self._status)
        start_response(status, self._get_headers())
        return [self._body]


    def set_cookie(self, name, value='', max_age=None, path='/',
                   domain=None, secure=False, httponly=False):
        self._cookies[name] = value
        if max_age is not None:
            self._cookies[name]['max-age'] = max_age
            if not max_age:
                expires_date = 'Thu, 01-Jan-1970 00:00:00 GMT'
            else:
                dt = formatdate(time.time() + max_age)
                expires_date = '%s-%s-%s GMT' % (dt[:7], dt[8:11], dt[12:25])

            self._cookies[name]['expires'] = expires_date


        if path is not None:
            self._cookies[name]['path'] = path
        if domain is not None:
            self._cookies[name]['domain'] = domain
        if secure:
            self._cookies[name]['secure'] = True
        if httponly:
            self._cookies[name]['httponly'] = True

    def delete_cookie(self, key, path='/', domain=None):
        self.set_cookie(key, max_age=0, path=path, domain=domain)

    def _get_headers(self):
        headers = [(x.encode('ascii'), y.encode('ascii')) for x, y in self._headers]
        for c in self._cookies.values():
            headers.append((b'Set-Cookie', c.output(header='').encode('ascii')))
        return headers
Ejemplo n.º 25
0
    def _load_cookies(self, data):
        """将str或dict或tuple-pair加载为cookie
        由于标准库的 load() 方法不支持list, 所以对list单独处理
        """
        if utils.like_list(data):
            data = collections.OrderedDict(data)

        simple_cookie = SimpleCookie(data)

        pairs = [(c.key, c.value) for c in simple_cookie.values()]

        pairs.sort(key=lambda item: self._find_key_pos(data, item[0]))

        self.update(pairs)
Ejemplo n.º 26
0
    def _hide_request_sensitive_data(self, request):
        request.body = self._replace_sensitive_data(request.body)

        if 'Cookie' in request.headers:
            cookie_string = request.headers['Cookie']
            cookie = SimpleCookie()
            cookie.load(str(cookie_string))
            cookies = [
                c.output(header='').strip() for c in list(cookie.values())
            ]
            request.headers['Cookie'] = '; '.join(
                self._filter_cookies(cookies))

        request.uri = request.uri.replace(self.real_login, self.fake_login)
Ejemplo n.º 27
0
    def cookies(self):
        if self._cached_cookies is None:
            cookie_header = self.get_header('Cookie', default='')
            parser = SimpleCookie()
            for cookie_part in cookie_header.split('; '):
                try:
                    parser.load(cookie_part)
                except CookieError:
                    log.error('Invalid Cookie: %s' % cookie_part)
            cookies = {}
            for morsel in parser.values():
                cookies[morsel.key] = morsel.value
            self._cached_cookies = cookies

        return self._cached_cookies.copy()
Ejemplo n.º 28
0
    def load_account_from_cookies(self):
        cookie_data = self.headers.get('Cookie')
        account = None
        if (cookie_data is not None):
            parsed_cookie = SimpleCookie(cookie_data)
            cookie = {}
            for c in parsed_cookie.values():
                cookie[c.key] = c.coded_value

            if "session" in cookie and cookie["session"] != "":
                try:
                    account = Account.sessions[cookie.get("session")]
                except KeyError:
                    pass
        return account
Ejemplo n.º 29
0
class HttpResponse:
    def __init__(self, body, *, status=200, content_type="text/html"):
        if isinstance(body, str):
            body = body.encode("utf-8")
        self._body = body
        self._status = int(status)
        self._cookies = SimpleCookie()
        self._headers = [
            ("Content-Encoding", "UTF-8"),
            ("Content-Type", content_type),
            ("Content-Length", str(len(body))),
        ]
        self._content_type = content_type

    def __call__(self, start_response):
        status = status_line(self._status)
        start_response(status, self._get_headers())
        return [self._body]

    def set_cookie(self, name, value="", max_age=None, path="/", domain=None, secure=False, httponly=False):
        self._cookies[name] = value
        if max_age is not None:
            self._cookies[name]["max-age"] = max_age
            if not max_age:
                expires_date = "Thu, 01-Jan-1970 00:00:00 GMT"
            else:
                dt = formatdate(time.time() + max_age)
                expires_date = "%s-%s-%s GMT" % (dt[:7], dt[8:11], dt[12:25])

            self._cookies[name]["expires"] = expires_date

        if path is not None:
            self._cookies[name]["path"] = path
        if domain is not None:
            self._cookies[name]["domain"] = domain
        if secure:
            self._cookies[name]["secure"] = True
        if httponly:
            self._cookies[name]["httponly"] = True

    def delete_cookie(self, key, path="/", domain=None):
        self.set_cookie(key, max_age=0, path=path, domain=domain)

    def _get_headers(self):
        headers = [(x.encode("ascii"), y.encode("ascii")) for x, y in self._headers]
        for c in self._cookies.values():
            headers.append((b"Set-Cookie", c.output(header="").encode("ascii")))
        return headers
Ejemplo n.º 30
0
 def __init__(self):
     self.pool = redis.ConnectionPool(host='192.168.2.74')
     self.r = redis.Redis(connection_pool=self.pool)
     # 初始话selenium
     self.option = Options()
     self.account_list = []
     # self.option.add_argument('--headless')
     self.option.add_argument('--disable-gpu')
     self.option.add_argument('--ignore-certificate-errors')
     cookie = 'SINAGLOBAL=2079748157535.4148.1562656336661; _s_tentry=-; Apache=6138501991200.746.1578878421920; ULV=1578878421977:15:2:1:6138501991200.746.1578878421920:1577966277068; login_sid_t=4041ea8280741ef6a60c24968bf7ed52; cross_origin_proto=SSL; WBtopGlobal_register_version=307744aa77dd5677; secsys_id=d3e0da3cb4c0fb539669ba32cf3d0751; ALF=1610614524; SSOLoginState=1579078525; SCF=AncjsCf7zbAbUzNBAKOizieYzy1LkJJc5eum43dQUuxtx1pHo_67XdOiZfYQ5pr9nZip8tM5XaA6dshnDiGWE1s.; SUB=_2A25zGqMmDeRhGeRG4loZ8S_LzTmIHXVQUZPurDV8PUNbmtAfLU7mkW9NTeZINGNGUkBCRAcmRLrsnuAuL4q2BGyT; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WFOfqD9fkE8ALcVFJfdDIpl5JpX5KzhUgL.FozR1KnReK2NSo-2dJLoIfQLxK-L12qL1KqLxKBLBonLB-2LxK-L1K5L12BLxK-LB-BL1KMLxKBLBo.L1-qLxK-LB.-L1hnLxK.L1-2LB.-LxK-L1K-L122LxKqL1hnL1K2LxK-L12-LB.zt; SUHB=0S7yje5GVwlj4F; wvr=6; UOR=,,v3.jqsocial.com; webim_unReadCount=%7B%22time%22%3A1579140897294%2C%22dm_pub_total%22%3A5%2C%22chat_group_client%22%3A0%2C%22allcountNum%22%3A43%2C%22msgbox%22%3A0%7D; WBStorage=42212210b087ca50|undefined'
     cookies = SimpleCookie(cookie)
     self.driver = webdriver.Chrome(options=self.option)
     self.driver.add_cookie({i.key: i.value for i in cookies.values()})
     self.driver.set_script_timeout(1)
     # 页面加载超时时间
     self.driver.set_page_load_timeout(10)
     # 微博主页
     self.login_url = 'https://weibo.com/'
Ejemplo n.º 31
0
 def __init__(self, autologin_url, auth_cookies=None, logout_url=None,
              splash_url=None, login_url=None, username=None, password=None,
              user_agent=None):
     self.autologin_url = autologin_url
     self.splash_url = splash_url
     self.login_url = login_url
     self.username = username
     self.password = password
     self.user_agent = user_agent
     if auth_cookies:
         cookies = SimpleCookie()
         cookies.load(auth_cookies)
         self.auth_cookies = [
             {'name': m.key, 'value': m.value} for m in cookies.values()]
         self.logged_in = True
     else:
         self.auth_cookies = None
         self.logged_in = False
     self.logout_urls = set()
     if logout_url:
         self.logout_urls.add(logout_url)
Ejemplo n.º 32
0
    def __init__(self, iterable, status, headers):
        self._text = None

        self._content = b''.join(iterable)
        if hasattr(iterable, 'close'):
            iterable.close()

        self._status = status
        self._status_code = int(status[:3])
        self._headers = CiDict(headers)

        cookies = SimpleCookie()
        for name, value in headers.items():
            if name.lower() == 'set-cookie':
                cookies.load(value)

        self._cookies = dict(
            (morsel.key, Cookie(morsel)) for morsel in cookies.values())

        self._encoding = content_type_encoding(
            self._headers.get('content-type'))
Ejemplo n.º 33
0
    def _check_cookies(self, vector, configs):
        """
        Checks the vector's cookies. If values were not specified in the cookies object, then the 'Cookies' header
        is checked. Each cookie in the header is parsed manually.
        :param vector: vector dictionary
        :param configs: AVA configs
        """
        cookies = vector['cookies']

        # check headers if not cookies
        if not cookies and 'Cookie' in vector['headers']:
            header = vector['headers']['Cookie']
            simple = SimpleCookie()

            # convert from 'key=value; key=value'
            simple.load(header)
            for morsel in simple.values():
                cookies[morsel.key] = morsel.value

        # add configs
        cookies.update(configs['cookies'])
Ejemplo n.º 34
0
def authenticate_via_cookie(c_user=None, xs=None):
    """Authenticate with facebook via cookie

    This function will look for cookie saved in '.fb_cookies'.
    The cookies values can be overriden by the input parameters.
    """
    global ACCESS_TOKEN

    authenticated = _authenticate_via_saved_token()

    if not authenticated:
        cookies = SimpleCookie()
        try:
            with open(COOKIES_FILE) as f:
                saved_cookies = f.read()
                cookies.load(saved_cookies)
        except IOError:
            pass

        if c_user:
            cookies['c_user'] = c_user
        if xs:
            cookies['xs'] = xs
        cookie_str = ';'.join(c.key + '=' + c.value for c in cookies.values())

        request = Request(
            oauth_url(APP_ID, 'https://www.facebook.com/connect/login_success.html', AUTH_SCOPE),
            headers={'Cookie': cookie_str})

        opener = urlopen(request)
        try:
            opener.read()
            url = opener.geturl()
        finally:
            opener.close()

        params = parse_qs(urlparse(url).fragment)
        authenticated = _save_access_token(params)

        return authenticated
Ejemplo n.º 35
0
class StreamResponse(collections.MutableMapping, HeadersMixin):

    _length_check = True

    def __init__(self, *, status=200, reason=None, headers=None):
        self._body = None
        self._keep_alive = None
        self._chunked = False
        self._compression = False
        self._compression_force = False
        self._cookies = SimpleCookie()

        self._req = None
        self._payload_writer = None
        self._eof_sent = False
        self._body_length = 0
        self._state = {}

        if headers is not None:
            self._headers = CIMultiDict(headers)
        else:
            self._headers = CIMultiDict()

        self.set_status(status, reason)

    @property
    def prepared(self):
        return self._payload_writer is not None

    @property
    def task(self):
        return getattr(self._req, 'task', None)

    @property
    def status(self):
        return self._status

    @property
    def chunked(self):
        return self._chunked

    @property
    def compression(self):
        return self._compression

    @property
    def reason(self):
        return self._reason

    def set_status(self, status, reason=None, _RESPONSES=RESPONSES):
        assert not self.prepared, \
            'Cannot change the response status code after ' \
            'the headers have been sent'
        self._status = int(status)
        if reason is None:
            try:
                reason = _RESPONSES[self._status][0]
            except Exception:
                reason = ''
        self._reason = reason

    @property
    def keep_alive(self):
        return self._keep_alive

    def force_close(self):
        self._keep_alive = False

    @property
    def body_length(self):
        return self._body_length

    @property
    def output_length(self):
        warnings.warn('output_length is deprecated', DeprecationWarning)
        return self._payload_writer.buffer_size

    def enable_chunked_encoding(self, chunk_size=None):
        """Enables automatic chunked transfer encoding."""
        self._chunked = True

        if hdrs.CONTENT_LENGTH in self._headers:
            raise RuntimeError("You can't enable chunked encoding when "
                               "a content length is set")
        if chunk_size is not None:
            warnings.warn('Chunk size is deprecated #1615', DeprecationWarning)

    def enable_compression(self, force=None):
        """Enables response compression encoding."""
        # Backwards compatibility for when force was a bool <0.17.
        if type(force) == bool:
            force = ContentCoding.deflate if force else ContentCoding.identity
        elif force is not None:
            assert isinstance(force, ContentCoding), ("force should one of "
                                                      "None, bool or "
                                                      "ContentEncoding")

        self._compression = True
        self._compression_force = force

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self):
        return self._cookies

    def set_cookie(self, name, value, *, expires=None,
                   domain=None, max_age=None, path='/',
                   secure=None, httponly=None, version=None):
        """Set or update response cookie.

        Sets new cookie or updates existent with new value.
        Also updates only those params which are not None.
        """

        old = self._cookies.get(name)
        if old is not None and old.coded_value == '':
            # deleted cookie
            self._cookies.pop(name, None)

        self._cookies[name] = value
        c = self._cookies[name]

        if expires is not None:
            c['expires'] = expires
        elif c.get('expires') == 'Thu, 01 Jan 1970 00:00:00 GMT':
            del c['expires']

        if domain is not None:
            c['domain'] = domain

        if max_age is not None:
            c['max-age'] = max_age
        elif 'max-age' in c:
            del c['max-age']

        c['path'] = path

        if secure is not None:
            c['secure'] = secure
        if httponly is not None:
            c['httponly'] = httponly
        if version is not None:
            c['version'] = version

    def del_cookie(self, name, *, domain=None, path='/'):
        """Delete cookie.

        Creates new empty expired cookie.
        """
        # TODO: do we need domain/path here?
        self._cookies.pop(name, None)
        self.set_cookie(name, '', max_age=0,
                        expires="Thu, 01 Jan 1970 00:00:00 GMT",
                        domain=domain, path=path)

    @property
    def content_length(self):
        # Just a placeholder for adding setter
        return super().content_length

    @content_length.setter
    def content_length(self, value):
        if value is not None:
            value = int(value)
            if self._chunked:
                raise RuntimeError("You can't set content length when "
                                   "chunked encoding is enable")
            self._headers[hdrs.CONTENT_LENGTH] = str(value)
        else:
            self._headers.pop(hdrs.CONTENT_LENGTH, None)

    @property
    def content_type(self):
        # Just a placeholder for adding setter
        return super().content_type

    @content_type.setter
    def content_type(self, value):
        self.content_type  # read header values if needed
        self._content_type = str(value)
        self._generate_content_type_header()

    @property
    def charset(self):
        # Just a placeholder for adding setter
        return super().charset

    @charset.setter
    def charset(self, value):
        ctype = self.content_type  # read header values if needed
        if ctype == 'application/octet-stream':
            raise RuntimeError("Setting charset for application/octet-stream "
                               "doesn't make sense, setup content_type first")
        if value is None:
            self._content_dict.pop('charset', None)
        else:
            self._content_dict['charset'] = str(value).lower()
        self._generate_content_type_header()

    @property
    def last_modified(self, _LAST_MODIFIED=hdrs.LAST_MODIFIED):
        """The value of Last-Modified HTTP header, or None.

        This header is represented as a `datetime` object.
        """
        httpdate = self.headers.get(_LAST_MODIFIED)
        if httpdate is not None:
            timetuple = parsedate(httpdate)
            if timetuple is not None:
                return datetime.datetime(*timetuple[:6],
                                         tzinfo=datetime.timezone.utc)
        return None

    @last_modified.setter
    def last_modified(self, value):
        if value is None:
            self.headers.pop(hdrs.LAST_MODIFIED, None)
        elif isinstance(value, (int, float)):
            self.headers[hdrs.LAST_MODIFIED] = time.strftime(
                "%a, %d %b %Y %H:%M:%S GMT", time.gmtime(math.ceil(value)))
        elif isinstance(value, datetime.datetime):
            self.headers[hdrs.LAST_MODIFIED] = time.strftime(
                "%a, %d %b %Y %H:%M:%S GMT", value.utctimetuple())
        elif isinstance(value, str):
            self.headers[hdrs.LAST_MODIFIED] = value

    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):
        params = '; '.join("%s=%s" % i for i in self._content_dict.items())
        if params:
            ctype = self._content_type + '; ' + params
        else:
            ctype = self._content_type
        self.headers[CONTENT_TYPE] = ctype

    def _do_start_compression(self, coding):
        if coding != ContentCoding.identity:
            self.headers[hdrs.CONTENT_ENCODING] = coding.value
            self._payload_writer.enable_compression(coding.value)
            # Compressed payload may have different content length,
            # remove the header
            self._headers.popall(hdrs.CONTENT_LENGTH, None)

    def _start_compression(self, request):
        if self._compression_force:
            self._do_start_compression(self._compression_force)
        else:
            accept_encoding = request.headers.get(
                hdrs.ACCEPT_ENCODING, '').lower()
            for coding in ContentCoding:
                if coding.value in accept_encoding:
                    self._do_start_compression(coding)
                    return

    async def prepare(self, request):
        if self._eof_sent:
            return
        if self._payload_writer is not None:
            return self._payload_writer

        await request._prepare_hook(self)
        return self._start(request)

    def _start(self, request,
               HttpVersion10=HttpVersion10,
               HttpVersion11=HttpVersion11,
               CONNECTION=hdrs.CONNECTION,
               DATE=hdrs.DATE,
               SERVER=hdrs.SERVER,
               CONTENT_TYPE=hdrs.CONTENT_TYPE,
               CONTENT_LENGTH=hdrs.CONTENT_LENGTH,
               SET_COOKIE=hdrs.SET_COOKIE,
               SERVER_SOFTWARE=SERVER_SOFTWARE,
               TRANSFER_ENCODING=hdrs.TRANSFER_ENCODING):
        self._req = request

        keep_alive = self._keep_alive
        if keep_alive is None:
            keep_alive = request.keep_alive
        self._keep_alive = keep_alive

        version = request.version
        writer = self._payload_writer = request._payload_writer

        headers = self._headers
        for cookie in self._cookies.values():
            value = cookie.output(header='')[1:]
            headers.add(SET_COOKIE, value)

        if self._compression:
            self._start_compression(request)

        if self._chunked:
            if version != HttpVersion11:
                raise RuntimeError(
                    "Using chunked encoding is forbidden "
                    "for HTTP/{0.major}.{0.minor}".format(request.version))
            writer.enable_chunking()
            headers[TRANSFER_ENCODING] = 'chunked'
            if CONTENT_LENGTH in headers:
                del headers[CONTENT_LENGTH]
        elif self._length_check:
            writer.length = self.content_length
            if writer.length is None:
                if version >= HttpVersion11:
                    writer.enable_chunking()
                    headers[TRANSFER_ENCODING] = 'chunked'
                    if CONTENT_LENGTH in headers:
                        del headers[CONTENT_LENGTH]
                else:
                    keep_alive = False

        headers.setdefault(CONTENT_TYPE, 'application/octet-stream')
        headers.setdefault(DATE, rfc822_formatted_time())
        headers.setdefault(SERVER, SERVER_SOFTWARE)

        # connection header
        if CONNECTION not in headers:
            if keep_alive:
                if version == HttpVersion10:
                    headers[CONNECTION] = 'keep-alive'
            else:
                if version == HttpVersion11:
                    headers[CONNECTION] = 'close'

        # status line
        status_line = 'HTTP/{}.{} {} {}\r\n'.format(
            version[0], version[1], self._status, self._reason)
        writer.write_headers(status_line, headers)

        return writer

    async def write(self, data):
        assert isinstance(data, (bytes, bytearray, memoryview)), \
            "data argument must be byte-ish (%r)" % type(data)

        if self._eof_sent:
            raise RuntimeError("Cannot call write() after write_eof()")
        if self._payload_writer is None:
            raise RuntimeError("Cannot call write() before prepare()")

        await self._payload_writer.write(data)

    async def drain(self):
        assert not self._eof_sent, "EOF has already been sent"
        assert self._payload_writer is not None, \
            "Response has not been started"
        warnings.warn("drain method is deprecated, use await resp.write()",
                      DeprecationWarning,
                      stacklevel=2)
        await self._payload_writer.drain()

    async def write_eof(self, data=b''):
        assert isinstance(data, (bytes, bytearray, memoryview)), \
            "data argument must be byte-ish (%r)" % type(data)

        if self._eof_sent:
            return

        assert self._payload_writer is not None, \
            "Response has not been started"

        await self._payload_writer.write_eof(data)
        self._eof_sent = True
        self._req = None
        self._body_length = self._payload_writer.output_size
        self._payload_writer = None

    def __repr__(self):
        if self._eof_sent:
            info = "eof"
        elif self.prepared:
            info = "{} {} ".format(self._req.method, self._req.path)
        else:
            info = "not prepared"
        return "<{} {} {}>".format(self.__class__.__name__,
                                   self.reason, info)

    def __getitem__(self, key):
        return self._state[key]

    def __setitem__(self, key, value):
        self._state[key] = value

    def __delitem__(self, key):
        del self._state[key]

    def __len__(self):
        return len(self._state)

    def __iter__(self):
        return iter(self._state)

    def __hash__(self):
        return hash(id(self))
Ejemplo n.º 36
0
class HTTPResponse( Plugin ):
    """Plugin to encapsulate HTTP response."""

    implements( IHTTPResponse )

    start_response = False
    """Response headers are already sent on the connection."""

    write_buffer = []
    """Either a list of byte-string buffered by write() method. Or a generator
    function created via chunk_generator() method."""

    flush_callback = None
    """Flush callback subscribed using flush() method."""

    finish_callback = None
    """Finish callback subscribed using set_finish_callback() method."""

    finished = False
    """A request is considered finished when there is no more response data to
    be sent for the on-going request. This is typically indicated by flushing
    the response with finishing=True argument."""

    def __init__( self, request ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.__init__`
        interface method."""
        # Initialize response attributes
        self.statuscode = b'200'
        self.reason = http.client.responses[ int(self.statuscode) ]
        self.version = request.httpconn.version
        self.headers = {}
        self.body = b''
        self.chunk_generator = None
        self.trailers = {}

        self.setcookies = SimpleCookie()

        # Initialize framework attributes
        self.request = request
        self.context = h.Context()
        self.media_type = None
        self.content_coding = None
        self.charset = self.webapp['encoding']
        self.language = self.webapp['language']

        # Book keeping
        self.httpconn = request.httpconn
        self.start_response = False
        self.write_buffer = []
        self.finished = False
        self.flush_callback = None
        self.finish_callback = None

    #---- IHTTPResponse APIs

    def set_status( self, code ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.set_status`
        interface method."""
        if isinstance(code, int) :
            self.statuscode = str(code).encode('utf-8')
        elif isinstance(code, str) :
            self.statuscode = code.encode('utf-8')
        else :
            self.statuscode = code
        return self.statuscode

    def set_header( self, name, value ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.set_header`
        interface method."""
        value = value if isinstance( value, bytes ) \
                      else str( value ).encode('utf-8')
        self.headers[ name ] = value
        return value

    def add_header( self, name, value ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.add_header`
        interface method."""
        value = value if isinstance(value,bytes) else str(value).encode('utf-8')
        pvalue = self.headers.get( name, b'' )
        self.headers[name] = b','.join([pvalue, value]) if pvalue else None
        return self.headers[name]

    def set_trailer( self, name, value ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.set_trailer`
        interface method."""
        value = value if isinstance(value,bytes) else str(value).encode('utf=8')
        self.trailers[name] = value
        return value

    def add_trailer( self, name, value ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.add_trailer`
        interface method."""
        value = value if isinstance(value,bytes) else str(value).encode('utf-8')
        pvalue = self.trailers.get(name, b'')
        self.trailers[name] = b','.join([pvalue, value]) if pvalue else None
        return self.trailers[name]

    def set_cookie( self, name, value, **kwargs ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.set_cookie`
        interface method."""
        return self.request.cookie.set_cookie(
                    self.setcookies, name, value, **kwargs )

    def set_secure_cookie( self, name, value, expires_days=30, **kwargs ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.set_secure_cookie`
        interface method."""
        cookie = self.request.cookie
        value = cookie.create_signed_value(name, value)
        return cookie.set_cookie( self.setcookies, name, value, **kwargs )

    def clear_cookie( self, name, path="/", domain=None ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.clear_cookie`
        interface method."""
        value = self.setcookies[ name ]
        expires = dt.datetime.utcnow() - dt.timedelta(days=365)
        self.request.cookie.set_cookie( 
                    self.setcookies, name, "", path=path, expires=expires, 
                    domain=domain )
        return value

    def clear_all_cookies(self):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.clear_all_cookies`
        interface method."""
        list( map( self.clear_cookie, self.setcookies.keys() ))
        return None

    def set_finish_callback(self, callback):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.set_finish_callback`
        interface method."""
        self.finish_callback = callback

    def has_finished( self ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.has_finished`
        interface method."""
        return self.finished

    def isstarted( self ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.isstarted`
        interface method."""
        return self.start_response

    def ischunked( self ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.ischunked`
        interface method."""
        vals = dict( h.parse_transfer_encoding(
                        self.headers.get( 'transfer_encoding', b'' ))).keys()
        return b'chunked' in list( vals )

    def write( self, data ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.write`
        interface method."""
        if self.has_finished() :
            raise Exception( "Cannot write() after the response is finished." )

        data = data.encode(self.charset) if isinstance(data, str) else data
        self.write_buffer = self.write_buffer or []
        self.write_buffer.append( data )

    def flush( self, finishing=False, callback=None ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.flush`
        interface method."""
        if callback :
            self.flush_callback = callback

        self.finished = finishing

        if callable( self.write_buffer ) :
            self._flush_chunk( finishing )
        else :
            self._flush_body( finishing )

    def httperror( self, statuscode=b'500', message=b'' ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.httperror`
        interface method."""
        self.statuscode = statuscode
        self.write( message ) if message else None
        self.flush( finishing=True )

    _renderers = {
        '.ttl' : 'tayra.TTLCompiler',
    }
    _renderer_plugins = {
    }
    def render( self, *args, **kwargs ):
        """:meth:`pluggdapps.interfaces.IHTTPResponse.render`
        interface method.
        
        positional argument,

        ``request``,
            Instance of plugin implement
            :class:`pluggdapps.web.interfaces.IHTTPRequest` interface.

        ``context``,
            Dictionary of context information to be passed.

        keyword arguments,

        ``file``,
            Template file to be used for rendering.

        ``text``,
            Template text to be used for rendering.

        ``ITemplate``,
            :class:`ITemplate` plugin to use for rendering. This argument
            must be in canonical form of plugin's name.

        If ``file`` keyword argument is passed, this method will resolve the
        correct renderer plugin based on file-extension. if ``text`` keyword
        argument is passed, better pass the ``ITemplate`` argument as
        well.
        """
        request, context = args[0], args[1]
        renderer = kwargs.get( 'ITemplate', None )
        if renderer is None :
            tfile = kwargs.get( 'file', '' )
            _, ext = splitext( tfile )
            renderer = self._renderers.get( ext, None ) if ext else None

            # If in debug mode enable ttl file reloading.
            tfile = h.abspath_from_asset_spec( tfile )
            if self['debug'] and isfile( tfile ):
                self.pa._monitoredfiles.append( tfile )

        if renderer in self._renderer_plugins :
            plugin = self._renderer_plugins[ renderer ]
        elif renderer :
            plugin = self.qp( ITemplate, renderer )
        else :
            plugin = None

        if plugin :
            self.media_type = 'text/html'
            self._renderer_plugins.setdefault( renderer, plugin )
            return plugin.render( context, **kwargs )
        else :
            raise Exception('Unknown renderer')

    def chunk_generator( self, callback, request, c ):
        """:meth:`pluggdapps.web.webinterfaces.IHTTPResponse.chunk_generator`
        interface method."""

        class ChunkGenerator( object ):

            def __iter__( self ):
                return self

            def next( self ):
                return callback( request, c )

        return ChunkGenerator()


    #---- Local functions

    def _try_start_headers( self, finishing=True ) :
        """Generate default headers for this response. And return the
        byte-string of response header to write. This can be overriden
        by view callable attributes."""
        if self.start_response : return b''
        self.start_response = True
        stline = self._status_line()
        return self._header_data( self.headers, stline=stline )

    def _status_line( self ):
        code = self.statuscode
        reason = http.client.responses[ int(code) ].encode( 'utf-8' )
        return b' '.join([ self.version, code, reason ])

    def _header_data( self, headers, stline=b'' ):
        # TODO : 3 header field types are specifically prohibited from
        # appearing as a trailer field: Transfer-Encoding, Content-Length and
        # Trailer.
        lines = [ stline ] if stline else []
        for n, v in headers.items() :
            nC =  h.hdr_str2camelcase.get( n, None )
            if nC == None :
                n = n.encode('utf-8')
                nC = b'-'.join([ x.capitalize() for x in n.split('_') ])
            lines.append( nC + b': ' + v )

        [ lines.append( b"Set-Cookie: " + cookie.OutputString() )
          for c in self.setcookies.values() ]

        return b"\r\n".join(lines) + b"\r\n\r\n"

    def _flush_body( self, finishing ):
        data = b''.join( self.write_buffer )
        for tr in self.webapp.out_transformers :
            data = tr.transform( self.request, data, finishing=finishing )
        if self._if_etag() :
            self.body = data 
        else :
            self.body = b''
        self.set_header( "content_length", len(self.body) )
        data = self._try_start_headers( finishing=finishing )
        if self.request.method == b'HEAD' :
            pass
        elif self.body :
            data += self.body
        self.httpconn.write( data, callback=self._onflush )
        self.write_buffer = []

    def _flush_chunk( self, finishing ):
        self.add_headers( 'transfer_encoding', 'chunked' )
        data = self._try_start_headers( finishing=finishing )

        chunk = self.write_buffer( self.request, self.c )
        for tr in self.webapp.out_transformers :
            chunk = tr.transform( self.request, chunk, finishing=finishing )

        if chunk :
            data += hex(len(chunk)).encode('utf-8') + b'\r\n' + chunk + b'\r\n'
        else :
            data += b'0\r\n'
            if self.trailers :
                data += self._header_data( self.trailers )
        self.httpconn.write( data, callback=self._onflush )

    def _if_etag( self ):
        etag = self.headers.get('etag', '')
        if self.ischunked() == False and etag :
            im = self.request.headers.get( "if_match", b'' ).strip()
            inm = self.request.headers.get( "if_none_match", b'' ).strip()
            if ( (im and im.find( etag ) == -1) or
                 (inm and inm.find( etag ) != -1) ) :
                self.set_status( b'304' )
                return False
        return True

    def _onflush( self ):
        if self.flush_callback :
            callback, self.flush_callback = self.flush_callback, None
            callback()

        if self.has_finished() : self._onfinish()

    def _onfinish( self ):
        if self.finish_callback :
            callback, self.finish_callback = self.finish_callback, None
            callback()
        self.request.onfinish()

    #---- ISettings interface methods

    @classmethod
    def default_settings( cls ):
        """:meth:`pluggdapps.plugin.interfaces.ISettings.default_settings`
        interface method."""
        return _ds1
Ejemplo n.º 37
0
class WSGIResponse(object):
    """A basic HTTP response with content, headers, and out-bound cookies

    The class variable ``defaults`` specifies default values for
    ``content_type``, ``charset`` and ``errors``. These can be overridden
    for the current request via the registry.

    """
    defaults = StackedObjectProxy(
        default=dict(content_type='text/html',
                     charset='utf-8',
                     errors='strict',
                     headers={'Cache-Control': 'no-cache'}))

    def __init__(self, content=b'', mimetype=None, code=200):
        self._iter = None
        self._is_str_iter = True

        self.content = content
        self.headers = HeaderDict()
        self.cookies = SimpleCookie()
        self.status_code = code

        defaults = self.defaults._current_obj()
        if not mimetype:
            mimetype = defaults.get('content_type', 'text/html')
            charset = defaults.get('charset')
            if charset:
                mimetype = '%s; charset=%s' % (mimetype, charset)
        self.headers.update(defaults.get('headers', {}))
        self.headers['Content-Type'] = mimetype
        self.errors = defaults.get('errors', 'strict')

    def __str__(self):
        """Returns a rendition of the full HTTP message, including headers.

        When the content is an iterator, the actual content is replaced with the
        output of str(iterator) (to avoid exhausting the iterator).
        """
        if self._is_str_iter:
            content = ''.join(self.get_content())
        else:
            content = str(self.content)
        return '\n'.join(['%s: %s' % (key, value)
            for key, value in self.headers.headeritems()]) \
            + '\n\n' + content

    def __call__(self, environ, start_response):
        """Convenience call to return output and set status information

        Conforms to the WSGI interface for calling purposes only.

        Example usage:

        .. code-block:: python

            def wsgi_app(environ, start_response):
                response = WSGIResponse()
                response.write("Hello world")
                response.headers['Content-Type'] = 'latin1'
                return response(environ, start_response)

        """
        status_text = STATUS_CODE_TEXT[self.status_code]
        status = '%s %s' % (self.status_code, status_text)
        response_headers = self.headers.headeritems()
        for c in self.cookies.values():
            response_headers.append(('Set-Cookie', c.output(header='')))
        start_response(status, response_headers)
        is_file = isinstance(self.content, file)
        if 'wsgi.file_wrapper' in environ and is_file:
            return environ['wsgi.file_wrapper'](self.content)
        elif is_file:
            return iter(lambda: self.content.read(), '')
        return self.get_content()

    def determine_charset(self):
        """
        Determine the encoding as specified by the Content-Type's charset
        parameter, if one is set
        """
        charset_match = _CHARSET_RE.search(self.headers.get(
            'Content-Type', ''))
        if charset_match:
            return charset_match.group(1)

    def has_header(self, header):
        """
        Case-insensitive check for a header
        """
        warnings.warn(
            'WSGIResponse.has_header is deprecated, use '
            'WSGIResponse.headers.has_key instead', DeprecationWarning, 2)
        return self.headers.has_key(header)

    def set_cookie(self,
                   key,
                   value='',
                   max_age=None,
                   expires=None,
                   path='/',
                   domain=None,
                   secure=None,
                   httponly=None):
        """
        Define a cookie to be sent via the outgoing HTTP headers
        """
        self.cookies[key] = value
        for var_name, var_value in [('max_age', max_age), ('path', path),
                                    ('domain', domain), ('secure', secure),
                                    ('expires', expires),
                                    ('httponly', httponly)]:
            if var_value is not None and var_value is not False:
                self.cookies[key][var_name.replace('_', '-')] = var_value

    def delete_cookie(self, key, path='/', domain=None):
        """
        Notify the browser the specified cookie has expired and should be
        deleted (via the outgoing HTTP headers)
        """
        self.cookies[key] = ''
        if path is not None:
            self.cookies[key]['path'] = path
        if domain is not None:
            self.cookies[key]['domain'] = domain
        self.cookies[key]['expires'] = 0
        self.cookies[key]['max-age'] = 0

    def _set_content(self, content):
        if not isinstance(content, (six.binary_type, six.text_type)):
            self._iter = content
            if isinstance(content, list):
                self._is_str_iter = True
            else:
                self._is_str_iter = False
        else:
            self._iter = [content]
            self._is_str_iter = True

    content = property(lambda self: self._iter,
                       _set_content,
                       doc='Get/set the specified content, where content can '
                       'be: a string, a list of strings, a generator function '
                       'that yields strings, or an iterable object that '
                       'produces strings.')

    def get_content(self):
        """
        Returns the content as an iterable of strings, encoding each element of
        the iterator from a Unicode object if necessary.
        """
        charset = self.determine_charset()
        if charset:
            return encode_unicode_app_iter(self.content, charset, self.errors)
        else:
            return self.content

    def wsgi_response(self):
        """
        Return this WSGIResponse as a tuple of WSGI formatted data, including:
        (status, headers, iterable)
        """
        status_text = STATUS_CODE_TEXT[self.status_code]
        status = '%s %s' % (self.status_code, status_text)
        response_headers = self.headers.headeritems()
        for c in self.cookies.values():
            response_headers.append(('Set-Cookie', c.output(header='')))
        return status, response_headers, self.get_content()

    # The remaining methods partially implement the file-like object interface.
    # See http://docs.python.org/lib/bltin-file-objects.html
    def write(self, content):
        if not self._is_str_iter:
            raise IOError(
                "This %s instance's content is not writable: (content "
                'is an iterator)' % self.__class__.__name__)
        self.content.append(content)

    def flush(self):
        pass

    def tell(self):
        if not self._is_str_iter:
            raise IOError(
                'This %s instance cannot tell its position: (content '
                'is an iterator)' % self.__class__.__name__)
        return sum([len(chunk) for chunk in self._iter])

    ########################################
    ## Content-type and charset

    def charset__get(self):
        """
        Get/set the charset (in the Content-Type)
        """
        header = self.headers.get('content-type')
        if not header:
            return None
        match = _CHARSET_RE.search(header)
        if match:
            return match.group(1)
        return None

    def charset__set(self, charset):
        if charset is None:
            del self.charset
            return
        try:
            header = self.headers.pop('content-type')
        except KeyError:
            raise AttributeError(
                "You cannot set the charset when no content-type is defined")
        match = _CHARSET_RE.search(header)
        if match:
            header = header[:match.start()] + header[match.end():]
        header += '; charset=%s' % charset
        self.headers['content-type'] = header

    def charset__del(self):
        try:
            header = self.headers.pop('content-type')
        except KeyError:
            # Don't need to remove anything
            return
        match = _CHARSET_RE.search(header)
        if match:
            header = header[:match.start()] + header[match.end():]
        self.headers['content-type'] = header

    charset = property(charset__get,
                       charset__set,
                       charset__del,
                       doc=charset__get.__doc__)

    def content_type__get(self):
        """
        Get/set the Content-Type header (or None), *without* the
        charset or any parameters.

        If you include parameters (or ``;`` at all) when setting the
        content_type, any existing parameters will be deleted;
        otherwise they will be preserved.
        """
        header = self.headers.get('content-type')
        if not header:
            return None
        return header.split(';', 1)[0]

    def content_type__set(self, value):
        if ';' not in value:
            header = self.headers.get('content-type', '')
            if ';' in header:
                params = header.split(';', 1)[1]
                value += ';' + params
        self.headers['content-type'] = value

    def content_type__del(self):
        try:
            del self.headers['content-type']
        except KeyError:
            pass

    content_type = property(content_type__get,
                            content_type__set,
                            content_type__del,
                            doc=content_type__get.__doc__)
Ejemplo n.º 38
0
class StreamResponse(collections.MutableMapping, HeadersMixin):

    _length_check = True

    def __init__(self, *, status=200, reason=None, headers=None):
        self._body = None
        self._keep_alive = None
        self._chunked = False
        self._compression = False
        self._compression_force = None
        self._cookies = SimpleCookie()

        self._req = None
        self._payload_writer = None
        self._eof_sent = False
        self._body_length = 0
        self._state = {}

        if headers is not None:
            self._headers = CIMultiDict(headers)
        else:
            self._headers = CIMultiDict()

        self.set_status(status, reason)

    @property
    def prepared(self):
        return self._payload_writer is not None

    @property
    def task(self):
        return getattr(self._req, 'task', None)

    @property
    def status(self):
        return self._status

    @property
    def chunked(self):
        return self._chunked

    @property
    def compression(self):
        return self._compression

    @property
    def reason(self):
        return self._reason

    def set_status(self, status, reason=None, _RESPONSES=RESPONSES):
        assert not self.prepared, \
            'Cannot change the response status code after ' \
            'the headers have been sent'
        self._status = int(status)
        if reason is None:
            try:
                reason = _RESPONSES[self._status][0]
            except Exception:
                reason = ''
        self._reason = reason

    @property
    def keep_alive(self):
        return self._keep_alive

    def force_close(self):
        self._keep_alive = False

    @property
    def body_length(self):
        return self._body_length

    @property
    def output_length(self):
        warnings.warn('output_length is deprecated', DeprecationWarning)
        return self._payload_writer.buffer_size

    def enable_chunked_encoding(self, chunk_size=None):
        """Enables automatic chunked transfer encoding."""
        self._chunked = True

        if hdrs.CONTENT_LENGTH in self._headers:
            raise RuntimeError("You can't enable chunked encoding when "
                               "a content length is set")
        if chunk_size is not None:
            warnings.warn('Chunk size is deprecated #1615', DeprecationWarning)

    def enable_compression(self, force=None):
        """Enables response compression encoding."""
        # Backwards compatibility for when force was a bool <0.17.
        if type(force) == bool:
            force = ContentCoding.deflate if force else ContentCoding.identity
        elif force is not None:
            assert isinstance(force, ContentCoding), ("force should one of "
                                                      "None, bool or "
                                                      "ContentEncoding")

        self._compression = True
        self._compression_force = force

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self):
        return self._cookies

    def set_cookie(self,
                   name,
                   value,
                   *,
                   expires=None,
                   domain=None,
                   max_age=None,
                   path='/',
                   secure=None,
                   httponly=None,
                   version=None):
        """Set or update response cookie.

        Sets new cookie or updates existent with new value.
        Also updates only those params which are not None.
        """

        old = self._cookies.get(name)
        if old is not None and old.coded_value == '':
            # deleted cookie
            self._cookies.pop(name, None)

        self._cookies[name] = value
        c = self._cookies[name]

        if expires is not None:
            c['expires'] = expires
        elif c.get('expires') == 'Thu, 01 Jan 1970 00:00:00 GMT':
            del c['expires']

        if domain is not None:
            c['domain'] = domain

        if max_age is not None:
            c['max-age'] = max_age
        elif 'max-age' in c:
            del c['max-age']

        c['path'] = path

        if secure is not None:
            c['secure'] = secure
        if httponly is not None:
            c['httponly'] = httponly
        if version is not None:
            c['version'] = version

    def del_cookie(self, name, *, domain=None, path='/'):
        """Delete cookie.

        Creates new empty expired cookie.
        """
        # TODO: do we need domain/path here?
        self._cookies.pop(name, None)
        self.set_cookie(name,
                        '',
                        max_age=0,
                        expires="Thu, 01 Jan 1970 00:00:00 GMT",
                        domain=domain,
                        path=path)

    @property
    def content_length(self):
        # Just a placeholder for adding setter
        return super().content_length

    @content_length.setter
    def content_length(self, value):
        if value is not None:
            value = int(value)
            if self._chunked:
                raise RuntimeError("You can't set content length when "
                                   "chunked encoding is enable")
            self._headers[hdrs.CONTENT_LENGTH] = str(value)
        else:
            self._headers.pop(hdrs.CONTENT_LENGTH, None)

    @property
    def content_type(self):
        # Just a placeholder for adding setter
        return super().content_type

    @content_type.setter
    def content_type(self, value):
        self.content_type  # read header values if needed
        self._content_type = str(value)
        self._generate_content_type_header()

    @property
    def charset(self):
        # Just a placeholder for adding setter
        return super().charset

    @charset.setter
    def charset(self, value):
        ctype = self.content_type  # read header values if needed
        if ctype == 'application/octet-stream':
            raise RuntimeError("Setting charset for application/octet-stream "
                               "doesn't make sense, setup content_type first")
        if value is None:
            self._content_dict.pop('charset', None)
        else:
            self._content_dict['charset'] = str(value).lower()
        self._generate_content_type_header()

    @property
    def last_modified(self, _LAST_MODIFIED=hdrs.LAST_MODIFIED):
        """The value of Last-Modified HTTP header, or None.

        This header is represented as a `datetime` object.
        """
        httpdate = self.headers.get(_LAST_MODIFIED)
        if httpdate is not None:
            timetuple = parsedate(httpdate)
            if timetuple is not None:
                return datetime.datetime(*timetuple[:6],
                                         tzinfo=datetime.timezone.utc)
        return None

    @last_modified.setter
    def last_modified(self, value):
        if value is None:
            self.headers.pop(hdrs.LAST_MODIFIED, None)
        elif isinstance(value, (int, float)):
            self.headers[hdrs.LAST_MODIFIED] = time.strftime(
                "%a, %d %b %Y %H:%M:%S GMT", time.gmtime(math.ceil(value)))
        elif isinstance(value, datetime.datetime):
            self.headers[hdrs.LAST_MODIFIED] = time.strftime(
                "%a, %d %b %Y %H:%M:%S GMT", value.utctimetuple())
        elif isinstance(value, str):
            self.headers[hdrs.LAST_MODIFIED] = value

    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):
        params = '; '.join("%s=%s" % i for i in self._content_dict.items())
        if params:
            ctype = self._content_type + '; ' + params
        else:
            ctype = self._content_type
        self.headers[CONTENT_TYPE] = ctype

    def _do_start_compression(self, coding):
        if coding != ContentCoding.identity:
            self.headers[hdrs.CONTENT_ENCODING] = coding.value
            self._payload_writer.enable_compression(coding.value)
            # Compressed payload may have different content length,
            # remove the header
            self._headers.popall(hdrs.CONTENT_LENGTH, None)

    def _start_compression(self, request):
        if self._compression_force:
            self._do_start_compression(self._compression_force)
        else:
            accept_encoding = request.headers.get(hdrs.ACCEPT_ENCODING,
                                                  '').lower()
            for coding in ContentCoding:
                if coding.value in accept_encoding:
                    self._do_start_compression(coding)
                    return

    async def prepare(self, request):
        if self._eof_sent:
            return
        if self._payload_writer is not None:
            return self._payload_writer

        await request._prepare_hook(self)
        return await self._start(request)

    async def _start(self,
                     request,
                     HttpVersion10=HttpVersion10,
                     HttpVersion11=HttpVersion11,
                     CONNECTION=hdrs.CONNECTION,
                     DATE=hdrs.DATE,
                     SERVER=hdrs.SERVER,
                     CONTENT_TYPE=hdrs.CONTENT_TYPE,
                     CONTENT_LENGTH=hdrs.CONTENT_LENGTH,
                     SET_COOKIE=hdrs.SET_COOKIE,
                     SERVER_SOFTWARE=SERVER_SOFTWARE,
                     TRANSFER_ENCODING=hdrs.TRANSFER_ENCODING):
        self._req = request

        keep_alive = self._keep_alive
        if keep_alive is None:
            keep_alive = request.keep_alive
        self._keep_alive = keep_alive

        version = request.version
        writer = self._payload_writer = request._payload_writer

        headers = self._headers
        for cookie in self._cookies.values():
            value = cookie.output(header='')[1:]
            headers.add(SET_COOKIE, value)

        if self._compression:
            self._start_compression(request)

        if self._chunked:
            if version != HttpVersion11:
                raise RuntimeError("Using chunked encoding is forbidden "
                                   "for HTTP/{0.major}.{0.minor}".format(
                                       request.version))
            writer.enable_chunking()
            headers[TRANSFER_ENCODING] = 'chunked'
            if CONTENT_LENGTH in headers:
                del headers[CONTENT_LENGTH]
        elif self._length_check:
            writer.length = self.content_length
            if writer.length is None:
                if version >= HttpVersion11:
                    writer.enable_chunking()
                    headers[TRANSFER_ENCODING] = 'chunked'
                    if CONTENT_LENGTH in headers:
                        del headers[CONTENT_LENGTH]
                else:
                    keep_alive = False

        headers.setdefault(CONTENT_TYPE, 'application/octet-stream')
        headers.setdefault(DATE, rfc822_formatted_time())
        headers.setdefault(SERVER, SERVER_SOFTWARE)

        # connection header
        if CONNECTION not in headers:
            if keep_alive:
                if version == HttpVersion10:
                    headers[CONNECTION] = 'keep-alive'
            else:
                if version == HttpVersion11:
                    headers[CONNECTION] = 'close'

        # status line
        status_line = 'HTTP/{}.{} {} {}\r\n'.format(version[0], version[1],
                                                    self._status, self._reason)
        await writer.write_headers(status_line, headers)

        return writer

    async def write(self, data):
        assert isinstance(data, (bytes, bytearray, memoryview)), \
            "data argument must be byte-ish (%r)" % type(data)

        if self._eof_sent:
            raise RuntimeError("Cannot call write() after write_eof()")
        if self._payload_writer is None:
            raise RuntimeError("Cannot call write() before prepare()")

        await self._payload_writer.write(data)

    async def drain(self):
        assert not self._eof_sent, "EOF has already been sent"
        assert self._payload_writer is not None, \
            "Response has not been started"
        warnings.warn("drain method is deprecated, use await resp.write()",
                      DeprecationWarning,
                      stacklevel=2)
        await self._payload_writer.drain()

    async def write_eof(self, data=b''):
        assert isinstance(data, (bytes, bytearray, memoryview)), \
            "data argument must be byte-ish (%r)" % type(data)

        if self._eof_sent:
            return

        assert self._payload_writer is not None, \
            "Response has not been started"

        await self._payload_writer.write_eof(data)
        self._eof_sent = True
        self._req = None
        self._body_length = self._payload_writer.output_size
        self._payload_writer = None

    def __repr__(self):
        if self._eof_sent:
            info = "eof"
        elif self.prepared:
            info = "{} {} ".format(self._req.method, self._req.path)
        else:
            info = "not prepared"
        return "<{} {} {}>".format(self.__class__.__name__, self.reason, info)

    def __getitem__(self, key):
        return self._state[key]

    def __setitem__(self, key, value):
        self._state[key] = value

    def __delitem__(self, key):
        del self._state[key]

    def __len__(self):
        return len(self._state)

    def __iter__(self):
        return iter(self._state)

    def __hash__(self):
        return hash(id(self))
Ejemplo n.º 39
0
class WSGIResponse(object):
    """A basic HTTP response with content, headers, and out-bound cookies

    The class variable ``defaults`` specifies default values for
    ``content_type``, ``charset`` and ``errors``. These can be overridden
    for the current request via the registry.

    """
    defaults = StackedObjectProxy(
        default=dict(content_type='text/html', charset='utf-8',
                     errors='strict', headers={'Cache-Control':'no-cache'})
        )
    def __init__(self, content=b'', mimetype=None, code=200):
        self._iter = None
        self._is_str_iter = True

        self.content = content
        self.headers = HeaderDict()
        self.cookies = SimpleCookie()
        self.status_code = code

        defaults = self.defaults._current_obj()
        if not mimetype:
            mimetype = defaults.get('content_type', 'text/html')
            charset = defaults.get('charset')
            if charset:
                mimetype = '%s; charset=%s' % (mimetype, charset)
        self.headers.update(defaults.get('headers', {}))
        self.headers['Content-Type'] = mimetype
        self.errors = defaults.get('errors', 'strict')

    def __str__(self):
        """Returns a rendition of the full HTTP message, including headers.

        When the content is an iterator, the actual content is replaced with the
        output of str(iterator) (to avoid exhausting the iterator).
        """
        if self._is_str_iter:
            content = ''.join(self.get_content())
        else:
            content = str(self.content)
        return '\n'.join(['%s: %s' % (key, value)
            for key, value in self.headers.headeritems()]) \
            + '\n\n' + content

    def __call__(self, environ, start_response):
        """Convenience call to return output and set status information

        Conforms to the WSGI interface for calling purposes only.

        Example usage:

        .. code-block:: python

            def wsgi_app(environ, start_response):
                response = WSGIResponse()
                response.write("Hello world")
                response.headers['Content-Type'] = 'latin1'
                return response(environ, start_response)

        """
        status_text = STATUS_CODE_TEXT[self.status_code]
        status = '%s %s' % (self.status_code, status_text)
        response_headers = self.headers.headeritems()
        for c in self.cookies.values():
            response_headers.append(('Set-Cookie', c.output(header='')))
        start_response(status, response_headers)
        is_file = isinstance(self.content, file)
        if 'wsgi.file_wrapper' in environ and is_file:
            return environ['wsgi.file_wrapper'](self.content)
        elif is_file:
            return iter(lambda: self.content.read(), '')
        return self.get_content()

    def determine_charset(self):
        """
        Determine the encoding as specified by the Content-Type's charset
        parameter, if one is set
        """
        charset_match = _CHARSET_RE.search(self.headers.get('Content-Type', ''))
        if charset_match:
            return charset_match.group(1)

    def has_header(self, header):
        """
        Case-insensitive check for a header
        """
        warnings.warn('WSGIResponse.has_header is deprecated, use '
                      'WSGIResponse.headers.has_key instead', DeprecationWarning,
                      2)
        return self.headers.has_key(header)

    def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
                   domain=None, secure=None, httponly=None):
        """
        Define a cookie to be sent via the outgoing HTTP headers
        """
        self.cookies[key] = value
        for var_name, var_value in [
            ('max_age', max_age), ('path', path), ('domain', domain),
            ('secure', secure), ('expires', expires), ('httponly', httponly)]:
            if var_value is not None and var_value is not False:
                self.cookies[key][var_name.replace('_', '-')] = var_value

    def delete_cookie(self, key, path='/', domain=None):
        """
        Notify the browser the specified cookie has expired and should be
        deleted (via the outgoing HTTP headers)
        """
        self.cookies[key] = ''
        if path is not None:
            self.cookies[key]['path'] = path
        if domain is not None:
            self.cookies[key]['domain'] = domain
        self.cookies[key]['expires'] = 0
        self.cookies[key]['max-age'] = 0

    def _set_content(self, content):
        if not isinstance(content, (six.binary_type, six.text_type)):
            self._iter = content
            if isinstance(content, list):
                self._is_str_iter = True
            else:
                self._is_str_iter = False
        else:
            self._iter = [content]
            self._is_str_iter = True
    content = property(lambda self: self._iter, _set_content,
                       doc='Get/set the specified content, where content can '
                       'be: a string, a list of strings, a generator function '
                       'that yields strings, or an iterable object that '
                       'produces strings.')

    def get_content(self):
        """
        Returns the content as an iterable of strings, encoding each element of
        the iterator from a Unicode object if necessary.
        """
        charset = self.determine_charset()
        if charset:
            return encode_unicode_app_iter(self.content, charset, self.errors)
        else:
            return self.content

    def wsgi_response(self):
        """
        Return this WSGIResponse as a tuple of WSGI formatted data, including:
        (status, headers, iterable)
        """
        status_text = STATUS_CODE_TEXT[self.status_code]
        status = '%s %s' % (self.status_code, status_text)
        response_headers = self.headers.headeritems()
        for c in self.cookies.values():
            response_headers.append(('Set-Cookie', c.output(header='')))
        return status, response_headers, self.get_content()

    # The remaining methods partially implement the file-like object interface.
    # See http://docs.python.org/lib/bltin-file-objects.html
    def write(self, content):
        if not self._is_str_iter:
            raise IOError("This %s instance's content is not writable: (content "
                'is an iterator)' % self.__class__.__name__)
        self.content.append(content)

    def flush(self):
        pass

    def tell(self):
        if not self._is_str_iter:
            raise IOError('This %s instance cannot tell its position: (content '
                'is an iterator)' % self.__class__.__name__)
        return sum([len(chunk) for chunk in self._iter])

    ########################################
    ## Content-type and charset

    def charset__get(self):
        """
        Get/set the charset (in the Content-Type)
        """
        header = self.headers.get('content-type')
        if not header:
            return None
        match = _CHARSET_RE.search(header)
        if match:
            return match.group(1)
        return None

    def charset__set(self, charset):
        if charset is None:
            del self.charset
            return
        try:
            header = self.headers.pop('content-type')
        except KeyError:
            raise AttributeError(
                "You cannot set the charset when no content-type is defined")
        match = _CHARSET_RE.search(header)
        if match:
            header = header[:match.start()] + header[match.end():]
        header += '; charset=%s' % charset
        self.headers['content-type'] = header

    def charset__del(self):
        try:
            header = self.headers.pop('content-type')
        except KeyError:
            # Don't need to remove anything
            return
        match = _CHARSET_RE.search(header)
        if match:
            header = header[:match.start()] + header[match.end():]
        self.headers['content-type'] = header

    charset = property(charset__get, charset__set, charset__del, doc=charset__get.__doc__)

    def content_type__get(self):
        """
        Get/set the Content-Type header (or None), *without* the
        charset or any parameters.

        If you include parameters (or ``;`` at all) when setting the
        content_type, any existing parameters will be deleted;
        otherwise they will be preserved.
        """
        header = self.headers.get('content-type')
        if not header:
            return None
        return header.split(';', 1)[0]

    def content_type__set(self, value):
        if ';' not in value:
            header = self.headers.get('content-type', '')
            if ';' in header:
                params = header.split(';', 1)[1]
                value += ';' + params
        self.headers['content-type'] = value

    def content_type__del(self):
        try:
            del self.headers['content-type']
        except KeyError:
            pass

    content_type = property(content_type__get, content_type__set,
                            content_type__del, doc=content_type__get.__doc__)
Ejemplo n.º 40
0
def trans_cookie(c*k):
    '''将字符串转换为字典形式的cookies'''
    cookie = SimpleCookie(c*k)
    return {i.key: i.value for i in cookie.values()}
Ejemplo n.º 41
0
 def accept_cookies_for_response(self, cookies: HTTPCookies):
     """
     Insert all the cookies as response set cookie headers.
     """
     for cookie_morsel in cookies.values():
         self.add("set-cookie", cookie_morsel.OutputString())
Ejemplo n.º 42
0
class Response(object):
    default_status_code = STATUS.OK
    def __init__(self, content='', status_code=None, content_type='text/html',
            status_message=None, **kwargs):
        self.content = content
        self.encoding = kwargs.get('encoding', DEFAULT_ENCODING)
        if status_code is None:
            status_code = self.default_status_code
        self.status_code = status_code
        self.status_message = status_message
        self.headers = {}
        self.headers['Content-Type'] = content_type
        self.cookies = SimpleCookie()

    def __iter__(self):
        '''WSGI Iterates response content.'''
        value = self.content
        if not hasattr(value, '__iter__') or isinstance(value, (bytes, str)):
            value = [value]

        for chunk in value:
            # Don't encode when already bytes or Content-Encoding set
            if not isinstance(chunk, bytes):
                chunk = chunk.encode(self.encoding)
            yield chunk

    def build_headers(self):
        '''
        Return the list of headers as two-tuples
        '''
        if not 'Content-Type' in self.headers:
            content_type = self.content_type
            if self.encoding != DEFAULT_ENCODING:
                content_type += '; charset=%s' % self.encoding
            self.headers['Content-Type'] = content_type

        headers = list(self.headers.items())
        # Append cookies
        headers += [
            ('Set-Cookie', cookie.OutputString())
            for cookie in self.cookies.values()
        ]
        return headers

    def add_cookie(self, key, value, **attrs):
        '''
        Finer control over cookies.  Allow specifying an Morsel arguments.
        '''
        if attrs:
            c = Morsel()
            c.set(key, value, **attrs)
            self.cookies[key] = c
        else:
            self.cookies[key] = value

    @property
    def status(self):
        '''Allow custom status messages'''
        message = self.status_message
        if message is None:
            message = STATUS[self.status_code]
        return '%s %s' % (self.status_code, message)

    def close(self):
        '''Close our content if it can be.

        WSGI must call close if the response object has one.
        '''
        if callable(getattr(self.content, 'read', None)):
            self.content.close()
Ejemplo n.º 43
0
class RavelloClient(object):
    """A client for the Ravello API.

    The client is a thin wrapper around the Ravello RESTful API. The client
    manages a single HTTPS connection, and implements login, redirect and retry
    functionality. A single generic :meth:`request` method is provided to issue
    API requests.

    On top of this, most existing RESTful API calls are mapped as methods on
    this class. These mapped methods are simple wrappers around the generic
    :meth:`request` method. Some general comments on this mapping:

    * The calls are named "<method>_<resource>", for example
      ":meth:`create_keypair`" and ":meth:`get_blueprints`". A method is always
      an English verb, while a resource is can be a singular or plural Englush
      noun.
    * The standard methods are "get", "create", "update" and "delete". Not all
      methods are defined for all resources, and some resources have additional
      methods.
    * The available resources are "application", "blueprint", "image",
      "keypair" and "vm". The plural versions of these exist as well.
    * There is no client-side object model. The return value from any API call
      is simply the parsed JSON response.
    * Resources are returned as a dict or a list of dicts. A dict always
      represents a single object, and its key/value pairs correspond to the
      object's attributes. Lists always represents multiple objects.
    * Objects are identifed by a numeric ID, which is always the key "id".
    * All methods that accept an object ID either accept this parameter
      as a simple Python int, or alternatively as a dict with an "id" key
      containing the ID. In the latter case, the dict is typically returned
      previsouly by another API call.
    * HTTP response codes in the 4xx or 5xx range are considered errors, and
      are turned into :class:`RavelloError` exceptions (except for 404 which
      results in a response of ``None``).
    """

    default_url = 'https://cloud.ravellosystems.com/api/v1'
    default_timeout = 60
    default_retries = 3
    default_redirects = 3

    def __init__(self, username=None, password=None, url=None, timeout=None, retries=None, proxy_url=None):
        """Create a new client.

        The *username* and *password* parameters specify the credentials to use
        when connecting to the API. The *timeout* and *retries* parameters
        specify the default network system call time timeout and maximum number
        of retries respectively.
        """
        self._username = username
        self._password = password
        self.timeout = timeout if timeout is not None else self.default_timeout
        self.retries = retries if retries is not None else self.default_retries
        self.redirects = self.default_redirects
        self._logger = logging.getLogger('ravello')
        self._autologin = True
        self._cookies = None
        self._user_info = None
        self._set_url(url or self.default_url)
        self._proxies = {}
        if proxy_url is not None:
            self._proxies = {"http": proxy_url, "https": proxy_url}

    @property
    def url(self):
        """The parsed URL of the API endpoint, which is a
        :class:`urllib.parse.SplitResult` instance."""
        return self._url

    @property
    def connected(self):
        """Whether or not the client is connected to the API."""
        return self._cookies is not None

    @property
    def have_credentials(self):
        """Whether or not credentials are available."""
        return self._username is not None and self._password is not None

    @property
    def logged_in(self):
        """Whether or not the client is logged in."""
        return self._cookies is not None

    @property
    def user_info(self):
        """Return information about the current logged-in user."""
        return self._user_info

    def _set_url(self, url):
        if self.connected:
            raise RuntimeError('cannot change URL when connected')
        self._url = urlsplit2(url)

    def connect(self, url=None, proxy_url=None):
        """Connect to the API.

        It is not mandatory to call this method. If this method is not called,
        the client will automatically connect when required.
        """
        if url is not None:
            self._set_url(url)
        if proxy_url is not None:
            self._proxies = {"http": proxy_url, "https": proxy_url}

    def login(self, username=None, password=None):
        """Login to the API.

        This method performs a login to the API, and store the resulting
        authentication cookie in memory.

        It is not mandatory to call this method. If this method is not called,
        the client will automatically login when required.
        """
        if self.logged_in:
            raise RuntimeError('already logged in')
        if username is not None:
            self._username = username
        if password is not None:
            self._password = password
        self._login()

    def _login(self):
        if not self.have_credentials:
            raise RuntimeError('no credentials set')
        self._logger.debug('performing a username/password login')
        self._autologin = False
        auth = '{0}:{1}'.format(self._username, self._password)
        auth = base64.b64encode(auth.encode('ascii')).decode('ascii')
        headers = [('Authorization', 'Basic {0}'.format(auth))]
        response = self._request('POST', '/login', b'', headers)
        self._cookies = SimpleCookie()
        self._cookies.load(response.headers.get('Set-Cookie'))
        self._autologin = True
        self._user_info = response.entity

    def logout(self):
        """Logout from the API. This invalidates the authentication cookie."""
        if not self.logged_in:
            return
        self.request('POST', '/logout')
        self._cookies = None

    def close(self):
        """Close the connection to the API."""
        if not self.connected:
            return
        self._cookies = None

    # The request() method is the main function. All other methods are a small
    # shim on top of this.

    def request(self, method, path, entity=None, headers=None):
        """Issues a request to the API.

        The parsed entity is returned, or a :class:`RavelloError` exception is
        raised on error.

        This method can be used in case a certain API call has not yet been
        added as a method.
        """
        body = json.dumps(entity).encode('utf8') if entity is not None else b''
        headers = headers if headers is not None else []
        response = self._request(method, path, body, headers)
        return response.entity

    def _request(self, method, path, body=b'', headers=None):
        rpath = self._url.path + path
        abpath = self.default_url + path
        hdict = {'Accept': 'application/json'}
        for key, value in headers:
            hdict[key] = value
        if body:
            hdict['Content-Type'] = 'application/json'
        retries = redirects = 0
        while retries < self.retries and redirects < self.redirects:
            if not self.logged_in and self.have_credentials and self._autologin:
                self._login()
            if self._cookies:
                cookies = ['{0}={1}'.format(c.key, c.coded_value) for c in self._cookies.values()]
                hdict['Cookie'] = '; '.join(cookies)
            try:
                self._logger.debug('request: {0} {1}'.format(method, rpath))
                response = http_methods[method](abpath, data=body, headers=hdict, proxies=self._proxies, timeout=self.timeout)
                status = response.status_code
                ctype = response.headers.get('Content-Type')
                if ctype == 'application/json':
                    entity = response.json()
                else:
                    entity = None
                self._logger.debug('response: {0} ({1})'.format(status, ctype))
                if 200 <= status < 299:
                    if isinstance(entity, dict) and entity.get('id'):
                        if response.headers.get('Content-Location'):
                            href = urlsplit2(response.headers.get('Content-Location')).path
                        elif response.headers.get('Location'):
                            href = urlsplit2(response.headers.get('Location')).path
                        elif method == 'POST':
                            # missing Location header e.g. with /pubkeys
                            href = '{0}/{1}'.format(abpath, entity['id'])
                        else:
                            href = abpath
                        entity['_href'] = href[len(self._url.path):]
                    elif isinstance(entity, list):
                        for elem in entity:
                            if 'id' in elem:
                                elem['_href'] = '{0}/{1}'.format(path, elem['id'])
                elif 300 <= status < 399:
                    loc = response.headers.get('Location')
                    if loc is None:
                        raise RavelloError('no location for {0} response'.format(status))
                    if loc.startswith('/'):
                        rpath = loc
                    else:
                        url = urlsplit2(loc)
                        if url.netloc != self._url.netloc:
                            raise RavelloError('will not chase referral to {0}'.format(loc))
                        rpath = url.path
                    redirects += 1
                elif status == 404:
                    entity = None
                else:
                    code = response.headers.get('ERROR-CODE', 'unknown')
                    msg = response.headers.get('ERROR-MESSAGE', 'unknown')
                    raise RavelloError('got status {0} ({1}/{2})' .format(status, code, msg))
                response.entity = entity
            except (socket.timeout, ValueError) as e:
                self._logger.debug('error: {0!s}'.format(e))
                self.close()
                if not _idempotent(method):
                    self._logger.debug('not retrying {0} request'.format(method))
                    raise RavelloError('request timeout')
                retries += 1
                continue
            break
        if retries == self.retries:
            raise RavelloError('maximum number of retries reached')
        if redirects == self.redirects:
            raise RavelloError('maximum number of redirects reached')
        return response

    def reload(self, obj):
        """Reload the object *obj*.

        The object must have been returned by the API, and must be a dict with
        an ``"_href"`` key.
        """
        href = obj.get('_href')
        if href is None:
            raise RuntimeError('obj must have an "_href" key')
        return self.request('GET', href)

    def wait_for(self, obj, cond, timeout=None):
        """Wait for a condition on *obj* to become true.

        The object *obj* must be reloadable. See :meth:`reload` for more
        details.

        The condition *cond* must be a dict or a callable. If it is a dict, it
        lists the keys and values that the object must have. If it is a
        callable, it will be called with the object as an argument, and it
        should return True or False.

        The *timeout* argument specifies the total time to wait. If not
        specified, it will default to the system call timeout passed to the
        constructor.

        If the condition does not become true before the timeout, a
        :class:`RavelloError` exception is raised.
        """
        end_time = time.time() + timeout
        while end_time > time.time():
            obj = self.reload(obj)
            if _match_filter(obj, cond):
                break
            time.sleep(5)
        if end_time < time.time():
            raise RavelloError('timeout waiting for condition')

    # Mapped API calls below

    def get_application_by_name(self, app_name, aspect=None):
      
        criteria = dict()
        criteria['type'] = 'COMPLEX'
        criteria['operator'] = 'And'
        criteria['criteria'] = [1]
        
        criterion = dict()
        criterion['type'] = 'SIMPLE'
        criterion['operator'] = 'Equals'
        criterion['propertyName'] = 'name'
        criterion['operand'] = app_name
        criteria['criteria'][0] = criterion
         
        apps = self.request('POST', '/applications/filter',criteria)
        if len(apps) == 0:
            raise RavelloError('app "{0}" not found'.format(app_name))
        if len(apps) > 1:
            raise RavelloError('multiple apps for name "{0}" found'.format(app_name))
        app = apps[0]
        if aspect is not 'properties':
            app = self.get_application(app,aspect)
        return app
    
    def get_application(self, app, aspect=None):
        """Return the application with ID *app*, or None if it does not exist.

        The *aspect* parameter can be used to return the application only with
        the specified aspect (e.g., design, deployment, properties).
        """
        if isinstance(app, dict): app = app['id']
        if aspect is not None:
            app = '{0};{1}'.format(app, aspect)
        return self.request('GET', '/applications/{0}'.format(app))

    def get_applications(self, filter=None):
        """Return a list with all applications.
        The *filter* argument can be used to return only a subset of the
        applications. See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        apps = self.request('GET', '/applications')
        if filter is not None:
            apps = _match_filter(apps, filter)
        return apps

    def create_application(self, app):
        """Create a new application.

        The *app* parameter must be a dict describing the application to
        create.

        The new application is returned.
        """
        return self.request('POST', '/applications', app)

    def update_application(self, app):
        """Update an existing application.

        The *app* parameter must be the updated application. The way to update
        an application (or any other resource) is to first retrieve it, make
        the updates client-side, and then use this method to make the update.

        The updated application is returned.
        """
        return self.request('PUT', '/applications/{0}'.format(app['id']), app)

    def delete_application(self, app):
        """Delete an application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        self.request('DELETE', '/applications/{0}'.format(app))

    def publish_application(self, app, req=None):
        """Publish the application with ID *app*.

        The *req* parameter, if provided, must be a dict with publish
        parameters.
        """
        if isinstance(app, dict):
            app = app['id']
        self.request('POST', '/applications/{0}/publish'.format(app), req)

    def start_application(self, app, req=None):
        """Start the application with ID *app*.

        The *req* parameter, if provided, must be a dict with start
        parameters.
        """
        if isinstance(app, dict): app = app['id']
        self.request('POST', '/applications/{0}/start'.format(app), req)

    def stop_application(self, app, req=None):
        """Stop the application with ID *app*.

        The *req* parameter, if provided, must be a dict with stop
        parameters.
        """
        if isinstance(app, dict): app = app['id']
        self.request('POST', '/applications/{0}/stop'.format(app), req)

    def restart_application(self, app, req=None):
        """Restart the application with ID *app*.

        The *req* parameter, if provided, must be a dict with restart
        parameters.
        """
        if isinstance(app, dict): app = app['id']
        self.request('POST', '/applications/{0}/restart'.format(app), req)

    def publish_application_updates(self, app, autostart=True):
        """Publish updates for the application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        url = '/applications/{0}/publishUpdates'.format(app)
        if not autostart:
            url += '?startAllDraftVms=false'
        self.request('POST', url)

    def set_application_expiration(self, app, req):
        """Set the expiration for the application with ID *app*.

        The *req* parameter must be a dict describing the new expiration.
        """
        if isinstance(app, dict): app = app['id']
        self.request('POST', '/applications/{0}/setExpiration'.format(app), req)

    def get_application_publish_locations(self, app, req=None):
        """Get a list of locations where *app* can be published."""
        if isinstance(app, dict): app = app['id']
        url = '/applications/{0}/findPublishLocations'.format(app)
        return self.request('POST', url, req)

    def get_blueprint_publish_locations(self, bp, req=None):
        """Get a list of locations where *bp* can be published."""
        if isinstance(bp, dict): bp = bp['id']
        url = '/blueprints/{0}/findPublishLocations'.format(bp)
        return self.request('POST', url, req)

    def get_vm(self, app, vm, aspect=None):
        """Return the vm with ID *vm* in the appplication with ID *app*,
        or None if it does not exist.

        The *aspect* parameter (design, deployment) can be used to return
        the vm as designed or as deployed in the cloud.
        """
        if isinstance(app, dict): app = app['id']
        if isinstance(vm, dict): vm = vm['id']
        if aspect is not None:
            app = '{0};{1}'.format(app, aspect)
        return self.request('GET', '/applications/{0}/vms/{1}'.format(app, vm))

    def get_vms(self, app, filter=None):
        """Return a list with all vms (for a given app).

        The *filter* argument can be used to return only a subset of the
        applications. See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        if isinstance(app, dict): app = app['id']
        apps = self.request('GET', '/applications/{0}/vms'.format(app))
        if filter is not None:
            apps = _match_filter(apps, filter)
        return apps

    def start_vm(self, app, vm):
        """Start the VM with ID *vm* in the application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        if isinstance(vm, dict): vm = vm['id']
        self.request('POST', '/applications/{0}/vms/{1}/start'.format(app, vm))

    def stop_vm(self, app, vm):
        """Stop the VM with ID *vm* in the application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        if isinstance(vm, dict): vm = vm['id']
        self.request('POST', '/applications/{0}/vms/{1}/stop'.format(app, vm))

    def poweroff_vm(self, app, vm):
        """Power off the VM with ID *vm* in the application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        if isinstance(vm, dict): vm = vm['id']
        self.request('POST', '/applications/{0}/vms/{1}/poweroff'.format(app, vm))

    def restart_vm(self, app, vm):
        """Restart the VM with ID *vm* in the application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        if isinstance(vm, dict): vm = vm['id']
        self.request('POST', '/applications/{0}/vms/{1}/restart'.format(app, vm))

    def redeploy_vm(self, app, vm):
        """Redeploy the VM with ID *vm* in the application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        if isinstance(vm, dict): vm = vm['id']
        self.request('POST', '/applications/{0}/vms/{1}/redeploy'.format(app, vm))

    def get_vnc_url(self, app, vm):
        """Get the VNC URL for the VM with ID *vm* in the application with ID *app*."""
        if isinstance(app, dict): app = app['id']
        if isinstance(vm, dict): vm = vm['id']
        headers = [('Accept', 'text/plain')]
        url = self.request('GET', '/applications/{0}/vms/{1}/vncUrl'.format(app, vm),
                           headers=headers)
        return url.decode('iso-8859-1')

    def get_blueprint(self, bp):
        """Return the blueprint with ID *bp*, or None if it does not exist."""
        if isinstance(bp, dict): bp = bp['id']
        return self.request('GET', '/blueprints/{0}'.format(bp))

    def get_blueprints(self, filter=None):
        """Return a list with all blueprints.

        The *filter* argument can be used to return only a subset of the
        applications. See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        bps = self.request('GET', '/blueprints')
        if filter is not None:
            bps = _match_filter(bps, filter)
        return bps

    def create_blueprint(self, bp):
        """Create a new blueprint.

        The *bp* parameter must be a dict describing the blueprint to
        create.

        The new blueprint is returned.
        """
        return self.request('POST', '/blueprints', bp)

    def delete_blueprint(self, bp):
        """Delete the blueprint with ID *bp*."""
        if isinstance(bp, dict): bp = bp['id']
        self.request('DELETE', '/blueprints/{0}'.format(bp))

    def get_image(self, img):
        """Return the image with ID *img*, or None if it does not exist."""
        if isinstance(img, dict): img = img['id']
        return self.request('GET', '/images/{0}'.format(img))

    def get_images(self, filter=None):
        """Return a list with all images.

        The *filter* argument can be used to return only a subset of the
        images. See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        imgs = self.request('GET', '/images')
        if filter is not None:
            imgs = _match_filter(imgs, filter)
        return imgs

    def create_image(self, image):
        """Create a new image.

        The *image* parameter must be a dict describing the image to create.

        The new image is returned.
        """
        return self.request('POST', '/images', image)

    def update_image(self, img):
        """Update an existing image.

        The *img* parameter must be the updated image.  The updated image is
        returned.
        """
        return self.request('PUT', '/images/{0}'.format(img['id']), img)

    def delete_image(self, img):
        """Delete the image with ID *img*."""
        if isinstance(img, dict): img = img['id']
        self.request('DELETE', '/images/{0}'.format(img))

    def get_diskimage(self, img):
        """Return the disk image with ID *img*, or None if it does not exist."""
        if isinstance(img, dict): img = img['id']
        return self.request('GET', '/diskImages/{0}'.format(img))

    def get_diskimages(self, filter=None):
        """Return a list with all disk images.

        The *filter* argument can be used to return only a subset of the
        disk images. See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        imgs = self.request('GET', '/diskImages')
        if filter is not None:
            imgs = _match_filter(imgs, filter)
        return imgs

    def create_diskimage(self, img):
        """Create a new disk image.

        The *img* parameter must be a dict describing the disk image to create.

        The new disk image is returned.
        """
        return self.request('POST', '/diskImages', img)

    def update_diskimage(self, img):
        """Update an existing image.

        The *img* parameter must be the updated image.  The updated disk image
        is returned.
        """
        return self.request('PUT', '/diskImages/{0}'.format(img['id']), img)

    def delete_diskimage(self, img):
        """Delete the image with ID *img*."""
        if isinstance(img, dict): img = img['id']
        self.request('DELETE', '/diskImages/{0}'.format(img))

    def get_keypair(self, kp):
        """Return the keypair with ID *kp*, or None if it does not exist."""
        if isinstance(kp, dict): kp = kp['id']
        return self.request('GET', '/keypairs/{0}'.format(kp))

    def get_keypairs(self, filter=None):
        """Return a list with all keypairs.

        The *filter* argument can be used to return only a subset of the
        keypairs.  See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        kps = self.request('GET', '/keypairs')
        if filter is not None:
            kps = _match_filter(kps, filter)
        return kps

    def create_keypair(self, kp):
        """Create a new keypair.

        The *kp* parameter must be a dict describing the keypair to create.

        The new blueprint is returned.
        """
        return self.request('POST', '/keypairs', kp)

    def update_keypair(self, kp):
        """Update an existing keypair.

        The *kp* parameter must be the updated keypair. The updated keypair is
        returned.
        """
        return self.request('PUT', '/keypairs/{0}'.format(kp['id']), kp)

    def delete_keypair(self, kp):
        """Delete the keypair with ID *kp*."""
        if isinstance(kp, dict): kp = kp['id']
        self.request('DELETE', '/keypairs/{0}'.format(kp))

    def generate_keypair(self):
        """Generate a new keypair and return it."""
        return self.request('POST', '/keypairs/generate')

    def get_user(self, user):
        """Return the user with ID *user*, or None if it does not exist."""
        if isinstance(user, dict): user = user['id']
        return self.request('GET', '/users/{0}'.format(user))

    def get_users(self, filter=None):
        """Return a list with all users.

        The *filter* argument can be used to return only a subset of the
        users. See the description of the *cond* argument to :meth:`wait_for`.
        """
        users = self.request('GET', '/users')
        if filter is not None:
            users = _match_filter(users, filter)
        return users

    def create_user(self, user):
        """Invite a new user to organization.

        The *user* parameter must be a dict describing the user to invite.

        The new user is returned.
        """
        org = self.get_organization()['id']
        return self.request('POST', '/organizations/{0}/users'.format(org), user)

    def update_user(self, user, userId):
        """Update an existing user.

        The *user* parameter must be the updated user. The way to update a
        user (or any other resource) is to first retrieve it, make the
        updates client-side, and then use this method to make the update.
        In this case, note however that you can only provide email, name,
        roles, and surname (and email cannot be changed).
        
        The updated user is returned.
        """
        return self.request('PUT', '/users/{0}'.format(userId), user)

    def delete_user(self, user):
        """Delete a user with ID *user*."""
        if isinstance(user, dict): user = user['id']
        self.request('DELETE', '/users/{0}'.format(user))

    def changepw_user(self, passwords, user):
        """Change the password of a user with ID *user*.

        The *passwords* parameter must be a dict describing the existing
        and new passwords.
        """
        return self.request('PUT', '/users/{0}/changepw'.format(user), passwords)

    def get_billing(self, filter=None):
        """Return a list with all applications' charges incurred since
        beginning of the month.

        The *filter* argument can be used to return only a subset of the
        applications. See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        billing = self.request('GET', '/billing')
        if filter is not None:
            billing = _match_filter(billing, filter)
        return billing

    def get_billing_for_month(self, year, month):
        """Return a list with all applications' charges incurred during the
        specified month and year.
        """
        return self.request('GET', '/billing?year={0}&month={1}'.format(year, month))

    def get_events(self):
        """Return a list of all possible event names."""
        return self.request('GET', '/events')

    def get_alerts(self):
        """Return a list of all alerts that user is registered to.
        
        If user is an administrator, list contains all alerts that the
        organization is registered too. 
        """
        return self.request('GET', '/userAlerts')

    def create_alert(self, eventName, userId=None):
        """Registers a user to an alert.
        
        User must be an administrator to specify a *userId*.
        """
        req = {'eventName': eventName}
        if isinstance(userId, int): req['userId'] = userId
        return self.request('POST', '/userAlerts', req)
    
    def delete_alert(self, alertId):
        """Delete a specific userAlert.
        
        Specifiy an *alertId* to unregister a user from it.
        """
        return self.request('DELETE', '/userAlerts/{0}'.format(alertId))

    def search_notifications(self, query):
        """Return list of notifications regarding given criteria.
        
        The *query* parameter must be a dict describing the notifications to
        match. Technically, all 4 of the following params are optional:
        appId, notificationLevel, maxResults, dateRange
        """
        return self.request('POST', '/notifications/search', query)

    def get_organization(self, org=None):
        """Return the authenticated user organization's details.

        The *org* parameter can be used to instead return details according to
        organization ID.
        """
        if isinstance(org, dict): org = org['id']
        if org is None:
            org = ''
        else:
            org = 's/{0}'.format(org)
        return self.request('GET', '/organization{0}'.format(org))

    def update_organization(self, org):
        """Update an organization's details.

        The *org* parameter must be the updated organization. The way to update
        an organization (or any other resource) is to first retrieve it, make
        the updates client-side, and then use this method to make the update.

        The updated organization is returned.
        """
        return self.request('PUT', '/organizations/{0}'.format(org['id']), org)

    def get_permgroup(self, pg):
        """Return the permission group with ID *pg*, or None if it does not exist."""
        if isinstance(pg, dict): pg = pg['id']
        return self.request('GET', '/permissionsGroups/{0}'.format(pg))

    def get_permgroups(self, filter=None):
        """Return a list with all permission groups.

        The *filter* argument can be used to return only a subset of the
        permission groups. See the description of the *cond* argument to
        :meth:`wait_for`.
        """
        pgs = self.request('GET', '/permissionsGroups')
        if filter is not None:
            pgs = _match_filter(pgs, filter)
        return pgs

    def create_permgroup(self, pg):
        """Create a new permission group.

        The *pg* parameter must be a dict describing the permission group
        to create.

        The new permission group is returned.
        """
        return self.request('POST', '/permissionsGroups', pg)

    def update_permgroup(self, pg):
        """Update an existing permission group.

        The *pg* parameter must be the updated permission group. The way to
        update a permission group (or any other resource) is to first retrieve
        it, make the updates client-side, and then use this method to make the
        update.

        The updated permission group is returned.
        """
        return self.request('PUT', '/permissionsGroups/{0}'.format(pg['id']), pg)

    def delete_permgroup(self, pg):
        """Delete a permission group with ID *pg*."""
        if isinstance(pg, dict): pg = pg['id']
        self.request('DELETE', '/permissionsGroups/{0}'.format(pg))

    def get_users_in_permgroup(self, pg):
        """List all of the users in a permission group."""
        if isinstance(pg, dict): pg = pg['id']
        return self.request('GET', '/permissionsGroups/{0}/users'.format(pg))

    def add_user_to_permgroup(self, pg, user):
        """Add a user to a permission group.

        The *user* parameter must be a valid user id.
        """
        if isinstance(pg, dict): pg = pg['id']
        req = {'userId': user}
        return self.request('POST', '/permissionsGroups/{0}/users'.format(pg), req)

    def del_user_from_permgroup(self, pg, user):
        """Delete a user from a permission group.

        The *user* parameter must be a valid user id.
        """
        if isinstance(pg, dict): pg = pg['id']
        return self.request('DELETE', '/permissionsGroups/{0}/users/{1}'.format(pg, user))

    def get_permgroup_descriptors(self):
        """Return a list of resource permission descriptors."""
        return self.request('GET', '/permissionsGroups/describe')
Ejemplo n.º 44
0
    def post(self):
        form = RequestDataForm()
        data = form.data.data
        lines = data.splitlines(True)
        if len(lines) < 3:
            return 'data less 3 lines'

        origin_headers = []
        body = []
        body_start = False

        for index, line in enumerate(lines):
            if index == 0:
                method, path, _ = line.split(' ')
                continue

            if not line.split():
                body_start = True
                continue

            if body_start:
                body.append(line)
            else:
                line = line.strip()
                key, value = line.split(': ', 1)
                origin_headers.append((key, value))

        # for get header value
        header_dict = CaseInsensitiveDict(origin_headers)

        method = method.lower()
        body = ''.join(body)
        content_type = header_dict.get('Content-Type', '')

        # set headers
        headers = []
        origin_host = header_dict.get('Host')
        if form.host.data and origin_host and form.host.data != origin_host:
            headers.append(('Host', header_dict.get('Host')))
        user_agent = header_dict.get('User-Agent')
        referer = header_dict.get('Referer')
        if user_agent:
            headers.append(('User-Agent', user_agent))
        if referer:
            headers.append(('Referer', referer))

        # set cookie
        cookies = []
        cookie = header_dict.get('Cookie')
        C = SimpleCookie(cookie)
        for morsel in C.values():
            cookies.append((morsel.key, morsel.coded_value))

        host = form.host.data or header_dict.get('Host')
        p = urlsplit(path)
        url = urljoin('http://{}'.format(host), p.path)
        params = [(x, repr_value(y)) for x, y in parse_qsl(p.query)]

        if not content_type:
            pass
        elif 'x-www-form-urlencoded' in content_type:
            body = [(x, repr_value(y)) for x, y in parse_qsl(body)]
        elif 'json' in content_type:
            body = [(x, repr_value(y)) for x, y in json.loads(body).items()]
        else:
            headers.append(('Content-Type', content_type))

        code = render_template(
            'code.html',
            method=method,
            url=url,
            params=params,
            body=body,
            headers=headers,
            cookies=cookies,
            content_type=content_type
        )
        return render_template('code-page.html', code=code)
Ejemplo n.º 45
0
class Response(object):
    """
    Response is an object for describing a HTTP response. Every view is
    responsible for either returning a response or raising an exception.
    """

    status_code = 200
    """The HTTP status code for the response."""

    def __init__(self, content='', status=None,
                 content_type='text/html; charset=utf8'):
        self.content = content

        if status:
            self.status_code = status

        self.headers = {'Content-Type': content_type}
        self.cookies = SimpleCookie()

    def __str__(self):
        headers = ['%s: %s' % (key, value) for key, value in self.headers.items()]
        return '\n'.join(headers) + '\n\n' + self.content

    def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
                   domain=None, secure=False):
        """
        Sets a cookie. The parameters are the same as in the Cookie.Morsel
        object in the Python standard library.
        """

        self.cookies[key] = value

        if max_age is not None:
            self.cookies[key]['max-age'] = max_age

        if expires is not None:
            if isinstance(expires, datetime.datetime):
                expires = expires.strftime('%a, %d %b %Y %H:%M:%S')

            self.cookies[key]['expires'] = expires

        if path is not None:
            self.cookies[key]['path'] = path

        if domain is not None:
            self.cookies[key]['domain'] = domain

        if secure:
            self.cookies[key]['secure'] = True

    def delete_cookie(self, key, path='/', domain=None):
        """
        Deletes the cookie with the given key. Fails silently if the key
        doesn't exist
        """

        self.set_cookie(key, max_age=0, path=path, domain=domain,
                        expires='Thu, 01-Jan-1970 00:00:00 GMT')

    def headers_items(self):
        headers = [(k, v) for k, v in self.headers.items()]

        for cookie in self.cookies.values():
            headers.append(('Set-Cookie', str(cookie.output(header=''))))

        return headers
Ejemplo n.º 46
0
class Response:
    __slots__ = [
        "req",
        "status_code",
        "content",
        "encoding",
        "media",
        "headers",
        "formats",
        "cookies",
        "session",
        "mimetype",
        "_stream",
    ]

    text = content_setter("text/plain")
    html = content_setter("text/html")

    def __init__(self, req, *, formats):
        self.req = req
        self.status_code = None  #: The HTTP Status Code to use for the Response.
        self.content = None  #: A bytes representation of the response body.
        self.mimetype = None
        self.encoding = DEFAULT_ENCODING
        self.media = (
            None
        )  #: A Python object that will be content-negotiated and sent back to the client. Typically, in JSON formatting.
        self._stream = None
        self.headers = (
            {}
        )  #: A Python dictionary of ``{key: value}``, representing the headers of the response.
        self.formats = formats
        self.cookies = SimpleCookie()  #: The cookies set in the Response
        self.session = (
            req.session
        )  #: The cookie-based session data, in dict form, to add to the Response.

    # Property or func/dec
    def stream(self, func, *args, **kwargs):
        assert inspect.isasyncgenfunction(func)

        self._stream = functools.partial(func, *args, **kwargs)

        return func

    def redirect(self, location, *, set_text=True, status_code=HTTP_301):
        self.status_code = status_code
        if set_text:
            self.text = f"Redirecting to: {location}"
        self.headers.update({"Location": location})

    @property
    async def body(self):
        if self._stream is not None:
            return (self._stream(), {})

        if self.content is not None:
            headers = {}
            content = self.content
            if self.mimetype is not None:
                headers["Content-Type"] = self.mimetype
            if self.mimetype == "text/plain" and self.encoding is not None:
                headers["Encoding"] = self.encoding
                content = content.encode(self.encoding)
            return (content, headers)

        for format in self.formats:
            if self.req.accepts(format):
                return (await self.formats[format](self, encode=True)), {}

        # Default to JSON anyway.
        return (
            await self.formats["json"](self, encode=True),
            {
                "Content-Type": "application/json"
            },
        )

    def set_cookie(
        self,
        key,
        value="",
        expires=None,
        path="/",
        domain=None,
        max_age=None,
        secure=False,
        httponly=True,
    ):
        self.cookies[key] = value
        morsel = self.cookies[key]
        if expires is not None:
            morsel["expires"] = expires
        if path is not None:
            morsel["path"] = path
        if domain is not None:
            morsel["domain"] = domain
        if max_age is not None:
            morsel["max-age"] = max_age
        morsel["secure"] = secure
        morsel["httponly"] = httponly

    def _prepare_cookies(self, starlette_response):
        cookie_header = ((b"set-cookie",
                          morsel.output(header="").lstrip().encode("latin-1"))
                         for morsel in self.cookies.values())
        starlette_response.raw_headers.extend(cookie_header)

    async def __call__(self, scope, receive, send):
        body, headers = await self.body
        if self.headers:
            headers.update(self.headers)

        if self._stream is not None:
            response_cls = StarletteStreamingResponse
        else:
            response_cls = StarletteResponse

        response = response_cls(body,
                                status_code=self.status_code,
                                headers=headers)
        self._prepare_cookies(response)

        await response(scope, receive, send)
Ejemplo n.º 47
0
class BaseResponse:
    """Base class for Response"""
    default_status = 200
    default_content_type = 'text/plain;'

    def __init__(self, body=b'', status=None, headers=None):
        self.headers = Headers()
        self._body = body
        self._status_code = status or self.default_status
        self._cookies = SimpleCookie()

        if headers:
            for name, value in headers.items():
                self.headers.add_header(name, value)

    @property
    def body(self):
        return [self._body]

    @property
    def status_code(self):
        """ The HTTP status code as an integer (e.g. 404)."""
        return self._status_code

    @property
    def status(self):
        """ The HTTP status line as a string (e.g. ``404 Not Found``)."""
        if not 100 <= self._status_code <= 999:
            raise ValueError('Status code out of range.')
        status = _HTTP_STATUS_LINES.get(self._status_code)
        return str(status or ('{} Unknown'.format(self._status_code)))

    @status.setter
    def status(self, status_code):
        if not 100 <= status_code <= 999:
            raise ValueError('Status code out of range.')
        self._status_code = status_code

    @property
    def headerlist(self):
        """ WSGI conform list of (header, value) tuples. """
        if 'Content-Type' not in self.headers:
            self.headers.add_header('Content-Type', self.default_content_type)
        if self._cookies:
            for c in self._cookies.values():
                self.headers.add_header('Set-Cookie', c.OutputString())
        return self.headers.items()

    def set_cookie(self, key, value, expires=None, max_age=None, path=None,
                   secret=None, digestmod=hashlib.sha256):
        if secret:
            if isinstance(secret, str):
                secret = secret.encode('utf-8')
            encoded = base64.b64encode(pickle.dumps((key, value), pickle.HIGHEST_PROTOCOL))
            sig = base64.b64encode(hmac.new(secret, encoded, digestmod=digestmod).digest())
            value_bytes = b'!' + sig + b'?' + encoded
            value = value_bytes.decode('utf-8')

        self._cookies[key] = value
        if len(key) + len(value) > 3800:
            raise ValueError('Content does not fit into a cookie.')

        if max_age is not None:
            if isinstance(max_age, int):
                max_age_value = max_age
            else:
                max_age_value = max_age.seconds + max_age.days * 24 * 3600
            self._cookies[key]['max-age'] = max_age_value
        if expires is not None:
            if isinstance(expires, int):
                expires_value = expires
            else:
                expires_value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", expires.timetuple())
            self._cookies[key]['expires'] = expires_value
        if path:
            self._cookies[key]['path'] = path

    def delete_cookie(self, key, **kwargs):
        kwargs['max_age'] = -1
        kwargs['expires'] = 0
        self.set_cookie(key, '', **kwargs)
Ejemplo n.º 48
0
class Response(object):
    charset = 'utf-8'

    def __init__(self, output, headers=None, status=200,
                 content_type='text/html', wrapped=False):
        self.output, self.status, self.wrapped = output, status, wrapped
        self.headers = HTTPHeaders(headers)
        if status != 304 and 'Content-Type' not in self.headers:
            if ';' not in content_type and (
                    content_type.startswith('text/') or
                    content_type == 'application/xml' or
                    (content_type.startswith('application/') and
                     content_type.endswith('+xml'))):
                content_type += '; charset=' + self.charset
            self.headers['Content-Type'] = content_type

    def set_cookie(self, name, value, domain=None, expires=None, path="/",
                   expires_days=None, signed=None, **kwargs):
        """Set the given cookie name/value with the given options."""
        name = str(name)
        value = value if isinstance(value, str) else value.encode('utf-8')
        if re.search(r"[\x00-\x20]", name + value):
            raise ValueError("Invalid cookie %r: %r" % (name, value))
        if expires_days is not None and not expires:
            expires = datetime.utcnow() + timedelta(days=expires_days)
        attrs = [("domain", domain), ("path", path),
                 ("expires", expires and format_timestamp(expires))]
        if "max_age" in kwargs:
            attrs.append(("max-age", kwargs.pop("max_age")))
        if not hasattr(self, "_new_cookie"):
            self._new_cookie = SimpleCookie()
        elif name in self._new_cookie:
            del self._new_cookie[name]
        self._new_cookie[name] = value if not signed else ""
        morsel = self._new_cookie[name]
        for (key, val) in attrs + list(kwargs.items()):
            morsel[key] = val or ""
        morsel._signed = signed and value

    def clear_cookie(self, name, path="/", domain=None):
        """Delete the cookie with the given name."""
        self.set_cookie(
            name, value="", path=path, expires_days=(-365), domain=domain)

    def set_secure_cookie(self, name, value, expires_days=30, **kwargs):
        """Sign and timestamp a cookie so it cannot be forged."""
        self.set_cookie(
            name, value, expires_days=expires_days, signed=True, **kwargs)

    def send(self, environ, start_response):
        """Send the headers and return the body of the response."""
        status = "%d %s" % (self.status, HTTP_CODES.get(self.status))
        body = (self.output if self.wrapped else
                [tobytes(self.output)]) if self.output else []
        if not self.wrapped:
            self.headers['Content-Length'] = str(body and len(body[0]) or 0)
        if hasattr(self, "_new_cookie"):
            app = environ['fiole.app']
            for cookie in self._new_cookie.values():
                if cookie._signed is not None:
                    self._new_cookie[cookie.key] = (
                        app.encode_signed(cookie.key, cookie._signed))
                self.headers.add("Set-Cookie", cookie.OutputString(None))
        start_response(status, self.headers.to_list())
        if environ['REQUEST_METHOD'] != 'HEAD':
            return body
        if hasattr(body, 'close'):
            body.close()
        return []
Ejemplo n.º 49
0
def format_cookie(text):
    '''将字符串转换为字典形式的cookies'''
    cookie = SimpleCookie(text)
    return {i.key: i.value for i in cookie.values()}
Ejemplo n.º 50
0
class HttpResponse(HttpResponseException, metaclass=abc.ABCMeta):
    """
    A basic HTTP response, with content and dictionary-accessed headers.
    """
    @abc.abstractproperty
    def status_code(self):
        pass

    @abc.abstractproperty
    def status_text(self):
        pass

    def __init__(self, content='', content_type=None):
        # _headers is a mapping of the lower-case name to the original
        # case of the header and the header value.
        self._headers = {}

        # Content-Type
        self.charset = 'utf-8'  # TODO settings
        self.set_content(content, content_type)

        # Cookies
        self._cookies = SimpleCookie()

    def set_content(self, content, content_type=None, content_encoding=None):
        self._content = content or ''

        if content_type:
            # Parsing the string
            self.content_type, pdict = cgi.parse_header(content_type)
            self.charset = pdict.get('charset', self.charset)

        else:
            self.content_type = 'text/html'  # TODO settings

        if type(content) == str:
            content_type = "%s; charset=%s" % (self.content_type, self.charset)
        else:
            content_type = self.content_type
        # Setting header Content-Type
        self['Content-Type'] = content_type

        # Setting header Content-Encoding
        if content_encoding:
            self['Content-Encoding'] = content_encoding

    def set_cookies(self, cookies):
        self._cookies.load(cookies.output(header=''))

    @property
    def status(self):
        return '%s %s' % (self.status_code, self.status_text)

    @property
    def headers(self):
        response_headers = [
            (key, value)
            for key, value in self._headers.values()
        ]

        response_headers += [
            ('Set-Cookie', morsel.output(header='').strip())
            for morsel in self._cookies.values()
        ]

        return response_headers

    @property
    def content(self):
        if type(self._content) == str:
            content = self._content.encode(self.charset)
        else:
            content = self._content
        return [ content ]

    def __setitem__(self, header, value):
        self._headers[header.lower()] = (header, value)

    def __delitem__(self, header):
        try:
            del self._headers[header.lower()]
        except KeyError:
            pass

    def __getitem__(self, header):
        return self._headers[header.lower()][1]

    def __str__(self):
        return 'HttpResponse <%s, %s>' % (self.status_code, self.status_text)
Ejemplo n.º 51
0
class StreamResponse(HeadersMixin):

    def __init__(self, *, status=200, reason=None, headers=None):
        self._body = None
        self._keep_alive = None
        self._chunked = False
        self._chunk_size = None
        self._compression = False
        self._compression_force = False
        self._headers = CIMultiDict()
        self._cookies = SimpleCookie()

        self._req = None
        self._resp_impl = None
        self._eof_sent = False

        self._task = None

        if headers is not None:
            # TODO: optimize CIMultiDict extending
            self._headers.extend(headers)
        self._headers.setdefault(hdrs.CONTENT_TYPE, 'application/octet-stream')

        self.set_status(status, reason)

    @property
    def prepared(self):
        return self._resp_impl is not None

    @property
    def started(self):
        warnings.warn('use Response.prepared instead', DeprecationWarning)
        return self.prepared

    @property
    def task(self):
        return self._task

    @property
    def status(self):
        return self._status

    @property
    def chunked(self):
        return self._chunked

    @property
    def compression(self):
        return self._compression

    @property
    def reason(self):
        return self._reason

    def set_status(self, status, reason=None):
        if self.prepared:
            raise RuntimeError("Cannot change the response status code after "
                               "the headers have been sent")
        self._status = int(status)
        if reason is None:
            reason = ResponseImpl.calc_reason(status)
        self._reason = reason

    @property
    def keep_alive(self):
        return self._keep_alive

    def force_close(self):
        self._keep_alive = False

    @property
    def body_length(self):
        return self._resp_impl.body_length

    @property
    def output_length(self):
        return self._resp_impl.output_length

    def enable_chunked_encoding(self, chunk_size=None):
        """Enables automatic chunked transfer encoding."""
        self._chunked = True
        self._chunk_size = chunk_size

    def enable_compression(self, force=None):
        """Enables response compression encoding."""
        # Backwards compatibility for when force was a bool <0.17.
        if type(force) == bool:
            force = ContentCoding.deflate if force else ContentCoding.identity
        elif force is not None:
            assert isinstance(force, ContentCoding), ("force should one of "
                                                      "None, bool or "
                                                      "ContentEncoding")

        self._compression = True
        self._compression_force = force

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self):
        return self._cookies

    def set_cookie(self, name, value, *, expires=None,
                   domain=None, max_age=None, path='/',
                   secure=None, httponly=None, version=None):
        """Set or update response cookie.

        Sets new cookie or updates existent with new value.
        Also updates only those params which are not None.
        """

        old = self._cookies.get(name)
        if old is not None and old.coded_value == '':
            # deleted cookie
            self._cookies.pop(name, None)

        self._cookies[name] = value
        c = self._cookies[name]

        if expires is not None:
            c['expires'] = expires
        elif c.get('expires') == 'Thu, 01 Jan 1970 00:00:00 GMT':
            del c['expires']

        if domain is not None:
            c['domain'] = domain

        if max_age is not None:
            c['max-age'] = max_age
        elif 'max-age' in c:
            del c['max-age']

        c['path'] = path

        if secure is not None:
            c['secure'] = secure
        if httponly is not None:
            c['httponly'] = httponly
        if version is not None:
            c['version'] = version

    def del_cookie(self, name, *, domain=None, path='/'):
        """Delete cookie.

        Creates new empty expired cookie.
        """
        # TODO: do we need domain/path here?
        self._cookies.pop(name, None)
        self.set_cookie(name, '', max_age=0,
                        expires="Thu, 01 Jan 1970 00:00:00 GMT",
                        domain=domain, path=path)

    @property
    def content_length(self):
        # Just a placeholder for adding setter
        return super().content_length

    @content_length.setter
    def content_length(self, value):
        if value is not None:
            value = int(value)
            # TODO: raise error if chunked enabled
            self.headers[hdrs.CONTENT_LENGTH] = str(value)
        else:
            self.headers.pop(hdrs.CONTENT_LENGTH, None)

    @property
    def content_type(self):
        # Just a placeholder for adding setter
        return super().content_type

    @content_type.setter
    def content_type(self, value):
        self.content_type  # read header values if needed
        self._content_type = str(value)
        self._generate_content_type_header()

    @property
    def charset(self):
        # Just a placeholder for adding setter
        return super().charset

    @charset.setter
    def charset(self, value):
        ctype = self.content_type  # read header values if needed
        if ctype == 'application/octet-stream':
            raise RuntimeError("Setting charset for application/octet-stream "
                               "doesn't make sense, setup content_type first")
        if value is None:
            self._content_dict.pop('charset', None)
        else:
            self._content_dict['charset'] = str(value).lower()
        self._generate_content_type_header()

    @property
    def last_modified(self, _LAST_MODIFIED=hdrs.LAST_MODIFIED):
        """The value of Last-Modified HTTP header, or None.

        This header is represented as a `datetime` object.
        """
        httpdate = self.headers.get(_LAST_MODIFIED)
        if httpdate is not None:
            timetuple = parsedate(httpdate)
            if timetuple is not None:
                return datetime.datetime(*timetuple[:6],
                                         tzinfo=datetime.timezone.utc)
        return None

    @last_modified.setter
    def last_modified(self, value):
        if value is None:
            self.headers.pop(hdrs.LAST_MODIFIED, None)
        elif isinstance(value, (int, float)):
            self.headers[hdrs.LAST_MODIFIED] = time.strftime(
                "%a, %d %b %Y %H:%M:%S GMT", time.gmtime(math.ceil(value)))
        elif isinstance(value, datetime.datetime):
            self.headers[hdrs.LAST_MODIFIED] = time.strftime(
                "%a, %d %b %Y %H:%M:%S GMT", value.utctimetuple())
        elif isinstance(value, str):
            self.headers[hdrs.LAST_MODIFIED] = value

    @property
    def tcp_nodelay(self):
        resp_impl = self._resp_impl
        if resp_impl is None:
            raise RuntimeError("Cannot get tcp_nodelay for "
                               "not prepared response")
        return resp_impl.transport.tcp_nodelay

    def set_tcp_nodelay(self, value):
        resp_impl = self._resp_impl
        if resp_impl is None:
            raise RuntimeError("Cannot set tcp_nodelay for "
                               "not prepared response")
        resp_impl.transport.set_tcp_nodelay(value)

    @property
    def tcp_cork(self):
        resp_impl = self._resp_impl
        if resp_impl is None:
            raise RuntimeError("Cannot get tcp_cork for "
                               "not prepared response")
        return resp_impl.transport.tcp_cork

    def set_tcp_cork(self, value):
        resp_impl = self._resp_impl
        if resp_impl is None:
            raise RuntimeError("Cannot set tcp_cork for "
                               "not prepared response")
        resp_impl.transport.set_tcp_cork(value)

    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):
        params = '; '.join("%s=%s" % i for i in self._content_dict.items())
        if params:
            ctype = self._content_type + '; ' + params
        else:
            ctype = self._content_type
        self.headers[CONTENT_TYPE] = ctype

    def _start_pre_check(self, request):
        if self._resp_impl is not None:
            if self._req is not request:
                raise RuntimeError(
                    "Response has been started with different request.")
            else:
                return self._resp_impl
        else:
            return None

    def _do_start_compression(self, coding):
        if coding != ContentCoding.identity:
            self.headers[hdrs.CONTENT_ENCODING] = coding.value
            self._resp_impl.add_compression_filter(coding.value)
            self.content_length = None

    def _start_compression(self, request):
        if self._compression_force:
            self._do_start_compression(self._compression_force)
        else:
            accept_encoding = request.headers.get(
                hdrs.ACCEPT_ENCODING, '').lower()
            for coding in ContentCoding:
                if coding.value in accept_encoding:
                    self._do_start_compression(coding)
                    return

    def start(self, request):
        warnings.warn('use .prepare(request) instead', DeprecationWarning)
        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl

        return self._start(request)

    @asyncio.coroutine
    def prepare(self, request):
        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl
        yield from request._prepare_hook(self)

        return self._start(request)

    def _start(self, request,
               HttpVersion10=HttpVersion10,
               HttpVersion11=HttpVersion11,
               CONNECTION=hdrs.CONNECTION,
               DATE=hdrs.DATE,
               SERVER=hdrs.SERVER,
               SET_COOKIE=hdrs.SET_COOKIE,
               TRANSFER_ENCODING=hdrs.TRANSFER_ENCODING):
        self._req = request
        keep_alive = self._keep_alive
        if keep_alive is None:
            keep_alive = request.keep_alive
        self._keep_alive = keep_alive
        version = request.version

        resp_impl = self._resp_impl = ResponseImpl(
            request._writer,
            self._status,
            version,
            not keep_alive,
            self._reason)

        headers = self.headers
        for cookie in self._cookies.values():
            value = cookie.output(header='')[1:]
            headers.add(SET_COOKIE, value)

        if self._compression:
            self._start_compression(request)

        if self._chunked:
            if request.version != HttpVersion11:
                raise RuntimeError("Using chunked encoding is forbidden "
                                   "for HTTP/{0.major}.{0.minor}".format(
                                       request.version))
            resp_impl.chunked = True
            if self._chunk_size:
                resp_impl.add_chunking_filter(self._chunk_size)
            headers[TRANSFER_ENCODING] = 'chunked'
        else:
            resp_impl.length = self.content_length

        headers.setdefault(DATE, request.time_service.strtime())
        headers.setdefault(SERVER, resp_impl.SERVER_SOFTWARE)
        if CONNECTION not in headers:
            if keep_alive:
                if version == HttpVersion10:
                    headers[CONNECTION] = 'keep-alive'
            else:
                if version == HttpVersion11:
                    headers[CONNECTION] = 'close'

        resp_impl.headers = headers

        self._send_headers(resp_impl)
        self._task = request._task
        return resp_impl

    def _send_headers(self, resp_impl):
        # Durty hack required for
        # https://github.com/KeepSafe/aiohttp/issues/1093
        # File sender may override it
        resp_impl.send_headers()

    def write(self, data):
        assert isinstance(data, (bytes, bytearray, memoryview)), \
            "data argument must be byte-ish (%r)" % type(data)

        if self._eof_sent:
            raise RuntimeError("Cannot call write() after write_eof()")
        if self._resp_impl is None:
            raise RuntimeError("Cannot call write() before start()")

        if data:
            return self._resp_impl.write(data)
        else:
            return ()

    @asyncio.coroutine
    def drain(self):
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")
        yield from self._resp_impl.transport.drain()

    @asyncio.coroutine
    def write_eof(self):
        if self._eof_sent:
            return
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")

        yield from self._resp_impl.write_eof()
        self._eof_sent = True

    def __repr__(self):
        if self.started:
            info = "{} {} ".format(self._req.method, self._req.path)
        else:
            info = "not started"
        return "<{} {} {}>".format(self.__class__.__name__,
                                   self.reason, info)