respect cache header

This commit is contained in:
Florian Dold 2020-09-02 12:23:11 +05:30
parent 0ffea74ad5
commit 659e9cdbe6
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 42 additions and 4 deletions

View File

@ -43,16 +43,19 @@ import {
WALLET_CACHE_BREAKER_CLIENT_VERSION, WALLET_CACHE_BREAKER_CLIENT_VERSION,
WALLET_EXCHANGE_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION,
} from "./versions"; } from "./versions";
import { getTimestampNow, Duration } from "../util/time"; import { getTimestampNow, Duration, isTimestampExpired } from "../util/time";
import { compare } from "../util/libtoolVersion"; import { compare } from "../util/libtoolVersion";
import { createRecoupGroup, processRecoupGroup } from "./recoup"; import { createRecoupGroup, processRecoupGroup } from "./recoup";
import { TalerErrorCode } from "../TalerErrorCode"; import { TalerErrorCode } from "../TalerErrorCode";
import { import {
readSuccessResponseJsonOrThrow, readSuccessResponseJsonOrThrow,
readSuccessResponseTextOrThrow, readSuccessResponseTextOrThrow,
getExpiryTimestamp,
} from "../util/http"; } from "../util/http";
import { Logger } from "../util/logging"; import { Logger } from "../util/logging";
import { URL } from "../util/url"; import { URL } from "../util/url";
import { reconcileReserveHistory } from "../util/reserveHistoryUtil";
import { checkDbInvariant } from "../util/invariants";
const logger = new Logger("exchanges.ts"); const logger = new Logger("exchanges.ts");
@ -195,6 +198,7 @@ async function updateExchangeWithKeys(
masterPublicKey: exchangeKeysJson.master_public_key, masterPublicKey: exchangeKeysJson.master_public_key,
protocolVersion: protocolVersion, protocolVersion: protocolVersion,
signingKeys: exchangeKeysJson.signkeys, signingKeys: exchangeKeysJson.signkeys,
nextUpdateTime: getExpiryTimestamp(resp),
}; };
r.updateStatus = ExchangeUpdateStatus.FetchWire; r.updateStatus = ExchangeUpdateStatus.FetchWire;
r.lastError = undefined; r.lastError = undefined;
@ -459,7 +463,7 @@ async function updateExchangeFromUrlImpl(
const now = getTimestampNow(); const now = getTimestampNow();
baseUrl = canonicalizeBaseUrl(baseUrl); baseUrl = canonicalizeBaseUrl(baseUrl);
const r = await ws.db.get(Stores.exchanges, baseUrl); let r = await ws.db.get(Stores.exchanges, baseUrl);
if (!r) { if (!r) {
const newExchangeRecord: ExchangeRecord = { const newExchangeRecord: ExchangeRecord = {
builtIn: false, builtIn: false,
@ -476,7 +480,6 @@ async function updateExchangeFromUrlImpl(
termsOfServiceAcceptedTimestamp: undefined, termsOfServiceAcceptedTimestamp: undefined,
termsOfServiceLastEtag: undefined, termsOfServiceLastEtag: undefined,
termsOfServiceText: undefined, termsOfServiceText: undefined,
updateDiff: undefined,
}; };
await ws.db.put(Stores.exchanges, newExchangeRecord); await ws.db.put(Stores.exchanges, newExchangeRecord);
} else { } else {
@ -498,6 +501,16 @@ async function updateExchangeFromUrlImpl(
}); });
} }
r = await ws.db.get(Stores.exchanges, baseUrl);
checkDbInvariant(!!r);
const t = r.details?.nextUpdateTime;
if (!forceNow && t && !isTimestampExpired(t)) {
logger.trace("using cached exchange info");
return r;
}
await updateExchangeWithKeys(ws, baseUrl); await updateExchangeWithKeys(ws, baseUrl);
await updateExchangeWithWireInfo(ws, baseUrl); await updateExchangeWithWireInfo(ws, baseUrl);
await updateExchangeWithTermsOfService(ws, baseUrl); await updateExchangeWithTermsOfService(ws, baseUrl);

View File

@ -539,6 +539,11 @@ export interface ExchangeDetails {
* Timestamp for last update. * Timestamp for last update.
*/ */
lastUpdateTime: Timestamp; lastUpdateTime: Timestamp;
/**
* When should we next update the information about the exchange?
*/
nextUpdateTime: Timestamp;
} }
export enum ExchangeUpdateStatus { export enum ExchangeUpdateStatus {

View File

@ -26,7 +26,7 @@ import { Codec } from "./codec";
import { OperationFailedError, makeErrorDetails } from "../operations/errors"; import { OperationFailedError, makeErrorDetails } from "../operations/errors";
import { TalerErrorCode } from "../TalerErrorCode"; import { TalerErrorCode } from "../TalerErrorCode";
import { Logger } from "./logging"; import { Logger } from "./logging";
import { Duration } from "./time"; import { Duration, Timestamp, getTimestampNow } from "./time";
const logger = new Logger("http.ts"); const logger = new Logger("http.ts");
@ -253,3 +253,19 @@ export async function readSuccessResponseTextOrThrow<T>(
} }
throwUnexpectedRequestError(httpResponse, r.talerErrorResponse); throwUnexpectedRequestError(httpResponse, r.talerErrorResponse);
} }
/**
* Get the timestamp at which the response's content is considered expired.
*/
export function getExpiryTimestamp(httpResponse: HttpResponse): Timestamp {
const expiryDateMs = new Date(
httpResponse.headers.get("expiry") ?? "",
).getTime();
if (Number.isNaN(expiryDateMs)) {
return getTimestampNow();
} else {
return {
t_ms: expiryDateMs,
}
}
}

View File

@ -46,6 +46,10 @@ export function getTimestampNow(): Timestamp {
}; };
} }
export function isTimestampExpired(t: Timestamp) {
return timestampCmp(t, getTimestampNow()) <= 0;
}
export function getDurationRemaining( export function getDurationRemaining(
deadline: Timestamp, deadline: Timestamp,
now = getTimestampNow(), now = getTimestampNow(),