Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: output full HTTP request and response in a JSON structure #1007

Open
hughpv opened this issue Dec 21, 2020 · 7 comments
Open
Labels
enhancement New feature or enhancement needs product design We like the idea, but we want to explore the problem deeper, and consider the solution holistically

Comments

@hughpv
Copy link

hughpv commented Dec 21, 2020

I would love to capture the full HTTP request and response in a JSON structure so I can extract the various parts programmatically without parsing the plaintext output.

For example:

http --output-full-json ...
{
  "request": {
    "headers": "Accept: application/json\r\nContent-Type: application/json",
    "body": "<request body here, escaped/encoded as necessary to work as a string value>"
  },
  "response": {
    "headers": "201 Created\r\nContent-Type: application/json\r\nContent-Length: 42",
    "body": "<response body here, escaped/encoded as necessary to work as a string value>"
  }
}
@jkbrzt
Copy link
Member

jkbrzt commented Dec 21, 2020

Thanks for the suggestion, @hughpv. I’ve been considering this for a while.

Initial thoughts

  • Output options should be respected.
  • With --all, an Array of exchanges is printed instead of a single object.
  • What about binary bodies? Apply base64 encoding like httpbin.org? Let user choose to skip/base64-encode?
  • JSON body, when valid, could be embedded into the structure directly (i.e., not as string).
  • A structured object for headers would be nice, but we should support multiple headers of the same name somehow.
  • UI — --output-format which could be raw (default) + json + potentially more in the future.
  • User can always pipe to jq but consider supporting simple selectors directly. Something like --select=response.headers.content-type. These would work on the JSON structure so when used, then --output-format=json would be implied. Need to consider the multi-exchange scenario (where index might be needed).

Hypothetical example

$ http -v --output-format=json  example.org
// Exchange
{
  "request": {
    "headers": {
      "Foo": "Bar"
    },
    "body": "…"
  },
  "response": {
    "headers": {
      "Foo": "Bar"
    },
    "body": {
      "result": "ok"
    }
  }
}

@jkbrzt jkbrzt added the enhancement New feature or enhancement label Dec 21, 2020
@hughpv
Copy link
Author

hughpv commented Dec 21, 2020

Awesome ideas; love them all. Let me just expand a little on my use case --

I have it in mind to build a test suite that, in addition to testing functionality, also captures output for use in documentation, e.g.:

= Example Request
----
include:request.http[]
----

= Example Response
----
include:response.http[]
----

... where request.http and response.http would be built from the httpie output.

Thus what I'm looking for is essentially a "verbatim" or "raw" option. All the other stuff makes sense but the raw output was the main idea I came here with.

Thanks!

@jkbrzt
Copy link
Member

jkbrzt commented Dec 21, 2020

I see, so for your use case, the structured headers would be a complication, because you would have to manually un-structure them for the docs?

@hughpv
Copy link
Author

hughpv commented Dec 21, 2020

Correct. :) But I could definitely see where some users would find structured headers quite useful.

@Ousret

This comment was marked as spam.

@ahmadnassri
Copy link

HAR would be a great format to use for this

@jkbrzt jkbrzt added the needs product design We like the idea, but we want to explore the problem deeper, and consider the solution holistically label Feb 20, 2021
@morgen-peschke
Copy link

morgen-peschke commented Dec 20, 2022

This would be extremely helpful for some testing use cases (I've currently got a really ugly shell function that mimics this with vanilla curl).

Some thoughts on the format:

  • It's probably worth exposing the HTTP code as a number (makes success checks really easy)
  • Wrapping the header values in an array may provide a low-friction way to deal with multiple array values.
    The default case only requires adding [0], which is a pretty low cost.
  • It may be worth treating it as an opt-in sort of thing, as this format is more likely to be useful in scripts than interactively, so being a little more explicit is probably not going to be an issue.

Borrowing language from git, this could be an alternative to --print: --porcelain.

Examples

Raw

Command:

--porcelain=req.headers.raw,req.body.text,req.method,req.uri.raw,resp.headers.raw,resp.body.text,resp.code,resp.status

Output:

{
  "request": {
    "uri": {
      "raw": "http://example.com:8081/test?foo=bar"
    },
    "method": "POST",
    "headers": {
      "raw": "Accept: application/json\r\nContent-Type: application/json"
    },
    "body": {
      "text": "Lorem ipsum dolor sit amet"
    }
  },
  "response": {
    "code": 201,
    "status": "Created",
    "headers": {
      "raw": "Content-Type: application/json\r\nContent-Length: 42"
    },
    "body": {
      "text": "consectetur adipiscing elit"
    }
  }
}

Structured/Parsed

Command:

--porcelain=req.headers.json,req.body.base64,req.method,req.uri.json,resp.headers.text,resp.body.json,resp.code

Output:

{
  "request": {
    "uri": {
      "json": {
        "scheme": "http",
        "host": "example.com",
        "port": 8081,
        "path": "/test",
        "query": {
          "foo": [
            "bar"
          ]
        }
      }
    },
    "method": "POST",
    "headers": {
      "json": {
        "Accept": [
          "application/json"
        ],
        "Content-Type": [
          "application/json"
        ]
      }
    },
    "body": {
      "base64": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ="
    }
  },
  "response": {
    "code": 201,
    "headers": {
      "text": [
        "Content-Type: application/json",
        "Content-Length: 42"
      ]
    },
    "body": {
      "json": "\"consectetur adipiscing elit\""
    }
  }
}

Mixed

Command:

--porcelain=req.method,req.uri.raw,resp.headers.raw,resp.headers.json,resp.body.base64,resp.code

Output:

{
  "request": {
    "uri": {
      "raw": "http://example.com:8081/test?foo=bar"
    },
    "method": "POST"
  },
  "response": {
    "code": 201,
    "headers": {
      "raw": "Accept: application/json\r\nContent-Type: application/json",
      "json": {
        "Accept": [
          "application/json"
        ],
        "Content-Type": [
          "application/json"
        ]
      }
    },
    "body": {
      "base64": "Y29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0"
    }
  }
}

Sorry for the edits, I kept hitting the key for "submit" instead of "newline+indent" 🤦🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or enhancement needs product design We like the idea, but we want to explore the problem deeper, and consider the solution holistically
Projects
None yet
Development

No branches or pull requests

5 participants