Skip to content

Write the usage sharing record in the specified form #8

Open

Description

@jwrosewell

Usage sharing is being aligned across every language to the record the .NET packages write, which draft specifications#65 describes byte for byte. The Python record, read at 1342d3e, differs from it in these ways.

  1. Document. There is no byte order mark and no XML declaration, and the root is <Devices version="1.1"> where the specification has <Devices> (share_usage.py#L194-L199).
  2. Element order. Version, Product, FlowElement, Language, LanguageVersion and Platform come first, then SessionId, Sequence, ClientIP and DateSent. The specification starts with SessionId, Sequence, DateSent and ClientIP (share_usage.py#L221-L236).
  3. ServerIP is never written (share_usage.py#L125-L147).
  4. DateSent is datetime.datetime.now().isoformat(), local time with microseconds such as 2026-11-03T14:11:33.760848, where the specification has UTC as yyyy-MM-ddTHH:mm:ss (share_usage.py#L235).
  5. Sequence is empty when it is absent or not a number, where the specification has 0, and is not limited to 32 bits (share_usage.py#L308-L316).
  6. Empty elements are written as <SessionId></SessionId> rather than <SessionId />, and SessionId and ClientIP are written without escaping (share_usage.py#L228-L232).
  7. The three special keys are matched with case, so Query.Session-Id becomes an ordinary entry (share_usage.py#L301-L308).
  8. FlowElement holds the bare class name, such as ShareUsage, rather than the fully qualified name (share_usage.py#L154).
  9. Values are element text with &, ", ', < and > escaped, never CDATA, and the Name attribute and element names are not escaped at all (share_usage.py#L244-L255).
  10. Truncation cuts at 512 code points and marks the entry truncated="true". The cut comes after escaping, so it can split an entity such as &amp; and break the XML. The specification cuts at 10,000 UTF-16 code units behind the [TRUNCATED BY USAGE SHARING] prefix (share_usage.py#L349-L358).
  11. Character replacement turns every character outside U+0020 to U+00FF into U+FFFD, tab, line feed and carriage return included, and marks the entry replaced="true". Names are not checked. The specification replaces only control characters other than tab, line feed and carriage return, writes each as \x and four lowercase hex digits, and adds <BadSchema>true</BadSchema> (share_usage.py#L325-L367).
  12. A key with no . raises IndexError in the Evidence filter, so no record is written and, with default settings, process() raises the error to the caller. The specification writes such a key as an element named by the whole key (share_usage_evidencekeyfilter.py#L73-L76).
  13. A response other than 200 is not treated as a failure, and its status and content are not logged. Only exceptions are (share_usage.py#L198-L210).

Also found, outside the record writer:

  • When a cookie, allow list or block list is set, a cookie not starting 51D_ raises AttributeError, because self.include_session is never set (share_usage_evidencekeyfilter.py#L52-L103).
  • The Flask helper lowercases header names before they become Evidence, so Name is lower case whatever the client sent, where the specification keeps the key's case (web.py#L41).

What to change

Write the record exactly as the Processing section in specifications#65 describes, so the same Evidence gives the same bytes as the .NET packages, apart from DateSent and the values that name the runtime.

The pull request goes into the rc branch, not main, because changes are collected there and released together.

Done when

A test writes a record from fixed Evidence, the specification's example among it, and compares the document byte for byte with the expected text. The cases include the byte order mark and declaration, an absent session id (<SessionId />), an absent and a non-numeric sequence (0), a value of more than 10,000 UTF-16 code units, a control character, a value containing ]]> and a key with no ..

The same change for the Rust crates is 51Degrees/51degrees-rust#232.

Activity

  1. jwrosewell commented on Nov 9, 2026

    @jwrosewell
    ContributorAuthor

    The record to write is the one in the Processing section of usage-sharing-element.md on rc, not the one this issue first pointed to. That draft, specifications#65, is closed and was not adopted.

    So a replaced character marks its element escaped="true", a shortened value marks it truncated="true", and there is no CDATA and no BadSchema. .NET keeps its current record for now, as reference-implementation-notes.md records, and moves to the specification's record later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions