优化文件结构

This commit is contained in:
boyce
2020-08-01 17:57:34 +08:00
parent c140eb3fa1
commit 396b7e5929
6 changed files with 10 additions and 10 deletions

View File

@@ -0,0 +1,545 @@
package httpservice
import (
"fmt"
"github.com/duanhf2012/origin/event"
"github.com/duanhf2012/origin/network"
"github.com/duanhf2012/origin/service"
"github.com/duanhf2012/origin/util/uuid"
jsoniter "github.com/json-iterator/go"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
var Default_ReadTimeout time.Duration = time.Second*10
var Default_WriteTimeout time.Duration = time.Second*10
var Default_ProcessTimeout time.Duration = time.Second*10
var Default_HttpRouter *HttpRouter= &HttpRouter{}
//http redirect
type HttpRedirectData struct {
Url string
CookieList []*http.Cookie
}
type HTTP_METHOD int
const (
METHOD_NONE HTTP_METHOD = iota
METHOD_GET
METHOD_POST
METHOD_INVALID
)
type HttpHandle func(session *HttpSession)
type routerMatchData struct {
matchURL string
httpHandle HttpHandle
}
type routerServeFileData struct {
matchUrl string
localPath string
method HTTP_METHOD
}
type IHttpRouter interface {
//RegRouter(method HTTP_METHOD, url string, handle HttpHandle) bool
GET(url string, handle HttpHandle) bool
POST(url string, handle HttpHandle) bool
Router(session *HttpSession)
SetServeFile(method HTTP_METHOD, urlpath string, dirname string) error
SetFormFileKey(formFileKey string)
GetFormFileKey()string
AddHttpFiltrate(FiltrateFun HttpFiltrate) bool
}
type HttpRouter struct {
pathRouter map[HTTP_METHOD] map[string] routerMatchData //url地址对应本service地址
serveFileData map[string] *routerServeFileData
//eventReciver event.IEventHandler
httpFiltrateList [] HttpFiltrate
formFileKey string
}
type HttpSession struct {
httpRouter IHttpRouter
r *http.Request
w http.ResponseWriter
//parse result
mapParam map[string]string
body []byte
//processor result
statusCode int
msg []byte
fileData *routerServeFileData
redirectData *HttpRedirectData
sessionDone chan *HttpSession
}
type HttpService struct {
service.Service
httpServer network.HttpServer
postAliasUrl map[HTTP_METHOD] map[string]routerMatchData //url地址对应本service地址
httpRouter IHttpRouter
listenAddr string
corsHeader *CORSHeader
processTimeout time.Duration
}
func (slf *HttpService) AddFiltrate(FiltrateFun HttpFiltrate) bool {
return slf.httpRouter.AddHttpFiltrate(FiltrateFun)
}
func NewHttpHttpRouter() IHttpRouter {
httpRouter := &HttpRouter{}
//httpRouter.eventReciver = eventHandler
httpRouter.pathRouter =map[HTTP_METHOD] map[string] routerMatchData{}
httpRouter.serveFileData = map[string] *routerServeFileData{}
httpRouter.formFileKey = "file"
for i:=METHOD_NONE+1;i<METHOD_INVALID;i++{
httpRouter.pathRouter[i] = map[string] routerMatchData{}
}
return httpRouter
}
func (slf *HttpSession) Query(key string) (string, bool) {
if slf.mapParam == nil {
slf.mapParam = make(map[string]string)
paramStr := strings.Trim(slf.r.URL.RawQuery, "/")
paramStrList := strings.Split(paramStr, "&")
for _, val := range paramStrList {
index := strings.Index(val, "=")
if index >= 0 {
slf.mapParam[val[0:index]] = val[index+1:]
}
}
}
ret, ok := slf.mapParam[key]
return ret, ok
}
func (slf *HttpSession) GetBody() []byte{
return slf.body
}
func (slf *HttpSession) GetMethod() HTTP_METHOD {
return slf.getMethod(slf.r.Method)
}
func (slf *HttpSession) GetPath() string{
return strings.Trim(slf.r.URL.Path,"/")
}
func (slf *HttpSession) SetHeader(key, value string) {
slf.w.Header().Set(key,value)
}
func (slf *HttpSession) AddHeader(key, value string) {
slf.w.Header().Add(key,value)
}
func (slf *HttpSession) GetHeader(key string) string{
return slf.r.Header.Get(key)
}
func (slf *HttpSession) DelHeader(key string) {
slf.r.Header.Del(key)
}
func (slf *HttpSession) WriteStatusCode(statusCode int){
slf.statusCode = statusCode
}
func (slf *HttpSession) Write(msg []byte) {
slf.msg = msg
}
func (slf *HttpSession) WriteJsonDone(statusCode int,msgJson interface{}) error {
msg, err := json.Marshal(msgJson)
if err == nil {
slf.Write(msg)
}
slf.Done()
return err
}
func (slf *HttpSession) flush() {
slf.w.WriteHeader(slf.statusCode)
if slf.msg!=nil {
slf.w.Write(slf.msg)
}
}
func (slf *HttpSession) Done(){
slf.sessionDone <- slf
}
func (slf *HttpSession) getMethod(method string) HTTP_METHOD {
switch method {
case "POST":
return METHOD_POST
case "GET":
return METHOD_GET
}
return METHOD_INVALID
}
func (slf *HttpRouter) analysisRouterUrl(url string) (string, error) {
//替换所有空格
url = strings.ReplaceAll(url, " ", "")
if len(url) <= 1 || url[0] != '/' {
return "", fmt.Errorf("url %s format is error!", url)
}
//去掉尾部的/
return strings.Trim(url, "/"), nil
}
func (slf *HttpSession) Handle(){
slf.httpRouter.Router(slf)
}
func (slf *HttpRouter) SetFormFileKey(formFileKey string){
slf.formFileKey = formFileKey
}
func (slf *HttpRouter) GetFormFileKey()string{
return slf.formFileKey
}
func (slf *HttpRouter) GET(url string, handle HttpHandle) bool {
return slf.regRouter(METHOD_GET, url, handle)
}
func (slf *HttpRouter) POST(url string, handle HttpHandle) bool {
return slf.regRouter(METHOD_POST, url, handle)
}
func (slf *HttpRouter) regRouter(method HTTP_METHOD, url string, handle HttpHandle) bool{
mapRouter,ok := slf.pathRouter[method]
if ok == false{
return false
}
mapRouter[strings.Trim(url,"/")] = routerMatchData{httpHandle:handle}
return true
}
func (slf *HttpRouter) Router(session *HttpSession){
if slf.httpFiltrateList!=nil {
for _,fun := range slf.httpFiltrateList{
if fun(session) == false {
//session.done()
return
}
}
}
urlPath := session.GetPath()
for {
mapRouter, ok := slf.pathRouter[session.GetMethod()]
if ok == false {
break
}
v, ok := mapRouter[urlPath]
if ok == false {
break
}
v.httpHandle(session)
//session.done()
return
}
for k, v := range slf.serveFileData {
idx := strings.Index(urlPath, k)
if idx != -1 {
session.fileData = v
session.Done()
return
}
}
session.WriteStatusCode(http.StatusNotFound)
session.Done()
}
func (slf *HttpService) HttpEventHandler(ev *event.Event) {
ev.Data.(*HttpSession).Handle()
}
func (slf *HttpService) SetHttpRouter(httpRouter IHttpRouter,eventHandler event.IEventHandler) {
slf.httpRouter = httpRouter
slf.RegEventReciverFunc(event.Sys_Event_Http_Event,eventHandler,slf.HttpEventHandler)
}
func (slf *HttpRouter) SetServeFile(method HTTP_METHOD, urlpath string, dirname string) error {
_, err := os.Stat(dirname)
if err != nil {
return err
}
matchURL, aErr := slf.analysisRouterUrl(urlpath)
if aErr != nil {
return aErr
}
var routerData routerServeFileData
routerData.method = method
routerData.localPath = dirname
routerData.matchUrl = matchURL
slf.serveFileData[matchURL] = &routerData
return nil
}
type HttpFiltrate func(session *HttpSession) bool //true is pass
func (slf *HttpRouter) AddHttpFiltrate(FiltrateFun HttpFiltrate) bool {
slf.httpFiltrateList = append(slf.httpFiltrateList, FiltrateFun)
return false
}
func (slf *HttpSession) Redirect(url string, cookieList []*http.Cookie) {
redirectData := &HttpRedirectData{}
redirectData.Url = url
redirectData.CookieList = cookieList
slf.redirectData = redirectData
}
func (slf *HttpSession) redirects() {
if slf.redirectData == nil {
return
}
if slf.redirectData.CookieList != nil {
for _, v := range slf.redirectData.CookieList {
http.SetCookie(slf.w, v)
}
}
http.Redirect(slf.w, slf.r, slf.redirectData.Url,
http.StatusTemporaryRedirect)
}
func (slf *HttpService) OnInit() error {
iConfig := slf.GetServiceCfg()
if iConfig == nil {
return fmt.Errorf("%s service config is error!",slf.GetName())
}
tcpCfg := iConfig.(map[string]interface{})
addr,ok := tcpCfg["ListenAddr"]
if ok == false {
return fmt.Errorf("%s service config is error!",slf.GetName())
}
var readTimeout time.Duration = Default_ReadTimeout
var writeTimeout time.Duration = Default_WriteTimeout
if cfgRead,ok := tcpCfg["ReadTimeout"];ok == true {
readTimeout = time.Duration(cfgRead.(float64))*time.Millisecond
}
if cfgWrite,ok := tcpCfg["WriteTimeout"];ok == true {
writeTimeout = time.Duration(cfgWrite.(float64))*time.Millisecond
}
slf.processTimeout = Default_ProcessTimeout
if cfgProcessTimeout,ok := tcpCfg["ProcessTimeout"];ok == true {
slf.processTimeout = time.Duration(cfgProcessTimeout.(float64))*time.Millisecond
}
slf.httpServer.Init(addr.(string), slf, readTimeout, writeTimeout)
//Set CAFile
caFileList,ok := tcpCfg["CAFile"]
if ok == false {
return nil
}
iCaList := caFileList.([]interface{})
var caFile [] network.CAFile
for _,i := range iCaList {
mapCAFile := i.(map[string]interface{})
c,ok := mapCAFile["Certfile"]
if ok == false{
continue
}
k,ok := mapCAFile["Certfile"]
if ok == false{
continue
}
if c.(string)!="" && k.(string)!="" {
caFile = append(caFile,network.CAFile{
Certfile: c.(string),
Keyfile: k.(string),
})
}
}
slf.httpServer.SetCAFile(caFile)
slf.httpServer.Start()
return nil
}
func (slf *HttpService) SetAllowCORS(corsHeader *CORSHeader) {
slf.corsHeader = corsHeader
}
func (slf *HttpService) ProcessFile(session *HttpSession){
upath := session.r.URL.Path
idx := strings.Index(upath, session.fileData.matchUrl)
subPath := strings.Trim(upath[idx+len(session.fileData.matchUrl):], "/")
destLocalPath := session.fileData.localPath + "/"+subPath
switch session.GetMethod() {
case METHOD_GET:
//判断文件夹是否存在
_, err := os.Stat(destLocalPath)
if err == nil {
http.ServeFile(session.w, session.r, destLocalPath)
} else {
session.WriteStatusCode(http.StatusNotFound)
session.flush()
return
}
//上传资源
case METHOD_POST:
// 在这儿处理例外路由接口
session.r.ParseMultipartForm(32 << 20) // max memory is set to 32MB
resourceFile, resourceFileHeader, err := session.r.FormFile(session.httpRouter.GetFormFileKey())
if err != nil {
session.WriteStatusCode(http.StatusNotFound)
session.flush()
return
}
defer resourceFile.Close()
//重新拼接文件名
imgFormat := strings.Split(resourceFileHeader.Filename, ".")
if len(imgFormat) < 2 {
session.WriteStatusCode(http.StatusNotFound)
session.flush()
return
}
filePrefixName := uuid.Rand().HexEx()
fileName := filePrefixName + "." + imgFormat[len(imgFormat)-1]
//创建文件
localpath := fmt.Sprintf("%s%s", destLocalPath, fileName)
localfd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
session.WriteStatusCode(http.StatusNotFound)
session.flush()
return
}
defer localfd.Close()
io.Copy(localfd, resourceFile)
session.WriteStatusCode(http.StatusOK)
session.Write([]byte(upath+"/"+fileName))
session.flush()
}
}
type CORSHeader struct {
AllowCORSHeader map[string][]string
}
func NewAllowCORSHeader() *CORSHeader{
header := &CORSHeader{}
header.AllowCORSHeader = map[string][]string{}
header.AllowCORSHeader["Access-Control-Allow-Origin"] = []string{"*"}
header.AllowCORSHeader["Access-Control-Allow-Methods"] =[]string{ "POST, GET, OPTIONS, PUT, DELETE"}
header.AllowCORSHeader["Access-Control-Allow-Headers"] = []string{"Content-Type"}
return header
}
func (slf *CORSHeader) AddAllowHeader(key string,val string){
slf.AllowCORSHeader["Access-Control-Allow-Headers"] = append(slf.AllowCORSHeader["Access-Control-Allow-Headers"],fmt.Sprintf("%s,%s",key,val))
}
func (slf *CORSHeader) copyTo(header http.Header){
for k,v := range slf.AllowCORSHeader{
for _,val := range v{
header.Add(k,val)
}
}
}
func (slf *HttpService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if slf.corsHeader != nil {
if origin := r.Header.Get("Origin"); origin != "" {
slf.corsHeader.copyTo(w.Header())
}
}
if r.Method == "OPTIONS" {
return
}
session := &HttpSession{sessionDone:make(chan *HttpSession,1),httpRouter:slf.httpRouter,statusCode:http.StatusOK}
session.r = r
session.w = w
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
session.WriteStatusCode(http.StatusGatewayTimeout)
session.flush()
return
}
session.body = body
slf.GetEventHandler().NotifyEvent(&event.Event{Type:event.Sys_Event_Http_Event,Data:session})
ticker := time.NewTicker(slf.processTimeout)
select {
case <-ticker.C:
session.WriteStatusCode(http.StatusGatewayTimeout)
session.flush()
break
case <- session.sessionDone:
if session.fileData!=nil {
slf.ProcessFile(session)
}else if session.redirectData!=nil {
session.redirects()
}else{
session.flush()
}
}
}