| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- package fairwind
- import (
- "encoding/json"
- "fmt"
- "io"
- "net"
- "net/http"
- "reflect"
- "time"
- )
- type HTTPServerRequest struct {
- Headers HTTPHeaders
- Data any
- }
- type HTTPServerResponse struct {
- Headers HTTPHeaders
- Status int
- Error error
- Encoding int
- Plain []byte
- Data any
- }
- func ResponseOK() *HTTPServerResponse {
- return &HTTPServerResponse{
- Headers: HTTPHeaders{},
- Plain: []byte{},
- Encoding: ENCODING_PLAIN,
- }
- }
- func ResponsePlain(headers HTTPHeaders, plain []byte) *HTTPServerResponse {
- return &HTTPServerResponse{
- Headers: headers,
- Plain: plain,
- Encoding: ENCODING_PLAIN,
- }
- }
- func ResponseJSON(headers HTTPHeaders, data any) *HTTPServerResponse {
- return &HTTPServerResponse{
- Headers: headers,
- Data: data,
- Encoding: ENCODING_JSON,
- }
- }
- func ResponseErr(status int, err error) *HTTPServerResponse {
- return &HTTPServerResponse{
- Status: status,
- Error: err,
- }
- }
- type HTTPServerCallback func(request *HTTPServerRequest) *HTTPServerResponse
- type HTTPServerHandler struct {
- Callback HTTPServerCallback
- Data any
- Buffer int
- Validators map[string][]Validator
- }
- type HTTPServerAction struct {
- Method int
- Path string
- }
- type HTTPServer struct {
- log *Log
- host string
- port string
- handlers map[HTTPServerAction]HTTPServerHandler
- }
- func NewHTTPServer(log *Log, host string, port string, handlers map[HTTPServerAction]HTTPServerHandler) *HTTPServer {
- return &HTTPServer{
- log: log,
- host: host,
- port: port,
- handlers: handlers,
- }
- }
- func (this *HTTPServer) Start() error {
- listener, err := net.Listen("tcp", fmt.Sprintf("%s:%s", this.host, this.port))
- if err != nil {
- return err
- }
- channel := make(chan error)
- go func(listener net.Listener, channel chan<- error) {
- server := &http.Server{
- Handler: this,
- }
- channel <- server.Serve(listener)
- }(listener, channel)
- err = <-channel
- if err != nil {
- return err
- }
- return nil
- }
- func (this *HTTPServer) ServeHTTP(responseStream http.ResponseWriter, requestStream *http.Request) {
- // Find handler
- method := METHOD_NONE
- switch requestStream.Method {
- case "GET":
- method = METHOD_GET
- case "POST":
- method = METHOD_POST
- }
- if method == METHOD_NONE {
- this.log.Error("invalid method", LogValue("method", requestStream.Method))
- responseStream.WriteHeader(http.StatusInternalServerError)
- return
- }
- handlerSpec, ok := this.handlers[HTTPServerAction{Method: method, Path: requestStream.URL.Path}]
- if !ok {
- this.log.Error("handler not found", LogValue("path", requestStream.URL.Path))
- responseStream.WriteHeader(http.StatusInternalServerError)
- return
- }
- handler := HTTPServerHandler{}
- copyStruct(&handler, &handlerSpec)
- handlerSpec = handler
- // Parse GET query
- if method == METHOD_GET && handler.Data != nil {
- // TODO: support arrays
- query := map[string]any{}
- for key, values := range requestStream.URL.Query() {
- for _, value := range values {
- query[key] = value
- }
- }
- buffer, err := json.Marshal(&query)
- if err != nil {
- this.log.Error("malformed request body", LogValue("path", requestStream.URL.Path), LogError(err))
- responseStream.WriteHeader(http.StatusInternalServerError)
- return
- }
- err = json.Unmarshal(buffer, handler.Data)
- if err != nil {
- this.log.Error("malformed request body", LogValue("path", requestStream.URL.Path), LogError(err))
- responseStream.WriteHeader(http.StatusInternalServerError)
- return
- }
- }
- // Parse POST body
- if method == METHOD_POST && handler.Data != nil {
- size := 1
- if handler.Buffer > 0 {
- size = handler.Buffer
- }
- buffer := make([]byte, 1024*size)
- n, err := requestStream.Body.Read(buffer)
- if err != io.EOF {
- this.log.Error("malformed request body", LogValue("path", requestStream.URL.Path), LogError(err))
- responseStream.WriteHeader(http.StatusInternalServerError)
- return
- }
- err = json.Unmarshal(buffer[:n], handler.Data)
- if err != nil {
- this.log.Error("malformed request body", LogValue("path", requestStream.URL.Path), LogError(err))
- responseStream.WriteHeader(http.StatusInternalServerError)
- return
- }
- if !validateData(handler) {
- this.log.Error("validation failed", LogValue("path", requestStream.URL.Path))
- responseStream.WriteHeader(http.StatusNotAcceptable)
- return
- }
- }
- // Log query
- start := time.Now()
- defer func() {
- this.log.Debug(
- "request",
- LogValue("method", requestStream.Method),
- LogValue("path", requestStream.URL.Path),
- LogValue("duration", time.Since(start)),
- )
- }()
- // Handle
- requestHeaders := HTTPHeaders{}
- for key, values := range requestStream.Header {
- for _, value := range values {
- requestHeaders[key] = value
- }
- }
- response := handler.Callback(
- &HTTPServerRequest{
- Headers: requestHeaders,
- Data: handler.Data,
- },
- )
- if response.Error != nil {
- this.log.Error("error serving request", LogError(response.Error))
- }
- // Headers
- for key, value := range response.Headers {
- responseStream.Header().Add(key, value)
- }
- if response.Encoding == ENCODING_PLAIN {
- responseStream.Header().Add("Content-Type", "text/plain")
- }
- if response.Encoding == ENCODING_JSON {
- responseStream.Header().Add("Content-Type", "application/json")
- }
- // Status
- if response.Status == 0 {
- responseStream.WriteHeader(http.StatusOK)
- } else {
- responseStream.WriteHeader(response.Status)
- }
- // Body
- if response.Encoding == ENCODING_PLAIN {
- _, err := responseStream.Write(response.Plain)
- if err != nil {
- this.log.Error("malformed response body", LogValue("path", requestStream.URL.Path), LogError(err))
- return
- }
- }
- if response.Encoding == ENCODING_JSON {
- buffer, err := json.Marshal(response.Data)
- if err != nil {
- this.log.Error("malformed response body", LogValue("path", requestStream.URL.Path), LogError(err))
- return
- }
- _, err = responseStream.Write(buffer)
- if err != nil {
- this.log.Error("malformed response body", LogValue("path", requestStream.URL.Path), LogError(err))
- return
- }
- }
- }
- func copyStruct(dst interface{}, src interface{}) {
- dstVal := reflect.ValueOf(dst)
- srcVal := reflect.ValueOf(src)
- if dstVal.Kind() != reflect.Ptr || dstVal.Elem().Kind() != reflect.Struct {
- return
- }
- if srcVal.Kind() == reflect.Ptr {
- srcVal = srcVal.Elem()
- }
- if srcVal.Kind() != reflect.Struct {
- return
- }
- dstVal = dstVal.Elem()
- for i := 0; i < srcVal.NumField(); i++ {
- srcField := srcVal.Field(i)
- fieldName := srcVal.Type().Field(i).Name
- dstField := dstVal.FieldByName(fieldName)
- if dstField.IsValid() && dstField.CanSet() && dstField.Type() == srcField.Type() {
- dstField.Set(srcField)
- }
- }
- }
- func validateData(handler HTTPServerHandler) bool {
- if handler.Validators == nil {
- return true
- }
- value := reflect.ValueOf(handler.Data)
- if value.Kind() != reflect.Ptr {
- return true
- }
- value = value.Elem()
- if value.Kind() != reflect.Struct {
- return true
- }
- for field := range value.Fields() {
- validators, ok := handler.Validators[field.Name]
- if !ok {
- continue
- }
- if len(validators) == 0 {
- continue
- }
- for _, validator := range validators {
- if validator.Validate(value.Interface()) {
- continue
- }
- return false
- }
- }
- return true
- }
|