srctree

Gregory Mullen parent 3abb4a2b b63a27bc
add some basic bot detection code

inlinesplit
build.zig added: 84, removed: 10, total 74
@@ -4,11 +4,20 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
 
//if (b.args) |args| for (args) |arg| std.debug.print("arg {s}\n", .{arg});
 
//std.debug.print("default: {s}\n", .{b.default_step.name});
 
// root build options
const template_path = b.option(std.Build.LazyPath, "template-path", "path for the templates generated at comptime") orelse b.path("examples/templates/");
const bot_detection = b.option(bool, "bot-detection", "path for the templates generated at comptime") orelse
false;
 
const ver = version(b);
const options = b.addOptions();
 
options.addOption([]const u8, "version", ver);
options.addOption(bool, "botdetection", true);
options.addOption(bool, "botdetection", bot_detection);
 
const verse_lib = b.addModule("verse", .{
.root_source_file = b.path("src/verse.zig"),
@@ -25,8 +34,6 @@ pub fn build(b: *std.Build) !void {
});
lib_unit_tests.root_module.addOptions("verse_buildopts", options);
 
const template_path = b.option(std.Build.LazyPath, "template-path", "path for the templates generated at comptime") orelse b.path("examples/templates/");
 
var compiler = Compiler.init(b);
compiler.addDir(template_path);
 
 
src/bot-detection.zig added: 84, removed: 10, total 74
@@ -3,10 +3,51 @@
//! It does something, what that something is? who know, but it's big!
 
bot: bool,
malicious: bool,
 
const BotDetection = @This();
 
pub fn init() BotDetection {}
pub fn init(r: *const Request) BotDetection {
if (r.user_agent == null) return .{ .bot = true, .malicious = true };
const ua = r.user_agent.?;
switch (ua.resolved) {
.bot => {
return .{
.bot = true,
.malicious = false,
};
},
.browser => |browser| {
switch (browser.name) {
.chrome => {
return .{
.bot = true,
.malicious = false,
};
},
else => {
return .{
.bot = true,
.malicious = false,
};
},
}
},
.script => {
return .{
.bot = true,
.malicious = false,
};
},
.unknown => {
return .{
.bot = true,
.malicious = std.mem.startsWith(u8, ua.string, "Mozilla/"),
};
},
}
comptime unreachable;
}
 
pub const Browsers = struct {
const Date = i64;
@@ -86,3 +127,4 @@ test Browsers {
 
const std = @import("std");
const UA = @import("user-agent.zig");
const Request = @import("request.zig");
 
src/request.zig added: 84, removed: 10, total 74
@@ -13,6 +13,7 @@ referer: ?Referer,
accept: ?Accept,
accept_encoding: Encoding = .default,
authorization: ?Authorization,
protocol: Protocol,
 
headers: Headers,
/// Default API, still unstable, but unlike to drastically change
@@ -91,6 +92,11 @@ pub const Methods = enum(u9) {
}
};
 
pub const Protocol = struct {
// TODO split name and version
str: []const u8,
};
 
fn initCommon(
a: Allocator,
remote_addr: RemoteAddr,
@@ -104,6 +110,7 @@ fn initCommon(
authorization: ?Authorization,
headers: Headers,
cookies: ?[]const u8,
proto: []const u8,
data: Data,
raw: RawReq,
) !Request {
@@ -127,6 +134,7 @@ fn initCommon(
.remote_addr = remote_addr,
.uri = uri,
.user_agent = if (ua) |u| .init(u) else null,
.protocol = .{ .str = proto },
};
}
 
@@ -142,6 +150,7 @@ pub fn initZWSGI(a: Allocator, zwsgi: *zWSGIRequest, data: Data) !Request {
var encoding: Encoding = Encoding.default;
var authorization: ?Authorization = null;
var cookie_header: ?[]const u8 = null;
var proto: []const u8 = "ERROR";
 
for (zwsgi.vars) |v| {
try headers.addCustom(v.key, v.val);
@@ -168,6 +177,8 @@ pub fn initZWSGI(a: Allocator, zwsgi: *zWSGIRequest, data: Data) !Request {
authorization = v.val;
} else if (eqlIgnoreCase("HTTP_COOKIE", v.key)) {
cookie_header = v.val;
} else if (eqlIgnoreCase("SERVER_PROTOCOL", v.key)) {
proto = v.val;
}
}
 
@@ -184,6 +195,7 @@ pub fn initZWSGI(a: Allocator, zwsgi: *zWSGIRequest, data: Data) !Request {
authorization,
headers,
cookie_header,
proto,
data,
.{ .zwsgi = zwsgi },
);
@@ -200,6 +212,7 @@ pub fn initHttp(a: Allocator, http: *std.http.Server.Request, data: Data) !Reque
var encoding: Encoding = Encoding.default;
var authorization: ?Authorization = null;
var cookie_header: ?[]const u8 = null;
const proto: []const u8 = @tagName(http.head.version);
 
while (itr.next()) |head| {
try headers.addCustom(head.name, head.value);
@@ -241,6 +254,7 @@ pub fn initHttp(a: Allocator, http: *std.http.Server.Request, data: Data) !Reque
authorization,
headers,
cookie_header,
proto,
data,
.{ .http = http },
);
 
src/user-agent.zig added: 84, removed: 10, total 74
@@ -8,6 +8,14 @@ resolved: Resolved,
 
const UserAgent = @This();
 
pub fn botDetectionDump(ua: UserAgent, r: *const Request) void {
if (comptime !BOTDETC_ENABLED) @compileError("Bot Detection is currently disabled");
 
const bd: BotDetection = .init(r);
std.debug.print("ua detection: {} \n", .{ua});
std.debug.print("bot detection: {} \n", .{bd});
}
 
pub const Resolved = union(enum) {
bot: Bot,
browser: Browser,
@@ -141,14 +149,14 @@ pub const Browser = struct {
version_string: []const u8 = "",
 
pub fn age(b: Browser) !i64 {
if (comptime !verse_buildopts.botdetection) @compileError("Bot Detection is currently disabled");
if (comptime !BOTDETC_ENABLED) @compileError("Bot Detection is currently disabled");
const versions = BotDetection.Browsers.Versions[@intFromEnum(b.name)];
if (b.version >= versions.len) return error.UnknownVersion;
return std.time.timestamp() - versions[b.version];
}
 
test age {
if (!verse_buildopts.botdetection) return error.SkipZigTest;
if (!BOTDETC_ENABLED) return error.SkipZigTest;
const browser = Browser{ .name = .chrome, .version = 134 };
try std.testing.expect(try browser.age() < 86400 * 3650); // breaks in 10 years, good luck future me!
try std.testing.expect(try browser.age() > 3148551);
@@ -184,12 +192,15 @@ pub fn init(ua_str: []const u8) UserAgent {
const Request = @import("request.zig");
const BotDetection = @import("bot-detection.zig");
 
const BOTDETC_ENABLED: bool = verse_buildopts.botdetection or builtin.is_test;
 
test UserAgent {
std.testing.refAllDecls(@This());
_ = &BotDetection;
}
 
const std = @import("std");
const builtin = @import("builtin");
const verse_buildopts = @import("verse_buildopts");
const startsWith = std.mem.startsWith;
const endsWith = std.mem.endsWith;