Skip to content

Commit ba603aa

Browse files
committed
initial import
0 parents  commit ba603aa

File tree

22 files changed

+712
-0
lines changed

22 files changed

+712
-0
lines changed

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
trim_trailing_whitespace = true
7+
8+
[*.{conf,yml}]
9+
indent_style = space
10+
indent_size = 4
11+
insert_final_newline = true

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/*-proxy/logs/*
2+
!/*-proxy/logs/.gitkeep

Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM debian:stable-slim
2+
3+
ARG DEBIAN_FRONTEND=noninteractive
4+
5+
RUN apt update && \
6+
apt upgrade -y && \
7+
apt install -y --no-install-recommends --no-install-suggests ca-certificates dumb-init nginx-full libnginx-mod-http-lua && \
8+
rm -rf /var/lib/apt/lists/*
9+
10+
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
11+
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# An nginx lab in Docker
2+
3+
This repository contains a virtual "lab" that can be used to experiment with Nginx; it sets up two reverse proxies
4+
(which can simulate having Cloudflare in front of your server) and a simple backend application.
5+
6+
## Requirements
7+
8+
- Docker
9+
- docker-compose
10+
11+
## Usage
12+
13+
Run `docker-compose up` to spin up the whole stack, then your main (outer) frontend can be accessed on `localhost:8080`
14+
and your secondary (inner) frontend on `localhost:9090`; the backend application is bound to the `/app` location on both
15+
proxies.

backend/Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# First stage
2+
FROM golang:1.18 as builder
3+
4+
WORKDIR /app
5+
COPY go.mod go.sum ./
6+
RUN go mod download
7+
8+
COPY . ./
9+
RUN go build -v -o backend
10+
11+
# Second stage
12+
FROM debian:stable-slim
13+
14+
ARG DEBIAN_FRONTEND=noninteractive
15+
16+
RUN apt update && \
17+
apt upgrade -y && \
18+
apt install -y --no-install-recommends ca-certificates && \
19+
rm -rf /var/lib/apt/lists/*
20+
21+
COPY --from=builder /app/backend /app/
22+
CMD ["/app/backend"]

backend/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/piger/nginx-lab
2+
3+
go 1.17

backend/go.sum

Whitespace-only changes.

backend/main.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// This program is a simple web application used to simulate a backend service exposed through nginx.
2+
package main
3+
4+
import (
5+
"flag"
6+
"log"
7+
8+
"github.com/piger/nginx-lab/server"
9+
)
10+
11+
var (
12+
listenAddress = flag.String("addr", "0.0.0.0", "Address to bind to")
13+
listenPort = flag.Int("port", 4444, "Port to listen on")
14+
)
15+
16+
func run() error {
17+
flag.Parse()
18+
19+
srv := server.New(*listenAddress, *listenPort)
20+
if err := srv.Run(); err != nil {
21+
return err
22+
}
23+
24+
return nil
25+
}
26+
27+
func main() {
28+
if err := run(); err != nil {
29+
log.Fatalf("error: %s", err)
30+
}
31+
}

backend/server/routes.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package server
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"strings"
9+
)
10+
11+
// Response describes the payload sent back to a client querying this webapp.
12+
type Response struct {
13+
Method string `json:"method"`
14+
URL string `json:"url"`
15+
Proto string `json:"proto"`
16+
Headers map[string]string `json:"headers"`
17+
Host string `json:"host"`
18+
RequestURI string `json:"request_uri"`
19+
}
20+
21+
func (s *Server) baseHandler(w http.ResponseWriter, r *http.Request) {
22+
log.Printf("new request: %+v\n", r)
23+
24+
headers := make(map[string]string)
25+
for k, v := range r.Header {
26+
headers[k] = strings.Join(v, ", ")
27+
}
28+
29+
response := Response{
30+
Method: r.Method,
31+
URL: r.URL.String(),
32+
Proto: r.Proto,
33+
Headers: headers,
34+
Host: r.Host,
35+
RequestURI: r.RequestURI,
36+
}
37+
38+
w.Header().Set("Content-Type", "application/json")
39+
40+
if err := json.NewEncoder(w).Encode(response); err != nil {
41+
w.WriteHeader(http.StatusInternalServerError)
42+
fmt.Fprintf(w, "ERROR: %s\n", err)
43+
return
44+
}
45+
}

backend/server/server.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"log"
8+
"net/http"
9+
"os"
10+
"os/signal"
11+
"syscall"
12+
"time"
13+
)
14+
15+
type Server struct {
16+
Address string
17+
Port int
18+
}
19+
20+
func New(address string, port int) *Server {
21+
s := &Server{
22+
Address: address,
23+
Port: port,
24+
}
25+
return s
26+
}
27+
28+
func (s *Server) Run() error {
29+
ctx := context.Background()
30+
31+
mux := http.NewServeMux()
32+
mux.HandleFunc("/", s.baseHandler)
33+
34+
hs := &http.Server{
35+
Addr: fmt.Sprintf("%s:%d", s.Address, s.Port),
36+
Handler: mux,
37+
ReadTimeout: 10 * time.Second,
38+
WriteTimeout: 10 * time.Second,
39+
MaxHeaderBytes: 1 << 20,
40+
}
41+
42+
sigc := make(chan os.Signal, 1)
43+
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
44+
done := make(chan struct{}, 1)
45+
46+
go func() {
47+
<-sigc
48+
log.Print("signal received, shutting down server")
49+
50+
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
51+
defer cancel()
52+
53+
if err := hs.Shutdown(ctx); err != nil {
54+
log.Printf("error shutting down HTTP server: %s", err)
55+
}
56+
done <- struct{}{}
57+
}()
58+
59+
log.Printf("Starting server on %s:%d", s.Address, s.Port)
60+
if err := hs.ListenAndServe(); err != nil {
61+
if !errors.Is(err, http.ErrServerClosed) {
62+
log.Printf("HTTP server error: %s", err)
63+
}
64+
}
65+
66+
<-done
67+
68+
return nil
69+
}

0 commit comments

Comments
 (0)