@@ -109,6 +109,18 @@ def init(self) -> None:
109109 with favicon .open ("rb" ) as favicon_fd :
110110 type(self ).favicon_data = favicon_fd .read ()
111111
112+ def redirect_to (
113+ self ,
114+ path : str ,
115+ response : ResponseHeader ,
116+ status : http .HTTPStatus = http .HTTPStatus .TEMPORARY_REDIRECT ,
117+ ) -> bytes :
118+ """Handle responding with redirection status."""
119+
120+ response ["Location" ] = path
121+ response .status = status
122+ return bytes ()
123+
112124 async def try_redirect (
113125 self , path : PathMaybeQuery , response : ResponseHeader
114126 ) -> Optional [bytes ]:
@@ -118,33 +130,39 @@ async def try_redirect(
118130
119131 curr = Path (path [0 ])
120132 if curr in self .class_redirect_paths :
121- response ["Location" ] = str (self .class_redirect_paths [curr ])
122- response .status = http .HTTPStatus .TEMPORARY_REDIRECT
123-
124- # No data payload, but signal to caller that a response is ready.
125- result = bytes ()
133+ result = self .redirect_to (
134+ str (self .class_redirect_paths [curr ]), response
135+ )
126136
127137 return result
128138
129- async def render_markdown (
130- self , path : Path , response : ResponseHeader , ** kwargs
139+ def render_markdown (
140+ self , content : str , response : ResponseHeader , ** kwargs
131141 ) -> bytes :
132- """Render a markdown file as HTML and return the result ."""
142+ """Return rendered markdown content ."""
133143
134144 document = get_html ()
135-
136- async with aiofiles .open (path , mode = "r" ) as path_fd :
137- with IndentedFileWriter .string () as writer :
138- writer .write_markdown (await path_fd .read (), ** kwargs )
139- full_markdown_page (
140- document ,
141- writer .stream .getvalue (), # type: ignore
142- )
145+ with IndentedFileWriter .string () as writer :
146+ writer .write_markdown (content , ** kwargs )
147+ full_markdown_page (
148+ document ,
149+ writer .stream .getvalue (), # type: ignore
150+ )
143151
144152 response ["Content-Type" ] = f"text/html; charset={ DEFAULT_ENCODING } "
145153
146154 return document .encode_str ().encode ()
147155
156+ async def render_markdown_file (
157+ self , path : Path , response : ResponseHeader , ** kwargs
158+ ) -> bytes :
159+ """Render a markdown file as HTML and return the result."""
160+
161+ async with aiofiles .open (path , mode = "r" ) as path_fd :
162+ return self .render_markdown (
163+ await path_fd .read (), response , ** kwargs
164+ )
165+
148166 async def try_file (
149167 self , path : PathMaybeQuery , response : ResponseHeader
150168 ) -> Optional [bytes ]:
@@ -157,9 +175,12 @@ async def try_file(
157175 candidate = search .joinpath (path [0 ][1 :])
158176
159177 # Handle markdown sources.
160- md_candidate = candidate .with_suffix (".md" )
161- if md_candidate .is_file ():
162- return await self .render_markdown (md_candidate , response )
178+ if candidate .name :
179+ md_candidate = candidate .with_suffix (".md" )
180+ if md_candidate .is_file ():
181+ return await self .render_markdown_file (
182+ md_candidate , response
183+ )
163184
164185 if candidate .is_file ():
165186 mime , encoding = mimetypes .guess_type (candidate , strict = False )
@@ -239,6 +260,7 @@ async def get_handler(
239260 request .log (self .logger , False , level = logging .INFO )
240261
241262 result = None
263+ populated = False
242264
243265 with StringIO () as stream :
244266 if request .target .origin_form :
@@ -266,10 +288,11 @@ async def get_handler(
266288 request_data ,
267289 self .json_data ,
268290 )
291+ populated = True
269292
270293 # Serve the application.
271294 else :
272- await html_handler (
295+ populated = await html_handler (
273296 type (self ).apps ,
274297 stream ,
275298 request ,
@@ -278,6 +301,7 @@ async def get_handler(
278301 default_app = type (self ).default_app ,
279302 )
280303
281- result = stream .getvalue ().encode ()
304+ if populated :
305+ result = stream .getvalue ().encode ()
282306
283- return result
307+ return result or self . redirect_to ( "/404.html" , response )
0 commit comments