/* * Decode the DMP header. If data is null we're expected to use the last * header we got. * @param headers the HeaderSet to add to * @param data a pointer to the data * @param length length of the data * @returns true if successful, false otherwise */ bool DMPInflator::DecodeHeader(HeaderSet &headers, const uint8_t *data, unsigned int length, unsigned int &bytes_used) { if (data) { // the header bit was set, decode it if (length >= DMPHeader::DMP_HEADER_SIZE) { DMPHeader header(*data); m_last_header = header; m_last_header_valid = true; headers.SetDMPHeader(header); bytes_used = DMPHeader::DMP_HEADER_SIZE; return true; } bytes_used = 0; return false; } // use the last header if it exists bytes_used = 0; if (!m_last_header_valid) { OLA_WARN << "Missing DMP Header data"; return false; } headers.SetDMPHeader(m_last_header); return true; }
/** * Tries to construct an HTTP request object from the given string. * @return Returns the object representing the request on success, or NULL if * the string could not be parsed. If set, consumed will be filled with * the amount of data that was consumed from the string to parse the request. */ Request* Request::fromString(const string &str, unsigned int *consumed) { unsigned int cons = 0; //Parse the first line which consists of the request type. size_t firstCRLF = str.find("\r\n"); if (firstCRLF == string::npos) return NULL; string firstLine = str.substr(0, firstCRLF); cons = firstCRLF + 2; size_t methodPos = firstLine.find(" "); if (methodPos == string::npos) return NULL; string method = firstLine.substr(0, methodPos); Type type; if (method == "GET") type = kGET; else if (method == "POST") type = kPOST; else if (method == "DELETE") type = kDELETE; else if (method == "PUT") type = kPUT; else { throw new GenericError(string("Received HTTP request with unknown method '") + method + "'."); } size_t pathPos = firstLine.find(" ", methodPos + 1); if (pathPos == string::npos) return NULL; string path = firstLine.substr(methodPos + 1, pathPos - methodPos - 1); //Parse the header. unsigned int hsConsumed = 0; HeaderSet *hs = HeaderSet::fromString(str.substr(firstCRLF + 2), &hsConsumed); cons += hsConsumed; if (!hs) return NULL; //Read the content. int expectedLength = 0; if (hs->has("Content-Length")) { expectedLength = atoi(hs->get("Content-Length").c_str()); if (expectedLength > str.size() - cons) { return NULL; } } //Create the request object. Request *req = new Request; req->type = type; req->path = path; req->headers = *hs; req->content = str.substr(cons, expectedLength); cons += expectedLength; if (consumed) *consumed = cons; return req; }
/* * Handle a E1.33 Status PDU. */ bool E133StatusInflator::HandlePDUData(uint32_t vector, const HeaderSet &headers, const uint8_t *data, unsigned int pdu_len) { unsigned int size = std::min( pdu_len, static_cast<unsigned int>(ola::e133::MAX_E133_STATUS_STRING_SIZE)); string description(reinterpret_cast<const char*>(&data[0]), size); m_handler->Run(&headers.GetTransportHeader(), &headers.GetE133Header(), static_cast<uint16_t>(vector), description); return true; }
/* * Handle a DMP PDU for E1.33. */ bool RDMInflator::HandlePDUData(uint32_t vector, const HeaderSet &headers, const uint8_t *data, unsigned int pdu_len) { if (vector != VECTOR_RDMNET_DATA) { OLA_INFO << "Not a RDM message, vector was " << vector; return true; } string rdm_message(reinterpret_cast<const char*>(&data[0]), pdu_len); E133Header e133_header = headers.GetE133Header(); if (m_rdm_handler.get()) { m_rdm_handler->Run(&headers.GetTransportHeader(), &e133_header, rdm_message); } else { OLA_WARN << "No RDM handler defined!"; } return true; }
/* * Check that the header set works */ void HeaderSetTest::testHeaderSet() { HeaderSet headers; RootHeader root_header; E131Header e131_header("e131", 1, 2, 6001); E133Header e133_header("foo", 1, 2050); DMPHeader dmp_header(false, false, NON_RANGE, ONE_BYTES); // test the root header component CID cid = CID::Generate(); root_header.SetCid(cid); headers.SetRootHeader(root_header); OLA_ASSERT(root_header == headers.GetRootHeader()); // test the E1.31 header component headers.SetE131Header(e131_header); OLA_ASSERT(e131_header == headers.GetE131Header()); // test the E1.33 header component headers.SetE133Header(e133_header); OLA_ASSERT(e133_header == headers.GetE133Header()); // test the DMP headers component headers.SetDMPHeader(dmp_header); OLA_ASSERT(dmp_header == headers.GetDMPHeader()); // test assign HeaderSet headers2 = headers; OLA_ASSERT(root_header == headers2.GetRootHeader()); OLA_ASSERT(e131_header == headers2.GetE131Header()); OLA_ASSERT(e133_header == headers2.GetE133Header()); OLA_ASSERT(dmp_header == headers2.GetDMPHeader()); OLA_ASSERT(headers2 == headers); // test copy HeaderSet headers3(headers); OLA_ASSERT(root_header == headers3.GetRootHeader()); OLA_ASSERT(e131_header == headers3.GetE131Header()); OLA_ASSERT(e133_header == headers3.GetE133Header()); OLA_ASSERT(dmp_header == headers3.GetDMPHeader()); OLA_ASSERT(headers3 == headers); }
/* * Verify a PDU is what we expected */ bool MockDMPInflator::HandlePDUData(uint32_t vector, HeaderSet &headers, const uint8_t *data, unsigned int pdu_len) { DMPHeader header = headers.GetDMPHeader(); CPPUNIT_ASSERT_EQUAL(expected_vector, vector); CPPUNIT_ASSERT_EQUAL(expected_virtual, header.IsVirtual()); CPPUNIT_ASSERT_EQUAL(expected_relative, header.IsRelative()); CPPUNIT_ASSERT(expected_type == header.Type()); CPPUNIT_ASSERT(expected_size == header.Size()); if (vector == DMP_GET_PROPERTY_VECTOR || vector == DMP_SET_PROPERTY_VECTOR) { unsigned int length = pdu_len; const BaseDMPAddress *addr = DecodeAddress(header.Size(), header.Type(), data, length); CPPUNIT_ASSERT(addr); CPPUNIT_ASSERT_EQUAL(expected_start, addr->Start()); CPPUNIT_ASSERT_EQUAL(expected_increment, addr->Increment()); CPPUNIT_ASSERT_EQUAL(expected_number, addr->Number()); delete addr; } return true; }
/* * Handle a DMP PDU for E1.31. */ bool DMPE131Inflator::HandlePDUData(uint32_t vector, const HeaderSet &headers, const uint8_t *data, unsigned int pdu_len) { if (vector != ola::acn::DMP_SET_PROPERTY_VECTOR) { OLA_INFO << "not a set property msg: " << vector; return true; } E131Header e131_header = headers.GetE131Header(); UniverseHandlers::iterator universe_iter = m_handlers.find(e131_header.Universe()); if (e131_header.PreviewData() && m_ignore_preview) { OLA_DEBUG << "Ignoring preview data"; return true; } if (universe_iter == m_handlers.end()) return true; DMPHeader dmp_header = headers.GetDMPHeader(); if (!dmp_header.IsVirtual() || dmp_header.IsRelative() || dmp_header.Size() != TWO_BYTES || dmp_header.Type() != RANGE_EQUAL) { OLA_INFO << "malformed E1.31 dmp header " << dmp_header.Header(); return true; } if (e131_header.Priority() > MAX_PRIORITY) { OLA_INFO << "Priority " << static_cast<int>(e131_header.Priority()) << " is greater than the max priority (" << static_cast<int>(MAX_PRIORITY) << "), ignoring data"; return true; } unsigned int available_length = pdu_len; std::auto_ptr<const BaseDMPAddress> address( DecodeAddress(dmp_header.Size(), dmp_header.Type(), data, &available_length)); if (!address.get()) { OLA_INFO << "DMP address parsing failed, the length is probably too small"; return true; } if (address->Increment() != 1) { OLA_INFO << "E1.31 DMP packet with increment " << address->Increment() << ", disarding"; return true; } unsigned int length_remaining = pdu_len - available_length; int start_code = -1; if (e131_header.UsingRev2()) start_code = static_cast<int>(address->Start()); else if (length_remaining && address->Number()) start_code = *(data + available_length); // The only time we want to continue processing a non-0 start code is if it // contains a Terminate message. if (start_code && !e131_header.StreamTerminated()) { OLA_INFO << "Skipping packet with non-0 start code: " << start_code; return true; } DmxBuffer *target_buffer; if (!TrackSourceIfRequired(&universe_iter->second, headers, &target_buffer)) { // no need to continue processing return true; } // Reaching here means that we actually have new data and we should merge. if (target_buffer && start_code == 0) { unsigned int channels = std::min(length_remaining, address->Number()); if (e131_header.UsingRev2()) target_buffer->Set(data + available_length, channels); else target_buffer->Set(data + available_length + 1, channels - 1); } if (universe_iter->second.priority) *universe_iter->second.priority = universe_iter->second.active_priority; // merge the sources switch (universe_iter->second.sources.size()) { case 0: universe_iter->second.buffer->Reset(); break; case 1: universe_iter->second.buffer->Set( universe_iter->second.sources[0].buffer); universe_iter->second.closure->Run(); break; default: // HTP Merge universe_iter->second.buffer->Reset(); std::vector<dmx_source>::const_iterator source_iter = universe_iter->second.sources.begin(); for (; source_iter != universe_iter->second.sources.end(); ++source_iter) universe_iter->second.buffer->HTPMerge(source_iter->buffer); universe_iter->second.closure->Run(); } return true; }
/* * Check if this source is operating at the highest priority for this universe. * This takes care of tracking all sources for a universe at the active * priority. * @param universe_data the universe_handler struct for this universe, * @param HeaderSet the set of headers in this packet * @param buffer, if set to a non-NULL pointer, the caller should copy the data * in the buffer. * @returns true if we should remerge the data, false otherwise. */ bool DMPE131Inflator::TrackSourceIfRequired( universe_handler *universe_data, const HeaderSet &headers, DmxBuffer **buffer) { *buffer = NULL; // default the buffer to NULL ola::TimeStamp now; m_clock.CurrentTime(&now); const E131Header &e131_header = headers.GetE131Header(); uint8_t priority = e131_header.Priority(); vector<dmx_source> &sources = universe_data->sources; vector<dmx_source>::iterator iter = sources.begin(); while (iter != sources.end()) { if (iter->cid != headers.GetRootHeader().GetCid()) { TimeStamp expiry_time = iter->last_heard_from + EXPIRY_INTERVAL; if (now > expiry_time) { OLA_INFO << "source " << iter->cid.ToString() << " has expired"; iter = sources.erase(iter); continue; } } iter++; } if (sources.empty()) universe_data->active_priority = 0; for (iter = sources.begin(); iter != sources.end(); ++iter) { if (iter->cid == headers.GetRootHeader().GetCid()) break; } if (iter == sources.end()) { // This is an untracked source if (e131_header.StreamTerminated() || priority < universe_data->active_priority) return false; if (priority > universe_data->active_priority) { OLA_INFO << "Raising priority for universe " << e131_header.Universe() << " from " << static_cast<int>(universe_data->active_priority) << " to " << static_cast<int>(priority); sources.clear(); universe_data->active_priority = priority; } if (sources.size() == MAX_MERGE_SOURCES) { // TODO(simon): flag this in the export map OLA_WARN << "Max merge sources reached for universe " << e131_header.Universe() << ", " << headers.GetRootHeader().GetCid().ToString() << " won't be tracked"; return false; } else { OLA_INFO << "Added new E1.31 source: " << headers.GetRootHeader().GetCid().ToString(); dmx_source new_source; new_source.cid = headers.GetRootHeader().GetCid(); new_source.sequence = e131_header.Sequence(); new_source.last_heard_from = now; iter = sources.insert(sources.end(), new_source); *buffer = &iter->buffer; return true; } } else { // We already know about this one, check the seq # int8_t seq_diff = static_cast<int8_t>(e131_header.Sequence() - iter->sequence); if (seq_diff <= 0 && seq_diff > SEQUENCE_DIFF_THRESHOLD) { OLA_INFO << "Old packet received, ignoring, this # " << static_cast<int>(e131_header.Sequence()) << ", last " << static_cast<int>(iter->sequence); return false; } iter->sequence = e131_header.Sequence(); if (e131_header.StreamTerminated()) { OLA_INFO << "CID " << headers.GetRootHeader().GetCid().ToString() << " sent a termination for universe " << e131_header.Universe(); sources.erase(iter); if (sources.empty()) universe_data->active_priority = 0; // We need to trigger a merge here else the buffer will be stale, we keep // the buffer as NULL though so we don't use the data. return true; } iter->last_heard_from = now; if (priority < universe_data->active_priority) { if (sources.size() == 1) { universe_data->active_priority = priority; } else { sources.erase(iter); return true; } } else if (priority > universe_data->active_priority) { // new active priority universe_data->active_priority = priority; if (sources.size() != 1) { // clear all sources other than this one dmx_source this_source = *iter; sources.clear(); iter = sources.insert(sources.end(), this_source); } } *buffer = &iter->buffer; return true; } }
/** * This runs the on_data callback if we have one */ bool RootInflator::PostHeader(uint32_t, const HeaderSet &headers) { if (m_on_data.get()) m_on_data->Run(headers.GetTransportHeader()); return true; }