Skip to content

Instantly share code, notes, and snippets.

@nakamuray
Created April 3, 2013 14:43
Show Gist options
  • Save nakamuray/5301815 to your computer and use it in GitHub Desktop.
Save nakamuray/5301815 to your computer and use it in GitHub Desktop.
go はじめました。
package main
import (
"bytes"
"fmt"
"io"
"net"
"os"
"path"
"path/filepath"
"strings"
)
var documentRoot string
type Request struct {
Method string
Path string
Header Header
}
type Response struct {
Code int
Message string
Header Header
Body io.Reader
}
type Header map[string]string
func requestHandler(request Request) Response {
fmt.Printf("%q\n", request)
if !filepath.IsAbs(request.Path) {
return errorResponse(400, "Bad Request")
}
requestedPath, err := filepath.Abs(request.Path)
if err != nil {
return errorResponse(400, "Bad Request")
}
fpath := filepath.Join(documentRoot, requestedPath)
if !pathExists(fpath) {
return errorResponse(404, "File Not Found")
}
if pathIsDir(fpath) {
return directoryIndex(requestedPath, fpath)
}
f, err := os.Open(fpath)
if err != nil {
return errorResponse(403, "Forbidden")
}
var contentType string
switch path.Ext(fpath) {
case ".txt", ".go":
contentType = "text/plain"
case ".html", ".htm":
contentType = "text/html"
default:
contentType = "application/octet-stream"
}
return Response{200, "OK", Header{"Content-Type": contentType}, f}
}
func errorResponse(code int, msg string) Response {
return Response{code, msg, Header{"Content-Type": "text/plain"}, strings.NewReader(msg + "\n")}
}
func pathExists(path string) bool {
_, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
func pathIsDir(path string) bool {
stat, err := os.Stat(path)
if err != nil {
return false
}
return stat.IsDir()
}
func directoryIndex(requestedPath, path string) Response {
f, err := os.Open(path)
if err != nil {
return errorResponse(403, "Forbidden")
}
fileInfos, err := f.Readdir(-1)
if err != nil {
return errorResponse(403, "Forbidden")
}
buf := bytes.NewBuffer(nil)
if !strings.HasSuffix(requestedPath, "/") {
requestedPath += "/"
}
buf.WriteString("<html>\n")
buf.WriteString(fmt.Sprintf("<head><title>%s</title></head>\n", requestedPath))
buf.WriteString("<body>\n")
buf.WriteString(fmt.Sprintf("<h1>%s</h1>\n", requestedPath))
buf.WriteString("<ul>\n")
for _, info := range fileInfos {
name := info.Name()
if info.IsDir() {
buf.WriteString(fmt.Sprintf("<li><a href=\"./%s/\">%s/</a></li>\n", name, name))
} else {
buf.WriteString(fmt.Sprintf("<li><a href=\"./%s\">%s</a></li>\n", name, name))
}
}
buf.WriteString("</ul>\n")
buf.WriteString("</body>\n")
buf.WriteString("</html>\n")
return Response{200, "OK", Header{"Content-Type": "text/html"}, buf}
}
func readRequest(input io.Reader) (Request, error) {
var method string
var path string
var buffer bytes.Buffer
p := make([]byte, 4096)
for {
n, err := input.Read(p)
if err != nil {
return Request{}, err
}
buffer.Write(p[0:n])
if bytes.HasSuffix(buffer.Bytes(), []byte("\r\n\r\n")) {
break
}
}
data := buffer.String()
lines := strings.Split(data, "\r\n")
buf := strings.Split(lines[0], " ")
method = buf[0]
path = buf[1]
header := make(map[string]string)
for i := 1; i < len(lines)-2; i++ {
buf = strings.Split(lines[i], ":")
header[strings.TrimSpace(buf[0])] = strings.TrimSpace(buf[1])
}
return Request{method, path, header}, nil
}
func writeResponse(output io.Writer, response Response) {
fmt.Fprintf(output, "HTTP/1.0 %d %s\r\n", response.Code, response.Message)
for k, v := range response.Header {
fmt.Fprintf(output, "%s: %s\r\n", k, v)
}
io.WriteString(output, "\r\n")
io.Copy(output, response.Body)
}
func worker(conn io.ReadWriteCloser) {
request, err := readRequest(conn)
if err != nil {
fmt.Println(err)
return
}
response := requestHandler(request)
writeResponse(conn, response)
conn.Close()
}
func exitIfError(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func main() {
ln, err := net.Listen("tcp", ":8888")
exitIfError(err)
wd, err := os.Getwd()
exitIfError(err)
documentRoot = wd
fmt.Println("server started on http://localhost:8888/")
for {
conn, err := ln.Accept()
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
go worker(conn)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment