Skip to content

Commit

Permalink
Merge pull request #1 from orkunkaraduman/develop
Browse files Browse the repository at this point in the history
v0.1.1
  • Loading branch information
orkunkaraduman authored Aug 4, 2023
2 parents d1493d0 + ebf0b45 commit c847ae5
Show file tree
Hide file tree
Showing 28 changed files with 2,879 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git

/vendor

/LOCALNOTES
29 changes: 29 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# syntax=docker.io/docker/dockerfile:1.4.3

FROM golang:1.20.7-alpine3.18 AS builder

WORKDIR /src
COPY . .

RUN mkdir /target

RUN \
--mount=type=cache,target=/go \
--mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 go install -v . && cp -a /go/bin/oscdn /target/

FROM alpine:3.18

RUN apk upgrade --no-cache && apk add --no-cache \
bash openssl ca-certificates tzdata

WORKDIR /app

COPY --from=builder /target/oscdn .
RUN touch oscdn.conf
COPY --from=builder /src/config.yaml .
RUN mkdir /store

ENV OSCDN_CONF=oscdn.conf

ENTRYPOINT [ "./oscdn", "-store-path", "/store" ]
1 change: 1 addition & 0 deletions apps/apps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package apps
14 changes: 14 additions & 0 deletions apps/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//go:build debug
// +build debug

package apps

import "net/http/pprof"

func init() {
mgmtDebugMux.HandleFunc("/debug/pprof/", pprof.Index)
mgmtDebugMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mgmtDebugMux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mgmtDebugMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mgmtDebugMux.HandleFunc("/debug/pprof/trace", pprof.Trace)
}
157 changes: 157 additions & 0 deletions apps/httpapp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package apps

import (
"context"
"crypto/tls"
"io"
"log"
"net"
"net/http"
"sync"
"time"

"github.com/goinsane/logng"
"github.com/goinsane/xcontext"
"github.com/valyala/tcplisten"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"

"github.com/orkunkaraduman/oscdn/cdn"
)

type HttpApp struct {
Logger *logng.Logger
Listen string
ListenBacklog int
HandleH2C bool
TLSConfig *tls.Config
Handler *cdn.Handler

ctx xcontext.CancelableContext
wg sync.WaitGroup

listener net.Listener
httpSrv *http.Server
}

func (a *HttpApp) Start(ctx xcontext.CancelableContext) {
var err error

a.ctx = xcontext.WithCancelable2(context.Background())

logger := a.Logger

if a.ListenBacklog > 0 {
a.listener, err = (&tcplisten.Config{
ReusePort: true,
DeferAccept: false,
FastOpen: true,
Backlog: a.ListenBacklog,
}).NewListener("tcp4", a.Listen)
} else {
a.listener, err = net.Listen("tcp4", a.Listen)
}
if err != nil {
logger.Errorf("listen error: %w", err)
ctx.Cancel()
return
}
logger.Infof("listening %q.", a.Listen)

var httpHandler http.Handler
httpHandler = a.Handler
if a.HandleH2C {
httpHandler = h2c.NewHandler(http.HandlerFunc(a.httpHandler), &http2.Server{
MaxHandlers: 0,
MaxConcurrentStreams: 0,
MaxReadFrameSize: 0,
PermitProhibitedCipherSuites: false,
IdleTimeout: 65 * time.Second,
MaxUploadBufferPerConnection: 0,
MaxUploadBufferPerStream: 0,
NewWriteScheduler: nil,
})
}
a.httpSrv = &http.Server{
Handler: httpHandler,
TLSConfig: a.TLSConfig.Clone(),
ReadHeaderTimeout: 5 * time.Second,
IdleTimeout: 65 * time.Second,
MaxHeaderBytes: 1 << 20,
ErrorLog: log.New(io.Discard, "", log.LstdFlags),
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
var tcpConn *net.TCPConn
switch conn := c.(type) {
case *net.TCPConn:
tcpConn = conn
case *tls.Conn:
tcpConn = conn.NetConn().(*net.TCPConn)
default:
panic("unknown conn type")
}
_ = tcpConn.SetLinger(-1)
_ = tcpConn.SetReadBuffer(128 * 1024)
_ = tcpConn.SetWriteBuffer(128 * 1024)
return ctx
},
}
}

func (a *HttpApp) Run(ctx xcontext.CancelableContext) {
logger := a.Logger

logger.Info("started.")

if a.httpSrv.TLSConfig == nil {
if e := a.httpSrv.Serve(a.listener); e != nil && e != http.ErrServerClosed {
logger.Errorf("http serve error: %w", e)
ctx.Cancel()
return
}
} else {
if e := a.httpSrv.ServeTLS(a.listener, "", ""); e != nil && e != http.ErrServerClosed {
logger.Errorf("https serve error: %w", e)
ctx.Cancel()
return
}
}
}

