basic HTTP server
This commit is contained in:
parent
872b216455
commit
2b1c780df3
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
160
example-httptcp/main.go
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user