feat: add curl() method to Request for generating cURL commands#8897
Conversation
This commit introduces a new `curl()` method in the `Request` class that generates a cURL command equivalent for the HTTP request. This is useful for debugging, logging, and reproducing requests outside of the application. Key features: - Includes HTTP method (`-X`), headers (`-H`), and request body (`--data`) if present. - Handles escaping of special characters in body content. - Appends the request URL. - Provides KDoc consistent with the existing codebase. - Added unit tests for: - GET requests with headers. - POST requests with complex JSON bodies containing nested objects and arrays.
|
Some prior art at https://github.com/mrmike/Ok2Curl |
Thanks @yschimke for pointing that out! I’m aware of Ok2Curl — it’s a great tool and definitely prior art for this feature. However, the goal of this curl() implementation is to: Ok2Curl is a fantastic external option for projects needing richer cURL logging features, but this curl() method provides a simple, dependency-free API directly on Request for users who want quick cURL generation without extra setup. |
8ea4c21 to
4b35802
Compare
|
This feels exactly like the type of thing that should be in a library. There is no need for it to be built into OkHttp. I suggest submitting a PR to the Ok2Curl project to work on Request also. |
swankjesse
left a comment
There was a problem hiding this comment.
I like this idea because curl arguments are kind of a universal format for an HTTP request. I can do ‘copy as curl’ in Chrome, or IntelliJ’s HTTP tool, or whatever.
I request some changes:
- Bound the command size, probably to something that’s a typical limit for a Linux command line. If the command is too large, we gotta truncate it somehow? (What does Chrome do?)
- Support binary responses, and differentiate text vs. binary by looking at the response body data (instead of the content-type)
- Replace ByteArray-based reads with okio.Buffer for efficiency - Detect binary data by inspecting bytes instead of relying on Content-Type - Add BinaryMode options: HEX, OMIT, FILE, STDIN - Default binary mode is now STDIN (`--data-binary @-`)
- use intArrayOf instead of listOf - update binaryMode param default value - don't clone buffer twice
| /** | ||
| * Detects binary data by checking for non-printable characters in a buffer. | ||
| */ | ||
| private fun isBinaryData(peekBuffer: Buffer): Boolean { |
There was a problem hiding this comment.
This says isBinaryData() but it’s actually checking if it’s non-ASCII.
For example, it returns true for this string of regular UTF-8 characters:
val binaryData = isBinaryData(Buffer().writeUtf8("Слава Україні!"))
Please switch to use our existing function, isProbablyUtf8(). Also please a parameter to that function to change its scope from 16 codepoints / 64 bytes to 4096 codepoints / 16384 bytes ?
| when (binaryMode) { | ||
| BinaryMode.HEX -> { | ||
| curl.append(" --data-binary \"") | ||
| val hexBuffer = buffer.clone() |
There was a problem hiding this comment.
We’ve got a function for this, buffer.readByteString().hex()
| } | ||
| } | ||
| } else { | ||
| val bodyString = buffer.readString(StandardCharsets.UTF_8) |
|
This is great! I hope you don’t mind I’m going to merge now and address some PR feedback in my own PR! |
|
Follow-ups: #9112 |
This commit introduces a new
toCurl()method in theRequestclass that generates a cURL command equivalent for the HTTP request. This is useful for debugging, logging, and reproducing requests outside of the application.Key features:
-X), headers (-H), and request body (--data) if present.Example generated cURL:
curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer xyz789" \ --data "{\"user\":{\"id\":123,\"name\":\"John Doe\"},\"roles\":[\"admin\",\"editor\"],\"active\":true}" \ "https://api.example.com/users"