@ -18,8 +18,9 @@ package cmd
import (
import (
"context"
"context"
"io "
"fmt "
"net/http"
"net/http"
"strconv"
"strings"
"strings"
"github.com/gorilla/mux"
"github.com/gorilla/mux"
@ -159,8 +160,13 @@ func (api objectAPIHandlers) ListObjectsV2MHandler(w http.ResponseWriter, r *htt
return
return
}
}
if proxyListRequest ( ctx , w , r , bucket ) {
// Analyze continuation token and route the request accordingly
return
subToken , nodeIndex , parsed := parseRequestToken ( token )
if parsed {
if proxyRequestByNodeIndex ( ctx , w , r , nodeIndex ) {
return
}
token = subToken
}
}
listObjectsV2 := objectAPI . ListObjectsV2
listObjectsV2 := objectAPI . ListObjectsV2
@ -185,8 +191,10 @@ func (api objectAPIHandlers) ListObjectsV2MHandler(w http.ResponseWriter, r *htt
}
}
}
}
response := generateListObjectsV2Response ( bucket , prefix , token ,
// The next continuation token has id@node_index format to optimize paginated listing
listObjectsV2Info . NextContinuationToken , startAfter ,
nextContinuationToken := fmt . Sprintf ( "%s@%d" , listObjectsV2Info . NextContinuationToken , getLocalNodeIndex ( ) )
response := generateListObjectsV2Response ( bucket , prefix , token , nextContinuationToken , startAfter ,
delimiter , encodingType , fetchOwner , listObjectsV2Info . IsTruncated ,
delimiter , encodingType , fetchOwner , listObjectsV2Info . IsTruncated ,
maxKeys , listObjectsV2Info . Objects , listObjectsV2Info . Prefixes , true )
maxKeys , listObjectsV2Info . Objects , listObjectsV2Info . Prefixes , true )
@ -237,8 +245,13 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
return
return
}
}
if proxyListRequest ( ctx , w , r , bucket ) {
// Analyze continuation token and route the request accordingly
return
subToken , nodeIndex , parsed := parseRequestToken ( token )
if parsed {
if proxyRequestByNodeIndex ( ctx , w , r , nodeIndex ) {
return
}
token = subToken
}
}
listObjectsV2 := objectAPI . ListObjectsV2
listObjectsV2 := objectAPI . ListObjectsV2
@ -263,8 +276,10 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
}
}
}
}
response := generateListObjectsV2Response ( bucket , prefix , token ,
// The next continuation token has id@node_index format to optimize paginated listing
listObjectsV2Info . NextContinuationToken , startAfter ,
nextContinuationToken := fmt . Sprintf ( "%s@%d" , listObjectsV2Info . NextContinuationToken , getLocalNodeIndex ( ) )
response := generateListObjectsV2Response ( bucket , prefix , token , nextContinuationToken , startAfter ,
delimiter , encodingType , fetchOwner , listObjectsV2Info . IsTruncated ,
delimiter , encodingType , fetchOwner , listObjectsV2Info . IsTruncated ,
maxKeys , listObjectsV2Info . Objects , listObjectsV2Info . Prefixes , false )
maxKeys , listObjectsV2Info . Objects , listObjectsV2Info . Prefixes , false )
@ -272,41 +287,47 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
writeSuccessResponseXML ( w , encodeResponse ( response ) )
writeSuccessResponseXML ( w , encodeResponse ( response ) )
}
}
func getListEndpoint ( bucket string ) ListEndpoint {
func getLocalNodeIndex ( ) int {
return globalListEndpoints [ crcHashMod ( bucket , len ( globalListEndpoints ) ) ]
if len ( globalProxyEndpoints ) == 0 {
return - 1
}
for i , ep := range globalProxyEndpoints {
if ep . IsLocal {
return i
}
}
return - 1
}
}
// Proxy the list request to the right server.
func parseRequestToken ( token string ) ( subToken string , nodeIndex int , success bool ) {
func proxyListRequest ( ctx context . Context , w http . ResponseWriter , r * http . Request , bucket string ) ( success bool ) {
i := strings . Index ( token , "@" )
if len ( globalListEndpoints ) == 0 {
if i < 0 {
return false
return "" , - 1 , false
}
nodeIndex , err := strconv . Atoi ( token [ i + 1 : ] )
if err != nil {
return "" , - 1 , false
}
}
ep := getListEndpoint ( bucket )
subToken = token [ : i ]
if ep . isLocal {
return subToken , nodeIndex , true
}
func proxyRequestByNodeIndex ( ctx context . Context , w http . ResponseWriter , r * http . Request , index int ) ( success bool ) {
if len ( globalProxyEndpoints ) == 0 {
return false
return false
}
}
ctx = r . Context ( )
if index < 0 || index >= len ( globalProxyEndpoints ) {
outreq := r . Clone ( ctx )
outreq . URL . Scheme = "http"
outreq . URL . Host = ep . host
outreq . URL . Path = r . URL . Path
outreq . Header . Add ( "Host" , r . Host )
if globalIsSSL {
outreq . URL . Scheme = "https"
}
outreq . Host = r . Host
res , err := ep . t . RoundTrip ( outreq )
if err != nil {
return false
return false
}
}
for k , vv := range res . Header {
ep := globalProxyEndpoints [ index ]
for _ , v := range vv {
if ep . IsLocal {
w . Header ( ) . Set ( k , v )
return false
}
}
}
w . WriteHeader ( res . StatusCode )
return proxyRequest ( ctx , w , r , ep )
io . Copy ( w , res . Body )
}
return true
func proxyRequestByBucket ( ctx context . Context , w http . ResponseWriter , r * http . Request , bucket string ) ( success bool ) {
return proxyRequestByNodeIndex ( ctx , w , r , crcHashMod ( bucket , len ( globalProxyEndpoints ) ) )
}
}
// ListObjectsV1Handler - GET Bucket (List Objects) Version 1.
// ListObjectsV1Handler - GET Bucket (List Objects) Version 1.
@ -347,7 +368,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
return
return
}
}
if proxyList Request ( ctx , w , r , bucket ) {
if proxyRequestByBucke t ( ctx , w , r , bucket ) {
return
return
}
}