Example #1
0
def _load_static_table():
    """Parses the hpack static table, which was copied from
    http://http2.github.io/http2-spec/compression.html#static.table
    corresponding to
    http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-A
    """
    # start the table with a dummy entry 0
    table = [None]
    with open(os.path.join(os.path.dirname(__file__),
                           'hpack_static_table.txt')) as f:
        for line in f:
            if not line:
                continue
            fields = line.split('\t')
            if int(fields[0]) != len(table):
                raise ValueError("inconsistent numbering in static table")
            name = utf8(fields[1].strip())
            value = utf8(fields[2].strip()) if len(fields) > 2 else None
            table.append((name, value))
    static_keys = {}
    static_pairs = {}
    for i, pair in enumerate(table):
        if pair is None:
            continue
        if pair[0] not in static_keys:
            # For repeated keys, prefer the earlier one.
            static_keys[pair[0]] = i
        static_pairs[pair] = i
    return table, static_keys, static_pairs
Example #2
0
    def get_authenticated_user(self, callback, http_client=None):
        """Gets the OAuth authorized user and access token on callback.

        This method should be called from the handler for your registered
        OAuth Callback URL to complete the registration process. We call
        callback with the authenticated user, which in addition to standard
        attributes like 'name' includes the 'access_key' attribute, which
        contains the OAuth access you can use to make authorized requests
        to this service on behalf of the user.

        """
        request_key = escape.utf8(self.get_argument("oauth_token"))
        oauth_verifier = self.get_argument("oauth_verifier", None)
        request_cookie = self.get_cookie("_oauth_request_token")
        if not request_cookie:
            logging.warning("Missing OAuth request token cookie")
            callback(None)
            return
        self.clear_cookie("_oauth_request_token")
        cookie_key, cookie_secret = [base64.b64decode(escape.utf8(i)) for i in request_cookie.split("|")]
        if cookie_key != request_key:
            logging.info((cookie_key, request_key, request_cookie))
            logging.warning("Request token does not match cookie")
            callback(None)
            return
        token = dict(key=cookie_key, secret=cookie_secret)
        if oauth_verifier:
            token["verifier"] = oauth_verifier
        if http_client is None:
            http_client = httpclient.AsyncHTTPClient()
        http_client.fetch(self._oauth_access_token_url(token),
                          self.async_callback(self._on_access_token, callback))
Example #3
0
def fix_top_recommend_users():
    thumb_up_recorder = UserAnswerThumbUpRecorder()
    db = mongoclient.fbt
    for exp in db.answers.find({}):
        tags_with_class = [utf8(exp["class2"]) + ":" + utf8(tag) for tag in exp["tags"]]
        if exp["thumb_up_num"] > 0:
            thumb_up_recorder.thumb_up_for_user(exp["publisher"], tags_with_class, exp["thumb_up_num"])
Example #4
0
    def post(self):
        data = self.post_schema()
        name = data['name']
        password = data['password']
        try:
            user = User.objects(name=name).get()
        except DoesNotExist:
            # 失败
            self.write_not_found_entity_response()
            # self.render("login.html", error="name not found")
            return

        hashed_password = bcrypt.hashpw(utf8(password),
                                        utf8(user.hashed_password))
        if hashed_password == user.hashed_password:
            # 成功
            self.set_secure_cookie("shanbay_user", str(user.id))
            self.write_response(user.format_response())
            return
            # self.render("home.html")
            # self.redirect(self.get_argument("next", "/"))
        else:
            logging.error("incorrect password")
            self.render("login.html", error="incorrect password")
            return
    def write_error(self, status_code, **kwargs):
        http_explanations = {
            400: 'Request not properly formatted or contains languages that Apertium APy does not support',
            404: 'Resource requested does not exist. URL may have been mistyped',
            408: 'Server did not receive a complete request within the time it was prepared to wait. Try again',
            500: 'Unexpected condition on server. Request could not be fulfilled.',
        }
        explanation = kwargs.get('explanation', http_explanations.get(status_code, ''))
        if 'exc_info' in kwargs and len(kwargs['exc_info']) > 1:
            exception = kwargs['exc_info'][1]
            if hasattr(exception, 'log_message') and exception.log_message:
                explanation = exception.log_message % exception.args
            elif hasattr(exception, 'reason'):
                explanation = exception.reason or tornado.httputil.responses.get(status_code, 'Unknown')
            else:
                explanation = tornado.httputil.responses.get(status_code, 'Unknown')

        result = {
            'status': 'error',
            'code': status_code,
            'message': tornado.httputil.responses.get(status_code, 'Unknown'),
            'explanation': explanation,
        }

        data = escape.json_encode(result)
        self.set_header('Content-Type', 'application/json; charset=UTF-8')

        if self.callback:
            self.set_header('Content-Type', 'application/javascript; charset=UTF-8')
            self._write_buffer.append(utf8('%s(%s)' % (self.callback, data)))
        else:
            self._write_buffer.append(utf8(data))
        self.finish()
def save_blog(blog_url):
    print blog_url
    req = urllib2.Request(blog_url)    
    html = urllib2.urlopen(req)    
##    print html
    #html = response.read()  
    # soup = BeautifulSoup(open("/Users/mio/Desktop/r_blog.html"), "html.parser")
    soup = BeautifulSoup(html, "html.parser")
##    print soup

    # 日期
    blog_date = soup.find('span', class_="blogDetail-ownerOther-date")
    blog_date = utf8(blog_date.contents[0])
    print blog_date
    # 标题
    title = soup.find('h2', class_="blogDetail-title")
    title = utf8(title.contents[0])
    title = title.replace("/", "\\")
    print title

    # print soup
    a = soup.find_all("div", class_="blogDetail-content")
    blog_content = a[0]
    filename = blog_date.replace(':','-')
    with open("{}{}.html".format(download_dir, filename), "wb") as fw:
        fw.write("# {}\n".format(title))
        fw.write("> {}\n\n".format(blog_date))
        for i in blog_content:
            try:
                fw.write(str(i))
            except Exception as e:
                print e
                pass

    return get_next(soup)
Example #7
0
def save_blog(blog_url):
    print blog_url
    html = requests.get(blog_url, cookies=cookie).content
    # soup = BeautifulSoup(open("/Users/mio/Desktop/r_blog.html"), "html.parser")
    soup = BeautifulSoup(html, "html.parser")

    # 日期
    blog_date = soup.find('span', class_="blogDetail-ownerOther-date")
    blog_date = utf8(blog_date.contents[0])
    # 标题
    title = soup.find('h2', class_="blogDetail-title")
    title = utf8(title.contents[0])
    title = title.replace("/", "\\")
    print title

    # print soup
    a = soup.find_all("div", class_="blogDetail-content")
    blog_content = a[0]

    with open("{}{}.md".format(download_dir, title), "wb") as fw:
        fw.write("# {}\n".format(title))
        fw.write("> {}\n\n".format(blog_date))
        for i in blog_content:
            try:
                fw.write(str(i))
            except Exception as e:
                print e
                pass

    return get_next(soup)
Example #8
0
 def _on_connect(self, parsed):
     if self._timeout is not None:
         self.io_loop.remove_timeout(self._timeout)
         self._timeout = None
     if self.request.request_timeout:
         self._timeout = self.io_loop.add_timeout(
             self.start_time + self.request.request_timeout,
             self._on_timeout)
     if (self.request.validate_cert and
         isinstance(self.stream, SSLIOStream)):
         match_hostname(self.stream.socket.getpeercert(),
                        parsed.hostname)
     if (self.request.method not in self._SUPPORTED_METHODS and
         not self.request.allow_nonstandard_methods):
         raise KeyError("unknown method %s" % self.request.method)
     for key in ('network_interface',
                 'proxy_host', 'proxy_port',
                 'proxy_username', 'proxy_password'):
         if getattr(self.request, key, None):
             raise NotImplementedError('%s not supported' % key)
     if "Host" not in self.request.headers:
         self.request.headers["Host"] = parsed.netloc
     username, password = None, None
     if parsed.username is not None:
         username, password = parsed.username, parsed.password
     elif self.request.auth_username is not None:
         username = self.request.auth_username
         password = self.request.auth_password
     if username is not None:
         auth = utf8(username) + b(":") + utf8(password)
         self.request.headers["Authorization"] = (b("Basic ") +
                                                  base64.b64encode(auth))
     if self.request.user_agent:
         self.request.headers["User-Agent"] = self.request.user_agent
     if not self.request.allow_nonstandard_methods:
         if self.request.method in ("POST", "PUT"):
             assert self.request.body is not None
         else:
             assert self.request.body is None
     if self.request.body is not None:
         self.request.headers["Content-Length"] = str(len(
                 self.request.body))
     if (self.request.method == "POST" and
         "Content-Type" not in self.request.headers):
         self.request.headers["Content-Type"] = "application/x-www-form-urlencoded"
     if self.request.use_gzip:
         self.request.headers["Accept-Encoding"] = "gzip"
     req_path = ((parsed.path or '/') +
             (('?' + parsed.query) if parsed.query else ''))
     request_lines = [utf8("%s %s HTTP/1.1" % (self.request.method,
                                               req_path))]
     for k, v in self.request.headers.get_all():
         line = utf8(k) + b(": ") + utf8(v)
         if b('\n') in line:
             raise ValueError('Newline in header: ' + repr(line))
         request_lines.append(line)
     self.stream.write(b("\r\n").join(request_lines) + b("\r\n\r\n"))
     if self.request.body is not None:
         self.stream.write(self.request.body)
     self.stream.read_until_regex(b("\r?\n\r?\n"), self._on_headers)
Example #9
0
 def test_unrecognised_command(self):
     self.connect()
     for command in ['', '  ']:
         self.stream.write(utf8('%s\r\n' % command))
         data = self.read_response()
         self.assertEqual(data, utf8('500 Error: bad syntax\r\n'))
     self.close()
Example #10
0
def any_to_bytes(s):
    if isinstance(s, unicode_type):
        return utf8(s)
    elif isinstance(s, bytes):
        return s

    return utf8(str(s))
