goweb

package module
v0.0.0-...-15eeed8 Latest Latest
Warning

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

Go to latest
Published: Nov 10, 2017 License: GPL-3.0 Imports: 20 Imported by: 0

README

GoWeb

GoWeb framework provide a ncie and easy way to create a webserver. It handles configuration from a text file, sets up listener, database connections, auto-restart of goroutines and an amazing and flexible multi-destination logger. All that remains to be done is a few lines of code to start it and your own templates/handlers using the httprouter.

Author

  • Robert "iptable" Glonek
  • Best Method Limited, UK
  • github.com/bestmethod

Quickstart guide

Feel free to replace loginExample in the git clone command with simpleExample

go get github.com/bestmethod/GoWeb
cd ~
mkdir GoWebLoginExample
cd GoWebLoginExample
git clone -b loginExample https://github.com/bestmethod/GoWeb.git
cd GoWeb
go run main.go config_file.txt

Now connect from your browser to http://127.0.0.1:8080

Session handling builtin

The builtin session handling uses session-id and session-key to ensure safe sessions without the possibility of session stealing. It also uses DB-backed sessions for horizontal scaling. It also has a builtin session cleaner, which periodically removes old sessions from the database.

goweb.GetSession(w http.ResponseWriter, r *http.Request) *SessionStruct <- automatically get a session (DB/Cookie based). Create new one if needed

goweb.UpdateSession(Session *SessionStruct) <- automatically update the session so that again there is one hour till expiry. Good to use at end of each call if you want to log user out after 1 hour or no activity on the site.

goweb.NewSession(w http.ResponseWriter, r *http.Request) *SessionStruct <- should get called by other functions, creates a new session and cookies are set

goweb.KeepMeLoggedIn(Session *SessionStruct) <- to be called if you want the session to expire after 20 years instead of 1 hour. Good if user selects "keep me loggee in" box.

Example code using this import

Example code can be found in this repo, using the following branches:

simpleExample <- branch containing a simple example to build on (essentially the below)

loginExample <- branch containing an example of an index (with redirect), login, register pages (all working) together with session handling.

The Webserver struct

goweb.Init() returns a Webserver struct, which contains loads of useful stuff. For most uses, the following is important:

Webserver.Logger.(Init|Debug|Warn|Error|Critical) <- pass this function a string to be logged

Webserver.DbConn <- an *sql.DB instance

Webserver.Config.Website.Name <- configured name of website from the configuration file

It is advisable to use meddler for database handling.

Example configuration file

[General]
ServiceName="SomeServiceName"
DebugMainLoop=false
MonitorSleepSeconds=5
 
[Database]
# Type= sqlite3|MySQL
Type="sqlite3"
# can be an IP:PORT, or domain:port
Server="./exampleDb.sqlite3"
User="None"
Password="None"
DbName="None"
UseTLS=false
# the below 2 are mutually exclusive (and only required if the above is true). To use ssl_ca, ensure TLSSkipVerify=false
TLSSkipVerify=false
# ssl_ca="/some/path"
 
[Listener]
ListenIp="0.0.0.0"
ListenPort=8080
UseSSL=false
# SSLCrtPath="/some/path"
# SSLKeyPath="/some/path"
CookieLifetimeSeconds=315360000
SessionExpireSeconds=3600
SessionCleanerIntervalSeconds=10
SessionCleanerRun=true
RpcListenerRun=true
SessionDebug=false
 
[Website]
Name="SomeWebsiteName"
 
######### LOGGERS #########
 
[[Logger]]
LogLevel="INFO"
RpcLogLevel="DEBUG"
Destination="stdout"
 
# [[Logger]]
# LogLevel="DEBUG"
# RpcLogLevel="DEBUG"
# Destination="devlog"
 
# [[Logger]]
# LogLevel="ERROR"
# RpcLogLevel="ERROR"
# Destination="stderr"
 
# [[Logger]]
# LogLevel="INFO"
# RpcLogLevel="INFO"
# Destination="udp://127.0.0.1:11514"

Database sql

CREATE TABLE session (
    id INTEGER PRIMARY KEY,
    user_id INTEGER,
    session_id string(50),
    session_key string(130),
    expires INTEGER,
    keep_logged_in bool
);

Other dependencies

go get github.com/BurntSushi/toml
go get github.com/mattn/go-sqlite3
go get github.com/russross/meddler
go get github.com/go-sql-driver/mysql
go get github.com/davecgh/go-spew/spew
go get github.com/julienschmidt/httprouter
go get github.com/leonelquinteros/gorand

Simple usage example

main bits
# import the basics
import (
    "github.com/julienschmidt/httprouter"
    "net/http"
    "github.com/bestmethod/goweb"
    "strconv"
    "html/template"
    "fmt"
)
 
# main func
func main() {
 
    # create a goweb instance
    ws := goweb.Init()
 
    # create a http router and add an index page
    router := httprouter.New()
    router.GET("/", indexFunc)
 
    # start the router
    ws.Start(router)
}
indexFunc and struct - standard httprouter stuff
type index struct {
    Username string
    Title    *string
    Subtitle string
}

func Index(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    model := new(index)
    model.Username = "Robert"
    model.Title = ws.Config.Website.Name
    model.Subtitle = " - Index Page!"
    
    t := template.New("index")
    var err error
    t, err = t.ParseFiles("index.html")
    if err != nil {
        ws.Logger.Error(fmt.Sprintf("There was an error serving page template: %s", err))
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
    err = t.Execute(w, &model)
    if err != nil {
        ws.Logger.Error(fmt.Sprintf("There was an error executing templates: %s", err))
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}
index.html to complement the indexFunc
{{ define "index" }}
<html><head><Title>{{.Title}}{{.Subtitle}}</Title></head>
<body>
<center>
Hello, {{.Username}}!<br>
<a href="?logout=true">Click here to logout</a>
</center>
</body></html
{{ end }}

TODO

  • unit tests

Documentation

Index

Constants

View Source
const (
	//early logging lines - before logger is setup - Loader
	EARLY_LOAD_MAINCONF    = "LOADCONFIG:FROMFILE: DEBUG Loading main configuration parts"
	EARLY_LOAD_LOGCONF     = "LOADCONFIG:FROMFILE: DEBUG Loading loggers"
	EARLY_INIT_CONFSTRUCT  = "LOADCONFIG: DEBUG Initializing config structure"
	EARLY_LOADING_FILECONF = "LOADCONFIG: DEBUG Loading config from file"
)
View Source
const (
	//Serve
	LOG_CONFIG_GLOBALS   = "Configuring rpcListener global vars"
	LOG_ROUTER_MAKE      = "Creating new router and setting session data"
	LOG_STARTLISTEN      = "Starting Listener on %s:%d, TLS=%t\n"
	LOG_CONF_DISPATCHERS = "Configuring router dispatchers"

	//Server :: session functions
	LOG_DB_QUERY_FAIL  = "Could not find cookie session data in the DB: %s\n"
	LOG_DB_INSERT_FAIL = "Could not insert cookie session data to the DB: %s\n"
	LOG_DB_UPDATE_FAIL = "Could not update cookie session data in the DB: %s\n"
	LOG_RAND_FAIL      = "Could not generate random number for a cookie, this is serious!\n"

	//SessionCleaner
	LOG_CLEANER_FART = "Could not cleanup session data in the DB: %s\n"
	LOG_CLEANER_DONE = "Session Cleaner Complete"
)
View Source
const (
	//logging lines for DB - db
	LOG_SQLITE3_OPEN       = "sqlite3 connection, opening"
	LOG_MYSQL_OPEN         = "MySQL connection, opening"
	LOG_MYSQL_TLS_TRUE     = "MySQL tls true"
	LOG_MYSQL_TLS_SKIP     = "MySQL tls skip-verify"
	LOG_MYSQL_VOODOO_START = "MySQL ssl_ca voodoo starting"
	LOG_MYSQL_PEMFAIL      = "Failed to append PEM."
	LOG_MYSQL_VOODOO_DONE  = "MySQL ssl_ca voodoo done, tls custom"
	LOG_MYSQL_CONN         = "MySQL opening connection"
	LOG_WRONG_DBTYPE       = "ERROR: The only supported database types are: MySQL | sqlite3. Currently set: %s\n"
	LOG_MEDDLER_DEFAULTS   = "Configuring meddler defaults"
)

db

View Source
const (
	//log header definitions
	LOGHEADER_MAIN    = "MAIN"
	LOGHEADER_RPC     = "RPC-GOROUTINE"
	LOGHEADER_CLEANER = "CLEANER-GOROUTINE"
	LOGHEADER_RPCWEB  = "RPC-WEBSERVER"
	LOGHEADER_CLNSESS = "RPC-SESSIONCLEANER"
	LOGHEADER_DB      = "DBCONNECT"

	//early logging lines - before logger is setup
	EARLY_ARGS_FAIL      = "Early Fail: Usage: %s [--early-debug] {config_file_name}\n" //process name
	EARLY_LOADING_ARGS   = "MAIN: Processing command line parameters"
	EARLY_DEBUG_ON       = "MAIN: DEBUG Early Debug enabled\nMAIN: DEBUG Conf file: %s\n" //config file name
	EARLY_LOADING_CONF   = "MAIN: Loading configuration"
	EARLY_LOADING_LOGGER = "MAIN: Loading logger"

	//logging lines
	LOG_CONF_CHANNELS         = "Configuring channels for goroutines"
	LOG_BASIC_LOAD_DONE       = "Config loaded, database connection tested, logger initialized ; soon there will be cake!"
	LOG_START_RPC             = "Starting RPC"
	LOG_RESTART_RPC           = "Restarting RPC"
	LOG_INITIAL_DONE          = "Now we have icing!"
	LOG_GETTING_DB_CONN       = "Getting Database Connection for Rpc"
	LOG_ENTER_MAIN_LOOP       = "Entering main loop"
	LOG_MAINLOOP_STARTRPC     = "MainLoop, Processing StartRpc"
	LOG_MAINLOOP_STARTCLEANER = "MainLoop, Processing StartCleaner"
	LOG_MAINLOOP_SLEEP        = "Sleeping in main loop"
	LOG_START_CLEANER         = "Starting Session Cleaner"
	LOG_RESTART_CLEANER       = "Restarting Session Cleaner"
	LOG_PANIC_CAPTURED        = "Panic captured: %s!" //panic details
	LOG_RPC_EXIT              = "Rpc Webserver exit gracefully for some reason!"
	LOG_CLEANER_EXIT          = "Session Cleaner exit gracefully for some reason!"
	LOG_CREATE_WEBSERVER      = "Creating WebServer"
	LOG_RUNCLEAN              = "Running Cleaner"
	LOG_GOTDBERROR            = "Received error from db connection, exiting!"
	LOG_CONFDUMP              = "INIT of configurator and multiLogger done, dumping configuration"
)

main

View Source
const (
	//early logging lines - before logger is setup
	EARLY_CREATING_HANDLER = "LOADLOGGER: DEBUG Creating log handler"
	EARLY_ENTER_CONFLOOP   = "LOADLOGGER: DEBUG Entering configuration loop"
	EARLY_LOOP_POS         = "LOADLOGGER: DEBUG Run %d Setting log level\n"    //run number in for loop
	EARLY_CHECKING_DEST    = "LOADLOGGER: DEBUG Run %d Checking destination\n" //run number in loop
	EARLY_DIALLING_SYSLOG  = "LOADLOGGER: DEBUG Run %d Dialling syslog: %s\n"  //run number in loop, syslog destination
	EARLY_LOGGER_FAIL      = "ERROR: Could not initialize logger:%s\n"         //error details
	EARLY_CREATE_STDOUT    = "LOADLOGGER: DEBUG Run %d Creating stdout logger\n"
	EARLY_CREATE_STDERR    = "LOADLOGGER: DEBUG Run %d Creating stderr logger\n"
	EARLY_SET_DEVLOG       = "LOADLOGGER: DEBUG Run %d Dialling syslog:devlog\n"
	EARLY_APPEND_ARR       = "LOADLOGGER: DEBUG Run %d Appending array\n"
)

multiLogger

View Source
const (
	LEVEL_DEBUG    = 5
	LEVEL_INFO     = 4
	LEVEL_WARN     = 3
	LEVEL_ERROR    = 2
	LEVEL_CRITICAL = 1
)

Variables

This section is empty.

Functions

func Connect

func Connect(conn *DbConf, logger *LogHandler) (db *sql.DB, errRet error)

func KeepMeLoggedIn

func KeepMeLoggedIn(Session *SessionStruct, yesNo bool)

switch keepmeloggedin to true

func SessionCleaner

func SessionCleaner(logger *LogHandler, db *sql.DB)

func StartRpcListener

func StartRpcListener(logger *LogHandler, newLogger *LogHandler, rpcConf *RpcConf, rpcWorker chan int, dbConn *sql.DB, webConf *WebConf, router *httprouter.Router)

func StartSessionCleaner

func StartSessionCleaner(logger *LogHandler, dbConn *sql.DB, cleanerSleep int, cleaner chan int)

func UpdateSession

func UpdateSession(Session *SessionStruct)

internal for getSession, no need to use otherwise

Types

type Config

type Config struct {
	General    *generic
	Website    *WebConf
	Database   *DbConf
	Listener   *RpcConf
	Loggers    *LoggersConf
	EarlyDebug bool
}

func Load

func Load(fn string, early bool) (conf *Config)

func (*Config) FromFile

func (conf *Config) FromFile(ConfigFile string)

type DbConf

type DbConf struct {
	Type              string
	Server            string
	User              string
	Password          string
	DbName            string
	UseTLS            bool
	TLSSkipVerify     bool
	Ssl_ca            string
	ListenerConfID    int
	LoadLoggersFromDB bool
}

type LogHandler

type LogHandler struct {
	Dispatchers []destination
	Header      string
	ServiceName string
	// contains filtered or unexported fields
}

func LoggerInit

func LoggerInit(LogConf *LoggersConf, earlyDebug bool, ServiceName string) (l *LogHandler)

func (*LogHandler) Critical

func (l *LogHandler) Critical(m string)

func (*LogHandler) Debug

func (l *LogHandler) Debug(m string)

func (*LogHandler) Error

func (l *LogHandler) Error(m string)

func (*LogHandler) Fatal

func (l *LogHandler) Fatal(m string)

func (*LogHandler) Info

func (l *LogHandler) Info(m string)

func (*LogHandler) Warn

func (l *LogHandler) Warn(m string)

type LoggersConf

type LoggersConf struct {
	Logger []*loggerConf
}

type RpcConf

type RpcConf struct {
	Id                            int
	ListenIp                      string
	ListenPort                    int
	UseSSL                        bool //will only support TLS1.2 by default, not adding support for others.
	SSLCrtPath                    string
	SSLKeyPath                    string
	CookieLifetimeSeconds         int
	SessionExpireSeconds          int
	SessionCleanerRun             bool
	SessionCleanerIntervalSeconds int
	RpcListenerRun                bool
	SessionDebug                  bool
}

type SessionStruct

type SessionStruct struct {
	Id             int64  `meddler:"id,pk"`
	UserId         int64  `meddler:"user_id"`
	SessionId      string `meddler:"session_id"`
	SessionKey     string `meddler:"session_key"`
	Expires        int64  `meddler:"expires"`
	KeepMeLoggedIn bool   `meddler:"keep_logged_in"`
}

session struct, to have cookies, session ID, whateva

func GetSession

func GetSession(w http.ResponseWriter, r *http.Request) *SessionStruct

helper function for endpoints - call at any point to get the session struct for a given cookie (or set the cookie)

func NewSession

func NewSession(w http.ResponseWriter, r *http.Request) *SessionStruct

internal for getSession, no need to use otherwise

type WebConf

type WebConf struct {
	Name *string
}
var WebsiteConf *WebConf

type WebServer

type WebServer struct {
	Logger  *LogHandler
	RpcConf *RpcConf
	DbConn  *sql.DB
	Router  *httprouter.Router
}

BUILTIN webserver basic struct with configs

func (*WebServer) Serve

func (ws *WebServer) Serve(wc *WebConf)

main webserver serve - set routes, global params and listen to connections

type Webserver

type Webserver struct {
	Logger *LogHandler
	DbConn *sql.DB
	Config *Config
	// contains filtered or unexported fields
}

func Init

func Init() *Webserver

func (*Webserver) Start

func (ws *Webserver) Start(router *httprouter.Router)

Jump to

Keyboard shortcuts

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