using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Best.HTTP.Examples.Helpers;
using Best.HTTP.Response;
using Best.HTTP.Shared;
using Best.HTTP.Shared.Extensions;
using Best.HTTP.Shared.PlatformSupport.Memory;
using UnityEngine;
using UnityEngine.Scripting;
namespace Best.HTTP.Examples
{
///
/// Example demonstrating usage of BeginCache, EndCache functions and HTTPCacheContentWriter class to manually store content for an url.
///
class PopulateCacheManuallySample : SampleBase
{
// Constants passed to the API endpoint stored in _baseAddress
const int FromUserId = 1;
const int ToUserId = 10;
///
/// String template used for generating unique uris for every user stored individually in the local cache.
///
const string LocalCacheUserURLTemplate = "httpcache://userapi/user/{0}";
#pragma warning disable 0649, 0169
[Header("Sample Fields")]
///
/// GameObject that will be used as a root for new UI objects.
///
[SerializeField]
private RectTransform _contentRoot;
///
/// Prefab of a UI object with two Text fields.
///
[SerializeField]
private MultiTextListItem _listItemPrefab;
#pragma warning restore
///
/// Address of the used end point.
///
private string _baseAddress = "https://besthttpwebgldemo.azurewebsites.net/users/{0}/{1}";
protected override async void Start()
{
base.Start();
CreateUIItem("Loading users...");
await PopulateLocalCache();
await LoadUsersFromLocalCache();
}
async Task PopulateLocalCache()
{
try
{
// Load a list of users from the server
var users = await HTTPRequest.CreateGet(string.Format(_baseAddress, FromUserId, ToUserId))
.GetFromJsonResultAsync>();
CreateUIItem($"Received {users.Count} users from /users/{FromUserId}/{ToUserId}");
StoreUsersInLocalCache(users);
}
catch (AsyncHTTPException ex)
{
CreateUIItem($"/Users request failed: {ex.Message}");
}
}
void StoreUsersInLocalCache(List users)
{
// Go over all the users and save them to the local cache with a custom uri
foreach (var user in users)
{
var userUri = new Uri(string.Format(LocalCacheUserURLTemplate, user.Id));
// convert the user object to json string and get the string's bytes
var content = UserToByteArray(user);
// BeginCache expects at least one caching header ("cache-control" with "max-age" directive, "etag", "expires" or "last-modified").
// A cache-control with a max-age directive is a good choice for fabricated urls as the plugin will not try to valide the content's freshness.
var headers = new Dictionary> { { "cache-control", new List { $"max-age={TimeSpan.FromDays(360).TotalSeconds}" } } };
// Start the caching procedure by calling BeginCache and pass all the required parameters.
var cacheWriter = HTTPManager.LocalCache.BeginCache(HTTPMethods.Get, userUri, HTTPStatusCodes.OK, headers, null);
// If the writer is null, something prevents caching, these can be one of the following:
// - caching itself isn't supported
// - userUri doesn't start with 'http'
// - can't acquire a write lock on the resource (a request currently reading or writing the same uri)
// - IO error
// - If the headers passed to BeginCache has a "content-length" header too, BeginCache checks whether it's in the limits of the cache. If it can make enough room to fit a null value will be returned.
if (cacheWriter == null)
{
CreateUIItem($"Can't cache user('{user.Id}')!");
continue;
}
try
{
// Write content to the cache.
// Under the hood this writes to a file, if presumably it will take a long time (large content and/or slow media) it's advised to use a Thread.
cacheWriter.Write(content);
// Finish the caching process by calling EndCache.
// This call will do all housekeeping, like disposing internal streams and releasing the write lock on the uri.
// After this call, requests to this very same uri will load and serve from the local cache.
HTTPManager.LocalCache.EndCache(cacheWriter, true, null);
CreateUIItem($"User cached as '{userUri}'");
}
catch (Exception ex)
{
Debug.LogException(ex);
// We must call EndCache even if there's an error, otherwise not all resources are disposed and
// new attempts to write to this cache entry will fail!
HTTPManager.LocalCache.EndCache(cacheWriter, false, null);
CreateUIItem($"Writing to cache failed with user '{user.Id}'");
}
finally
{
// content's byte[] is borrowed from the BufferPool, it's a nice thing to return it!
BufferPool.Release(content);
}
}
}
///
/// This function will test/emulate requests loading cached individual users
///
async Task LoadUsersFromLocalCache()
{
for (int id = FromUserId; id < ToUserId; id++)
{
// Create and execute a HTTP request to get one individual user's data from the local cache
var user = await HTTPRequest.CreateGet(string.Format(LocalCacheUserURLTemplate, id))
.GetFromJsonResultAsync();
CreateUIItem($"User '{user.Id}' loaded from local cache (uri: '{string.Format(LocalCacheUserURLTemplate, id)}')!");
}
}
///
/// Converts a User object to JSon string and returns with the byte representation of the string
///
BufferSegment UserToByteArray(User user)
{
string json = JSON.LitJson.JsonMapper.ToJson(user);
int length = Encoding.UTF8.GetByteCount(json);
byte[] result = BufferPool.Get(length, true);
Encoding.UTF8.GetBytes(json, 0, json.Length, result, 0);
return result.AsBuffer(length);
}
MultiTextListItem CreateUIItem(string str)
=> Instantiate(this._listItemPrefab, this._contentRoot)
.SetText(str) as MultiTextListItem;
}
[Preserve]
class User
{
[Preserve] public string Id { get; set; }
[Preserve] public string Name { get; set; }
[Preserve] public DateTime Joined { get; set; }
}
}