sharexhandler

package module
v0.0.0-...-7b7cc06 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2017 License: MIT Imports: 7 Imported by: 0

README

ShareX custom upload handler

This is a simple library which can be used to create your own custom Golang upload handler for your ShareX client. It is based on the http mux library which I really do recommend for working with http handlers in Golang.

Introduction

In ShareX you can choose between a bunch of different default built-in upload servers - but according to my expirience it was nearly impossible to manage all my uploaded files. Thats why I decided to write my own upload server (library). I chose Golang because it is a young and really powerful programming language which in my opinion has much potencial.

Tutorial

I will not write an own Wiki for this repository because in my opinion the usage is really simple and by explaining you some examples you should be able to use the code.

Design

The complete code is based on interfaces which can be used to code an own implementation. Moreover you can customize all paths/uris which should be used to handle processes. If thats still not enough you can just edit the code directly because of the really flexible MIT license.

Examples

The tutorial for itself only contains a basic example which is explained with comments. The complete design code (interfaces, basic methods/structs) is commented so that you can find your way through the code.

Basic example with SSL and some custom headers
package main

import (
	"github.com/michivip/sharexhandler"
	"net/http"
	"github.com/gorilla/mux"
	"crypto/tls"
	"strings"
	"log"
	"os"
	"github.com/michivip/sharexhandler/storages"
	"gopkg.in/mgo.v2"
	"bufio"
)

func main() {
	log.Println("Initializing storage...")
	fileFolder := "files/" // The folder where our uploaded files will be stored at.
	if err := os.Mkdir(fileFolder, os.ModePerm); err != nil { // Creating the folder
		panic(err)
	} else {
		// This example goes with the default built-in implemented MongoStorage but in your case you can use a different one.
		storage := &storages.MongoStorage{
			DialInfo: &mgo.DialInfo{ // The dial info. More information: https://godoc.org/labix.org/v2/mgo#DialInfo
				Addrs: []string{"localhost"},
			},
			Configuration: &storages.MongoStorageConfiguration{ // MongoDB configuration
				DatabaseName:         "sharex",   // Database name where collections are created in.
				UploadCollectionName: "uploads",  // Collection where the upload file information (not the file data) is stored at.
				FileFolderPath:       fileFolder, // The folder where the file data is stored at - no information.
			},
		}
		if err := storage.Initialize(); err != nil { // Initializing the storage - in our case connecting to the database.
			panic(err)
		} else {
			log.Println("Initialized!")
			log.Println("Set up custom upload server. Running it in background...")
			srv := setupHttpsServer(storage)
			go srv.ListenAndServeTLS("cert.pem", "private.pem") // Our cert file and our key file which are laying in our directory.
			// Now just a stop hook - nothing special.
			log.Println("Done! Enter \"stop\" to stop the webservers.")
			reader := bufio.NewReader(os.Stdin)
			var text string
			for ; strings.TrimSuffix(text, "\n") != "stop"; text, _ = reader.ReadString('\n') {
			}
			log.Println("Stopping...")
			os.Exit(0)
		}
	}
}

// Main method where our SSL https server is set up in.
func setupHttpsServer(storage sharexhandler.Storage) *http.Server {
	// New router from the mux package
	router := mux.NewRouter()
	// Instantiating the main struct ShareXHandler.
	shareXHandler := &sharexhandler.ShareXHandler{
		// The path which has to start with a slash and should end with no slash. This path will be appended after the host. Example: https://localhost/share
		Path: "/share",
		// Configuration of all paths where requests are handled.
		PathConfiguration: &sharexhandler.PathConfiguration{
			// Upload POST requests are handled on this path. Example about: https://localhost/share/upload/
			UploadPath: "/upload/",
			// All GET requests which request uploaded files by their id with file ending.
			// The handler would be called with the example about with the following value: https://localhost/share/get/MYSSUPERCOOLID.PNG
			GetPath: "/get/{id}",
		},
		// The storage which is an implemented interface.
		Storage: storage,
		// Buffer size which is used to write data to the incoming GET request clients.
		BufferSize: 1024,
		// The host which is used to respond to an upload request and name the id URL.
		ProtocolHost: "https://localhost",
		// A custom handler hook to set headers.
		OutgoingFunction: handleTlsRequest,
		// This will display every png/jpg image and plain text (.txt file) in the client`s browser - every other content type will be downloaded manually.
		WhitelistedContentTypes: []string{"image/png", "image/jpg", "text/plain"},
	}
	// Internal method which binds the ShareX handler with the given configuration to the parent router.
	shareXHandler.BindToRouter(router)
	// A simple TLS configuration
	cfg := &tls.Config{
		MinVersion:               tls.VersionTLS12,
		CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
		PreferServerCipherSuites: true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
		},
	}
	// Listening on 443 to get incoming https requests.
	srv := &http.Server{
		Addr:         "localhost:443",
		Handler:      router,
		TLSConfig:    cfg,
		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
	}
	return srv
}

