feat(upload): auto-detect file content-type if not sent#5361
feat(upload): auto-detect file content-type if not sent#5361akrem-chabchoub merged 13 commits intomasterfrom
Conversation
|
what is the status of this PR? |
|
Still not fully tested yet. |
| r.Header.Set(ContentTypeHeader, contentTypeHdr) | ||
| mt, _, errParseCT := mime.ParseMediaType(contentTypeHdr) | ||
| isMultipart := errParseCT == nil && mt == multiPartFormData | ||
| if headers.IsDir || isMultipart { |
There was a problem hiding this comment.
if (headers.IsDir || isMultipart) && contentTypeHdr == "" {...}
There was a problem hiding this comment.
Maybe this is more readable, wdyt ?
isDirUpload := headers.IsDir || isMultipart
if !isDirUpload {
s.fileUploadHandler(ctx, logger, span, ow, r, putter, headers.Encrypt, tag, headers.RLevel, headers.Act, headers.HistoryAddress)
return
}
if contentTypeHdr == "" {
logger.Error(nil, "content-type required for directory upload")
jsonhttp.BadRequest(w, errInvalidContentType)
return
}
s.dirUploadHandler(ctx, logger, span, ow, r, putter, headers.Encrypt, tag, headers.RLevel, headers.Act, headers.HistoryAddress)There was a problem hiding this comment.
both are fine 👍 the point was to just reduce nesting
…and directory uploads
| if headers.IsDir || headers.ContentType == multiPartFormData { | ||
| s.dirUploadHandler(ctx, logger, span, ow, r, putter, r.Header.Get(ContentTypeHeader), headers.Encrypt, tag, headers.RLevel, headers.Act, headers.HistoryAddress) | ||
| contentTypeHdr := strings.TrimSpace(headers.ContentType) | ||
| r.Header.Set(ContentTypeHeader, contentTypeHdr) |
There was a problem hiding this comment.
Before my changes the dirUploadHandler was accepting contentTypeString (although it is present request).
So I changed that here and did trim and set the new value in main handler.
There was a problem hiding this comment.
I think that I agree with @martinconic that this is not really clear why this is needed. If you're already reading, cleaning, parsing and checking the content type header - it does not make any more sense to update the value in the Header type.
There was a problem hiding this comment.
@acud @martinconic
Good point, i agree this can look a bit strange at first.
i set the header on purpose, so we clean Content-Type one time, and then all next steps read the same value.
without this, one part can use the trimmed value, but another part (fileUploadHandler) can still read the original raw header. this can cause small inconsistent behavior later, especially for multipart handling and metadata.
if changing the header feels too implicit, i can refactor and pass the cleaned value explicitly to the next functions. i am fine with both styles, but i think we should keep one canonical content-type value in one place.
There was a problem hiding this comment.
Thanks @akrem-chabchoub.
The problem from my perspective is that you're already doing cleaning and checking of a value, mutating it, but then you're telling downstream function calls to potentially do the same, again, because you've removed the argument from the signature. This is from my perspective not necessary and potentially duplicates code.
This may work in middleware style calls where the middlewares are decoupled (and you want to be setting a header on one middleware that processes for example a user-login or something along those lines, providing you with extra details down the call stack), but I can't see how this is needed here (you're already at the last middleware in the API).
I won't block the PR on this, please choose how you'd like to proceed with this.
| @@ -58,7 +57,7 @@ func (s *Service) dirUploadHandler( | |||
| } | |||
|
|
|||
| // The error is ignored because the header was already validated by the caller. | |||
There was a problem hiding this comment.
The comment is not relevant anymore, right? the header is not validated by the caller. Maybe something like this:
// Parse error is ignored; unsupported media types are caught by the default case below.
, or do we want to validate here?
There was a problem hiding this comment.
Good catch, agreed.
I’d keep validation behavior as-is here, since parse failures and unsupported types is handled in the switch case.
sbackend123
left a comment
There was a problem hiding this comment.
How would it work on edge cases like:
- empty body
| if headers.IsDir || headers.ContentType == multiPartFormData { | ||
| s.dirUploadHandler(ctx, logger, span, ow, r, putter, r.Header.Get(ContentTypeHeader), headers.Encrypt, tag, headers.RLevel, headers.Act, headers.HistoryAddress) | ||
| contentTypeHdr := strings.TrimSpace(headers.ContentType) | ||
| r.Header.Set(ContentTypeHeader, contentTypeHdr) |
There was a problem hiding this comment.
Thanks @akrem-chabchoub.
The problem from my perspective is that you're already doing cleaning and checking of a value, mutating it, but then you're telling downstream function calls to potentially do the same, again, because you've removed the argument from the signature. This is from my perspective not necessary and potentially duplicates code.
This may work in middleware style calls where the middlewares are decoupled (and you want to be setting a header on one middleware that processes for example a user-login or something along those lines, providing you with extra details down the call stack), but I can't see how this is needed here (you're already at the last middleware in the API).
I won't block the PR on this, please choose how you'd like to proceed with this.
Checklist
Description
For single-file POST
/bzz, Content-Type is optional: if the client sends it, we keep it exactly, if not we infer it from the first bytes of the body.Open API Spec Version Changes (if applicable)
Motivation and Context (Optional)
Related Issue (Optional)
#5346
Screenshots (if appropriate):