Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,17 @@ out/example.com/61ac5fbb9d3dd054006ae82630b045ba730d8618:3:> TRACE /robots.txt H
out/example.com/bd8d9f4c470ffa0e6ec8cfa8ba1c51d62289b6dd:3:> TRACE /.well-known/security.txt HTTP/1.1
...
```

### JSON Output

Instead of the usual meg output it is also possible to output the results
as a JSON string with the `-j` or `--jsonOut` option:

```
▶ meg --jsonOut
{"request":{"url":"","hostname":"","method":"","path":"","host":"","headers":[{"headerkey":"","headervalue":""},{"headerkey":"","headervalue":""}],"body":"","follow":false},"response":{"statuscode":,"status":"","headers":[{"headerkey":"","headervalue":""}],"body":""}}
```

### No headers

With --no-headers you suppress the output of HTTP headers.
11 changes: 10 additions & 1 deletion args.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type config struct {
saveStatus saveStatusArgs
timeout int
verbose bool
jsonOut bool

paths string
hosts string
Expand Down Expand Up @@ -106,6 +107,11 @@ func processArgs() config {
flag.BoolVar(&rawHTTP, "rawhttp", false, "")
flag.BoolVar(&rawHTTP, "r", false, "")

// Json Output param
jsonOut := false
flag.BoolVar(&jsonOut, "jsonOut", false, "")
flag.BoolVar(&jsonOut, "j", false, "")

// no headers
noHeaders := false
flag.BoolVar(&noHeaders, "no-headers", false, "")
Expand Down Expand Up @@ -156,6 +162,7 @@ func processArgs() config {
hosts: hosts,
output: output,
noHeaders: noHeaders,
jsonOut: jsonOut,
}
}

Expand All @@ -176,7 +183,9 @@ func init() {
h += " -s, --savestatus <status> Save only responses with specific status code\n"
h += " -t, --timeout <millis> Set the HTTP timeout (default: 10000)\n"
h += " -v, --verbose Verbose mode\n"
h += " -X, --method <method> HTTP method (default: GET)\n\n"
h += " -X, --method <method> HTTP method (default: GET)\n"
h += " --no-headers Don't output headers\n"
h += " -j, --jsonOut Output in JSON\n\n"

h += "Defaults:\n"
h += " pathsFile: ./paths\n"
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func main() {
continue
}

path, err := res.save(c.output, c.noHeaders)
path, err := res.save(c.output, c.noHeaders, c.jsonOut)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to save file: %s\n", err)
}
Expand Down
119 changes: 104 additions & 15 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package main
import (
"bytes"
"crypto/sha1"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
)

// a response is a wrapper around an HTTP response;
Expand All @@ -21,8 +23,100 @@ type response struct {
err error
}

// String returns a string representation of the request and response
func (r response) String() string {
// jsonString returns a JSON string representation of the request and response
func (r response) jsonString(noHeaders bool) string {
// Building the structs for the various JSON elements (root element, headers, body, ...)

// Struct for Headers (Key, Value)
type JsonHeader struct {
JsonHeaderKey string `json:"headerkey"`
JsonHeaderValue string `json:"headervalue"`
}
// Struct representing the request (URL, Hostname, HTTP Method, Path, Host, Headers (struct), Body, Follow (-L))
type JsonRequest struct {
JsonRequestUrl string `json:"url"`
JsonRequestHostname string `json:"hostname"`
JsonRequestMethod string `json:"method"`
JsonRequestPath string `json:"path"`
JsonRequestHost string `json:"host"`
JsonRequestHeaders []JsonHeader `json:"headers"`
JsonRequestBody string `json:"body"`
JsonRequestFollow bool `json:"follow"`
}
// Struct representing the response (HTTP status code, status text, headers(struct), body)
type JsonResponse struct {
JsonResponseStatusCode int `json:"statuscode"`
JsonResponseStatus string `json:"status"`
JsonResponseHeader []JsonHeader `json:"headers"`
JsonBody string `json:"body"`
}
// Struct representing the root element (Request, Response)
type JsonOut struct {
JsonRequest JsonRequest `json:"request"`
JsonResponse JsonResponse `json:"response"`
}

// Prepare the Header structs which will be inserted into the JSON element
var jsonRequestHeaders []JsonHeader
var jsonResponseHeaders []JsonHeader
// If we specify --no-headers we will simply receive "headers":null in both the request and response
// Otherwise we will extract the headers from the request/response and fill the header elements
if !noHeaders {
// Fill the request header struct
for _, h := range r.request.headers {
// We use SplitN because then we only split on the first colon occurrence
x := strings.SplitN(h, ":", 2)
jsonRequestHeaders = append(jsonRequestHeaders, JsonHeader{x[0], strings.TrimSpace(x[1])})
}
// Fill the response header struct
for _, h := range r.headers {
x := strings.SplitN(h, ":", 2)
jsonResponseHeaders = append(jsonResponseHeaders, JsonHeader{x[0], strings.TrimSpace(x[1])})
}
}
// Create the JSON Body element
jsonBody := &bytes.Buffer{}
jsonBody.Write(r.body)

// Create the JSON element
// Root Element
resp := JsonOut{
// Request Element
JsonRequest{
r.request.URL(),
r.request.Hostname(),
r.request.method,
r.request.path,
r.request.host,
jsonRequestHeaders,
r.request.body,
r.request.followLocation,
},
// Response Element
JsonResponse{
r.statusCode,
r.status,
jsonResponseHeaders,
jsonBody.String(),
},
}
// Marshal the JSON struct
ba, err := json.Marshal(resp)
if err != nil {
fmt.Println("error:", err)
}

return string(ba)
}

// megString returns a string representation of the request and response in a "traditional" meg format
func (r response) megString(noHeaders bool) string {
if noHeaders {
b := &bytes.Buffer{}
b.Write(r.body)
return b.String()
}

b := &bytes.Buffer{}

b.WriteString(r.request.URL())
Expand Down Expand Up @@ -51,20 +145,15 @@ func (r response) String() string {
return b.String()
}

func (r response) StringNoHeaders() string {
b := &bytes.Buffer{}

b.Write(r.body)

return b.String()
}

// save write a request and response output to disk
func (r response) save(pathPrefix string, noHeaders bool) (string, error) {

content := []byte(r.String())
if noHeaders {
content = []byte(r.StringNoHeaders())
func (r response) save(pathPrefix string, noHeaders bool, json bool) (string, error) {
var content []byte

// Depending if we want to save a json or meg string we call the corresponding String method
if json {
content = []byte(r.jsonString(noHeaders))
} else {
content = []byte(r.megString(noHeaders))
}

checksum := sha1.Sum(content)
Expand Down