tsenart / vegeta

HTTP load testing tool and library. It's over 9000!
http://godoc.org/github.com/tsenart/vegeta/lib
MIT License
23.5k stars 1.36k forks source link

Vegeta POST attack not including POST body (JSON file) #192

Closed therealfakemoot closed 8 years ago

therealfakemoot commented 8 years ago

As per the specification in the docs, I'm pretty sure I've spelled everything correctly. The main possibility I see here is that my JSON file is somehow wrong. I generated it using Python's json.dumps() method which has never failed me before. I was able to confirm that Vegeta is hitting my server (entries in the access log bearing the go-http-client useragent string), but the POST bodies are 100% empty.

Is my target file formatted/spelled incorrectly? Is my JSON malformed? Is there some vegeta flag I need to add to my invocation? Inquiring minds must know.

The contents of my target file:

POST http://ndumas.com
@/home/redacted/work/engintron-tests/tests/write.json

The contents of my JSON file:

{"loadtest": "true", "write": "true", "value": "1234", "key": "1234"}

Vegeta version:

➜  engintron-tests git:(master) ✗ ./vegeta -version
???

Vegeta Invocation:

./vegeta attack -targets tests/write -duration 10s -rate 1 -output whatever.bin

Access Logs

199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:39:57 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:39:57 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:39:57 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:39:58 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:39:59 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:40:00 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:40:01 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:40:02 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:40:03 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"
199.223.114.18 199.223.114.18 - - [21/Jul/2016:12:40:04 -0400] "POST / HTTP/1.1" 200 24369 "-" "Go-http-client/2.0"

POST Body Log:

➜  public_html tail -n10 POST_LOG                     
RAW_POST:
RAW_POST: 
RAW_POST: 
RAW_POST: 
RAW_POST: 
RAW_POST: 
RAW_POST: 
RAW_POST: 
RAW_POST: 
RAW_POST:

For operational security, I'd rather not expose the full breadth of my code but the relevant segment for producing the log above:

error_log('RAW_POST: ' . var_export($_POST) . "\n", 3, 'POST_LOG');
therealfakemoot commented 8 years ago

I've tried another invocation of Vegeta to try to eliminate my targets file as the culprit:

echo "POST http://ndumas.com" |./vegeta attack -body tests/write.json -duration 10s -rate 1 -output whatever.bin

No success. POST requests are received by the server but by every indication I can check, there's no POST body. This makes me lean heavily towards something being wrong with my JSON file, but then I tried this.

Python 2.6.6 (r266:84292, Jul 23 2015, 15:22:56) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-11)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> with open('write.json') as f:
...     write_json = json.load(f)
... 
>>> write_json
{u'loadtest': u'true', u'write': u'true', u'key': u'1234', u'value': u'1234'}
>>> 
therealfakemoot commented 8 years ago

After looking through the codebase I've chased it down to here and here where the processing of the body files should be occurring but as far as I can tell those values are all just instantiated and then ignored?

tsenart commented 8 years ago

Please run this small Go program and direct the attack there instead. Tell me what you see then.

package main

import (
    "fmt"
    "net/http"
    "net/http/httputil"
)

func main() {
    http.ListenAndServe(":9000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        dump, err := httputil.DumpRequest(r, true)
        if err != nil {
            panic(err)
        }
        fmt.Println(string(dump))
    }))
}
therealfakemoot commented 8 years ago

It looks like the POST body is being sent, as literal JSON. The issue I'm having is that it isn't being sent as form-urlencoded, which is what PHP expects. I think it'd be useful to allow an attack/target specify this option.

POST / HTTP/1.1
Host: 199.223.114.18:9000
Accept-Encoding: gzip
User-Agent: Go-http-client/1.1

{"loadtest": "true", "write": "true", "value": "1234", "key": "1234"}

For reference, I ran my Python test script against the debug service to demonstrate the difference.

POST / HTTP/1.1
Host: 199.223.114.18:9000
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
User-Agent: python-requests/2.9.1

loadtest=true&write=true&value=sup+jadon&key=1234
tsenart commented 8 years ago

If you want to send URL encoded POST request bodies then do so, but you can't expect JSON to be automatically converted to them. Don't forget to set the appropriate Content-Type header.

therealfakemoot commented 8 years ago

Yeah, as it turns out, I was using Vegeta wrong. I was confused by the section of the README relating to defining targets with custom bodies. They referenced a JSON file and it wasn't immediately clear that the body file was sent raw. Thanks for clarifying this.