basic HTTP server

This commit is contained in:
Meutel 2017-07-11 19:56:50 +02:00
parent 872b216455
commit 2b1c780df3
2 changed files with 161 additions and 0 deletions

1
.gitignore vendored
View File

@ -50,3 +50,4 @@ echoserver/echoserver
example-redis/example-redis
echorot13/echorot13
chatserver/chatserver
example-httptcp/example-httptcp

160
example-httptcp/main.go Normal file
View File

@ -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)
}
}