restfool

package module
v0.0.0-...-6c8de78 Latest Latest
Warning

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

Go to latest
Published: Dec 4, 2019 License: MIT Imports: 13 Imported by: 0

README

Simple Restfool JSON API

GoDoc Go Report Card License Build Status

is a stupidly "foolish" and simple approach of implementing a JSON Restful API library.

! This project is not maintained anymore !

Features

  • path routing using goji
  • database wrapper
  • TLS
  • pretty print
  • Etag / If-None-Match Clientside caching
  • rate limiting using tollbooth middleware
  • basic auth
  • config using TOML format
  • error handler
  • logging

TODO

  • Authentication - oauth(2)

simple example

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"

	rest "github.com/cseeger-epages/restfool-go"
)

func main() {
	confFile := flag.String("c", "conf/api.conf", "path to config ile")
	flag.Parse()

	// initialize rest api using conf file
	api, err := rest.New(*confFile)
	if err != nil {
		log.Fatal(err)
	}

	// add handler
	err = api.AddHandler("Index", "GET", "/", "default page", Index)
	if err != nil {
		log.Fatal(err)
	}

	// start
	err = api.Serve()
	if err != nil {
		log.Fatal(err)
	}
}

func Index(w http.ResponseWriter, r *http.Request) {
	// dont need to cache ?
	w.Header().Set("Cache-Control", "no-store")

	qs := rest.ParseQueryStrings(r)
	message := fmt.Sprintf("Welcome to restfool take a look at https://%s/help", r.Host)
	msg := rest.Msg{Message: message}
	rest.EncodeAndSend(w, r, qs, msg)
}

config example

# default API configuration file
# using the TOML File format

[general]
# listen supports also IPv6 addresses like :: or ::1
listen = "127.0.0.1"
port = "9443"
basicauth = false

[certs]
public = "certs/server.crt"
private = "certs/server.key"

[tls]
# enable encryption
encryption = true
# supported minimal ssl/tls version
# minversion = ["ssl30", "tls10", "tls11", "tls12"]
minversion = "tls12"
# used eliptical curves
# curveprefs = ["p256","p384","p521","x25519"]
curveprefs = ["p256","p384","p521"]
# allowed ciphers
# ciphers = [        
#  "TLS_RSA_WITH_RC4_128_SHA",
#  "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
#  "TLS_RSA_WITH_AES_128_CBC_SHA",
#  "TLS_RSA_WITH_AES_256_CBC_SHA",
#  "TLS_RSA_WITH_AES_128_CBC_SHA256",
#  "TLS_RSA_WITH_AES_128_GCM_SHA256",
#  "TLS_RSA_WITH_AES_256_GCM_SHA384",
#  "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
#  "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
#  "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
#  "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
#  "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
#  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
#  "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
#  "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
#  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
#  "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
#  "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
#  "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
#  "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
#  "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
#  "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
#]
ciphers = [
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
    "TLS_RSA_WITH_AES_256_GCM_SHA384",
]
# if not set equal to false
preferserverciphers = true
# HTTP Strict Transport Security
hsts = true
hstsmaxage = 63072000

# cross origin policy
[cors]
allowcrossorigin = true
# corsmethods = ["POST", "GET", "OPTIONS", "PUT", "DELETE"]
corsmethods = ["POST", "GET"]
allowfrom = "https://localhost:8443"

[logging]
# type = ["text","json"]
type = "text"
# loglevel = ["info","error","debug"]
loglevel = "debug"
# output = ["stdout","logfile"]
output = "stdout"
# only if output = "logfile"
logfile = "mylog.log"

[ratelimit]
limit = 1500
burst = 300

[[user]]
username = "testuser"
password = "testpass"

[[user]]
username = "username"
password = "password"

Dont fool the reaper ?

Documentation

Overview

Package restfool is a stupidly "foolish" and simple approach of implementing a JSON Restful API.

Initialize the api

confFile := flag.String("c", "conf/api.conf", "path to config ile")
flag.Parse()

api, err := rest.New(*confFile)
if err != nil {
	log.Fatal(err)
}

Add your own handler and go

err = api.AddHandler("Index", "GET", "/", "default page", Index)
if err != nil {
	log.Fatal(err)
}

err = api.Serve()
if err != nil {
	log.Fatal(err)
}

Handler definition

func Index(w http.ResponseWriter, r *http.Request) {
	qs := rest.ParseQueryStrings(r)
	message := fmt.Sprintf("Welcome to restfool take a look at https://%s/help", r.Host)
	msg := rest.Msg{Message: message}
	rest.EncodeAndSend(w, r, qs, msg)
}