Example #11
0
def _valid_cache(value, handler, condition, new_condtion, anonymous, now):
    if not options.cache_enabled:
        return False

    if anonymous and handler.current_user:
        return False

    if condition:
        old_cond = value.get("condition", "") if value else ""
        rows = conn.mysql.query(condition)

        new_cond = ""
        for r in rows:
            new_cond += str(r)

        # unify to utf8, the string result return by pymongo is unicode
        new_cond = utf8(new_cond)
        old_cond = utf8(old_cond if old_cond else "")

        if old_cond != new_cond:
            new_condtion["condition"] = new_cond
            return False

    if value:
        if value["expire"] > now:
            return True
        else:
            return False
    else:
        return False
    def __call__(self, request):
        data = {}
        response = []
        def start_response(status, response_headers, exc_info=None):
            data["status"] = status
            data["headers"] = response_headers
            return response.append
        app_response = self.wsgi_application(
            WSGIContainer.environ(request), start_response)
        response.extend(app_response)
        body = b("").join(response)
        if hasattr(app_response, "close"):
            app_response.close()
        if not data: raise Exception("WSGI app did not call start_response")

        status_code = int(data["status"].split()[0])
        headers = data["headers"]
        header_set = set(k.lower() for (k,v) in headers)
        body = escape.utf8(body)
        if "content-length" not in header_set:
            headers.append(("Content-Length", str(len(body))))
        if "content-type" not in header_set:
            headers.append(("Content-Type", "text/html; charset=UTF-8"))
        if "server" not in header_set:
            headers.append(("Server", "TornadoServer/%s" % tornado.version))

        parts = [escape.utf8("HTTP/1.1 " + data["status"] + "\r\n")]
        for key, value in headers:
            parts.append(escape.utf8(key) + b(": ") + escape.utf8(value) + b("\r\n"))
        parts.append(b("\r\n"))
        parts.append(body)
        request.write(b("").join(parts))
        request.finish()
        self._log(status_code, request)
Example #13
0
    def post(self):
        login_email = self.get_argument(app_db_model.DOC_KEY_ACCOUNT_EMAIL)
        login_password = escape.utf8(self.get_argument(app_db_model.DOC_KEY_ACCOUNT_PASSWORD))

        account = None
        try:
            account_cursor = self.db.account.find({
                app_db_model.DOC_KEY_ACCOUNT_EMAIL: login_email
            })
            while (yield account_cursor.fetch_next):
                account = account_cursor.next_object()
                break
        except Exception as e:
            logger.error(e)

        if not account:
            self.set_status(404, 'login error')
            self.finish()
            return

        hashed_password = bcrypt.hashpw(login_password, escape.utf8(account[app_db_model.DOC_KEY_ACCOUNT_PASSWORD]))
        if hashed_password:
            self.set_secure_cookie('user', str(account[app_db_model.DOC_KEY_ID]))
            self.set_current_user(
                str(account[app_db_model.DOC_KEY_ID]),
                account[app_db_model.DOC_KEY_ACCOUNT_NAME],
                account[app_db_model.DOC_KEY_ACCOUNT_EMAIL])
            self.redirect('/')
        else:
            self.set_status(404, 'login error')
            self.finish()
Example #14
0
 def test_unicode_literal_expression(self):
     # Unicode literals should be usable in templates.  Note that this
     # test simulates unicode characters appearing directly in the
     # template file (with utf8 encoding), i.e. \u escapes would not
     # be used in the template file itself.
     template = Template(utf8(u'{{ "\u00e9" }}'))
     self.assertEqual(template.generate(), utf8(u"\u00e9"))
Example #15
0
 def _render_parts(self, value, parts=[]):
     self._LOGGER.info('rendering parts')
     self._LOGGER.debug(value)
     if isinstance(value, str) or isinstance(value, unicode):
         #parts.append(escape.xhtml_escape(value))
         parts.append(value)
     elif isinstance(value, int):
         parts.append(str(value))
     elif isinstance(value, datetime.datetime):
         parts.append(value.strftime("%Y-%m-%dT%H:%M:%S.000Z"))
     elif isinstance(value, dict):
         for name, subvalue in value.iteritems():
             if not isinstance(subvalue, list):
                 subvalue = [subvalue]
             for subsubvalue in subvalue:
                 
                 parts.append('<' + escape.utf8(name) + '>')
                 self._render_parts(subsubvalue, parts)
                 parts.append('</' + escape.utf8(name) + '>')
     elif value == None:
         parts.append("")
     else:
         self._LOGGER.debug(parts)
         self._LOGGER.error("Unknown S3 value type %r", value)
         raise Exception("Unknown S3 value type %r", value)
Example #16
0
    def write_error(self, status_code, **kwargs):
        # TODO: Is there a tornado fn to get the full list?
        http_messages = {
            400: 'Bad Request',
            404: 'Not Found',
            408: 'Request Timeout',
            500: 'Internal Error'
        }

        result = {
            'status': 'error',
            'code': status_code,
            'message': http_messages.get(status_code, ''),
            'explanation': kwargs.get('explanation', '')
        }

        data = escape.json_encode(result)
        self.set_header('Content-Type', 'application/json; charset=UTF-8')

        if self.callback:
            self.set_header('Content-Type', 'application/javascript; charset=UTF-8')
            self._write_buffer.append(utf8('%s(%s)' % (self.callback, data)))
        else:
            self._write_buffer.append(utf8(data))
        self.finish()
Example #17
0
	def get_authenticated_user(self, callback, http_client=None):
		"""Gets the OAuth authorized user and access token.

		This method should be called from the handler for your
		OAuth callback URL to complete the registration process. We run the
		callback with the authenticated user dictionary.  This dictionary
		will contain an ``access_key`` which can be used to make authorized
		requests to this service on behalf of the user.  The dictionary will
		also contain other fields such as ``name``, depending on the service
		used.
		"""
		future = callback
		request_key = escape.utf8(self.get_argument("oauth_token"))
		oauth_verifier = self.get_argument("oauth_verifier", None)
		request_cookie = self.get_cookie("_oauth_request_token")
		if not request_cookie:
			future.set_exception(AuthError(
				"Missing OAuth request token cookie"))
			return
		self.clear_cookie("_oauth_request_token")
		cookie_key, cookie_secret = [base64.b64decode(escape.utf8(i)) for i in request_cookie.split("|")]
		if cookie_key != request_key:
			future.set_exception(AuthError(
				"Request token does not match cookie"))
			return
		token = dict(key=cookie_key, secret=cookie_secret)
		if oauth_verifier:
			token["verifier"] = oauth_verifier
		if http_client is None:
			http_client = self.get_auth_http_client()
		http_client.fetch(self._oauth_access_token_url(token),
						  self.async_callback(self._on_access_token, callback))
Example #18
0
    def get(self, path="."):
        download = self.get_argument("dl", None)
        if download:
            cwd = os.getcwd()
            try:
                os.chdir(utf8(path))
                self._download_dir_package(path, download)
            finally:
                os.chdir(cwd)
            return

        dirs, files = [], []
        path = utf8(path)
        for i in os.listdir(path):
            p = os.path.join(path, i)
            try:
                st = os.stat(p)
            except OSError:
                continue
            padding = " " * (30 - len(i))
            if os.path.isdir(p):
                dirs.append(
                    (i + "/", padding + "{:>15}".format(st.st_nlink))
                )
            else:
                files.append(
                    (i, padding + "{:>16}".format(humanize.naturalsize(st.st_size, gnu=True)))
                )
        lst = sorted(dirs) + sorted(files)
        self.render("dir.html", path=path, lst=lst)
Example #19
0
 def write_headers(self, start_line, headers, chunk=None, callback=None):
     """Implements `.HTTPConnection.write_headers`."""
     if self.is_client:
         self._request_start_line = start_line
         # Client requests with a non-empty body must have either a
         # Content-Length or a Transfer-Encoding.
         self._chunking_output = (
             start_line.method in ('POST', 'PUT', 'PATCH') and
             'Content-Length' not in headers and
             'Transfer-Encoding' not in headers)
     else:
         self._response_start_line = start_line
         self._chunking_output = (
             # TODO: should this use
             # self._request_start_line.version or
             # start_line.version?
             self._request_start_line.version == 'HTTP/1.1' and
             # 304 responses have no body (not even a zero-length body), and so
             # should not have either Content-Length or Transfer-Encoding.
             # headers.
             start_line.code != 304 and
             # No need to chunk the output if a Content-Length is specified.
             'Content-Length' not in headers and
             # Applications are discouraged from touching Transfer-Encoding,
             # but if they do, leave it alone.
             'Transfer-Encoding' not in headers)
         # If a 1.0 client asked for keep-alive, add the header.
         if (self._request_start_line.version == 'HTTP/1.0' and
             (self._request_headers.get('Connection', '').lower()
              == 'keep-alive')):
             headers['Connection'] = 'Keep-Alive'
     if self._chunking_output:
         headers['Transfer-Encoding'] = 'chunked'
     if (not self.is_client and
         (self._request_start_line.method == 'HEAD' or
          start_line.code == 304)):
         self._expected_content_remaining = 0
     elif 'Content-Length' in headers:
         self._expected_content_remaining = int(headers['Content-Length'])
     else:
         self._expected_content_remaining = None
     lines = [utf8("%s %s %s" % start_line)]
     lines.extend([utf8(n) + b": " + utf8(v) for n, v in headers.get_all()])
     for line in lines:
         if b'\n' in line:
             raise ValueError('Newline in header: ' + repr(line))
     if self.stream.closed():
         self._write_future = Future()
         self._write_future.set_exception(iostream.StreamClosedError())
     else:
         if callback is not None:
             self._write_callback = stack_context.wrap(callback)
         else:
             self._write_future = Future()
         data = b"\r\n".join(lines) + b"\r\n\r\n"
         if chunk:
             data += self._format_chunk(chunk)
         self._pending_write = self.stream.write(data)
         self._pending_write.add_done_callback(self._on_write_complete)
     return self._write_future
    def get(self):
        realm = 'test'
        opaque = 'asdf'
        # Real implementations would use a random nonce.
        nonce = "1234"

        auth_header = self.request.headers.get('Authorization', None)
        if auth_header is not None:
            auth_mode, params = auth_header.split(' ', 1)
            assert auth_mode == 'Digest'
            param_dict = {}
            for pair in params.split(','):
                k, v = pair.strip().split('=', 1)
                if v[0] == '"' and v[-1] == '"':
                    v = v[1:-1]
                param_dict[k] = v
            assert param_dict['realm'] == realm
            assert param_dict['opaque'] == opaque
            assert param_dict['nonce'] == nonce
            assert param_dict['username'] == self.username
            assert param_dict['uri'] == self.request.path
            h1 = md5(utf8('%s:%s:%s' % (self.username, realm, self.password))).hexdigest()
            h2 = md5(utf8('%s:%s' % (self.request.method,
                                     self.request.path))).hexdigest()
            digest = md5(utf8('%s:%s:%s' % (h1, nonce, h2))).hexdigest()
            if digest == param_dict['response']:
                self.write('ok')
            else:
                self.write('fail')
        else:
            self.set_status(401)
            self.set_header('WWW-Authenticate',
                            'Digest realm="%s", nonce="%s", opaque="%s"' %
                            (realm, nonce, opaque))
