mirror of
https://github.com/element-hq/dendrite.git
synced 2025-09-13 21:02:25 +03:00
Merge a73828405a
into e546df2e1e
This commit is contained in:
commit
eee8464835
1 changed files with 98 additions and 2 deletions
|
@ -9,6 +9,7 @@ package routing
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
@ -938,7 +939,6 @@ func parseMultipartResponse(r *downloadRequest, resp *http.Response, maxFileSize
|
||||||
if err = json.NewDecoder(p).Decode(&meta); err != nil {
|
if err = json.NewDecoder(p).Decode(&meta); err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
defer p.Close() // nolint: errcheck
|
|
||||||
|
|
||||||
// Get the actual media content
|
// Get the actual media content
|
||||||
p, err = mr.NextPart()
|
p, err = mr.NextPart()
|
||||||
|
@ -948,7 +948,8 @@ func parseMultipartResponse(r *downloadRequest, resp *http.Response, maxFileSize
|
||||||
|
|
||||||
redirect := p.Header.Get("Location")
|
redirect := p.Header.Get("Location")
|
||||||
if redirect != "" {
|
if redirect != "" {
|
||||||
return 0, nil, fmt.Errorf("Location header is not yet supported")
|
// Handle redirect
|
||||||
|
return handleMultipartRedirect(r, redirect, maxFileSizeBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentLength, reader, err := r.GetContentLengthAndReader(p.Header.Get("Content-Length"), p, maxFileSizeBytes)
|
contentLength, reader, err := r.GetContentLengthAndReader(p.Header.Get("Content-Length"), p, maxFileSizeBytes)
|
||||||
|
@ -957,6 +958,101 @@ func parseMultipartResponse(r *downloadRequest, resp *http.Response, maxFileSize
|
||||||
return contentLength, reader, err
|
return contentLength, reader, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleMultipartRedirect processes a redirect URL from a multipart response
|
||||||
|
func handleMultipartRedirect(r *downloadRequest, redirectURL string, maxFileSizeBytes config.FileSizeBytes) (int64, io.Reader, error) {
|
||||||
|
const maxRedirects = 10
|
||||||
|
redirectCount := 0
|
||||||
|
currentURL := redirectURL
|
||||||
|
var lastResponse *http.Response
|
||||||
|
|
||||||
|
// Ensure we clean up any response body if we exit early
|
||||||
|
defer func() {
|
||||||
|
if lastResponse != nil && lastResponse.Body != nil {
|
||||||
|
lastResponse.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for redirectCount < maxRedirects {
|
||||||
|
// Validate the redirect URL
|
||||||
|
parsedURL, err := url.Parse(currentURL)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("invalid redirect URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new request for the redirect
|
||||||
|
req, err := http.NewRequest("GET", currentURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("failed to create redirect request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the previous response body before making a new request
|
||||||
|
if lastResponse != nil {
|
||||||
|
lastResponse.Body.Close()
|
||||||
|
lastResponse = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a regular client for redirects, as they might point to external storage
|
||||||
|
client := &http.Client{
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse // Prevent auto-redirect
|
||||||
|
},
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("failed to follow redirect: %w", err)
|
||||||
|
}
|
||||||
|
lastResponse = resp
|
||||||
|
|
||||||
|
// Check if we get another redirect
|
||||||
|
if resp.StatusCode == http.StatusFound || resp.StatusCode == http.StatusMovedPermanently {
|
||||||
|
nextURL := resp.Header.Get("Location")
|
||||||
|
if nextURL == "" {
|
||||||
|
return 0, nil, fmt.Errorf("redirect response without Location header")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle relative URLs
|
||||||
|
nextParsedURL, err := url.Parse(nextURL)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("invalid redirect URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nextParsedURL.IsAbs() {
|
||||||
|
nextParsedURL = parsedURL.ResolveReference(nextParsedURL)
|
||||||
|
nextURL = nextParsedURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
currentURL = nextURL
|
||||||
|
redirectCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got a successful response, process it
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
// Check if the response is multipart
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
if strings.HasPrefix(contentType, "multipart/") {
|
||||||
|
// For multipart responses, we need to keep the response body open
|
||||||
|
// The caller will be responsible for closing it
|
||||||
|
lastResponse = nil // Don't close in defer
|
||||||
|
return parseMultipartResponse(r, resp, maxFileSizeBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For regular responses, create a new reader that will close the response body
|
||||||
|
body := resp.Body
|
||||||
|
lastResponse = nil // Don't close in defer
|
||||||
|
reader := io.NopCloser(body)
|
||||||
|
|
||||||
|
return r.GetContentLengthAndReader(resp.Header.Get("Content-Length"), reader, maxFileSizeBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil, fmt.Errorf("unexpected status code following redirect: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil, fmt.Errorf("too many redirects (max %d)", maxRedirects)
|
||||||
|
}
|
||||||
|
|
||||||
// contentDispositionFor returns the Content-Disposition for a given
|
// contentDispositionFor returns the Content-Disposition for a given
|
||||||
// content type.
|
// content type.
|
||||||
func contentDispositionFor(contentType types.ContentType) string {
|
func contentDispositionFor(contentType types.ContentType) string {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue