Skip to content

Instantly share code, notes, and snippets.

@birowo
Created May 16, 2020 04:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save birowo/d94e2a8b58eeb854ebfe56d12f241a7e to your computer and use it in GitHub Desktop.
Save birowo/d94e2a8b58eeb854ebfe56d12f241a7e to your computer and use it in GitHub Desktop.
desc.
// Copyright 2019 Andy Pan. All rights reserved.
// Copyright 2017 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"fmt"
"log"
"strconv"
"strings"
"time"
"unsafe"
"github.com/panjf2000/gnet"
)
var res string
type request struct {
proto, method string
path, query string
head, body string
remoteAddr string
}
var errMsg = "Internal Server Error"
var errMsgBytes = []byte(errMsg)
type httpServer struct {
*gnet.EventServer
}
func (hs *httpServer) OnInitComplete(srv gnet.Server) (action gnet.Action) {
fmt.Println("OnInitComplete event")
log.Printf("HTTP server is listening on %s (multi-cores: %t, loops: %d)\n",
srv.Addr.String(), srv.Multicore, srv.NumEventLoop)
return
}
type Ctx struct {
req *request
err error
}
func (hs *httpServer) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) {
fmt.Println("OnOpened event")
c.SetContext(&Ctx{new(request), nil}) //request ditaruh di context (connection scope)
return
}
func (hs *httpServer) React(frame []byte, c gnet.Conn) (out []byte, action gnet.Action) {
fmt.Println("React event")
ctx := c.Context().(*Ctx)
if ctx.err != nil {
// bad thing happened
out = errMsgBytes
action = gnet.Close
return
}
// handle the request
out = frame
return
}
func (hs *httpServer) OnClosed(c gnet.Conn, err error) (action gnet.Action) {
fmt.Println("OnClosed event")
return
}
type httpCodec struct {
// req request //request tidak tepat kalau ditaruh di sini(main scope)
}
func (hc *httpCodec) Encode(c gnet.Conn, buf []byte) (out []byte, err error) {
fmt.Println("Encode")
ctx := c.Context().(*Ctx)
if ctx.err == nil {
return buf, nil
}
return appendResp(out, "500 Error", "", errMsg+"\n"), nil
}
func (hc *httpCodec) Decode(c gnet.Conn) (out []byte, err error) {
fmt.Println("Decode")
buf := c.Read()
c.ResetBuffer()
ctx := c.Context().(*Ctx)
// process the pipeline
var leftover []byte
pipeline:
leftover, err = parseReq(buf, ctx.req)
// bad thing happened
if err != nil {
ctx.err = err
return nil, err
} else if len(leftover) == len(buf) {
// request not ready, yet
return
}
//percobaan:
time.Sleep(9 * time.Second)
switch ctx.req.path {
case "/abc":
res = "abc"
case "/def":
res = "def"
}
/*
delay 9 detik untuk test apakah suatu koneksi memblok koneksi lainnya
akses http://localhost/abc (setelah 9 detik akan tampil abc) kemudian sebelum 9 detik,
akses(di tab / browser lain) http://localhost/def (setelah 9 detik akan tampil def)
terbukti di percobaan ini, bahwa suatu koneksi tidak memblok koneksi lainnya
*/
out = appendHandle(out, res)
buf = leftover
goto pipeline
}
func main() {
var port int
var multicore bool
// Example command: go run http.go --port 8080 --multicore=true
flag.IntVar(&port, "port", 8080, "server port")
flag.BoolVar(&multicore, "multicore", true, "multicore")
flag.Parse()
res = "Hello World!\r\n"
http := new(httpServer)
hc := new(httpCodec)
// Start serving!
log.Fatal(gnet.Serve(http, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore), gnet.WithCodec(hc)))
}
// appendHandle handles the incoming request and appends the response to
// the provided bytes, which is then returned to the caller.
func appendHandle(b []byte, res string) []byte {
return appendResp(b, "200 OK", "", res)
}
// appendResp will append a valid http response to the provide bytes.
// The status param should be the code plus text such as "200 OK".
// The head parameter should be a series of lines ending with "\r\n" or empty.
func appendResp(b []byte, status, head, body string) []byte {
b = append(b, "HTTP/1.1"...)
b = append(b, ' ')
b = append(b, status...)
b = append(b, '\r', '\n')
// b = append(b, "Server: gnet\r\n"...)
b = append(b, "Date: "...)
b = time.Now().AppendFormat(b, "Mon, 02 Jan 2006 15:04:05 GMT")
b = append(b, '\r', '\n')
if len(body) > 0 {
b = append(b, "Content-Length: "...)
b = strconv.AppendInt(b, int64(len(body)), 10)
b = append(b, '\r', '\n')
}
b = append(b, head...)
b = append(b, '\r', '\n')
if len(body) > 0 {
b = append(b, body...)
}
return b
}
func b2s(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// parseReq is a very simple http request parser. This operation
// waits for the entire payload to be buffered before returning a
// valid request.
func parseReq(data []byte, req *request) (leftover []byte, err error) {
sdata := b2s(data)
var i, s int
var head string
var clen int
var q = -1
// method, path, proto line
for ; i < len(sdata); i++ {
if sdata[i] == ' ' {
req.method = sdata[s:i]
for i, s = i+1, i+1; i < len(sdata); i++ {
if sdata[i] == '?' && q == -1 {
q = i - s
} else if sdata[i] == ' ' {
if q != -1 {
req.path = sdata[s:q]
req.query = req.path[q+1 : i]
} else {
req.path = sdata[s:i]
}
for i, s = i+1, i+1; i < len(sdata); i++ {
if sdata[i] == '\n' && sdata[i-1] == '\r' {
req.proto = sdata[s:i]
i, s = i+1, i+1
break
}
}
break
}
}
break
}
}
if req.proto == "" {
return data, fmt.Errorf("malformed request")
}
head = sdata[:s]
for ; i < len(sdata); i++ {
if i > 1 && sdata[i] == '\n' && sdata[i-1] == '\r' {
line := sdata[s : i-1]
s = i + 1
if line == "" {
req.head = sdata[len(head)+2 : i+1]
i++
if clen > 0 {
if len(sdata[i:]) < clen {
break
}
req.body = sdata[i : i+clen]
i += clen
}
return data[i:], nil
}
if strings.HasPrefix(line, "Content-Length:") {
n, err := strconv.ParseInt(strings.TrimSpace(line[len("Content-Length:"):]), 10, 64)
if err == nil {
clen = int(n)
}
}
}
}
// not enough data
return data, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment