mirror of
https://github.com/foomo/gofoomo.git
synced 2025-10-16 12:25:44 +00:00
initial package layout, first working parts
This commit is contained in:
commit
7dbb05bd74
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.*
|
||||
!.git*
|
||||
|
||||
35
README.md
Normal file
35
README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# gofoomo
|
||||
|
||||
Gofoomo lets you use Go in your foomo project. It also lets you use php in your Go project.
|
||||
|
||||
We want to use Go, but it is not the right language for a everyone, who is using php. Php´s save and reload work cycle in combination with it´s dynamic character is a great fit especially when doing frontend work.
|
||||
|
||||
## Complementing your LAMP stack
|
||||
|
||||
Go is a much younger and cleaner stack than LAMP.
|
||||
|
||||
- Serve static files without bugging your prefork apache
|
||||
- Keep slow connections away from your php processes (not implemented yet)
|
||||
- Hijack foomo json rpc services methods
|
||||
- Your code is also running the server, this puts you in a place, where you can solve problems, that you can not solve in php
|
||||
- Go´s runtime model is pretty much the opposite of the php runtime model
|
||||
- all requests vs one request per lifetime
|
||||
- shared memory vs process and memory isolation
|
||||
- one bug to kill them all vs one bug kills one request
|
||||
- hard, but fast vs easy but slow
|
||||
|
||||
## Sitting in front of your foomo LAMP app with Go
|
||||
|
||||
Go or php? It is up to you, to decide which tool provides better solutions for your problem and who on your team will be more productive with php or Go.
|
||||
|
||||
## Hijacking json rpc calls
|
||||
|
||||
Gofoomo lets you intercept and implement calls to foomo json rpc services. In addition Foomo.Go gives you an infrastructure to generate golang structs for php value objects.
|
||||
|
||||
## 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.
|
||||
|
||||
## More to come, but not much more
|
||||
|
||||
We are going to add features, as we are going to need them. The focus is to have a simple interface between foomo and Go.
|
||||
41
foomo/core/client.go
Normal file
41
foomo/core/client.go
Normal file
@ -0,0 +1,41 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func get(foomo *foomo.Foomo, path ...string) (data []byte, err error) {
|
||||
callUrl := foomo.GetURLWithCredentialsForDefaultBasicAuthDomain()
|
||||
encodedPath := ""
|
||||
for _, pathEntry := range path {
|
||||
encodedPath += "/" + url.QueryEscape(pathEntry)
|
||||
}
|
||||
resp, err := http.Get(callUrl + "/foomo/core.php" + encodedPath)
|
||||
if err == nil {
|
||||
// handle error
|
||||
defer resp.Body.Close()
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
61
foomo/core/client_test.go
Normal file
61
foomo/core/client_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type CoreConfig struct {
|
||||
EnabledModules []string
|
||||
AvailableModules []string
|
||||
RootHttp string
|
||||
buildNumber int64
|
||||
}
|
||||
|
||||
var testFoomo *foomo.Foomo
|
||||
|
||||
func getTestFoomo() *foomo.Foomo {
|
||||
if testFoomo == nil {
|
||||
f, _ := foomo.NewFoomo("/Users/jan/vagrant/schild/www/schild", "test", "http://schild-local-test.bestbytes.net")
|
||||
testFoomo = f
|
||||
}
|
||||
return testFoomo
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
f := getTestFoomo()
|
||||
data, err := get(f, "config", "Foomo", "Foomo.core")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var jsonData interface{}
|
||||
err = json.Unmarshal(data, &jsonData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJSON(t *testing.T) {
|
||||
f := getTestFoomo()
|
||||
config := new(CoreConfig)
|
||||
err := GetJSON(f, config, "config", "Foomo", "Foomo.core")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(config.EnabledModules) < 1 {
|
||||
t.Fatal("there must be at least Foomo enabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
f := getTestFoomo()
|
||||
config := new(CoreConfig)
|
||||
err := GetConfig(f, config, "Foomo", "Foomo.core", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(config.EnabledModules) < 1 {
|
||||
t.Fatal("there must be at least Foomo enabled")
|
||||
}
|
||||
}
|
||||
100
foomo/foomo.go
Normal file
100
foomo/foomo.go
Normal file
@ -0,0 +1,100 @@
|
||||
package foomo
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
u "net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Foomo struct {
|
||||
Root string
|
||||
RunMode string
|
||||
URL *u.URL
|
||||
basicAuthCredentials struct {
|
||||
user string
|
||||
password string
|
||||
}
|
||||
}
|
||||
|
||||
func NewFoomo(foomoDir string, runMode string, url string) (f *Foomo, err error) {
|
||||
f, err = makeFoomo(foomoDir, runMode, url, true)
|
||||
return
|
||||
}
|
||||
|
||||
func makeFoomo(foomoDir string, runMode string, url string, init bool) (foomo *Foomo, err error) {
|
||||
f := new(Foomo)
|
||||
f.Root = foomoDir
|
||||
f.URL, err = u.Parse(url)
|
||||
f.RunMode = runMode
|
||||
if init {
|
||||
f.setupBasicAuthCredentials()
|
||||
}
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (f *Foomo) getBasicAuthFileContentsForDomain(domain string) string {
|
||||
basicAuthFilename := f.GetBasicAuthFilename("default")
|
||||
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 {
|
||||
newLines = append(newLines, line)
|
||||
}
|
||||
}
|
||||
|
||||
s := sha1.New()
|
||||
s.Write([]byte(password))
|
||||
passwordSum := []byte(s.Sum(nil))
|
||||
newLines = append(newLines, user+":{SHA}"+base64.StdEncoding.EncodeToString(passwordSum))
|
||||
return strings.Join(newLines, "\n")
|
||||
}
|
||||
|
||||
func (f *Foomo) GetURLWithCredentialsForDefaultBasicAuthDomain() string {
|
||||
url, _ := u.Parse(f.URL.String())
|
||||
url.User = u.UserPassword(f.basicAuthCredentials.user, f.basicAuthCredentials.password)
|
||||
return url.String()
|
||||
}
|
||||
|
||||
func (f *Foomo) GetBasicAuthCredentialsForDefaultBasicAuthDomain() (user string, password string) {
|
||||
return f.basicAuthCredentials.user, f.basicAuthCredentials.password
|
||||
}
|
||||
|
||||
func (f *Foomo) GetModuleDir(moduleName string, dir string) string {
|
||||
return f.Root + "/modules/" + moduleName + "/" + dir
|
||||
}
|
||||
|
||||
func (f *Foomo) GetVarDir() string {
|
||||
return f.Root + "/var/" + f.RunMode
|
||||
}
|
||||
|
||||
func (f *Foomo) GetModuleHtdocsDir(moduleName string) string {
|
||||
return f.GetModuleDir(moduleName, "htdocs")
|
||||
}
|
||||
|
||||
func (f *Foomo) GetModuleHtdocsVarDir(moduleName string) string {
|
||||
return f.GetVarDir() + "/htdocs/modulesVar/" + moduleName
|
||||
}
|
||||
|
||||
func (f *Foomo) GetBasicAuthFilename(domain string) string {
|
||||
return f.GetVarDir() + "/basicAuth/" + domain
|
||||
}
|
||||
47
foomo/foomo_test.go
Normal file
47
foomo/foomo_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package foomo
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"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)
|
||||
return f
|
||||
}
|
||||
|
||||
func assertStringsEqual(t *testing.T, topic string, expected string, actual string) {
|
||||
if actual != expected {
|
||||
t.Fatal(topic, "actual: ", actual, " != expected: ", expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVarDir(t *testing.T) {
|
||||
actual := getTestFoomoForFSStuff().GetVarDir()
|
||||
expected := "/var/www/foomo/var/test"
|
||||
assertStringsEqual(t, "var dir", expected, actual)
|
||||
}
|
||||
|
||||
func TestGetModuleDir(t *testing.T) {
|
||||
assertStringsEqual(t, "module dir", "/var/www/foomo/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"))
|
||||
}
|
||||
|
||||
func TestGetModuleHtdocsVarDir(t *testing.T) {
|
||||
assertStringsEqual(t, "module htdocs var dir", "/var/www/foomo/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"))
|
||||
}
|
||||
30
foomo/token.go
Normal file
30
foomo/token.go
Normal file
@ -0,0 +1,30 @@
|
||||
package foomo
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
func makeToken(length int) string {
|
||||
chars := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$&*-_")
|
||||
token := make([]byte, length)
|
||||
randomData := make([]byte, length+(length/4)) // storage for random bytes.
|
||||
clen := byte(len(chars))
|
||||
maxrb := byte(256 - (256 % len(chars)))
|
||||
i := 0
|
||||
for {
|
||||
if _, err := io.ReadFull(rand.Reader, randomData); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, c := range randomData {
|
||||
if c >= maxrb {
|
||||
continue
|
||||
}
|
||||
token[i] = chars[c%clen]
|
||||
i++
|
||||
if i == length {
|
||||
return string(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
gofoomo.go
Normal file
13
gofoomo.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Integrate golang with the foomo php framework. Think of gophers riding elephants or
|
||||
// maybe also think of gophers pulling toy elephants.
|
||||
//
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// f := gofoomo.NewFoomo("/Users/jan/vagrant/schild/www/schild", "test")
|
||||
// p := proxy.NewProxy(f, "http://schild-local-test.bestbytes.net")
|
||||
// // the static files handler will keep requests to static files away from apache
|
||||
// p.AddHandler(handler.NewStaticFiles(f))
|
||||
// http.ListenAndServe(":8080", p)
|
||||
//
|
||||
package gofoomo
|
||||
2
proxy/handler/handler.go
Normal file
2
proxy/handler/handler.go
Normal file
@ -0,0 +1,2 @@
|
||||
// Common handlers for the gofoomo.proxy.
|
||||
package handler
|
||||
116
proxy/handler/rpc.go
Normal file
116
proxy/handler/rpc.go
Normal file
@ -0,0 +1,116 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/foomo/gofoomo/rpc"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// This handler 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")
|
||||
// p := proxy.NewProxy(f, "http://test.myapp")
|
||||
// service := NewFooService()
|
||||
// rpcHandler := handler.NewRPC(service, "/foomo/modules/MyModule/services/foo.php")
|
||||
// f.AddHandler(rpcHandler)
|
||||
//
|
||||
// Happy service hijacking!
|
||||
type RPC struct {
|
||||
path string
|
||||
serviceObject interface{}
|
||||
}
|
||||
|
||||
func NewRPC(serviceObject interface{}, path string) *RPC {
|
||||
rpc := new(RPC)
|
||||
rpc.path = path
|
||||
rpc.serviceObject = serviceObject
|
||||
return rpc
|
||||
}
|
||||
|
||||
func (r *RPC) getApplicationPath(path string) string {
|
||||
return path[len(r.path+"/Foomo.Services.RPC/serve")+1:]
|
||||
}
|
||||
|
||||
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 ""
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RPC) handlesMethod(methodName string) bool {
|
||||
return reflect.ValueOf(r.serviceObject).MethodByName(methodName).IsValid()
|
||||
}
|
||||
|
||||
func (r *RPC) handlesPath(path string) bool {
|
||||
return strings.HasPrefix(path, r.path) && r.handlesMethod(r.getMethodFromPath(path))
|
||||
}
|
||||
|
||||
func (r *RPC) HandlesRequest(incomingRequest *http.Request) bool {
|
||||
return incomingRequest.Method == "POST" && r.handlesPath(incomingRequest.URL.Path)
|
||||
}
|
||||
|
||||
func (r *RPC) callServiceObjectWithHTTPRequest(incomingRequest *http.Request) (reply *rpc.MethodReply) {
|
||||
reply = &rpc.MethodReply{}
|
||||
path := incomingRequest.URL.Path
|
||||
argumentMap := extractPostData(incomingRequest)
|
||||
methodName := r.getMethodFromPath(path)
|
||||
arguments := r.extractArguments(path)
|
||||
r.callServiceObject(methodName, arguments, argumentMap, reply)
|
||||
return reply
|
||||
}
|
||||
|
||||
func (r *RPC) extractArguments(path string) (args []string) {
|
||||
for _, value := range strings.Split(r.getApplicationPath(path), "/")[1:] {
|
||||
unescapedArg, err := url.QueryUnescape(value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
args = append(args, unescapedArg)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (r *RPC) callServiceObject(methodName string, arguments []string, argumentMap map[string]interface{}, reply *rpc.MethodReply) {
|
||||
reflectionArgs := []reflect.Value{}
|
||||
reflectionArgs = append(reflectionArgs, reflect.ValueOf(arguments), reflect.ValueOf(argumentMap), reflect.ValueOf(reply))
|
||||
reflect.ValueOf(r.serviceObject).MethodByName(methodName).Call(reflectionArgs)
|
||||
}
|
||||
|
||||
func extractPostData(incomingRequest *http.Request) map[string]interface{} {
|
||||
body, err := ioutil.ReadAll(incomingRequest.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return jsonDecode(body).(map[string]interface{})
|
||||
}
|
||||
|
||||
func jsonDecode(jsonData []byte) (data interface{}) {
|
||||
err := json.Unmarshal(jsonData, &data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
func jsonEncode(data interface{}) []byte {
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RPC) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(jsonEncode(r.callServiceObjectWithHTTPRequest(incomingRequest)))
|
||||
}
|
||||
79
proxy/handler/rpc_test.go
Normal file
79
proxy/handler/rpc_test.go
Normal file
@ -0,0 +1,79 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/foomo/gofoomo/rpc"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestService struct {
|
||||
}
|
||||
|
||||
func NewTestService() *TestService {
|
||||
t := new(TestService)
|
||||
return t
|
||||
}
|
||||
|
||||
func getTestRPC() *RPC {
|
||||
return NewRPC(NewTestService(), "/services/test.php")
|
||||
}
|
||||
|
||||
func (t *TestService) Test(arguments []string, argumentMap map[string]interface{}, reply *rpc.MethodReply) {
|
||||
reply.Value = true
|
||||
}
|
||||
|
||||
func TestHandlesMethod(t *testing.T) {
|
||||
r := getTestRPC()
|
||||
if r.handlesMethod("Test") == false {
|
||||
t.Fail()
|
||||
}
|
||||
if r.handlesMethod("testi") == true {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetApplicationPath(t *testing.T) {
|
||||
p := getTestRPC().getApplicationPath("/services/test.php/Foomo.Services.RPC/serve/test")
|
||||
if p != "test" {
|
||||
t.Fatal("i do not like this path", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlesPath(t *testing.T) {
|
||||
r := getTestRPC()
|
||||
if r.handlesPath("/services/test.php/Foomo.Services.RPC/serve/test") == false {
|
||||
t.Fatal("/services/test.php/Foomo.Services.RPC/serve/test")
|
||||
}
|
||||
if r.handlesPath("/services/test.php/Foomo.Services.RPC/serve/test/foo") == false {
|
||||
t.Fatal("/services/test.php/Foomo.Services.RPC/serve/test/foo")
|
||||
}
|
||||
if r.handlesPath("/services/test.php/Foomo.Services.RPC/serve/testi/foo") == true {
|
||||
t.Fatal("/services/test.php/Foomo.Services.RPC/serve/testi/foo")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractArguments(t *testing.T) {
|
||||
r := getTestRPC()
|
||||
args := r.extractArguments("/services/test.php/Foomo.Services.RPC/serve/test/%C3%BCb%C3%A4l/B%C3%A4r")
|
||||
if len(args) != 2 {
|
||||
t.Fatal("wrong args length", args)
|
||||
}
|
||||
if args[0] != "übäl" {
|
||||
t.Fatal("no übäl")
|
||||
}
|
||||
if args[1] != "Bär" {
|
||||
t.Fatal("where is the bear")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallServiceObject(t *testing.T) {
|
||||
r := getTestRPC()
|
||||
var argumentMap map[string]interface{}
|
||||
var arguments []string
|
||||
reply := &rpc.MethodReply{}
|
||||
r.callServiceObject("Test", arguments, argumentMap, reply)
|
||||
if reply.Value != true {
|
||||
log.Println(reply)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
87
proxy/handler/staticfiles.go
Normal file
87
proxy/handler/staticfiles.go
Normal file
@ -0,0 +1,87 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func NewStaticFiles(foomo *foomo.Foomo) *StaticFiles {
|
||||
sf := new(StaticFiles)
|
||||
sf.foomo = foomo
|
||||
return sf
|
||||
}
|
||||
|
||||
func (files *StaticFiles) HandlesRequest(incomingRequest *http.Request) bool {
|
||||
if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modules/") {
|
||||
parts := strings.Split(incomingRequest.URL.Path, "/")
|
||||
if len(parts) > 3 {
|
||||
moduleNameParts := strings.Split(parts[3], "-")
|
||||
return fileExists(files.foomo.GetModuleHtdocsDir(moduleNameParts[0]) + "/" + strings.Join(parts[4:], "/"))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modulesVar/") {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (files *StaticFiles) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
||||
parts := strings.Split(incomingRequest.URL.Path, "/")
|
||||
path := strings.Join(parts[4:], "/")
|
||||
moduleNameParts := strings.Split(parts[3], "-")
|
||||
moduleName := moduleNameParts[0]
|
||||
var moduleDir string
|
||||
if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modules/") {
|
||||
moduleDir = files.foomo.GetModuleHtdocsDir(moduleName)
|
||||
} else {
|
||||
moduleDir = files.foomo.GetModuleHtdocsVarDir(moduleName)
|
||||
}
|
||||
f, err := os.Open(moduleDir + "/" + path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
defer f.Close()
|
||||
w.Header().Set("Content-Type", getContentType(path))
|
||||
io.Copy(w, f)
|
||||
}
|
||||
}
|
||||
|
||||
func getContentType(path string) string {
|
||||
if strings.HasSuffix(path, ".png") {
|
||||
return "image/png"
|
||||
} else if strings.HasSuffix(path, ".jpg") {
|
||||
return "image/jpeg"
|
||||
} else if strings.HasSuffix(path, ".jpeg") {
|
||||
return "image/jpeg"
|
||||
} else if strings.HasSuffix(path, ".gif") {
|
||||
return "image/gif"
|
||||
} else if strings.HasSuffix(path, ".css") {
|
||||
return "text/css"
|
||||
} else if strings.HasSuffix(path, ".js") {
|
||||
return "application/javascript"
|
||||
} else if strings.HasSuffix(path, ".html") {
|
||||
return "text/html"
|
||||
} else if strings.HasSuffix(path, ".") {
|
||||
return ""
|
||||
} else {
|
||||
return "octet/stream"
|
||||
}
|
||||
}
|
||||
41
proxy/proxy.go
Normal file
41
proxy/proxy.go
Normal file
@ -0,0 +1,41 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/foomo/gofoomo/foomo"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
type Handler interface {
|
||||
HandlesRequest(incomingRequest *http.Request) bool
|
||||
ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request)
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
foomo *foomo.Foomo
|
||||
reverseProxy *httputil.ReverseProxy
|
||||
handlers []Handler
|
||||
}
|
||||
|
||||
func NewProxy(f *foomo.Foomo) *Proxy {
|
||||
proxy := new(Proxy)
|
||||
proxy.foomo = f
|
||||
proxy.reverseProxy = httputil.NewSingleHostReverseProxy(proxy.foomo.URL)
|
||||
return proxy
|
||||
}
|
||||
|
||||
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
||||
for _, handler := range proxy.handlers {
|
||||
if handler.HandlesRequest(incomingRequest) {
|
||||
handler.ServeHTTP(w, incomingRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
incomingRequest.Host = proxy.foomo.URL.Host
|
||||
incomingRequest.URL.Opaque = incomingRequest.RequestURI
|
||||
proxy.reverseProxy.ServeHTTP(w, incomingRequest)
|
||||
}
|
||||
|
||||
func (proxy *Proxy) AddHandler(handler Handler) {
|
||||
proxy.handlers = append(proxy.handlers, handler)
|
||||
}
|
||||
1
rpc/rpc.go
Normal file
1
rpc/rpc.go
Normal file
@ -0,0 +1 @@
|
||||
package rpc
|
||||
30
rpc/value_objects.go
Normal file
30
rpc/value_objects.go
Normal file
@ -0,0 +1,30 @@
|
||||
package rpc
|
||||
|
||||
// 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"`
|
||||
// name of the method to be called
|
||||
Method string `json:"method"`
|
||||
// the method call arguments
|
||||
Arguments []struct {
|
||||
Name string `json:"name"`
|
||||
Value interface{} `json:"value"`
|
||||
} `json:"arguments"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
// return value
|
||||
Value interface{} `json:"value"`
|
||||
// server side exception
|
||||
Exception interface{} `json:"exception"`
|
||||
// messages from the server
|
||||
// possibly many of them
|
||||
// possibly many types
|
||||
Messages interface{} `json:"messages"`
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user