Skip to content

Commit

Permalink
Rolled back to stable
Browse files Browse the repository at this point in the history
  • Loading branch information
joewalnes committed Oct 2, 2014
1 parent 2376a79 commit 98d35c0
Show file tree
Hide file tree
Showing 15 changed files with 352 additions and 651 deletions.
1 change: 0 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ func parseCommandLine() *Config {
mainConfig.Addr = []string{fmt.Sprintf(":%d", port)}
}
mainConfig.MaxForks = *maxForksFlag

mainConfig.BasePath = *basePathFlag
mainConfig.LogLevel = libwebsocketd.LevelFromString(*logLevelFlag)
if mainConfig.LogLevel == libwebsocketd.LogUnknown {
Expand Down
33 changes: 33 additions & 0 deletions libwebsocketd/endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2013 Joe Walnes and the websocketd team.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package libwebsocketd

type Endpoint interface {
StartReading()
Terminate()
Output() chan string
Send(string) bool
}

func PipeEndpoints(e1, e2 Endpoint) {
e1.StartReading()
e2.StartReading()

defer e1.Terminate()
defer e2.Terminate()
for {
select {
case msgOne, ok := <-e1.Output():
if !ok || !e2.Send(msgOne) {
return
}
case msgTwo, ok := <-e2.Output():
if !ok || !e1.Send(msgTwo) {
return
}
}
}
}
73 changes: 73 additions & 0 deletions libwebsocketd/endpoint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package libwebsocketd

import (
"strconv"
"testing"
"time"
)

var eol_tests = []string{
"", "\n", "\r\n", "ok\n", "ok\n",
"quite long string for our test\n",
"quite long string for our test\r\n",
}

var eol_answers = []string{
"", "", "", "ok", "ok",
"quite long string for our test", "quite long string for our test",
}

func TestTrimEOL(t *testing.T) {
for n := 0; n < len(eol_tests); n++ {
answ := trimEOL(eol_tests[n])
if answ != eol_answers[n] {
t.Errorf("Answer '%s' did not match predicted '%s'", answ, eol_answers[n])
}
}
}

func BenchmarkTrimEOL(b *testing.B) {
for n := 0; n < b.N; n++ {
trimEOL(eol_tests[n%len(eol_tests)])
}
}

type TestEndpoint struct {
limit int
prefix string
c chan string
result []string
}

func (e *TestEndpoint) StartReading() {
go func() {
for i := 0; i < e.limit; i++ {
e.c <- e.prefix + strconv.Itoa(i)
}
time.Sleep(time.Millisecond) // should be enough for smaller channel to catch up with long one
close(e.c)
}()
}

func (e *TestEndpoint) Terminate() {
}

func (e *TestEndpoint) Output() chan string {
return e.c
}

func (e *TestEndpoint) Send(msg string) bool {
e.result = append(e.result, msg)
return true
}

func TestEndpointPipe(t *testing.T) {
one := &TestEndpoint{2, "one:", make(chan string), make([]string, 0)}
two := &TestEndpoint{4, "two:", make(chan string), make([]string, 0)}
PipeEndpoints(one, two)
if len(one.result) != 4 || len(two.result) != 2 {
t.Errorf("Invalid lengths, should be 4 and 2: %v %v", one.result, two.result)
} else if one.result[0] != "two:0" || two.result[0] != "one:0" {
t.Errorf("Invalid first results, should be two:0 and one:0: %#v %#v", one.result[0], two.result[0])
}
}
93 changes: 16 additions & 77 deletions libwebsocketd/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"time"
)

var ErrScriptNotFound = errors.New("Script not found")
var ScriptNotFoundError = errors.New("script not found")

