mirror of
https://github.com/foomo/gofoomo.git
synced 2025-10-16 12:25:44 +00:00
housekeeping: code cleanup, tests and hello travis
This commit is contained in:
parent
a7f428b26d
commit
9164c6064e
29
README.md
29
README.md
@ -1,3 +1,5 @@
|
||||
[](https://travis-ci.org/foomo/gofoomo)
|
||||
|
||||
# gofoomo
|
||||
|
||||
Gofoomo lets you use Go in your foomo project. It also lets you use php in your Go project.
|
||||
@ -28,7 +30,32 @@ Gofoomo lets you intercept and implement calls to foomo json rpc services. In ad
|
||||
|
||||
## Access foomo configurations
|
||||
|
||||
Gofoomo gives you access to foomo configurations from Go. Hint: if your php configuration objects are well annotated they are essentially value objects and corresponding structs can easily be generated with Foomo.Go.
|
||||
Gofoomo gives you access to foomo configurations from Go. Hint: if your php configuration objects are well annotated they are essentially value objects and corresponding structs can easily be generated with Foomo.Go.
|
||||
|
||||
## foomo-bert
|
||||
|
||||
Is a command line utility, that helps you with the setup of foomo installations.
|
||||
|
||||
```bash
|
||||
go install github.com/foomo/gofoomo/foomo-bert
|
||||
foomo-bert -help
|
||||
usage: foomo-bert <command>
|
||||
foomo-bert prepare :
|
||||
-dir string
|
||||
path/to/your/foomo/root
|
||||
-run-mode string
|
||||
foomo run mode test | development | production
|
||||
foomo-bert reset :
|
||||
-addr string
|
||||
address of the foomo server
|
||||
-dir string
|
||||
path/to/your/foomo/root
|
||||
-main-module string
|
||||
name of main module (default "Foomo")
|
||||
-run-mode string
|
||||
foomo run mode test | development | production
|
||||
|
||||
```
|
||||
|
||||
## More to come, but not much more
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ var flagConfig = flag.String("config", "", "config file")
|
||||
func main() {
|
||||
fmt.Println("I am a little demo program")
|
||||
flag.Parse()
|
||||
proxyServer, err := proxy.NewProxyServerWithConfig(*flagConfig)
|
||||
proxyServer, err := proxy.NewServerWithConfig(*flagConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/gofoomo/foomo/bert"
|
||||
"github.com/foomo/gofoomo/utils"
|
||||
@ -144,14 +143,12 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(*flagsPrepare.admin) > 0 {
|
||||
fmt.Println("adding admin user", *flagsPrepare.admin)
|
||||
passwd, err := speakeasy.Ask("enter password for " + *flagsPrepare.admin)
|
||||
err = b.PrepareAdmin(*flagsPrepare.admin)
|
||||
if err != nil {
|
||||
fmt.Println("could not read password, giving up")
|
||||
fmt.Println("could not set password for " + *flagsPrepare.admin + " " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("adding password", passwd, "for", *flagsPrepare.admin)
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
case "reset":
|
||||
fs, flagsReset := foomoFlagsetReset()
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
package bert
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func readConfig(filename string) (config map[string][]string, err error) {
|
||||
config = make(map[string][]string)
|
||||
yamlBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = yaml.Unmarshal(yamlBytes, config)
|
||||
return
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package bert
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/gofoomo/utils"
|
||||
)
|
||||
|
||||
func TestReadConfig(t *testing.T) {
|
||||
config, err := readConfig(path.Join(utils.GetCurrentDir(), "basicauth_test", "test.yml"))
|
||||
utils.PanicOnErr(err)
|
||||
expectedAuthDomains := map[string][]string{
|
||||
"default": []string{
|
||||
"secret/foo",
|
||||
"secret/bar",
|
||||
},
|
||||
"sepp": []string{
|
||||
"secret/sepp/test",
|
||||
},
|
||||
}
|
||||
for expectedAuthDomain, secrets := range expectedAuthDomains {
|
||||
configSecrets, ok := config[expectedAuthDomain]
|
||||
if !ok {
|
||||
t.Fatal("missing auth domain", expectedAuthDomain)
|
||||
}
|
||||
for index, secret := range secrets {
|
||||
configSecret := configSecrets[index]
|
||||
if configSecret != secret {
|
||||
t.Fatal("unexpected secret", secret, "should be", configSecret)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
jan:$2y$05$xHK1qpXKtLof8.5WYEQGVuR0GFuTdzcbjNHfUMhK6PjoU4TtD6DmW
|
||||
test:$2y$05$aUToamwisvuLuRtFcpEvY./yT83dGwlMMa8bKD5IduagQwYIv4gym
|
||||
foo:$2y$05$25AK9DVp0P4hpqRq5Zu4HeA0iWoaR/exloH07mKCrdZ4pMD0q8H6.
|
||||
@ -1,6 +0,0 @@
|
||||
---
|
||||
default:
|
||||
- secret/foo
|
||||
- secret/bar
|
||||
sepp:
|
||||
- secret/sepp/test
|
||||
@ -1,20 +1,19 @@
|
||||
package bert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
)
|
||||
|
||||
// Bert is a foomo helper
|
||||
type Bert struct {
|
||||
foomo *foomo.Foomo
|
||||
}
|
||||
|
||||
// NewBert constructor
|
||||
func NewBert(f *foomo.Foomo) *Bert {
|
||||
return &Bert{
|
||||
foomo: f,
|
||||
@ -27,29 +26,7 @@ type moduleList struct {
|
||||
|
||||
const line = "-------------------------------------------------------------------------------"
|
||||
|
||||
func (b *Bert) Reset(mainModuleName string) error {
|
||||
err := b.get("resetting everything", "/foomo/hiccup.php?class=hiccup&action=resetEverything")
|
||||
if err != nil {
|
||||
return errors.New("failed to reset everything: " + err.Error())
|
||||
}
|
||||
|
||||
err = b.get("enabling main module "+mainModuleName, "/foomo/core.php/enableModule/"+mainModuleName)
|
||||
if err != nil {
|
||||
return errors.New("enabling main module failed" + err.Error())
|
||||
}
|
||||
|
||||
err = b.get("trying to create missing module resources", "/foomo/core.php/tryCreateModuleResources")
|
||||
if err != nil {
|
||||
return errors.New("failed to create module resources" + err.Error())
|
||||
}
|
||||
|
||||
err = b.get("running make clean all", "/foomo/core.php/make/clean,all")
|
||||
if err != nil {
|
||||
return errors.New("make clean, all failed: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// plain http get
|
||||
func (b *Bert) get(explanation string, path string) error {
|
||||
fmt.Println(explanation)
|
||||
fmt.Println(line)
|
||||
@ -73,31 +50,3 @@ func (b *Bert) get(explanation string, path string) error {
|
||||
fmt.Println(line)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bert) Prepare() error {
|
||||
b.foomo.GetVarDir()
|
||||
dirs := []string{
|
||||
"composer",
|
||||
"docker",
|
||||
path.Join("config", b.foomo.RunMode, "foomo"),
|
||||
path.Join("var", b.foomo.RunMode),
|
||||
path.Join("var", b.foomo.RunMode, "logs"),
|
||||
path.Join("var", b.foomo.RunMode, "basicAuth"),
|
||||
path.Join("var", b.foomo.RunMode, "sessions"),
|
||||
path.Join("var", b.foomo.RunMode, "tmp"),
|
||||
path.Join("var", b.foomo.RunMode, "logs"),
|
||||
path.Join("var", b.foomo.RunMode, "cache"),
|
||||
path.Join("var", b.foomo.RunMode, "modules"),
|
||||
path.Join("var", b.foomo.RunMode, "htdocs", "modules"),
|
||||
path.Join("var", b.foomo.RunMode, "htdocs", "modulesVar"),
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
dirToMake := path.Join(b.foomo.Root, dir)
|
||||
mkdirErr := os.MkdirAll(dirToMake, 0744)
|
||||
if mkdirErr != nil {
|
||||
return mkdirErr
|
||||
}
|
||||
fmt.Println("created path", dirToMake)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
63
foomo/bert/prepare.go
Normal file
63
foomo/bert/prepare.go
Normal file
@ -0,0 +1,63 @@
|
||||
package bert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/htpasswd"
|
||||
)
|
||||
|
||||
// Prepare an installation (very basic skeleton)
|
||||
func (b *Bert) Prepare() error {
|
||||
b.foomo.GetVarDir()
|
||||
dirs := []string{
|
||||
"composer",
|
||||
"docker",
|
||||
path.Join("config", b.foomo.RunMode, "foomo"),
|
||||
path.Join("var", b.foomo.RunMode),
|
||||
path.Join("var", b.foomo.RunMode, "logs"),
|
||||
path.Join("var", b.foomo.RunMode, "basicAuth"),
|
||||
path.Join("var", b.foomo.RunMode, "sessions"),
|
||||
path.Join("var", b.foomo.RunMode, "tmp"),
|
||||
path.Join("var", b.foomo.RunMode, "logs"),
|
||||
path.Join("var", b.foomo.RunMode, "cache"),
|
||||
path.Join("var", b.foomo.RunMode, "modules"),
|
||||
path.Join("var", b.foomo.RunMode, "htdocs", "modules"),
|
||||
path.Join("var", b.foomo.RunMode, "htdocs", "modulesVar"),
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
dirToMake := path.Join(b.foomo.Root, dir)
|
||||
mkdirErr := os.MkdirAll(dirToMake, 0744)
|
||||
if mkdirErr != nil {
|
||||
return mkdirErr
|
||||
}
|
||||
fmt.Println("created path", dirToMake)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrepareAdmin asks and adds basic auth credentials for foomo default auth\
|
||||
// domain
|
||||
func (b *Bert) PrepareAdmin(admin string) error {
|
||||
|
||||
fmt.Println("adding admin user", admin)
|
||||
|
||||
passwd, err := speakeasy.Ask("enter password for " + admin + " 🔑 :\n")
|
||||
if err != nil {
|
||||
return errors.New("could not read password, giving up")
|
||||
}
|
||||
passwordFile := b.foomo.GetBasicAuthFilename(foomo.DefaultBasicAuthDomainName)
|
||||
fmt.Println("adding password for admin in:", passwordFile)
|
||||
|
||||
err = htpasswd.SetPassword(passwordFile, admin, passwd, htpasswd.HashBCrypt)
|
||||
if err != nil {
|
||||
return errors.New("could not write default basic auth password for admin: " + err.Error())
|
||||
}
|
||||
fmt.Println("added password")
|
||||
fmt.Println("DONE")
|
||||
return nil
|
||||
}
|
||||
27
foomo/bert/reset.go
Normal file
27
foomo/bert/reset.go
Normal file
@ -0,0 +1,27 @@
|
||||
package bert
|
||||
|
||||
import "errors"
|
||||
|
||||
// Reset reset a bert installation
|
||||
func (b *Bert) Reset(mainModuleName string) error {
|
||||
err := b.get("resetting everything", "/foomo/hiccup.php?class=hiccup&action=resetEverything")
|
||||
if err != nil {
|
||||
return errors.New("failed to reset everything: " + err.Error())
|
||||
}
|
||||
|
||||
err = b.get("enabling main module "+mainModuleName, "/foomo/core.php/enableModule/"+mainModuleName)
|
||||
if err != nil {
|
||||
return errors.New("enabling main module failed" + err.Error())
|
||||
}
|
||||
|
||||
err = b.get("trying to create missing module resources", "/foomo/core.php/tryCreateModuleResources")
|
||||
if err != nil {
|
||||
return errors.New("failed to create module resources" + err.Error())
|
||||
}
|
||||
|
||||
err = b.get("running make clean all", "/foomo/core.php/make/clean,all")
|
||||
if err != nil {
|
||||
return errors.New("make clean, all failed: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -11,12 +11,12 @@ import (
|
||||
)
|
||||
|
||||
func get(foomo *foomo.Foomo, path ...string) (data []byte, err error) {
|
||||
callUrl := foomo.GetURLWithCredentialsForDefaultBasicAuthDomain()
|
||||
callURL := foomo.GetURLWithCredentialsForDefaultBasicAuthDomain()
|
||||
encodedPath := ""
|
||||
for _, pathEntry := range path {
|
||||
encodedPath += "/" + url.QueryEscape(pathEntry)
|
||||
}
|
||||
req, err := http.NewRequest("GET", callUrl+"/foomo/core.php"+encodedPath, nil)
|
||||
req, err := http.NewRequest("GET", callURL+"/foomo/core.php"+encodedPath, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -36,19 +36,19 @@ func get(foomo *foomo.Foomo, path ...string) (data []byte, err error) {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// GetJSON call into foomo and unmarshal response using encoding/json
|
||||
func GetJSON(foomo *foomo.Foomo, target interface{}, path ...string) error {
|
||||
data, err := get(foomo, path...)
|
||||
if err == nil {
|
||||
return json.Unmarshal(data, &target)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetConfig retrieve a domain config
|
||||
func GetConfig(foomo *foomo.Foomo, target interface{}, moduleName string, configName string, domain string) (err error) {
|
||||
if len(domain) == 0 {
|
||||
return GetJSON(foomo, target, "config", moduleName, configName)
|
||||
} else {
|
||||
return GetJSON(foomo, target, "config", moduleName, configName, domain)
|
||||
}
|
||||
return GetJSON(foomo, target, "config", moduleName, configName, domain)
|
||||
}
|
||||
|
||||
@ -2,8 +2,15 @@ package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/gofoomo/foomo/bert"
|
||||
)
|
||||
|
||||
type CoreConfig struct {
|
||||
@ -15,9 +22,41 @@ type CoreConfig struct {
|
||||
|
||||
var testFoomo *foomo.Foomo
|
||||
|
||||
func poe(err error, msg string) {
|
||||
if err != nil {
|
||||
panic(msg + " : " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func getTestServer() *httptest.Server {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`{
|
||||
"enabledModules": [
|
||||
"Foomo"
|
||||
],
|
||||
"availableModules": [
|
||||
"Foomo"
|
||||
]
|
||||
}`))
|
||||
}))
|
||||
return ts
|
||||
}
|
||||
|
||||
func getTestFoomo() *foomo.Foomo {
|
||||
|
||||
if testFoomo == nil {
|
||||
f, _ := foomo.NewFoomo("/Users/jan/vagrant/schild/www/schild", "test", "http://schild-local-test.bestbytes.net")
|
||||
ts := getTestServer()
|
||||
tmp := os.TempDir()
|
||||
dir, err := ioutil.TempDir(tmp, "dummy-foomo")
|
||||
poe(err, "failed to get temp dir")
|
||||
bareFoomo, err := foomo.BareFoomo(dir, "test")
|
||||
poe(err, "failed to get bare foomo")
|
||||
b := bert.NewBert(bareFoomo)
|
||||
b.Prepare()
|
||||
f, err := foomo.NewFoomo(dir, "test", fmt.Sprint(ts.URL))
|
||||
if err != nil {
|
||||
panic("invalid test foomo " + err.Error())
|
||||
}
|
||||
testFoomo = f
|
||||
}
|
||||
return testFoomo
|
||||
|
||||
100
foomo/foomo.go
100
foomo/foomo.go
@ -1,25 +1,25 @@
|
||||
package foomo
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
u "net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/foomo/htpasswd"
|
||||
)
|
||||
|
||||
const (
|
||||
RunModeTest = "test"
|
||||
// RunModeTest run mode test
|
||||
RunModeTest = "test"
|
||||
// RunModeDevelopment run mode development
|
||||
RunModeDevelopment = "development"
|
||||
RunModeProduction = "production"
|
||||
// RunModeProduction run mode production
|
||||
RunModeProduction = "production"
|
||||
// DefaultBasicAuthDomainName name of default basic auth domain
|
||||
DefaultBasicAuthDomainName = "default"
|
||||
)
|
||||
|
||||
const shaPrefix = "{SHA}"
|
||||
|
||||
// Foomo foomo go wrapper
|
||||
type Foomo struct {
|
||||
Root string
|
||||
@ -72,118 +72,56 @@ func makeFoomo(foomoDir string, runMode string, address string, init bool) (f *F
|
||||
if init {
|
||||
authErr := f.setupBasicAuthCredentials()
|
||||
if authErr != nil {
|
||||
return nil, authErr
|
||||
return nil, errors.New("can not set up auth: " + authErr.Error())
|
||||
}
|
||||
}
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (f *Foomo) BasicAuth(domain string, user string, password string) bool {
|
||||
for _, line := range strings.Split(f.getBasicAuthFileContentsForDomain(domain), "\n") {
|
||||
lineParts := strings.Split(line, ":")
|
||||
if len(lineParts) == 2 && lineParts[0] == user {
|
||||
hash := getBasicAuthHash(password)
|
||||
return hash == lineParts[1]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *Foomo) BasicAuthForRequest(w http.ResponseWriter, incomingRequest *http.Request, domain string, realm string, denialHTML string) bool {
|
||||
forbidden := func() bool {
|
||||
realm := strings.Replace(realm, "\"", "'", -1)
|
||||
w.Header().Set("Www-Authenticate", "Basic realm=\""+realm+"\"")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(denialHTML))
|
||||
return false
|
||||
}
|
||||
authHeader := incomingRequest.Header.Get("Authorization")
|
||||
if len(authHeader) == 0 {
|
||||
return forbidden()
|
||||
}
|
||||
auth, base64DecodingErr := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, "Basic "))
|
||||
if base64DecodingErr != nil {
|
||||
return forbidden()
|
||||
}
|
||||
authParts := strings.Split(string(auth), ":")
|
||||
if len(authParts) != 2 {
|
||||
return forbidden()
|
||||
}
|
||||
if f.BasicAuth(domain, authParts[0], authParts[1]) {
|
||||
return true
|
||||
} else {
|
||||
return forbidden()
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Foomo) getBasicAuthFileContentsForDomain(domain string) string {
|
||||
basicAuthFilename := f.GetBasicAuthFilename(domain)
|
||||
bytes, err := ioutil.ReadFile(basicAuthFilename)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Foomo) setupBasicAuthCredentials() error {
|
||||
f.basicAuthCredentials.user = "gofoomo"
|
||||
f.basicAuthCredentials.password = makeToken(50)
|
||||
return ioutil.WriteFile(f.GetBasicAuthFilename("default"), []byte(setBasicAuthForUserInBasicAuthFileContents(f.getBasicAuthFileContentsForDomain("default"), f.basicAuthCredentials.user, f.basicAuthCredentials.password)), 0644)
|
||||
}
|
||||
|
||||
func setBasicAuthForUserInBasicAuthFileContents(basicAuthFileContents string, user string, password string) string {
|
||||
newLines := make([]string, 0)
|
||||
LineLoop:
|
||||
for _, line := range strings.Split(basicAuthFileContents, "\n") {
|
||||
lineParts := strings.Split(line, ":")
|
||||
if len(lineParts) == 2 && lineParts[0] == user {
|
||||
continue LineLoop
|
||||
} else if len(line) > 0 {
|
||||
newLines = append(newLines, line)
|
||||
}
|
||||
}
|
||||
newLines = append(newLines, user+":"+getBasicAuthHash(password))
|
||||
return strings.Join(newLines, "\n") + "\n"
|
||||
}
|
||||
|
||||
func getBasicAuthHash(password string) string {
|
||||
s := sha1.New()
|
||||
s.Write([]byte(password))
|
||||
passwordSum := []byte(s.Sum(nil))
|
||||
return shaPrefix + base64.StdEncoding.EncodeToString(passwordSum)
|
||||
return htpasswd.SetPassword(f.GetBasicAuthFilename("default"), f.basicAuthCredentials.user, f.basicAuthCredentials.password, htpasswd.HashBCrypt)
|
||||
}
|
||||
|
||||
// GetURLWithCredentialsForDefaultBasicAuthDomain i.e. sth. like http(s)://user:password@foomo-server.org(:8080)
|
||||
func (f *Foomo) GetURLWithCredentialsForDefaultBasicAuthDomain() string {
|
||||
url, _ := u.Parse(f.URL.String())
|
||||
url.User = u.UserPassword(f.basicAuthCredentials.user, f.basicAuthCredentials.password)
|
||||
return url.String()
|
||||
}
|
||||
|
||||
// GetBasicAuthCredentialsForDefaultBasicAuthDomain user, password generated for the local foomo instance
|
||||
func (f *Foomo) GetBasicAuthCredentialsForDefaultBasicAuthDomain() (user string, password string) {
|
||||
return f.basicAuthCredentials.user, f.basicAuthCredentials.password
|
||||
}
|
||||
|
||||
// GetModuleDir root dir of a module
|
||||
func (f *Foomo) GetModuleDir(moduleName string, dir string) string {
|
||||
return f.Root + "/modules/" + moduleName + "/" + dir
|
||||
}
|
||||
|
||||
// GetVarDir root var dir for the current run mode
|
||||
func (f *Foomo) GetVarDir() string {
|
||||
return f.Root + "/var/" + f.RunMode
|
||||
}
|
||||
|
||||
// GetModuleHtdocsDir htdocs dir in a module
|
||||
func (f *Foomo) GetModuleHtdocsDir(moduleName string) string {
|
||||
return f.GetModuleDir(moduleName, "htdocs")
|
||||
}
|
||||
|
||||
// GetModuleCacheDir cache dir for a module
|
||||
func (f *Foomo) GetModuleCacheDir(moduleName string) string {
|
||||
return f.GetVarDir() + "/cache/" + moduleName
|
||||
}
|
||||
|
||||
// GetModuleHtdocsVarDir module htdocs var dir
|
||||
func (f *Foomo) GetModuleHtdocsVarDir(moduleName string) string {
|
||||
return f.GetVarDir() + "/htdocs/modulesVar/" + moduleName
|
||||
}
|
||||
|
||||
// GetBasicAuthFilename basic auth file name for a domain
|
||||
func (f *Foomo) GetBasicAuthFilename(domain string) string {
|
||||
return f.GetVarDir() + "/basicAuth/" + domain
|
||||
}
|
||||
|
||||
@ -1,23 +1,22 @@
|
||||
package foomo
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetBasicAuthForUserInBasicAuthFileContents(t *testing.T) {
|
||||
ba := "foo:bar\ntest:gone\nhansi:toll"
|
||||
newBa := setBasicAuthForUserInBasicAuthFileContents(ba, "test", "test")
|
||||
if len(strings.Split(newBa, "\n")) != 3 {
|
||||
t.Fatal("wrong line count")
|
||||
}
|
||||
}
|
||||
|
||||
func getTestFoomoForFSStuff() *Foomo {
|
||||
f, _ := makeFoomo("/var/www/foomo", "test", "http://test.foomo", false)
|
||||
tempDir := os.TempDir()
|
||||
f, err := BareFoomo(tempDir[0:len(tempDir)-1], "test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func assertTempPath(t *testing.T, topic string, expected string, actual string) {
|
||||
assertStringsEqual(t, topic, os.TempDir()+expected, actual)
|
||||
}
|
||||
func assertStringsEqual(t *testing.T, topic string, expected string, actual string) {
|
||||
if actual != expected {
|
||||
t.Fatal(topic, "actual: ", actual, " != expected: ", expected)
|
||||
@ -26,22 +25,42 @@ func assertStringsEqual(t *testing.T, topic string, expected string, actual stri
|
||||
|
||||
func TestGetVarDir(t *testing.T) {
|
||||
actual := getTestFoomoForFSStuff().GetVarDir()
|
||||
expected := "/var/www/foomo/var/test"
|
||||
assertStringsEqual(t, "var dir", expected, actual)
|
||||
expected := "var/test"
|
||||
assertTempPath(t, "var dir", expected, actual)
|
||||
}
|
||||
|
||||
func TestGetModuleDir(t *testing.T) {
|
||||
assertStringsEqual(t, "module dir", "/var/www/foomo/modules/Foomo/htdocs", getTestFoomoForFSStuff().GetModuleDir("Foomo", "htdocs"))
|
||||
assertTempPath(
|
||||
t,
|
||||
"module dir",
|
||||
"modules/Foomo/htdocs",
|
||||
getTestFoomoForFSStuff().GetModuleDir("Foomo", "htdocs"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetModuleHtdocsDir(t *testing.T) {
|
||||
assertStringsEqual(t, "module htdocs dir", "/var/www/foomo/modules/Foomo/htdocs", getTestFoomoForFSStuff().GetModuleHtdocsDir("Foomo"))
|
||||
assertTempPath(
|
||||
t,
|
||||
"module htdocs dir",
|
||||
"modules/Foomo/htdocs",
|
||||
getTestFoomoForFSStuff().GetModuleHtdocsDir("Foomo"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetModuleHtdocsVarDir(t *testing.T) {
|
||||
assertStringsEqual(t, "module htdocs var dir", "/var/www/foomo/var/test/htdocs/modulesVar/Foomo", getTestFoomoForFSStuff().GetModuleHtdocsVarDir("Foomo"))
|
||||
assertTempPath(
|
||||
t,
|
||||
"module htdocs var dir",
|
||||
"var/test/htdocs/modulesVar/Foomo",
|
||||
getTestFoomoForFSStuff().GetModuleHtdocsVarDir("Foomo"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetBasicAuthFilename(t *testing.T) {
|
||||
assertStringsEqual(t, "basic auth file", "/var/www/foomo/var/test/basicAuth/sepp", getTestFoomoForFSStuff().GetBasicAuthFilename("sepp"))
|
||||
assertTempPath(
|
||||
t,
|
||||
"basic auth file",
|
||||
"var/test/basicAuth/sepp",
|
||||
getTestFoomoForFSStuff().GetBasicAuthFilename("sepp"),
|
||||
)
|
||||
}
|
||||
|
||||
@ -5,32 +5,26 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/foomo/tlsconfig"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Auth basic auth
|
||||
type Auth struct {
|
||||
Domain string
|
||||
Realm string
|
||||
}
|
||||
|
||||
// TLS proxy tls config vo
|
||||
type TLS struct {
|
||||
Mode string
|
||||
Mode tlsconfig.TLSModeServer
|
||||
Address string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
}
|
||||
|
||||
const (
|
||||
// this is serious and we do not mind loosing clients (= Mozilla "modern" compatibility)
|
||||
// Compatible clients have versions equal or greater than Firefox 27, Chrome 22, IE 11, Opera 14, Safari 7, Android 4.4, Java 8
|
||||
TLSModeStrict = "strict"
|
||||
// ecommerce compromise
|
||||
// Compatible clients (>=): Firefox 1, Chrome 1, IE 7, Opera 5, Safari 1, Windows XP IE8, Android 2.3, Java 7
|
||||
TLSModeLoose = "loose"
|
||||
// standard crypto/tls.Config un touched - highly compatible, but possibly insecure
|
||||
TLSModeDefault = "default"
|
||||
)
|
||||
|
||||
// Config proxy configuration
|
||||
type Config struct {
|
||||
// how should the proxy server run
|
||||
Server struct {
|
||||
@ -51,6 +45,7 @@ type Config struct {
|
||||
AppOptions map[string]string
|
||||
}
|
||||
|
||||
// ReadConfig from a file
|
||||
func ReadConfig(filename string) (config *Config, err error) {
|
||||
config = &Config{}
|
||||
yamlBytes, err := ioutil.ReadFile(filename)
|
||||
@ -62,12 +57,12 @@ func ReadConfig(filename string) (config *Config, err error) {
|
||||
return nil, err
|
||||
}
|
||||
if config.Server.TLS.Mode == "" {
|
||||
config.Server.TLS.Mode = TLSModeDefault
|
||||
config.Server.TLS.Mode = tlsconfig.TLSModeServerDefault
|
||||
}
|
||||
switch config.Server.TLS.Mode {
|
||||
case TLSModeDefault, TLSModeLoose, TLSModeStrict:
|
||||
case tlsconfig.TLSModeServerDefault, tlsconfig.TLSModeServerLoose, tlsconfig.TLSModeServerStrict:
|
||||
default:
|
||||
return nil, errors.New("unknown server.tls.mode: " + config.Server.TLS.Mode + " - must be one of: " + fmt.Sprintln([]string{TLSModeDefault, TLSModeLoose, TLSModeStrict}))
|
||||
return nil, errors.New("unknown server.tls.mode: " + string(config.Server.TLS.Mode) + " - must be one of: " + fmt.Sprintln([]tlsconfig.TLSModeServer{tlsconfig.TLSModeServerDefault, tlsconfig.TLSModeServerLoose, tlsconfig.TLSModeServerStrict}))
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@ -1,38 +1,53 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/abbot/go-http-auth"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/gofoomo/proxy"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// AuthWrapper wraps proxy handlers with a basic authentication
|
||||
type AuthWrapper struct {
|
||||
handler proxy.Handler
|
||||
foomo *foomo.Foomo
|
||||
authDomain string
|
||||
handler proxy.Handler
|
||||
foomo *foomo.Foomo
|
||||
// authDomain string
|
||||
denialHTML string
|
||||
realm string
|
||||
//realm string
|
||||
authenticator *auth.BasicAuth
|
||||
}
|
||||
|
||||
// NewAuthWrapper wrap a proxy handler with basic auth
|
||||
func NewAuthWrapper(f *foomo.Foomo, h proxy.Handler, authDomain string, realm string, denialHTML string) *AuthWrapper {
|
||||
secretProvider := auth.HtpasswdFileProvider(f.GetBasicAuthFilename(authDomain))
|
||||
return &AuthWrapper{
|
||||
handler: h,
|
||||
foomo: f,
|
||||
realm: realm,
|
||||
authDomain: authDomain,
|
||||
denialHTML: denialHTML,
|
||||
handler: h,
|
||||
foomo: f,
|
||||
// realm: realm,
|
||||
// authDomain: authDomain,
|
||||
denialHTML: denialHTML,
|
||||
authenticator: auth.NewBasicAuthenticator(realm, secretProvider),
|
||||
}
|
||||
}
|
||||
|
||||
// HandlesRequest am I responsible to handle that request
|
||||
func (a *AuthWrapper) HandlesRequest(incomingRequest *http.Request) bool {
|
||||
// asking my underlying handler
|
||||
return a.handler.HandlesRequest(incomingRequest)
|
||||
}
|
||||
|
||||
func (a *AuthWrapper) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
||||
if !a.foomo.BasicAuthForRequest(w, incomingRequest, a.authDomain, "authenticate", "nope") {
|
||||
log.Println("auth wrapper access denied for", incomingRequest.RequestURI)
|
||||
func (a *AuthWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
user := a.authenticator.CheckAuth(r)
|
||||
if len(user) == 0 {
|
||||
a.authenticator.RequireAuth(w, r)
|
||||
}
|
||||
user = a.authenticator.CheckAuth(r)
|
||||
if len(user) == 0 {
|
||||
w.Write([]byte(a.denialHTML))
|
||||
return
|
||||
}
|
||||
a.handler.ServeHTTP(w, incomingRequest)
|
||||
|
||||
a.handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
@ -2,14 +2,16 @@ package images
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
)
|
||||
|
||||
// ImageInfo image cache meta data
|
||||
type ImageInfo struct {
|
||||
Filename string
|
||||
Etag string
|
||||
@ -18,6 +20,7 @@ type ImageInfo struct {
|
||||
Expires int64
|
||||
}
|
||||
|
||||
// NewImageInfo constructor
|
||||
func NewImageInfo(response *http.Response) *ImageInfo {
|
||||
i := new(ImageInfo)
|
||||
if response != nil {
|
||||
@ -35,11 +38,13 @@ func (i *ImageInfo) getHeader(name string) []string {
|
||||
return h
|
||||
}
|
||||
|
||||
// ImageRequestResult result vo
|
||||
type ImageRequestResult struct {
|
||||
Error error
|
||||
Request *ImageRequest
|
||||
}
|
||||
|
||||
// ImageRequest request vo
|
||||
type ImageRequest struct {
|
||||
Id string
|
||||
IncomingRequest *http.Request
|
||||
@ -48,6 +53,7 @@ type ImageRequest struct {
|
||||
ImageInfo *ImageInfo
|
||||
}
|
||||
|
||||
// NewImageRequest image request constructor
|
||||
func NewImageRequest(id string, incomingRequest *http.Request, foomoMediaClientInfoCookie *http.Cookie) *ImageRequest {
|
||||
r := new(ImageRequest)
|
||||
r.Id = id
|
||||
@ -64,6 +70,7 @@ func (i *ImageRequest) execute(cache *Cache) error {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// Cache image cache
|
||||
type Cache struct {
|
||||
Directory map[string]*ImageInfo
|
||||
Foomo *foomo.Foomo
|
||||
@ -75,6 +82,7 @@ type Cache struct {
|
||||
//doneChannel chan *ImageRequest
|
||||
}
|
||||
|
||||
// NewCache cache constructor
|
||||
func NewCache(f *foomo.Foomo) *Cache {
|
||||
c := new(Cache)
|
||||
c.Foomo = f
|
||||
@ -124,6 +132,7 @@ func (c *Cache) runLoop() {
|
||||
|
||||
}
|
||||
|
||||
// Get get image with the given breakpoints
|
||||
func (c *Cache) Get(request *http.Request, breakPoints []int64) (info *ImageInfo, err error) {
|
||||
|
||||
cookie := getFoomoMediaClientInfoCookie(request.Cookies(), breakPoints)
|
||||
@ -174,49 +183,47 @@ func (c *Cache) getImage(incomingRequest *http.Request, foomoMediaClientInfoCook
|
||||
|
||||
if err != nil {
|
||||
return NewImageInfo(nil), err
|
||||
} else {
|
||||
request.AddCookie(foomoMediaClientInfoCookie)
|
||||
if c.foomoSessionCookie != nil {
|
||||
request.AddCookie(c.foomoSessionCookie)
|
||||
}
|
||||
request.AddCookie(foomoMediaClientInfoCookie)
|
||||
if c.foomoSessionCookie != nil {
|
||||
request.AddCookie(c.foomoSessionCookie)
|
||||
}
|
||||
request.URL.Opaque = incomingRequest.URL.Opaque
|
||||
request.URL.Host = c.Foomo.URL.Host
|
||||
request.URL.Scheme = c.Foomo.URL.Scheme
|
||||
imageServerResponse, err := c.client.Do(request)
|
||||
i = NewImageInfo(imageServerResponse)
|
||||
if imageServerResponse != nil {
|
||||
defer imageServerResponse.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
if imageServerResponse != nil && imageServerResponse.StatusCode == http.StatusMovedPermanently {
|
||||
return nil, errors.New("unexpected redirect")
|
||||
}
|
||||
request.URL.Opaque = incomingRequest.URL.Opaque
|
||||
request.URL.Host = c.Foomo.URL.Host
|
||||
request.URL.Scheme = c.Foomo.URL.Scheme
|
||||
imageServerResponse, err := c.client.Do(request)
|
||||
i := NewImageInfo(imageServerResponse)
|
||||
if imageServerResponse != nil {
|
||||
defer imageServerResponse.Body.Close()
|
||||
return nil, errors.New("unexpected error " + err.Error())
|
||||
}
|
||||
|
||||
i.StatusCode = imageServerResponse.StatusCode
|
||||
c.checkFoomoSessionCookie(imageServerResponse)
|
||||
switch i.StatusCode {
|
||||
case http.StatusOK, http.StatusNotFound:
|
||||
t, timeErr := time.Parse(time.RFC1123, imageServerResponse.Header.Get("Expires"))
|
||||
if timeErr == nil {
|
||||
i.Expires = t.Unix()
|
||||
} else {
|
||||
i.Expires = 0
|
||||
i.Expires = time.Now().Unix() + 3600
|
||||
log.Println("could not parse expiration time", timeErr)
|
||||
}
|
||||
if err != nil {
|
||||
if imageServerResponse != nil && imageServerResponse.StatusCode == http.StatusMovedPermanently {
|
||||
return nil, errors.New("unexpected redirect")
|
||||
} else {
|
||||
return nil, errors.New("unexpected error " + err.Error())
|
||||
}
|
||||
panic(errors.New("unexpected error " + err.Error()))
|
||||
} else {
|
||||
|
||||
i.StatusCode = imageServerResponse.StatusCode
|
||||
c.checkFoomoSessionCookie(imageServerResponse)
|
||||
switch i.StatusCode {
|
||||
case http.StatusOK, http.StatusNotFound:
|
||||
t, timeErr := time.Parse(time.RFC1123, imageServerResponse.Header.Get("Expires"))
|
||||
if timeErr == nil {
|
||||
i.Expires = t.Unix()
|
||||
} else {
|
||||
i.Expires = 0
|
||||
i.Expires = time.Now().Unix() + 3600
|
||||
log.Println("could not parse expiration time", timeErr)
|
||||
}
|
||||
if err != nil {
|
||||
panic(errors.New("unexpected error " + err.Error()))
|
||||
} else {
|
||||
i.Etag = imageServerResponse.Header.Get("Etag")
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("unexpected reply with status " + imageServerResponse.Status)
|
||||
}
|
||||
i.Etag = imageServerResponse.Header.Get("Etag")
|
||||
}
|
||||
return i, nil
|
||||
|
||||
default:
|
||||
return nil, errors.New("unexpected reply with status " + imageServerResponse.Status)
|
||||
}
|
||||
return i, nil
|
||||
|
||||
}
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
package images
|
||||
|
||||
import (
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/gofoomo/foomo/core"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/gofoomo/foomo/core"
|
||||
)
|
||||
|
||||
// MediaServerConfig foomo config helper
|
||||
type MediaServerConfig struct {
|
||||
Grid map[string]map[string]int64 `json:"grid"`
|
||||
}
|
||||
|
||||
// SessionConfig foomo config helper
|
||||
type SessionConfig struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
@ -24,7 +27,7 @@ func getBreakPoints(f *foomo.Foomo) []int64 {
|
||||
}
|
||||
breakPointsInt := []int{}
|
||||
|
||||
for breakPointString, _ := range c.Grid {
|
||||
for breakPointString := range c.Grid {
|
||||
breakPointInt, _ := strconv.Atoi(breakPointString)
|
||||
if breakPointInt > 0 {
|
||||
breakPointsInt = append(breakPointsInt, breakPointInt)
|
||||
|
||||
@ -10,18 +10,24 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultScreenWidth = 1024
|
||||
DefaultScreenHeight = 768
|
||||
DefaultPixelRatio = 1.0
|
||||
// DefaultScreenWidth still 1024
|
||||
DefaultScreenWidth = 1024
|
||||
// DefaultScreenHeight like in the good old days
|
||||
DefaultScreenHeight = 768
|
||||
// DefaultPixelRatio in case of doubt lightweight
|
||||
DefaultPixelRatio = 1.0
|
||||
// FoomoMediaClientInfoCookieName that is the name of the cookie we inspect to extract the client informattions from
|
||||
FoomoMediaClientInfoCookieName = "foomoMediaClientInfo"
|
||||
)
|
||||
|
||||
// ClientInfo vo
|
||||
type ClientInfo struct {
|
||||
screenWidth int64
|
||||
screenHeight int64
|
||||
pixelRatio float64
|
||||
}
|
||||
|
||||
// NewClientInfo constructor
|
||||
func NewClientInfo() *ClientInfo {
|
||||
info := new(ClientInfo)
|
||||
info.screenWidth = DefaultScreenWidth
|
||||
@ -36,18 +42,17 @@ func readFoomoMediaClientInfo(cookie string) (clientInfo *ClientInfo, err error)
|
||||
if len(parts) != 2 {
|
||||
err = errors.New(fmt.Sprint("could not separate screen size from pixel ratio", parts))
|
||||
return clientInfo, err
|
||||
} else {
|
||||
screenSizeParts := strings.Split(parts[0], "x")
|
||||
if len(screenSizeParts) != 2 {
|
||||
err = errors.New(fmt.Sprint("could not find screen size components ", len(screenSizeParts), " in ", parts[0]))
|
||||
} else {
|
||||
clientInfo = NewClientInfo()
|
||||
clientInfo.pixelRatio, _ = strconv.ParseFloat(parts[1], 32)
|
||||
clientInfo.screenWidth, _ = strconv.ParseInt(screenSizeParts[0], 0, 32)
|
||||
clientInfo.screenHeight, _ = strconv.ParseInt(screenSizeParts[1], 0, 32)
|
||||
}
|
||||
return clientInfo, err
|
||||
}
|
||||
screenSizeParts := strings.Split(parts[0], "x")
|
||||
if len(screenSizeParts) != 2 {
|
||||
err = errors.New(fmt.Sprint("could not find screen size components ", len(screenSizeParts), " in ", parts[0]))
|
||||
} else {
|
||||
clientInfo = NewClientInfo()
|
||||
clientInfo.pixelRatio, _ = strconv.ParseFloat(parts[1], 32)
|
||||
clientInfo.screenWidth, _ = strconv.ParseInt(screenSizeParts[0], 0, 32)
|
||||
clientInfo.screenHeight, _ = strconv.ParseInt(screenSizeParts[1], 0, 32)
|
||||
}
|
||||
return clientInfo, err
|
||||
}
|
||||
|
||||
func clampScreenWidthToGrid(screenWidth int64, breakPoints []int64) int64 {
|
||||
|
||||
@ -2,21 +2,23 @@ package images
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
//"io"
|
||||
//"log"
|
||||
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
)
|
||||
|
||||
// Adaptive adaptive images
|
||||
type Adaptive struct {
|
||||
Paths []string
|
||||
BreakPoints []int64
|
||||
Cache *Cache
|
||||
}
|
||||
|
||||
// NewAdaptive constructor
|
||||
func NewAdaptive(paths []string, f *foomo.Foomo) *Adaptive {
|
||||
a := new(Adaptive)
|
||||
a.Paths = paths
|
||||
@ -25,6 +27,7 @@ func NewAdaptive(paths []string, f *foomo.Foomo) *Adaptive {
|
||||
return a
|
||||
}
|
||||
|
||||
// HandlesRequest request handler interface implementation
|
||||
func (a *Adaptive) HandlesRequest(incomingRequest *http.Request) bool {
|
||||
for _, p := range a.Paths {
|
||||
if strings.HasPrefix(incomingRequest.URL.Path, p) {
|
||||
@ -35,57 +38,27 @@ func (a *Adaptive) HandlesRequest(incomingRequest *http.Request) bool {
|
||||
}
|
||||
|
||||
func (a *Adaptive) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
||||
|
||||
info, err := a.Cache.Get(incomingRequest, a.BreakPoints)
|
||||
|
||||
//log.Println("info for request" + incomingRequest.RequestURI)
|
||||
//log.Println(info)
|
||||
//log.Println("------------------------------------")
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if info == nil {
|
||||
panic(errors.New("could not get image"))
|
||||
} else {
|
||||
// 304 handling
|
||||
file, err := os.Open(info.Filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
fileInfo, err := file.Stat()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
w.Header().Set("Expires", time.Now().Add(time.Hour * 24 * 7).Format(http.TimeFormat))
|
||||
|
||||
//this part handles etags in parallel with expires
|
||||
/* browserEtag := incomingRequest.Header.Get("If-None-Match")
|
||||
|
||||
if browserEtag == info.Etag {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
writeHeaders(w, info)
|
||||
|
||||
} else {
|
||||
writeHeaders(w, info)
|
||||
file, err := os.Open(info.Filename)
|
||||
if err != nil {
|
||||
// dummy image ?!
|
||||
panic(errors.New("could not open image file " + info.Filename + " " + err.Error()))
|
||||
} else {
|
||||
io.Copy(w, file)
|
||||
defer file.Close()
|
||||
|
||||
}
|
||||
}
|
||||
//end of ETag handling
|
||||
*/
|
||||
http.ServeContent(w, incomingRequest, file.Name(), fileInfo.ModTime(), file)
|
||||
|
||||
}
|
||||
// 304 handling
|
||||
file, err := os.Open(info.Filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
fileInfo, err := file.Stat()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
w.Header().Set("Expires", time.Now().Add(time.Hour*24*7).Format(http.TimeFormat))
|
||||
http.ServeContent(w, incomingRequest, file.Name(), fileInfo.ModTime(), file)
|
||||
}
|
||||
|
||||
func writeHeaders(w http.ResponseWriter, info *ImageInfo) {
|
||||
@ -93,15 +66,8 @@ func writeHeaders(w http.ResponseWriter, info *ImageInfo) {
|
||||
if key == "Set-Cookie" {
|
||||
continue
|
||||
}
|
||||
|
||||
/*if key == "Expires" {
|
||||
//force browser cache for 1 year
|
||||
w.Header().Add(key, time.Now().Add(time.Hour*24*365).Format(http.TimeFormat))
|
||||
|
||||
}*/
|
||||
for _, value := range values {
|
||||
w.Header().Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import (
|
||||
|
||||
func TestReadFoomoMediaClientInfo(t *testing.T) {
|
||||
// 2560x1440@0.8999999761581421
|
||||
info, err := ReadFoomoMediaClientInfo("2560x1440@0.8999999761581421")
|
||||
info, err := readFoomoMediaClientInfo("2560x1440@0.8999999761581421")
|
||||
if err != nil {
|
||||
t.Log("parse error", err)
|
||||
t.Fail()
|
||||
@ -28,11 +28,11 @@ func TestClampScreenWidthToGrid(t *testing.T) {
|
||||
t.Error("expected", expected, "actual", actual)
|
||||
}
|
||||
}
|
||||
assertSize(t, 320, ClampScreenWidthToGrid(298, breakPoints))
|
||||
assertSize(t, 768, ClampScreenWidthToGrid(321, breakPoints))
|
||||
assertSize(t, 768, ClampScreenWidthToGrid(766, breakPoints))
|
||||
assertSize(t, 1024, ClampScreenWidthToGrid(1024, breakPoints))
|
||||
assertSize(t, 1440, ClampScreenWidthToGrid(1440, breakPoints))
|
||||
assertSize(t, 1440, ClampScreenWidthToGrid(1441, breakPoints))
|
||||
assertSize(t, 1440, ClampScreenWidthToGrid(10000, breakPoints))
|
||||
assertSize(t, 320, clampScreenWidthToGrid(298, breakPoints))
|
||||
assertSize(t, 768, clampScreenWidthToGrid(321, breakPoints))
|
||||
assertSize(t, 768, clampScreenWidthToGrid(766, breakPoints))
|
||||
assertSize(t, 1024, clampScreenWidthToGrid(1024, breakPoints))
|
||||
assertSize(t, 1440, clampScreenWidthToGrid(1440, breakPoints))
|
||||
assertSize(t, 1440, clampScreenWidthToGrid(1441, breakPoints))
|
||||
assertSize(t, 1440, clampScreenWidthToGrid(10000, breakPoints))
|
||||
}
|
||||
|
||||
@ -2,17 +2,18 @@ package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/foomo/gofoomo/proxy/utils"
|
||||
"github.com/foomo/gofoomo/rpc"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/foomo/gofoomo/proxy/utils"
|
||||
"github.com/foomo/gofoomo/rpc"
|
||||
)
|
||||
|
||||
// This handler helps you to hijack foomo rpc services. Actually it is even
|
||||
// RPC helps you to hijack foomo rpc services. Actually it is even
|
||||
// better, you can hijack them method by method.
|
||||
//
|
||||
// f := gofoomo.NewFoomo("/var/www/myApp", "test")
|
||||
@ -27,6 +28,7 @@ type RPC struct {
|
||||
serviceObject interface{}
|
||||
}
|
||||
|
||||
// NewRPC rpc constructor, path is the path in the url, that you intend to hijack
|
||||
func NewRPC(serviceObject interface{}, path string) *RPC {
|
||||
rpc := new(RPC)
|
||||
rpc.path = path
|
||||
@ -42,9 +44,8 @@ func (r *RPC) getMethodFromPath(path string) string {
|
||||
parts := strings.Split(r.getApplicationPath(path), "/")
|
||||
if len(parts) > 0 {
|
||||
return strings.ToUpper(parts[0][0:1]) + parts[0][1:]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *RPC) handlesMethod(methodName string) bool {
|
||||
@ -55,6 +56,7 @@ func (r *RPC) handlesPath(path string) bool {
|
||||
return strings.HasPrefix(path, r.path) && r.handlesMethod(r.getMethodFromPath(path))
|
||||
}
|
||||
|
||||
// HandlesRequest implementation of request handler interface
|
||||
func (r *RPC) HandlesRequest(incomingRequest *http.Request) bool {
|
||||
return incomingRequest.Method == "POST" && r.handlesPath(incomingRequest.URL.Path)
|
||||
}
|
||||
@ -93,9 +95,8 @@ func extractPostData(incomingRequest *http.Request) map[string]interface{} {
|
||||
}
|
||||
if len(body) > 0 {
|
||||
return jsonDecode(body).(map[string]interface{})
|
||||
} else {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
|
||||
func jsonDecode(jsonData []byte) (data interface{}) {
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/foomo/gofoomo/rpc"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/gofoomo/rpc"
|
||||
)
|
||||
|
||||
type TestService struct {
|
||||
|
||||
@ -1,46 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/gofoomo/proxy/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/gofoomo/proxy/utils"
|
||||
)
|
||||
|
||||
// Handles serving static files from the local file system. It knows about
|
||||
// StaticFiles handles serving static files from the local file system. It knows about
|
||||
// foomos hierarchy and serves files from the htdocs directories of modules.
|
||||
// Currently it will also serve files of disabled modules.
|
||||
|
||||
type StaticFiles struct {
|
||||
foomo *foomo.Foomo
|
||||
}
|
||||
|
||||
// NewStaticFiles constructor
|
||||
func NewStaticFiles(foomo *foomo.Foomo) *StaticFiles {
|
||||
sf := new(StaticFiles)
|
||||
sf.foomo = foomo
|
||||
return sf
|
||||
}
|
||||
|
||||
// HandlesRequest request handler implementation
|
||||
func (files *StaticFiles) HandlesRequest(incomingRequest *http.Request) bool {
|
||||
if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modulesVar/") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modules/") {
|
||||
parts := strings.Split(incomingRequest.URL.Path, "/")
|
||||
if len(parts) > 3 {
|
||||
moduleNameParts := strings.Split(parts[3], "-")
|
||||
if strings.HasSuffix(parts[len(parts)-1], ".php") {
|
||||
return false
|
||||
} else {
|
||||
return fileExists(files.foomo.GetModuleHtdocsDir(moduleNameParts[0]) + "/" + strings.Join(parts[4:], "/"))
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
return fileExists(files.foomo.GetModuleHtdocsDir(moduleNameParts[0]) + "/" + strings.Join(parts[4:], "/"))
|
||||
}
|
||||
} else if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modulesVar/") {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
@ -76,26 +76,14 @@ func (files *StaticFiles) ServeHTTP(w http.ResponseWriter, incomingRequest *http
|
||||
panicOnErr(err)
|
||||
//const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||
w.Header().Set("Expires", time.Now().Add(time.Hour*24*365).Format(http.TimeFormat))
|
||||
// should we really compress all static file types ?!
|
||||
if compress && strings.Contains(incomingRequest.Header.Get("Accept-Encoding"), "gzip") {
|
||||
w.Header().Set("Content-Encoding", "gzip")
|
||||
crw := utils.NewCompressedResponseWriter(w)
|
||||
defer crw.Close()
|
||||
w = crw
|
||||
}
|
||||
|
||||
http.ServeContent(w, incomingRequest, f.Name(), fileInfo.ModTime(), f)
|
||||
/* if compress {
|
||||
err := utils.ServeCompressed(w, incomingRequest, func(writer io.Writer) error {
|
||||
_, err := io.Copy(writer, f)
|
||||
return err
|
||||
})
|
||||
panicOnErr(err)
|
||||
} else {
|
||||
_, err := io.Copy(w, f)
|
||||
panicOnErr(err)
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
func getContentType(path string) (string, bool) {
|
||||
|
||||
@ -8,18 +8,22 @@ import (
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"github.com/foomo/tlsconfig"
|
||||
)
|
||||
|
||||
// Handler proxy handler
|
||||
type Handler interface {
|
||||
HandlesRequest(incomingRequest *http.Request) bool
|
||||
ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request)
|
||||
}
|
||||
|
||||
// Listener deprecated
|
||||
type Listener interface {
|
||||
ListenServeHTTPStart(w http.ResponseWriter, incomingRequest *http.Request) http.ResponseWriter
|
||||
ListenServeHTTPDone(w http.ResponseWriter, incomingRequest *http.Request)
|
||||
}
|
||||
|
||||
// Proxy foomo proxy
|
||||
type Proxy struct {
|
||||
foomo *foomo.Foomo
|
||||
ReverseProxy *httputil.ReverseProxy
|
||||
@ -29,13 +33,15 @@ type Proxy struct {
|
||||
ServeHTTPFunc func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
type ProxyServer struct {
|
||||
// Server server for Proxy
|
||||
type Server struct {
|
||||
Proxy *Proxy
|
||||
Config *Config
|
||||
TLSConfig *tls.Config
|
||||
Foomo *foomo.Foomo
|
||||
}
|
||||
|
||||
// NewProxy constructor
|
||||
func NewProxy(f *foomo.Foomo) *Proxy {
|
||||
proxy := &Proxy{
|
||||
foomo: f,
|
||||
@ -50,8 +56,9 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Reque
|
||||
}
|
||||
|
||||
func (proxy *Proxy) serveHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
||||
if proxy.auth != nil && len(proxy.auth.Domain) > 0 && !proxy.foomo.BasicAuthForRequest(w, incomingRequest, proxy.auth.Domain, proxy.auth.Realm, "access denied") {
|
||||
return
|
||||
if proxy.auth != nil && len(proxy.auth.Domain) > 0 {
|
||||
//&& !proxy.foomo.BasicAuthForRequest(w, incomingRequest, proxy.auth.Domain, proxy.auth.Realm, "access denied") {
|
||||
|
||||
}
|
||||
for _, listener := range proxy.listeners {
|
||||
w = listener.ListenServeHTTPStart(w, incomingRequest)
|
||||
@ -72,82 +79,44 @@ func (proxy *Proxy) serveHTTP(w http.ResponseWriter, incomingRequest *http.Reque
|
||||
|
||||
}
|
||||
|
||||
// AddHandler add a handler
|
||||
func (proxy *Proxy) AddHandler(handler Handler) {
|
||||
proxy.handlers = append(proxy.handlers, handler)
|
||||
}
|
||||
|
||||
// AddListener deprecated
|
||||
func (proxy *Proxy) AddListener(listener Listener) {
|
||||
proxy.listeners = append(proxy.listeners, listener)
|
||||
}
|
||||
|
||||
func NewProxyServerWithConfig(filename string) (p *ProxyServer, err error) {
|
||||
// NewServerWithConfig constructor with a config file
|
||||
func NewServerWithConfig(filename string) (p *Server, err error) {
|
||||
config, err := ReadConfig(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewProxyServer(config)
|
||||
return NewServer(config)
|
||||
}
|
||||
|
||||
func NewProxyServer(config *Config) (p *ProxyServer, err error) {
|
||||
proxyServer := new(ProxyServer)
|
||||
proxyServer.Config = config
|
||||
// NewServer constructor with config struct
|
||||
func NewServer(config *Config) (p *Server, err error) {
|
||||
p = &Server{
|
||||
Config: config,
|
||||
}
|
||||
f, err := foomo.NewFoomo(config.Foomo.Dir, config.Foomo.RunMode, config.Foomo.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proxyServer.Foomo = f
|
||||
proxyServer.Proxy = NewProxy(proxyServer.Foomo)
|
||||
proxyServer.Proxy.auth = config.Server.Auth
|
||||
proxyServer.TLSConfig = setupTLSConfig(proxyServer.Config.Server.TLS)
|
||||
return proxyServer, nil
|
||||
p.Foomo = f
|
||||
p.Proxy = NewProxy(p.Foomo)
|
||||
p.Proxy.auth = config.Server.Auth
|
||||
p.TLSConfig = tlsconfig.NewServerTLSConfig(p.Config.Server.TLS.Mode)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func setupTLSConfig(tlsConfig TLS) *tls.Config {
|
||||
c := &tls.Config{}
|
||||
switch tlsConfig.Mode {
|
||||
case TLSModeDefault:
|
||||
// will not touch this one, but trust the golang team
|
||||
case TLSModeLoose:
|
||||
c.MinVersion = tls.VersionTLS10
|
||||
c.CipherSuites = []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
}
|
||||
c.CurvePreferences = []tls.CurveID{
|
||||
tls.CurveP256,
|
||||
tls.CurveP384,
|
||||
tls.CurveP521,
|
||||
}
|
||||
case TLSModeStrict:
|
||||
c.MinVersion = tls.VersionTLS12
|
||||
c.PreferServerCipherSuites = true
|
||||
c.CipherSuites = []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
}
|
||||
c.CurvePreferences = []tls.CurveID{
|
||||
tls.CurveP256,
|
||||
tls.CurveP384,
|
||||
tls.CurveP521,
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (p *ProxyServer) ListenAndServe() error {
|
||||
// ListenAndServe until things go bad, depending upon configuration this will\
|
||||
// listen to http and https requests
|
||||
func (p *Server) ListenAndServe() error {
|
||||
c := p.Config.Server
|
||||
errorChan := make(chan error)
|
||||
startedHTTPS := false
|
||||
@ -161,7 +130,6 @@ func (p *ProxyServer) ListenAndServe() error {
|
||||
TLSConfig: p.TLSConfig,
|
||||
}
|
||||
errorChan <- tlsServer.ListenAndServeTLS(c.TLS.CertFile, c.TLS.KeyFile)
|
||||
// errorChan <- http.ListenAndServeTLS(c.TLS.Address, c.TLS.CertFile, c.TLS.KeyFile, p.Proxy)
|
||||
}()
|
||||
startedHTTPS = true
|
||||
}
|
||||
|
||||
@ -2,16 +2,16 @@ package utils
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
//"io"
|
||||
"net/http"
|
||||
//"strings"
|
||||
)
|
||||
|
||||
// CompressedResponseWriter helper to write compressed responses
|
||||
type CompressedResponseWriter struct {
|
||||
responseWriter http.ResponseWriter
|
||||
gz *gzip.Writer
|
||||
}
|
||||
|
||||
// NewCompressedResponseWriter constructor
|
||||
func NewCompressedResponseWriter(responseWriter http.ResponseWriter) *CompressedResponseWriter {
|
||||
crw := &CompressedResponseWriter{
|
||||
responseWriter: responseWriter,
|
||||
@ -45,6 +45,7 @@ func (crw *CompressedResponseWriter) WriteHeader(code int) {
|
||||
crw.responseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
// Close me please
|
||||
func (crw *CompressedResponseWriter) Close() {
|
||||
crw.gz.Close()
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ServeCompressed serve a request with a compressed response, if the client accepts it
|
||||
func ServeCompressed(w http.ResponseWriter, incomingRequest *http.Request, writeCallback func(writer io.Writer) error) error {
|
||||
var writer io.Writer
|
||||
writer = w
|
||||
@ -20,8 +21,10 @@ func ServeCompressed(w http.ResponseWriter, incomingRequest *http.Request, write
|
||||
|
||||
}
|
||||
|
||||
// Resource304 vo
|
||||
type Resource304 struct {
|
||||
Etag string
|
||||
}
|
||||
|
||||
// Handle304 304 handler
|
||||
func Handle304(r *http.Request) {}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package rpc
|
||||
|
||||
// from php class Foomo\Services\RPC\Protocol\Call\MethodCall
|
||||
// MethodCall from php class Foomo\Services\RPC\Protocol\Call\MethodCall
|
||||
// serializing a method call
|
||||
type MethodCall struct {
|
||||
// id of the method call
|
||||
Id string `json:"id"`
|
||||
ID string `json:"id"`
|
||||
// name of the method to be called
|
||||
Method string `json:"method"`
|
||||
// the method call arguments
|
||||
@ -14,11 +14,11 @@ type MethodCall struct {
|
||||
} `json:"arguments"`
|
||||
}
|
||||
|
||||
// from php class Foomo\Services\RPC\Protocol\Reply\MethodReply
|
||||
// MethodReply from php class Foomo\Services\RPC\Protocol\Reply\MethodReply
|
||||
// reply to a method call
|
||||
type MethodReply struct {
|
||||
// id of the method call
|
||||
Id string `json:"id"`
|
||||
ID string `json:"id"`
|
||||
// return value
|
||||
Value interface{} `json:"value"`
|
||||
// server side exception
|
||||
|
||||
@ -1 +0,0 @@
|
||||
package utils
|
||||
Loading…
Reference in New Issue
Block a user