Example #21
0
    def prepare_request(cls, request, default_host):
        parsed = urlparse.urlsplit(_unicode(request.url))
        if request.method not in cls._SUPPORTED_METHODS and not request.allow_nonstandard_methods:
            raise KeyError("unknown method %s" % request.method)
        request.follow_redirects = False
        for key in (
            "network_interface",
            "proxy_host",
            "proxy_port",
            "proxy_username",
            "proxy_password",
            "expect_100_continue",
            "body_producer",
        ):
            if getattr(request, key, None):
                raise NotImplementedError("%s not supported" % key)

        request.headers.pop("Connection", None)
        if "Host" not in request.headers:
            if not parsed.netloc:
                request.headers["Host"] = default_host
            elif "@" in parsed.netloc:
                request.headers["Host"] = parsed.netloc.rpartition("@")[-1]
            else:
                request.headers["Host"] = parsed.netloc
        username, password = None, None
        if parsed.username is not None:
            username, password = parsed.username, parsed.password
        elif request.auth_username is not None:
            username = request.auth_username
            password = request.auth_password or ""
        if username is not None:
            if request.auth_mode not in (None, "basic"):
                raise ValueError("unsupported auth_mode %s", request.auth_mode)
            auth = utf8(username) + b":" + utf8(password)
            request.headers["Authorization"] = b"Basic " + base64.b64encode(auth)
        if request.user_agent:
            request.headers["User-Agent"] = request.user_agent
        if not request.allow_nonstandard_methods:
            # Some HTTP methods nearly always have bodies while others
            # almost never do. Fail in this case unless the user has
            # opted out of sanity checks with allow_nonstandard_methods.
            body_expected = request.method in ("POST", "PATCH", "PUT")
            body_present = request.body is not None or request.body_producer is not None
            if (body_expected and not body_present) or (body_present and not body_expected):
                raise ValueError(
                    "Body must %sbe None for method %s (unless "
                    "allow_nonstandard_methods is true)" % ("not " if body_expected else "", request.method)
                )
        if request.body is not None:
            # When body_producer is used the caller is responsible for
            # setting Content-Length (or else chunked encoding will be used).
            request.headers["Content-Length"] = str(len(request.body))
        if request.method == "POST" and "Content-Type" not in request.headers:
            request.headers["Content-Type"] = "application/x-www-form-urlencoded"
        if request.decompress_response:
            request.headers["Accept-Encoding"] = "gzip"

        request.url = (parsed.path or "/") + (("?" + parsed.query) if parsed.query else "")
        return request
Example #22
0
def _oauth10a_signature(
    consumer_token: Dict[str, Any],
    method: str,
    url: str,
    parameters: Dict[str, Any] = {},
    token: Dict[str, Any] = None,
) -> bytes:
    """Calculates the HMAC-SHA1 OAuth 1.0a signature for the given request.

    See http://oauth.net/core/1.0a/#signing_process
    """
    parts = urllib.parse.urlparse(url)
    scheme, netloc, path = parts[:3]
    normalized_url = scheme.lower() + "://" + netloc.lower() + path

    base_elems = []
    base_elems.append(method.upper())
    base_elems.append(normalized_url)
    base_elems.append(
        "&".join(
            "%s=%s" % (k, _oauth_escape(str(v))) for k, v in sorted(parameters.items())
        )
    )

    base_string = "&".join(_oauth_escape(e) for e in base_elems)
    key_elems = [escape.utf8(urllib.parse.quote(consumer_token["secret"], safe="~"))]
    key_elems.append(
        escape.utf8(urllib.parse.quote(token["secret"], safe="~") if token else "")
    )
    key = b"&".join(key_elems)

    hash = hmac.new(key, escape.utf8(base_string), hashlib.sha1)
    return binascii.b2a_base64(hash.digest())[:-1]
Example #23
0
def send_email(from_, to_, subject, body, html=None, attachments=[]):
    if isinstance(from_, EmailAddress):
        from_ = str(from_)
    else:
        from_ = utf8(from_)
    
    to = [utf8(t) for t in to_]
    
    if html:
        message = MIMEMultipart("alternative")
        message.attach(MIMEText(body, "plain"))
        message.attach(MIMEText(html, "html"))
    else:
        message = MIMEText(body)
        
    if attachments:
        part = message
        message = MIMEMultipart("mixed")
        message.attach(part)
        for filename, data in attachments:
            part = MIMEBase("application", "octet-stream")
            part.set_payload(data)
            encoders.encode_base64(part)
            part.add_header("Content-Disposition", "attachment", filename=filename)
            message.attach(part)
    
    message["Date"] = formatdate(time.time())
    message["From"] = from_
    message["To"] = COMMASPACE.join(to)
    message["Subject"] = utf8(subject)
Example #24
0
File: util.py Project: hhru/frontik
def any_to_bytes(s):
    if isinstance(s, str):
        return utf8(s)
    elif isinstance(s, bytes):
        return s

    return utf8(str(s))
Example #25
0
 def publish(self, channel, message, method=None):
     """
     Publish message into channel of stream.
     """
     method = method or DEFAULT_PUBLISH_METHOD
     to_publish = [utf8(channel), utf8(method), utf8(message)]
     self.pub_stream.send_multipart(to_publish)
 def _on_connect(self):
     self._remove_timeout()
     if self.request.request_timeout:
         self._timeout = self.io_loop.add_timeout(
             self.start_time + self.request.request_timeout,
             stack_context.wrap(self._on_timeout))
     if (self.request.method not in self._SUPPORTED_METHODS and
             not self.request.allow_nonstandard_methods):
         raise KeyError("unknown method %s" % self.request.method)
     for key in ('network_interface',
                 'proxy_host', 'proxy_port',
                 'proxy_username', 'proxy_password'):
         if getattr(self.request, key, None):
             raise NotImplementedError('%s not supported' % key)
     if "Connection" not in self.request.headers:
         self.request.headers["Connection"] = "close"
     if "Host" not in self.request.headers:
         if '@' in self.parsed.netloc:
             self.request.headers["Host"] = self.parsed.netloc.rpartition('@')[-1]
         else:
             self.request.headers["Host"] = self.parsed.netloc
     username, password = None, None
     if self.parsed.username is not None:
         username, password = self.parsed.username, self.parsed.password
     elif self.request.auth_username is not None:
         username = self.request.auth_username
         password = self.request.auth_password or ''
     if username is not None:
         auth = utf8(username) + b":" + utf8(password)
         self.request.headers["Authorization"] = (b"Basic " +
                                                  base64.b64encode(auth))
     if self.request.user_agent:
         self.request.headers["User-Agent"] = self.request.user_agent
     if not self.request.allow_nonstandard_methods:
         if self.request.method in ("POST", "PATCH", "PUT"):
             assert self.request.body is not None
         else:
             assert self.request.body is None
     if self.request.body is not None:
         self.request.headers["Content-Length"] = str(len(
             self.request.body))
     if (self.request.method == "POST" and
             "Content-Type" not in self.request.headers):
         self.request.headers["Content-Type"] = "application/x-www-form-urlencoded"
     if self.request.use_gzip:
         self.request.headers["Accept-Encoding"] = "gzip"
     req_path = ((self.parsed.path or '/') +
                (('?' + self.parsed.query) if self.parsed.query else ''))
     request_lines = [utf8("%s %s HTTP/1.1" % (self.request.method,
                                               req_path))]
     for k, v in self.request.headers.get_all():
         line = utf8(k) + b": " + utf8(v)
         if b'\n' in line:
             raise ValueError('Newline in header: ' + repr(line))
         request_lines.append(line)
     self.stream.write(b"\r\n".join(request_lines) + b"\r\n\r\n")
     if self.request.body is not None:
         self.stream.write(self.request.body)
     self.stream.read_until_regex(b"\r?\n\r?\n", self._on_headers)
Example #27
0
File: ksdb.py Project: kzahel/ksdb
 def make_request_headers(self):
     req_path = "/"
     request_lines = [utf8("%s %s HTTP/1.1" % (self.method, req_path))]
     for k, v in self.headers.items():
         line = utf8(k) + b(": ") + utf8(v)
         request_lines.append(line)
     toreturn = b("\r\n").join(request_lines) + b("\r\n\r\n")
     return toreturn
Example #28
0
 def send_status(self,code,message):
     print('status',code,message)
     self.code = code
     self.message = message
     self.status_sent = True
     return self.stream.write(b'HTTP/1.1 '+
             utf8(denumber(code))+
             b' '+utf8(message)+b'\r\n')
Example #29
0
 def test_not_implemented_command(self):
     self.connect()
     for command in ['BADCOMMAND', 'unknown', 'Bonzo']:
         self.stream.write(utf8('%s\r\n' % command))
         data = self.read_response()
         self.assertEqual(data, utf8('502 Error: command "%s" not '
                                     'implemented\r\n' % command))
     self.close()
