github.com/dnutiu/simplFT@v0.0.0-20181023061248-df4b14cdce57/server/connection.go (about)

     1  package server
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"net"
     9  
    10  	"os"
    11  
    12  	"time"
    13  
    14  	"os/signal"
    15  	"syscall"
    16  
    17  	"sync"
    18  
    19  	"github.com/dnutiu/simplFT/server/config"
    20  	"github.com/fsnotify/fsnotify"
    21  	"github.com/spf13/viper"
    22  )
    23  
    24  // ConfigPath is used by the config package to find the path of the config file.
    25  var ConfigPath string
    26  
    27  // ConfigName is used by the config package to find the config file.
    28  var ConfigName string
    29  
    30  // uploadDirectory is the directory where the files will be uploaded
    31  var uploadDirectory string
    32  
    33  // uploadTimeout is the amount in seconds the server will wait for a file to be uploaded
    34  var uploadTimeout time.Duration
    35  
    36  // uploadEnabled holds true of false, if in the config upload was enabled when the upload server was started.
    37  var uploadEnabled bool
    38  
    39  // Shutdown is the shutdown where SIGINT and SIGTERM is send too
    40  var Shutdown = make(chan os.Signal)
    41  var ftpShutdown = make(chan struct{})
    42  var uploadShutdown = make(chan struct{})
    43  
    44  var uploadListener net.Listener
    45  var ftpListener net.Listener
    46  
    47  // All connected clients
    48  var clients map[Client]bool
    49  
    50  // Client interface provides the blueprints for the Client that is used by the server.
    51  type Client interface {
    52  	Connection() net.Conn        // Connection returns the connection stream.
    53  	SetConnection(conn net.Conn) // SetConnection sets the connection for the client.
    54  	Disconnect()                 // Disconnect closes the Client's connections and clears up resources.
    55  	Stack() *StringStack         // Returns the underlying String Stack.
    56  }
    57  
    58  // FTPClient represents a FTPClient connection, it holds a root cage and the underlying connection.
    59  type FTPClient struct {
    60  	rootCage   *StringStack // rootCage is a StringStack that is used to represent the current directory the client is in.
    61  	connection net.Conn
    62  }
    63  
    64  // Stack returns the root cage stack.
    65  func (c *FTPClient) Stack() *StringStack {
    66  	return c.rootCage
    67  }
    68  
    69  // SetStack sets the stack for the FTPClient.
    70  func (c *FTPClient) SetStack(stack *StringStack) {
    71  	c.rootCage = stack
    72  }
    73  
    74  // Connection returns the Connection of the client.
    75  func (c *FTPClient) Connection() net.Conn {
    76  	return c.connection
    77  }
    78  
    79  // SetConnection sets the given connection to the client.
    80  func (c *FTPClient) SetConnection(conn net.Conn) {
    81  	c.connection = conn
    82  }
    83  
    84  // Disconnects the client.
    85  func (c *FTPClient) Disconnect() {
    86  	c.connection.Close()
    87  }
    88  
    89  func shutdownHandler() {
    90  	for {
    91  		select {
    92  		case <-Shutdown:
    93  			log.Println("Shutdown signal received")
    94  			var wg sync.WaitGroup
    95  			wg.Add(1)
    96  
    97  			go func() { // Disconnect all the clients.
    98  				for k := range clients {
    99  					k.Disconnect()
   100  				}
   101  				wg.Done()
   102  			}()
   103  			wg.Wait()
   104  
   105  			ShutdownUploadServer()
   106  			ShutdownFtpServer()
   107  			return
   108  		}
   109  	}
   110  }
   111  
   112  func ShutdownUploadServer() {
   113  	if uploadEnabled == true {
   114  		if uploadListener != nil {
   115  			uploadListener.Close()
   116  		}
   117  		uploadShutdown <- struct{}{}
   118  	}
   119  }
   120  
   121  func ShutdownFtpServer() {
   122  	if ftpListener != nil {
   123  		ftpListener.Close()
   124  	}
   125  	ftpShutdown <- struct{}{}
   126  }
   127  
   128  func Init() {
   129  	signal.Notify(Shutdown, syscall.SIGINT, syscall.SIGTERM)
   130  
   131  	clients = make(map[Client]bool)
   132  	go shutdownHandler()
   133  
   134  	config.InitializeConfiguration(ConfigName, ConfigPath)
   135  	config.ChangeCallback(func(event fsnotify.Event) {
   136  		log.Println("Configuration reloaded successfully!")
   137  	})
   138  }
   139  
   140  func HandleConnection(client Client) {
   141  	defer client.Disconnect()
   142  	defer func() {
   143  		if r := recover(); r != nil {
   144  			log.Println("PANIC: ", r)
   145  
   146  			recoveryError, ok := r.(string)
   147  			if ok {
   148  				io.WriteString(client.Connection(), fmt.Sprintf("PANIC: %s", recoveryError))
   149  			}
   150  		}
   151  	}()
   152  
   153  	log.Println(client.Connection().RemoteAddr(), "has connected.")
   154  	clients[client] = true
   155  
   156  	// Process input
   157  	input := bufio.NewScanner(client.Connection())
   158  
   159  	for input.Scan() {
   160  		log.Println(client.Connection().RemoteAddr(), ":", input.Text())
   161  
   162  		err := ProcessInput(client, input.Text())
   163  		if err != nil {
   164  			log.Println(err)
   165  			io.WriteString(client.Connection(), err.Error()+"\n")
   166  		}
   167  	}
   168  
   169  	// Client has left.
   170  	delete(clients, client)
   171  	log.Println(client.Connection().RemoteAddr(), "has disconnected.")
   172  }
   173  
   174  func StartFtpServer(wg *sync.WaitGroup) error {
   175  	defer wg.Done()
   176  	Addr := viper.GetString("address")
   177  	Port := viper.GetInt("port")
   178  	DirDepth := viper.GetInt("maxDirDepth")
   179  	BasePath = viper.GetString("absoluteServePath")
   180  
   181  	// Start the server
   182  	var err error
   183  	ftpListener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", Addr, Port))
   184  	if err != nil {
   185  		log.Fatal(err)
   186  		return err
   187  	}
   188  	defer ftpListener.Close()
   189  
   190  	log.Println("Hello world!")
   191  	log.Println("Ftp server running on:", Addr, "port", Port)
   192  
   193  	for {
   194  		conn, err := ftpListener.Accept()
   195  
   196  		// Handle shutdown
   197  		select {
   198  		case <-ftpShutdown:
   199  			goto exit
   200  		default:
   201  			if err != nil {
   202  				log.Print(err)
   203  				continue
   204  			}
   205  
   206  			client := FTPClient{}
   207  			client.SetStack(MakeStringStack(DirDepth))
   208  			client.SetConnection(conn)
   209  
   210  			go HandleConnection(&client)
   211  		}
   212  	}
   213  exit:
   214  	log.Println("Ftp server exited.")
   215  	return nil
   216  }
   217  
   218  func HandleUpload(conn net.Conn) {
   219  	// Initialize Client
   220  	client := FTPClient{}
   221  	client.SetStack(MakeStringStack(2))
   222  
   223  	// Upload directory
   224  	client.Stack().Push(uploadDirectory)
   225  	client.SetConnection(conn)
   226  	defer client.Disconnect()
   227  
   228  	// Create the file on the disk and make sure that the filename is random.
   229  	var filename = randSeq(10)
   230  	var _, err = os.Stat(MakePathFromStringStack(client.Stack()) + filename)
   231  
   232  	for !os.IsNotExist(err) {
   233  		filename = randSeq(10)
   234  		_, err = os.Stat(MakePathFromStringStack(client.Stack()) + filename)
   235  	}
   236  
   237  	// This channel will be used to store the uploadResult
   238  	c1 := make(chan error, 1)
   239  	log.Println(conn.RemoteAddr().String() + " is uploading something.")
   240  	clients[&client] = true
   241  
   242  	// Create a new Go routine for uploading
   243  	go func() {
   244  		err := UploadFile(&client, filename)
   245  		c1 <- err
   246  	}()
   247  
   248  	// Wait for either UploadResult or Timeout
   249  	select {
   250  	case result := <-c1:
   251  		{
   252  			if result == nil {
   253  				io.WriteString(conn, filename)
   254  				log.Println(conn.RemoteAddr().String() + "'s upload finished.")
   255  			} else {
   256  				log.Println(fmt.Sprintf("%s: %s %s", "HandleUpload", conn.RemoteAddr().String(), result.Error()))
   257  
   258  				client.Stack().Push(filename)
   259  				os.Remove(MakePathFromStringStack(client.Stack()))
   260  
   261  				io.WriteString(conn, result.Error())
   262  			}
   263  
   264  			conn.Close()
   265  		}
   266  	case <-time.After(time.Second * uploadTimeout):
   267  		{
   268  			io.WriteString(conn, "Timeout")
   269  			conn.Close()
   270  		}
   271  	}
   272  
   273  	delete(clients, &client)
   274  }
   275  
   276  // StartUploadServer starts the uploading server
   277  func StartUploadServer(wg *sync.WaitGroup) error {
   278  	defer wg.Done()
   279  	var err error
   280  	uploadEnabled = viper.GetBool("upload.enabled")
   281  	if uploadEnabled == false {
   282  		log.Println("Uploading not enabled. To enable modify the config file and restart the server")
   283  		return ErrUploadServerFailure
   284  	}
   285  
   286  	addr := viper.GetString("upload.address")
   287  	port := viper.GetInt("upload.port")
   288  	uploadDirectory = viper.GetString("upload.directory")
   289  	uploadTimeout = time.Duration(viper.GetInt("upload.timeout"))
   290  
   291  	uploadListener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
   292  	if err != nil {
   293  		log.Println(err)
   294  		goto exit
   295  	}
   296  	defer uploadListener.Close()
   297  
   298  	err = os.Mkdir(BasePath+"/"+uploadDirectory, 0740)
   299  	if err != nil {
   300  		if _, err := os.Stat(BasePath + "/" + uploadDirectory); err != nil {
   301  			if os.IsNotExist(err) {
   302  				log.Println("Can't create upload directory!")
   303  				goto exit
   304  			}
   305  		}
   306  	}
   307  
   308  	log.Println("Upload server running on:", addr, "port", port)
   309  
   310  	for {
   311  		conn, err := uploadListener.Accept()
   312  		// Handle shutdown
   313  		select {
   314  		case <-uploadShutdown:
   315  			goto exit
   316  		default:
   317  			if err != nil {
   318  				log.Print(err)
   319  				continue
   320  			}
   321  
   322  			go HandleUpload(conn)
   323  		}
   324  	}
   325  
   326  exit:
   327  	log.Println("Upload server exited.")
   328  	return nil
   329  }