Configuration example

[general]
# listen supports also IPv6 addresses like :: or ::1
listen = "127.0.0.1"
port = "9443"
basicauth = false

[certs]
public = "certs/server.crt"
private = "certs/server.key"

[tls]
# supported minimal ssl/tls version
# minversion = ["ssl30", "tls10", "tls11", "tls12"]
minversion = "tls12"
# used eliptical curves
# curveprefs = ["p256","p384","p521","x25519"]
curveprefs = ["p256","p384","p521"]
# allowed ciphers
# ciphers = [
#  "TLS_RSA_WITH_RC4_128_SHA",
#  "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
#  "TLS_RSA_WITH_AES_128_CBC_SHA",
#  "TLS_RSA_WITH_AES_256_CBC_SHA",
#  "TLS_RSA_WITH_AES_128_CBC_SHA256",
#  "TLS_RSA_WITH_AES_128_GCM_SHA256",
#  "TLS_RSA_WITH_AES_256_GCM_SHA384",
#  "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
#  "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
#  "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
#  "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
#  "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
#  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
#  "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
#  "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
#  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
#  "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
#  "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
#  "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
#  "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
#  "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
#  "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
#]
ciphers = [
		"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
		"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
		"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
		"TLS_RSA_WITH_AES_256_GCM_SHA384",
]
# if not set equal to false
preferserverciphers = true
# HTTP Strict Transport Security
hsts = true
hstsmaxage = 63072000

# cross origin policy
[cors]
allowcrossorigin = true
# corsmethods = ["POST", "GET", "OPTIONS", "PUT", "DELETE"]
corsmethods = ["POST", "GET"]
allowfrom = "https://localhost:8443"

[logging]
# type = ["text","json"]
type = "text"
# loglevel = ["info","error","debug"]
loglevel = "debug"
# output = ["stdout","logfile"]
output = "stdout"
# only if output = "logfile"
logfile = "mylog.log"

[ratelimit]
limit = 1500
burst = 300

[[user]]
username = "testuser"
password = "testpass"

Additional filters

As every useful restful JSON API `?prettify` can be used for pretty print.

Default handlers

By default the throttled.HTTPRateLimiter is used, Basic Auth is supported and CORS Headers are set by the default handlers.

Default routes/path

By default the /help path is added and provides the description interface{} of your added handlers. A nice looking description could look like this:

description := map[string]interface{}{
	"Message": "description message",
	"Post-parameter": map[string]string{
		"parameter": "type - description",
	},
}

Index

Constants

View Source
const (
	// INFO constant
	INFO string = "info"
	// ERROR constant
	ERROR string = "error"
	// DEBUG constant
	DEBUG string = "debug"
	// LOGSTDOUT constant
	LOGSTDOUT string = "stdout"
	// LOGFILE constant
	LOGFILE string = "logfile"
	// LOGFORMATJSON constant
	LOGFORMATJSON string = "json"
	// LOGFORMATTEXT constant
	LOGFORMATTEXT string = "text"
	// SSL30 constant
	SSL30 string = "ssl30"
	// TLS10 constant
	TLS10 string = "tls10"
	// TLS11 constant
	TLS11 string = "tls11"
	// TLS12 constant
	TLS12 string = "tls12"
	// CURVEP256 definitio
	CURVEP256 string = "p256"
	// CURVEP384 constant
	CURVEP384 string = "p384"
	// CURVEP521 constant
	CURVEP521 string = "p521"
	// X25519 constant
	X25519 string = "x25519"
)

Variables

View Source
var CipherMap = map[string]uint16{
	"TLS_RSA_WITH_RC4_128_SHA":                tls.TLS_RSA_WITH_RC4_128_SHA,
	"TLS_RSA_WITH_3DES_EDE_CBC_SHA":           tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
	"TLS_RSA_WITH_AES_128_CBC_SHA":            tls.TLS_RSA_WITH_AES_128_CBC_SHA,
	"TLS_RSA_WITH_AES_256_CBC_SHA":            tls.TLS_RSA_WITH_AES_256_CBC_SHA,
	"TLS_RSA_WITH_AES_128_CBC_SHA256":         tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
	"TLS_RSA_WITH_AES_128_GCM_SHA256":         tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
	"TLS_RSA_WITH_AES_256_GCM_SHA384":         tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
	"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":        tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
	"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
	"TLS_ECDHE_RSA_WITH_RC4_128_SHA":          tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
	"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":     tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
	"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
	"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
	"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":   tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
	"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":   tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
	"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
	"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":   tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
	"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
	"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":    tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
	"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":  tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}