Example #30
0
 def generate_headers(self):
     request_lines = [utf8("%s %s HTTP/1.1" % (self.method,
                                               self.uri))]
     for k, v in self.headers.items():
         line = utf8(k) + b(": ") + utf8(v)
         request_lines.append(line)
     toreturn = b("\r\n").join(request_lines) + b("\r\n\r\n")
     return toreturn
Example #31
0
 def test_unicode_template(self):
     template = Template(utf8(u"\u00e9"))
     self.assertEqual(template.generate(), utf8(u"\u00e9"))
Example #32
0
    def test_apply(self):
        def upper(s):
            return s.upper()

        template = Template(utf8("{% apply upper %}foo{% end %}"))
        self.assertEqual(template.generate(upper=upper), b("FOO"))
Example #33
0
 def test_body_setter(self):
     request = HTTPRequest('http://example.com')
     request.body = 'foo'
     self.assertEqual(request.body, utf8('foo'))
 def _finish_with_json(self, callback):
     self.log.debug('finishing without templating')
     if self.handler._headers.get('Content-Type') is None:
         self.handler.set_header('Content-Type',
                                 'application/json; charset=utf-8')
     callback(utf8(self.json.to_string()))
Example #35
0
def _curl_setup_request(curl, request, buffer, headers):
    curl.setopt(pycurl.URL, request.url)
    # Request headers may be either a regular dict or HTTPHeaders object
    if isinstance(request.headers, httputil.HTTPHeaders):
        curl.setopt(pycurl.HTTPHEADER,
                    [_utf8("%s: %s" % i) for i in request.headers.get_all()])
    else:
        curl.setopt(pycurl.HTTPHEADER,
                    [_utf8("%s: %s" % i) for i in request.headers.iteritems()])
    if request.header_callback:
        curl.setopt(pycurl.HEADERFUNCTION, request.header_callback)
    else:
        curl.setopt(pycurl.HEADERFUNCTION,
                    lambda line: _curl_header_callback(headers, line))
    if request.streaming_callback:
        curl.setopt(pycurl.WRITEFUNCTION, request.streaming_callback)
    else:
        curl.setopt(pycurl.WRITEFUNCTION, buffer.write)
    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)
    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)
    curl.setopt(pycurl.CONNECTTIMEOUT, int(request.connect_timeout))
    curl.setopt(pycurl.TIMEOUT, int(request.request_timeout))
    if request.user_agent:
        curl.setopt(pycurl.USERAGENT, _utf8(request.user_agent))
    else:
        curl.setopt(pycurl.USERAGENT, "Mozilla/5.0 (compatible; pycurl)")
    if request.network_interface:
        curl.setopt(pycurl.INTERFACE, request.network_interface)
    if request.use_gzip:
        curl.setopt(pycurl.ENCODING, "gzip,deflate")
    else:
        curl.setopt(pycurl.ENCODING, "none")
    if request.proxy_host and request.proxy_port:
        curl.setopt(pycurl.PROXY, request.proxy_host)
        curl.setopt(pycurl.PROXYPORT, request.proxy_port)
        if request.proxy_username:
            credentials = '%s:%s' % (request.proxy_username,
                    request.proxy_password)
            curl.setopt(pycurl.PROXYUSERPWD, credentials)
    else:
        curl.setopt(pycurl.PROXY, '')

    # Set the request method through curl's retarded interface which makes
    # up names for almost every single method
    curl_options = {
        "GET": pycurl.HTTPGET,
        "POST": pycurl.POST,
        "PUT": pycurl.UPLOAD,
        "HEAD": pycurl.NOBODY,
    }
    custom_methods = set(["DELETE"])
    for o in curl_options.values():
        curl.setopt(o, False)
    if request.method in curl_options:
        curl.unsetopt(pycurl.CUSTOMREQUEST)
        curl.setopt(curl_options[request.method], True)
    elif request.allow_nonstandard_methods or request.method in custom_methods:
        curl.setopt(pycurl.CUSTOMREQUEST, request.method)
    else:
        raise KeyError('unknown method ' + request.method)

    # Handle curl's cryptic options for every individual HTTP method
    if request.method in ("POST", "PUT"):
        request_buffer =  cStringIO.StringIO(escape.utf8(request.body))
        curl.setopt(pycurl.READFUNCTION, request_buffer.read)
        if request.method == "POST":
            def ioctl(cmd):
                if cmd == curl.IOCMD_RESTARTREAD:
                    request_buffer.seek(0)
            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)
            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))
        else:
            curl.setopt(pycurl.INFILESIZE, len(request.body))

    if request.auth_username and request.auth_password:
        userpwd = "%s:%s" % (request.auth_username, request.auth_password)
        curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
        curl.setopt(pycurl.USERPWD, userpwd)
        logging.info("%s %s (username: %r)", request.method, request.url,
                     request.auth_username)
    else:
        curl.unsetopt(pycurl.USERPWD)
        logging.info("%s %s", request.method, request.url)
    if threading.activeCount() > 1:
        # libcurl/pycurl is not thread-safe by default.  When multiple threads
        # are used, signals should be disabled.  This has the side effect
        # of disabling DNS timeouts in some environments (when libcurl is
        # not linked against ares), so we don't do it when there is only one
        # thread.  Applications that use many short-lived threads may need
        # to set NOSIGNAL manually in a prepare_curl_callback since
        # there may not be any other threads running at the time we call
        # threading.activeCount.
        curl.setopt(pycurl.NOSIGNAL, 1)
    if request.prepare_curl_callback is not None:
        request.prepare_curl_callback(curl)
Example #36
0
 def _on_connect(self, stream):
     if self.final_callback is None:
         # final_callback is cleared if we've hit our timeout.
         stream.close()
         return
     self.stream = stream
     self.stream.set_close_callback(self.on_connection_close)
     self._remove_timeout()
     if self.final_callback is None:
         return
     if self.request.request_timeout:
         self._timeout = self.io_loop.add_timeout(
             self.start_time + self.request.request_timeout,
             stack_context.wrap(self._on_timeout))
     if (self.request.method not in self._SUPPORTED_METHODS
             and not self.request.allow_nonstandard_methods):
         raise KeyError("unknown method %s" % self.request.method)
     for key in ('network_interface', 'proxy_host', 'proxy_port',
                 'proxy_username', 'proxy_password'):
         if getattr(self.request, key, None):
             raise NotImplementedError('%s not supported' % key)
     if "Connection" not in self.request.headers:
         self.request.headers["Connection"] = "close"
     if "Host" not in self.request.headers:
         if '@' in self.parsed.netloc:
             self.request.headers["Host"] = self.parsed.netloc.rpartition(
                 '@')[-1]
         else:
             self.request.headers["Host"] = self.parsed.netloc
     username, password = None, None
     if self.parsed.username is not None:
         username, password = self.parsed.username, self.parsed.password
     elif self.request.auth_username is not None:
         username = self.request.auth_username
         password = self.request.auth_password or ''
     if username is not None:
         if self.request.auth_mode not in (None, "basic"):
             raise ValueError("unsupported auth_mode %s",
                              self.request.auth_mode)
         auth = utf8(username) + b":" + utf8(password)
         self.request.headers["Authorization"] = (b"Basic " +
                                                  base64.b64encode(auth))
     if self.request.user_agent:
         self.request.headers["User-Agent"] = self.request.user_agent
     if not self.request.allow_nonstandard_methods:
         # Some HTTP methods nearly always have bodies while others
         # almost never do. Fail in this case unless the user has
         # opted out of sanity checks with allow_nonstandard_methods.
         body_expected = self.request.method in ("POST", "PATCH", "PUT")
         body_present = (self.request.body is not None
                         or self.request.body_producer is not None)
         if ((body_expected and not body_present)
                 or (body_present and not body_expected)):
             raise ValueError(
                 'Body must %sbe None for method %s (unelss '
                 'allow_nonstandard_methods is true)' %
                 ('not ' if body_expected else '', self.request.method))
     if self.request.expect_100_continue:
         self.request.headers["Expect"] = "100-continue"
     if self.request.body is not None:
         # When body_producer is used the caller is responsible for
         # setting Content-Length (or else chunked encoding will be used).
         self.request.headers["Content-Length"] = str(len(
             self.request.body))
     if (self.request.method == "POST"
             and "Content-Type" not in self.request.headers):
         self.request.headers[
             "Content-Type"] = "application/x-www-form-urlencoded"
     if self.request.decompress_response:
         self.request.headers["Accept-Encoding"] = "gzip"
     req_path = ((self.parsed.path or '/') +
                 (('?' + self.parsed.query) if self.parsed.query else ''))
     self.stream.set_nodelay(True)
     self.connection = HTTP1Connection(
         self.stream, True,
         HTTP1ConnectionParameters(
             no_keep_alive=True,
             max_header_size=self.max_header_size,
             decompress=self.request.decompress_response), self._sockaddr)
     start_line = httputil.RequestStartLine(self.request.method, req_path,
                                            'HTTP/1.1')
     self.connection.write_headers(start_line, self.request.headers)
     if self.request.expect_100_continue:
         self._read_response()
     else:
         self._write_body(True)
Example #37
0
 def test_comment_directive(self):
     template = Template(utf8("{% comment blah blah %}foo"))
     self.assertEqual(template.generate(), b("foo"))
 def write_headers(self, start_line, headers, chunk=None, callback=None):
     """Implements `.HTTPConnection.write_headers`."""
     if self.is_client:
         self._request_start_line = start_line
         # Client requests with a non-empty body must have either a
         # Content-Length or a Transfer-Encoding.
         # 不检查是否 Http/1.0 是不完备的。
         self._chunking_output = (
             start_line.method in ('POST', 'PUT', 'PATCH') and
             'Content-Length' not in headers and
             'Transfer-Encoding' not in headers)
     else:
         self._response_start_line = start_line
         # 对于 HTTP/1.0 ``self._chunking_output=False``,不支持分块传输编码。
         self._chunking_output = (
             # TODO: should this use
             # self._request_start_line.version or
             # start_line.version?
             self._request_start_line.version == 'HTTP/1.1' and
             # 304 responses have no body (not even a zero-length body), and so
             # should not have either Content-Length or Transfer-Encoding.
             # headers.
             start_line.code != 304 and
             # No need to chunk the output if a Content-Length is specified.
             'Content-Length' not in headers and
             # Applications are discouraged from touching Transfer-Encoding,
             # but if they do, leave it alone.
             'Transfer-Encoding' not in headers)
         # If a 1.0 client asked for keep-alive, add the header.
         # HTTP/1.1 默认就是持久化连接,不需要单独指定。
         # 假设客户端请求使用 HTTP/1.0 和 `Connection:Keep-Alive`,服务端响应时没有指定
         # `Content-Length` (比如在 handler 中多次调用 flush 方法),那么响应数据就无法
         # 判断边界,代码中应该对这个条件做特别处理。
         if (self._request_start_line.version == 'HTTP/1.0' and
             (self._request_headers.get('Connection', '').lower()
              == 'keep-alive')):
             headers['Connection'] = 'Keep-Alive'
     if self._chunking_output:
         headers['Transfer-Encoding'] = 'chunked'
     # 服务端响应 `HEAD` 或者 304 时不需要 body 数据。
     if (not self.is_client and
         (self._request_start_line.method == 'HEAD' or
          start_line.code == 304)):
         self._expected_content_remaining = 0
     elif 'Content-Length' in headers:
         self._expected_content_remaining = int(headers['Content-Length'])
     else:
         self._expected_content_remaining = None
     lines = [utf8("%s %s %s" % start_line)]
     # 通过 add 添加的响应头会输出多个,比如:“Set-Cookie” 响应头。
     lines.extend([utf8(n) + b": " + utf8(v) for n, v in headers.get_all()])
     for line in lines:
         if b'\n' in line:
             raise ValueError('Newline in header: ' + repr(line))
     future = None
     if self.stream.closed():
         future = self._write_future = Future()
         future.set_exception(iostream.StreamClosedError())
     else:
         # "写回调" 是一个实例字段 `_write_callback`,当上一次写操作还没有回调时就再次执行
         # 写操作,那么上一次写操作的回调将被放弃(callback is not None)
         if callback is not None:
             self._write_callback = stack_context.wrap(callback)
         else:
             # 没有 callback 时,返回 Future(self._write_future)
             future = self._write_future = Future()
         # Headers
         data = b"\r\n".join(lines) + b"\r\n\r\n"
         # message-body
         if chunk:
             data += self._format_chunk(chunk)
         self._pending_write = self.stream.write(data)
         self._pending_write.add_done_callback(self._on_write_complete)
     return future
Example #39
0
 def __str__(self):
     return '%s <%s>' % (utf8(self.name), utf8(self.addr))
Example #40
0
    def _get_path(self, path, multipart=False):
        # If the file doesn't exist, raise a 404: Not Found
        try:
            path = path.resolve()
        except OSError:
            raise HTTPError(status_code=NOT_FOUND)

        self.path = path
        if self.path.is_dir():
            self.file = self.path / self.default_filename if self.default_filename else self.path
            if not (self.default_filename
                    and self.file.exists()) and not self.index:
                raise HTTPError(status_code=NOT_FOUND)
            # Ensure URL has a trailing '/' when displaying the index / default file
            if not self.request.path.endswith('/'):
                self.redirect(self.request.path + '/', permanent=True)
                return
        else:
            self.file = self.path
            if not self.file.exists():
                raise HTTPError(status_code=NOT_FOUND)
            elif not self.file.is_file():
                raise HTTPError(status_code=FORBIDDEN,
                                log_message='%s is not a file' % self.path)

        if not self.allowed(self.file):
            raise HTTPError(status_code=FORBIDDEN)

        if self.path.is_dir() and self.index and not (self.default_filename
                                                      and self.file.exists()):
            self.set_header('Content-Type', 'text/html; charset=UTF-8')
            content = []
            file_template = string.Template(
                u'<li><a href="$path">$name</a></li>')
            for path in self.path.iterdir():
                if path.is_symlink():
                    name_suffix, path_suffix = ' &#x25ba;', ''
                elif path.is_dir():
                    name_suffix = path_suffix = '/'
                else:
                    name_suffix = path_suffix = ''
                # On Windows, pathlib on Python 2.7 won't handle Unicode. Ignore such files.
                # https://bitbucket.org/pitrou/pathlib/issues/25
                try:
                    path = str(path.relative_to(self.path))
                    content.append(
                        file_template.substitute(
                            path=path + path_suffix,
                            name=path + name_suffix,
                        ))
                except UnicodeDecodeError:
                    app_log.warning(
                        "FileHandler can't show unicode file {!r:s}".format(
                            path))
            content.append(u'</ul>')
            self.content = self.index_template.substitute(
                path=self.path, body=''.join(content))

        else:
            modified = self.file.stat().st_mtime
            self.set_header('Last-Modified',
                            datetime.datetime.utcfromtimestamp(modified))

            mime_type = mimetypes.types_map.get(self.file.suffix)
            if mime_type is not None:
                if mime_type.startswith('text/'):
                    mime_type += '; charset=UTF-8'
                self.set_header('Content-Type', mime_type)

            for header_name, header_value in self.headers.items():
                if isinstance(header_value, dict):
                    if _match(self.file, header_name):
                        for header_name, header_value in header_value.items():
                            self.set_header(header_name, header_value)
                else:
                    self.set_header(header_name, header_value)

            transform = {}
            for pattern, trans in self.transform.items():
                if _match(self.file, pattern):
                    transform = trans
                    break

            encoding = transform.get('encoding')
            with self.file.open('rb' if encoding is None else 'r',
                                encoding=encoding) as file:
                self.content = file.read()
                if transform:
                    for header_name, header_value in transform[
                            'headers'].items():
                        self.set_header(header_name, header_value)

                    output = []
                    for item in transform['function'](content=self.content,
                                                      handler=self):
                        if tornado.concurrent.is_future(item):
                            item = yield item
                        output.append(item)
                    self.content = ''.join(output)
                self.set_header('Content-Length', len(utf8(self.content)))

        if self.include_body:
            self.write(self.content)
            # Do not flush unless it's multipart. Flushing disables Etag
            if multipart:
                self.flush()
Example #41
0
 def _trigger_include_host_check(self, include_host):
     path = "/override_static_url/robots.txt?include_host=%s"
     response = self.fetch(path % int(include_host))
     self.assertEqual(response.body, utf8(str(True)))
Example #42
0
 def __init__(self,
              url,
              method="GET",
              headers=None,
              body=None,
              auth_username=None,
              auth_password=None,
              connect_timeout=20.0,
              request_timeout=20.0,
              if_modified_since=None,
              follow_redirects=True,
              max_redirects=5,
              user_agent=None,
              use_gzip=True,
              network_interface=None,
              streaming_callback=None,
              header_callback=None,
              prepare_curl_callback=None,
              proxy_host=None,
              proxy_port=None,
              proxy_username=None,
              proxy_password='',
              allow_nonstandard_methods=False,
              validate_cert=True,
              ca_certs=None):
     if headers is None:
         headers = httputil.HTTPHeaders()
     if if_modified_since:
         timestamp = calendar.timegm(if_modified_since.utctimetuple())
         headers["If-Modified-Since"] = email.utils.formatdate(
             timestamp, localtime=False, usegmt=True)
     # Proxy support: proxy_host and proxy_port must be set to connect via
     # proxy.  The username and password credentials are optional.
     self.proxy_host = proxy_host
     self.proxy_port = proxy_port
     self.proxy_username = proxy_username
     self.proxy_password = proxy_password
     self.url = utf8(url)
     self.method = method
     self.headers = headers
     self.body = body
     self.auth_username = utf8(auth_username)
     self.auth_password = utf8(auth_password)
     self.connect_timeout = connect_timeout
     self.request_timeout = request_timeout
     self.follow_redirects = follow_redirects
     self.max_redirects = max_redirects
     self.user_agent = user_agent
     self.use_gzip = use_gzip
     self.network_interface = network_interface
     self.streaming_callback = streaming_callback
     self.header_callback = header_callback
     self.prepare_curl_callback = prepare_curl_callback
     self.allow_nonstandard_methods = allow_nonstandard_methods
     # SSL certificate validation:
     # validate_cert: boolean, set to False to disable validation
     # ca_certs: filename of CA certificates in PEM format, or
     #     None to use defaults
     # Note that in the curl-based HTTP client, if any request
     # uses a custom ca_certs file, they all must (they don't have to
     # all use the same ca_certs, but it's not possible to mix requests
     # with ca_certs and requests that use the defaults).
     # SimpleAsyncHTTPClient does not have this limitation.
     self.validate_cert = validate_cert
     self.ca_certs = ca_certs
     self.start_time = time.time()
Example #43
0
 def test_comment(self):
     template = Template("Hello{# TODO i18n #} {{ name }}!")
     self.assertEqual(template.generate(name=utf8("Ben")), b("Hello Ben!"))
Example #44
0
 def body(self, value: Union[bytes, str]) -> None:
     self._body = utf8(value)
Example #45
0
 def test_bytes(self):
     template = Template("Hello {{ name }}!")
     self.assertEqual(template.generate(name=utf8("Ben")), b("Hello Ben!"))
