Skip to content

Optionally include Content-Disposition header in statement results API response#17827

Closed
jgoz wants to merge 2 commits intoapache:masterfrom
jgoz:msq-content-disposition-header
Closed

Optionally include Content-Disposition header in statement results API response#17827
jgoz wants to merge 2 commits intoapache:masterfrom
jgoz:msq-content-disposition-header

Conversation

@jgoz
Copy link
Copy Markdown
Contributor

@jgoz jgoz commented Mar 21, 2025

Description

Adds support for an optional filename query parameter to the /druid/v2/sql/statements/{queryId}/results API. When provided, the response will include a header Content-Disposition: attachment; filename={filename}, which will instruct a web browser to save the response as a file rather than displaying it inline.

This save-as-attachment behavior could be achieved by adding a "download" attribute to the results link, but this only works for same-origin URLs (as in the Web Console). If the UI origin is different from the Druid API origin, browsers will ignore the attribute and serve the results inline, which is poor UX for files that are potentially very large.

For the sake of consistency, all successful responses in SqlStatementResource.doGetResults may include this header, even if there are no results.

Release note

Improved: The "Get query results" statements API supports an optional filename query parameter. When provided, the response will instruct web browsers to save the results as a file instead of showing them inline (via the Content-Disposition header).


Key changed/added classes in this PR
  • SqlStatementResource

This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • been tested in a test Druid cluster.

@github-actions github-actions Bot added Area - Documentation Area - Batch Ingestion Area - Querying Area - MSQ For multi stage queries - https://github.com/apache/druid/issues/12262 labels Mar 21, 2025
@vogievetsky vogievetsky added the Needs web console change Backend API changes that would benefit from frontend support in the web console label Mar 21, 2025
);
throwIfQueryIsNotSuccessful(queryId, statusPlus);

final String contentDispositionHeaderValue = filename != null ? StringUtils.format("attachment; filename=%s", filename) : null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there should be some validation of filename, such as:

  • maximum length
  • allowed characters
  • anything else that may make sense given how it's expected to be used

as to "allowed characters", please check into what happens if ; or whitespace is provided in the filename?

if (!signature.isPresent() || MSQControllerTask.isIngestion(msqControllerTask.getQuerySpec())) {
// Since it's not a select query, nothing to return.
return Response.ok().build();
final Response.ResponseBuilder responseBuilder = Response.ok();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please factor this into a method so it doesn't need to be repeated three times. the signature of the method could be Response.ResponseBuilder addContentDisposition(Response.ResponseBuilder, String)

* Defines the format in which the results are presented. The following options are supported `arrayLines`,`objectLines`,`array`,`object`, and `csv`. The default is `object`.
* `filename` (optional)
* Type: String
* If set, attaches a `Content-Disposition` header to the response with the value of `attachment; filename={filename}`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

once a max length and allowed characters are implemented, document them here.

@vogievetsky
Copy link
Copy Markdown
Contributor

Following Gian's comments I think it makes sense to restrict the filename to 255 chars and to ensure that it does not contain /, \, :, *, ?, ", <, >, |, \0 (null byte), \n, \r. Also instead of doing filename=%s it should be filename="%s" to allow for spaces in the filename. And maybe even encoded for non ASCII chars like by setting filename*. Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Disposition

@kgyrtkirk
Copy link
Copy Markdown
Member

inclided in #17840

@kgyrtkirk kgyrtkirk closed this Mar 28, 2025
@jgoz
Copy link
Copy Markdown
Contributor Author

jgoz commented Mar 28, 2025

I had the comments addressed in a commit, but was waiting to go over them with @vogievetsky before pushing. They were nearly identical to the changes that ended up being merged, so that is probably good :)

Anyway, thank you for taking this over!

@jgoz jgoz deleted the msq-content-disposition-header branch March 31, 2025 14:15
@kgyrtkirk kgyrtkirk removed this from the 33.0.0 milestone Apr 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area - Batch Ingestion Area - Documentation Area - MSQ For multi stage queries - https://github.com/apache/druid/issues/12262 Area - Querying Needs web console change Backend API changes that would benefit from frontend support in the web console

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants