2024-05-19 20:48:06 +03:00
|
|
|
package ss
|
2024-01-10 20:11:02 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
2024-02-29 23:19:07 +03:00
|
|
|
"strings"
|
2024-01-10 20:11:02 +03:00
|
|
|
"encoding/json"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"fmt"
|
2024-05-19 20:48:06 +03:00
|
|
|
"surdeus.su/core/ss/contents"
|
|
|
|
"surdeus.su/core/ss/urlenc"
|
2024-01-10 20:11:02 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type Context struct {
|
|
|
|
R *Request
|
2024-01-10 21:19:23 +03:00
|
|
|
RelUrl *url.URL
|
2024-01-10 20:11:02 +03:00
|
|
|
W ResponseWriter
|
|
|
|
// Custom data to store stuff.
|
|
|
|
Data any
|
|
|
|
|
|
|
|
scanErr error
|
|
|
|
dec Decoder
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) Method() ReqMethod {
|
|
|
|
return ReqMethod(c.R.Method)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the reply status code.
|
|
|
|
func (c *Context) SetStatus(status Status) {
|
2024-01-10 22:42:50 +03:00
|
|
|
c.W.WriteHeader(int(status))
|
2024-01-10 20:11:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the reply content type.
|
2024-02-29 23:19:07 +03:00
|
|
|
func (c *Context) SetContentType(typ contents.Type, opts ...contents.Option) {
|
|
|
|
ret := string(typ)
|
|
|
|
for _, opt := range opts {
|
|
|
|
kv := opt.KeyValue()
|
|
|
|
ret += ";"+kv[0]+"="+kv[1]
|
|
|
|
}
|
|
|
|
c.SetHeader("Content-Type", ret)
|
2024-01-10 20:11:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the request content type.
|
2024-02-29 23:19:07 +03:00
|
|
|
func (c *Context) ContentType() contents.Type {
|
2024-01-10 20:11:02 +03:00
|
|
|
ret, ok := c.Header("Content-Type")
|
|
|
|
if !ok {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
if len(ret) < 1 {
|
|
|
|
return ""
|
|
|
|
}
|
2024-02-29 23:19:07 +03:00
|
|
|
splits := strings.SplitN(ret[0], ";", 2)
|
|
|
|
if len(splits) < 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return contents.Type(splits[0])
|
2024-01-10 20:11:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) SetHeader(k, v string) {
|
|
|
|
c.W.Header().Set(k, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) Header(name string) ([]string, bool) {
|
|
|
|
ret, ok := c.R.Header[name]
|
|
|
|
return ret, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// Closes the requests body after finishes scaning.
|
|
|
|
func (c *Context) Close() {
|
|
|
|
c.R.Body.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scan the incoming value from body depending
|
2024-01-14 16:25:28 +03:00
|
|
|
// on the content type of the request into the
|
|
|
|
// structure or map[string] any.
|
2024-01-10 20:11:02 +03:00
|
|
|
func (c *Context) Scan(v any) bool {
|
|
|
|
if c.dec == nil {
|
|
|
|
typ := c.ContentType()
|
|
|
|
switch typ {
|
|
|
|
case contents.Json:
|
|
|
|
c.dec = json.NewDecoder(c.R.Body)
|
2024-01-14 16:25:28 +03:00
|
|
|
case contents.UrlEncoded:
|
|
|
|
body, _ := io.ReadAll(c.R.Body)
|
|
|
|
err := urlenc.Unmarshal(body, v)
|
|
|
|
if err != nil {
|
|
|
|
c.scanErr = err
|
|
|
|
}
|
|
|
|
return false
|
2024-01-10 20:11:02 +03:00
|
|
|
default:
|
|
|
|
c.scanErr = UnknownContentTypeErr
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2024-03-13 20:58:02 +03:00
|
|
|
|
2024-01-10 20:11:02 +03:00
|
|
|
err := c.dec.Decode(v)
|
|
|
|
if err != nil {
|
|
|
|
if err != io.EOF {
|
|
|
|
c.scanErr = err
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) ScanErr() error {
|
|
|
|
return c.scanErr
|
|
|
|
}
|
|
|
|
|
2024-01-10 21:19:23 +03:00
|
|
|
func (c *Context) AbsPath() string {
|
2024-01-10 20:11:02 +03:00
|
|
|
return c.R.URL.Path
|
|
|
|
}
|
|
|
|
|
2024-01-10 21:19:23 +03:00
|
|
|
func (c *Context) PathPrefix() string {
|
|
|
|
pth := c.AbsPath()
|
|
|
|
rpth := c.Path()
|
|
|
|
return pth[:len(pth)-len(rpth)]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) Path() string {
|
|
|
|
return c.RelUrl.Path
|
|
|
|
}
|
|
|
|
|
2024-01-10 20:11:02 +03:00
|
|
|
func (c *Context) NotFound() {
|
|
|
|
http.NotFound(c.W, c.R)
|
|
|
|
}
|
|
|
|
|
2024-02-29 23:19:07 +03:00
|
|
|
func (c *Context) InternalServerError(err error) {
|
|
|
|
c.W.WriteHeader(http.StatusInternalServerError)
|
|
|
|
if err != nil {
|
|
|
|
c.Printf("500 Internal Server Error: %q", err)
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) Print(v ...any) (int, error) {
|
|
|
|
return fmt.Print(v...)
|
|
|
|
}
|
|
|
|
|
2024-01-10 20:11:02 +03:00
|
|
|
func (c *Context) Printf(format string, v ...any) (int, error) {
|
|
|
|
return fmt.Fprintf(c.W, format, v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) Query() url.Values {
|
|
|
|
return c.R.URL.Query()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) Redirect(u string, status Status) {
|
2024-01-10 21:19:23 +03:00
|
|
|
pth := c.AbsPath()
|
|
|
|
if len(pth) > 0 && pth[len(pth)-1] != '/' {
|
|
|
|
pth += "/"
|
|
|
|
}
|
|
|
|
c.R.URL.Path = pth
|
2024-01-10 20:11:02 +03:00
|
|
|
http.Redirect(c.W, c.R, u, int(status))
|
|
|
|
}
|