Example #46
0
    def flush_messages(self):
        # type: () -> bool
        if self.client_closed or len(self.message_queue) == 0:
            return False
        msg = ("{\"msgs\":[" + ",".join(self.message_queue) + "]}")
        self.message_queue = []

        try:
            binmsg = utf8(msg)
            self.total_message_bytes += len(binmsg)
            if self.deflate:
                # Compress like in deflate-frame extension:
                # Apply deflate, flush, then remove the 00 00 FF FF
                # at the end
                compressed = self._compressobj.compress(binmsg)
                compressed += self._compressobj.flush(zlib.Z_SYNC_FLUSH)
                compressed = compressed[:-4]
                self.compressed_bytes_sent += len(compressed)
                f = self.write_message(compressed, binary=True)
            else:
                self.uncompressed_bytes_sent += len(binmsg)
                f = self.write_message(binmsg)

            import traceback
            cur_stack = traceback.format_stack()

            # handle any exceptions lingering in the Future
            # TODO: this whole call chain should be converted to use coroutines
            def after_write_callback(f):
                try:
                    f.result()
                except tornado.websocket.StreamClosedError as e:
                    # not supposed to be raised here in current versions of
                    # tornado, but in some older versions it is.
                    if self.failed_messages == 0:
                        self.logger.warning(
                            "Connection closed during async write_message")
                    self.failed_messages += 1
                    if self.ws_connection is not None:
                        self.ws_connection._abort()
                except tornado.websocket.WebSocketClosedError as e:
                    if self.failed_messages == 0:
                        self.logger.warning(
                            "Connection closed during async write_message")
                    self.failed_messages += 1
                    if self.ws_connection is not None:
                        self.ws_connection._abort()
                except Exception as e:
                    self.logger.warning(
                        "Exception during async write_message, stack at call:")
                    self.logger.warning("".join(cur_stack))
                    self.logger.warning(e, exc_info=True)
                    self.failed_messages += 1
                    if self.ws_connection is not None:
                        self.ws_connection._abort()

            # extreme back-compat try-except block, `f` should be None in
            # ancient tornado versions
            try:
                f.add_done_callback(after_write_callback)
            except:
                pass
            # true means that something was queued up to send, but it may be
            # async
            return True
        except:
            self.logger.warning("Exception trying to send message.",
                                exc_info=True)
            self.failed_messages += 1
            if self.ws_connection is not None:
                self.ws_connection._abort()
            return False
Example #47
0
 def _on_connect(self):
     self._remove_timeout()
     if self.request.request_timeout:
         self._timeout = self.io_loop.add_timeout(
             self.start_time + self.request.request_timeout,
             stack_context.wrap(self._on_timeout))
     if (self.request.method not in self._SUPPORTED_METHODS and
             not self.request.allow_nonstandard_methods):
         raise KeyError("unknown method %s" % self.request.method)
     for key in ('network_interface',
                 'proxy_host', 'proxy_port',
                 'proxy_username', 'proxy_password'):
         if getattr(self.request, key, None):
             raise NotImplementedError('%s not supported' % key)
     if "Connection" not in self.request.headers:
         self.request.headers["Connection"] = "close"
     if "Host" not in self.request.headers:
         if '@' in self.parsed.netloc:
             self.request.headers["Host"] = self.parsed.netloc.rpartition('@')[-1]
         else:
             self.request.headers["Host"] = self.parsed.netloc
     username, password = None, None
     if self.parsed.username is not None:
         username, password = self.parsed.username, self.parsed.password
     elif self.request.auth_username is not None:
         username = self.request.auth_username
         password = self.request.auth_password or ''
     if username is not None:
         if self.request.auth_mode not in (None, "basic"):
             raise ValueError("unsupported auth_mode %s",
                              self.request.auth_mode)
         auth = utf8(username) + b":" + utf8(password)
         self.request.headers["Authorization"] = (b"Basic " +
                                                  base64.b64encode(auth))
     if self.request.user_agent:
         self.request.headers["User-Agent"] = self.request.user_agent
     if not self.request.allow_nonstandard_methods:
         if self.request.method in ("POST", "PATCH", "PUT"):
             assert self.request.body is not None
         else:
             assert self.request.body is None
     if self.request.body is not None:
         self.request.headers["Content-Length"] = str(len(
             self.request.body))
     if (self.request.method == "POST" and
             "Content-Type" not in self.request.headers):
         self.request.headers["Content-Type"] = "application/x-www-form-urlencoded"
     if self.request.use_gzip:
         self.request.headers["Accept-Encoding"] = "gzip"
     req_path = ((self.parsed.path or '/') +
                (('?' + self.parsed.query) if self.parsed.query else ''))
     request_lines = [utf8("%s %s HTTP/1.1" % (self.request.method,
                                               req_path))]
     for k, v in self.request.headers.get_all():
         line = utf8(k) + b": " + utf8(v)
         if b'\n' in line:
             raise ValueError('Newline in header: ' + repr(line))
         request_lines.append(line)
     request_str = b"\r\n".join(request_lines) + b"\r\n\r\n"
     if self.request.body is not None:
         request_str += self.request.body
     self.stream.set_nodelay(True)
     self.stream.write(request_str)
     self.stream.read_until_regex(b"\r?\n\r?\n", self._on_headers)
Example #48
0
 def body(self, value):
     self._body = utf8(value)
Example #49
0
 def test_absolute_static_url(self):
     response = self.fetch("/abs_static_url/robots.txt")
     self.assertEqual(response.body,
                      utf8(self.get_url("/") + "static/robots.txt?v=f71d2"))
Example #50
0
 def write_response(self):
     yield self.stream.write(
         utf8("HTTP/1.0 200 OK\r\nContent-Length: %s\r\n\r\nok" %
              self.get_argument("value")))
     self.stream.close()
Example #51
0
 def test_unicode_escapes(self):
     self.assertEqual(utf8(u'\u00e9'), b'\xc3\xa9')
def _curl_setup_request(curl, request, buffer, headers):
    curl.setopt(pycurl.URL, native_str(request.url))

    # libcurl's magic "Expect: 100-continue" behavior causes delays
    # with servers that don't support it (which include, among others,
    # Google's OpenID endpoint).  Additionally, this behavior has
    # a bug in conjunction with the curl_multi_socket_action API
    # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),
    # which increases the delays.  It's more trouble than it's worth,
    # so just turn off the feature (yes, setting Expect: to an empty
    # value is the official way to disable this)
    if "Expect" not in request.headers:
        request.headers["Expect"] = ""

    # libcurl adds Pragma: no-cache by default; disable that too
    if "Pragma" not in request.headers:
        request.headers["Pragma"] = ""

    # Request headers may be either a regular dict or HTTPHeaders object
    if isinstance(request.headers, httputil.HTTPHeaders):
        curl.setopt(
            pycurl.HTTPHEADER,
            [native_str("%s: %s" % i) for i in request.headers.get_all()])
    else:
        curl.setopt(
            pycurl.HTTPHEADER,
            [native_str("%s: %s" % i) for i in request.headers.items()])

    if request.header_callback:
        curl.setopt(pycurl.HEADERFUNCTION,
                    lambda line: request.header_callback(native_str(line)))
    else:
        curl.setopt(
            pycurl.HEADERFUNCTION,
            lambda line: _curl_header_callback(headers, native_str(line)))
    if request.streaming_callback:
        write_function = request.streaming_callback
    else:
        write_function = buffer.write
    if bytes_type is str:  # py2
        curl.setopt(pycurl.WRITEFUNCTION, write_function)
    else:  # py3
        # Upstream pycurl doesn't support py3, but ubuntu 12.10 includes
        # a fork/port.  That version has a bug in which it passes unicode
        # strings instead of bytes to the WRITEFUNCTION.  This means that
        # if you use a WRITEFUNCTION (which tornado always does), you cannot
        # download arbitrary binary data.  This needs to be fixed in the
        # ported pycurl package, but in the meantime this lambda will
        # make it work for downloading (utf8) text.
        curl.setopt(pycurl.WRITEFUNCTION, lambda s: write_function(utf8(s)))
    curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)
    curl.setopt(pycurl.MAXREDIRS, request.max_redirects)
    curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))
    curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))
    if request.user_agent:
        curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))
    else:
        curl.setopt(pycurl.USERAGENT, "Mozilla/5.0 (compatible; pycurl)")
    if request.network_interface:
        curl.setopt(pycurl.INTERFACE, request.network_interface)
    if request.use_gzip:
        curl.setopt(pycurl.ENCODING, "gzip,deflate")
    else:
        curl.setopt(pycurl.ENCODING, "none")
    if request.proxy_host and request.proxy_port:
        curl.setopt(pycurl.PROXY, request.proxy_host)
        curl.setopt(pycurl.PROXYPORT, request.proxy_port)
        if request.proxy_username:
            credentials = '%s:%s' % (request.proxy_username,
                                     request.proxy_password)
            curl.setopt(pycurl.PROXYUSERPWD, credentials)
    else:
        curl.setopt(pycurl.PROXY, '')
        curl.unsetopt(pycurl.PROXYUSERPWD)
    if request.validate_cert:
        curl.setopt(pycurl.SSL_VERIFYPEER, 1)
        curl.setopt(pycurl.SSL_VERIFYHOST, 2)
    else:
        curl.setopt(pycurl.SSL_VERIFYPEER, 0)
        curl.setopt(pycurl.SSL_VERIFYHOST, 0)
    if request.ca_certs is not None:
        curl.setopt(pycurl.CAINFO, request.ca_certs)
    else:
        # There is no way to restore pycurl.CAINFO to its default value
        # (Using unsetopt makes it reject all certificates).
        # I don't see any way to read the default value from python so it
        # can be restored later.  We'll have to just leave CAINFO untouched
        # if no ca_certs file was specified, and require that if any
        # request uses a custom ca_certs file, they all must.
        pass

    if request.allow_ipv6 is False:
        # Curl behaves reasonably when DNS resolution gives an ipv6 address
        # that we can't reach, so allow ipv6 unless the user asks to disable.
        # (but see version check in _process_queue above)
        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
    else:
        curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)

    # Set the request method through curl's irritating interface which makes
    # up names for almost every single method
    curl_options = {
        "GET": pycurl.HTTPGET,
        "POST": pycurl.POST,
        "PUT": pycurl.UPLOAD,
        "HEAD": pycurl.NOBODY,
    }
    custom_methods = set(["DELETE", "OPTIONS", "PATCH"])
    for o in curl_options.values():
        curl.setopt(o, False)
    if request.method in curl_options:
        curl.unsetopt(pycurl.CUSTOMREQUEST)
        curl.setopt(curl_options[request.method], True)
    elif request.allow_nonstandard_methods or request.method in custom_methods:
        curl.setopt(pycurl.CUSTOMREQUEST, request.method)
    else:
        raise KeyError('unknown method ' + request.method)

    # Handle curl's cryptic options for every individual HTTP method
    if request.method in ("POST", "PUT"):
        if request.body is None:
            raise AssertionError('Body must not be empty for "%s" request' %
                                 request.method)

        request_buffer = BytesIO(utf8(request.body))
        curl.setopt(pycurl.READFUNCTION, request_buffer.read)
        if request.method == "POST":

            def ioctl(cmd):
                if cmd == curl.IOCMD_RESTARTREAD:
                    request_buffer.seek(0)

            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)
            curl.setopt(pycurl.POSTFIELDSIZE, len(request.body))
        else:
            curl.setopt(pycurl.INFILESIZE, len(request.body))
    elif request.method == "GET":
        if request.body is not None:
            raise AssertionError('Body must be empty for GET request')

    if request.auth_username is not None:
        userpwd = "%s:%s" % (request.auth_username, request.auth_password
                             or '')

        if request.auth_mode is None or request.auth_mode == "basic":
            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
        elif request.auth_mode == "digest":
            curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)
        else:
            raise ValueError("Unsupported auth_mode %s" % request.auth_mode)

        curl.setopt(pycurl.USERPWD, native_str(userpwd))
        gen_log.debug("%s %s (username: %r)", request.method, request.url,
                      request.auth_username)
    else:
        curl.unsetopt(pycurl.USERPWD)
        gen_log.debug("%s %s", request.method, request.url)

    if request.client_cert is not None:
        curl.setopt(pycurl.SSLCERT, request.client_cert)

    if request.client_key is not None:
        curl.setopt(pycurl.SSLKEY, request.client_key)

    if threading.activeCount() > 1:
        # libcurl/pycurl is not thread-safe by default.  When multiple threads
        # are used, signals should be disabled.  This has the side effect
        # of disabling DNS timeouts in some environments (when libcurl is
        # not linked against ares), so we don't do it when there is only one
        # thread.  Applications that use many short-lived threads may need
        # to set NOSIGNAL manually in a prepare_curl_callback since
        # there may not be any other threads running at the time we call
        # threading.activeCount.
        curl.setopt(pycurl.NOSIGNAL, 1)
    if request.prepare_curl_callback is not None:
        request.prepare_curl_callback(curl)