func (a *HttpApp) Terminate(ctx context.Context) {
logger := a.Logger

if e := a.httpSrv.Shutdown(ctx); e != nil {
logger.Errorf("http server shutdown error: %w", e)
}

logger.Info("terminated.")
}

func (a *HttpApp) Stop() {
logger := a.Logger

a.ctx.Cancel()

if a.listener != nil {
_ = a.listener.Close()
}

a.wg.Wait()
logger.Info("stopped.")
}

func (a *HttpApp) httpHandler(w http.ResponseWriter, req *http.Request) {
defer func() {
if p := recover(); p != nil {
logng.Fatal(p)
}
}()

a.wg.Add(1)
defer a.wg.Done()
if a.ctx.Err() != nil {
return
}

a.Handler.ServeHTTP(w, req)
}
163 changes: 163 additions & 0 deletions apps/mgmtapp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package apps

import (
"context"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"sync"
"time"

"github.com/goinsane/logng"
"github.com/goinsane/xcontext"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/orkunkaraduman/oscdn/store"
)

type MgmtApp struct {
Logger *logng.Logger
Listen string
Store *store.Store

ctx xcontext.CancelableContext
wg sync.WaitGroup

listener net.Listener
httpServeMux *http.ServeMux
httpSrv *http.Server
}

func (a *MgmtApp) Start(ctx xcontext.CancelableContext) {
var err error

a.ctx = xcontext.WithCancelable2(context.Background())

logger := a.Logger

a.listener, err = net.Listen("tcp4", a.Listen)
if err != nil {
logger.Errorf("listen error: %w", err)
ctx.Cancel()
return
}
logger.Infof("listening %q.", a.Listen)

a.httpServeMux = new(http.ServeMux)
a.httpServeMux.Handle("/debug/", mgmtDebugMux)
a.httpServeMux.Handle("/metrics/", promhttp.Handler())
a.httpServeMux.Handle("/cdn/", http.StripPrefix("/cdn", http.HandlerFunc(a.cdnHandler)))

a.httpSrv = &http.Server{
Handler: http.HandlerFunc(a.httpHandler),
TLSConfig: nil,
ReadHeaderTimeout: 5 * time.Second,
IdleTimeout: 65 * time.Second,
MaxHeaderBytes: 1 << 20,
ErrorLog: log.New(io.Discard, "", log.LstdFlags),
}
}

func (a *MgmtApp) Run(ctx xcontext.CancelableContext) {
logger := a.Logger

logger.Info("started.")

if e := a.httpSrv.Serve(a.listener); e != nil && e != http.ErrServerClosed {
logger.Errorf("http serve error: %w", e)
ctx.Cancel()
return
}
}

func (a *MgmtApp) Terminate(ctx context.Context) {
logger := a.Logger

if e := a.httpSrv.Shutdown(ctx); e != nil {
logger.Errorf("http server shutdown error: %w", e)
}

logger.Info("terminated.")
}

func (a *MgmtApp) Stop() {
logger := a.Logger

a.ctx.Cancel()

if a.listener != nil {
_ = a.listener.Close()
}

a.wg.Wait()
logger.Info("stopped.")
}

func (a *MgmtApp) httpHandler(w http.ResponseWriter, req *http.Request) {
defer func() {
if p := recover(); p != nil {
logng.Fatal(p)
}
}()

a.wg.Add(1)
defer a.wg.Done()
if a.ctx.Err() != nil {
return
}

a.httpServeMux.ServeHTTP(w, req)
}

func (a *MgmtApp) cdnHandler(w http.ResponseWriter, req *http.Request) {
var err error

ctx := context.WithValue(a.ctx, "logger", a.Logger)

values, _ := url.ParseQuery(req.URL.RawQuery)

switch {

case req.URL.Path == "/":
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)

case req.URL.Path == "/purge":
if req.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
break
}
err = a.Store.Purge(ctx, values.Get("url"), values.Get("host"))
switch err {
case store.ErrNotExists:
http.Error(w, "content not exists", http.StatusGone)
default:
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
case nil:
_, _ = fmt.Fprintln(w, "content purged")
}

case req.URL.Path == "/purge_host":
if req.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
break
}
err = a.Store.PurgeHost(ctx, values.Get("host"))
switch err {
case store.ErrNotExists:
http.Error(w, "host not exists", http.StatusGone)
default:
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
case nil:
_, _ = fmt.Fprintln(w, "host purged")
}

default:
http.NotFound(w, req)

}
}

var mgmtDebugMux = new(http.ServeMux)
1 change: 1 addition & 0 deletions cdn/cdn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package cdn
14 changes: 14 additions & 0 deletions cdn/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cdn

type HostConfig struct {
Origin struct {
Scheme string
Host string
}
HttpsRedirect bool
HttpsRedirectPort int
DomainOverride bool
IgnoreQuery bool
UploadBurst int64
UploadRate int64
}
Loading

0 comments on commit c847ae5

Please sign in to comment.