package cdp import ( "encoding/json" "io/ioutil" "net/http" "src.dualinventive.com/go/lib/dilog" ) // Endpoint for T-Mobile CDP HTTP JSON type Endpoint struct { mux http.Handler url string log dilog.Logger uplinkMsgReportHandler UplinkMsgReportHandlerFunc basicAuth *endpointBasicAuth } // NewEndpoint create a new endpoint on url func NewEndpoint(url string, opts ...EndpointOption) (*Endpoint, error) { ep := &Endpoint{} for _, option := range opts { err := option(ep) if err != nil { return nil, err } } if ep.log == nil { ep.log = dilog.Discard } ep.url = url mux := http.NewServeMux() mux.HandleFunc("/", ep.uplinkMsgDataHandler) ep.mux = mux return ep, nil } // ListenAndServe listens and serves the endpoint func (ep *Endpoint) ListenAndServe() error { return http.ListenAndServe(ep.url, ep.mux) } // uplinkMsgDataHandlerReadEndpointMessage reads the EndpointMessage from the http request func uplinkMsgDataHandlerReadEndpointMessage(r *http.Request) (*EndpointMessage, error) { data, err := ioutil.ReadAll(r.Body) if err != nil { return nil, err } err = r.Body.Close() if err != nil { return nil, err } epMsg := &EndpointMessage{} err = json.Unmarshal(data, epMsg) if err != nil { return nil, err } return epMsg, nil } func (ep *Endpoint) uplinkMsgDataHandler(w http.ResponseWriter, r *http.Request) { if ep.uplinkMsgReportHandler == nil { w.WriteHeader(http.StatusInternalServerError) return } // When basic authentication option is set we validate the request if ep.basicAuth != nil { if !ep.basicAuth.IsRequestAuthenticated(r) { ep.log.WithFields(dilog.Fields{ "ip": getIPAddress(r), }).Error("client not authenticated") w.Header().Set("WWW-Authenticate", "Basic") w.WriteHeader(http.StatusUnauthorized) return } } // It is possible the IoT portal probes if the URL is valid with an empty request if r.ContentLength == 0 { w.WriteHeader(http.StatusOK) return } epMsg, err := uplinkMsgDataHandlerReadEndpointMessage(r) if err != nil { w.WriteHeader(http.StatusBadRequest) ep.log.WithError(err).Error("uplinkMsgDataHandlerReadEndpointMessage failed") return } for _, report := range epMsg.Reports { report := report if report.ResourcePath != ResourcePathUplinkMsgData { continue } ep.uplinkMsgReportHandler(&report) } }