-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Aaron Raddon
committed
Nov 27, 2011
1 parent
879ee1b
commit 3f2a57a
Showing
9 changed files
with
750 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
include $(GOROOT)/src/Make.inc | ||
|
||
TARG=m2go | ||
GOFMT=gofmt -spaces=true -tabindent=false -tabwidth=2 | ||
|
||
GOFILES=\ | ||
m2go.go\ | ||
tnet.go\ | ||
signals.go\ | ||
|
||
include $(GOROOT)/src/Make.pkg | ||
|
||
format: | ||
${GOFMT} -w m2go_test.go | ||
${GOFMT} -w tnet_test.go | ||
${GOFMT} -w example.go | ||
${GOFMT} -w ${GOFILES} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
m2go, a `Mongrel2 http server<http://mongrel2.org>`_ for go. Includes adapter to run `Web.go <https://github.com/hoisie/web.go>`_ | ||
|
||
Installation | ||
======================== | ||
|
||
Requires mongrel2, and a configured mongrel2 website. | ||
|
||
Currently built against go-weekly weekly-2011-11-18 | ||
|
||
Uses a custom version of Web.go (see github) until patches make it in. | ||
|
||
|
||
Git clone && gomake install or goinstall |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
include $(GOROOT)/src/Make.inc | ||
|
||
ALL=example | ||
|
||
all: $(ALL) | ||
|
||
clean: | ||
rm -rf *.[68] $(ALL) | ||
|
||
%: %.go | ||
$(GC) $*.go | ||
$(LD) -o $@ $*.$O |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//#!~/gocode/bin/gorun | ||
package main | ||
|
||
import ( | ||
"m2go" | ||
//"fmt" | ||
"web" | ||
) | ||
|
||
func hello(val string) string { | ||
return "hello " + val | ||
} | ||
|
||
func main() { | ||
web.Get("/(.*)", hello) | ||
web.Post("/(.*)", hello) | ||
|
||
m2go.Run( "tcp://127.0.0.1:9555|tcp://127.0.0.1:9556|54c6755b-9628-40a4-9a2d-cc82a816345e") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
/* | ||
Mongrel2 Handler, Parser, and Web.go Adapter | ||
*/ | ||
package m2go | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
"net/url" | ||
"os" | ||
"strings" | ||
//"strconv" | ||
"web" | ||
zmq "github.com/alecthomas/gozmq" | ||
//config "github.com/kless/goconfig/config" | ||
) | ||
|
||
var Logger = log.New(os.Stdout, "", log.Ldate|log.Ltime) | ||
|
||
var er error | ||
|
||
type Config struct { | ||
logfile string | ||
configFile string | ||
useconsole bool | ||
} | ||
|
||
var M2Config = &Config{"log/dev.log","prod.conf", true} | ||
|
||
|
||
/* | ||
M2 Request and server handling | ||
*/ | ||
|
||
type M2Request struct { | ||
uuid string | ||
requestid string | ||
path string | ||
rest string | ||
body string | ||
headers string | ||
cid uint //customer id | ||
} | ||
|
||
type Response struct { | ||
out string // response text | ||
status string // OK etc | ||
code string // 404, 500, etc | ||
} | ||
|
||
type m2Conn struct { | ||
headers map[string][]string | ||
wroteHeaders bool | ||
out bytes.Buffer | ||
} | ||
|
||
func (conn *m2Conn) Write(data []byte) (n int, err error) { | ||
var buf bytes.Buffer | ||
|
||
if !conn.wroteHeaders { | ||
conn.wroteHeaders = true | ||
for k, v := range conn.headers { | ||
for _, i := range v { | ||
buf.WriteString(k + ": " + i + "\r\n") | ||
} | ||
} | ||
|
||
buf.WriteString("\r\n") | ||
conn.out.Write(buf.Bytes()) | ||
} | ||
return conn.out.Write(data) | ||
} | ||
|
||
func (conn *m2Conn) StartResponse(status int) { | ||
var buf bytes.Buffer | ||
text := web.StatusText[status] | ||
|
||
fmt.Fprintf(&buf, "HTTP/1.1 %d %s\r\n", status, text) | ||
conn.out.Write(buf.Bytes()) | ||
} | ||
|
||
func (conn *m2Conn) SetHeader(hdr string, val string, unique bool) { | ||
if _, contains := conn.headers[hdr]; !contains { | ||
conn.headers[hdr] = []string{val} | ||
return | ||
} | ||
|
||
if unique { | ||
//just overwrite the first value | ||
conn.headers[hdr][0] = val | ||
} else { | ||
newHeaders := make([]string, len(conn.headers)+1) | ||
copy(newHeaders, conn.headers[hdr]) | ||
newHeaders[len(newHeaders)-1] = val | ||
conn.headers[hdr] = newHeaders | ||
} | ||
} | ||
|
||
func (conn *m2Conn) Close() { | ||
|
||
} | ||
|
||
func NewHttpRequestFromM2(headers http.Header, body io.Reader) *web.Request { | ||
|
||
host := headers.Get("host") | ||
method := strings.ToUpper(headers.Get("method")) | ||
path := headers.Get("uri") | ||
proto := headers.Get("version") | ||
rawurl := "http://" + host + path | ||
url_, _ := url.Parse(rawurl) | ||
useragent := headers.Get("user-agent") | ||
remoteAddr := headers.Get("x-forwarded-for") | ||
//remotePort, _ := strconv.Atoi(headers.Get("REMOTE_PORT")) | ||
|
||
if method == "POST" { | ||
if ctype, ok := headers["CONTENT_TYPE"]; ok { | ||
headers["Content-Type"] = ctype | ||
} | ||
|
||
if clength, ok := headers["CONTENT_LENGTH"]; ok { | ||
headers["Content-Length"] = clength | ||
} | ||
} | ||
|
||
//read the cookies | ||
cookies := web.ReadCookies(headers) | ||
|
||
return &web.Request{ | ||
Method: method, | ||
RawURL: rawurl, | ||
URL: url_, | ||
Proto: proto, | ||
Host: host, | ||
UserAgent: useragent, | ||
Body: body, | ||
Headers: headers, | ||
RemoteAddr: remoteAddr, | ||
//RemotePort: remotePort, | ||
Cookie: cookies, | ||
} | ||
|
||
} | ||
|
||
// Parse an incoming request of format/type mongrel2 | ||
// and return an M2Request object | ||
func M2Parse(reqs string) (r *M2Request, err error){ | ||
|
||
r = new(M2Request) | ||
//Logger.Printf("raw\n\n %s \n\n", strconv.Quote(string(reqs))) | ||
|
||
parts := strings.SplitN(string(reqs)," ",4) | ||
r.uuid = parts[0] | ||
r.requestid = parts[1] | ||
r.path = parts[2] | ||
r.rest = strings.ToLower(parts[3]) | ||
|
||
tn, err := NewTnet(r.rest) | ||
if err != nil { | ||
err = errors.New("Error parsing request " + err.Error()) | ||
return | ||
} | ||
r.headers = tn.Value.(string) | ||
tnetb, err := tn.Next() | ||
if err != nil { | ||
err = errors.New("Error parsing request " + err.Error()) | ||
return | ||
} | ||
r.body = tnetb.Value.(string) | ||
|
||
return | ||
} | ||
|
||
func MakeWebGoRequest(m2req *M2Request) (req *web.Request, err error) { | ||
headers := make(http.Header) | ||
|
||
var f map[string]interface{} | ||
|
||
err = json.Unmarshal([]byte(m2req.headers), &f) | ||
|
||
if err != nil { | ||
Logger.Printf("arg, error %s", err) | ||
} | ||
for k, v := range f { | ||
headers.Set(k, v.(string)) | ||
} | ||
body := bytes.NewBuffer([]byte(m2req.body)) | ||
|
||
req = NewHttpRequestFromM2(headers, body) | ||
|
||
return req, nil | ||
} | ||
|
||
func HandleM2Request(s *web.Server, datain []byte, response func(response string)()) { | ||
|
||
m2req, tnerr := M2Parse(string(datain)) | ||
if tnerr != nil { | ||
// TODO: tap into web.go 500 mechanism | ||
Logger.Println("ERROR", tnerr.Error()) | ||
} | ||
|
||
wreq, err := MakeWebGoRequest(m2req) | ||
if err != nil { | ||
// TODO: tap into web.go 500 mechanism | ||
Logger.Println("error", err.Error()) | ||
} | ||
|
||
conn := m2Conn{ make(map[string][]string), false, bytes.Buffer{}} | ||
s.RouteHandler(wreq, &conn) | ||
|
||
msg := fmt.Sprintf("%s %d:%s, %s", m2req.uuid, len(m2req.requestid), m2req.requestid, string(conn.out.Bytes())) | ||
Logger.Print("--------------------RESPONSE---------------------\n" + msg + "\n") | ||
response(msg) | ||
} | ||
|
||
// default package init function | ||
func init() { | ||
|
||
RegisterSignalHandler(os.SIGINT, func() { | ||
fmt.Print("in signal handler SIGINT") | ||
Exit(0) | ||
}) | ||
RegisterSignalHandler(os.SIGTERM, func() { | ||
fmt.Print("in signal handler SIGTERM") | ||
Exit(0) | ||
}) | ||
RegisterSignalHandler(os.SIGUSR1, func() { | ||
fmt.Print("in signal handler USR1") | ||
}) | ||
|
||
go handleSignals() | ||
|
||
loadConfig() | ||
|
||
} | ||
|
||
|
||
// server Method to initialize with config | ||
func loadConfig(){ | ||
|
||
flag.StringVar(&M2Config.configFile, "config", "none", "Config File to use") | ||
flag.BoolVar(&M2Config.useconsole, "useconsole", true, "log to console?") | ||
flag.Parse() | ||
|
||
if M2Config.configFile != "none" { | ||
//c, _ := config.ReadDefault(M2Config.configFile) | ||
// read log file etc | ||
|
||
} else { | ||
|
||
if M2Config.useconsole == true { | ||
fmt.Println("Logging to console") | ||
} | ||
} | ||
|
||
} | ||
|
||
//Runs the web application and serves scgi requests | ||
func Run(addr string) { | ||
web.Runner(addr, M2Runner) | ||
} | ||
|
||
// method for server that Runs the web application, sets up m2 connections | ||
// and serves http requests | ||
// @addr = string config parameter like this: | ||
// "tcp://127.0.0.1:9555|tcp://127.0.0.1:9556|54c6755b-9628-40a4-9a2d-cc82a816345e" | ||
func M2Runner(s *web.Server, addr string) { | ||
|
||
var Context zmq.Context | ||
var SocketIn zmq.Socket | ||
var SocketOut zmq.Socket | ||
m2addr := strings.Split(addr,"|")// | ||
|
||
s.Config.StaticDir = "NONE" | ||
|
||
//mainServer.RunM2(addr) | ||
if s.Logger == nil { | ||
s.Logger = Logger | ||
} | ||
s.Logger.Printf("web.go serving m2 %s\n", addr) | ||
|
||
/* | ||
Connection to ZMQ setup | ||
*/ | ||
var err error | ||
if Context, err = zmq.NewContext(); err != nil { | ||
panic("No ZMQ Context?") | ||
} | ||
defer Context.Close() | ||
|
||
// listen for incoming requests | ||
if SocketIn, err = Context.NewSocket(zmq.PULL); err != nil { | ||
panic("No ZMQ Socket?") | ||
} | ||
defer SocketIn.Close() | ||
SocketIn.Connect(m2addr[0]) | ||
|
||
|
||
if SocketOut, err = Context.NewSocket(zmq.PUB); err != nil { | ||
panic("No ZMQ Socket Outbound??") | ||
} | ||
// outbound response on a different channel | ||
SocketOut.SetSockOptString(zmq.IDENTITY, m2addr[2]) | ||
//socket.SetSockOptString(zmq.SUBSCRIBE, filter) | ||
defer SocketOut.Close() | ||
SocketOut.Connect(m2addr[1]) | ||
|
||
handleResponse := func(response string) { | ||
SocketOut.Send([]byte(response), 0) | ||
} | ||
|
||
for { | ||
// each inbound request | ||
datapt, err := SocketIn.Recv(0) | ||
if err != nil { | ||
Logger.Println("ZMQ Socket Input accept error", err.Error()) | ||
} | ||
go HandleM2Request(s,datapt,handleResponse) | ||
} | ||
|
||
} |
Oops, something went wrong.