Skip to content

feat: add curl() method to Request for generating cURL commands#8897

Merged
swankjesse merged 8 commits into
square:masterfrom
hamza-badar:master
Oct 6, 2025
Merged

feat: add curl() method to Request for generating cURL commands#8897
swankjesse merged 8 commits into
square:masterfrom
hamza-badar:master

Conversation

@hamza-badar

@hamza-badar hamza-badar commented Jul 2, 2025

Copy link
Copy Markdown
Contributor

This commit introduces a new toCurl() 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.

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"

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.
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt Outdated
Comment thread okhttp/src/jvmTest/kotlin/okhttp3/RequestTest.kt Outdated
Comment thread okhttp/src/jvmTest/kotlin/okhttp3/RequestTest.kt Outdated
@yschimke

yschimke commented Jul 2, 2025

Copy link
Copy Markdown
Collaborator

Some prior art at https://github.com/mrmike/Ok2Curl

@hamza-badar

hamza-badar commented Jul 2, 2025

Copy link
Copy Markdown
Contributor Author

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:
• Avoid introducing additional dependencies into OkHttp core.
• Provide a lightweight, self-contained utility that aligns with OkHttp’s philosophy of minimalism and portability.
• Avoid side effects (no logging, no interceptors, no I/O).

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.

@hamza-badar hamza-badar requested a review from JakeWharton July 2, 2025 15:52
@hamza-badar hamza-badar force-pushed the master branch 2 times, most recently from 8ea4c21 to 4b35802 Compare July 2, 2025 16:18
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt Outdated
Comment thread okhttp/src/jvmTest/kotlin/okhttp3/RequestTest.kt Outdated
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt Outdated
@yschimke

yschimke commented Jul 5, 2025

Copy link
Copy Markdown
Collaborator

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.

Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt

@swankjesse swankjesse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. 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?)
  2. Support binary responses, and differentiate text vs. binary by looking at the response body data (instead of the content-type)

yschimke and others added 3 commits August 4, 2025 08:25
- 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 @-`)
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt Outdated
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt
Comment thread okhttp/api/jvm/okhttp.api Outdated
Comment thread okhttp/src/commonJvmAndroid/kotlin/okhttp3/Request.kt Outdated
Comment thread okhttp/src/jvmTest/kotlin/okhttp3/RequestTest.kt
- 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 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We’ve got a function for this, buffer.readByteString().hex()

}
}
} else {
val bodyString = buffer.readString(StandardCharsets.UTF_8)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buffer.readUtf8()

@swankjesse

Copy link
Copy Markdown
Collaborator

This is great! I hope you don’t mind I’m going to merge now and address some PR feedback in my own PR!

@swankjesse swankjesse merged commit fa84a6e into square:master Oct 6, 2025
@swankjesse

Copy link
Copy Markdown
Collaborator

Follow-ups: #9112

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants