github.com/minio/console@v1.4.1/api/user_watch.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package api
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  	"regexp"
    25  	"strings"
    26  
    27  	mc "github.com/minio/mc/cmd"
    28  	"github.com/minio/websocket"
    29  )
    30  
    31  type watchOptions struct {
    32  	BucketName string
    33  	mc.WatchOptions
    34  }
    35  
    36  func startWatch(ctx context.Context, conn WSConn, wsc MCClient, options *watchOptions) error {
    37  	wo, pErr := wsc.watch(ctx, options.WatchOptions)
    38  	if pErr != nil {
    39  		LogError("error initializing watch: %v", pErr.Cause)
    40  		return pErr.Cause
    41  	}
    42  	for {
    43  		select {
    44  		case <-ctx.Done():
    45  			close(wo.DoneChan)
    46  			return nil
    47  		case events, ok := <-wo.Events():
    48  			// zero value returned because the channel is closed and empty
    49  			if !ok {
    50  				return nil
    51  			}
    52  			for _, event := range events {
    53  				// Serialize message to be sent
    54  				bytes, err := json.Marshal(event)
    55  				if err != nil {
    56  					LogError("error on json.Marshal: %v", err)
    57  					return err
    58  				}
    59  				// Send Message through websocket connection
    60  				err = conn.writeMessage(websocket.TextMessage, bytes)
    61  				if err != nil {
    62  					LogError("error writeMessage: %v", err)
    63  					return err
    64  				}
    65  			}
    66  		case pErr, ok := <-wo.Errors():
    67  			// zero value returned because the channel is closed and empty
    68  			if !ok {
    69  				return nil
    70  			}
    71  			if pErr != nil {
    72  				LogError("error on watch: %v", pErr.Cause)
    73  				return pErr.Cause
    74  
    75  			}
    76  		}
    77  	}
    78  }
    79  
    80  // getWatchOptionsFromReq gets bucket name, events, prefix, suffix from a websocket
    81  // watch path if defined.
    82  // path come as : `/watch/bucket1` and query
    83  // params come on request form
    84  func getWatchOptionsFromReq(req *http.Request) (*watchOptions, error) {
    85  	wOptions := watchOptions{}
    86  	// Default Events if not defined
    87  	wOptions.Events = []string{"put", "get", "delete"}
    88  
    89  	re := regexp.MustCompile(`(/watch/)(.*?$)`)
    90  	matches := re.FindAllSubmatch([]byte(req.URL.Path), -1)
    91  	// matches comes as e.g.
    92  	// [["...", "/watch/", "bucket1"]]
    93  	// [["/watch/" "/watch/" ""]]
    94  
    95  	if len(matches) == 0 || len(matches[0]) < 3 {
    96  		return nil, fmt.Errorf("invalid url: %s", req.URL.Path)
    97  	}
    98  
    99  	wOptions.BucketName = strings.TrimSpace(string(matches[0][2]))
   100  
   101  	events := req.FormValue("events")
   102  	if strings.TrimSpace(events) != "" {
   103  		wOptions.Events = strings.Split(events, ",")
   104  	}
   105  	wOptions.Prefix = req.FormValue("prefix")
   106  	wOptions.Suffix = req.FormValue("suffix")
   107  	return &wOptions, nil
   108  }