diff --git a/.gitignore b/.gitignore index ce7e81c..b67c4dc 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ echoserver/echoserver example-redis/example-redis echorot13/echorot13 chatserver/chatserver +example-httptcp/example-httptcp diff --git a/example-httptcp/main.go b/example-httptcp/main.go new file mode 100644 index 0000000..61b8b78 --- /dev/null +++ b/example-httptcp/main.go @@ -0,0 +1,160 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + "io" + "log" + "net" + "strings" +) + +type HttpRequest struct { + Method string + Url string + Proto string + Version string + Headers map[string]string + Body string +} + +type HttpResponse struct { + Proto string + Code int + Status string + Body string +} + +func checkMethod(m string) error { + switch m { + case "GET": + case "POST": + case "PUT": + case "HEAD": + case "DELETE": + case "OPTIONS": + default: + return errors.New("Unknown method") + } + return nil +} +func checkProto(p string) error { + switch p { + case "HTTP/1.1": + case "HTTP/1.0": + default: + return errors.New("Unknown proto") + } + return nil +} + +func (req *HttpRequest) parseRequestLine(line string) error { + parts := strings.Fields(line) + if len(parts) != 3 { + return errors.New("Invalid request line") + } + for i, p := range parts { + switch i { + case 0: + if err := checkMethod(p); err != nil { + return err + } + req.Method = p + case 1: + req.Url = p + case 2: + if err := checkProto(p); err != nil { + return err + } + req.Proto = p + } + } + req.Headers = make(map[string]string) + return nil +} +func (req *HttpRequest) parseHeader(line string) error { + parts := strings.Fields(line) + if len(parts) < 2 { + return errors.New("Invalid header") + } + name := parts[0] + if !strings.HasSuffix(name, ":") { + return errors.New("Invalid header") + } + value := strings.Join(parts[1:], " ") + req.Headers[name[:len(name)-1]] = value + return nil +} +func (req *HttpRequest) appendBody(line string) error { + req.Body += line + "\n" + return nil +} +func (req *HttpRequest) handleRequest() *HttpResponse { + resp := &HttpResponse{ + Proto: req.Proto, + Code: 200, + Status: "OK", + Body: "URL = " + req.Url + "\n", + } + return resp +} +func (resp *HttpResponse) String() string { + return fmt.Sprintf("%s %d %s\r\nContent-Length: %d\r\n\r\n%s", + resp.Proto, resp.Code, resp.Status, len(resp.Body), resp.Body) +} + +func handle(conn net.Conn) { + defer conn.Close() + + req := new(HttpRequest) + scanner := bufio.NewScanner(conn) + requestLine := true + headers := false + for scanner.Scan() { + line := scanner.Text() + if requestLine { + fmt.Println("Request line:", line) + if err := req.parseRequestLine(line); err != nil { + log.Fatalln("error request line", err) // TODO response error + } + requestLine, headers = false, true + } else if headers { + if line == "" { + fmt.Println("End headers") + headers = false + break + } else { + fmt.Println("Header:", line) + if err := req.parseHeader(line); err != nil { + log.Println("error header", err) + } + } + } else { + break + /* + if err := req.appendBody(line); err != nil { + log.Println("error body", err) + } + */ + } + } + resp := req.handleRequest() + io.WriteString(conn, resp.String()) +} + +func main() { + server, err := net.Listen("tcp", ":9000") + if err != nil { + log.Fatalln(err) + } + defer server.Close() + + for { + conn, err := server.Accept() + if err != nil { + log.Fatalln(err) + } + go handle(conn) + } +}