| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- using Inversion;
- using Astralis;
- using Invercargill;
- using Invercargill.DataStructures;
- namespace Spry {
- public class FileStaticResource : Object, StaticResource {
- private string _name;
- private string _content_type;
- private string _e_tag;
- private ImmutableBuffer<EncodingHeader> _encodings;
- private Dictionary<string, EncodingHeader> _encoding_mapping;
- public string file_path { get; private set; }
- public uint64 header_length { get; private set; }
- public FileStaticResource(string path) throws Error {
- this.file_path = path;
- var stream = new DataInputStream(File.new_for_path(path).read());
- read_header(stream);
- stream.close();
- }
- public string get_best_encoding (Set<string> supported) {
- return _encodings.first_or_default(e => supported.has(e.type))?.type ?? "identity";
- }
- public HttpResult to_result(string encoding, StatusCode status = StatusCode.OK) throws Error {
- return new StreamingResult(this, _encoding_mapping[encoding], status);
- }
- public string name { get { return _name; } }
- public string content_type { get { return _content_type; } }
-
- public string get_etag_for (string encoding) {
- return @"\"$encoding-$_e_tag\"";
- }
- private void read_header(DataInputStream stream) throws Error {
- var magic = new uint8[8];
- stream.read(magic);
- if(!Wrap.array(magic).equals(Iterate.these<uint8>('s', 'p', 'r', 'y', '-', 's', 'r', 0))) {
- throw new IOError.INVALID_DATA("Incorrect magic number on Spry Static Resource file.");
- }
- _name = read_string_field(stream);
- _content_type = read_string_field(stream);
-
- var hash_len = (size_t)64;
- var hash_composition = new ByteComposition();
- while(hash_composition.read_in (stream.read_bytes(hash_len), ref hash_len));
- _e_tag = hash_composition.to_base64();
- var encodings = new Buffer<EncodingHeader>(stream.read_byte());
- for(var i = 0; i < encodings.length; i++) {
- encodings[i] = new EncodingHeader(stream);
- }
- _encodings = encodings
- .order_by<uint64?>(e => e.size)
- .to_immutable_buffer();
- _encoding_mapping = encodings
- .to_dictionary<string>(e => e.type);
- header_length = stream.tell();
- if(!_encoding_mapping.has("identity")) {
- throw new IOError.INVALID_DATA("Spry Static Resource file does not contain required \'identity\' encoding.");
- }
- }
- private static string read_string_field(DataInputStream stream) throws Error {
- var len = (size_t)stream.read_byte();
- var composition = new ByteComposition();
- while(composition.read_in (stream.read_bytes(len), ref len));
- return composition.to_raw_string();
- }
- private class EncodingHeader {
- public string type;
- public uint64 offset;
- public uint64 size;
- public EncodingHeader(DataInputStream stream) throws Error {
- type = read_string_field(stream);
- offset = stream.read_uint64();
- size = stream.read_uint64();
- }
- }
- private class StreamingResult : HttpResult {
- private FileStaticResource resource;
- private EncodingHeader encoding;
- public StreamingResult(FileStaticResource resource, EncodingHeader encoding, StatusCode status) {
- base(status);
- this.resource = resource;
- this.encoding = encoding;
- set_header("Content-Type", resource.content_type);
- set_header("Content-Encoding", encoding.type);
- set_header("Content-Length", encoding.size.to_string());
- set_header("ETag", resource.get_etag_for(encoding.type));
- }
- public async override void send_body(Astralis.AsyncOutput output) throws Error {
- var stream = yield File.new_for_path(resource.file_path).read_async();
- var data_len = encoding.size;
- stream.seek((int64)resource.header_length, SeekType.SET);
-
- while(data_len > 0) {
- var to_read = (size_t)uint64.min(data_len, 1024 * 1024 * 4);
- var bytes = yield stream.read_bytes_async(to_read);
- data_len -= bytes.length;
- yield output.write(Wrap.bytes(bytes));
- }
- }
- }
- }
- }
|