Avec un parser soap un peu plus modulaire et un debut de ControlHandler sur les services qui fait des choses

This commit is contained in:
2025-09-07 11:45:43 +02:00
parent 437d9db4b7
commit a04f8cce6b
9 changed files with 368 additions and 42 deletions

View File

@@ -147,9 +147,6 @@ func (svc *ServiceInstance) EventSubHandler() func(w http.ResponseWriter, r *htt
func (svc *ServiceInstance) ControlHandler() func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
log.Infof("📡 Control request for %s:%s", svc.device.Name(), svc.Name())
log.Infof("➡️ Method: %s URL: %s", r.Method, r.URL.Path)
log.Infof("Header SOAPACTION: %s", r.Header.Get("SOAPACTION"))
log.Infof("Header Content-Type: %s", r.Header.Get("Content-Type"))
body, err := io.ReadAll(r.Body)
if err != nil {
@@ -158,14 +155,85 @@ func (svc *ServiceInstance) ControlHandler() func(w http.ResponseWriter, r *http
return
}
soap.ParseSOAPGeneric(body)
// Callback générique pour décoder chaque paramètre
decoder := func(action, param, value string) (string, interface{}, error) {
// 1⃣ Chercher si le param correspond à une StateVarInstance
// Réponse minimale SOAP
sv, ok := svc.statevariables[param]
log.Warnf("Look for a variable named : %s -> %v", param, ok)
if ok {
if sv.HasParser() {
v, err := sv.ParseValue(value)
if err != nil {
return param, value, err
}
return param, v, err
}
v, err := sv.Cast(value)
if err != nil {
return param, value, err
}
return param, v, nil
}
// 2⃣ Chercher si le param correspond à une ActionInstance et appliquer un parseur associé
act, ok := svc.actions[action]
log.Debugf("Look for an action named : %s -> %v", action, ok)
if ok {
if argument, ok := act.Arguments(param); ok {
sv_name := argument.StateVariable().Name()
sv := svc.statevariables[sv_name]
v, err := sv.ParseValue(value)
log.Debugf("It corresponds to variable : %s with a parser %v", sv_name, sv.HasParser())
if err != nil {
return param, value, err
}
return param, v, nil
}
}
// 3⃣ Sinon, valeur brute
return param, value, nil
}
env, err := soap.ParseSOAPEnvelope(body)
if err != nil {
log.Errorf("❌ Failed to parse SOAP enveloppe: %v", err)
soapResp, _ := soap.BuildSOAPFault("s:Client", "Invalid Args", err.Error())
w.Header().Set("Content-Type", `text/xml; charset="utf-8"`)
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write(soapResp)
return
}
req, err := soap.ParseUPnPAction(env, decoder)
if err != nil {
log.Errorf("❌ Failed to parse SOAP Action: %v", err)
soapResp, _ := soap.BuildSOAPFault("s:Client", "Invalid Args", err.Error())
w.Header().Set("Content-Type", `text/xml; charset="utf-8"`)
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write(soapResp)
return
}
log.Infof("➡️ SOAP Action: %s", req.Name)
for k, v := range req.Args {
if mi, ok := v.(Markdownable); ok {
v = mi.ToMarkdown()
}
log.Infof(" %s:%s = %v", req.Name, k, v)
}
// Ici tu peux appeler l'action correspondante sur svc.actions[req.Name] et récupérer le résultat
// Exemple de réponse minimale :
resp, _ := soap.BuildUPnPResponse(svc.ServiceType(), req.Name, map[string]string{})
w.Header().Set("Content-Type", `text/xml; charset="utf-8"`)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body/>
</s:Envelope>`))
_, _ = w.Write(resp)
}
}