Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
abdadf1e
Commit
abdadf1e
authored
Mar 19, 2015
by
Matthew Holt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improvements to websocket middleware
parent
d7ae9fb4
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
84 additions
and
37 deletions
+84
-37
middleware/websockets/websocket.go
middleware/websockets/websocket.go
+18
-11
middleware/websockets/websockets.go
middleware/websockets/websockets.go
+66
-26
No files found.
middleware/websockets/websocket.go
View file @
abdadf1e
...
...
@@ -10,7 +10,7 @@ import (
)
// WebSocket represents a web socket server instance. A WebSocket
//
struct is instantiated for each new websocket request
.
//
is instantiated for each new websocket request/connection
.
type
WebSocket
struct
{
WSConfig
*
http
.
Request
...
...
@@ -21,33 +21,40 @@ type WebSocket struct {
// the command's stdin and stdout.
func
(
ws
WebSocket
)
Handle
(
conn
*
websocket
.
Conn
)
{
cmd
:=
exec
.
Command
(
ws
.
Command
,
ws
.
Arguments
...
)
cmd
.
Stdin
=
conn
cmd
.
Stdout
=
conn
cmd
.
Stderr
=
conn
// TODO: Make this configurable from the Caddyfile
err
:=
ws
.
buildEnv
(
cmd
)
metavars
,
err
:=
ws
.
buildEnv
(
cmd
.
Path
)
if
err
!=
nil
{
// TODO
panic
(
err
)
// TODO
}
cmd
.
Env
=
metavars
err
=
cmd
.
Run
()
if
err
!=
nil
{
panic
(
err
)
}
}
// buildEnv
set
s the meta-variables for the child process according
// buildEnv
create
s the meta-variables for the child process according
// to the CGI 1.1 specification: http://tools.ietf.org/html/rfc3875#section-4.1
func
(
ws
WebSocket
)
buildEnv
(
cmd
*
exec
.
Cmd
)
error
{
// cmdPath should be the path of the command being run.
// The returned string slice can be set to the command's Env property.
func
(
ws
WebSocket
)
buildEnv
(
cmdPath
string
)
(
metavars
[]
string
,
err
error
)
{
remoteHost
,
remotePort
,
err
:=
net
.
SplitHostPort
(
ws
.
RemoteAddr
)
if
err
!=
nil
{
return
err
return
}
serverHost
,
serverPort
,
err
:=
net
.
SplitHostPort
(
ws
.
Host
)
if
err
!=
nil
{
return
err
return
}
cmd
.
Env
=
[]
string
{
metavars
=
[]
string
{
`AUTH_TYPE=`
,
// Not used
`CONTENT_LENGTH=`
,
// Not used
`CONTENT_TYPE=`
,
// Not used
...
...
@@ -62,7 +69,7 @@ func (ws WebSocket) buildEnv(cmd *exec.Cmd) error {
`REMOTE_USER=`
,
// Not used,
`REQUEST_METHOD=`
+
ws
.
Method
,
`REQUEST_URI=`
+
ws
.
RequestURI
,
`SCRIPT_NAME=`
,
// TODO - absolute path to program being executed?
`SCRIPT_NAME=`
+
cmdPath
,
// path of the program being executed
`SERVER_NAME=`
+
serverHost
,
`SERVER_PORT=`
+
serverPort
,
`SERVER_PROTOCOL=`
+
ws
.
Proto
,
...
...
@@ -75,8 +82,8 @@ func (ws WebSocket) buildEnv(cmd *exec.Cmd) error {
header
=
strings
.
ToUpper
(
header
)
header
=
strings
.
Replace
(
header
,
"-"
,
"_"
,
-
1
)
value
=
strings
.
Replace
(
value
,
"
\n
"
,
" "
,
-
1
)
cmd
.
Env
=
append
(
cmd
.
Env
,
"HTTP_"
+
header
+
"="
+
value
)
metavars
=
append
(
metavars
,
"HTTP_"
+
header
+
"="
+
value
)
}
return
nil
return
}
middleware/websockets/websockets.go
View file @
abdadf1e
...
...
@@ -17,16 +17,20 @@ type (
// websocket middleware generally, like a list of all the
// websocket endpoints.
WebSockets
struct
{
// Next is the next HTTP handler in the chain for when the path doesn't match
Next
http
.
HandlerFunc
// Sockets holds all the web socket endpoint configurations
Sockets
[]
WSConfig
}
// WSConfig holds the configuration for a single websocket
// endpoint which may serve
zero or mor
e websocket connections.
// endpoint which may serve
multipl
e websocket connections.
WSConfig
struct
{
Path
string
Command
string
Arguments
[]
string
Respawn
bool
// TODO: Not used, but parser supports it until we decide on it
}
)
...
...
@@ -42,11 +46,27 @@ func (ws WebSockets) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
}
// Didn't match a websocket path, so pass-thru
ws
.
Next
(
w
,
r
)
}
// New constructs and configures a new websockets middleware instance.
func
New
(
c
middleware
.
Controller
)
(
middleware
.
Middleware
,
error
)
{
var
websocks
[]
WSConfig
var
respawn
bool
optionalBlock
:=
func
()
(
hadBlock
bool
,
err
error
)
{
for
c
.
NextBlock
()
{
hadBlock
=
true
if
c
.
Val
()
==
"respawn"
{
respawn
=
true
}
else
{
return
true
,
c
.
Err
(
"Expected websocket configuration parameter in block"
)
}
}
return
}
for
c
.
Next
()
{
var
val
,
path
,
command
string
...
...
@@ -57,38 +77,40 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
}
val
=
c
.
Val
()
// The rest of the arguments are the command
if
c
.
NextArg
()
{
path
=
val
command
=
c
.
Val
()
for
c
.
NextArg
()
{
command
+=
" "
+
c
.
Val
()
}
}
else
{
path
=
"/"
command
=
val
// Extra configuration may be in a block
hadBlock
,
err
:=
optionalBlock
()
if
err
!=
nil
{
return
nil
,
err
}
// Split command into the actual command and its arguments
var
cmd
string
var
args
[]
string
if
!
hadBlock
{
// The next argument on this line will be the command or an open curly brace
if
c
.
NextArg
()
{
path
=
val
command
=
c
.
Val
()
}
else
{
path
=
"/"
command
=
val
}
parts
,
err
:=
shlex
.
Split
(
command
)
if
err
!=
nil
{
return
nil
,
errors
.
New
(
"Error parsing command for websocket use: "
+
err
.
Error
())
}
else
if
len
(
parts
)
==
0
{
return
nil
,
errors
.
New
(
"No command found for use by websocket"
)
// Okay, check again for optional block
hadBlock
,
err
=
optionalBlock
()
if
err
!=
nil
{
return
nil
,
err
}
}
cmd
=
parts
[
0
]
if
len
(
parts
)
>
1
{
args
=
parts
[
1
:
]
// Split command into the actual command and its arguments
cmd
,
args
,
err
:=
parseCommandAndArgs
(
command
)
if
err
!=
nil
{
return
nil
,
err
}
websocks
=
append
(
websocks
,
WSConfig
{
Path
:
path
,
Command
:
cmd
,
Arguments
:
args
,
Respawn
:
respawn
,
})
}
...
...
@@ -96,12 +118,30 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
ServerSoftware
=
envServerSoftware
return
func
(
next
http
.
HandlerFunc
)
http
.
HandlerFunc
{
// We don't use next because websockets aren't HTTP,
// so we don't invoke other middleware after this.
return
WebSockets
{
Sockets
:
websocks
}
.
ServeHTTP
return
WebSockets
{
Next
:
next
,
Sockets
:
websocks
}
.
ServeHTTP
},
nil
}
// parseCommandAndArgs takes a command string and parses it
// shell-style into the command and its separate arguments.
func
parseCommandAndArgs
(
command
string
)
(
cmd
string
,
args
[]
string
,
err
error
)
{
parts
,
err
:=
shlex
.
Split
(
command
)
if
err
!=
nil
{
err
=
errors
.
New
(
"Error parsing command for websocket: "
+
err
.
Error
())
return
}
else
if
len
(
parts
)
==
0
{
err
=
errors
.
New
(
"No command found for use by websocket"
)
return
}
cmd
=
parts
[
0
]
if
len
(
parts
)
>
1
{
args
=
parts
[
1
:
]
}
return
}
var
(
// See CGI spec, 4.1.4
GatewayInterface
string
...
...
@@ -112,5 +152,5 @@ var (
const
(
envGatewayInterface
=
"caddy-CGI/1.1"
envServerSoftware
=
"caddy/
0.1.0"
envServerSoftware
=
"caddy/
?.?.?"
// TODO
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment