fcsonline / drill

Drill is an HTTP load testing application written in Rust
GNU General Public License v3.0
2.09k stars 111 forks source link

drill does not download the full body #182

Open bebehei opened 1 year ago

bebehei commented 1 year ago

I'm currently load-testing nginx with drill. It's always the same file, which is requested and always the same content delivered.

It came to my mind, that the logged $body_bytes_sent in the nginx access log vary from request to request. This should not be the case, since it's the same file.

I'm running the current master and build it with:

cargo run --release -- --benchmark example/benchmark.yml

# benchmark.yaml
---
base: 'https://domain'
iterations: 10
concurrency: 1
rampup: 0

plan:
  - name: Download 1MB
    request:
      url: /1m.bin

The file behind /1m.bin is 1 Megabyte of random data.

# nginx access log
# Executed: drill with benchmark.yml as refercenced above
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 229116 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 130812 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 196348 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 130812 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 147196 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 196348 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 147196 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 147196 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 163580 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:19 +0000] "GET /1m.bin HTTP/1.1" 200 147196 "-" "drill" "-"

# Executed: curl https://domain/1m.bin -O
172.17.0.1 - - [07/May/2023:19:10:30 +0000] "GET /1m.bin HTTP/2.0" 200 1048576 "-" "curl/7.86.0" "-"

# Adding `assign: memory` to the benchmark.yml and re-running it:
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"
172.17.0.1 - - [07/May/2023:19:10:33 +0000] "GET /1m.bin HTTP/1.1" 200 1048576 "-" "drill" "-"

As you can see, the body is only fully downloaded, if the body gets assigned. Obviously for some reasons, drill does behave different with the assign action.

I haven't been able to produce a working patch for drill yet, but the assign was my workaround.

What has to get patched, so that drill downloads the full file by default?

bebehei commented 1 year ago

So, I found a code fix for drill, but I'm not confident, this is working for every use-case in drill.

diff --git a/src/actions/request.rs b/src/actions/request.rs
index e81f427..2a2f1cd 100644
--- a/src/actions/request.rs
+++ b/src/actions/request.rs
@@ -293,17 +296,16 @@ impl Runnable for Request {

           context.insert("cookies".to_string(), json!(cookies));
         }
+        let mut headers = Map::new();

-        let data = if let Some(ref key) = self.assign {
-          let mut headers = Map::new();
-
-          response.headers().iter().for_each(|(header, value)| {
-            headers.insert(header.to_string(), json!(value.to_str().unwrap()));
-          });
+        response.headers().iter().for_each(|(header, value)| {
+          headers.insert(header.to_string(), json!(value.to_str().unwrap()));
+        });
+        let body_data = response.text().await.unwrap();

-          let data = response.text().await.unwrap();
+        let data = if let Some(ref key) = self.assign {

-          let body: Value = serde_json::from_str(&data).unwrap_or(serde_json::Value::Null);
+          let body: Value = serde_json::from_str(&body_data).unwrap_or(serde_json::Value::Null);

           let assigned = AssignedRequest {
             status,
@@ -315,7 +317,7 @@ impl Runnable for Request {

           context.insert(key.to_owned(), value);

-          Some(data)
+          Some(body_data)
         } else {
           None
         };