TUint CpiDeviceDv::Subscribe(CpiSubscription& aSubscription, const OpenHome::Uri& /*aSubscriber*/) { Brh sid; iDeviceDv.CreateSid(sid); Brn tmp(sid); Brh transfer(tmp); aSubscription.SetSid(transfer); TUint durationSecs = iDeviceCp->GetCpStack().Env().InitParams()->SubscriptionDurationSecs(); DviSubscription* subscriptionDv = new DviSubscription(iDeviceDv.GetDvStack(), iDeviceDv, *this, NULL, sid); subscriptionDv->AddRef(); // guard against subscription expiring before client tries to renew or unsubscribe iDeviceDv.GetDvStack().SubscriptionManager().AddSubscription(*subscriptionDv); subscriptionDv->SetDuration(durationSecs); iLock.Wait(); if (iSubscriptions.size() == 0) { iShutdownSem.Wait(); // consume shutdown signal now the map is non-empty } Brn sid2(subscriptionDv->Sid()); Subscription* subscription = new Subscription(aSubscription, subscriptionDv); iSubscriptions.insert(std::pair<Brn,Subscription*>(sid2, subscription)); iDeviceCp->AddRef(); iLock.Signal(); DviService* service = iDeviceDv.ServiceReference(aSubscription.ServiceType()); ASSERT(service != NULL); service->AddSubscription(subscriptionDv); service->RemoveRef(); return durationSecs; }
CpiSubscription* CpiSubscriptionManager::FindSubscription(const Brx& aSid) { AutoMutex a(iLock); Brn sid(aSid); Map::iterator it = iMap.find(sid); if (it == iMap.end()) { return NULL; } CpiSubscription* subscription = it->second; subscription->AddRef(); return subscription; }
void CpiDeviceUpnp::Unsubscribe(CpiSubscription& aSubscription, const Brx& aSid) { Uri uri; GetServiceUri(uri, "eventSubURL", aSubscription.ServiceType()); EventUpnp eventUpnp(iDevice->GetCpStack(), aSubscription); eventUpnp.Unsubscribe(uri, aSid); }
TUint CpiDeviceUpnp::Renew(CpiSubscription& aSubscription) { TUint durationSecs = iDevice->GetCpStack().Env().InitParams()->SubscriptionDurationSecs(); Uri uri; GetServiceUri(uri, "eventSubURL", aSubscription.ServiceType()); EventUpnp eventUpnp(iDevice->GetCpStack(), aSubscription); eventUpnp.RenewSubscription(uri, durationSecs); return durationSecs; }
TUint CpiDeviceUpnp::Renew(CpiSubscription& aSubscription) { TUint durationSecs = 30 * 60; // 30 minutes Uri uri; GetServiceUri(uri, "eventSubURL", aSubscription.ServiceType()); EventUpnp eventUpnp(aSubscription); eventUpnp.RenewSubscription(uri, durationSecs); return durationSecs; }
TUint CpiDeviceUpnp::Subscribe(CpiSubscription& aSubscription, const Uri& aSubscriber) { TUint durationSecs = CpiSubscription::kDefaultDurationSecs; Uri uri; GetServiceUri(uri, "eventSubURL", aSubscription.ServiceType()); EventUpnp eventUpnp(aSubscription); eventUpnp.Subscribe(uri, aSubscriber, durationSecs); return durationSecs; }
TUint CpiDeviceUpnp::Subscribe(CpiSubscription& aSubscription, const Uri& aSubscriber) { TUint durationSecs = iDevice->GetCpStack().Env().InitParams().SubscriptionDurationSecs(); Uri uri; GetServiceUri(uri, "eventSubURL", aSubscription.ServiceType()); EventUpnp eventUpnp(iDevice->GetCpStack(), aSubscription); eventUpnp.Subscribe(uri, aSubscriber, durationSecs); return durationSecs; }
void CpiSubscriptionManager::Add(CpiSubscription& aSubscription) { iLock.Wait(); Brn sid(aSubscription.Sid()); ASSERT(sid.Bytes() > 0); iMap.insert(std::pair<Brn,CpiSubscription*>(sid, &aSubscription)); RemovePendingAdds(sid); iLock.Signal(); }
TUint CpiDeviceDv::Renew(CpiSubscription& aSubscription) { Brn sid(aSubscription.Sid()); TUint durationSecs = iDeviceCp->GetCpStack().Env().InitParams()->SubscriptionDurationSecs(); AutoMutex _(iLock); SubscriptionMap::iterator it = iSubscriptions.find(sid); if (it != iSubscriptions.end()) { it->second->iDv->Renew(durationSecs); } return durationSecs; }
void CpiDeviceLpec::Unsubscribe(CpiSubscription& aSubscription, const Brx& /*aSid*/) { AutoMutex a(iLock); iWriteBuffer->Write(Lpec::kMethodUnsubscribe); iWriteBuffer->Write(' '); iWriteBuffer->Write(iLpecName); iWriteBuffer->Write('/'); iWriteBuffer->Write(aSubscription.ServiceType().Name()); iWriteBuffer->Write(Lpec::kMsgTerminator); iWriteBuffer->WriteFlush(); // no great benefit in waiting for a response }
TUint CpiDeviceLpec::Subscribe(CpiSubscription& aSubscription, const OpenHome::Uri& /*aSubscriber*/) { Semaphore sem("CLS2", 0); SubscriptionResponse resp(sem); AutoMutex a(iLock); iResponseHandler = &resp; iWriteBuffer->Write(Lpec::kMethodSubscribe); iWriteBuffer->Write(' '); iWriteBuffer->Write(iLpecName); iWriteBuffer->Write('/'); iWriteBuffer->Write(aSubscription.ServiceType().Name()); iWriteBuffer->Write(Lpec::kMsgTerminator); iWriteBuffer->WriteFlush(); sem.Wait(); Bws<128> sid(iDevice->Udn()); sid.Append('-'); sid.Append(resp.SidFragment()); Brh sid2(sid); aSubscription.SetSid(sid2); iResponseHandler = NULL; return UINT_MAX; // subscription never expires so report the longest possible duration }
void CpiSubscriptionManager::Remove(CpiSubscription& aSubscription) { iLock.Wait(); Brn sid(aSubscription.Sid()); Map::iterator it = iMap.find(sid); if (it != iMap.end()) { it->second = NULL; iMap.erase(it); } TBool shutdownSignal = ReadyForShutdown(); iLock.Signal(); if (shutdownSignal) { iShutdownSem.Signal(); } }
void CpiDeviceDv::Unsubscribe(CpiSubscription& aSubscription, const Brx& aSid) { iLock.Wait(); Brn sid(aSid); SubscriptionMap::iterator it = iSubscriptions.find(sid); if (it == iSubscriptions.end()) { iLock.Signal(); return; } Subscription* subscription = it->second; iLock.Signal(); DviService* service = iDeviceDv.ServiceReference(aSubscription.ServiceType()); if (service != NULL) { service->RemoveSubscription(aSid); service->RemoveRef(); } subscription->iCp = NULL; subscription->iDv->RemoveRef(); // can't safely access subscription now - RemoveRef() above may have resulted in it being deleted }
void EventSessionUpnp::Run() { CpiSubscription* subscription = NULL; iErrorStatus = &HttpStatus::kOk; try { iReaderRequest->Flush(); iReaderRequest->Read(); // check headers if (iReaderRequest->MethodNotAllowed()) { Error(HttpStatus::kBadRequest); } if (!iHeaderNt.Received() || !iHeaderNts.Received()) { Error(HttpStatus::kBadRequest); } if (iHeaderNt.Value() != kExpectedNt || iHeaderNts.Value() != kExpectedNts || !iHeaderSid.Received() || iHeaderSid.Sid().Bytes() == 0 || !iHeaderSeq.Received()) { Error(HttpStatus::kPreconditionFailed); } subscription = CpiSubscriptionManager::FindSubscription(iHeaderSid.Sid()); if (subscription == NULL) { /* the UPnP spec contains a potential race condition where the first NOTIFY message can be processed ahead of the SUBSCRIBE reply which provides the sid. Wait until any in-progress subscriptions complete and try again in case that's what has happened here */ CpiSubscriptionManager::WaitForPendingAdds(); subscription = CpiSubscriptionManager::FindSubscription(iHeaderSid.Sid()); if (subscription == NULL) { LOG2(kEvent, kError, "notification for unexpected device - ") LOG2(kEvent, kError, iHeaderSid.Sid()); LOG2(kEvent, kError, "\n"); Error(HttpStatus::kPreconditionFailed); } } if (!subscription->UpdateSequenceNumber(iHeaderSeq.Seq())) { subscription->SetNotificationError(); subscription->RemoveRef(); subscription = NULL; } } catch(HttpError) {} catch(ReaderError) {} try { // write response Sws<128> writerBuffer(*this); WriterHttpResponse response(writerBuffer); response.WriteStatus(*iErrorStatus, Http::eHttp11); response.WriteFlush(); // read entity if (subscription != NULL) { Bwh entity; if (iHeaderTransferEncoding.IsChunked()) { ReaderHttpChunked dechunker(*iReadBuffer); dechunker.Read(); dechunker.TransferTo(entity); } else { TUint length = iHeaderContentLength.ContentLength(); if (length == 0) { THROW(HttpError); } entity.Grow(length); while (length > 0) { TUint readBytes = (length<kMaxReadBytes? length : kMaxReadBytes); entity.Append(iReadBuffer->Read(readBytes)); length -= readBytes; } } // process entity LOG(kEvent, "EventSessionUpnp::Run, sid - "); LOG(kEvent, iHeaderSid.Sid()); LOG(kEvent, " seq - %u\n", iHeaderSeq.Seq()); ProcessNotification(*subscription, entity); } } catch(HttpError) { LogError(subscription, "HttpError"); } catch(ReaderError) { LogError(subscription, "ReaderError"); } catch(WriterError) { LogError(subscription, "WriterError"); } catch(NetworkError) { LogError(subscription, "NetworkError"); } catch(XmlError) { LogError(subscription, "XmlError"); } if (subscription != NULL) { subscription->RemoveRef(); } }
void EventSessionUpnp::Run() { AutoSemaphore a(iShutdownSem); CpiSubscription* subscription = NULL; iErrorStatus = &HttpStatus::kOk; iDechunker->SetChunked(false); iDechunker->ReadFlush(); try { iReaderRequest->Flush(); iReaderRequest->Read(kReadTimeoutMs); // check headers if (iReaderRequest->MethodNotAllowed()) { Error(HttpStatus::kBadRequest); } if (!iHeaderNt.Received() || !iHeaderNts.Received()) { Error(HttpStatus::kBadRequest); } if (iHeaderNt.Value() != kExpectedNt || iHeaderNts.Value() != kExpectedNts || !iHeaderSid.Received() || iHeaderSid.Sid().Bytes() == 0 || !iHeaderSeq.Received()) { Error(HttpStatus::kPreconditionFailed); } Parser parser(iReaderRequest->Uri()); (void)parser.Next('/'); Brn idBuf = parser.Next('/'); TUint id = 0; try { id = Ascii::Uint(idBuf); } catch (AsciiError&) { const Brx& sid = iHeaderSid.Sid(); LOG2(kEvent, kError, "notification for %.*s failed to include id in path\n", PBUF(sid)); Error(HttpStatus::kPreconditionFailed); } subscription = iCpStack.SubscriptionManager().FindSubscription(id); if (subscription == NULL) { const Brx& sid = iHeaderSid.Sid(); LOG2(kEvent, kError, "notification for unexpected device - %.*s\n", PBUF(sid)) Error(HttpStatus::kPreconditionFailed); } } catch(HttpError&) {} catch(ReaderError&) {} try { // write response Sws<128> writerBuffer(*this); WriterHttpResponse response(writerBuffer); response.WriteStatus(*iErrorStatus, Http::eHttp11); response.WriteFlush(); // read entity if (subscription != NULL) { Bwh entity; WriterBwh writer(1024); if (iHeaderTransferEncoding.IsChunked()) { iDechunker->SetChunked(true); for (;;) { Brn buf = iDechunker->Read(kMaxReadBytes); writer.Write(buf); if (buf.Bytes() == 0) { // end of stream break; } } } else { TUint length = iHeaderContentLength.ContentLength(); if (length == 0) { // no Content-Length header, so read until remote socket closed (so ReaderError is thrown) try { for (;;) { writer.Write(iReaderUntil->Read(kMaxReadBytes)); } } catch (ReaderError&) { } } else { TUint remaining = length; do { Brn buf = iReaderUntil->Read(kMaxReadBytes); remaining -= buf.Bytes(); writer.Write(buf); } while (remaining > 0); } } writer.TransferTo(entity); // process entity { const Brx& sid = iHeaderSid.Sid(); LOG(kEvent, "EventSessionUpnp::Run, sid - %.*s seq - %u\n", PBUF(sid), iHeaderSeq.Seq()); } /* defer validating the seq number till now to avoid holding subscription's lock during potentially long-running network reads */ if (subscription->UpdateSequenceNumber(iHeaderSeq.Seq())) { try { ProcessNotification(*subscription, entity); } catch (Exception& ex) { Log::Print("EventSessionUpnp::Run() unexpected exception %s from %s:%u\n", ex.Message(), ex.File(), ex.Line()); ASSERTS(); // ProcessNotification isn't expected to throw } subscription->Unlock(); } else { subscription->SetNotificationError(); } } } catch(HttpError&) { LogError(subscription, "HttpError"); } catch(ReaderError&) { LogError(subscription, "ReaderError"); } catch(WriterError&) { LogError(subscription, "WriterError"); } catch(NetworkError&) { LogError(subscription, "NetworkError"); } catch(XmlError&) { LogError(subscription, "XmlError"); } if (subscription != NULL) { subscription->RemoveRef(); } }
void CpiDeviceLpec::HandleEventedUpdate(const Brx& aUpdate) { Parser parser(aUpdate); Brn lpecId = parser.Next(' '); Bws<128> sid(iDevice->Udn()); sid.Append('-'); sid.Append(lpecId); CpiSubscription* subscription = iCpStack.SubscriptionManager().FindSubscription(sid); if (subscription == NULL) { /* There is a very short window between Subscribe() returning and the new subscription being added to its manager. As a lazy workaround for this, sleep for a short period and retry before rejecting the update */ Thread::Sleep(1000); subscription = iCpStack.SubscriptionManager().FindSubscription(sid); } if (subscription == NULL) { LOG(kLpec, "LPEC: evented update received for unknown subscription - "); LOG(kLpec, sid); LOG(kLpec, "\n"); return; } Brn seqBuf = parser.Next(' '); TUint seq; try { seq = Ascii::Uint(seqBuf); } catch (AsciiError&) { LOG(kLpec, "LPEC: invalid sequence number - "); LOG(kLpec, seqBuf); LOG(kLpec, "in evented update\n"); subscription->RemoveRef(); return; } if (!subscription->UpdateSequenceNumber(seq)) { LOG(kLpec, "LPEC: out of sequence update (%d) for ", seq); LOG(kLpec, sid); LOG(kLpec, "\n"); subscription->SetNotificationError(); subscription->RemoveRef(); return; } IEventProcessor* processor = static_cast<IEventProcessor*>(subscription); processor->EventUpdateStart(); OutputProcessor outputProcessor; try { for (;;) { Brn propName = parser.Next(' '); if (propName.Bytes() == 0) { // processed entire update break; } (void)parser.Next(Lpec::kArgumentDelimiter); Brn propVal = parser.Next(Lpec::kArgumentDelimiter); processor->EventUpdate(propName, propVal, outputProcessor); } processor->EventUpdateEnd(); } catch (AsciiError&) { LOG2(kLpec, kError, "LPEC: Invalid evented update - "); LOG2(kLpec, kError, aUpdate); LOG2(kLpec, kError, "\n"); processor->EventUpdateError(); } subscription->Unlock(); subscription->RemoveRef(); }