Skip to content

Commit b31def5

Browse files
committed
fix route match
1 parent 71cbf4c commit b31def5

File tree

1 file changed

+144
-127
lines changed

1 file changed

+144
-127
lines changed

router-core/src/app/proxy_fast.rs

Lines changed: 144 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -265,150 +265,167 @@ impl ProxyApp {
265265
}
266266

267267
// Regex-based HTTP request line parser and rewriter
268-
fn rewrite_http_request(&self, buffer: &mut [u8], length: usize) -> (usize, bool) {
269-
// First convert the buffer to a string for processing
270-
let request_str = match std::str::from_utf8(&buffer[..length]) {
271-
Ok(s) => s,
272-
Err(_) => return (length, false), // Not valid UTF-8, return unchanged
273-
};
268+
fn rewrite_http_request(&self, buffer: &mut [u8], length: usize) -> (usize, bool) {
269+
// First convert the buffer to a string for processing
270+
let request_str = match std::str::from_utf8(&buffer[..length]) {
271+
Ok(s) => s,
272+
Err(_) => return (length, false), // Not valid UTF-8, return unchanged
273+
};
274+
275+
// Flag to track if this is a WebSocket upgrade request
276+
let is_websocket = request_str.contains("Upgrade: websocket")
277+
|| request_str.contains("Upgrade: WebSocket");
278+
279+
// Check if this looks like an HTTP request
280+
if !request_str.starts_with("GET ")
281+
&& !request_str.starts_with("POST ")
282+
&& !request_str.starts_with("PUT ")
283+
&& !request_str.starts_with("DELETE ")
284+
&& !request_str.starts_with("CONNECT ")
285+
&& !request_str.starts_with("OPTIONS ")
286+
{
287+
return (length, is_websocket);
288+
}
274289

275-
// Flag to track if this is a WebSocket upgrade request
276-
let is_websocket = request_str.contains("Upgrade: websocket")
277-
|| request_str.contains("Upgrade: WebSocket");
278-
279-
// Check if this looks like an HTTP request
280-
if !request_str.starts_with("GET ")
281-
&& !request_str.starts_with("POST ")
282-
&& !request_str.starts_with("PUT ")
283-
&& !request_str.starts_with("DELETE ")
284-
&& !request_str.starts_with("CONNECT ")
285-
&& !request_str.starts_with("OPTIONS ")
286-
{
287-
return (length, is_websocket);
290+
// First check for configuration changes at regular intervals
291+
self.check_and_reload_config_if_needed();
292+
293+
// Check if we already have this request in the cache
294+
if let Some(cached_request) = self.rewrite_cache.get(&request_str.to_string()) {
295+
debug!("Cache hit for request rewrite");
296+
let new_bytes = cached_request.as_bytes();
297+
let new_len = new_bytes.len();
298+
299+
// Make sure we don't overflow the buffer
300+
if new_len <= buffer.len() {
301+
buffer[..new_len].copy_from_slice(new_bytes);
302+
return (new_len, is_websocket);
303+
} else {
304+
debug!("Cached rewritten request too large for buffer");
305+
return (length, false);
288306
}
307+
}
289308

290-
// First check for configuration changes at regular intervals
291-
self.check_and_reload_config_if_needed();
309+
debug!("Cache miss for request rewrite");
292310

293-
// Check if we already have this request in the cache
294-
if let Some(cached_request) = self.rewrite_cache.get(&request_str.to_string()) {
295-
debug!("Cache hit for request rewrite");
296-
let new_bytes = cached_request.as_bytes();
297-
let new_len = new_bytes.len();
311+
// Find the first line of the request (the request line)
312+
let line_end = match request_str.find("\r\n") {
313+
Some(pos) => pos,
314+
None => return (length, is_websocket), // Not a complete HTTP request line
315+
};
298316

299-
// Make sure we don't overflow the buffer
300-
if new_len <= buffer.len() {
301-
buffer[..new_len].copy_from_slice(new_bytes);
302-
return (new_len, is_websocket);
303-
} else {
304-
debug!("Cached rewritten request too large for buffer");
305-
return (length, false);
306-
}
307-
}
317+
let request_line = &request_str[..line_end];
318+
let rest_of_request = &request_str[line_end..];
308319

309-
debug!("Cache miss for request rewrite");
320+
// Log the full request line for debugging
321+
debug!("Received request line: '{}'", request_line);
310322

311-
// Find the first line of the request (the request line)
312-
let line_end = match request_str.find("\r\n") {
313-
Some(pos) => pos,
314-
None => return (length, is_websocket), // Not a complete HTTP request line
315-
};
323+
// Parse the request line to extract Method, Path, and Protocol
324+
let parts: Vec<&str> = request_line.splitn(3, ' ').collect();
325+
if parts.len() < 3 {
326+
debug!("Malformed request line: '{}'", request_line);
327+
return (length, is_websocket); // Malformed, return original
328+
}
329+
let method = parts[0];
330+
let request_path_with_query = parts[1];
331+
let protocol = parts[2];
332+
333+
// CRITICAL FIX: Separate path and query string
334+
let (request_path, query_string) = match request_path_with_query.find('?') {
335+
Some(pos) => {
336+
let (path, query) = request_path_with_query.split_at(pos);
337+
(path, Some(query)) // query includes the '?' character
338+
}
339+
None => (request_path_with_query, None)
340+
};
341+
342+
debug!("Parsed request path: '{}', query: '{:?}'", request_path, query_string);
343+
344+
// Try each rewrite rule
345+
let rules_guard = match self.path_rewrites.read() {
346+
Ok(guard) => guard,
347+
Err(e) => {
348+
error!("Failed to acquire read lock on path_rewrites: {}", e);
349+
return (length, is_websocket); // Return original length and websocket flag if lock acquisition fails
350+
}
351+
};
352+
for rule in rules_guard.iter() {
353+
// Find all matches in the PATH ONLY (not including query)
354+
let mut matches = Vec::new();
355+
let mut captures = Vec::new();
356+
357+
log::debug!("Checking rewrite rule pattern for request_path: '{}'", request_path);
358+
359+
// Use regex-automata to find matches in the path (excluding query)
360+
for mat in rule.pattern.find_iter(request_path.as_bytes()) {
361+
matches.push((mat.start(), mat.end()));
362+
363+
// Extract capture groups
364+
// This is simplified since regex-automata's Match doesn't directly provide captures
365+
// In a real implementation, you'd need to extract captures based on the match bounds
366+
let matched_text = &request_path[mat.start()..mat.end()];
367+
captures.push(matched_text);
368+
}
316369

317-
let request_line = &request_str[..line_end];
318-
let rest_of_request = &request_str[line_end..];
370+
// If we have a match, perform the rewrite
371+
if let Some((start, end)) = matches.first() {
372+
debug!("Matched regex pattern for rewrite");
319373

320-
// Log the full request line for debugging
321-
debug!("Received request line: '{}'", request_line);
374+
// Get parts of the *path* before and after the match
375+
let before = &request_path[..*start];
376+
let after = &request_path[*end..];
322377

323-
// Parse the request line to extract Method, Path, and Protocol
324-
let parts: Vec<&str> = request_line.splitn(3, ' ').collect();
325-
if parts.len() < 3 {
326-
debug!("Malformed request line: '{}'", request_line);
327-
return (length, is_websocket); // Malformed, return original
328-
}
329-
let method = parts[0];
330-
let request_path = parts[1];
331-
let protocol = parts[2];
332-
debug!("Parsed request path: '{}'", request_path);
333-
334-
// Try each rewrite rule
335-
let rules_guard = match self.path_rewrites.read() {
336-
Ok(guard) => guard,
337-
Err(e) => {
338-
error!("Failed to acquire read lock on path_rewrites: {}", e);
339-
return (length, is_websocket); // Return original length and websocket flag if lock acquisition fails
340-
}
341-
};
342-
for rule in rules_guard.iter() {
343-
// Find all matches in the request line
344-
let mut matches = Vec::new();
345-
let mut captures = Vec::new();
346-
347-
log::debug!("Checking rewrite rule pattern for request_path: '{}'", request_path);
348-
349-
// Use regex-automata to find matches in the path
350-
for mat in rule.pattern.find_iter(request_path.as_bytes()) {
351-
matches.push((mat.start(), mat.end()));
352-
353-
// Extract capture groups
354-
// This is simplified since regex-automata's Match doesn't directly provide captures
355-
// In a real implementation, you'd need to extract captures based on the match bounds
356-
let matched_text = &request_path[mat.start()..mat.end()];
357-
captures.push(matched_text);
378+
// Process replacement template with capture references
379+
let replacement = self.process_replacement(
380+
&captures.iter().map(|s| *s).collect::<Vec<&str>>(),
381+
&rule.replacement,
382+
);
383+
384+
// Create the new *path* without query, then add query if present
385+
let new_request_path_only = format!("{}{}{}", before, replacement, after);
386+
387+
// Reconstruct the full path with query string if it was present
388+
let new_request_path = match query_string {
389+
Some(q) => format!("{}{}", new_request_path_only, q), // q already includes '?'
390+
None => new_request_path_only,
391+
};
392+
393+
let new_request_line = format!("{} {} {}", method, new_request_path, protocol);
394+
let new_request = format!("{}{}", new_request_line, rest_of_request);
395+
396+
// Log rewrite information with special note for WebSocket upgrades
397+
if is_websocket {
398+
debug!("Rewrote WebSocket upgrade request: {} -> {}", request_line, new_request_line);
399+
} else {
400+
debug!("Rewrote request: {} -> {}", request_line, new_request_line);
358401
}
359402

360-
// If we have a match, perform the rewrite
361-
if let Some((start, end)) = matches.first() {
362-
debug!("Matched regex pattern for rewrite");
363-
364-
// Get parts of the *path* before and after the match
365-
let before = &request_path[..*start];
366-
let after = &request_path[*end..];
367-
368-
// Process replacement template with capture references
369-
let replacement = self.process_replacement(
370-
&captures.iter().map(|s| *s).collect::<Vec<&str>>(),
371-
&rule.replacement,
372-
);
373-
374-
// Create the new *path* and then the new request line
375-
let new_request_path = format!("{}{}{}", before, replacement, after);
376-
let new_request_line = format!("{} {} {}", method, new_request_path, protocol);
377-
let new_request = format!("{}{}", new_request_line, rest_of_request);
378-
379-
// Log rewrite information with special note for WebSocket upgrades
380-
if is_websocket {
381-
debug!("Rewrote WebSocket upgrade request: {} -> {}", request_line, new_request_line);
382-
} else {
383-
debug!("Rewrote request: {} -> {}", request_line, new_request_line);
384-
}
403+
// Store the rewritten request in the cache
404+
self.rewrite_cache.insert(request_str.to_string(), new_request.clone());
405+
debug!("Stored rewritten request in cache");
385406

386-
// Store the rewritten request in the cache
387-
self.rewrite_cache.insert(request_str.to_string(), new_request.clone());
388-
debug!("Stored rewritten request in cache");
389-
390-
// Convert back to bytes and copy to the buffer
391-
let new_bytes = new_request.as_bytes();
392-
let new_len = new_bytes.len();
393-
394-
// Make sure we don't overflow the buffer
395-
if new_len <= buffer.len() {
396-
// Copy the new request into the buffer
397-
buffer[..new_len].copy_from_slice(new_bytes);
398-
return (new_len,is_websocket);
399-
} else {
400-
debug!("Rewritten request too large for buffer");
401-
return (length,is_websocket); // Return original length if new request is too large
402-
}
407+
// Convert back to bytes and copy to the buffer
408+
let new_bytes = new_request.as_bytes();
409+
let new_len = new_bytes.len();
410+
411+
// Make sure we don't overflow the buffer
412+
if new_len <= buffer.len() {
413+
// Copy the new request into the buffer
414+
buffer[..new_len].copy_from_slice(new_bytes);
415+
return (new_len, is_websocket);
416+
} else {
417+
debug!("Rewritten request too large for buffer");
418+
return (length, is_websocket); // Return original length if new request is too large
403419
}
404420
}
405-
406-
// No rewrite performed
407-
debug!("No rewrite rule matched for request path: {}", request_path);
408-
// should close if no match
409-
(0,is_websocket)
410421
}
411422

423+
// No rewrite performed
424+
debug!("No rewrite rule matched for request path: {}", request_path);
425+
// should close if no match
426+
(0, is_websocket)
427+
}
428+
412429
/// Checks if the configuration should be reloaded based on time interval.
413430
fn check_and_reload_config_if_needed(&self) {
414431
let now = std::time::Instant::now();

0 commit comments

Comments
 (0)