diff --git a/.gitignore b/.gitignore index ead350d..26ee814 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -public/ +frontend/public/ output.log **/node_modules/ .DS_Store diff --git a/Makefile b/Makefile index d0be0b9..07fead2 100644 --- a/Makefile +++ b/Makefile @@ -9,44 +9,33 @@ TTY_SENDER=tty_sender TTY_SERVER_SRC=$(filter-out ./tty-server/assets_bundle.go, $(wildcard ./tty-server/*.go)) ./tty-server/assets_bundle.go TTY_SENDER_SRC=$(wildcard ./tty-sender/*.go) EXTRA_BUILD_DEPS=$(wildcard ./common/*go) -TTY_SERVER_ASSETS=$(addprefix ./public/templates/,$(notdir $(wildcard ./frontend/templates/*))) public/bundle.js +TTY_SERVER_ASSETS=$(wildcard frontend/public/*) all: $(TTY_SERVER) $(TTY_SENDER) @echo "All done" -$(TTY_SERVER): $(TTY_SERVER_SRC) $(EXTRA_BUILD_DEPS) $(TTY_SERVER_ASSETS) +$(TTY_SERVER): $(TTY_SERVER_SRC) $(EXTRA_BUILD_DEPS) go build -o $@ $(TTY_SERVER_SRC) $(TTY_SENDER): $(TTY_SENDER_SRC) $(EXTRA_BUILD_DEPS) go build -o $@ $(TTY_SENDER_SRC) -# TODO: perhaps replace all these paths with variables? -frontend/bundle.js: $(wildcard ./frontend/src/*) - cd frontend && npm run build && cd - - -public/bundle.js: frontend/bundle.js - mkdir -p $(dir $@) - cp $^ $@ - -public/templates/%: frontend/templates/% - mkdir -p $(dir $@) - cp $^ $@ - tty-server/assets_bundle.go: $(TTY_SERVER_ASSETS) - go-bindata --prefix public -o $@ $^ + go-bindata --prefix frontend/public/ -o $@ $^ frontend: FORCE cd frontend && npm run build && cd - clean: - @rm -f $(TTY_SERVER) $(TTY_SENDER) + rm -f $(TTY_SERVER) $(TTY_SENDER) + rm -fr frontend/out/ @echo "Cleaned" runs: $(TTY_SERVER) - @./$(TTY_SERVER) --url http://localhost:9090 --web_address :9090 --sender_address :7654 + ./$(TTY_SERVER) --url http://localhost:9090 --web_address :9090 --sender_address :7654 runc: $(TTY_SENDER) - @./$(TTY_SENDER) --logfile output.log + ./$(TTY_SENDER) --logfile output.log --useTLS=false test: @go test github.com/elisescu/tty-share/testing -v diff --git a/doc/http_routes.md b/doc/http_routes.md new file mode 100644 index 0000000..1325922 --- /dev/null +++ b/doc/http_routes.md @@ -0,0 +1,10 @@ +Routes +====== + +These are the routes the server will listen to: + +* `/` - the main page which probably will be a redirect +* `/ws/` - will serve the websockets session +* `/s/` - will serve the tty-receiver webpage, which will make some further requests for + the resources +* `/static/` - serving the static resources: 404 page, js and css files diff --git a/frontend/package.json b/frontend/package.json index 76459ad..1f75f48 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,9 +4,8 @@ "description": "", "main": "", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "webpack", - "watch": "webpack --watch --hot" + "watch": "TTY_SHARE_ENV=development webpack --watch", + "build": "TTY_SHARE_ENV=production webpack" }, "author": "", "license": "", @@ -24,7 +23,9 @@ "source-map-loader": "0.2.2", "style-loader": "0.19.0", "webpack": "3.7.1", - "webpack-dev-server": "2.9.1", "xterm": "2.9.2" + }, + "devDependencies": { + "copy-webpack-plugin": "4.5.1" } } diff --git a/frontend/templates/index.html b/frontend/templates/tty-receiver.in.html similarity index 89% rename from frontend/templates/index.html rename to frontend/templates/tty-receiver.in.html index a1a6179..2fc2e5d 100644 --- a/frontend/templates/index.html +++ b/frontend/templates/tty-receiver.in.html @@ -15,7 +15,7 @@ } console.log("Initial data", window.ttyInitialData) - + diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 985758e..e9dc1b8 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -1,11 +1,16 @@ -var path = require('path'); -module.exports = { - entry: './src/main.js', +const webpack = require("webpack"); +const copyWebpackPlugin = require('copy-webpack-plugin') + +const develBuild = process.env.TTY_SHARE_ENV === 'development'; + +let mainConfig = { + entry: { + 'tty-receiver': './tty-receiver/main.js', + }, output: { - path: __dirname, - filename: 'bundle.js' + path: __dirname + '/public/', + filename: '[name].js', }, - devtool: 'inline-source-map', module: { rules: [ { @@ -47,5 +52,21 @@ module.exports = { use: ['source-map-loader'] } ] - } -}; \ No newline at end of file + }, + plugins: [ + new copyWebpackPlugin([ + 'static', + 'templates', + ], { + debug: 'info', + }), + ], +}; + +if (develBuild) { + mainConfig.devtool = 'inline-source-map'; +} else { + mainConfig.plugins.push(new webpack.optimize.UglifyJsPlugin( { minimize: true })); +} + +module.exports = mainConfig; diff --git a/tty-server/server.go b/tty-server/server.go index d850b7a..9db011c 100644 --- a/tty-server/server.go +++ b/tty-server/server.go @@ -2,16 +2,24 @@ package main import ( "errors" - "fmt" "html/template" + "mime" "net" "net/http" + "os" + "path/filepath" "sync" "github.com/gorilla/mux" "github.com/gorilla/websocket" ) +const ( + errorInvalidSession = iota + errorNotFound = iota + errorNotAllowed = iota +) + var log = MainLogger // SessionTemplateModel used for templating @@ -41,6 +49,35 @@ type TTYProxyServer struct { activeSessionsRWLock sync.RWMutex } +func (server *TTYProxyServer) serveContent(w http.ResponseWriter, r *http.Request, name string) { + // If a path to the frontend resources was passed, serve from there, otherwise, serve from the + // builtin bundle + if server.config.FrontendPath == "" { + file, err := Asset(name) + + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + + ctype := mime.TypeByExtension(filepath.Ext(name)) + if ctype == "" { + ctype = http.DetectContentType(file) + } + w.Header().Set("Content-Type", ctype) + w.Write(file) + } else { + _, err := os.Open(server.config.FrontendPath + string(os.PathSeparator) + name) + + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + + http.ServeFile(w, r, name) + } +} + // NewTTYProxyServer creates a new instance func NewTTYProxyServer(config TTYProxyServerConfig) (server *TTYProxyServer) { server = &TTYProxyServer{ @@ -51,29 +88,22 @@ func NewTTYProxyServer(config TTYProxyServerConfig) (server *TTYProxyServer) { } routesHandler := mux.NewRouter() - if config.FrontendPath != "" { - routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/", - http.FileServer(http.Dir(config.FrontendPath)))) - } else { - // Serve the bundled assets - routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/", - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, err := Asset(r.URL.Path) - if err != nil { - w.WriteHeader(http.StatusNotFound) - return - } - w.Write(data) - log.Infof("Delivered %s from the bundle", r.URL.Path) - }))) - } + routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/", + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server.serveContent(w, r, r.URL.Path) + }))) - routesHandler.HandleFunc("/", defaultHandler) + routesHandler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + server.serveContent(w, r, "index.html") + }) routesHandler.HandleFunc("/s/{sessionID}", func(w http.ResponseWriter, r *http.Request) { - sessionsHandler(server, w, r) + server.handleSession(w, r) }) routesHandler.HandleFunc("/ws/{sessionID}", func(w http.ResponseWriter, r *http.Request) { - websocketHandler(server, w, r) + server.handleWebsocket(w, r) + }) + routesHandler.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server.serveContent(w, r, "404.html") }) server.activeSessions = make(map[string]*ttyShareSession) @@ -85,14 +115,14 @@ func getWSPath(sessionID string) string { return "/ws/" + sessionID } -func websocketHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Request) { +func (server *TTYProxyServer) handleWebsocket(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) sessionID := vars["sessionID"] defer log.Debug("Finished WS connection for ", sessionID) // Validate incoming request. if r.Method != "GET" { - w.WriteHeader(http.StatusMethodNotAllowed) + w.WriteHeader(http.StatusForbidden) return } @@ -112,20 +142,14 @@ func websocketHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Req if session == nil { log.Error("WE connection for invalid sessionID: ", sessionID, ". Killing it.") - // TODO: Create a proper way to communicate with the remote WS end, so that the server can send - // control messages or data messages to go directly to the terminal. - conn.WriteMessage(websocket.TextMessage, []byte("$ access denied.")) + w.WriteHeader(http.StatusForbidden) return } session.HandleReceiver(newWSConnection(conn)) } -func defaultHandler(http.ResponseWriter, *http.Request) { - log.Debug("Default handler ") -} - -func sessionsHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Request) { +func (server *TTYProxyServer) handleSession(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) sessionID := vars["sessionID"] @@ -133,31 +157,41 @@ func sessionsHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Requ session := getSession(server, sessionID) + // No valid session with this ID if session == nil { - w.WriteHeader(http.StatusMethodNotAllowed) + server.serveContent(w, r, "invalid-session.html") return } - templateDta, err := Asset("templates/index.html") + var t *template.Template + var err error + if server.config.FrontendPath == "" { + templateDta, err := Asset("tty-receiver.in.html") - if err != nil { - fmt.Fprintf(w, err.Error()) - return - } + if err != nil { + panic("Cannot find the tty-receiver html template") + } - t := template.New("index.html") - _, err = t.Parse(string(templateDta)) + t = template.New("tty-receiver.html") + _, err = t.Parse(string(templateDta)) + } else { + t, err = template.ParseFiles(server.config.FrontendPath + string(os.PathSeparator) + "tty-receiver.in.html") + } if err != nil { - fmt.Fprintf(w, err.Error()) - return + panic("Cannot parse the tty-receiver html template") } + templateModel := SessionTemplateModel{ SessionID: sessionID, Salt: "salt&pepper", WSPath: getWSPath(sessionID), } - t.Execute(w, templateModel) + err = t.Execute(w, templateModel) + + if err != nil { + panic("Cannot execute the tty-receiver html template") + } } func addNewSession(server *TTYProxyServer, session *ttyShareSession) {