// WebsocketdHandler is a single request information and processing structure, it handles WS requests out of all that daemon can handle (static, cgi, devconsole)
type WebsocketdHandler struct {
Expand All @@ -27,7 +27,7 @@ type WebsocketdHandler struct {
command string
}

// NewWebsocketdHandler constructs the handler struct. It also prepares *Info elements and generates process environment.
// NewWebsocketdHandler constructs the struct and parses all required things in it...
func NewWebsocketdHandler(s *WebsocketdServer, req *http.Request, log *LogScope) (wsh *WebsocketdHandler, err error) {
wsh = &WebsocketdHandler{server: s, Id: generateId()}
log.Associate("id", wsh.Id)
Expand All @@ -49,7 +49,6 @@ func NewWebsocketdHandler(s *WebsocketdServer, req *http.Request, log *LogScope)
if s.Config.UsingScriptDir {
wsh.command = wsh.URLInfo.FilePath
}

log.Associate("command", wsh.command)

wsh.Env = createEnv(wsh, req, log)
Expand All @@ -60,88 +59,28 @@ func NewWebsocketdHandler(s *WebsocketdServer, req *http.Request, log *LogScope)
// wshandler returns function that executes code with given log context
func (wsh *WebsocketdHandler) wshandler(log *LogScope) websocket.Handler {
return websocket.Handler(func(ws *websocket.Conn) {
wsh.accept(&WebsocketWrapper{ws}, log)
wsh.accept(ws, log)
})
}

type wsConn interface {
Close() error
Receive(*string) error
Send(string) error
}

type WebsocketWrapper struct {
*websocket.Conn
}

func (ww *WebsocketWrapper) Receive(ptr *string) error {
return websocket.Message.Receive(ww.Conn, ptr)
}
func (ww *WebsocketWrapper) Send(s string) error {
return websocket.Message.Send(ww.Conn, s)
}

// accept connects process and websocket.
func (wsh *WebsocketdHandler) accept(ws wsConn, log *LogScope) {
func (wsh *WebsocketdHandler) accept(ws *websocket.Conn, log *LogScope) {
defer ws.Close()

log.Access("handler", "CONNECT")
defer log.Access("handler", "DISCONNECT")
log.Access("session", "CONNECT")
defer log.Access("session", "DISCONNECT")

p, output, err := wsh.server.launchServerProcess(wsh.command, wsh.Env, log)
launched, err := launchCmd(wsh.command, wsh.server.Config.CommandArgs, wsh.Env)
if err != nil {
log.Error("handler", "Could not launch process %s %s (%s)", wsh.command, strings.Join(wsh.server.Config.CommandArgs, " "), err)
log.Error("process", "Could not launch process %s %s (%s)", wsh.command, strings.Join(wsh.server.Config.CommandArgs, " "), err)
return
}

/// we need to unsubscribe as soon as we done.
defer p.Terminate()

// send websocket data to process
input := make(chan string)
go func() {
for {
var msg string
err := ws.Receive(&msg)
if err != nil {
close(input)
return
}
input <- msg
}
}()

for {
select {
case msg, ok := <-input:
if ok {
err = p.PassInput(msg)
if err != nil {
log.Info("handler", "Dropping input message, process input returned %s", err)
}
} else {
log.Info("handler", "Websocket client closed connection....")
return
}
case str, ok := <-output:
if !ok {
// we might still be able to pass input from websocket to process
log.Trace("handler", "Process stopped producing results")
return
}
err = ws.Send(str)
if err != nil {
log.Trace("handler", "Process data cannot be passed to websocket due to %s", err)
return
}
case <-time.After(time.Millisecond * 100):
// check every 100ms if process has been finished
if p.cmd.ProcessState != nil {
log.Trace("handler", "Process ended")
return
}
}
}
log.Associate("pid", strconv.Itoa(launched.cmd.Process.Pid))

process := NewProcessEndpoint(launched, log)
wsEndpoint := NewWebSocketEndpoint(ws, log)

PipeEndpoints(process, wsEndpoint)
}

// RemoteInfo holds information about remote http client
Expand Down Expand Up @@ -195,12 +134,12 @@ func GetURLInfo(path string, config *Config) (*URLInfo, error) {

// not a valid path
if err != nil {
return nil, ErrScriptNotFound
return nil, ScriptNotFoundError
}

// at the end of url but is a dir
if isLastPart && statInfo.IsDir() {
return nil, ErrScriptNotFound
return nil, ScriptNotFoundError
}

// we've hit a dir, carry on looking
Expand Down
16 changes: 2 additions & 14 deletions libwebsocketd/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestParsePathWithScriptDir(t *testing.T) {
if err == nil {
t.Error("non-existing file should fail")
}
if err != ErrScriptNotFound {
if err != ScriptNotFoundError {
t.Error("should fail with script not found")
}

Expand All @@ -77,7 +77,7 @@ func TestParsePathWithScriptDir(t *testing.T) {
if err == nil {
t.Error("non-existing dir should fail")
}
if err != ErrScriptNotFound {
if err != ScriptNotFoundError {
t.Error("should fail with script not found")
}
}
Expand All @@ -100,15 +100,3 @@ func TestParsePathExplicitScript(t *testing.T) {
t.Error("filePath")
}
}

func TestHandlerBasics(t *testing.T) {
wh := WebsocketdHandler{
server: nil,
Id: "",
RemoteInfo: &RemoteInfo{"", "", ""},
URLInfo: &URLInfo{"", "", ""},
Env: []string{},
command: "/bin/echo",
}
logger_helper(t.Log)
}
15 changes: 3 additions & 12 deletions libwebsocketd/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ import (
"net/http/cgi"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
)

var ErrForkNotAllowed = errors.New("Too many forks active")
var ForkNotAllowedError = errors.New("too many forks active")

// WebsocketdServer presents http.Handler interface for requests libwebsocketd is handling.
type WebsocketdServer struct {
Expand Down Expand Up @@ -65,7 +64,7 @@ func (h *WebsocketdServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {

handler, err := NewWebsocketdHandler(h, req, log)
if err != nil {
if err == ErrScriptNotFound {
if err == ScriptNotFoundError {
log.Access("session", "NOT FOUND: %s", err)
http.Error(w, "404 Not Found", 404)
} else {
Expand Down Expand Up @@ -160,7 +159,7 @@ func (h *WebsocketdServer) noteForkCreated() error {
case h.forks <- 1:
return nil
default:
return ErrForkNotAllowed
return ForkNotAllowedError
}
} else {
return nil
Expand All @@ -182,14 +181,6 @@ func (h *WebsocketdServer) noteForkCompled() {
return
}

func (h *WebsocketdServer) launchServerProcess(command string, env []string, log *LogScope) (*ExternalProcess, <-chan string, error) {
cmd := exec.Command(command, h.Config.CommandArgs...)
cmd.Env = env

log.Debug("process", "Starting %s", command)
return LaunchProcess(cmd, log)
}

func checkOrigin(wsconf *websocket.Config, req *http.Request, config *Config, log *LogScope) (err error) {
// check for origin to be correct in future
// handshaker triggers answering with 403 if error was returned
Expand Down
4 changes: 3 additions & 1 deletion libwebsocketd/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ Sec-WebSocket-Version: 13
t.Fatal("request", err)
}

log := logger_helper(t.Log)
log := new(LogScope)
log.LogFunc = func(*LogScope, LogLevel, string, string, string, ...interface{}) {}

wsconf := &websocket.Config{Version: websocket.ProtocolVersionHybi13}
config := new(Config)

Expand Down
45 changes: 45 additions & 0 deletions libwebsocketd/launcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2013 Joe Walnes and the websocketd team.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package libwebsocketd

import (
"io"
"os/exec"
)

type LaunchedProcess struct {
cmd *exec.Cmd
stdin io.WriteCloser
stdout io.ReadCloser
stderr io.ReadCloser
}

func launchCmd(commandName string, commandArgs []string, env []string) (*LaunchedProcess, error) {
cmd := exec.Command(commandName, commandArgs...)
cmd.Env = env

stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}

stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}

stdin, err := cmd.StdinPipe()
if err != nil {
return nil, err
}

err = cmd.Start()
if err != nil {
return nil, err
}

return &LaunchedProcess{cmd, stdin, stdout, stderr}, err
}
Loading

0 comments on commit 98d35c0

Please sign in to comment.