"bufio"
"flag"
"fmt"
+ "io"
"io/ioutil"
"log"
"net"
"os"
+ "strconv"
"strings"
)
)
var cfg = struct {
- bindIface string
- bindPortTcp string
- fqdn string
- docRoot string
- indexFile string
- verbose bool
+ iface string
+ port string
+ fqdn string
+ docRoot string
+ header string
+ verbose bool
}{
- bindIface: "localhost",
- bindPortTcp: "7070",
- fqdn: "localhost",
- docRoot: ".",
- indexFile: "index.gox",
- verbose: false,
+ iface: "localhost",
+ port: "7070",
+ fqdn: "localhost",
+ docRoot: ".",
+ header: "igogopherd on localhost \r\n",
+ verbose: false,
}
func initialize() {
- flag.StringVar(&cfg.bindIface, "i", cfg.bindIface, "interface to bind to")
- flag.StringVar(&cfg.bindPortTcp, "p", cfg.bindPortTcp, "TCP port to listen on")
+ flag.StringVar(&cfg.iface, "i", cfg.iface, "interface to bind to")
+ flag.StringVar(&cfg.port, "p", cfg.port, "TCP port to listen on")
flag.StringVar(&cfg.docRoot, "d", cfg.docRoot, "document root directory")
- flag.StringVar(&cfg.indexFile, "x", cfg.indexFile, "index file")
+ flag.StringVar(&cfg.header, "H", cfg.header, "index header")
flag.BoolVar(&cfg.verbose, "v", cfg.verbose, "produce verbose output")
flag.Parse()
if 0 != len(flag.Args()) {
tracer = log.New(ioutil.Discard, "", 0)
}
- tracer.Print("interface: ", cfg.bindIface)
- tracer.Print("TCP port: ", cfg.bindPortTcp)
- tracer.Print("doc root: ", cfg.docRoot)
- tracer.Print("index file: ", cfg.indexFile)
- tracer.Print("verbose: ", cfg.verbose)
+ tracer.Print("interface: ", cfg.iface)
+ tracer.Print("TCP port: ", cfg.port)
+ tracer.Print("doc root: ", cfg.docRoot)
+ tracer.Print("header: ", cfg.header)
+ tracer.Print("verbose: ", cfg.verbose)
}
func checkFatal(err error, msg string) {
}
}
-func check(err error, msg string)(error) {
+func check(err error, msg string) error {
if err != nil {
tracer.Print(msg, ": ", err.Error())
}
return err
}
-func getErrorString(code string) (string) {
- // TODO flesh this out
- res := "Error: " + code + " Resource not found. "
- return res;
-}
-
-func sanitizeSelector(selector string) (string) {
+func sanitizeSelector(selector string) string {
selector = strings.Replace(selector, "\r", "", -1)
selector = strings.Replace(selector, "\n", "", -1)
selector = strings.Replace(selector, "../", "/", -1)
return selector
}
-func getResource(selector string) ([]byte, error) {
- if ( selector == "" || selector == "/" ) {
- selector = cfg.indexFile
+func makeIndex(selector string) (string, error) {
+ dirname := cfg.docRoot + selector
+ d, err := os.Open(dirname)
+ defer d.Close()
+ if check(err, "Open "+dirname) != nil {
+ return "", err
+ }
+ fi, err := d.Readdir(-1)
+ if check(err, "Readdir "+dirname) != nil {
+ return "", err
+ }
+ list := ""
+ loc := "\t" + cfg.fqdn + "\t" + cfg.port + "\r\n"
+ for _, fi := range fi {
+ if fi.Mode().IsRegular() {
+ list += "0" + fi.Name() + " (" + strconv.FormatInt(fi.Size(), 10) + ")\t" + selector + "/" + fi.Name() + loc
+ } else if fi.Mode().IsDir() {
+ // TODO: determine file type
+ list += "1" + fi.Name() + "\t" + selector + "/" + fi.Name() + loc
+ }
}
- resPath := cfg.docRoot + "/" + selector
- res, err := ioutil.ReadFile(resPath)
- if check(err,"ReadFile " + resPath) != nil { res = []byte(""); }
- return res, err;
+ return list, error(nil)
}
func handleRequest(conn net.Conn) {
defer conn.Close()
- reply := []byte("")
req, err := bufio.NewReader(conn).ReadString('\n')
- if ( check(err,"ReadString") != nil ) {
+ if check(err, "ReadString") != nil {
return
+ }
+ selector := sanitizeSelector("/" + req)
+ tracer.Print("selector: '", selector, "'")
+
+ path := cfg.docRoot + selector
+ fi, err := os.Stat(path)
+ if check(err, "Stat "+path) != nil {
+ return
+ }
+ var nb int64
+ if fi.IsDir() {
+ sres, err := makeIndex(selector)
+ if check(err, "makeIndex "+path) != nil {
+ return
+ }
+ b, err := conn.Write([]byte(cfg.header + sres))
+ if check(err, "Write") != nil {
+ return
+ }
+ nb = int64(b)
+ } else if fi.Mode().IsRegular() {
+ file, err := os.Open(path)
+ if check(err, "Open "+path) != nil {
+ return
+ }
+ defer file.Close()
+ nb, err = io.Copy(conn, file)
+ if check(err, "Copy "+path) != nil {
+ return
+ }
} else {
- tracer.Print("request: '", req, "'")
- selector := sanitizeSelector(req)
- tracer.Print("selector: '", selector, "'")
- reply, err = getResource(selector)
- if ( err != nil ) { reply = []byte(getErrorString("404") + selector) }
+ // TODO: handle other cases?
+ nb = -1
}
- conn.Write(reply)
+ tracer.Print(strconv.FormatInt(nb, 10) + " bytes sent.")
+ return
}
func serveTCP(sock net.Listener) {
func main() {
initialize()
- bindaddr := cfg.bindIface + ":" + cfg.bindPortTcp
+ bindaddr := cfg.iface + ":" + cfg.port
tsock, err := net.Listen("tcp", bindaddr)
- checkFatal(err, "net.Listen tcp " + bindaddr)
+ checkFatal(err, "net.Listen tcp "+bindaddr)
defer tsock.Close()
tracer.Print("listening on TCP ", bindaddr)
go serveTCP(tsock)