add beehttp module
Showing
3 changed files
with
405 additions
and
0 deletions
beehttp/context.go
0 → 100644
| 1 | package beehttp | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "net/http" | ||
| 5 | ) | ||
| 6 | |||
| 7 | type Context struct { | ||
| 8 | Input *BeegoInput | ||
| 9 | Output *BeegoOutput | ||
| 10 | Request *http.Request | ||
| 11 | ResponseWriter http.ResponseWriter | ||
| 12 | } | ||
| 13 | |||
| 14 | func (ctx *Context) Redirect(status int, localurl string) { | ||
| 15 | ctx.Output.Header("Location", localurl) | ||
| 16 | ctx.Output.SetStatus(status) | ||
| 17 | } | ||
| 18 | |||
| 19 | func (ctx *Context) WriteString(content string) { | ||
| 20 | ctx.Output.Body([]byte(content)) | ||
| 21 | } | ||
| 22 | |||
| 23 | func (ctx *Context) GetCookie(key string) string { | ||
| 24 | return ctx.Input.Cookie(key) | ||
| 25 | } | ||
| 26 | |||
| 27 | func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { | ||
| 28 | ctx.Output.Cookie(name, value, others...) | ||
| 29 | } |
beehttp/input.go
0 → 100644
| 1 | package beehttp | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bytes" | ||
| 5 | "github.com/astaxie/beego/session" | ||
| 6 | "io/ioutil" | ||
| 7 | "net/http" | ||
| 8 | "strconv" | ||
| 9 | "strings" | ||
| 10 | ) | ||
| 11 | |||
| 12 | type BeegoInput struct { | ||
| 13 | CruSession session.SessionStore | ||
| 14 | Param map[string]string | ||
| 15 | req *http.Request | ||
| 16 | RequestBody []byte | ||
| 17 | } | ||
| 18 | |||
| 19 | func NewInput(req *http.Request) *BeegoInput { | ||
| 20 | return &BeegoInput{ | ||
| 21 | Param: make(map[string]string), | ||
| 22 | req: req, | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | func (input *BeegoInput) Protocol() string { | ||
| 27 | return input.req.Proto | ||
| 28 | } | ||
| 29 | |||
| 30 | func (input *BeegoInput) Uri() string { | ||
| 31 | return input.req.RequestURI | ||
| 32 | } | ||
| 33 | |||
| 34 | func (input *BeegoInput) Url() string { | ||
| 35 | return input.req.URL.String() | ||
| 36 | } | ||
| 37 | |||
| 38 | func (input *BeegoInput) Site() string { | ||
| 39 | return input.Scheme() + "://" + input.Domain() | ||
| 40 | } | ||
| 41 | |||
| 42 | func (input *BeegoInput) Scheme() string { | ||
| 43 | return input.req.URL.Scheme | ||
| 44 | } | ||
| 45 | |||
| 46 | func (input *BeegoInput) Domain() string { | ||
| 47 | return input.Host() | ||
| 48 | } | ||
| 49 | |||
| 50 | func (input *BeegoInput) Host() string { | ||
| 51 | if input.req.Host != "" { | ||
| 52 | hostParts := strings.Split(input.req.Host, ":") | ||
| 53 | if len(hostParts) > 0 { | ||
| 54 | return hostParts[0] | ||
| 55 | } | ||
| 56 | return input.req.Host | ||
| 57 | } | ||
| 58 | return "localhost" | ||
| 59 | } | ||
| 60 | |||
| 61 | func (input *BeegoInput) Method() string { | ||
| 62 | return input.req.Method | ||
| 63 | } | ||
| 64 | |||
| 65 | func (input *BeegoInput) Is(method string) bool { | ||
| 66 | return input.Method() == method | ||
| 67 | } | ||
| 68 | |||
| 69 | func (input *BeegoInput) IsAjax() bool { | ||
| 70 | return input.Header("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest" | ||
| 71 | } | ||
| 72 | |||
| 73 | func (input *BeegoInput) IsSecure() bool { | ||
| 74 | return input.Scheme() == "https" | ||
| 75 | } | ||
| 76 | |||
| 77 | func (input *BeegoInput) IsUpload() bool { | ||
| 78 | return input.req.MultipartForm != nil | ||
| 79 | } | ||
| 80 | |||
| 81 | func (input *BeegoInput) IP() string { | ||
| 82 | ips := input.Proxy() | ||
| 83 | if len(ips) > 0 && ips[0] != "" { | ||
| 84 | return ips[0] | ||
| 85 | } | ||
| 86 | ip := strings.Split(input.req.RemoteAddr, ":") | ||
| 87 | if len(ip) > 0 { | ||
| 88 | return ip[0] | ||
| 89 | } | ||
| 90 | return "127.0.0.1" | ||
| 91 | } | ||
| 92 | |||
| 93 | func (input *BeegoInput) Proxy() []string { | ||
| 94 | if ips := input.Header("HTTP_X_FORWARDED_FOR"); ips != "" { | ||
| 95 | return strings.Split(ips, ",") | ||
| 96 | } | ||
| 97 | return []string{} | ||
| 98 | } | ||
| 99 | |||
| 100 | func (input *BeegoInput) Refer() string { | ||
| 101 | return input.Header("HTTP_REFERER") | ||
| 102 | } | ||
| 103 | |||
| 104 | func (input *BeegoInput) SubDomains() string { | ||
| 105 | parts := strings.Split(input.Host(), ".") | ||
| 106 | return strings.Join(parts[len(parts)-2:], ".") | ||
| 107 | } | ||
| 108 | |||
| 109 | func (input *BeegoInput) Port() int { | ||
| 110 | parts := strings.Split(input.req.Host, ":") | ||
| 111 | if len(parts) == 2 { | ||
| 112 | port, _ := strconv.Atoi(parts[1]) | ||
| 113 | return port | ||
| 114 | } | ||
| 115 | return 80 | ||
| 116 | } | ||
| 117 | |||
| 118 | func (input *BeegoInput) UserAgent() string { | ||
| 119 | return input.Header("HTTP_USER_AGENT") | ||
| 120 | } | ||
| 121 | |||
| 122 | func (input *BeegoInput) Params(key string) string { | ||
| 123 | if v, ok := input.Param[key]; ok { | ||
| 124 | return v | ||
| 125 | } | ||
| 126 | return "" | ||
| 127 | } | ||
| 128 | |||
| 129 | func (input *BeegoInput) Query(key string) string { | ||
| 130 | return input.req.Form.Get(key) | ||
| 131 | } | ||
| 132 | |||
| 133 | func (input *BeegoInput) Header(key string) string { | ||
| 134 | return input.req.Header.Get(key) | ||
| 135 | } | ||
| 136 | |||
| 137 | func (input *BeegoInput) Cookie(key string) string { | ||
| 138 | ck, err := input.req.Cookie(key) | ||
| 139 | if err != nil { | ||
| 140 | return "" | ||
| 141 | } | ||
| 142 | return ck.Value | ||
| 143 | } | ||
| 144 | |||
| 145 | func (input *BeegoInput) Session(key interface{}) interface{} { | ||
| 146 | return input.CruSession.Get(key) | ||
| 147 | } | ||
| 148 | |||
| 149 | func (input *BeegoInput) Body() []byte { | ||
| 150 | requestbody, _ := ioutil.ReadAll(input.req.Body) | ||
| 151 | input.req.Body.Close() | ||
| 152 | bf := bytes.NewBuffer(requestbody) | ||
| 153 | input.req.Body = ioutil.NopCloser(bf) | ||
| 154 | return requestbody | ||
| 155 | } |
beehttp/output.go
0 → 100644
| 1 | package beehttp | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bytes" | ||
| 5 | "compress/flate" | ||
| 6 | "compress/gzip" | ||
| 7 | "encoding/json" | ||
| 8 | "encoding/xml" | ||
| 9 | "errors" | ||
| 10 | "fmt" | ||
| 11 | "io" | ||
| 12 | "mime" | ||
| 13 | "net/http" | ||
| 14 | "path/filepath" | ||
| 15 | "strconv" | ||
| 16 | "strings" | ||
| 17 | ) | ||
| 18 | |||
| 19 | type BeegoOutput struct { | ||
| 20 | context *Context | ||
| 21 | Status int | ||
| 22 | EnableGzip bool | ||
| 23 | res http.ResponseWriter | ||
| 24 | } | ||
| 25 | |||
| 26 | func NewOutput(res http.ResponseWriter) *BeegoOutput { | ||
| 27 | return &BeegoOutput{ | ||
| 28 | res: res, | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | func (output *BeegoOutput) Header(key, val string) { | ||
| 33 | output.res.Header().Set(key, val) | ||
| 34 | } | ||
| 35 | |||
| 36 | func (output *BeegoOutput) Body(content []byte) { | ||
| 37 | output_writer := output.res.(io.Writer) | ||
| 38 | if output.EnableGzip == true && output.context.Input.Header("Accept-Encoding") != "" { | ||
| 39 | splitted := strings.SplitN(output.context.Input.Header("Accept-Encoding"), ",", -1) | ||
| 40 | encodings := make([]string, len(splitted)) | ||
| 41 | |||
| 42 | for i, val := range splitted { | ||
| 43 | encodings[i] = strings.TrimSpace(val) | ||
| 44 | } | ||
| 45 | for _, val := range encodings { | ||
| 46 | if val == "gzip" { | ||
| 47 | output.Header("Content-Encoding", "gzip") | ||
| 48 | output_writer, _ = gzip.NewWriterLevel(output.res, gzip.BestSpeed) | ||
| 49 | |||
| 50 | break | ||
| 51 | } else if val == "deflate" { | ||
| 52 | output.Header("Content-Encoding", "deflate") | ||
| 53 | output_writer, _ = flate.NewWriter(output.res, flate.BestSpeed) | ||
| 54 | break | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } else { | ||
| 58 | output.Header("Content-Length", strconv.Itoa(len(content))) | ||
| 59 | } | ||
| 60 | output_writer.Write(content) | ||
| 61 | switch output_writer.(type) { | ||
| 62 | case *gzip.Writer: | ||
| 63 | output_writer.(*gzip.Writer).Close() | ||
| 64 | case *flate.Writer: | ||
| 65 | output_writer.(*flate.Writer).Close() | ||
| 66 | case io.WriteCloser: | ||
| 67 | output_writer.(io.WriteCloser).Close() | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) { | ||
| 72 | var b bytes.Buffer | ||
| 73 | fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value)) | ||
| 74 | if len(others) > 0 { | ||
| 75 | switch others[0].(type) { | ||
| 76 | case int: | ||
| 77 | if others[0].(int) > 0 { | ||
| 78 | fmt.Fprintf(&b, "; Max-Age=%d", others[0].(int)) | ||
| 79 | } else if others[0].(int) < 0 { | ||
| 80 | fmt.Fprintf(&b, "; Max-Age=0") | ||
| 81 | } | ||
| 82 | case int64: | ||
| 83 | if others[0].(int64) > 0 { | ||
| 84 | fmt.Fprintf(&b, "; Max-Age=%d", others[0].(int64)) | ||
| 85 | } else if others[0].(int64) < 0 { | ||
| 86 | fmt.Fprintf(&b, "; Max-Age=0") | ||
| 87 | } | ||
| 88 | case int32: | ||
| 89 | if others[0].(int32) > 0 { | ||
| 90 | fmt.Fprintf(&b, "; Max-Age=%d", others[0].(int32)) | ||
| 91 | } else if others[0].(int32) < 0 { | ||
| 92 | fmt.Fprintf(&b, "; Max-Age=0") | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | if len(others) > 1 { | ||
| 97 | fmt.Fprintf(&b, "; Path=%s", sanitizeValue(others[1].(string))) | ||
| 98 | } | ||
| 99 | if len(others) > 2 { | ||
| 100 | fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(others[2].(string))) | ||
| 101 | } | ||
| 102 | if len(others) > 3 { | ||
| 103 | fmt.Fprintf(&b, "; Secure") | ||
| 104 | } | ||
| 105 | if len(others) > 4 { | ||
| 106 | fmt.Fprintf(&b, "; HttpOnly") | ||
| 107 | } | ||
| 108 | output.res.Header().Add("Set-Cookie", b.String()) | ||
| 109 | } | ||
| 110 | |||
| 111 | var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") | ||
| 112 | |||
| 113 | func sanitizeName(n string) string { | ||
| 114 | return cookieNameSanitizer.Replace(n) | ||
| 115 | } | ||
| 116 | |||
| 117 | var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ") | ||
| 118 | |||
| 119 | func sanitizeValue(v string) string { | ||
| 120 | return cookieValueSanitizer.Replace(v) | ||
| 121 | } | ||
| 122 | |||
| 123 | func (output *BeegoOutput) Json(data string) error { | ||
| 124 | output.Header("Content-Type", "application/json;charset=UTF-8") | ||
| 125 | content, err := json.Marshal(data) | ||
| 126 | if err != nil { | ||
| 127 | return err | ||
| 128 | } | ||
| 129 | output.Body(content) | ||
| 130 | return nil | ||
| 131 | } | ||
| 132 | |||
| 133 | func (output *BeegoOutput) Jsonp(data string) error { | ||
| 134 | output.Header("Content-Type", "application/javascript;charset=UTF-8") | ||
| 135 | content, err := json.Marshal(data) | ||
| 136 | if err != nil { | ||
| 137 | return err | ||
| 138 | } | ||
| 139 | callback := output.context.Input.Query("callback") | ||
| 140 | if callback == "" { | ||
| 141 | return errors.New(`"callback" parameter required`) | ||
| 142 | } | ||
| 143 | callback_content := bytes.NewBufferString(callback) | ||
| 144 | callback_content.WriteString("(") | ||
| 145 | callback_content.Write(content) | ||
| 146 | callback_content.WriteString(");\r\n") | ||
| 147 | output.Body(callback_content.Bytes()) | ||
| 148 | return nil | ||
| 149 | } | ||
| 150 | |||
| 151 | func (output *BeegoOutput) Xml(data string) error { | ||
| 152 | output.Header("Content-Type", "application/xml;charset=UTF-8") | ||
| 153 | content, err := xml.Marshal(data) | ||
| 154 | if err != nil { | ||
| 155 | return err | ||
| 156 | } | ||
| 157 | output.Body(content) | ||
| 158 | return nil | ||
| 159 | } | ||
| 160 | |||
| 161 | func (output *BeegoOutput) Download(file string) { | ||
| 162 | output.Header("Content-Description", "File Transfer") | ||
| 163 | output.Header("Content-Type", "application/octet-stream") | ||
| 164 | output.Header("Content-Disposition", "attachment; filename="+filepath.Base(file)) | ||
| 165 | output.Header("Content-Transfer-Encoding", "binary") | ||
| 166 | output.Header("Expires", "0") | ||
| 167 | output.Header("Cache-Control", "must-revalidate") | ||
| 168 | output.Header("Pragma", "public") | ||
| 169 | http.ServeFile(output.res, output.context.Request, file) | ||
| 170 | } | ||
| 171 | |||
| 172 | func (output *BeegoOutput) ContentType(ext string) { | ||
| 173 | if !strings.HasPrefix(ext, ".") { | ||
| 174 | ext = "." + ext | ||
| 175 | } | ||
| 176 | ctype := mime.TypeByExtension(ext) | ||
| 177 | if ctype != "" { | ||
| 178 | output.Header("Content-Type", ctype) | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | func (output *BeegoOutput) SetStatus(status int) { | ||
| 183 | output.res.WriteHeader(status) | ||
| 184 | output.Status = status | ||
| 185 | } | ||
| 186 | |||
| 187 | func (output *BeegoOutput) IsCachable(status int) bool { | ||
| 188 | return output.Status >= 200 && output.Status < 300 || output.Status == 304 | ||
| 189 | } | ||
| 190 | |||
| 191 | func (output *BeegoOutput) IsEmpty(status int) bool { | ||
| 192 | return output.Status == 201 || output.Status == 204 || output.Status == 304 | ||
| 193 | } | ||
| 194 | |||
| 195 | func (output *BeegoOutput) IsOk(status int) bool { | ||
| 196 | return output.Status == 200 | ||
| 197 | } | ||
| 198 | |||
| 199 | func (output *BeegoOutput) IsSuccessful(status int) bool { | ||
| 200 | return output.Status >= 200 && output.Status < 300 | ||
| 201 | } | ||
| 202 | |||
| 203 | func (output *BeegoOutput) IsRedirect(status int) bool { | ||
| 204 | return output.Status == 301 || output.Status == 302 || output.Status == 303 || output.Status == 307 | ||
| 205 | } | ||
| 206 | |||
| 207 | func (output *BeegoOutput) IsForbidden(status int) bool { | ||
| 208 | return output.Status == 403 | ||
| 209 | } | ||
| 210 | |||
| 211 | func (output *BeegoOutput) IsNotFound(status int) bool { | ||
| 212 | return output.Status == 404 | ||
| 213 | } | ||
| 214 | |||
| 215 | func (output *BeegoOutput) IsClientError(status int) bool { | ||
| 216 | return output.Status >= 400 && output.Status < 500 | ||
| 217 | } | ||
| 218 | |||
| 219 | func (output *BeegoOutput) IsServerError(status int) bool { | ||
| 220 | return output.Status >= 500 && output.Status < 600 | ||
| 221 | } |
-
Please register or sign in to post a comment