Repository: mkdocs/mkdocs

Issue:
V.1.2: Markdown links earlier processed as html files, are now processed as directories
The new version brings a curious issue. With versions < 1.2, I used internal links in this way, to make a call to page `bar.md`, from page `foo.md`:

```markdown
[linking to content in bar](../bar#content)
```

Which was transformed correctly into:

```html
<a href="../bar/#content">linking to content in bar</a>
```

Since version 1.2, the `bar` is intepreted differently by `mkdocs serve`, which breaks all such links.  I realize this is highly irregular and correct call should have been:

```markdown
[linking to content in bar](bar.md#content)
```

So please dont throw rotten tomatoes at me 🍅 .

But just to help me fix this: why this change of behavior? Has some part been reimplemented? Or has this been intentional, to prevent that kind of error in the future?



Discussion:
Your finding is correct. Let me expand on this.
([In addition to documentation](https://www.mkdocs.org/user-guide/writing-your-docs/#linking-to-pages), where I can't find references to this exact situation)

Regarding links that look like:
`[linking to content in bar](../bar#content)`
MkDocs completely ignores links to paths without an extension: doesn't validate or alter them.

So your link stays as pointing to a "file" named `bar`.

There is no strict definition or consensus among HTTP servers whether `bar` should be understood as `bar/` (hence `bar/index.html`) if it happens to be a directory. So, your links would be broken on at least *some* servers. It's just that the new server for `mkdocs serve` happens to not support this. I had no intention of breaking this behavior or preventing anything, but I *did* intentionally [rely on the trailing slash](https://github.com/mkdocs/mkdocs/blob/bd167cf3f65cccbdb289f74c0dd99ad083b4d22b/mkdocs/livereload/__init__.py#L224-L225) as that's much simpler. The fact is that MkDocs-endorsed links never rely on this behavior, so this wasn't seen as a problem.

As such, I would not consider this a bug. But I will **not** go as far as to say that the new behavior is better.
@oprypin Thanks a lot for this useful clarification! Also the pointer to the documentation started to put some clarity in my mind, about how mkdocs modifies internal links when it generates the html pages.

Perhaps, it would be a good idea to give additional details (or examples) on the rules that mkdocs uses, so that we can think with border cases, etc.?
@oprypin, hello. Found this topic when tried to fix my problem with trailing slash. 
Right now if I have a site `https://example.com` with directory structure `docs/somepath/index.md` I can't call `example.com/somepath` (note missing slash at the end). This call shows 404 error. If I add a slash at the end like `example.com/somepath/` I will see a page `index.md` without problems. 
On main mkdocs site when I enter a link without trailing slash `https://www.mkdocs.org/user-guide/configuration` the site redirects me to the page with trailing slash (so no 404 error).

The question is why the behaviour differs and how to get a redirect to a page with a trailing slash (if it wasn't specified)? For me it's a problem because my current site is viewed without slashes at the end and my app has links to that pages without slashes. And if I move to mkdocs this behavior will break previous versions of the app.
@avently 

(Note that generally, when linking *from within* MkDocs, you must write the links as `somepath/index.md`, only then they are properly supported by MkDocs.)

When hosting the site on a server and linking from elsewhere, these redirects rely on particular behaviors of particular servers. The fact that `mkdocs serve` supports or doesn't support such redirects has no effect on the final deployment server, which you will pick yourself, so there will be no problem.

To put it differently, a deployed MkDocs site is just a collection of HTML files. Of course HTML files themselves cannot define how redirects work on any particular server. The server used by `mkdocs serve` happens to not support these redirects, and actually I think it's a good thing, because then it represents the "lowest common denominator" of webservers.
You were so fast. I didn't even fast enough to write a comment related to my testing with `python -m http.server --directory site`. In this case the behavior is expected and works correctly. So yeah, I can switch to mkdocs, thanks!


Resolving patch:
diff --git a/mkdocs/livereload/__init__.py b/mkdocs/livereload/__init__.py
--- a/mkdocs/livereload/__init__.py
+++ b/mkdocs/livereload/__init__.py
@@ -183,10 +183,10 @@ def condition():
         if path == "/js/livereload.js":
             file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "livereload.js")
         elif path.startswith(self.mount_path):
+            rel_file_path = path[len(self.mount_path):].lstrip("/")
             if path.endswith("/"):
-                path += "index.html"
-            path = path[len(self.mount_path):]
-            file_path = os.path.join(self.root, path.lstrip("/"))
+                rel_file_path += "index.html"
+            file_path = os.path.join(self.root, rel_file_path)
         elif path == "/":
             start_response("302 Found", [("Location", self.mount_path)])
             return []
@@ -201,9 +201,12 @@ def condition():
         try:
             file = open(file_path, "rb")
         except OSError:
+            if not path.endswith("/") and os.path.isfile(os.path.join(file_path, "index.html")):
+                start_response("302 Found", [("Location", path + "/")])
+                return []
             return None  # Not found
 
-        if path.endswith(".html"):
+        if file_path.endswith(".html"):
             with file:
                 content = file.read()
             content = self._inject_js_into_html(content, epoch)


Test patch:
diff --git a/mkdocs/tests/livereload_tests.py b/mkdocs/tests/livereload_tests.py
--- a/mkdocs/tests/livereload_tests.py
+++ b/mkdocs/tests/livereload_tests.py
@@ -293,7 +293,7 @@ def test_serves_modified_html(self, site_dir):
             self.assertRegex(output, fr"^<body>foo</body><body>bar{SCRIPT_REGEX}</body>$")
 
     @tempdir({"index.html": "<body>aaa</body>", "foo/index.html": "<body>bbb</body>"})
-    def test_serves_modified_index(self, site_dir):
+    def test_serves_directory_index(self, site_dir):
         with testing_server(site_dir) as server:
             headers, output = do_request(server, "GET /")
             self.assertRegex(output, fr"^<body>aaa{SCRIPT_REGEX}</body>$")
@@ -301,8 +301,21 @@ def test_serves_modified_index(self, site_dir):
             self.assertEqual(headers.get("content-type"), "text/html")
             self.assertEqual(headers.get("content-length"), str(len(output)))
 
-            _, output = do_request(server, "GET /foo/")
-            self.assertRegex(output, fr"^<body>bbb{SCRIPT_REGEX}</body>$")
+            for path in "/foo/", "/foo/index.html":
+                _, output = do_request(server, "GET /foo/")
+                self.assertRegex(output, fr"^<body>bbb{SCRIPT_REGEX}</body>$")
+
+            with self.assertLogs("mkdocs.livereload"):
+                headers, _ = do_request(server, "GET /foo/index.html/")
+            self.assertEqual(headers["_status"], "404 Not Found")
+
+    @tempdir({"foo/bar/index.html": "<body>aaa</body>"})
+    def test_redirects_to_directory(self, site_dir):
+        with testing_server(site_dir, mount_path="/sub") as server:
+            with self.assertLogs("mkdocs.livereload"):
+                headers, _ = do_request(server, "GET /sub/foo/bar")
+            self.assertEqual(headers["_status"], "302 Found")
+            self.assertEqual(headers.get("location"), "/sub/foo/bar/")
 
     @tempdir({"я.html": "<body>aaa</body>", "测试2/index.html": "<body>bbb</body>"})
     def test_serves_with_unicode_characters(self, site_dir):