diff --git a/internal/caches/list_file.go b/internal/caches/list_file.go index 73cb6e6..bcbbe8b 100644 --- a/internal/caches/list_file.go +++ b/internal/caches/list_file.go @@ -186,7 +186,7 @@ func (this *FileList) CleanPrefix(prefix string) error { var count = int64(10000) for { - result, err := this.db.Exec(`UPDATE "`+this.itemsTableName+`" SET expiredAt=0 WHERE id IN (SELECT id FROM "`+this.itemsTableName+`" WHERE expiredAt>0 AND createdAt<=? AND INSTR("key", ?)==1 LIMIT `+strconv.FormatInt(count, 10)+`)`, utils.UnixTime(), prefix) + result, err := this.db.Exec(`UPDATE "`+this.itemsTableName+`" SET expiredAt=0 WHERE id IN (SELECT id FROM "`+this.itemsTableName+`" WHERE expiredAt>0 AND createdAt<=? AND INSTR("key", ?)=1 LIMIT `+strconv.FormatInt(count, 10)+`)`, utils.UnixTime(), prefix) if err != nil { return err } diff --git a/internal/caches/writer.go b/internal/caches/writer.go index 67e12eb..ca180a1 100644 --- a/internal/caches/writer.go +++ b/internal/caches/writer.go @@ -1,31 +1,31 @@ package caches -// 缓存内容写入接口 +// Writer 缓存内容写入接口 type Writer interface { - // 写入Header数据 + // WriteHeader 写入Header数据 WriteHeader(data []byte) (n int, err error) - // 写入Body数据 + // Write 写入Body数据 Write(data []byte) (n int, err error) - // 写入的Header数据大小 + // HeaderSize 写入的Header数据大小 HeaderSize() int64 - // 写入的Body数据大小 + // BodySize 写入的Body数据大小 BodySize() int64 - // 关闭 + // Close 关闭 Close() error - // 丢弃 + // Discard 丢弃 Discard() error - // Key + // Key Key Key() string - // 过期时间 + // ExpiredAt 过期时间 ExpiredAt() int64 - // 内容类型 + // ItemType 内容类型 ItemType() ItemType } diff --git a/internal/nodes/http_request.go b/internal/nodes/http_request.go index 2944139..fd0c276 100644 --- a/internal/nodes/http_request.go +++ b/internal/nodes/http_request.go @@ -46,6 +46,7 @@ type HTTPRequest struct { IsHTTPS bool // 内部参数 + isSubRequest bool writer *HTTPWriter web *serverconfigs.HTTPWebConfig // Web配置,重要提示:由于引用了别的共享的配置,所以操作中只能读取不要修改 reverseProxyRef *serverconfigs.ReverseProxyRef // 反向代理引用 @@ -129,7 +130,12 @@ func (this *HTTPRequest) Do() { } // 访问控制 - // TODO 需要实现 + if !this.isSubRequest && this.web.Auth != nil && this.web.Auth.IsOn { + if this.doAuth() { + this.doEnd() + return + } + } // 自动跳转到HTTPS if this.IsHTTP && this.web.RedirectToHttps != nil && this.web.RedirectToHttps.IsOn { @@ -352,6 +358,11 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo this.web.FastcgiList = web.FastcgiList } + // auth + if web.Auth != nil && (web.Auth.IsPrior || isTop) { + this.web.Auth = web.Auth + } + // 重写规则 if len(web.RewriteRefs) > 0 { for index, ref := range web.RewriteRefs { @@ -919,6 +930,11 @@ func (this *HTTPRequest) requestServerPort() int { return 0 } +// 获取完整的URL +func (this *HTTPRequest) requestFullURL() string { + return this.requestScheme() + "://" + this.Host + this.uri +} + // 设置代理相关头部信息 // 参考:https://tools.ietf.org/html/rfc7239 func (this *HTTPRequest) setForwardHeaders(header http.Header) { @@ -1149,6 +1165,11 @@ func (this *HTTPRequest) canIgnore(err error) bool { return true } + // 已读到头 + if err == io.EOF || err == io.ErrUnexpectedEOF { + return true + } + // 网络错误 _, ok := err.(*net.OpError) if ok { diff --git a/internal/nodes/http_request_auth.go b/internal/nodes/http_request_auth.go new file mode 100644 index 0000000..f36d667 --- /dev/null +++ b/internal/nodes/http_request_auth.go @@ -0,0 +1,61 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package nodes + +import ( + "bytes" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "io/ioutil" + "net/http" +) + +// 执行认证 +func (this *HTTPRequest) doAuth() (shouldStop bool) { + if this.web.Auth == nil || !this.web.Auth.IsOn { + return + } + + for _, ref := range this.web.Auth.PolicyRefs { + if !ref.IsOn || ref.AuthPolicy == nil || !ref.AuthPolicy.IsOn { + continue + } + b, err := ref.AuthPolicy.Filter(this.RawReq, func(subReq *http.Request) (status int, err error) { + subReq.TLS = this.RawReq.TLS + subReq.RemoteAddr = this.RawReq.RemoteAddr + subReq.Host = this.RawReq.Host + subReq.Proto = this.RawReq.Proto + subReq.ProtoMinor = this.RawReq.ProtoMinor + subReq.ProtoMajor = this.RawReq.ProtoMajor + subReq.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) + subReq.Header.Set("Referer", this.requestFullURL()) + var writer = NewEmptyResponseWriter(this.writer) + this.doSubRequest(writer, subReq) + return writer.StatusCode(), nil + }, this.Format) + if err != nil { + this.write502(err) + return + } + if b { + return + } else { + if ref.AuthPolicy.Type == serverconfigs.HTTPAuthTypeBasicAuth { + var method = ref.AuthPolicy.Method().(*serverconfigs.HTTPAuthBasicMethod) + var headerValue = "Basic realm=\"" + if len(method.Realm) > 0 { + headerValue += method.Realm + } else { + headerValue += this.Host + } + headerValue += "\"" + if len(method.Charset) > 0 { + headerValue += ", charset=\"" + method.Charset + "\"" + } + this.writer.Header()["WWW-Authenticate"] = []string{headerValue} + } + this.writer.WriteHeader(http.StatusUnauthorized) + return true + } + } + return +} diff --git a/internal/nodes/http_request_sub.go b/internal/nodes/http_request_sub.go new file mode 100644 index 0000000..595259a --- /dev/null +++ b/internal/nodes/http_request_sub.go @@ -0,0 +1,22 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package nodes + +import "net/http" + +// 执行子请求 +func (this *HTTPRequest) doSubRequest(writer http.ResponseWriter, rawReq *http.Request) { + // 包装新请求对象 + req := &HTTPRequest{ + RawReq: rawReq, + RawWriter: writer, + Server: this.Server, + Host: this.Host, + ServerName: this.ServerName, + ServerAddr: this.ServerAddr, + IsHTTP: this.IsHTTP, + IsHTTPS: this.IsHTTPS, + } + req.isSubRequest = true + req.Do() +} diff --git a/internal/nodes/http_writer_empty.go b/internal/nodes/http_writer_empty.go new file mode 100644 index 0000000..136243a --- /dev/null +++ b/internal/nodes/http_writer_empty.go @@ -0,0 +1,63 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package nodes + +import ( + "bufio" + "net" + "net/http" +) + +// EmptyResponseWriter 空的响应Writer +type EmptyResponseWriter struct { + header http.Header + parentWriter http.ResponseWriter + + statusCode int +} + +func NewEmptyResponseWriter(parentWriter http.ResponseWriter) *EmptyResponseWriter { + return &EmptyResponseWriter{ + header: http.Header{}, + parentWriter: parentWriter, + } +} + +func (this *EmptyResponseWriter) Header() http.Header { + return this.header +} + +func (this *EmptyResponseWriter) Write(data []byte) (int, error) { + if this.statusCode > 300 && this.parentWriter != nil { + return this.parentWriter.Write(data) + } + return 0, nil +} + +func (this *EmptyResponseWriter) WriteHeader(statusCode int) { + this.statusCode = statusCode + + if this.statusCode > 300 && this.parentWriter != nil { + var parentHeader = this.parentWriter.Header() + for k, v := range this.header { + parentHeader[k] = v + } + this.parentWriter.WriteHeader(this.statusCode) + } +} + +func (this *EmptyResponseWriter) StatusCode() int { + return this.statusCode +} + +// Hijack Hijack +func (this *EmptyResponseWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) { + if this.parentWriter == nil { + return + } + hijack, ok := this.parentWriter.(http.Hijacker) + if ok { + return hijack.Hijack() + } + return +}