CipherMap contains all cipers

Functions

func Debug

func Debug(msg string, params interface{})

Debug uses the same syntax as Error function but does not support error type and does not check for errors e.g. Debug("debug", fmt.Errorf("my debug msg")) or using log.Fields / map[string]interface type Debug("debug msg", map[string]interface{}{"val1": "foo", "val2": "bar"}

func DebugMsg

func DebugMsg(msg string)

DebugMsg used for simple debug logging without fields

func EncodeAndSend

func EncodeAndSend(w http.ResponseWriter, r *http.Request, qs QueryStrings, msg interface{})

EncodeAndSend handles some filters, json encodes and outputs msg

func Error

func Error(msg string, params interface{})

Error logs out errors using fields e.g. Error("error msg", fmt.Errorf("my error")) or using log.Fields / map[string]interface type Error("error msg", map[string]interface{}{"val1": "foo", "val2": "bar"}

func ErrorMsg

func ErrorMsg(msg string)

ErrorMsg used for simple error logging without fields

func Info

func Info(msg string, params interface{})

Info uses the same syntax as Error function but does not support error type and does not check for errors e.g. Info("info", fmt.Errorf("my info msg")) or using log.Fields / map[string]interface type

Info("info msg", map[string]interface{}{
	"err": "foo",
	"someField": "bar"
}

func InfoMsg

func InfoMsg(msg string)

InfoMsg used for simple info logging without fields

func Log

func Log(msg string, params map[string]interface{}, loglevel string)

Log is the generic logging function used by Debug, Error and Info

func Logger

func Logger(inner http.Handler, name string) http.Handler

Logger is the default logging handler

func SetLogger

func SetLogger(newLogger *log.Logger)

SetLogger replaces the standard logger with custom configuration

Types

type Certs

type Certs struct {
	Public  string
	Private string
}

type Config

type Config struct {
	General   General   `toml:"general"`
	Certs     Certs     `toml:"certs"`
	TLS       TlsConf   `toml:"tls"`
	Cors      Cors      `toml:"cors"`
	Logging   Logging   `toml:"logging"`
	RateLimit RateLimit `toml:"ratelimit"`
	Users     []User    `toml:"user"`
}

config contains all config information

type Cors

type Cors struct {
	AllowCrossOrigin bool
	CorsMethods      []string
	AllowFrom        string
}

type ErrMsg

type ErrMsg struct {
	Error string `json:"error"`
}

ErrMsg is the standard error message type

type General

type General struct {
	Listen    string
	Port      string
	BasicAuth bool
}

type Logging

type Logging struct {
	Type     string
	Loglevel string
	Output   string
	Logfile  string
}

type Msg

type Msg struct {
	Message string `json:"message"`
}

Msg is the standard message type

type QueryStrings

type QueryStrings struct {
	// contains filtered or unexported fields
}

QueryStrings contains all possible query options

func ParseQueryStrings

func ParseQueryStrings(r *http.Request) QueryStrings

ParseQueryStrings parses filters

type RateLimit

type RateLimit struct {
	Limit int
}

type RestAPI

type RestAPI struct {
	Conf   Config
	Routes []Route
}

RestAPI contains api data

func New

func New(c Config) (RestAPI, error)

New is the restfool constructor

func (*RestAPI) AddHandler

func (a *RestAPI) AddHandler(name string, method string, path string, description interface{}, callback http.HandlerFunc) error

AddHandler adds a new handler to the routing list

func (*RestAPI) AddRoutes

func (a *RestAPI) AddRoutes(router *goji.Mux)

AddRoutes add default handler, routing and ratelimit

func (*RestAPI) InitLogger

func (a *RestAPI) InitLogger()

func (*RestAPI) NewRouter

func (a *RestAPI) NewRouter() *goji.Mux

NewRouter is the router constructor

func (*RestAPI) Serve

func (a *RestAPI) Serve() error

Serve creates and starts the restfull server and listener

type Route

type Route struct {
	Name        string
	Method      string
	Pattern     string
	Description interface{}
	HandlerFunc http.HandlerFunc
}

Route contains all information needed for path routing and help generation

type TlsConf

type TlsConf struct {
	Encryption          bool
	Minversion          string
	CurvePrefs          []string
	Ciphers             []string
	PreferServerCiphers bool
	Hsts                bool
	HstsMaxAge          int
}

type User

type User struct {
	Username string
	Password string
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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