def _on_authentication_verified(self, callback, response): if response.error or b("is_valid:true") not in response.body: log.msg("Invalid OpenID response: %s" % \ (response.error or response.body)) callback(None) return # Make sure we got back at least an email from attribute exchange ax_ns = None for name in self.request.arguments.iterkeys(): if name.startswith("openid.ns.") and \ self.get_argument(name) == u"http://openid.net/srv/ax/1.0": ax_ns = name[10:] break def get_ax_arg(uri): if not ax_ns: return u"" prefix = "openid." + ax_ns + ".type." ax_name = None for name in self.request.arguments.iterkeys(): if self.get_argument(name) == uri and name.startswith(prefix): part = name[len(prefix):] ax_name = "openid." + ax_ns + ".value." + part break if not ax_name: return u"" return self.get_argument(ax_name, u"") email = get_ax_arg("http://axschema.org/contact/email") name = get_ax_arg("http://axschema.org/namePerson") first_name = get_ax_arg("http://axschema.org/namePerson/first") last_name = get_ax_arg("http://axschema.org/namePerson/last") username = get_ax_arg("http://axschema.org/namePerson/friendly") locale = get_ax_arg("http://axschema.org/pref/language").lower() user = dict() name_parts = [] if first_name: user["first_name"] = first_name name_parts.append(first_name) if last_name: user["last_name"] = last_name name_parts.append(last_name) if name: user["name"] = name elif name_parts: user["name"] = u" ".join(name_parts) elif email: user["name"] = email.split("@")[0] if email: user["email"] = email if locale: user["locale"] = locale if username: user["username"] = username callback(user)
def parse_multipart_form_data(boundary, data, arguments, files): """Parses a multipart/form-data body. The boundary and data parameters are both byte strings. The dictionaries given in the arguments and files parameters will be updated with the contents of the body. """ # The standard allows for the boundary to be quoted in the header, # although it's rare (it happens at least for google app engine # xmpp). I think we're also supposed to handle backslash-escapes # here but I'll save that until we see a client that uses them # in the wild. if boundary.startswith(b('"')) and boundary.endswith(b('"')): boundary = boundary[1:-1] final_boundary_index = data.rfind(b("--") + boundary + b("--")) if final_boundary_index == -1: log.msg("Invalid multipart/form-data: no final boundary") return parts = data[:final_boundary_index].split(b("--") + boundary + b("\r\n")) for part in parts: if not part: continue eoh = part.find(b("\r\n\r\n")) if eoh == -1: log.msg("multipart/form-data missing headers") continue headers = HTTPHeaders.parse(part[:eoh].decode("utf-8")) disp_header = headers.get("Content-Disposition", "") disposition, disp_params = _parse_header(disp_header) if disposition != "form-data" or not part.endswith(b("\r\n")): log.msg("Invalid multipart/form-data") continue value = part[eoh + 4:-2] if not disp_params.get("name"): log.msg("multipart/form-data value missing name") continue name = disp_params["name"] if disp_params.get("filename"): ctype = headers.get("Content-Type", "application/unknown") files.setdefault(name, []).append( HTTPFile(filename=disp_params["filename"], body=value, content_type=ctype)) else: arguments.setdefault(name, []).append(value)
def parse_multipart_form_data(boundary, data, arguments, files): """Parses a multipart/form-data body. The boundary and data parameters are both byte strings. The dictionaries given in the arguments and files parameters will be updated with the contents of the body. """ # The standard allows for the boundary to be quoted in the header, # although it's rare (it happens at least for google app engine # xmpp). I think we're also supposed to handle backslash-escapes # here but I'll save that until we see a client that uses them # in the wild. if boundary.startswith(b('"')) and boundary.endswith(b('"')): boundary = boundary[1:-1] final_boundary_index = data.rfind(b("--") + boundary + b("--")) if final_boundary_index == -1: log.msg("Invalid multipart/form-data: no final boundary") return parts = data[:final_boundary_index].split(b("--") + boundary + b("\r\n")) for part in parts: if not part: continue eoh = part.find(b("\r\n\r\n")) if eoh == -1: log.msg("multipart/form-data missing headers") continue headers = HTTPHeaders.parse(part[:eoh].decode("utf-8")) disp_header = headers.get("Content-Disposition", "") disposition, disp_params = _parse_header(disp_header) if disposition != "form-data" or not part.endswith(b("\r\n")): log.msg("Invalid multipart/form-data") continue value = part[eoh + 4:-2] if not disp_params.get("name"): log.msg("multipart/form-data value missing name") continue name = disp_params["name"] if disp_params.get("filename"): ctype = headers.get("Content-Type", "application/unknown") files.setdefault(name, []).append(HTTPFile( filename=disp_params["filename"], body=value, content_type=ctype)) else: arguments.setdefault(name, []).append(value)
class HTTPConnection(basic.LineReceiver): """Handles a connection to an HTTP client, executing HTTP requests. We parse HTTP headers and bodies, and execute the request callback until the HTTP conection is closed. If ``xheaders`` is ``True``, we support the ``X-Real-Ip`` and ``X-Scheme`` headers, which override the remote IP and HTTP scheme for all requests. These headers are useful when running Tornado behind a reverse proxy or load balancer. """ delimiter = b("\r\n") def connectionMade(self): self._headersbuffer = [] self._contentbuffer = [] self._finish_callback = None self.no_keep_alive = False self.content_length = None self.request_callback = self.factory self.xheaders = self.factory.settings.get('xheaders', False) self._request = None self._request_finished = False def connectionLost(self, reason): if self._finish_callback: self._finish_callback.callback(reason.getErrorMessage()) self._finish_callback = None def notifyFinish(self): if self._finish_callback is None: self._finish_callback = defer.Deferred() return self._finish_callback def lineReceived(self, line): if line: self._headersbuffer.append(line + self.delimiter) else: buff = "".join(self._headersbuffer) self._headersbuffer = [] self._on_headers(buff) def rawDataReceived(self, data): if self.content_length is not None: data, rest = data[:self.content_length], data[self.content_length:] self.content_length -= len(data) else: rest = '' self._contentbuffer.append(data) if self.content_length == 0: buff = "".join(self._contentbuffer) self._contentbuffer = [] self.content_length = None self._on_request_body(buff) self.setLineMode(rest) def write(self, chunk): assert self._request, "Request closed" self.transport.write(chunk) def finish(self): assert self._request, "Request closed" self._request_finished = True self._finish_request() def _on_write_complete(self): if self._request_finished: self._finish_request() def _finish_request(self): if self.no_keep_alive: disconnect = True else: connection_header = self._request.headers.get("Connection") if self._request.supports_http_1_1(): disconnect = connection_header == "close" elif ("Content-Length" in self._request.headers or self._request.method in ("HEAD", "GET")): disconnect = connection_header != "Keep-Alive" else: disconnect = True if self._finish_callback: self._finish_callback.callback(None) self._finish_callback = None self._request = None self._request_finished = False if disconnect is True: self.transport.loseConnection() def _on_headers(self, data): try: data = native_str(data.decode("latin1")) eol = data.find("\r\n") start_line = data[:eol] try: method, uri, version = start_line.split(" ") except ValueError: raise _BadRequestException("Malformed HTTP request line") if not version.startswith("HTTP/"): raise _BadRequestException( "Malformed HTTP version in HTTP Request-Line") headers = httputil.HTTPHeaders.parse(data[eol:]) self._request = HTTPRequest( connection=self, method=method, uri=uri, version=version, headers=headers, remote_ip=self.transport.getPeer().host) content_length = int(headers.get("Content-Length", 0)) if content_length: if headers.get("Expect") == "100-continue": self.transport.write("HTTP/1.1 100 (Continue)\r\n\r\n") self.content_length = content_length self.setRawMode() return self.request_callback(self._request) except _BadRequestException, e: log.msg("Malformed HTTP request from %s: %s", self.transport.getPeer().host, e) self.transport.loseConnection()