Skip to content

Commit

Permalink
exporting methods
Browse files Browse the repository at this point in the history
  • Loading branch information
nidorx committed Sep 13, 2024
1 parent e3e257e commit b35b002
Show file tree
Hide file tree
Showing 22 changed files with 696 additions and 595 deletions.
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@
To create distributed systems in a simple, elegant and safe way.
</p>
</div>

[//]: # (<a href="https://github.com/syntax-framework/syntax"><img width="160" src="./docs/logo-syntax.png" /></a>)

[//]: # ()
[//]: # (**chain** is part of the [Syntax Framework]&#40;https://github.com/syntax-framework/syntax&#41;)

---

Chain is a core library that tries to provide all the necessary machinery to create distributed systems in a simple,
Expand Down
124 changes: 68 additions & 56 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package chain
import (
"context"
"net/http"
"strings"
)

type chainContextKey struct{}
Expand Down Expand Up @@ -32,19 +31,17 @@ type Context struct {
data map[any]any
handler Handle
router *Router
MatchedRoutePath string
Route *RouteInfo
Writer http.ResponseWriter
Request *http.Request
Crypto *cryptoImpl
root *Context
parent *Context
index int
children []*Context
}

// Set define um valor compartilhado no contexto de execução da requisição
func (ctx *Context) Set(key any, value any) {
if ctx.root != nil {
ctx.root.Set(key, value)
}
if ctx.data == nil {
ctx.data = make(map[any]any)
}
Expand All @@ -53,46 +50,86 @@ func (ctx *Context) Set(key any, value any) {

// Get obtém um valor compartilhado no contexto de execução da requisição
func (ctx *Context) Get(key any) (any, bool) {
if ctx.root != nil {
return ctx.root.Get(key)
if ctx.data != nil {
value, exists := ctx.data[key]
if exists {
return value, exists
}
}

if ctx.data == nil {
return nil, false
if ctx.parent != nil {
return ctx.parent.Get(key)
}
value, exists := ctx.data[key]
return value, exists
return nil, false
}

func (ctx *Context) WithParams(names []string, values []string) *Context {
func (ctx *Context) Destroy() {
if ctx.parent == nil {
// root context, will be removed automaticaly
return
}
if ctx.parent.children != nil {
ctx.parent.children[ctx.index] = nil
}
ctx.parent = nil
ctx.children = nil

if ctx.router != nil {
ctx.router.poolPutContext(ctx)
}
}

func (ctx *Context) Child() *Context {
var child *Context
if ctx.router != nil {
child = ctx.router.GetContext(ctx.Request, ctx.Writer, "")
child = ctx.router.poolGetContext(ctx.Request, ctx.Writer, "")
} else {
child = &Context{
Writer: ctx.Writer,
Request: ctx.Request,
handler: ctx.handler,
paramCount: len(names),
paramNames: ctx.paramNames,
paramValues: ctx.paramValues,
path: ctx.path,
Crypto: crypt,
Writer: ctx.Writer,
Request: ctx.Request,
handler: ctx.handler,
}
}
for i := 0; i < len(names); i++ {
child.paramNames[i] = names[i]
child.paramValues[i] = values[i]

child.paramCount = ctx.paramCount
child.paramNames = ctx.paramNames
child.paramValues = ctx.paramValues
child.pathSegments = ctx.pathSegments
child.pathSegmentsCount = ctx.pathSegmentsCount
child.Route = ctx.Route

child.parent = ctx

if ctx.children == nil {
ctx.children = make([]*Context, 0)
}
child.index = len(ctx.children)
ctx.children = append(ctx.children, child)

if ctx.root == nil {
child.root = ctx
} else {
child.root = ctx.root
return child
}

// func (ctx *Context) With(key any, value any) *Context {

// }

func (ctx *Context) WithParams(names []string, values []string) *Context {
child := ctx.Child()
child.paramCount = len(names)
child.paramNames = [32]string{}
child.paramValues = [32]string{}

for i, name := range ctx.paramNames {
child.paramNames[i] = name
child.paramValues[i] = ctx.paramValues[i]
}

if child.root.children == nil {
child.root.children = make([]*Context, 0)
for i := 0; i < len(names); i++ {
child.paramNames[i] = names[i]
child.paramValues[i] = values[i]
}
child.root.children = append(child.root.children, child)

return child
}
Expand Down Expand Up @@ -145,30 +182,5 @@ func (ctx *Context) addParameter(name string, value string) {
}

func (ctx *Context) parsePathSegments() {
var (
segmentStart = 0
segmentSize int
path = ctx.path
)
if len(path) > 0 {
path = path[1:]
}

ctx.pathSegments[0] = 0
ctx.pathSegmentsCount = 1

for {
segmentSize = strings.IndexByte(path, separator)
if segmentSize == -1 {
segmentSize = len(path)
}
ctx.pathSegments[ctx.pathSegmentsCount] = segmentStart + 1 + segmentSize

if segmentSize == len(path) {
break
}
ctx.pathSegmentsCount++
path = path[segmentSize+1:]
segmentStart = segmentStart + 1 + segmentSize
}
ctx.pathSegmentsCount = parsePathSegments(ctx.path, &ctx.pathSegments)
}
4 changes: 4 additions & 0 deletions context_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ func (ctx *Context) Write(data []byte) (int, error) {
return ctx.Writer.Write(data)
}

func (ctx *Context) GetStatus() int {
return ctx.Writer.(*ResponseWriterSpy).Status()
}

// Header returns the header map that will be sent by
// WriteHeader. The Header map also is the mechanism with which
// Handlers can set HTTP trailers.
Expand Down
Binary file removed docs/logo-syntax.png
Binary file not shown.
4 changes: 2 additions & 2 deletions middlewares/session/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
var globalManagers = map[*chain.Router]*Manager{}

var (
sessionKey = "syntax.chain.session." // Session on chain.Context
managerKey = "syntax.chain.session-manager." // Manager on chain.Context
sessionKey = "chain.session." // Session on chain.Context
managerKey = "chain.session-manager." // Manager on chain.Context
ErrCannotFetch = errors.New("cannot fetch session, check if there is a session.Manager configured")
)

Expand Down
9 changes: 1 addition & 8 deletions middlewares/session/store_cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@
// router := chain.New()
// router.Use(session.Manager{
// Store: session.Cookie{
// Key: "_my_app_session",
// Log: "debug"
// Key: "_my_app_session"
// }
// })

package session

import (
"log/slog"
"strings"

"github.com/nidorx/chain"
"github.com/nidorx/chain/crypto"
Expand All @@ -39,7 +37,6 @@ var (
// https://edgeapi.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html
// https://funcptr.net/2013/08/25/user-sessions,-what-data-should-be-stored-where-/
type Cookie struct {
Log string // Log level to use when the cookie cannot be decoded. Defaults to `debug`, can be set to false to disable it.
Serializer chain.Serializer // cookie serializer module that defines `Encode(any)` and `Decode(any)`. Defaults to `json`.
SigningKeyring *crypto.Keyring // a crypto.Keyring used with for signing/verifying a cookie.
EncryptionKeyring *crypto.Keyring // a crypto.Keyring used for encrypting/decrypting a cookie.
Expand All @@ -54,10 +51,6 @@ func (c *Cookie) Init(config Config, router *chain.Router) (err error) {
c.SigningKeyring = defaultSigningKeyring
}

if strings.TrimSpace(c.Log) == "" {
c.Log = "debug"
}

if c.Serializer == nil {
c.Serializer = defaultSerializer
}
Expand Down
8 changes: 4 additions & 4 deletions pubsub/pubsub.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var (
selfId = ksuid.New()
selfIdBytes = selfId.Bytes() // 20 bytes
selfIdString = selfId.String()
directTopic = "stx:direct:" + selfIdString
directTopic = "direct:" + selfIdString
ErrNoAdapter = errors.New("no adapter matches topic to broadcast the message")
)

Expand Down Expand Up @@ -129,7 +129,7 @@ func Broadcast(topic string, message []byte, options ...*Option) (err error) {
var compressed []byte
if compressed, err = compressPayload(msgToSend); err != nil {
slog.Warn(
"[chain] Failed to compress payload",
"[chain.pubsub] failed to compress payload",
slog.Any("error", err),
)
} else if len(compressed) < len(msgToSend) {
Expand Down Expand Up @@ -178,7 +178,7 @@ func DirectBroadcast(nodeId string, topic string, message []byte, options ...*Op
buf.WriteString(topic)
buf.Write(message)

return broadcastMessage(messageTypeDirectBroadcast, "stx:direct:"+nodeId, buf.Bytes(), options...)
return broadcastMessage(messageTypeDirectBroadcast, "direct:"+nodeId, buf.Bytes(), options...)
}

// Broadcast broadcasts message on given topic across the whole cluster.
Expand Down Expand Up @@ -214,7 +214,7 @@ func broadcastMessage(msgType messageType, topic string, message []byte, options
var compressed []byte
if compressed, err = compressPayload(msgToSend); err != nil {
slog.Warn(
"[chain] Failed to compress payload",
"[chain.pubsub] failed to compress payload",
slog.Any("error", err),
)
} else if len(compressed) < len(msgToSend) {
Expand Down
16 changes: 8 additions & 8 deletions registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ func (r *Registry) addHandle(path string, handle Handle) {
r.routes = []*Route{}
}

details := ParsePathDetails(path)
details := ParseRouteInfo(path)

// avoid conflicts
for _, route := range r.routes {
if details.conflictsWith(route.Path) {
slog.Error("[chain] wildcard conflicts", slog.String("new", details.path), slog.String("existing", route.Path.path))
if details.conflictsWith(route.Info) {
slog.Error("[chain] wildcard conflicts", slog.String("new", details.path), slog.String("existing", route.Info.path))
panic("[chain] wildcard conflicts")
}
}
Expand All @@ -77,17 +77,17 @@ func (r *Registry) addHandle(path string, handle Handle) {
r.storage.add(r.createRoute(handle, details))
}

func (r *Registry) createRoute(handle Handle, pathDetails *PathDetails) *Route {
func (r *Registry) createRoute(handle Handle, info *RouteInfo) *Route {
route := &Route{
Handle: handle,
Path: pathDetails,
Info: info,
middlewaresAdded: map[*Middleware]bool{},
}

r.routes = append(r.routes, route)

for _, middleware := range r.middlewares {
if route.middlewaresAdded[middleware] != true && middleware.Path.MaybeMatches(route.Path) {
if route.middlewaresAdded[middleware] != true && middleware.Path.Matches(route.Info) {
route.middlewaresAdded[middleware] = true
route.Middlewares = append(route.Middlewares, middleware)
}
Expand All @@ -103,15 +103,15 @@ func (r *Registry) addMiddleware(path string, middlewares []func(ctx *Context, n

for _, middleware := range middlewares {
info := &Middleware{
Path: ParsePathDetails(path),
Path: ParseRouteInfo(path),
Handle: middleware,
}

r.middlewares = append(r.middlewares, info)

// add this MiddlewareFunc to all compatible routes
for _, route := range r.routes {
if route.middlewaresAdded[info] != true && info.Path.MaybeMatches(route.Path) {
if route.middlewaresAdded[info] != true && info.Path.Matches(route.Info) {
route.middlewaresAdded[info] = true
route.Middlewares = append(route.Middlewares, info)
}
Expand Down
9 changes: 9 additions & 0 deletions response_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,29 @@ var ErrAlreadySent = errors.New("the response was already sent")

type ResponseWriterSpy struct {
http.ResponseWriter
status int // status code passed to WriteHeader
writeStarted bool
writeCalled bool
writeHeaderCalled bool
beforeWriteHeaderHooks []func()
afterWriteHooks []func()
}

func (w *ResponseWriterSpy) Status() int {
return w.status
}

func (w *ResponseWriterSpy) WriteHeader(status int) {
w.status = status
w.writeHeaderCalled = true
w.execBeforeWriteHeaderHooks()
w.ResponseWriter.WriteHeader(status)
}

func (w *ResponseWriterSpy) Write(b []byte) (int, error) {
if w.status == 0 {
w.status = http.StatusOK
}
w.writeCalled = true
if !w.writeStarted {
w.execBeforeWriteHeaderHooks()
Expand Down
Loading

0 comments on commit b35b002

Please sign in to comment.