|
| 1 | +use std::sync::Arc; |
| 2 | + |
| 3 | +use axum::{ |
| 4 | + extract::{Request, State}, |
| 5 | + middleware::Next, |
| 6 | + response::Response, |
| 7 | +}; |
| 8 | +use http::header::USER_AGENT; |
| 9 | +use isbot::Bots; |
| 10 | + |
| 11 | +#[derive(Clone, Debug)] |
| 12 | +pub struct IsBot(pub bool); |
| 13 | + |
| 14 | +pub struct IsBotState { |
| 15 | + bots: Bots, |
| 16 | +} |
| 17 | + |
| 18 | +impl Default for IsBotState { |
| 19 | + fn default() -> Self { |
| 20 | + let mut bots = Bots::default(); |
| 21 | + bots.append(&["GoogleOther"]); |
| 22 | + |
| 23 | + Self { bots } |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +impl IsBotState { |
| 28 | + pub fn is_bot(&self, ua: &str) -> bool { |
| 29 | + self.bots.is_bot(ua) |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +pub async fn middleware( |
| 34 | + State(state): State<Arc<IsBotState>>, |
| 35 | + mut request: Request, |
| 36 | + next: Next, |
| 37 | +) -> Response { |
| 38 | + let ua = request |
| 39 | + .headers() |
| 40 | + .get(USER_AGENT) |
| 41 | + .and_then(|x| x.to_str().ok()); |
| 42 | + |
| 43 | + let is_bot = IsBot(ua.is_some_and(|x| state.is_bot(x))); |
| 44 | + request.extensions_mut().insert(is_bot); |
| 45 | + |
| 46 | + next.run(request).await |
| 47 | +} |
| 48 | + |
| 49 | +#[cfg(test)] |
| 50 | +mod tests { |
| 51 | + use super::*; |
| 52 | + |
| 53 | + #[test] |
| 54 | + fn test_is_bot() { |
| 55 | + let state = IsBotState::default(); |
| 56 | + |
| 57 | + let bots = &[ |
| 58 | + "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", |
| 59 | + "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/W.X.Y.Z Safari/537.36", |
| 60 | + "Googlebot-Image/1.0", |
| 61 | + "Googlebot-Video/1.0", |
| 62 | + "Mozilla/5.0 (X11; Linux x86_64; Storebot-Google/1.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Safari/537.36", |
| 63 | + "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012; Storebot-Google/1.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36", |
| 64 | + "Mozilla/5.0 (compatible; Google-InspectionTool/1.0;)", |
| 65 | + "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0;)", |
| 66 | + "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; GoogleOther)", |
| 67 | + "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.3; +https://openai.com/gptbot)", |
| 68 | + "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot", |
| 69 | + "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/116.0.1938.76 Safari/537.36", |
| 70 | + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36; compatible; OAI-SearchBot/1.3; +https://openai.com/searchbot", |
| 71 | + "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)", |
| 72 | + ]; |
| 73 | + |
| 74 | + for ua in bots { |
| 75 | + assert!(state.is_bot(ua)) |
| 76 | + } |
| 77 | + |
| 78 | + let browsers = &[ |
| 79 | + "Mozilla/5.0 (Linux; Android 15; SM-S931B Build/AP3A.240905.015.A2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36", |
| 80 | + "Mozilla/5.0 (Linux; Android 15; SM-S931U Build/AP3A.240905.015.A2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36", |
| 81 | + "Mozilla/5.0 (Android 15; Mobile; SM-G556B/DS; rv:130.0) Gecko/130.0 Firefox/130.0", |
| 82 | + "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko", |
| 83 | + ]; |
| 84 | + |
| 85 | + for ua in browsers { |
| 86 | + assert!(!state.is_bot(ua)) |
| 87 | + } |
| 88 | + } |
| 89 | +} |
0 commit comments