summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile18
-rw-r--r--gin.go207
-rw-r--r--stack.go91
3 files changed, 316 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3d80c78
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+# You must use the current google protoc-gen-go
+#
+# cd ~/go/src/google.golang.org/protobuf/cmd/protoc-gen-go
+# go install
+
+all: goimports vet
+
+vet:
+ @GO111MODULE=off go vet
+ @echo this go library package builds okay
+
+# autofixes your import headers in your golang files
+goimports:
+ goimports -w *.go
+
+clean:
+ rm -f *.pb.go
+ -rm -f go.*
diff --git a/gin.go b/gin.go
new file mode 100644
index 0000000..3429817
--- /dev/null
+++ b/gin.go
@@ -0,0 +1,207 @@
+// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
+// Use of this source code is governed by the GPL 3.0
+
+package pinpb
+
+// this is similar to 'gin' but specifically only for
+// sending and working with protocol buffers
+//
+// also, it is as close to possible a golang 'primitive'
+// package (there is no go.sum file)
+
+import (
+ "net/http"
+ "net/url"
+ "sync"
+ "text/template"
+)
+
+// Param is a single URL parameter, consisting of a key and a value.
+type Param struct {
+ Key string
+ Value string
+}
+
+// Params is a Param-slice, as returned by the router.
+// The slice is ordered, the first URL parameter is also the first slice value.
+// It is therefore safe to read values by the index.
+type Params []Param
+
+func (engine *Engine) allocateContext(maxParams uint16) *Context {
+ v := make(Params, 0, maxParams)
+ return &Context{engine: engine, params: &v}
+}
+
+func New() *Engine {
+ engine := &Engine{
+ RouterGroup: RouterGroup{
+ Handlers: nil,
+ basePath: "/",
+ root: true,
+ },
+ FuncMap: template.FuncMap{},
+ RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
+ trustedProxies: []string{"0.0.0.0/0", "::/0"},
+ }
+ engine.RouterGroup.engine = engine
+ engine.pool.New = func() any {
+ return engine.allocateContext(engine.maxParams)
+ // return nil
+ }
+ // return engine.With(opts...)
+ return engine
+}
+
+// With returns a Engine with the configuration set in the OptionFunc.
+func (engine *Engine) With(opts ...OptionFunc) *Engine {
+ for _, opt := range opts {
+ opt(engine)
+ }
+
+ return engine
+}
+
+func Default(opts ...OptionFunc) *Engine {
+ engine := New()
+ // engine.Use(Logger(), Recovery())
+ return engine.With(opts...)
+}
+
+// Context is the most important part of gin. It allows us to pass variables between middleware,
+// manage the flow, validate the JSON of a request and render a JSON response for example.
+type Context struct {
+ engine *Engine
+ params *Params
+ Request *http.Request
+ handlers HandlersChain
+
+ // queryCache caches the query result from c.Request.URL.Query().
+ queryCache url.Values
+
+ // formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
+ // or PUT body parameters.
+ formCache url.Values
+
+ // SameSite allows a server to define a cookie attribute making it impossible for
+ // the browser to send this cookie along with cross-site requests.
+ sameSite http.SameSite
+}
+
+// HandlerFunc defines the handler used by gin middleware as return value.
+type HandlerFunc func(*Context)
+
+// OptionFunc defines the function to change the default configuration
+type OptionFunc func(*Engine)
+
+// HandlersChain defines a HandlerFunc slice.
+type HandlersChain []HandlerFunc
+
+// Last returns the last handler in the chain. i.e. the last handler is the main one.
+func (c HandlersChain) Last() HandlerFunc {
+ if length := len(c); length > 0 {
+ return c[length-1]
+ }
+ return nil
+}
+
+// RouterGroup is used internally to configure router, a RouterGroup is associated with
+// a prefix and an array of handlers (middleware).
+type RouterGroup struct {
+ Handlers HandlersChain
+ basePath string
+ engine *Engine
+ root bool
+}
+
+// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
+// Create an instance of Engine, by using New() or Default()
+type Engine struct {
+ RouterGroup
+
+ // RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
+ // handler for the path with (without) the trailing slash exists.
+ // For example if /foo/ is requested but a route only exists for /foo, the
+ // client is redirected to /foo with http status code 301 for GET requests
+ // and 307 for all other request methods.
+ RedirectTrailingSlash bool
+
+ // RedirectFixedPath if enabled, the router tries to fix the current request path, if no
+ // handle is registered for it.
+ // First superfluous path elements like ../ or // are removed.
+ // Afterwards the router does a case-insensitive lookup of the cleaned path.
+ // If a handle can be found for this route, the router makes a redirection
+ // to the corrected path with status code 301 for GET requests and 307 for
+ // all other request methods.
+ // For example /FOO and /..//Foo could be redirected to /foo.
+ // RedirectTrailingSlash is independent of this option.
+ RedirectFixedPath bool
+
+ // HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the
+ // current route, if the current request can not be routed.
+ // If this is the case, the request is answered with 'Method Not Allowed'
+ // and HTTP status code 405.
+ // If no other Method is allowed, the request is delegated to the NotFound
+ // handler.
+ HandleMethodNotAllowed bool
+
+ // ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that
+ // match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
+ // fetched, it falls back to the IP obtained from
+ // `(*gin.Context).Request.RemoteAddr`.
+ ForwardedByClientIP bool
+
+ // AppEngine was deprecated.
+ // Deprecated: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
+ // #726 #755 If enabled, it will trust some headers starting with
+ // 'X-AppEngine...' for better integration with that PaaS.
+ AppEngine bool
+
+ // UseRawPath if enabled, the url.RawPath will be used to find parameters.
+ UseRawPath bool
+
+ // UnescapePathValues if true, the path value will be unescaped.
+ // If UseRawPath is false (by default), the UnescapePathValues effectively is true,
+ // as url.Path gonna be used, which is already unescaped.
+ UnescapePathValues bool
+
+ // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
+ // See the PR #1817 and issue #1644
+ RemoveExtraSlash bool
+
+ // RemoteIPHeaders list of headers used to obtain the client IP when
+ // `(*gin.Engine).ForwardedByClientIP` is `true` and
+ // `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
+ // network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
+ RemoteIPHeaders []string
+
+ // TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by
+ // that platform, for example to determine the client IP
+ TrustedPlatform string
+
+ // MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
+ // method call.
+ MaxMultipartMemory int64
+
+ // UseH2C enable h2c support.
+ UseH2C bool
+
+ // ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.
+ ContextWithFallback bool
+
+ FuncMap template.FuncMap
+ trustedProxies []string
+ pool sync.Pool
+ maxParams uint16
+ /*
+ delims render.Delims
+ secureJSONPrefix string
+ HTMLRender render.HTMLRender
+ allNoRoute HandlersChain
+ allNoMethod HandlersChain
+ noRoute HandlersChain
+ noMethod HandlersChain
+ trees methodTrees
+ maxSections uint16
+ trustedCIDRs []*net.IPNet
+ */
+}
diff --git a/stack.go b/stack.go
new file mode 100644
index 0000000..d4fed60
--- /dev/null
+++ b/stack.go
@@ -0,0 +1,91 @@
+// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
+// Use of this source code is governed by the GPL 3.0
+
+package pinpb
+
+// this is similar to 'gin' but specifically only for
+// sending and working with protocol buffers
+//
+// also, it is as close to possible a golang 'primitive'
+// package (there is no go.sum file)
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "runtime"
+ "time"
+)
+
+var (
+ dunno = []byte("???")
+ centerDot = []byte("·")
+ dot = []byte(".")
+ slash = []byte("/")
+)
+
+// stack returns a nicely formatted stack frame, skipping skip frames.
+func Stack(skip int) []byte {
+ buf := new(bytes.Buffer) // the returned data
+ // As we loop, we open files and read them. These variables record the currently
+ // loaded file.
+ var lines [][]byte
+ var lastFile string
+ for i := skip; ; i++ { // Skip the expected number of frames
+ pc, file, line, ok := runtime.Caller(i)
+ if !ok {
+ break
+ }
+ // Print this much at least. If we can't find the source, it won't show.
+ fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
+ if file != lastFile {
+ data, err := os.ReadFile(file)
+ if err != nil {
+ continue
+ }
+ lines = bytes.Split(data, []byte{'\n'})
+ lastFile = file
+ }
+ fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
+ }
+ return buf.Bytes()
+}
+
+// source returns a space-trimmed slice of the n'th line.
+func source(lines [][]byte, n int) []byte {
+ n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
+ if n < 0 || n >= len(lines) {
+ return dunno
+ }
+ return bytes.TrimSpace(lines[n])
+}
+
+// function returns, if possible, the name of the function containing the PC.
+func function(pc uintptr) []byte {
+ fn := runtime.FuncForPC(pc)
+ if fn == nil {
+ return dunno
+ }
+ name := []byte(fn.Name())
+ // The name includes the path name to the package, which is unnecessary
+ // since the file name is already included. Plus, it has center dots.
+ // That is, we see
+ // runtime/debug.*T·ptrmethod
+ // and want
+ // *T.ptrmethod
+ // Also the package path might contain dot (e.g. code.google.com/...),
+ // so first eliminate the path prefix
+ if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 {
+ name = name[lastSlash+1:]
+ }
+ if period := bytes.Index(name, dot); period >= 0 {
+ name = name[period+1:]
+ }
+ name = bytes.ReplaceAll(name, centerDot, dot)
+ return name
+}
+
+// timeFormat returns a customized time string for logger.
+func timeFormat(t time.Time) string {
+ return t.Format("2006/01/02 - 15:04:05")
+}