def test_html_quote(self):
     self.assertEqual(quoting.html_quote(1), '1')
     self.assertEqual(quoting.html_quote(None), '')
     self.assertEqual(quoting.html_quote('<hey!>'), '&lt;hey!&gt;')
     if six.PY3:
         self.assertEqual(quoting.html_quote(u'<\u1029>'),
                          u'&lt;\u1029&gt;')
     else:
         self.assertEqual(quoting.html_quote(u'<\u1029>'),
                          '&lt;\xe1\x80\xa9&gt;')
Ejemplo n.º 2
0
 def test_html_quote(self):
     self.assertEqual(quoting.html_quote(1),
                      '1')
     self.assertEqual(quoting.html_quote(None),
                      '')
     self.assertEqual(quoting.html_quote('<hey!>'),
                      '&lt;hey!&gt;')
     if six.PY3:
         self.assertEqual(quoting.html_quote(u'<\u1029>'),
                          u'&lt;\u1029&gt;')
     else:
         self.assertEqual(quoting.html_quote(u'<\u1029>'),
                          '&lt;\xe1\x80\xa9&gt;')
Ejemplo n.º 3
0
	def connection_handler(self):
		"""The main request processing loop."""

		while True:

			# Read the first line of the HTTP request.
			try:
				line = yield self.read_line()
				# Ignore blank lines, as suggested by the RFC
				if not line:
					continue
				method, url, protocol = line.split(' ')
			except (tcp.ConnectionOverflowException, ValueError):
				yield self.send_error("400 Bad Request")
				self.close()
				return

			# Prepare WSGI environment.
			waiting_coro = []
			environ = {
				'chiral.http.connection': self,
				'wsgi.version': (1, 0),
				'wsgi.url_scheme': 'http',
				'wsgi.input': '',
				'wsgi.errors': sys.stderr,
				'wsgi.file_wrapper': WSGIFileWrapper,
				'wsgi.multithread': False,
				'wsgi.multiprocess': True,
				'wsgi.run_once': False,
				'REQUEST_METHOD': method,
				'SCRIPT_NAME': '',
				'SERVER_NAME': self.server.bind_addr[0],
				'SERVER_PORT': self.server.bind_addr[1],
				'SERVER_PROTOCOL': protocol
			}

			# Split query string out of URL, if present
			url = url.split('?', 1)
			if len(url) > 1:
				url, environ["QUERY_STRING"] = url
			else:
				url, = url

			# Read the rest of the request header, updating the WSGI environ dict
			# with each key/value pair
			last_key = None
			while True:
				try:
					line = yield self.read_line()
				except tcp.ConnectionOverflowException:
					yield self.send_error("400 Bad Request")
					self.close()
					return

				if not line:
					break

				# Allow for headers split over multiple lines.
				if last_key and line[0] in (" ", "\t"):
					environ[last_key] += "," + line
					continue

				if ":" not in line:
					yield self.send_error("400 Bad Request")
					self.close()
					return

				name, value = line.split(":", 1)

				# Convert the field name to WSGI's format
				key = "HTTP_" + name.upper().replace("-", "_")

				if key in environ:
					# RFC2616 4.2: Multiple copies of a header should be
					# treated as though they were separated by commas.
					environ[key] += "," + value.strip()
				else:
					environ[key] = value.strip()

			# Accept absolute URLs, as required by HTTP 1.1
			if url.startswith("http://"):
				url = url[7:]
				if "/" in url:
					environ["HTTP_HOST"], url = url.split("/", 1)
				else:
					environ["HTTP_HOST"], url = url, ""
	
			environ["PATH_INFO"] = url

			# HTTP/1.1 requires that requests without a Host: header result in 400
			#if protocol == "HTTP/1.1" and "HTTP_HOST" not in environ:
			#	yield self.send_error("400 Bad Request")
			#	continue

			# WSGI requires these two headers
			environ["CONTENT_TYPE"] = environ.get("HTTP_CONTENT_TYPE", "")
			environ["CONTENT_LENGTH"] = environ.get("HTTP_CONTENT_LENGTH", "")

			# If a 100 Continue is expected, send it now.
			if protocol == "HTTP/1.1" and "100-continue" in environ.get("HTTP_EXPECT", ""):
				yield self.sendall("HTTP/1.1 100 Continue\r\n\r\n")

			# If this is a POST request with Content-Length, read its data
			if method == "POST" and environ["CONTENT_LENGTH"]:
				postdata = yield self.read_exactly(int(environ["CONTENT_LENGTH"]))
				environ["wsgi.input"] = StringIO(postdata)

			def set_coro(coro):
				"""
				Tell the HTTP response to not close until coro has completed.
				"""
				waiting_coro[:] = [ coro ]
			environ['chiral.http.set_coro'] = set_coro

			# Prepare the response object. 
			response = HTTPResponse(self, environ)

			# Invoke the application.
			try:
				result = self.server.application(environ, response.start_response)
			except Exception:
				yield self.send_error(
					"500 Internal Server Error",
					response,
					"<pre>%s</pre>" % html_quote(traceback.format_exc())
				)

				# Close if necessary
				if response.should_keep_alive:
					continue
				else:
					self.close()
					break


			# If the iterable has length 1, then we can determine the length
			# of the whole result now.
			try:
				if len(result) == 1 and not waiting_coro:
					response.headers["Content-Length"] = len(result[0])
			except TypeError:
				pass

			# Handle WSGIFileWrapper.
			if isinstance(result, WSGIFileWrapper):
				yield result.send_to_connection(self, response)

				# We're now done with this request.
				if response.should_keep_alive:
					continue
				else:
					self.close()
					break

			# Did they use write()?
			write_data = response.write_data_buffer.getvalue()
			if write_data:
				headers_sent = True
				yield self.sendall(response.render_headers() + write_data)
			
			# Iterate through the result chunks provided by the application.
			res_iter = iter(result)
			headers_sent = False
			while True:
				try:
					data = res_iter.next()
				except StopIteration:
					break
				except Exception:
					yield self.send_error(
						"500 Internal Server Error",
						response,
						"<pre>%s</pre>" % (
							html_quote(traceback.format_exc()),
						)
					)
					self.close()
					return

				# Ignore empty chunks
				if not data:
					continue

				# Sending the headers is delayed until the first actual
				# data chunk comes back.
				if not headers_sent:
					headers_sent = True
					yield self.sendall(response.render_headers() + data)
				else:
					yield self.sendall(data)

			# If no data at all was returned, the headers won't have been sent yet.
			if not headers_sent and not waiting_coro:
				headers_sent = True
				yield self.sendall(response.render_headers(no_content=True))

			# If set_coro has been called, waiting_coro is a Coroutine that will
			# complete once the response is done.
			if waiting_coro:
				try:
					delayed_data = yield waiting_coro[0]
					del waiting_coro[:]

					# See if we can set Content-Length 
					try:
						if type(delayed_data) in (list, tuple) and len(delayed_data) == 1:
							response.headers["Content-Length"] = len(delayed_data[0])
					except TypeError:
						pass

					# Iterate through and send any delayed data as well
					if delayed_data:
						for data in delayed_data:
							# Ignore empty chunks
							if not data:
								continue

							# Add the headers, if not already sent
							if not headers_sent:
								headers_sent = True
								data = response.render_headers() + data

							yield self.sendall(data)
				except Exception:
					exc_formatted = "<pre>%s</pre>" % html_quote(traceback.format_exc())
					yield self.send_error("500 Internal Server Error", response, exc_formatted)

			# If the waiting coro didn't return any data at all, send the headers already.
			if not headers_sent:
				headers_sent = True
				yield self.sendall(response.render_headers(no_content=True))
				
			# Call any close handler on the WSGI app's result
			if hasattr(result, 'close'):
				result.close()

			# Close if necessary
			if not response.should_keep_alive:
				self.close()
				break