Example #53
0
    def __init__(self,
                 url,
                 method="GET",
                 headers=None,
                 body=None,
                 auth_username=None,
                 auth_password=None,
                 connect_timeout=20.0,
                 request_timeout=20.0,
                 if_modified_since=None,
                 follow_redirects=True,
                 max_redirects=5,
                 user_agent=None,
                 use_gzip=True,
                 network_interface=None,
                 streaming_callback=None,
                 header_callback=None,
                 prepare_curl_callback=None,
                 proxy_host=None,
                 proxy_port=None,
                 proxy_username=None,
                 proxy_password='',
                 allow_nonstandard_methods=False,
                 validate_cert=True,
                 ca_certs=None,
                 allow_ipv6=None,
                 client_key=None,
                 client_cert=None):
        """Creates an `HTTPRequest`.

        All parameters except `url` are optional.

        :arg string url: URL to fetch
        :arg string method: HTTP method, e.g. "GET" or "POST"
        :arg headers: Additional HTTP headers to pass on the request
        :type headers: `~tornado.httputil.HTTPHeaders` or `dict`
        :arg string auth_username: Username for HTTP "Basic" authentication
        :arg string auth_password: Password for HTTP "Basic" authentication
        :arg float connect_timeout: Timeout for initial connection in seconds
        :arg float request_timeout: Timeout for entire request in seconds
        :arg datetime if_modified_since: Timestamp for ``If-Modified-Since``
           header
        :arg bool follow_redirects: Should redirects be followed automatically
           or return the 3xx response?
        :arg int max_redirects: Limit for `follow_redirects`
        :arg string user_agent: String to send as ``User-Agent`` header
        :arg bool use_gzip: Request gzip encoding from the server
        :arg string network_interface: Network interface to use for request
        :arg callable streaming_callback: If set, `streaming_callback` will
           be run with each chunk of data as it is received, and
           `~HTTPResponse.body` and `~HTTPResponse.buffer` will be empty in
           the final response.
        :arg callable header_callback: If set, `header_callback` will
           be run with each header line as it is received, and
           `~HTTPResponse.headers` will be empty in the final response.
        :arg callable prepare_curl_callback: If set, will be called with
           a `pycurl.Curl` object to allow the application to make additional
           `setopt` calls.
        :arg string proxy_host: HTTP proxy hostname.  To use proxies,
           `proxy_host` and `proxy_port` must be set; `proxy_username` and
           `proxy_pass` are optional.  Proxies are currently only support
           with `curl_httpclient`.
        :arg int proxy_port: HTTP proxy port
        :arg string proxy_username: HTTP proxy username
        :arg string proxy_password: HTTP proxy password
        :arg bool allow_nonstandard_methods: Allow unknown values for `method`
           argument?
        :arg bool validate_cert: For HTTPS requests, validate the server's
           certificate?
        :arg string ca_certs: filename of CA certificates in PEM format,
           or None to use defaults.  Note that in `curl_httpclient`, if
           any request uses a custom `ca_certs` file, they all must (they
           don't have to all use the same `ca_certs`, but it's not possible
           to mix requests with ca_certs and requests that use the defaults.
        :arg bool allow_ipv6: Use IPv6 when available?  Default is false in
           `simple_httpclient` and true in `curl_httpclient`
        :arg string client_key: Filename for client SSL key, if any
        :arg string client_cert: Filename for client SSL certificate, if any
        """
        if headers is None:
            headers = httputil.HTTPHeaders()
        if if_modified_since:
            timestamp = calendar.timegm(if_modified_since.utctimetuple())
            headers["If-Modified-Since"] = email.utils.formatdate(
                timestamp, localtime=False, usegmt=True)
        self.proxy_host = proxy_host
        self.proxy_port = proxy_port
        self.proxy_username = proxy_username
        self.proxy_password = proxy_password
        self.url = url
        self.method = method
        self.headers = headers
        self.body = utf8(body)
        self.auth_username = auth_username
        self.auth_password = auth_password
        self.connect_timeout = connect_timeout
        self.request_timeout = request_timeout
        self.follow_redirects = follow_redirects
        self.max_redirects = max_redirects
        self.user_agent = user_agent
        self.use_gzip = use_gzip
        self.network_interface = network_interface
        self.streaming_callback = streaming_callback
        self.header_callback = header_callback
        self.prepare_curl_callback = prepare_curl_callback
        self.allow_nonstandard_methods = allow_nonstandard_methods
        self.validate_cert = validate_cert
        self.ca_certs = ca_certs
        self.allow_ipv6 = allow_ipv6
        self.client_key = client_key
        self.client_cert = client_cert
        self.start_time = time.time()
Example #54
0
 def test_if(self):
     template = Template(utf8("{% if x > 4 %}yes{% else %}no{% end %}"))
     self.assertEqual(template.generate(x=5), b("yes"))
     self.assertEqual(template.generate(x=3), b("no"))
Example #55
0
 def write_headers(
     self,
     start_line: Union[httputil.RequestStartLine,
                       httputil.ResponseStartLine],
     headers: httputil.HTTPHeaders,
     chunk: Optional[bytes] = None,
 ) -> "Future[None]":
     """Implements `.HTTPConnection.write_headers`."""
     lines = []
     if self.is_client:
         assert isinstance(start_line, httputil.RequestStartLine)
         self._request_start_line = start_line
         lines.append(
             utf8("%s %s HTTP/1.1" % (start_line[0], start_line[1])))
         # Client requests with a non-empty body must have either a
         # Content-Length or a Transfer-Encoding.
         self._chunking_output = (
             start_line.method in ("POST", "PUT", "PATCH")
             and "Content-Length" not in headers
             and ("Transfer-Encoding" not in headers
                  or headers["Transfer-Encoding"] == "chunked"))
     else:
         assert isinstance(start_line, httputil.ResponseStartLine)
         assert self._request_start_line is not None
         assert self._request_headers is not None
         self._response_start_line = start_line
         lines.append(
             utf8("HTTP/1.1 %d %s" % (start_line[1], start_line[2])))
         self._chunking_output = (
             # TODO: should this use
             # self._request_start_line.version or
             # start_line.version?
             self._request_start_line.version == "HTTP/1.1"
             # Omit payload header field for HEAD request.
             and self._request_start_line.method != "HEAD"
             # 1xx, 204 and 304 responses have no body (not even a zero-length
             # body), and so should not have either Content-Length or
             # Transfer-Encoding headers.
             and start_line.code not in (204, 304)
             and (start_line.code < 100 or start_line.code >= 200)
             # No need to chunk the output if a Content-Length is specified.
             and "Content-Length" not in headers
             # Applications are discouraged from touching Transfer-Encoding,
             # but if they do, leave it alone.
             and "Transfer-Encoding" not in headers)
         # If connection to a 1.1 client will be closed, inform client
         if (self._request_start_line.version == "HTTP/1.1"
                 and self._disconnect_on_finish):
             headers["Connection"] = "close"
         # If a 1.0 client asked for keep-alive, add the header.
         if (self._request_start_line.version == "HTTP/1.0"
                 and self._request_headers.get("Connection",
                                               "").lower() == "keep-alive"):
             headers["Connection"] = "Keep-Alive"
     if self._chunking_output:
         headers["Transfer-Encoding"] = "chunked"
     if not self.is_client and (self._request_start_line.method == "HEAD"
                                or cast(httputil.ResponseStartLine,
                                        start_line).code == 304):
         self._expected_content_remaining = 0
     elif "Content-Length" in headers:
         self._expected_content_remaining = int(headers["Content-Length"])
     else:
         self._expected_content_remaining = None
     # TODO: headers are supposed to be of type str, but we still have some
     # cases that let bytes slip through. Remove these native_str calls when those
     # are fixed.
     header_lines = (native_str(n) + ": " + native_str(v)
                     for n, v in headers.get_all())
     lines.extend(line.encode("latin1") for line in header_lines)
     for line in lines:
         if b"\n" in line:
             raise ValueError("Newline in header: " + repr(line))
     future = None
     if self.stream.closed():
         future = self._write_future = Future()
         future.set_exception(iostream.StreamClosedError())
         future.exception()
     else:
         future = self._write_future = Future()
         data = b"\r\n".join(lines) + b"\r\n\r\n"
         if chunk:
             data += self._format_chunk(chunk)
         self._pending_write = self.stream.write(data)
         future_add_done_callback(self._pending_write,
                                  self._on_write_complete)
     return future
 def handle_connect(self):
     logging.info("handle_connect")
     self.stream.write(utf8(self.request_data + "\n"))
     self.stream.read_until(b'\n', callback=self.handle_read)
Example #57
0
    def _curl_setup_request(
        self,
        curl: pycurl.Curl,
        request: HTTPRequest,
        buffer: BytesIO,
        headers: httputil.HTTPHeaders,
    ) -> None:
        curl.setopt(pycurl.URL, native_str(request.url))

        # libcurl's magic "Expect: 100-continue" behavior causes delays
        # with servers that don't support it (which include, among others,
        # Google's OpenID endpoint).  Additionally, this behavior has
        # a bug in conjunction with the curl_multi_socket_action API
        # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976),
        # which increases the delays.  It's more trouble than it's worth,
        # so just turn off the feature (yes, setting Expect: to an empty
        # value is the official way to disable this)
        if "Expect" not in request.headers:
            request.headers["Expect"] = ""

        # libcurl adds Pragma: no-cache by default; disable that too
        if "Pragma" not in request.headers:
            request.headers["Pragma"] = ""

        curl.setopt(
            pycurl.HTTPHEADER,
            [
                b"%s: %s"
                % (native_str(k).encode("ASCII"), native_str(v).encode("ISO8859-1"))
                for k, v in request.headers.get_all()
            ],
        )

        curl.setopt(
            pycurl.HEADERFUNCTION,
            functools.partial(
                self._curl_header_callback, headers, request.header_callback
            ),
        )
        if request.streaming_callback:

            def write_function(b: Union[bytes, bytearray]) -> int:
                assert request.streaming_callback is not None
                self.io_loop.add_callback(request.streaming_callback, b)
                return len(b)

        else:
            write_function = buffer.write  # type: ignore
        curl.setopt(pycurl.WRITEFUNCTION, write_function)
        curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects)
        curl.setopt(pycurl.MAXREDIRS, request.max_redirects)
        assert request.connect_timeout is not None
        curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout))
        assert request.request_timeout is not None
        curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout))
        if request.user_agent:
            curl.setopt(pycurl.USERAGENT, native_str(request.user_agent))
        else:
            curl.setopt(pycurl.USERAGENT, "Mozilla/5.0 (compatible; pycurl)")
        if request.network_interface:
            curl.setopt(pycurl.INTERFACE, request.network_interface)
        if request.decompress_response:
            curl.setopt(pycurl.ENCODING, "gzip,deflate")
        else:
            curl.setopt(pycurl.ENCODING, None)
        if request.proxy_host and request.proxy_port:
            curl.setopt(pycurl.PROXY, request.proxy_host)
            curl.setopt(pycurl.PROXYPORT, request.proxy_port)
            if request.proxy_username:
                assert request.proxy_password is not None
                credentials = httputil.encode_username_password(
                    request.proxy_username, request.proxy_password
                )
                curl.setopt(pycurl.PROXYUSERPWD, credentials)

            if request.proxy_auth_mode is None or request.proxy_auth_mode == "basic":
                curl.setopt(pycurl.PROXYAUTH, pycurl.HTTPAUTH_BASIC)
            elif request.proxy_auth_mode == "digest":
                curl.setopt(pycurl.PROXYAUTH, pycurl.HTTPAUTH_DIGEST)
            else:
                raise ValueError(
                    "Unsupported proxy_auth_mode %s" % request.proxy_auth_mode
                )
        else:
            try:
                curl.unsetopt(pycurl.PROXY)
            except TypeError:  # not supported, disable proxy
                curl.setopt(pycurl.PROXY, "")
            curl.unsetopt(pycurl.PROXYUSERPWD)
        if request.validate_cert:
            curl.setopt(pycurl.SSL_VERIFYPEER, 1)
            curl.setopt(pycurl.SSL_VERIFYHOST, 2)
        else:
            curl.setopt(pycurl.SSL_VERIFYPEER, 0)
            curl.setopt(pycurl.SSL_VERIFYHOST, 0)
        if request.ca_certs is not None:
            curl.setopt(pycurl.CAINFO, request.ca_certs)
        else:
            # There is no way to restore pycurl.CAINFO to its default value
            # (Using unsetopt makes it reject all certificates).
            # I don't see any way to read the default value from python so it
            # can be restored later.  We'll have to just leave CAINFO untouched
            # if no ca_certs file was specified, and require that if any
            # request uses a custom ca_certs file, they all must.
            pass

        if request.allow_ipv6 is False:
            # Curl behaves reasonably when DNS resolution gives an ipv6 address
            # that we can't reach, so allow ipv6 unless the user asks to disable.
            curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
        else:
            curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)

        # Set the request method through curl's irritating interface which makes
        # up names for almost every single method
        curl_options = {
            "GET": pycurl.HTTPGET,
            "POST": pycurl.POST,
            "PUT": pycurl.UPLOAD,
            "HEAD": pycurl.NOBODY,
        }
        custom_methods = set(["DELETE", "OPTIONS", "PATCH"])
        for o in curl_options.values():
            curl.setopt(o, False)
        if request.method in curl_options:
            curl.unsetopt(pycurl.CUSTOMREQUEST)
            curl.setopt(curl_options[request.method], True)
        elif request.allow_nonstandard_methods or request.method in custom_methods:
            curl.setopt(pycurl.CUSTOMREQUEST, request.method)
        else:
            raise KeyError("unknown method " + request.method)

        body_expected = request.method in ("POST", "PATCH", "PUT")
        body_present = request.body is not None
        if not request.allow_nonstandard_methods:
            # Some HTTP methods nearly always have bodies while others
            # almost never do. Fail in this case unless the user has
            # opted out of sanity checks with allow_nonstandard_methods.
            if (body_expected and not body_present) or (
                body_present and not body_expected
            ):
                raise ValueError(
                    "Body must %sbe None for method %s (unless "
                    "allow_nonstandard_methods is true)"
                    % ("not " if body_expected else "", request.method)
                )

        if body_expected or body_present:
            if request.method == "GET":
                # Even with `allow_nonstandard_methods` we disallow
                # GET with a body (because libcurl doesn't allow it
                # unless we use CUSTOMREQUEST). While the spec doesn't
                # forbid clients from sending a body, it arguably
                # disallows the server from doing anything with them.
                raise ValueError("Body must be None for GET request")
            request_buffer = BytesIO(utf8(request.body or ""))

            def ioctl(cmd: int) -> None:
                if cmd == curl.IOCMD_RESTARTREAD:  # type: ignore
                    request_buffer.seek(0)

            curl.setopt(pycurl.READFUNCTION, request_buffer.read)
            curl.setopt(pycurl.IOCTLFUNCTION, ioctl)
            if request.method == "POST":
                curl.setopt(pycurl.POSTFIELDSIZE, len(request.body or ""))
            else:
                curl.setopt(pycurl.UPLOAD, True)
                curl.setopt(pycurl.INFILESIZE, len(request.body or ""))

        if request.auth_username is not None:
            assert request.auth_password is not None
            if request.auth_mode is None or request.auth_mode == "basic":
                curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
            elif request.auth_mode == "digest":
                curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)
            else:
                raise ValueError("Unsupported auth_mode %s" % request.auth_mode)

            userpwd = httputil.encode_username_password(
                request.auth_username, request.auth_password
            )
            curl.setopt(pycurl.USERPWD, userpwd)
            curl_log.debug(
                "%s %s (username: %r)",
                request.method,
                request.url,
                request.auth_username,
            )
        else:
            curl.unsetopt(pycurl.USERPWD)
            curl_log.debug("%s %s", request.method, request.url)

        if request.client_cert is not None:
            curl.setopt(pycurl.SSLCERT, request.client_cert)

        if request.client_key is not None:
            curl.setopt(pycurl.SSLKEY, request.client_key)

        if request.ssl_options is not None:
            raise ValueError("ssl_options not supported in curl_httpclient")

        if threading.active_count() > 1:
            # libcurl/pycurl is not thread-safe by default.  When multiple threads
            # are used, signals should be disabled.  This has the side effect
            # of disabling DNS timeouts in some environments (when libcurl is
            # not linked against ares), so we don't do it when there is only one
            # thread.  Applications that use many short-lived threads may need
            # to set NOSIGNAL manually in a prepare_curl_callback since
            # there may not be any other threads running at the time we call
            # threading.activeCount.
            curl.setopt(pycurl.NOSIGNAL, 1)
        if request.prepare_curl_callback is not None:
            request.prepare_curl_callback(curl)
def gzip_encode(s):
    with closing(compat.StringIO()) as sio:
        with gzip.GzipFile(fileobj=sio, mode='wb') as gz_file:
            gz_file.write(escape.utf8(s))
        return sio.getvalue()
Example #59
0
 def test_unicode_logging(self):
     self.logger.error(u"\u00e9")
     self.assertEqual(self.get_output(), utf8(u"\u00e9"))
Example #60
0
 def test_bytes_logging(self):
     with ignore_bytes_warning():
         # This will be "\xe9" on python 2 or "b'\xe9'" on python 3
         self.logger.error(b"\xe9")
         self.assertEqual(self.get_output(), utf8(repr(b"\xe9")))