func (server *Server) contentDirectoryEventSubHandler(w http.ResponseWriter, r *http.Request) { if server.StallEventSubscribe { // I have an LG TV that doesn't like my eventing implementation. // Returning unimplemented (501?) errors, results in repeat subscribe // attempts which hits some kind of error count limit on the TV // causing it to forcefully disconnect. It also won't work if the CDS // service doesn't include an EventSubURL. The best thing I can do is // cause every attempt to subscribe to timeout on the TV end, which // reduces the error rate enough that the TV continues to operate // without eventing. // // I've not found a reliable way to identify this TV, since it and // others don't seem to include any client-identifying headers on // SUBSCRIBE requests. // // TODO: Get eventing to work with the problematic TV. t := time.Now() <-w.(http.CloseNotifier).CloseNotify() eventingLogger.Printf("stalled subscribe connection went away after %s", time.Since(t)) return } // The following code is a work in progress. It partially implements // the spec on eventing but hasn't been completed as I have nothing to // test it with. eventingLogger.Print(r.Header) service := server.services["ContentDirectory"] eventingLogger.Println(r.RemoteAddr, r.Method, r.Header.Get("SID")) if r.Method == "SUBSCRIBE" && r.Header.Get("SID") == "" { urls := upnp.ParseCallbackURLs(r.Header.Get("CALLBACK")) eventingLogger.Println(urls) var timeout int fmt.Sscanf(r.Header.Get("TIMEOUT"), "Second-%d", &timeout) eventingLogger.Println(timeout, r.Header.Get("TIMEOUT")) sid, timeout, _ := service.Subscribe(urls, timeout) w.Header()["SID"] = []string{sid} w.Header()["TIMEOUT"] = []string{fmt.Sprintf("Second-%d", timeout)} // TODO: Shouldn't have to do this to get headers logged. w.WriteHeader(http.StatusOK) go func() { time.Sleep(100 * time.Millisecond) server.contentDirectoryInitialEvent(urls, sid) }() } else if r.Method == "SUBSCRIBE" { http.Error(w, "meh", http.StatusPreconditionFailed) } else { eventingLogger.Printf("unhandled event method: %s", r.Method) } }
func (server *Server) contentDirectoryEventSubHandler(w http.ResponseWriter, r *http.Request) { if r.UserAgent() == "" && true { // This should block forever. Clearly it's a minor resource leak, // since the underlying transport should expire eventually. I have // an (LG?) TV that doesn't provide a User-Agent in this request // that will refuse to operate correctly with any eventing I've // implemented. Returning unimplemented (501?) errors, results in // repeat subscribe attempts which hits some kind of error count // limit on the TV causing it to forcefully disconnect. It also // won't work if the CDS service doesn't include an EventSubURL. // The best thing I can do is cause every attempt to subscribe to // timeout on the TV end, which reduces the error rate enough that // the TV continues to operate without eventing. // // TODO: Stall the underlying connection until it drops without // stalling the entire goroutine indefinitely, get eventing to // work with the problematic TV. timeoutSocketBelt.Ride() return } // The following code is a work in progress. It partially implements // the spec on eventing but hasn't been completed as I have nothing to // test it with. log.Print(r.Header) service := server.services["ContentDirectory"] log.Println(r.RemoteAddr, r.Method, r.Header.Get("SID")) if r.Method == "SUBSCRIBE" && r.Header.Get("SID") == "" { urls := upnp.ParseCallbackURLs(r.Header.Get("CALLBACK")) log.Println(urls) var timeout int fmt.Sscanf(r.Header.Get("TIMEOUT"), "Second-%d", &timeout) log.Println(timeout, r.Header.Get("TIMEOUT")) sid, timeout, _ := service.Subscribe(urls, timeout) w.Header()["SID"] = []string{sid} w.Header()["TIMEOUT"] = []string{fmt.Sprintf("Second-%d", timeout)} // TODO: Shouldn't have to do this to get headers logged. w.WriteHeader(http.StatusOK) go func() { time.Sleep(100 * time.Millisecond) server.contentDirectoryInitialEvent(urls, sid) }() } else if r.Method == "SUBSCRIBE" { http.Error(w, "meh", http.StatusPreconditionFailed) } else { log.Print("unhandled event") } }