alamin655 commited on
Commit
60b2fcc
2 Parent(s): 15d52b5 e581de3

Merge branch 'rolling' into feat-disallow-user-to-search-via-lists

Browse files
.gitignore CHANGED
@@ -4,3 +4,4 @@ package-lock.json
4
  dump.rdb
5
  .vscode
6
  megalinter-reports/
 
 
4
  dump.rdb
5
  .vscode
6
  megalinter-reports/
7
+ dhat-heap.json
Cargo.lock CHANGED
@@ -288,12 +288,24 @@ version = "1.0.75"
288
  source = "registry+https://github.com/rust-lang/crates.io-index"
289
  checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
290
 
 
 
 
 
 
 
291
  [[package]]
292
  name = "askama_escape"
293
  version = "0.10.3"
294
  source = "registry+https://github.com/rust-lang/crates.io-index"
295
  checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
296
 
 
 
 
 
 
 
297
  [[package]]
298
  name = "async-trait"
299
  version = "0.1.73"
@@ -559,7 +571,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
559
  checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
560
  dependencies = [
561
  "bytes 1.4.0",
 
562
  "memchr",
 
 
 
563
  ]
564
 
565
  [[package]]
@@ -820,6 +836,22 @@ dependencies = [
820
  "syn 1.0.109",
821
  ]
822
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
  [[package]]
824
  name = "digest"
825
  version = "0.10.7"
@@ -1049,6 +1081,21 @@ version = "0.1.31"
1049
  source = "registry+https://github.com/rust-lang/crates.io-index"
1050
  checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
1051
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1052
  [[package]]
1053
  name = "futures-channel"
1054
  version = "0.3.28"
@@ -1056,6 +1103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1056
  checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
1057
  dependencies = [
1058
  "futures-core",
 
1059
  ]
1060
 
1061
  [[package]]
@@ -1070,10 +1118,38 @@ version = "0.1.8"
1070
  source = "registry+https://github.com/rust-lang/crates.io-index"
1071
  checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
1072
  dependencies = [
1073
- "futures",
1074
  "num_cpus",
1075
  ]
1076
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1077
  [[package]]
1078
  name = "futures-sink"
1079
  version = "0.3.28"
@@ -1092,10 +1168,16 @@ version = "0.3.28"
1092
  source = "registry+https://github.com/rust-lang/crates.io-index"
1093
  checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
1094
  dependencies = [
 
1095
  "futures-core",
 
 
 
1096
  "futures-task",
 
1097
  "pin-project-lite",
1098
  "pin-utils",
 
1099
  ]
1100
 
1101
  [[package]]
@@ -1152,7 +1234,7 @@ dependencies = [
1152
  "byteorder",
1153
  "bytes 0.4.12",
1154
  "fnv",
1155
- "futures",
1156
  "http 0.1.21",
1157
  "indexmap",
1158
  "log",
@@ -1270,7 +1352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1270
  checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
1271
  dependencies = [
1272
  "bytes 0.4.12",
1273
- "futures",
1274
  "http 0.1.21",
1275
  "tokio-buf",
1276
  ]
@@ -1317,7 +1399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1317
  checksum = "5c843caf6296fc1f93444735205af9ed4e109a539005abb2564ae1d6fad34c52"
1318
  dependencies = [
1319
  "bytes 0.4.12",
1320
- "futures",
1321
  "futures-cpupool",
1322
  "h2 0.1.26",
1323
  "http 0.1.21",
@@ -1371,7 +1453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1371
  checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f"
1372
  dependencies = [
1373
  "bytes 0.4.12",
1374
- "futures",
1375
  "hyper 0.12.36",
1376
  "native-tls",
1377
  "tokio-io",
@@ -1525,6 +1607,16 @@ version = "0.2.147"
1525
  source = "registry+https://github.com/rust-lang/crates.io-index"
1526
  checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
1527
 
 
 
 
 
 
 
 
 
 
 
1528
  [[package]]
1529
  name = "linux-raw-sys"
1530
  version = "0.4.5"
@@ -1653,6 +1745,15 @@ dependencies = [
1653
  "autocfg 1.1.0",
1654
  ]
1655
 
 
 
 
 
 
 
 
 
 
1656
  [[package]]
1657
  name = "mime"
1658
  version = "0.3.17"
@@ -1678,6 +1779,16 @@ dependencies = [
1678
  "adler",
1679
  ]
1680
 
 
 
 
 
 
 
 
 
 
 
1681
  [[package]]
1682
  name = "mio"
1683
  version = "0.6.23"
@@ -1721,6 +1832,20 @@ dependencies = [
1721
  "ws2_32-sys",
1722
  ]
1723
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1724
  [[package]]
1725
  name = "native-tls"
1726
  version = "0.2.11"
@@ -2076,6 +2201,26 @@ dependencies = [
2076
  "siphasher 0.3.11",
2077
  ]
2078
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2079
  [[package]]
2080
  name = "pin-project-lite"
2081
  version = "0.2.13"
@@ -2353,12 +2498,21 @@ version = "0.23.3"
2353
  source = "registry+https://github.com/rust-lang/crates.io-index"
2354
  checksum = "4f49cdc0bb3f412bf8e7d1bd90fe1d9eb10bc5c399ba90973c14662a27b3f8ba"
2355
  dependencies = [
 
 
 
2356
  "combine",
 
 
2357
  "itoa 1.0.9",
2358
  "percent-encoding 2.3.0",
 
2359
  "ryu",
2360
  "sha1_smol",
2361
  "socket2 0.4.9",
 
 
 
2362
  "url 2.4.1",
2363
  ]
2364
 
@@ -2418,7 +2572,7 @@ dependencies = [
2418
  "cookie_store",
2419
  "encoding_rs",
2420
  "flate2",
2421
- "futures",
2422
  "http 0.1.21",
2423
  "hyper 0.12.36",
2424
  "hyper-tls 0.3.2",
@@ -2477,36 +2631,18 @@ dependencies = [
2477
  "winreg 0.50.0",
2478
  ]
2479
 
2480
- [[package]]
2481
- name = "rlua"
2482
- version = "0.19.7"
2483
- source = "registry+https://github.com/rust-lang/crates.io-index"
2484
- checksum = "5d33e5ba15c3d43178f283ed5863d4531e292fc0e56fb773f3bea45f18e3a42a"
2485
- dependencies = [
2486
- "bitflags 1.3.2",
2487
- "bstr",
2488
- "libc",
2489
- "num-traits",
2490
- "rlua-lua54-sys",
2491
- ]
2492
-
2493
- [[package]]
2494
- name = "rlua-lua54-sys"
2495
- version = "0.1.6"
2496
- source = "registry+https://github.com/rust-lang/crates.io-index"
2497
- checksum = "7aafabafe1895cb4a2be81a56d7ff3d46bf4b5d2f9cfdbea2ed404cdabe96474"
2498
- dependencies = [
2499
- "cc",
2500
- "libc",
2501
- "pkg-config",
2502
- ]
2503
-
2504
  [[package]]
2505
  name = "rustc-demangle"
2506
  version = "0.1.23"
2507
  source = "registry+https://github.com/rust-lang/crates.io-index"
2508
  checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
2509
 
 
 
 
 
 
 
2510
  [[package]]
2511
  name = "rustc_version"
2512
  version = "0.2.3"
@@ -2806,6 +2942,9 @@ name = "smallvec"
2806
  version = "1.11.0"
2807
  source = "registry+https://github.com/rust-lang/crates.io-index"
2808
  checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
 
 
 
2809
 
2810
  [[package]]
2811
  name = "socket2"
@@ -2947,6 +3086,16 @@ dependencies = [
2947
  "unicode-xid 0.2.4",
2948
  ]
2949
 
 
 
 
 
 
 
 
 
 
 
2950
  [[package]]
2951
  name = "tempfile"
2952
  version = "3.8.0"
@@ -3000,6 +3149,12 @@ dependencies = [
3000
  "syn 2.0.29",
3001
  ]
3002
 
 
 
 
 
 
 
3003
  [[package]]
3004
  name = "time"
3005
  version = "0.1.45"
@@ -3071,7 +3226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3071
  checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
3072
  dependencies = [
3073
  "bytes 0.4.12",
3074
- "futures",
3075
  "mio 0.6.23",
3076
  "num_cpus",
3077
  "tokio-current-thread",
@@ -3110,7 +3265,7 @@ checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
3110
  dependencies = [
3111
  "bytes 0.4.12",
3112
  "either",
3113
- "futures",
3114
  ]
3115
 
3116
  [[package]]
@@ -3119,7 +3274,7 @@ version = "0.1.7"
3119
  source = "registry+https://github.com/rust-lang/crates.io-index"
3120
  checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
3121
  dependencies = [
3122
- "futures",
3123
  "tokio-executor",
3124
  ]
3125
 
@@ -3130,7 +3285,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3130
  checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
3131
  dependencies = [
3132
  "crossbeam-utils 0.7.2",
3133
- "futures",
3134
  ]
3135
 
3136
  [[package]]
@@ -3140,7 +3295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3140
  checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
3141
  dependencies = [
3142
  "bytes 0.4.12",
3143
- "futures",
3144
  "log",
3145
  ]
3146
 
@@ -3172,7 +3327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3172
  checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
3173
  dependencies = [
3174
  "crossbeam-utils 0.7.2",
3175
- "futures",
3176
  "lazy_static",
3177
  "log",
3178
  "mio 0.6.23",
@@ -3184,6 +3339,17 @@ dependencies = [
3184
  "tokio-sync",
3185
  ]
3186
 
 
 
 
 
 
 
 
 
 
 
 
3187
  [[package]]
3188
  name = "tokio-sync"
3189
  version = "0.1.8"
@@ -3191,7 +3357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3191
  checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
3192
  dependencies = [
3193
  "fnv",
3194
- "futures",
3195
  ]
3196
 
3197
  [[package]]
@@ -3201,7 +3367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3201
  checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
3202
  dependencies = [
3203
  "bytes 0.4.12",
3204
- "futures",
3205
  "iovec",
3206
  "mio 0.6.23",
3207
  "tokio-io",
@@ -3217,7 +3383,7 @@ dependencies = [
3217
  "crossbeam-deque 0.7.4",
3218
  "crossbeam-queue",
3219
  "crossbeam-utils 0.7.2",
3220
- "futures",
3221
  "lazy_static",
3222
  "log",
3223
  "num_cpus",
@@ -3232,7 +3398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3232
  checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
3233
  dependencies = [
3234
  "crossbeam-utils 0.7.2",
3235
- "futures",
3236
  "slab",
3237
  "tokio-executor",
3238
  ]
@@ -3427,7 +3593,7 @@ version = "0.2.0"
3427
  source = "registry+https://github.com/rust-lang/crates.io-index"
3428
  checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230"
3429
  dependencies = [
3430
- "futures",
3431
  "log",
3432
  "try-lock",
3433
  ]
@@ -3536,24 +3702,29 @@ dependencies = [
3536
  "actix-cors",
3537
  "actix-files",
3538
  "actix-web",
 
3539
  "async-trait",
3540
  "criterion",
 
3541
  "env_logger",
3542
  "error-stack",
3543
  "fake-useragent",
 
3544
  "handlebars",
3545
  "log",
3546
  "md5",
 
 
3547
  "once_cell",
3548
  "rand 0.8.5",
3549
  "redis",
3550
  "regex",
3551
  "reqwest 0.11.20",
3552
- "rlua",
3553
  "rusty-hook",
3554
  "scraper",
3555
  "serde",
3556
  "serde_json",
 
3557
  "tempfile",
3558
  "tokio 1.32.0",
3559
  ]
 
288
  source = "registry+https://github.com/rust-lang/crates.io-index"
289
  checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
290
 
291
+ [[package]]
292
+ name = "arc-swap"
293
+ version = "1.6.0"
294
+ source = "registry+https://github.com/rust-lang/crates.io-index"
295
+ checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
296
+
297
  [[package]]
298
  name = "askama_escape"
299
  version = "0.10.3"
300
  source = "registry+https://github.com/rust-lang/crates.io-index"
301
  checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
302
 
303
+ [[package]]
304
+ name = "async-once-cell"
305
+ version = "0.5.3"
306
+ source = "registry+https://github.com/rust-lang/crates.io-index"
307
+ checksum = "9338790e78aa95a416786ec8389546c4b6a1dfc3dc36071ed9518a9413a542eb"
308
+
309
  [[package]]
310
  name = "async-trait"
311
  version = "0.1.73"
 
571
  checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
572
  dependencies = [
573
  "bytes 1.4.0",
574
+ "futures-core",
575
  "memchr",
576
+ "pin-project-lite",
577
+ "tokio 1.32.0",
578
+ "tokio-util",
579
  ]
580
 
581
  [[package]]
 
836
  "syn 1.0.109",
837
  ]
838
 
839
+ [[package]]
840
+ name = "dhat"
841
+ version = "0.3.2"
842
+ source = "registry+https://github.com/rust-lang/crates.io-index"
843
+ checksum = "4f2aaf837aaf456f6706cb46386ba8dffd4013a757e36f4ea05c20dd46b209a3"
844
+ dependencies = [
845
+ "backtrace",
846
+ "lazy_static",
847
+ "mintex",
848
+ "parking_lot 0.12.1",
849
+ "rustc-hash",
850
+ "serde",
851
+ "serde_json",
852
+ "thousands",
853
+ ]
854
+
855
  [[package]]
856
  name = "digest"
857
  version = "0.10.7"
 
1081
  source = "registry+https://github.com/rust-lang/crates.io-index"
1082
  checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
1083
 
1084
+ [[package]]
1085
+ name = "futures"
1086
+ version = "0.3.28"
1087
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1088
+ checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
1089
+ dependencies = [
1090
+ "futures-channel",
1091
+ "futures-core",
1092
+ "futures-executor",
1093
+ "futures-io",
1094
+ "futures-sink",
1095
+ "futures-task",
1096
+ "futures-util",
1097
+ ]
1098
+
1099
  [[package]]
1100
  name = "futures-channel"
1101
  version = "0.3.28"
 
1103
  checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
1104
  dependencies = [
1105
  "futures-core",
1106
+ "futures-sink",
1107
  ]
1108
 
1109
  [[package]]
 
1118
  source = "registry+https://github.com/rust-lang/crates.io-index"
1119
  checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
1120
  dependencies = [
1121
+ "futures 0.1.31",
1122
  "num_cpus",
1123
  ]
1124
 
1125
+ [[package]]
1126
+ name = "futures-executor"
1127
+ version = "0.3.28"
1128
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1129
+ checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
1130
+ dependencies = [
1131
+ "futures-core",
1132
+ "futures-task",
1133
+ "futures-util",
1134
+ ]
1135
+
1136
+ [[package]]
1137
+ name = "futures-io"
1138
+ version = "0.3.28"
1139
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1140
+ checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
1141
+
1142
+ [[package]]
1143
+ name = "futures-macro"
1144
+ version = "0.3.28"
1145
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1146
+ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
1147
+ dependencies = [
1148
+ "proc-macro2 1.0.66",
1149
+ "quote 1.0.33",
1150
+ "syn 2.0.29",
1151
+ ]
1152
+
1153
  [[package]]
1154
  name = "futures-sink"
1155
  version = "0.3.28"
 
1168
  source = "registry+https://github.com/rust-lang/crates.io-index"
1169
  checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
1170
  dependencies = [
1171
+ "futures-channel",
1172
  "futures-core",
1173
+ "futures-io",
1174
+ "futures-macro",
1175
+ "futures-sink",
1176
  "futures-task",
1177
+ "memchr",
1178
  "pin-project-lite",
1179
  "pin-utils",
1180
+ "slab",
1181
  ]
1182
 
1183
  [[package]]
 
1234
  "byteorder",
1235
  "bytes 0.4.12",
1236
  "fnv",
1237
+ "futures 0.1.31",
1238
  "http 0.1.21",
1239
  "indexmap",
1240
  "log",
 
1352
  checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
1353
  dependencies = [
1354
  "bytes 0.4.12",
1355
+ "futures 0.1.31",
1356
  "http 0.1.21",
1357
  "tokio-buf",
1358
  ]
 
1399
  checksum = "5c843caf6296fc1f93444735205af9ed4e109a539005abb2564ae1d6fad34c52"
1400
  dependencies = [
1401
  "bytes 0.4.12",
1402
+ "futures 0.1.31",
1403
  "futures-cpupool",
1404
  "h2 0.1.26",
1405
  "http 0.1.21",
 
1453
  checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f"
1454
  dependencies = [
1455
  "bytes 0.4.12",
1456
+ "futures 0.1.31",
1457
  "hyper 0.12.36",
1458
  "native-tls",
1459
  "tokio-io",
 
1607
  source = "registry+https://github.com/rust-lang/crates.io-index"
1608
  checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
1609
 
1610
+ [[package]]
1611
+ name = "libmimalloc-sys"
1612
+ version = "0.1.34"
1613
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1614
+ checksum = "25d058a81af0d1c22d7a1c948576bee6d673f7af3c0f35564abd6c81122f513d"
1615
+ dependencies = [
1616
+ "cc",
1617
+ "libc",
1618
+ ]
1619
+
1620
  [[package]]
1621
  name = "linux-raw-sys"
1622
  version = "0.4.5"
 
1745
  "autocfg 1.1.0",
1746
  ]
1747
 
1748
+ [[package]]
1749
+ name = "mimalloc"
1750
+ version = "0.1.38"
1751
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1752
+ checksum = "972e5f23f6716f62665760b0f4cbf592576a80c7b879ba9beaafc0e558894127"
1753
+ dependencies = [
1754
+ "libmimalloc-sys",
1755
+ ]
1756
+
1757
  [[package]]
1758
  name = "mime"
1759
  version = "0.3.17"
 
1779
  "adler",
1780
  ]
1781
 
1782
+ [[package]]
1783
+ name = "mintex"
1784
+ version = "0.1.2"
1785
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1786
+ checksum = "fd7c5ba1c3b5a23418d7bbf98c71c3d4946a0125002129231da8d6b723d559cb"
1787
+ dependencies = [
1788
+ "once_cell",
1789
+ "sys-info",
1790
+ ]
1791
+
1792
  [[package]]
1793
  name = "mio"
1794
  version = "0.6.23"
 
1832
  "ws2_32-sys",
1833
  ]
1834
 
1835
+ [[package]]
1836
+ name = "mlua"
1837
+ version = "0.8.10"
1838
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1839
+ checksum = "0bb37b0ba91f017aa7ca2b98ef99496827770cd635b4a932a6047c5b4bbe678e"
1840
+ dependencies = [
1841
+ "bstr",
1842
+ "cc",
1843
+ "num-traits",
1844
+ "once_cell",
1845
+ "pkg-config",
1846
+ "rustc-hash",
1847
+ ]
1848
+
1849
  [[package]]
1850
  name = "native-tls"
1851
  version = "0.2.11"
 
2201
  "siphasher 0.3.11",
2202
  ]
2203
 
2204
+ [[package]]
2205
+ name = "pin-project"
2206
+ version = "1.1.3"
2207
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2208
+ checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
2209
+ dependencies = [
2210
+ "pin-project-internal",
2211
+ ]
2212
+
2213
+ [[package]]
2214
+ name = "pin-project-internal"
2215
+ version = "1.1.3"
2216
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2217
+ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
2218
+ dependencies = [
2219
+ "proc-macro2 1.0.66",
2220
+ "quote 1.0.33",
2221
+ "syn 2.0.29",
2222
+ ]
2223
+
2224
  [[package]]
2225
  name = "pin-project-lite"
2226
  version = "0.2.13"
 
2498
  source = "registry+https://github.com/rust-lang/crates.io-index"
2499
  checksum = "4f49cdc0bb3f412bf8e7d1bd90fe1d9eb10bc5c399ba90973c14662a27b3f8ba"
2500
  dependencies = [
2501
+ "arc-swap",
2502
+ "async-trait",
2503
+ "bytes 1.4.0",
2504
  "combine",
2505
+ "futures 0.3.28",
2506
+ "futures-util",
2507
  "itoa 1.0.9",
2508
  "percent-encoding 2.3.0",
2509
+ "pin-project-lite",
2510
  "ryu",
2511
  "sha1_smol",
2512
  "socket2 0.4.9",
2513
+ "tokio 1.32.0",
2514
+ "tokio-retry",
2515
+ "tokio-util",
2516
  "url 2.4.1",
2517
  ]
2518
 
 
2572
  "cookie_store",
2573
  "encoding_rs",
2574
  "flate2",
2575
+ "futures 0.1.31",
2576
  "http 0.1.21",
2577
  "hyper 0.12.36",
2578
  "hyper-tls 0.3.2",
 
2631
  "winreg 0.50.0",
2632
  ]
2633
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2634
  [[package]]
2635
  name = "rustc-demangle"
2636
  version = "0.1.23"
2637
  source = "registry+https://github.com/rust-lang/crates.io-index"
2638
  checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
2639
 
2640
+ [[package]]
2641
+ name = "rustc-hash"
2642
+ version = "1.1.0"
2643
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2644
+ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
2645
+
2646
  [[package]]
2647
  name = "rustc_version"
2648
  version = "0.2.3"
 
2942
  version = "1.11.0"
2943
  source = "registry+https://github.com/rust-lang/crates.io-index"
2944
  checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
2945
+ dependencies = [
2946
+ "serde",
2947
+ ]
2948
 
2949
  [[package]]
2950
  name = "socket2"
 
3086
  "unicode-xid 0.2.4",
3087
  ]
3088
 
3089
+ [[package]]
3090
+ name = "sys-info"
3091
+ version = "0.9.1"
3092
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3093
+ checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c"
3094
+ dependencies = [
3095
+ "cc",
3096
+ "libc",
3097
+ ]
3098
+
3099
  [[package]]
3100
  name = "tempfile"
3101
  version = "3.8.0"
 
3149
  "syn 2.0.29",
3150
  ]
3151
 
3152
+ [[package]]
3153
+ name = "thousands"
3154
+ version = "0.2.0"
3155
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3156
+ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
3157
+
3158
  [[package]]
3159
  name = "time"
3160
  version = "0.1.45"
 
3226
  checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
3227
  dependencies = [
3228
  "bytes 0.4.12",
3229
+ "futures 0.1.31",
3230
  "mio 0.6.23",
3231
  "num_cpus",
3232
  "tokio-current-thread",
 
3265
  dependencies = [
3266
  "bytes 0.4.12",
3267
  "either",
3268
+ "futures 0.1.31",
3269
  ]
3270
 
3271
  [[package]]
 
3274
  source = "registry+https://github.com/rust-lang/crates.io-index"
3275
  checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
3276
  dependencies = [
3277
+ "futures 0.1.31",
3278
  "tokio-executor",
3279
  ]
3280
 
 
3285
  checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
3286
  dependencies = [
3287
  "crossbeam-utils 0.7.2",
3288
+ "futures 0.1.31",
3289
  ]
3290
 
3291
  [[package]]
 
3295
  checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
3296
  dependencies = [
3297
  "bytes 0.4.12",
3298
+ "futures 0.1.31",
3299
  "log",
3300
  ]
3301
 
 
3327
  checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
3328
  dependencies = [
3329
  "crossbeam-utils 0.7.2",
3330
+ "futures 0.1.31",
3331
  "lazy_static",
3332
  "log",
3333
  "mio 0.6.23",
 
3339
  "tokio-sync",
3340
  ]
3341
 
3342
+ [[package]]
3343
+ name = "tokio-retry"
3344
+ version = "0.3.0"
3345
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3346
+ checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
3347
+ dependencies = [
3348
+ "pin-project",
3349
+ "rand 0.8.5",
3350
+ "tokio 1.32.0",
3351
+ ]
3352
+
3353
  [[package]]
3354
  name = "tokio-sync"
3355
  version = "0.1.8"
 
3357
  checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
3358
  dependencies = [
3359
  "fnv",
3360
+ "futures 0.1.31",
3361
  ]
3362
 
3363
  [[package]]
 
3367
  checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
3368
  dependencies = [
3369
  "bytes 0.4.12",
3370
+ "futures 0.1.31",
3371
  "iovec",
3372
  "mio 0.6.23",
3373
  "tokio-io",
 
3383
  "crossbeam-deque 0.7.4",
3384
  "crossbeam-queue",
3385
  "crossbeam-utils 0.7.2",
3386
+ "futures 0.1.31",
3387
  "lazy_static",
3388
  "log",
3389
  "num_cpus",
 
3398
  checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
3399
  dependencies = [
3400
  "crossbeam-utils 0.7.2",
3401
+ "futures 0.1.31",
3402
  "slab",
3403
  "tokio-executor",
3404
  ]
 
3593
  source = "registry+https://github.com/rust-lang/crates.io-index"
3594
  checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230"
3595
  dependencies = [
3596
+ "futures 0.1.31",
3597
  "log",
3598
  "try-lock",
3599
  ]
 
3702
  "actix-cors",
3703
  "actix-files",
3704
  "actix-web",
3705
+ "async-once-cell",
3706
  "async-trait",
3707
  "criterion",
3708
+ "dhat",
3709
  "env_logger",
3710
  "error-stack",
3711
  "fake-useragent",
3712
+ "futures 0.3.28",
3713
  "handlebars",
3714
  "log",
3715
  "md5",
3716
+ "mimalloc",
3717
+ "mlua",
3718
  "once_cell",
3719
  "rand 0.8.5",
3720
  "redis",
3721
  "regex",
3722
  "reqwest 0.11.20",
 
3723
  "rusty-hook",
3724
  "scraper",
3725
  "serde",
3726
  "serde_json",
3727
+ "smallvec 1.11.0",
3728
  "tempfile",
3729
  "tokio 1.32.0",
3730
  ]
Cargo.toml CHANGED
@@ -8,7 +8,7 @@ license = "AGPL-3.0"
8
 
9
  [dependencies]
10
  reqwest = {version="0.11.20",features=["json"]}
11
- tokio = {version="1.32.0",features=["full"]}
12
  serde = {version="1.0.188",features=["derive"]}
13
  handlebars = { version = "4.4.0", features = ["dir_source"] }
14
  scraper = {version="0.17.1"}
@@ -27,6 +27,11 @@ once_cell = {version="1.18.0"}
27
  error-stack = {version="0.4.0"}
28
  async-trait = {version="0.1.73"}
29
  regex = {version="1.9.4", features=["perf"]}
 
 
 
 
 
30
 
31
  [dev-dependencies]
32
  rusty-hook = "^0.11.2"
@@ -47,13 +52,17 @@ rpath = false
47
 
48
  [profile.release]
49
  opt-level = 3
50
- debug = false
 
51
  split-debuginfo = '...'
52
  debug-assertions = false
53
  overflow-checks = false
54
- lto = 'thin'
55
  panic = 'abort'
56
  incremental = false
57
- codegen-units = 16
58
  rpath = false
59
  strip = "debuginfo"
 
 
 
 
8
 
9
  [dependencies]
10
  reqwest = {version="0.11.20",features=["json"]}
11
+ tokio = {version="1.32.0",features=["rt-multi-thread","macros"]}
12
  serde = {version="1.0.188",features=["derive"]}
13
  handlebars = { version = "4.4.0", features = ["dir_source"] }
14
  scraper = {version="0.17.1"}
 
27
  error-stack = {version="0.4.0"}
28
  async-trait = {version="0.1.73"}
29
  regex = {version="1.9.4", features=["perf"]}
30
+ smallvec = {version="1.11.0", features=["union", "serde"]}
31
+ futures = {version="0.3.28"}
32
+ dhat = {version="0.3.2", optional = true}
33
+ mimalloc = { version = "0.1.38", default-features = false }
34
+ async-once-cell = {version="0.5.3"}
35
 
36
  [dev-dependencies]
37
  rusty-hook = "^0.11.2"
 
52
 
53
  [profile.release]
54
  opt-level = 3
55
+ debug = false # This should only be commented when testing with dhat profiler
56
+ # debug = 1 # This should only be uncommented when testing with dhat profiler
57
  split-debuginfo = '...'
58
  debug-assertions = false
59
  overflow-checks = false
60
+ lto = true
61
  panic = 'abort'
62
  incremental = false
63
+ codegen-units = 1
64
  rpath = false
65
  strip = "debuginfo"
66
+
67
+ [features]
68
+ dhat-heap = ["dep:dhat"]
Dockerfile CHANGED
@@ -19,7 +19,7 @@ COPY . .
19
  RUN cargo install --path .
20
 
21
  # We do not need the Rust toolchain to run the binary!
22
- FROM gcr.io/distroless/cc-debian11
23
  COPY --from=builder /app/public/ /opt/websurfx/public/
24
  COPY --from=builder /app/websurfx/config.lua /etc/xdg/websurfx/config.lua
25
  COPY --from=builder /usr/local/cargo/bin/* /usr/local/bin/
 
19
  RUN cargo install --path .
20
 
21
  # We do not need the Rust toolchain to run the binary!
22
+ FROM gcr.io/distroless/cc-debian12
23
  COPY --from=builder /app/public/ /opt/websurfx/public/
24
  COPY --from=builder /app/websurfx/config.lua /etc/xdg/websurfx/config.lua
25
  COPY --from=builder /usr/local/cargo/bin/* /usr/local/bin/
README.md CHANGED
@@ -5,7 +5,7 @@
5
  <b align="center"><a href="README.md">Readme</a></b> |
6
  <b><a href="https://discord.gg/SWnda7Mw5u">Discord</a></b> |
7
  <b><a href="https://github.com/neon-mmd/websurfx">GitHub</a></b> |
8
- <b><a href="./docs/README.md">Documentation</a></b>
9
  <br /><br />
10
  <a href="#">
11
  <img
 
5
  <b align="center"><a href="README.md">Readme</a></b> |
6
  <b><a href="https://discord.gg/SWnda7Mw5u">Discord</a></b> |
7
  <b><a href="https://github.com/neon-mmd/websurfx">GitHub</a></b> |
8
+ <b><a href="../../tree/HEAD/docs/">Documentation</a></b>
9
  <br /><br />
10
  <a href="#">
11
  <img
docs/installation.md CHANGED
@@ -109,7 +109,7 @@ colorscheme = "catppuccin-mocha" -- the colorscheme name which should be used fo
109
  theme = "simple" -- the theme name which should be used for the website
110
 
111
  -- ### Caching ###
112
- redis_connection_url = "redis://redis:6379" -- redis connection url address on which the client should connect on.
113
 
114
  -- ### Search Engines ###
115
  upstream_search_engines = { DuckDuckGo = true, Searx = false } -- select the upstream search engines from which the results should be fetched.
 
109
  theme = "simple" -- the theme name which should be used for the website
110
 
111
  -- ### Caching ###
112
+ redis_url = "redis://redis:6379" -- redis connection url address on which the client should connect on.
113
 
114
  -- ### Search Engines ###
115
  upstream_search_engines = { DuckDuckGo = true, Searx = false } -- select the upstream search engines from which the results should be fetched.
src/bin/websurfx.rs CHANGED
@@ -3,9 +3,19 @@
3
  //! This module contains the main function which handles the logging of the application to the
4
  //! stdout and handles the command line arguments provided and launches the `websurfx` server.
5
 
 
6
  use std::net::TcpListener;
7
  use websurfx::{config::parser::Config, run};
8
 
 
 
 
 
 
 
 
 
 
9
  /// The function that launches the main server and registers all the routes of the website.
10
  ///
11
  /// # Error
@@ -14,6 +24,10 @@ use websurfx::{config::parser::Config, run};
14
  /// available for being used for other applications.
15
  #[actix_web::main]
16
  async fn main() -> std::io::Result<()> {
 
 
 
 
17
  // Initialize the parsed config file.
18
  let config = Config::parse(false).unwrap();
19
 
 
3
  //! This module contains the main function which handles the logging of the application to the
4
  //! stdout and handles the command line arguments provided and launches the `websurfx` server.
5
 
6
+ use mimalloc::MiMalloc;
7
  use std::net::TcpListener;
8
  use websurfx::{config::parser::Config, run};
9
 
10
+ /// A dhat heap memory profiler
11
+ #[cfg(feature = "dhat-heap")]
12
+ #[global_allocator]
13
+ static ALLOC: dhat::Alloc = dhat::Alloc;
14
+
15
+ #[cfg(not(feature = "dhat-heap"))]
16
+ #[global_allocator]
17
+ static GLOBAL: MiMalloc = MiMalloc;
18
+
19
  /// The function that launches the main server and registers all the routes of the website.
20
  ///
21
  /// # Error
 
24
  /// available for being used for other applications.
25
  #[actix_web::main]
26
  async fn main() -> std::io::Result<()> {
27
+ // A dhat heap profiler initialization.
28
+ #[cfg(feature = "dhat-heap")]
29
+ let _profiler = dhat::Profiler::new_heap();
30
+
31
  // Initialize the parsed config file.
32
  let config = Config::parse(false).unwrap();
33
 
src/cache/cacher.rs CHANGED
@@ -1,17 +1,27 @@
1
  //! This module provides the functionality to cache the aggregated results fetched and aggregated
2
  //! from the upstream search engines in a json format.
3
 
 
 
4
  use md5::compute;
5
- use redis::{Client, Commands, Connection};
 
 
6
 
7
  /// A named struct which stores the redis Connection url address to which the client will
8
  /// connect to.
9
  ///
10
  /// # Fields
11
  ///
12
- /// * `redis_connection_url` - It stores the redis Connection url address.
 
 
 
 
13
  pub struct RedisCache {
14
- connection: Connection,
 
 
15
  }
16
 
17
  impl RedisCache {
@@ -19,11 +29,25 @@ impl RedisCache {
19
  ///
20
  /// # Arguments
21
  ///
22
- /// * `redis_connection_url` - It stores the redis Connection url address.
23
- pub fn new(redis_connection_url: String) -> Result<Self, Box<dyn std::error::Error>> {
 
 
 
 
 
24
  let client = Client::open(redis_connection_url)?;
25
- let connection = client.get_connection()?;
26
- let redis_cache = RedisCache { connection };
 
 
 
 
 
 
 
 
 
27
  Ok(redis_cache)
28
  }
29
 
@@ -32,7 +56,7 @@ impl RedisCache {
32
  /// # Arguments
33
  ///
34
  /// * `url` - It takes an url as string.
35
- fn hash_url(url: &str) -> String {
36
  format!("{:?}", compute(url))
37
  }
38
 
@@ -41,9 +65,42 @@ impl RedisCache {
41
  /// # Arguments
42
  ///
43
  /// * `url` - It takes an url as a string.
44
- pub fn cached_json(&mut self, url: &str) -> Result<String, Box<dyn std::error::Error>> {
45
- let hashed_url_string = Self::hash_url(url);
46
- Ok(self.connection.get(hashed_url_string)?)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
  /// A function which caches the results by using the hashed `url` as the key and
@@ -54,21 +111,45 @@ impl RedisCache {
54
  ///
55
  /// * `json_results` - It takes the json results string as an argument.
56
  /// * `url` - It takes the url as a String.
57
- pub fn cache_results(
58
  &mut self,
59
- json_results: String,
60
  url: &str,
61
- ) -> Result<(), Box<dyn std::error::Error>> {
62
- let hashed_url_string = Self::hash_url(url);
63
-
64
- // put results_json into cache
65
- self.connection.set(&hashed_url_string, json_results)?;
66
 
67
- // Set the TTL for the key to 60 seconds
68
- self.connection
69
- .expire::<String, u32>(hashed_url_string, 60)
70
- .unwrap();
71
 
72
- Ok(())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
  }
 
1
  //! This module provides the functionality to cache the aggregated results fetched and aggregated
2
  //! from the upstream search engines in a json format.
3
 
4
+ use error_stack::Report;
5
+ use futures::future::try_join_all;
6
  use md5::compute;
7
+ use redis::{aio::ConnectionManager, AsyncCommands, Client, RedisError};
8
+
9
+ use super::error::PoolError;
10
 
11
  /// A named struct which stores the redis Connection url address to which the client will
12
  /// connect to.
13
  ///
14
  /// # Fields
15
  ///
16
+ /// * `connection_pool` - It stores a pool of connections ready to be used.
17
+ /// * `pool_size` - It stores the size of the connection pool (in other words the number of
18
+ /// connections that should be stored in the pool).
19
+ /// * `current_connection` - It stores the index of which connection is being used at the moment.
20
+ #[derive(Clone)]
21
  pub struct RedisCache {
22
+ connection_pool: Vec<ConnectionManager>,
23
+ pool_size: u8,
24
+ current_connection: u8,
25
  }
26
 
27
  impl RedisCache {
 
29
  ///
30
  /// # Arguments
31
  ///
32
+ /// * `redis_connection_url` - It takes the redis Connection url address.
33
+ /// * `pool_size` - It takes the size of the connection pool (in other words the number of
34
+ /// connections that should be stored in the pool).
35
+ pub async fn new(
36
+ redis_connection_url: &str,
37
+ pool_size: u8,
38
+ ) -> Result<Self, Box<dyn std::error::Error>> {
39
  let client = Client::open(redis_connection_url)?;
40
+ let mut tasks: Vec<_> = Vec::new();
41
+
42
+ for _ in 0..pool_size {
43
+ tasks.push(client.get_tokio_connection_manager());
44
+ }
45
+
46
+ let redis_cache = RedisCache {
47
+ connection_pool: try_join_all(tasks).await?,
48
+ pool_size,
49
+ current_connection: Default::default(),
50
+ };
51
  Ok(redis_cache)
52
  }
53
 
 
56
  /// # Arguments
57
  ///
58
  /// * `url` - It takes an url as string.
59
+ fn hash_url(&self, url: &str) -> String {
60
  format!("{:?}", compute(url))
61
  }
62
 
 
65
  /// # Arguments
66
  ///
67
  /// * `url` - It takes an url as a string.
68
+ pub async fn cached_json(&mut self, url: &str) -> Result<String, Report<PoolError>> {
69
+ self.current_connection = Default::default();
70
+ let hashed_url_string: &str = &self.hash_url(url);
71
+
72
+ let mut result: Result<String, RedisError> = self.connection_pool
73
+ [self.current_connection as usize]
74
+ .get(hashed_url_string)
75
+ .await;
76
+
77
+ // Code to check whether the current connection being used is dropped with connection error
78
+ // or not. if it drops with the connection error then the current connection is replaced
79
+ // with a new connection from the pool which is then used to run the redis command then
80
+ // that connection is also checked whether it is dropped or not if it is not then the
81
+ // result is passed as a `Result` or else the same process repeats again and if all of the
82
+ // connections in the pool result in connection drop error then a custom pool error is
83
+ // returned.
84
+ loop {
85
+ match result {
86
+ Err(error) => match error.is_connection_dropped() {
87
+ true => {
88
+ self.current_connection += 1;
89
+ if self.current_connection == self.pool_size {
90
+ return Err(Report::new(
91
+ PoolError::PoolExhaustionWithConnectionDropError,
92
+ ));
93
+ }
94
+ result = self.connection_pool[self.current_connection as usize]
95
+ .get(hashed_url_string)
96
+ .await;
97
+ continue;
98
+ }
99
+ false => return Err(Report::new(PoolError::RedisError(error))),
100
+ },
101
+ Ok(res) => return Ok(res),
102
+ }
103
+ }
104
  }
105
 
106
  /// A function which caches the results by using the hashed `url` as the key and
 
111
  ///
112
  /// * `json_results` - It takes the json results string as an argument.
113
  /// * `url` - It takes the url as a String.
114
+ pub async fn cache_results(
115
  &mut self,
116
+ json_results: &str,
117
  url: &str,
118
+ ) -> Result<(), Report<PoolError>> {
119
+ self.current_connection = Default::default();
120
+ let hashed_url_string: &str = &self.hash_url(url);
 
 
121
 
122
+ let mut result: Result<(), RedisError> = self.connection_pool
123
+ [self.current_connection as usize]
124
+ .set_ex(hashed_url_string, json_results, 60)
125
+ .await;
126
 
127
+ // Code to check whether the current connection being used is dropped with connection error
128
+ // or not. if it drops with the connection error then the current connection is replaced
129
+ // with a new connection from the pool which is then used to run the redis command then
130
+ // that connection is also checked whether it is dropped or not if it is not then the
131
+ // result is passed as a `Result` or else the same process repeats again and if all of the
132
+ // connections in the pool result in connection drop error then a custom pool error is
133
+ // returned.
134
+ loop {
135
+ match result {
136
+ Err(error) => match error.is_connection_dropped() {
137
+ true => {
138
+ self.current_connection += 1;
139
+ if self.current_connection == self.pool_size {
140
+ return Err(Report::new(
141
+ PoolError::PoolExhaustionWithConnectionDropError,
142
+ ));
143
+ }
144
+ result = self.connection_pool[self.current_connection as usize]
145
+ .set_ex(hashed_url_string, json_results, 60)
146
+ .await;
147
+ continue;
148
+ }
149
+ false => return Err(Report::new(PoolError::RedisError(error))),
150
+ },
151
+ Ok(_) => return Ok(()),
152
+ }
153
+ }
154
  }
155
  }
src/cache/error.rs ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //! This module provides the error enum to handle different errors associated while requesting data from
2
+ //! the redis server using an async connection pool.
3
+ use std::fmt;
4
+
5
+ use redis::RedisError;
6
+
7
+ /// A custom error type used for handling redis async pool associated errors.
8
+ ///
9
+ /// This enum provides variants three different categories of errors:
10
+ /// * `RedisError` - This variant handles all errors related to `RedisError`,
11
+ /// * `PoolExhaustionWithConnectionDropError` - This variant handles the error
12
+ /// which occurs when all the connections in the connection pool return a connection
13
+ /// dropped redis error.
14
+ #[derive(Debug)]
15
+ pub enum PoolError {
16
+ RedisError(RedisError),
17
+ PoolExhaustionWithConnectionDropError,
18
+ }
19
+
20
+ impl fmt::Display for PoolError {
21
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22
+ match self {
23
+ PoolError::RedisError(redis_error) => {
24
+ if let Some(detail) = redis_error.detail() {
25
+ write!(f, "{}", detail)
26
+ } else {
27
+ write!(f, "")
28
+ }
29
+ }
30
+ PoolError::PoolExhaustionWithConnectionDropError => {
31
+ write!(
32
+ f,
33
+ "Error all connections from the pool dropped with connection error"
34
+ )
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ impl error_stack::Context for PoolError {}
src/cache/mod.rs CHANGED
@@ -1 +1,2 @@
1
  pub mod cacher;
 
 
1
  pub mod cacher;
2
+ pub mod error;
src/config/parser.rs CHANGED
@@ -5,7 +5,7 @@ use crate::handler::paths::{file_path, FileType};
5
 
6
  use super::parser_models::Style;
7
  use log::LevelFilter;
8
- use rlua::Lua;
9
  use std::{collections::HashMap, fs, thread::available_parallelism};
10
 
11
  /// A named struct which stores the parsed config file options.
@@ -64,30 +64,31 @@ impl Config {
64
  /// or io error if the config.lua file doesn't exists otherwise it returns a newly constructed
65
  /// Config struct with all the parsed config options from the parsed config file.
66
  pub fn parse(logging_initialized: bool) -> Result<Self, Box<dyn std::error::Error>> {
67
- Lua::new().context(|context| -> Result<Self, Box<dyn std::error::Error>> {
68
- let globals = context.globals();
69
 
70
- context
71
- .load(&fs::read_to_string(file_path(FileType::Config)?)?)
72
- .exec()?;
73
 
74
- let parsed_threads: u8 = globals.get::<_, u8>("threads")?;
75
 
76
- let debug: bool = globals.get::<_, bool>("debug")?;
77
- let logging:bool= globals.get::<_, bool>("logging")?;
78
 
79
- if !logging_initialized {
80
- set_logging_level(debug, logging);
81
- }
82
 
83
- let threads: u8 = if parsed_threads == 0 {
84
- let total_num_of_threads: usize = available_parallelism()?.get() / 2;
85
- log::error!("Config Error: The value of `threads` option should be a non zero positive integer");
86
- log::error!("Falling back to using {} threads", total_num_of_threads);
87
- total_num_of_threads as u8
88
- } else {
89
- parsed_threads
90
- };
 
 
91
 
92
  let parsed_safe_search:u8 = globals.get::<_,u8>("safe_search")?;
93
  let safe_search: u8 = match parsed_safe_search {
 
5
 
6
  use super::parser_models::Style;
7
  use log::LevelFilter;
8
+ use mlua::Lua;
9
  use std::{collections::HashMap, fs, thread::available_parallelism};
10
 
11
  /// A named struct which stores the parsed config file options.
 
64
  /// or io error if the config.lua file doesn't exists otherwise it returns a newly constructed
65
  /// Config struct with all the parsed config options from the parsed config file.
66
  pub fn parse(logging_initialized: bool) -> Result<Self, Box<dyn std::error::Error>> {
67
+ let lua = Lua::new();
68
+ let globals = lua.globals();
69
 
70
+ lua.load(&fs::read_to_string(file_path(FileType::Config)?)?)
71
+ .exec()?;
 
72
 
73
+ let parsed_threads: u8 = globals.get::<_, u8>("threads")?;
74
 
75
+ let debug: bool = globals.get::<_, bool>("debug")?;
76
+ let logging: bool = globals.get::<_, bool>("logging")?;
77
 
78
+ if !logging_initialized {
79
+ set_logging_level(debug, logging);
80
+ }
81
 
82
+ let threads: u8 = if parsed_threads == 0 {
83
+ let total_num_of_threads: usize = available_parallelism()?.get() / 2;
84
+ log::error!(
85
+ "Config Error: The value of `threads` option should be a non zero positive integer"
86
+ );
87
+ log::error!("Falling back to using {} threads", total_num_of_threads);
88
+ total_num_of_threads as u8
89
+ } else {
90
+ parsed_threads
91
+ };
92
 
93
  let parsed_safe_search:u8 = globals.get::<_,u8>("safe_search")?;
94
  let safe_search: u8 = match parsed_safe_search {
src/engines/duckduckgo.rs CHANGED
@@ -4,14 +4,14 @@
4
 
5
  use std::collections::HashMap;
6
 
7
- use reqwest::header::{HeaderMap, CONTENT_TYPE, COOKIE, REFERER, USER_AGENT};
8
  use scraper::{Html, Selector};
9
 
10
  use crate::results::aggregation_models::SearchResult;
11
 
12
  use super::engine_models::{EngineError, SearchEngine};
13
 
14
- use error_stack::{IntoReport, Report, Result, ResultExt};
15
 
16
  /// A new DuckDuckGo engine type defined in-order to implement the `SearchEngine` trait which allows to
17
  /// reduce code duplication as well as allows to create vector of different search engines easily.
@@ -39,9 +39,9 @@ impl SearchEngine for DuckDuckGo {
39
  /// or HeaderMap fails to initialize.
40
  async fn results(
41
  &self,
42
- query: String,
43
  page: u32,
44
- user_agent: String,
45
  request_timeout: u8,
46
  _safe_search: u8,
47
  ) -> Result<HashMap<String, SearchResult>, EngineError> {
@@ -62,38 +62,19 @@ impl SearchEngine for DuckDuckGo {
62
  };
63
 
64
  // initializing HeaderMap and adding appropriate headers.
65
- let mut header_map = HeaderMap::new();
66
- header_map.insert(
67
- USER_AGENT,
68
- user_agent
69
- .parse()
70
- .into_report()
71
- .change_context(EngineError::UnexpectedError)?,
72
- );
73
- header_map.insert(
74
- REFERER,
75
- "https://google.com/"
76
- .parse()
77
- .into_report()
78
- .change_context(EngineError::UnexpectedError)?,
79
- );
80
- header_map.insert(
81
- CONTENT_TYPE,
82
- "application/x-www-form-urlencoded"
83
- .parse()
84
- .into_report()
85
- .change_context(EngineError::UnexpectedError)?,
86
- );
87
- header_map.insert(
88
- COOKIE,
89
- "kl=wt-wt"
90
- .parse()
91
- .into_report()
92
- .change_context(EngineError::UnexpectedError)?,
93
- );
94
 
95
  let document: Html = Html::parse_document(
96
- &DuckDuckGo::fetch_html_from_upstream(self, url, header_map, request_timeout).await?,
97
  );
98
 
99
  let no_result: Selector = Selector::parse(".no-results")
@@ -127,8 +108,7 @@ impl SearchEngine for DuckDuckGo {
127
  .next()
128
  .unwrap()
129
  .inner_html()
130
- .trim()
131
- .to_string(),
132
  format!(
133
  "https://{}",
134
  result
@@ -137,15 +117,15 @@ impl SearchEngine for DuckDuckGo {
137
  .unwrap()
138
  .inner_html()
139
  .trim()
140
- ),
 
141
  result
142
  .select(&result_desc)
143
  .next()
144
  .unwrap()
145
  .inner_html()
146
- .trim()
147
- .to_string(),
148
- vec!["duckduckgo".to_string()],
149
  )
150
  })
151
  .map(|search_result| (search_result.url.clone(), search_result))
 
4
 
5
  use std::collections::HashMap;
6
 
7
+ use reqwest::header::HeaderMap;
8
  use scraper::{Html, Selector};
9
 
10
  use crate::results::aggregation_models::SearchResult;
11
 
12
  use super::engine_models::{EngineError, SearchEngine};
13
 
14
+ use error_stack::{Report, Result, ResultExt};
15
 
16
  /// A new DuckDuckGo engine type defined in-order to implement the `SearchEngine` trait which allows to
17
  /// reduce code duplication as well as allows to create vector of different search engines easily.
 
39
  /// or HeaderMap fails to initialize.
40
  async fn results(
41
  &self,
42
+ query: &str,
43
  page: u32,
44
+ user_agent: &str,
45
  request_timeout: u8,
46
  _safe_search: u8,
47
  ) -> Result<HashMap<String, SearchResult>, EngineError> {
 
62
  };
63
 
64
  // initializing HeaderMap and adding appropriate headers.
65
+ let header_map = HeaderMap::try_from(&HashMap::from([
66
+ ("USER_AGENT".to_string(), user_agent.to_string()),
67
+ ("REFERER".to_string(), "https://google.com/".to_string()),
68
+ (
69
+ "CONTENT_TYPE".to_string(),
70
+ "application/x-www-form-urlencoded".to_string(),
71
+ ),
72
+ ("COOKIE".to_string(), "kl=wt-wt".to_string()),
73
+ ]))
74
+ .change_context(EngineError::UnexpectedError)?;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
  let document: Html = Html::parse_document(
77
+ &DuckDuckGo::fetch_html_from_upstream(self, &url, header_map, request_timeout).await?,
78
  );
79
 
80
  let no_result: Selector = Selector::parse(".no-results")
 
108
  .next()
109
  .unwrap()
110
  .inner_html()
111
+ .trim(),
 
112
  format!(
113
  "https://{}",
114
  result
 
117
  .unwrap()
118
  .inner_html()
119
  .trim()
120
+ )
121
+ .as_str(),
122
  result
123
  .select(&result_desc)
124
  .next()
125
  .unwrap()
126
  .inner_html()
127
+ .trim(),
128
+ &["duckduckgo"],
 
129
  )
130
  })
131
  .map(|search_result| (search_result.url.clone(), search_result))
src/engines/engine_models.rs CHANGED
@@ -2,7 +2,7 @@
2
  //! the upstream search engines with the search query provided by the user.
3
 
4
  use crate::results::aggregation_models::SearchResult;
5
- use error_stack::{IntoReport, Result, ResultExt};
6
  use std::{collections::HashMap, fmt, time::Duration};
7
 
8
  /// A custom error type used for handle engine associated errors.
@@ -48,7 +48,7 @@ impl error_stack::Context for EngineError {}
48
  pub trait SearchEngine: Sync + Send {
49
  async fn fetch_html_from_upstream(
50
  &self,
51
- url: String,
52
  header_map: reqwest::header::HeaderMap,
53
  request_timeout: u8,
54
  ) -> Result<String, EngineError> {
@@ -59,19 +59,17 @@ pub trait SearchEngine: Sync + Send {
59
  .headers(header_map) // add spoofed headers to emulate human behavior
60
  .send()
61
  .await
62
- .into_report()
63
  .change_context(EngineError::RequestError)?
64
  .text()
65
  .await
66
- .into_report()
67
  .change_context(EngineError::RequestError)?)
68
  }
69
 
70
  async fn results(
71
  &self,
72
- query: String,
73
  page: u32,
74
- user_agent: String,
75
  request_timeout: u8,
76
  safe_search: u8,
77
  ) -> Result<HashMap<String, SearchResult>, EngineError>;
 
2
  //! the upstream search engines with the search query provided by the user.
3
 
4
  use crate::results::aggregation_models::SearchResult;
5
+ use error_stack::{Result, ResultExt};
6
  use std::{collections::HashMap, fmt, time::Duration};
7
 
8
  /// A custom error type used for handle engine associated errors.
 
48
  pub trait SearchEngine: Sync + Send {
49
  async fn fetch_html_from_upstream(
50
  &self,
51
+ url: &str,
52
  header_map: reqwest::header::HeaderMap,
53
  request_timeout: u8,
54
  ) -> Result<String, EngineError> {
 
59
  .headers(header_map) // add spoofed headers to emulate human behavior
60
  .send()
61
  .await
 
62
  .change_context(EngineError::RequestError)?
63
  .text()
64
  .await
 
65
  .change_context(EngineError::RequestError)?)
66
  }
67
 
68
  async fn results(
69
  &self,
70
+ query: &str,
71
  page: u32,
72
+ user_agent: &str,
73
  request_timeout: u8,
74
  safe_search: u8,
75
  ) -> Result<HashMap<String, SearchResult>, EngineError>;
src/engines/searx.rs CHANGED
@@ -2,14 +2,14 @@
2
  //! by querying the upstream searx search engine instance with user provided query and with a page
3
  //! number if provided.
4
 
5
- use reqwest::header::{HeaderMap, CONTENT_TYPE, COOKIE, REFERER, USER_AGENT};
6
  use scraper::{Html, Selector};
7
  use std::collections::HashMap;
8
 
9
  use crate::results::aggregation_models::SearchResult;
10
 
11
  use super::engine_models::{EngineError, SearchEngine};
12
- use error_stack::{IntoReport, Report, Result, ResultExt};
13
 
14
  /// A new Searx engine type defined in-order to implement the `SearchEngine` trait which allows to
15
  /// reduce code duplication as well as allows to create vector of different search engines easily.
@@ -38,9 +38,9 @@ impl SearchEngine for Searx {
38
 
39
  async fn results(
40
  &self,
41
- query: String,
42
  page: u32,
43
- user_agent: String,
44
  request_timeout: u8,
45
  mut safe_search: u8,
46
  ) -> Result<HashMap<String, SearchResult>, EngineError> {
@@ -60,32 +60,16 @@ impl SearchEngine for Searx {
60
  };
61
 
62
  // initializing headers and adding appropriate headers.
63
- let mut header_map = HeaderMap::new();
64
- header_map.insert(
65
- USER_AGENT,
66
- user_agent
67
- .parse()
68
- .into_report()
69
- .change_context(EngineError::UnexpectedError)?,
70
- );
71
- header_map.insert(
72
- REFERER,
73
- "https://google.com/"
74
- .parse()
75
- .into_report()
76
- .change_context(EngineError::UnexpectedError)?,
77
- );
78
- header_map.insert(
79
- CONTENT_TYPE,
80
- "application/x-www-form-urlencoded"
81
- .parse()
82
- .into_report()
83
- .change_context(EngineError::UnexpectedError)?,
84
- );
85
- header_map.insert(COOKIE, "categories=general; language=auto; locale=en; autocomplete=duckduckgo; image_proxy=1; method=POST; safesearch=2; theme=simple; results_on_new_tab=1; doi_resolver=oadoi.org; simple_style=auto; center_alignment=1; query_in_title=1; infinite_scroll=0; disabled_engines=; enabled_engines=\"archive is__general\\054yep__general\\054curlie__general\\054currency__general\\054ddg definitions__general\\054wikidata__general\\054duckduckgo__general\\054tineye__general\\054lingva__general\\054startpage__general\\054yahoo__general\\054wiby__general\\054marginalia__general\\054alexandria__general\\054wikibooks__general\\054wikiquote__general\\054wikisource__general\\054wikiversity__general\\054wikivoyage__general\\054dictzone__general\\054seznam__general\\054mojeek__general\\054naver__general\\054wikimini__general\\054brave__general\\054petalsearch__general\\054goo__general\"; disabled_plugins=; enabled_plugins=\"searx.plugins.hostname_replace\\054searx.plugins.oa_doi_rewrite\\054searx.plugins.vim_hotkeys\"; tokens=; maintab=on; enginetab=on".parse().into_report().change_context(EngineError::UnexpectedError)?);
86
 
87
  let document: Html = Html::parse_document(
88
- &Searx::fetch_html_from_upstream(self, url, header_map, request_timeout).await?,
89
  );
90
 
91
  let no_result: Selector = Selector::parse("#urls>.dialog-error>p")
@@ -126,24 +110,21 @@ impl SearchEngine for Searx {
126
  .next()
127
  .unwrap()
128
  .inner_html()
129
- .trim()
130
- .to_string(),
131
  result
132
  .select(&result_url)
133
  .next()
134
  .unwrap()
135
  .value()
136
  .attr("href")
137
- .unwrap()
138
- .to_string(),
139
  result
140
  .select(&result_desc)
141
  .next()
142
  .unwrap()
143
  .inner_html()
144
- .trim()
145
- .to_string(),
146
- vec!["searx".to_string()],
147
  )
148
  })
149
  .map(|search_result| (search_result.url.clone(), search_result))
 
2
  //! by querying the upstream searx search engine instance with user provided query and with a page
3
  //! number if provided.
4
 
5
+ use reqwest::header::HeaderMap;
6
  use scraper::{Html, Selector};
7
  use std::collections::HashMap;
8
 
9
  use crate::results::aggregation_models::SearchResult;
10
 
11
  use super::engine_models::{EngineError, SearchEngine};
12
+ use error_stack::{Report, Result, ResultExt};
13
 
14
  /// A new Searx engine type defined in-order to implement the `SearchEngine` trait which allows to
15
  /// reduce code duplication as well as allows to create vector of different search engines easily.
 
38
 
39
  async fn results(
40
  &self,
41
+ query: &str,
42
  page: u32,
43
+ user_agent: &str,
44
  request_timeout: u8,
45
  mut safe_search: u8,
46
  ) -> Result<HashMap<String, SearchResult>, EngineError> {
 
60
  };
61
 
62
  // initializing headers and adding appropriate headers.
63
+ let header_map = HeaderMap::try_from(&HashMap::from([
64
+ ("USER_AGENT".to_string(), user_agent.to_string()),
65
+ ("REFERER".to_string(), "https://google.com/".to_string()),
66
+ ("CONTENT_TYPE".to_string(), "application/x-www-form-urlencoded".to_string()),
67
+ ("COOKIE".to_string(), "categories=general; language=auto; locale=en; autocomplete=duckduckgo; image_proxy=1; method=POST; safesearch=2; theme=simple; results_on_new_tab=1; doi_resolver=oadoi.org; simple_style=auto; center_alignment=1; query_in_title=1; infinite_scroll=0; disabled_engines=; enabled_engines=\"archive is__general\\054yep__general\\054curlie__general\\054currency__general\\054ddg definitions__general\\054wikidata__general\\054duckduckgo__general\\054tineye__general\\054lingva__general\\054startpage__general\\054yahoo__general\\054wiby__general\\054marginalia__general\\054alexandria__general\\054wikibooks__general\\054wikiquote__general\\054wikisource__general\\054wikiversity__general\\054wikivoyage__general\\054dictzone__general\\054seznam__general\\054mojeek__general\\054naver__general\\054wikimini__general\\054brave__general\\054petalsearch__general\\054goo__general\"; disabled_plugins=; enabled_plugins=\"searx.plugins.hostname_replace\\054searx.plugins.oa_doi_rewrite\\054searx.plugins.vim_hotkeys\"; tokens=; maintab=on; enginetab=on".to_string())
68
+ ]))
69
+ .change_context(EngineError::UnexpectedError)?;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  let document: Html = Html::parse_document(
72
+ &Searx::fetch_html_from_upstream(self, &url, header_map, request_timeout).await?,
73
  );
74
 
75
  let no_result: Selector = Selector::parse("#urls>.dialog-error>p")
 
110
  .next()
111
  .unwrap()
112
  .inner_html()
113
+ .trim(),
 
114
  result
115
  .select(&result_url)
116
  .next()
117
  .unwrap()
118
  .value()
119
  .attr("href")
120
+ .unwrap(),
 
121
  result
122
  .select(&result_desc)
123
  .next()
124
  .unwrap()
125
  .inner_html()
126
+ .trim(),
127
+ &["searx"],
 
128
  )
129
  })
130
  .map(|search_result| (search_result.url.clone(), search_result))
src/handler/paths.rs CHANGED
@@ -4,6 +4,7 @@
4
  use std::collections::HashMap;
5
  use std::io::Error;
6
  use std::path::Path;
 
7
 
8
  // ------- Constants --------
9
  static PUBLIC_DIRECTORY_NAME: &str = "public";
@@ -20,57 +21,7 @@ pub enum FileType {
20
  Theme,
21
  }
22
 
23
- static FILE_PATHS_FOR_DIFF_FILE_TYPES: once_cell::sync::Lazy<HashMap<FileType, Vec<String>>> =
24
- once_cell::sync::Lazy::new(|| {
25
- HashMap::from([
26
- (
27
- FileType::Config,
28
- vec![
29
- format!(
30
- "{}/.config/{}/{}",
31
- std::env::var("HOME").unwrap(),
32
- COMMON_DIRECTORY_NAME,
33
- CONFIG_FILE_NAME
34
- ),
35
- format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME),
36
- format!("./{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME),
37
- ],
38
- ),
39
- (
40
- FileType::Theme,
41
- vec![
42
- format!("/opt/websurfx/{}/", PUBLIC_DIRECTORY_NAME),
43
- format!("./{}/", PUBLIC_DIRECTORY_NAME),
44
- ],
45
- ),
46
- (
47
- FileType::AllowList,
48
- vec![
49
- format!(
50
- "{}/.config/{}/{}",
51
- std::env::var("HOME").unwrap(),
52
- COMMON_DIRECTORY_NAME,
53
- ALLOWLIST_FILE_NAME
54
- ),
55
- format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, ALLOWLIST_FILE_NAME),
56
- format!("./{}/{}", COMMON_DIRECTORY_NAME, ALLOWLIST_FILE_NAME),
57
- ],
58
- ),
59
- (
60
- FileType::BlockList,
61
- vec![
62
- format!(
63
- "{}/.config/{}/{}",
64
- std::env::var("HOME").unwrap(),
65
- COMMON_DIRECTORY_NAME,
66
- BLOCKLIST_FILE_NAME
67
- ),
68
- format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, BLOCKLIST_FILE_NAME),
69
- format!("./{}/{}", COMMON_DIRECTORY_NAME, BLOCKLIST_FILE_NAME),
70
- ],
71
- ),
72
- ])
73
- });
74
 
75
  /// A helper function which returns an appropriate config file path checking if the config
76
  /// file exists on that path.
@@ -95,11 +46,64 @@ static FILE_PATHS_FOR_DIFF_FILE_TYPES: once_cell::sync::Lazy<HashMap<FileType, V
95
  /// 1. `/opt/websurfx` if it not present here then it fallbacks to the next one (2)
96
  /// 2. Under project folder ( or codebase in other words) if it is not present
97
  /// here then it returns an error as mentioned above.
98
- pub fn file_path(file_type: FileType) -> Result<String, Error> {
99
- let file_path = FILE_PATHS_FOR_DIFF_FILE_TYPES.get(&file_type).unwrap();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  for (idx, _) in file_path.iter().enumerate() {
101
  if Path::new(file_path[idx].as_str()).exists() {
102
- return Ok(file_path[idx].clone());
103
  }
104
  }
105
 
 
4
  use std::collections::HashMap;
5
  use std::io::Error;
6
  use std::path::Path;
7
+ use std::sync::OnceLock;
8
 
9
  // ------- Constants --------
10
  static PUBLIC_DIRECTORY_NAME: &str = "public";
 
21
  Theme,
22
  }
23
 
24
+ static FILE_PATHS_FOR_DIFF_FILE_TYPES: OnceLock<HashMap<FileType, Vec<String>>> = OnceLock::new();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  /// A helper function which returns an appropriate config file path checking if the config
27
  /// file exists on that path.
 
46
  /// 1. `/opt/websurfx` if it not present here then it fallbacks to the next one (2)
47
  /// 2. Under project folder ( or codebase in other words) if it is not present
48
  /// here then it returns an error as mentioned above.
49
+ pub fn file_path(file_type: FileType) -> Result<&'static str, Error> {
50
+ let file_path: &Vec<String> = FILE_PATHS_FOR_DIFF_FILE_TYPES
51
+ .get_or_init(|| {
52
+ HashMap::from([
53
+ (
54
+ FileType::Config,
55
+ vec![
56
+ format!(
57
+ "{}/.config/{}/{}",
58
+ std::env::var("HOME").unwrap(),
59
+ COMMON_DIRECTORY_NAME,
60
+ CONFIG_FILE_NAME
61
+ ),
62
+ format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME),
63
+ format!("./{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME),
64
+ ],
65
+ ),
66
+ (
67
+ FileType::Theme,
68
+ vec![
69
+ format!("/opt/websurfx/{}/", PUBLIC_DIRECTORY_NAME),
70
+ format!("./{}/", PUBLIC_DIRECTORY_NAME),
71
+ ],
72
+ ),
73
+ (
74
+ FileType::AllowList,
75
+ vec![
76
+ format!(
77
+ "{}/.config/{}/{}",
78
+ std::env::var("HOME").unwrap(),
79
+ COMMON_DIRECTORY_NAME,
80
+ ALLOWLIST_FILE_NAME
81
+ ),
82
+ format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, ALLOWLIST_FILE_NAME),
83
+ format!("./{}/{}", COMMON_DIRECTORY_NAME, ALLOWLIST_FILE_NAME),
84
+ ],
85
+ ),
86
+ (
87
+ FileType::BlockList,
88
+ vec![
89
+ format!(
90
+ "{}/.config/{}/{}",
91
+ std::env::var("HOME").unwrap(),
92
+ COMMON_DIRECTORY_NAME,
93
+ BLOCKLIST_FILE_NAME
94
+ ),
95
+ format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, BLOCKLIST_FILE_NAME),
96
+ format!("./{}/{}", COMMON_DIRECTORY_NAME, BLOCKLIST_FILE_NAME),
97
+ ],
98
+ ),
99
+ ])
100
+ })
101
+ .get(&file_type)
102
+ .unwrap();
103
+
104
  for (idx, _) in file_path.iter().enumerate() {
105
  if Path::new(file_path[idx].as_str()).exists() {
106
+ return Ok(std::mem::take(&mut &*file_path[idx]));
107
  }
108
  }
109
 
src/lib.rs CHANGED
@@ -14,7 +14,12 @@ use crate::server::routes;
14
 
15
  use actix_cors::Cors;
16
  use actix_files as fs;
17
- use actix_web::{dev::Server, http::header, middleware::Logger, web, App, HttpServer};
 
 
 
 
 
18
  use config::parser::Config;
19
  use handlebars::Handlebars;
20
  use handler::paths::{file_path, FileType};
@@ -42,7 +47,7 @@ use handler::paths::{file_path, FileType};
42
  pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> {
43
  let mut handlebars: Handlebars = Handlebars::new();
44
 
45
- let public_folder_path: String = file_path(FileType::Theme)?;
46
 
47
  handlebars
48
  .register_templates_directory(".html", format!("{}/templates", public_folder_path))
@@ -68,6 +73,7 @@ pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> {
68
  .app_data(web::Data::new(config.clone()))
69
  .wrap(cors)
70
  .wrap(Logger::default()) // added logging middleware for logging.
 
71
  // Serve images and static files (css and js files).
72
  .service(
73
  fs::Files::new("/static", format!("{}/static", public_folder_path))
 
14
 
15
  use actix_cors::Cors;
16
  use actix_files as fs;
17
+ use actix_web::{
18
+ dev::Server,
19
+ http::header,
20
+ middleware::{Compress, Logger},
21
+ web, App, HttpServer,
22
+ };
23
  use config::parser::Config;
24
  use handlebars::Handlebars;
25
  use handler::paths::{file_path, FileType};
 
47
  pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> {
48
  let mut handlebars: Handlebars = Handlebars::new();
49
 
50
+ let public_folder_path: &str = file_path(FileType::Theme)?;
51
 
52
  handlebars
53
  .register_templates_directory(".html", format!("{}/templates", public_folder_path))
 
73
  .app_data(web::Data::new(config.clone()))
74
  .wrap(cors)
75
  .wrap(Logger::default()) // added logging middleware for logging.
76
+ .wrap(Compress::default()) // compress request headers to reduce memory usage.
77
  // Serve images and static files (css and js files).
78
  .service(
79
  fs::Files::new("/static", format!("{}/static", public_folder_path))
src/results/aggregation_models.rs CHANGED
@@ -2,6 +2,7 @@
2
  //! data scraped from the upstream search engines.
3
 
4
  use serde::{Deserialize, Serialize};
 
5
 
6
  use crate::{config::parser_models::Style, engines::engine_models::EngineError};
7
 
@@ -16,13 +17,13 @@ use crate::{config::parser_models::Style, engines::engine_models::EngineError};
16
  /// (href url in html in simple words).
17
  /// * `description` - The description of the search result.
18
  /// * `engine` - The names of the upstream engines from which this results were provided.
19
- #[derive(Clone, Serialize, Deserialize)]
20
  #[serde(rename_all = "camelCase")]
21
  pub struct SearchResult {
22
  pub title: String,
23
  pub url: String,
24
  pub description: String,
25
- pub engine: Vec<String>,
26
  }
27
 
28
  impl SearchResult {
@@ -35,12 +36,12 @@ impl SearchResult {
35
  /// (href url in html in simple words).
36
  /// * `description` - The description of the search result.
37
  /// * `engine` - The names of the upstream engines from which this results were provided.
38
- pub fn new(title: String, url: String, description: String, engine: Vec<String>) -> Self {
39
  SearchResult {
40
- title,
41
- url,
42
- description,
43
- engine,
44
  }
45
  }
46
 
@@ -49,8 +50,8 @@ impl SearchResult {
49
  /// # Arguments
50
  ///
51
  /// * `engine` - Takes an engine name provided as a String.
52
- pub fn add_engines(&mut self, engine: String) {
53
- self.engine.push(engine)
54
  }
55
 
56
  /// A function which returns the engine name stored from the struct as a string.
@@ -58,13 +59,12 @@ impl SearchResult {
58
  /// # Returns
59
  ///
60
  /// An engine name stored as a string from the struct.
61
- pub fn engine(self) -> String {
62
- self.engine.get(0).unwrap().to_string()
63
  }
64
  }
65
 
66
- ///
67
- #[derive(Serialize, Deserialize)]
68
  pub struct EngineErrorInfo {
69
  pub error: String,
70
  pub engine: String,
@@ -72,18 +72,18 @@ pub struct EngineErrorInfo {
72
  }
73
 
74
  impl EngineErrorInfo {
75
- pub fn new(error: &EngineError, engine: String) -> Self {
76
  Self {
77
  error: match error {
78
- EngineError::RequestError => String::from("RequestError"),
79
- EngineError::EmptyResultSet => String::from("EmptyResultSet"),
80
- EngineError::UnexpectedError => String::from("UnexpectedError"),
81
  },
82
- engine,
83
  severity_color: match error {
84
- EngineError::RequestError => String::from("green"),
85
- EngineError::EmptyResultSet => String::from("blue"),
86
- EngineError::UnexpectedError => String::from("red"),
87
  },
88
  }
89
  }
@@ -127,10 +127,10 @@ impl SearchResults {
127
  /// * ``
128
  pub fn new(
129
  results: Vec<SearchResult>,
130
- page_query: String,
131
- engine_errors_info: Vec<EngineErrorInfo>,
132
  ) -> Self {
133
- SearchResults {
134
  results,
135
  page_query,
136
  style: Style::default(),
 
2
  //! data scraped from the upstream search engines.
3
 
4
  use serde::{Deserialize, Serialize};
5
+ use smallvec::SmallVec;
6
 
7
  use crate::{config::parser_models::Style, engines::engine_models::EngineError};
8
 
 
17
  /// (href url in html in simple words).
18
  /// * `description` - The description of the search result.
19
  /// * `engine` - The names of the upstream engines from which this results were provided.
20
+ #[derive(Clone, Serialize, Deserialize, Debug)]
21
  #[serde(rename_all = "camelCase")]
22
  pub struct SearchResult {
23
  pub title: String,
24
  pub url: String,
25
  pub description: String,
26
+ pub engine: SmallVec<[String; 0]>,
27
  }
28
 
29
  impl SearchResult {
 
36
  /// (href url in html in simple words).
37
  /// * `description` - The description of the search result.
38
  /// * `engine` - The names of the upstream engines from which this results were provided.
39
+ pub fn new(title: &str, url: &str, description: &str, engine: &[&str]) -> Self {
40
  SearchResult {
41
+ title: title.to_owned(),
42
+ url: url.to_owned(),
43
+ description: description.to_owned(),
44
+ engine: engine.iter().map(|name| name.to_string()).collect(),
45
  }
46
  }
47
 
 
50
  /// # Arguments
51
  ///
52
  /// * `engine` - Takes an engine name provided as a String.
53
+ pub fn add_engines(&mut self, engine: &str) {
54
+ self.engine.push(engine.to_owned())
55
  }
56
 
57
  /// A function which returns the engine name stored from the struct as a string.
 
59
  /// # Returns
60
  ///
61
  /// An engine name stored as a string from the struct.
62
+ pub fn engine(&mut self) -> String {
63
+ std::mem::take(&mut self.engine[0])
64
  }
65
  }
66
 
67
+ #[derive(Serialize, Deserialize, Clone)]
 
68
  pub struct EngineErrorInfo {
69
  pub error: String,
70
  pub engine: String,
 
72
  }
73
 
74
  impl EngineErrorInfo {
75
+ pub fn new(error: &EngineError, engine: &str) -> Self {
76
  Self {
77
  error: match error {
78
+ EngineError::RequestError => "RequestError".to_owned(),
79
+ EngineError::EmptyResultSet => "EmptyResultSet".to_owned(),
80
+ EngineError::UnexpectedError => "UnexpectedError".to_owned(),
81
  },
82
+ engine: engine.to_owned(),
83
  severity_color: match error {
84
+ EngineError::RequestError => "green".to_owned(),
85
+ EngineError::EmptyResultSet => "blue".to_owned(),
86
+ EngineError::UnexpectedError => "red".to_owned(),
87
  },
88
  }
89
  }
 
127
  /// * ``
128
  pub fn new(
129
  results: Vec<SearchResult>,
130
+ page_query: &str,
131
+ engine_errors_info: &[EngineErrorInfo],
132
  ) -> Self {
133
+ Self {
134
  results,
135
  page_query,
136
  style: Style::default(),
src/results/aggregator.rs CHANGED
@@ -64,15 +64,15 @@ type FutureVec = Vec<JoinHandle<Result<HashMap<String, SearchResult>, Report<Eng
64
  /// function in either `searx` or `duckduckgo` or both otherwise returns a `SearchResults struct`
65
  /// containing appropriate values.
66
  pub async fn aggregate(
67
- query: String,
68
  page: u32,
69
  random_delay: bool,
70
  debug: bool,
71
- upstream_search_engines: Vec<EngineHandler>,
72
  request_timeout: u8,
73
  safe_search: u8,
74
  ) -> Result<SearchResults, Box<dyn std::error::Error>> {
75
- let user_agent: String = random_user_agent();
76
 
77
  // Add a random delay before making the request.
78
  if random_delay || !debug {
@@ -81,16 +81,15 @@ pub async fn aggregate(
81
  tokio::time::sleep(Duration::from_secs(delay_secs)).await;
82
  }
83
 
84
- let mut names: Vec<&str> = vec![];
85
 
86
  // create tasks for upstream result fetching
87
  let mut tasks: FutureVec = FutureVec::new();
88
 
89
  for engine_handler in upstream_search_engines {
90
- let (name, search_engine) = engine_handler.into_name_engine();
91
  names.push(name);
92
- let query: String = query.clone();
93
- let user_agent: String = user_agent.clone();
94
  tasks.push(tokio::spawn(async move {
95
  search_engine
96
  .results(
@@ -117,7 +116,7 @@ pub async fn aggregate(
117
  let mut result_map: HashMap<String, SearchResult> = HashMap::new();
118
  let mut engine_errors_info: Vec<EngineErrorInfo> = Vec::new();
119
 
120
- let mut handle_error = |error: Report<EngineError>, engine_name: String| {
121
  log::error!("Engine Error: {:?}", error);
122
  engine_errors_info.push(EngineErrorInfo::new(
123
  error.downcast_ref::<EngineError>().unwrap(),
@@ -127,7 +126,7 @@ pub async fn aggregate(
127
 
128
  for _ in 0..responses.len() {
129
  let response = responses.pop().unwrap();
130
- let engine = names.pop().unwrap().to_string();
131
 
132
  if result_map.is_empty() {
133
  match response {
@@ -135,7 +134,7 @@ pub async fn aggregate(
135
  result_map = results.clone();
136
  }
137
  Err(error) => {
138
- handle_error(error, engine);
139
  }
140
  }
141
  continue;
@@ -147,13 +146,13 @@ pub async fn aggregate(
147
  result_map
148
  .entry(key)
149
  .and_modify(|result| {
150
- result.add_engines(engine.clone());
151
  })
152
  .or_insert_with(|| -> SearchResult { value });
153
  });
154
  }
155
  Err(error) => {
156
- handle_error(error, engine);
157
  }
158
  }
159
  }
@@ -177,11 +176,7 @@ pub async fn aggregate(
177
 
178
  let results: Vec<SearchResult> = result_map.into_values().collect();
179
 
180
- Ok(SearchResults::new(
181
- results,
182
- query.to_string(),
183
- engine_errors_info,
184
- ))
185
  }
186
 
187
  /// Filters a map of search results using a list of regex patterns.
@@ -212,7 +207,10 @@ pub fn filter_with_lists(
212
  || re.is_match(&search_result.description.to_lowercase())
213
  {
214
  // If the search result matches the regex pattern, move it from the original map to the resultant map
215
- resultant_map.insert(url.clone(), map_to_be_filtered.remove(&url).unwrap());
 
 
 
216
  }
217
  }
218
  }
@@ -223,6 +221,7 @@ pub fn filter_with_lists(
223
  #[cfg(test)]
224
  mod tests {
225
  use super::*;
 
226
  use std::collections::HashMap;
227
  use std::io::Write;
228
  use tempfile::NamedTempFile;
@@ -232,22 +231,22 @@ mod tests {
232
  // Create a map of search results to filter
233
  let mut map_to_be_filtered = HashMap::new();
234
  map_to_be_filtered.insert(
235
- "https://www.example.com".to_string(),
236
  SearchResult {
237
- title: "Example Domain".to_string(),
238
- url: "https://www.example.com".to_string(),
239
  description: "This domain is for use in illustrative examples in documents."
240
- .to_string(),
241
- engine: vec!["Google".to_string(), "Bing".to_string()],
242
  },
243
  );
244
  map_to_be_filtered.insert(
245
- "https://www.rust-lang.org/".to_string(),
246
  SearchResult {
247
- title: "Rust Programming Language".to_string(),
248
- url: "https://www.rust-lang.org/".to_string(),
249
- description: "A systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.".to_string(),
250
- engine: vec!["Google".to_string(), "DuckDuckGo".to_string()],
251
  },
252
  );
253
 
@@ -276,22 +275,22 @@ mod tests {
276
  fn test_filter_with_lists_wildcard() -> Result<(), Box<dyn std::error::Error>> {
277
  let mut map_to_be_filtered = HashMap::new();
278
  map_to_be_filtered.insert(
279
- "https://www.example.com".to_string(),
280
  SearchResult {
281
- title: "Example Domain".to_string(),
282
- url: "https://www.example.com".to_string(),
283
  description: "This domain is for use in illustrative examples in documents."
284
- .to_string(),
285
- engine: vec!["Google".to_string(), "Bing".to_string()],
286
  },
287
  );
288
  map_to_be_filtered.insert(
289
- "https://www.rust-lang.org/".to_string(),
290
  SearchResult {
291
- title: "Rust Programming Language".to_string(),
292
- url: "https://www.rust-lang.org/".to_string(),
293
- description: "A systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.".to_string(),
294
- engine: vec!["Google".to_string(), "DuckDuckGo".to_string()],
295
  },
296
  );
297
 
@@ -336,13 +335,13 @@ mod tests {
336
  fn test_filter_with_lists_invalid_regex() {
337
  let mut map_to_be_filtered = HashMap::new();
338
  map_to_be_filtered.insert(
339
- "https://www.example.com".to_string(),
340
  SearchResult {
341
- title: "Example Domain".to_string(),
342
- url: "https://www.example.com".to_string(),
343
  description: "This domain is for use in illustrative examples in documents."
344
- .to_string(),
345
- engine: vec!["Google".to_string(), "Bing".to_string()],
346
  },
347
  );
348
 
 
64
  /// function in either `searx` or `duckduckgo` or both otherwise returns a `SearchResults struct`
65
  /// containing appropriate values.
66
  pub async fn aggregate(
67
+ query: &str,
68
  page: u32,
69
  random_delay: bool,
70
  debug: bool,
71
+ upstream_search_engines: &[EngineHandler],
72
  request_timeout: u8,
73
  safe_search: u8,
74
  ) -> Result<SearchResults, Box<dyn std::error::Error>> {
75
+ let user_agent: &str = random_user_agent();
76
 
77
  // Add a random delay before making the request.
78
  if random_delay || !debug {
 
81
  tokio::time::sleep(Duration::from_secs(delay_secs)).await;
82
  }
83
 
84
+ let mut names: Vec<&str> = Vec::with_capacity(0);
85
 
86
  // create tasks for upstream result fetching
87
  let mut tasks: FutureVec = FutureVec::new();
88
 
89
  for engine_handler in upstream_search_engines {
90
+ let (name, search_engine) = engine_handler.to_owned().into_name_engine();
91
  names.push(name);
92
+ let query: String = query.to_owned();
 
93
  tasks.push(tokio::spawn(async move {
94
  search_engine
95
  .results(
 
116
  let mut result_map: HashMap<String, SearchResult> = HashMap::new();
117
  let mut engine_errors_info: Vec<EngineErrorInfo> = Vec::new();
118
 
119
+ let mut handle_error = |error: &Report<EngineError>, engine_name: &'static str| {
120
  log::error!("Engine Error: {:?}", error);
121
  engine_errors_info.push(EngineErrorInfo::new(
122
  error.downcast_ref::<EngineError>().unwrap(),
 
126
 
127
  for _ in 0..responses.len() {
128
  let response = responses.pop().unwrap();
129
+ let engine = names.pop().unwrap();
130
 
131
  if result_map.is_empty() {
132
  match response {
 
134
  result_map = results.clone();
135
  }
136
  Err(error) => {
137
+ handle_error(&error, engine);
138
  }
139
  }
140
  continue;
 
146
  result_map
147
  .entry(key)
148
  .and_modify(|result| {
149
+ result.add_engines(engine);
150
  })
151
  .or_insert_with(|| -> SearchResult { value });
152
  });
153
  }
154
  Err(error) => {
155
+ handle_error(&error, engine);
156
  }
157
  }
158
  }
 
176
 
177
  let results: Vec<SearchResult> = result_map.into_values().collect();
178
 
179
+ Ok(SearchResults::new(results, query, &engine_errors_info))
 
 
 
 
180
  }
181
 
182
  /// Filters a map of search results using a list of regex patterns.
 
207
  || re.is_match(&search_result.description.to_lowercase())
208
  {
209
  // If the search result matches the regex pattern, move it from the original map to the resultant map
210
+ resultant_map.insert(
211
+ url.to_owned(),
212
+ map_to_be_filtered.remove(&url.to_owned()).unwrap(),
213
+ );
214
  }
215
  }
216
  }
 
221
  #[cfg(test)]
222
  mod tests {
223
  use super::*;
224
+ use smallvec::smallvec;
225
  use std::collections::HashMap;
226
  use std::io::Write;
227
  use tempfile::NamedTempFile;
 
231
  // Create a map of search results to filter
232
  let mut map_to_be_filtered = HashMap::new();
233
  map_to_be_filtered.insert(
234
+ "https://www.example.com".to_owned(),
235
  SearchResult {
236
+ title: "Example Domain".to_owned(),
237
+ url: "https://www.example.com".to_owned(),
238
  description: "This domain is for use in illustrative examples in documents."
239
+ .to_owned(),
240
+ engine: smallvec!["Google".to_owned(), "Bing".to_owned()],
241
  },
242
  );
243
  map_to_be_filtered.insert(
244
+ "https://www.rust-lang.org/".to_owned(),
245
  SearchResult {
246
+ title: "Rust Programming Language".to_owned(),
247
+ url: "https://www.rust-lang.org/".to_owned(),
248
+ description: "A systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.".to_owned(),
249
+ engine: smallvec!["Google".to_owned(), "DuckDuckGo".to_owned()],
250
  },
251
  );
252
 
 
275
  fn test_filter_with_lists_wildcard() -> Result<(), Box<dyn std::error::Error>> {
276
  let mut map_to_be_filtered = HashMap::new();
277
  map_to_be_filtered.insert(
278
+ "https://www.example.com".to_owned(),
279
  SearchResult {
280
+ title: "Example Domain".to_owned(),
281
+ url: "https://www.example.com".to_owned(),
282
  description: "This domain is for use in illustrative examples in documents."
283
+ .to_owned(),
284
+ engine: smallvec!["Google".to_owned(), "Bing".to_owned()],
285
  },
286
  );
287
  map_to_be_filtered.insert(
288
+ "https://www.rust-lang.org/".to_owned(),
289
  SearchResult {
290
+ title: "Rust Programming Language".to_owned(),
291
+ url: "https://www.rust-lang.org/".to_owned(),
292
+ description: "A systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.".to_owned(),
293
+ engine: smallvec!["Google".to_owned(), "DuckDuckGo".to_owned()],
294
  },
295
  );
296
 
 
335
  fn test_filter_with_lists_invalid_regex() {
336
  let mut map_to_be_filtered = HashMap::new();
337
  map_to_be_filtered.insert(
338
+ "https://www.example.com".to_owned(),
339
  SearchResult {
340
+ title: "Example Domain".to_owned(),
341
+ url: "https://www.example.com".to_owned(),
342
  description: "This domain is for use in illustrative examples in documents."
343
+ .to_owned(),
344
+ engine: smallvec!["Google".to_owned(), "Bing".to_owned()],
345
  },
346
  );
347
 
src/results/user_agent.rs CHANGED
@@ -1,28 +1,32 @@
1
  //! This module provides the functionality to generate random user agent string.
2
 
 
 
3
  use fake_useragent::{Browsers, UserAgents, UserAgentsBuilder};
4
 
5
- static USER_AGENTS: once_cell::sync::Lazy<UserAgents> = once_cell::sync::Lazy::new(|| {
6
- UserAgentsBuilder::new()
7
- .cache(false)
8
- .dir("/tmp")
9
- .thread(1)
10
- .set_browsers(
11
- Browsers::new()
12
- .set_chrome()
13
- .set_safari()
14
- .set_edge()
15
- .set_firefox()
16
- .set_mozilla(),
17
- )
18
- .build()
19
- });
20
 
21
  /// A function to generate random user agent to improve privacy of the user.
22
  ///
23
  /// # Returns
24
  ///
25
  /// A randomly generated user agent string.
26
- pub fn random_user_agent() -> String {
27
- USER_AGENTS.random().to_string()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
 
1
  //! This module provides the functionality to generate random user agent string.
2
 
3
+ use std::sync::OnceLock;
4
+
5
  use fake_useragent::{Browsers, UserAgents, UserAgentsBuilder};
6
 
7
+ static USER_AGENTS: OnceLock<UserAgents> = OnceLock::new();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  /// A function to generate random user agent to improve privacy of the user.
10
  ///
11
  /// # Returns
12
  ///
13
  /// A randomly generated user agent string.
14
+ pub fn random_user_agent() -> &'static str {
15
+ USER_AGENTS
16
+ .get_or_init(|| {
17
+ UserAgentsBuilder::new()
18
+ .cache(false)
19
+ .dir("/tmp")
20
+ .thread(1)
21
+ .set_browsers(
22
+ Browsers::new()
23
+ .set_chrome()
24
+ .set_safari()
25
+ .set_edge()
26
+ .set_firefox()
27
+ .set_mozilla(),
28
+ )
29
+ .build()
30
+ })
31
+ .random()
32
  }
src/server/routes.rs CHANGED
@@ -20,6 +20,10 @@ use regex::Regex;
20
  use serde::Deserialize;
21
  use tokio::join;
22
 
 
 
 
 
23
  /// A named struct which deserializes all the user provided search parameters and stores them.
24
  ///
25
  /// # Fields
@@ -67,10 +71,10 @@ pub async fn not_found(
67
  /// * `engines` - It stores the user selected upstream search engines selected from the UI.
68
  #[allow(dead_code)]
69
  #[derive(Deserialize)]
70
- struct Cookie {
71
- theme: String,
72
- colorscheme: String,
73
- engines: Vec<String>,
74
  }
75
 
76
  /// Handles the route of search page of the `websurfx` meta search engine website and it takes
@@ -128,7 +132,7 @@ pub async fn search(
128
  safe_search
129
  ),
130
  &config,
131
- query.to_string(),
132
  page - 1,
133
  req.clone(),
134
  safe_search
@@ -139,7 +143,7 @@ pub async fn search(
139
  config.binding_ip, config.port, query, page, safe_search
140
  ),
141
  &config,
142
- query.to_string(),
143
  page,
144
  req.clone(),
145
  safe_search
@@ -154,7 +158,7 @@ pub async fn search(
154
  safe_search
155
  ),
156
  &config,
157
- query.to_string(),
158
  page + 1,
159
  req.clone(),
160
  safe_search
@@ -175,15 +179,22 @@ pub async fn search(
175
  async fn results(
176
  url: String,
177
  config: &Config,
178
- query: String,
179
  page: u32,
180
  req: HttpRequest,
181
  safe_search: u8,
182
  ) -> Result<SearchResults, Box<dyn std::error::Error>> {
183
- //Initialize redis cache connection struct
184
- let mut redis_cache = RedisCache::new(config.redis_url.clone())?;
 
 
 
 
 
 
185
  // fetch the cached results json.
186
- let cached_results_json = redis_cache.cached_json(&url);
 
187
  // check if fetched cache results was indeed fetched or it was an error and if so
188
  // handle the data accordingly.
189
  match cached_results_json {
@@ -212,7 +223,7 @@ async fn results(
212
  Some(cookie_value) => {
213
  let cookie_value: Cookie = serde_json::from_str(cookie_value.name_value().1)?;
214
 
215
- let engines = cookie_value
216
  .engines
217
  .iter()
218
  .filter_map(|name| EngineHandler::new(name))
@@ -223,7 +234,7 @@ async fn results(
223
  page,
224
  config.aggregator.random_delay,
225
  config.debug,
226
- engines,
227
  config.request_timeout,
228
  safe_search,
229
  )
@@ -235,7 +246,7 @@ async fn results(
235
  page,
236
  config.aggregator.random_delay,
237
  config.debug,
238
- config.upstream_search_engines.clone(),
239
  config.request_timeout,
240
  safe_search,
241
  )
 
20
  use serde::Deserialize;
21
  use tokio::join;
22
 
23
+ // ---- Constants ----
24
+ /// Initialize redis cache connection once and store it on the heap.
25
+ const REDIS_CACHE: async_once_cell::OnceCell<RedisCache> = async_once_cell::OnceCell::new();
26
+
27
  /// A named struct which deserializes all the user provided search parameters and stores them.
28
  ///
29
  /// # Fields
 
71
  /// * `engines` - It stores the user selected upstream search engines selected from the UI.
72
  #[allow(dead_code)]
73
  #[derive(Deserialize)]
74
+ struct Cookie<'a> {
75
+ theme: &'a str,
76
+ colorscheme: &'a str,
77
+ engines: Vec<&'a str>,
78
  }
79
 
80
  /// Handles the route of search page of the `websurfx` meta search engine website and it takes
 
132
  safe_search
133
  ),
134
  &config,
135
+ query,
136
  page - 1,
137
  req.clone(),
138
  safe_search
 
143
  config.binding_ip, config.port, query, page, safe_search
144
  ),
145
  &config,
146
+ query,
147
  page,
148
  req.clone(),
149
  safe_search
 
158
  safe_search
159
  ),
160
  &config,
161
+ query,
162
  page + 1,
163
  req.clone(),
164
  safe_search
 
179
  async fn results(
180
  url: String,
181
  config: &Config,
182
+ query: &str,
183
  page: u32,
184
  req: HttpRequest,
185
  safe_search: u8,
186
  ) -> Result<SearchResults, Box<dyn std::error::Error>> {
187
+ let redis_cache: RedisCache = REDIS_CACHE
188
+ .get_or_init(async {
189
+ // Initialize redis cache connection pool only one and store it in the heap.
190
+ RedisCache::new(&config.redis_url, 5).await.unwrap()
191
+ })
192
+ .await
193
+ .clone();
194
+
195
  // fetch the cached results json.
196
+ let cached_results_json: Result<String, error_stack::Report<crate::cache::error::PoolError>> =
197
+ redis_cache.clone().cached_json(&url).await;
198
  // check if fetched cache results was indeed fetched or it was an error and if so
199
  // handle the data accordingly.
200
  match cached_results_json {
 
223
  Some(cookie_value) => {
224
  let cookie_value: Cookie = serde_json::from_str(cookie_value.name_value().1)?;
225
 
226
+ let engines: Vec<EngineHandler> = cookie_value
227
  .engines
228
  .iter()
229
  .filter_map(|name| EngineHandler::new(name))
 
234
  page,
235
  config.aggregator.random_delay,
236
  config.debug,
237
+ &engines,
238
  config.request_timeout,
239
  safe_search,
240
  )
 
246
  page,
247
  config.aggregator.random_delay,
248
  config.debug,
249
+ &config.upstream_search_engines,
250
  config.request_timeout,
251
  safe_search,
252
  )