mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
fix: url match logic of http server (denoland/deno_std#199)
Original: da188a7d30
This commit is contained in:
parent
954fe83f62
commit
bdeb6c43af
2 changed files with 84 additions and 20 deletions
|
@ -152,25 +152,15 @@ class HttpServerImpl implements HttpServer {
|
|||
|
||||
async listen(addr: string, cancel: Deferred = defer()) {
|
||||
for await (const { req, res } of serve(addr, cancel)) {
|
||||
let lastMatch: RegExpMatchArray;
|
||||
let lastHandler: HttpHandler;
|
||||
for (const { pattern, handler } of this.handlers) {
|
||||
const match = req.url.match(pattern);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
if (!lastMatch) {
|
||||
lastMatch = match;
|
||||
lastHandler = handler;
|
||||
} else if (match[0].length > lastMatch[0].length) {
|
||||
// use longest match
|
||||
lastMatch = match;
|
||||
lastHandler = handler;
|
||||
}
|
||||
}
|
||||
req.match = lastMatch;
|
||||
if (lastHandler) {
|
||||
await lastHandler(req, res);
|
||||
let { pathname } = new URL(req.url, addr);
|
||||
const { index, match } = findLongestAndNearestMatch(
|
||||
pathname,
|
||||
this.handlers.map(v => v.pattern)
|
||||
);
|
||||
req.match = match;
|
||||
if (index > -1) {
|
||||
const { handler } = this.handlers[index];
|
||||
await handler(req, res);
|
||||
if (!res.isResponded) {
|
||||
await res.respond({
|
||||
status: 500,
|
||||
|
@ -187,6 +177,36 @@ class HttpServerImpl implements HttpServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the match that appeared in the nearest position to the beginning of word.
|
||||
* If positions are same, the longest one will be picked.
|
||||
* Return -1 and null if no match found.
|
||||
* */
|
||||
export function findLongestAndNearestMatch(
|
||||
pathname: string,
|
||||
patterns: (string | RegExp)[]
|
||||
): { index: number; match: RegExpMatchArray } {
|
||||
let lastMatchIndex = pathname.length;
|
||||
let lastMatchLength = 0;
|
||||
let match: RegExpMatchArray = null;
|
||||
let index = -1;
|
||||
for (let i = 0; i < patterns.length; i++) {
|
||||
const pattern = patterns[i];
|
||||
const m = pathname.match(pattern);
|
||||
if (!m) continue;
|
||||
if (
|
||||
m.index < lastMatchIndex ||
|
||||
(m.index === lastMatchIndex && m[0].length > lastMatchLength)
|
||||
) {
|
||||
index = i;
|
||||
match = m;
|
||||
lastMatchIndex = m.index;
|
||||
lastMatchLength = m[0].length;
|
||||
}
|
||||
}
|
||||
return { index, match };
|
||||
}
|
||||
|
||||
class ServerResponderImpl implements ServerResponder {
|
||||
constructor(private w: Writer) {}
|
||||
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
// https://github.com/golang/go/blob/master/src/net/http/responsewrite_test.go
|
||||
|
||||
import { Buffer, copy, Reader } from "deno";
|
||||
import { assert, assertEqual, test } from "../testing/mod.ts";
|
||||
import { assert, assertEqual, runTests, test } from "../testing/mod.ts";
|
||||
import {
|
||||
createResponder,
|
||||
createServer,
|
||||
findLongestAndNearestMatch,
|
||||
readRequest,
|
||||
readResponse,
|
||||
ServerResponse,
|
||||
|
@ -102,6 +103,49 @@ test(async function httpReadRequestChunkedBody() {
|
|||
assert.equal(dest.toString(), "deno.land");
|
||||
});
|
||||
|
||||
test(function httpMatchNearest() {
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/foo", ["/foo", "/bar", "/f"]).index,
|
||||
0
|
||||
);
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/foo", ["/foo", "/foo/bar"]).index,
|
||||
0
|
||||
);
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/foo/bar", [
|
||||
"/",
|
||||
"/foo",
|
||||
"/hoo",
|
||||
"/hoo/foo/bar",
|
||||
"/foo/bar"
|
||||
]).index,
|
||||
4
|
||||
);
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/foo/bar/foo", ["/foo", "/foo/bar", "/bar/foo"])
|
||||
.index,
|
||||
1
|
||||
);
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/foo", ["/", "/hoo", "/hoo/foo"]).index,
|
||||
0
|
||||
);
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/deno/land", [/d(.+?)o/, /d(.+?)d/]).index,
|
||||
1
|
||||
);
|
||||
assert.equal(findLongestAndNearestMatch("/foo", ["/", "/a/foo"]).index, 0);
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/foo", [/\/foo/, /\/bar\/foo/]).index,
|
||||
0
|
||||
);
|
||||
assert.equal(
|
||||
findLongestAndNearestMatch("/foo", [/\/a\/foo/, /\/foo/]).index,
|
||||
1
|
||||
);
|
||||
});
|
||||
|
||||
test(async function httpServer() {
|
||||
const server = createServer();
|
||||
server.handle("/index", async (req, res) => {
|
||||
|
|
Loading…
Reference in a new issue