Basic server
This commit is contained in:
commit
b740f57488
210
main.go
Normal file
210
main.go
Normal file
@ -0,0 +1,210 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HTTPMethod string
|
||||
|
||||
const (
|
||||
HTTPMETHOD_GET HTTPMethod = "GET"
|
||||
HTTPMETHOD_POST HTTPMethod = "POST"
|
||||
)
|
||||
|
||||
/*
|
||||
A request-line begins with a method token, followed by a single space
|
||||
|
||||
(SP), the request-target, another single space (SP), the protocol
|
||||
version, and ends with CRLF.
|
||||
|
||||
request-line = method SP request-target SP HTTP-version CRLF
|
||||
HTTP-version = HTTP-name "/" DIGIT "." DIGIT
|
||||
*/
|
||||
type RequestLine struct {
|
||||
Method HTTPMethod
|
||||
RequestTarget string
|
||||
HTTPVersion string
|
||||
}
|
||||
|
||||
/*
|
||||
The first line of a response message is the status-line, consisting
|
||||
of the protocol version, a space (SP), the status code, another
|
||||
space, a possibly empty textual phrase describing the status code,
|
||||
and ending with CRLF.
|
||||
|
||||
status-line = HTTP-version SP status-code SP reason-phrase CRLF
|
||||
*/
|
||||
type StatusLine struct {
|
||||
}
|
||||
|
||||
/*
|
||||
All HTTP/1.1 messages consist of a start-line followed by a sequence
|
||||
of octets in a format similar to the Internet Message Format
|
||||
[RFC5322]: zero or more header fields (collectively referred to as
|
||||
the "headers" or the "header section"), an empty line indicating the
|
||||
end of the header section, and an optional message body.
|
||||
|
||||
HTTP-message = start-line
|
||||
*( header-field CRLF )
|
||||
CRLF
|
||||
[ message-body ]
|
||||
*/
|
||||
type HTTPRequest struct {
|
||||
StartLine RequestLine
|
||||
}
|
||||
|
||||
type HTTPResponse struct {
|
||||
StartLine StatusLine
|
||||
}
|
||||
|
||||
func printDiff(s1, s2 string) {
|
||||
length := len(s1)
|
||||
if len(s2) > length {
|
||||
length = len(s2)
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
var char1, char2 byte
|
||||
|
||||
if i < len(s1) {
|
||||
char1 = s1[i]
|
||||
} else {
|
||||
char1 = ' ' // padding for shorter string
|
||||
}
|
||||
|
||||
if i < len(s2) {
|
||||
char2 = s2[i]
|
||||
} else {
|
||||
char2 = ' ' // padding for shorter string
|
||||
}
|
||||
|
||||
if char1 != char2 {
|
||||
fmt.Printf("Difference at index %d: '%c' != '%c'\n", i, char1, char2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ParseHTTPRequest(b []byte) (HTTPRequest, error) {
|
||||
ret := HTTPRequest{}
|
||||
rs := string(b[:])
|
||||
|
||||
method, rs, found := strings.Cut(rs, " ")
|
||||
if !found {
|
||||
return ret, errors.New("could not find method in request")
|
||||
}
|
||||
|
||||
method = strings.ToUpper(strings.TrimSpace(method))
|
||||
ret.StartLine = RequestLine{}
|
||||
switch method {
|
||||
case string(HTTPMETHOD_GET):
|
||||
ret.StartLine.Method = HTTPMETHOD_GET
|
||||
case string(HTTPMETHOD_POST):
|
||||
ret.StartLine.Method = HTTPMETHOD_POST
|
||||
default:
|
||||
if method != "GET" {
|
||||
result1 := strings.Compare("GET", method)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(len("GET"), len(method))
|
||||
}
|
||||
|
||||
return ret, fmt.Errorf("unsupported method '%s'", method)
|
||||
}
|
||||
|
||||
rt, rs, found := strings.Cut(rs, " ")
|
||||
if !found {
|
||||
return ret, errors.New("could not find target in request")
|
||||
}
|
||||
ret.StartLine.RequestTarget = rt
|
||||
|
||||
hv, rs, found := strings.Cut(rs, "\r\n")
|
||||
if !found {
|
||||
return ret, errors.New("could not find http version in request")
|
||||
}
|
||||
if hv != "HTTP/1.0" && hv != "HTTP/1.1" {
|
||||
return ret, fmt.Errorf("unsupported http version %s", hv)
|
||||
}
|
||||
ret.StartLine.HTTPVersion = hv
|
||||
fmt.Println("rm: ", rs)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
logger := log.New(os.Stdout, "DWSHTTP", log.LstdFlags)
|
||||
|
||||
l, err := net.Listen("tcp", ":4221")
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
logger.Print("Started server on 4221")
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
|
||||
logger.Printf("New connection: %s", conn.RemoteAddr().String())
|
||||
go func(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
bSize := 0
|
||||
rSize := 256
|
||||
buf := make([]byte, bSize)
|
||||
tmp := make([]byte, rSize)
|
||||
err := c.SetReadDeadline(time.Now().Add(time.Second * 5))
|
||||
if err != nil {
|
||||
logger.Printf("ERROR %s - (%s) %s", c.RemoteAddr().String(), "connsetup", err)
|
||||
}
|
||||
|
||||
for {
|
||||
n, err := c.Read(tmp)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
logger.Fatalf("ERROR %s - (%s) %s", c.RemoteAddr().String(), "unexpected error while reading", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
buf = append(buf, tmp[:n]...)
|
||||
if n > 0 {
|
||||
if n < rSize {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
req, err := ParseHTTPRequest(buf)
|
||||
if err != nil {
|
||||
logger.Printf("ERROR %s - (%s) %s", c.RemoteAddr().String(), "parsing", err)
|
||||
bw, err := c.Write([]byte("HTTP/1.1 500 Internal Server Error\r\n\r\n"))
|
||||
if err != nil {
|
||||
logger.Printf("ERROR %s - (Could not write err) %s", c.RemoteAddr().String(), err)
|
||||
return
|
||||
}
|
||||
if bw < 1 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
logger.Printf("%s - %s - %s -> 200", req.StartLine.Method, req.StartLine.HTTPVersion, req.StartLine.RequestTarget)
|
||||
bw, err := c.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
|
||||
if err != nil {
|
||||
logger.Printf("ERROR %s - (Could not write err) %s", c.RemoteAddr().String(), err)
|
||||
return
|
||||
}
|
||||
if bw < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
}(conn)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user