Updated README, added code for logging handler and its record

remotes/origin/HEAD
Darshil Chanpura 5 years ago
parent 53f569e88e
commit bf3a40ffb7

@ -0,0 +1,18 @@
# dumb-http
## Simple HTTP Server
## Usage
```
dumb-http [-path path-to-serve] [port]
```
### Example
```
$ dumb-http -path ./docs
Serving at http://0.0.0.0:8000/ from ./docs
127.0.0.1 - - [05/Jul/2018 18:08:16] "GET / HTTP/1.1" 200 38 0.0000 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:61.0) Gecko/20100101 Firefox/61.0"
```
>Inspired by Python module http.server
> Reference: https://gist.github.com/cespare/3985516

@ -0,0 +1,61 @@
package main
import (
"io"
"net/http"
"strings"
"time"
)
// LoggingHandler is to be used as wrapper of mux.
type LoggingHandler struct {
handler http.Handler
out io.Writer
}
// NewLoggingHandler for creating a new Logging Handler
func NewLoggingHandler(handler http.Handler, out io.Writer) http.Handler {
return &LoggingHandler{
handler: handler,
out: out,
}
}
func (h LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ipPort := strings.Split(r.RemoteAddr, ":")
logRecord := &LogRecord{
ResponseWriter: w,
time: time.Time{},
method: r.Method,
statusCode: http.StatusOK,
protocol: r.Proto,
path: r.RequestURI,
clientIP: strings.Join(ipPort[:len(ipPort)-1], ":"),
referer: "-",
userAgent: "-",
totalTime: time.Duration(0),
}
// For logging Real IP Address
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
logRecord.clientIP = realIP
}
// For logging Referer URL
if referer := r.Header.Get("Referer"); referer != "" {
logRecord.referer = referer
}
// For logging User Agent
if userAgent := r.Header.Get("User-Agent"); userAgent != "" {
logRecord.userAgent = userAgent
}
startTime := time.Now()
h.handler.ServeHTTP(logRecord, r)
endTime := time.Now()
logRecord.time = endTime.UTC()
logRecord.totalTime = endTime.Sub(startTime) / 1000.0
logRecord.Log(h.out)
}

@ -0,0 +1,52 @@
package main
import (
"fmt"
"io"
"net/http"
"time"
)
const (
// LogPattern is format for writing the logRecord
LogPattern = "%s - - [%s] \"%s\" %d %d %.4f \"%s\" \"%s\"\n"
// DateTimeFormat is for logging Date and Time of request
DateTimeFormat = "02/Jan/2006 15:04:05" // Minimal
// DateTimeFormat = "Mon, 02 Jan 2006 15:04:05 MST" // Full
)
// LogRecord struct extends http.ResponseWriter interface which has different methods included in it.
type LogRecord struct {
http.ResponseWriter
method string
path string
clientIP string
time time.Time
contentLength int64
protocol string
statusCode int
referer string
userAgent string
totalTime time.Duration
}
// Log method to be called for logging to "out"
func (l *LogRecord) Log(out io.Writer) {
timeFormatted := l.time.Format(DateTimeFormat)
requestLine := l.method + " " + l.path + " " + l.protocol
fmt.Fprintf(out, LogPattern, l.clientIP, timeFormatted, requestLine,
l.statusCode, l.contentLength, l.totalTime.Seconds(), l.referer, l.userAgent)
}
// WriteHeader method has been extended to record status code from previous handler.
func (l *LogRecord) WriteHeader(statusCode int) {
l.statusCode = statusCode
l.ResponseWriter.WriteHeader(statusCode)
}
// Write method has been extended to record the bytes written or the content length
func (l *LogRecord) Write(p []byte) (int, error) {
written, err := l.ResponseWriter.Write(p)
l.contentLength += int64(written)
return written, err
}

@ -0,0 +1,44 @@
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
)
var (
localDir = "."
host = "0.0.0.0"
port = "8000"
)
func init() {
flag.StringVar(&localDir, "path", ".", "Path to serve from")
}
func main() {
flag.Parse()
// Check for port in commandline
if commandPort := flag.Arg(0); commandPort != "" {
port = commandPort
}
// Start serving...
serve(host + ":" + port)
}
func serve(addr string) {
mux := http.NewServeMux()
mux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(localDir))))
server := http.Server{
Addr: addr,
Handler: NewLoggingHandler(mux, os.Stdout),
}
fmt.Printf("Serving at http://%s/ from %s\n", addr, localDir)
err := server.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}
Loading…
Cancel
Save