How do I generate an http ETag in c#?

To generate a static file's ETag which will be compatible with Nginx:

// Generate ETag from file's size and last modification time as unix timestamp in seconds from 1970
public static string MakeEtag(long lastMod, long size)
{
    string etag = '"' + lastMod.ToString("x") + '-' + size.ToString("x") + '"';
    return etag;
}

public static void Main(string[] args)
{
    long lastMod = 1578315296;
    long size = 1047;
    string etag = MakeEtag(lastMod, size);
    Console.WriteLine("ETag: " + etag);
    //=> ETag: "5e132e20-417"
}

See my comment about different ETag schemas


Semantically the ETag should change when the content changes:

So the Hash seems appropriate... but the ETag must also be unique on different URLs and/or different timestamps of duplicate files... so to be on the safe side hash the file, concatenate that with the timestamp of the last modification and the url and hash that again...


The answer is that it depends.

There are two kinds of Etags, weak and strong ones. The weak ones let you do certain conditional operations, but most of them will require strong etags.

The only restriction on a strong etag is that if the representation changes for whatever reason, so does the etag. If it's a file you can generate a hash, but that forces you to keep the entity in-memory while you generate that. Alternatively, you could simply stream the content and add the Etag as a trailing http header, but nearly no server currently supports that (very useful and unloved) piece of functionality.

Ticks have a low resolution, and it is possible that two contiguous writes on the same file will have the same number of ticks, even though the content is different. At that stage, the etag is now invalid if it was strong and you're in muddy water. Same issue as with Last-Modified really. The way most HTTP servers deal with that is to give a file an etag based on multiple of its properties, aka timestamp, size and probably file object ID (inode in apache, probably adding the full path to the object store entry on NT, and IIS adds a counter to that value too so two config changes on a server would generate different etags, in case something changes).

If you use some sort of database, the id + version ought to be a strong etag (provided, again, that the content you ahve is not an aggregate of multiple things that each may change independently without the root changing versions too).

So how to calculate it really depends on your scenario, and persisting a hash of the file on write (before starting serving) will probably help you the most, especially as it's a very useful feature to have for many other reasons.

Tags:

C#

Http

Etag