func handleTlsRequest(w http.ResponseWriter, req *http.Request) {
	// Some TLS headers.
	w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
	w.Header().Add("Content-Security-Policy", "default-src 'none'; font-src 'none'; img-src 'self'; object-src 'none'; script-src 'self'; style-src 'unsafe-inline'")
	w.Header().Add("X-Content-Type-Options", "nosniff")
	w.Header().Add("X-Frame-Options", "DENY")
	w.Header().Add("X-XSS-Protection", "1; mode=block")
	w.Header().Add("Server", "Golang Webserver")
}

Used libraries

In this project I have used some different external libraries. The list below enumerates all of them:

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Entry

type Entry interface {
	// >>> Getter and setter methods
	// There is no SetId method because the id will be generated by the Storage
	// Returns a unique Id of the Entry
	GetId() string
	// Returns the author (access token)
	GetAuthor() string
	// Sets the author (access token)
	SetAuthor(author string)
	// Returns the filename
	GetFilename() string
	// Sets the filename
	SetFilename(filename string)
	// Returns the content type
	GetContentType() string
	// Sets the content type
	SetContentType(contentType string)
	// Returns the last modified date
	GetLastModifiedValue() time.Time
	// Sets the e-tag value
	SetLastModifiedValue(lastModified time.Time)
	// Returns the upload date
	GetUploadDate() time.Time
	// Sets the upload date
	SetUploadDate(uploadDate time.Time)
	// <<<
	//
	// >>> Storage methods
	// This method is used to initially save the storages object.
	// It sets the Id of the Entry.
	// Returns an error if something went wrong or nil if not.
	Save() error
	// This method is used to update the storages object.
	// It updates all fields.
	// Returns an error if something went wrong or nil if not.
	Update() error
	// This method is used to delete the storages object
	// It deletes the entry by its Id. Therefore only the Id has to be set.
	// Returns an error if something went wrong or nil if not.
	Delete() error
	// This method is used to get a read-seeker and therefore the content of the entry.
	// It returns a reader which can be used to read the data and an error if something went wrong or nil if not.
	GetReadSeeker() (io.ReadSeeker, error)
	// This method is used to store content of the file. It returns a writer which can be used to write all content.
	GetWriter() (io.WriteCloser, error)
}

type PathConfiguration

type PathConfiguration struct {
	UploadPath string // Path where POST-Requests of ShareX are routing at. Example: /upload
	GetPath    string // Path where clients get their files. The Id in the path must be {id}. Example: /get/{id}
}

Path configuration: All paths have to start with a slash ("/")

type ShareXHandler

type ShareXHandler struct {
	// The path configuration
	PathConfiguration *PathConfiguration
	// The Storage where files will be stored at/loaded from
	Storage Storage
	// A function which is called on every request (for example to set specific response headers).
	OutgoingFunction func(http.ResponseWriter, *http.Request)
	// Buffer size in bytes which is allocated when sending a file.
	SendBufferSize int
	// Buffer size in bytes which is allocated when receiving a file.
	ReceiveBufferSize int
	// The path has to start a slash ("/"). This is where the router gets bound on.
	Path string
	// This is used to respond to upload requests and refer the ShareX client to the right url. It has to end with a slash! Example: http://localhost:8080/
	ProtocolHost string
	// Whitelisted content types which will be displayed in the client`s browser.
	WhitelistedContentTypes []string
	// contains filtered or unexported fields
}

This is the main class which is used to use the ShareX handler

func (*ShareXHandler) BindToRouter

func (shareXHandler *ShareXHandler) BindToRouter(parentRouter *mux.Router) (subRouter *mux.Router)

This is the function which binds a ShareX handler router to the given path.

type Storage

type Storage interface {
	// Initializes the connection/files to/of the storages
	// Returns nil if the initialization was a success or a given error
	Initialize() error
	// Closes an existing connection/streams of the storages
	// Returns whether the close process was a success and error or nil
	Close() (bool, error)
	// Returns the pointer to completely fresh and new instance of a Entry which is not stored yet
	// To store it you should set the values and after that call the Save method.
	NewStorageEntry() Entry
	//
	LoadStorageEntry(id string) (bool, error, Entry)
}

This interface is used to store/update/delete given files

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL