161 lines
2.9 KiB
Go
161 lines
2.9 KiB
Go
|
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)
|
||
|
}
|
||
|
}
|