mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Cache HTTP headers and status
This commit is contained in:
		
							parent
							
								
									288a32cc1e
								
							
						
					
					
						commit
						3ff41f2b43
					
				| @ -4,7 +4,7 @@ const Database = require('../Database') | ||||
| 
 | ||||
| class ApiCacheManager { | ||||
| 
 | ||||
|   defaultCacheOptions = { max: 1000, maxSize: 10 * 1000 * 1000, sizeCalculation: item => item.length } | ||||
|   defaultCacheOptions = { max: 1000, maxSize: 10 * 1000 * 1000, sizeCalculation: item => (item.body.length + JSON.stringify(item.headers).length) } | ||||
|   defaultTtlOptions = { ttl: 30 * 60 * 1000 } | ||||
| 
 | ||||
|   constructor(cache = new LRUCache(this.defaultCacheOptions), ttlOptions = this.defaultTtlOptions) { | ||||
| @ -30,17 +30,20 @@ class ApiCacheManager { | ||||
|       const cached = this.cache.get(stringifiedKey) | ||||
|       if (cached) { | ||||
|         Logger.debug(`[ApiCacheManager] Cache hit: ${stringifiedKey}`) | ||||
|         res.send(cached) | ||||
|         res.set(cached.headers) | ||||
|         res.status(cached.statusCode) | ||||
|         res.send(cached.body) | ||||
|         return | ||||
|       } | ||||
|       res.originalSend = res.send | ||||
|       res.send = (body) => { | ||||
|         Logger.debug(`[ApiCacheManager] Cache miss: ${stringifiedKey}`) | ||||
|         const cached = { body, headers: res.getHeaders(), statusCode: res.statusCode } | ||||
|         if (key.url.search(/^\/libraries\/.*?\/personalized/) !== -1) { | ||||
|           Logger.debug(`[ApiCacheManager] Caching with ${this.ttlOptions.ttl} ms TTL`) | ||||
|           this.cache.set(stringifiedKey, body, this.ttlOptions) | ||||
|           this.cache.set(stringifiedKey, cached, this.ttlOptions) | ||||
|         } else { | ||||
|           this.cache.set(stringifiedKey, body) | ||||
|           this.cache.set(stringifiedKey, cached) | ||||
|         } | ||||
|         res.originalSend(body) | ||||
|       } | ||||
|  | ||||
| @ -13,14 +13,14 @@ describe('ApiCacheManager', () => { | ||||
|   beforeEach(() => { | ||||
|     cache = { get: sinon.stub(), set: sinon.spy() }  | ||||
|     req = { user: { username: 'testUser' }, url: '/test-url' } | ||||
|     res = { send: sinon.spy() } | ||||
|     res = { send: sinon.spy(), getHeaders: sinon.stub(), statusCode: 200, status: sinon.spy(), set: sinon.spy() } | ||||
|     next = sinon.spy() | ||||
|   }) | ||||
| 
 | ||||
|   describe('middleware', () => { | ||||
|     it('should send cached data if available', () => { | ||||
|       // Arrange
 | ||||
|       const cachedData = { data: 'cached data' } | ||||
|       const cachedData = { body: 'cached data', headers: { 'content-type': 'application/json' }, statusCode: 200 } | ||||
|       cache.get.returns(cachedData) | ||||
|       const key = JSON.stringify({ user: req.user.username, url: req.url }) | ||||
|       manager = new ApiCacheManager(cache) | ||||
| @ -31,8 +31,12 @@ describe('ApiCacheManager', () => { | ||||
|       // Assert
 | ||||
|       expect(cache.get.calledOnce).to.be.true | ||||
|       expect(cache.get.calledWith(key)).to.be.true | ||||
|       expect(res.set.calledOnce).to.be.true | ||||
|       expect(res.set.calledWith(cachedData.headers)).to.be.true | ||||
|       expect(res.status.calledOnce).to.be.true | ||||
|       expect(res.status.calledWith(cachedData.statusCode)).to.be.true | ||||
|       expect(res.send.calledOnce).to.be.true | ||||
|       expect(res.send.calledWith(cachedData)).to.be.true | ||||
|       expect(res.send.calledWith(cachedData.body)).to.be.true | ||||
|       expect(res.originalSend).to.be.undefined | ||||
|       expect(next.called).to.be.false | ||||
|       expect(cache.set.called).to.be.false | ||||
| @ -41,13 +45,17 @@ describe('ApiCacheManager', () => { | ||||
|     it('should cache and send response if data is not cached', () => { | ||||
|       // Arrange
 | ||||
|       cache.get.returns(null) | ||||
|       const responseData = { data: 'response data' } | ||||
|       const headers = { 'content-type': 'application/json' } | ||||
|       res.getHeaders.returns(headers) | ||||
|       const body = 'response data' | ||||
|       const statusCode = 200 | ||||
|       const responseData = { body, headers, statusCode } | ||||
|       const key = JSON.stringify({ user: req.user.username, url: req.url }) | ||||
|       manager = new ApiCacheManager(cache) | ||||
| 
 | ||||
|       // Act
 | ||||
|       manager.middleware(req, res, next) | ||||
|       res.send(responseData) | ||||
|       res.send(body) | ||||
| 
 | ||||
|       // Assert
 | ||||
|       expect(cache.get.calledOnce).to.be.true | ||||
| @ -56,13 +64,17 @@ describe('ApiCacheManager', () => { | ||||
|       expect(cache.set.calledOnce).to.be.true | ||||
|       expect(cache.set.calledWith(key, responseData)).to.be.true | ||||
|       expect(res.originalSend.calledOnce).to.be.true | ||||
|       expect(res.originalSend.calledWith(responseData)).to.be.true | ||||
|       expect(res.originalSend.calledWith(body)).to.be.true | ||||
|     }) | ||||
| 
 | ||||
|     it('should cache personalized response with 30 minutes TTL', () => { | ||||
|       // Arrange
 | ||||
|       cache.get.returns(null) | ||||
|       const responseData = { data: 'personalized data' } | ||||
|       const headers = { 'content-type': 'application/json' } | ||||
|       res.getHeaders.returns(headers) | ||||
|       const body = 'personalized data' | ||||
|       const statusCode = 200 | ||||
|       const responseData = { body, headers, statusCode } | ||||
|       req.url = '/libraries/id/personalized' | ||||
|       const key = JSON.stringify({ user: req.user.username, url: req.url }) | ||||
|       const ttlOptions = { ttl: 30 * 60 * 1000 } | ||||
| @ -70,7 +82,7 @@ describe('ApiCacheManager', () => { | ||||
| 
 | ||||
|       // Act
 | ||||
|       manager.middleware(req, res, next) | ||||
|       res.send(responseData) | ||||
|       res.send(body) | ||||
| 
 | ||||
|       // Assert
 | ||||
|       expect(cache.get.calledOnce).to.be.true | ||||
| @ -79,7 +91,7 @@ describe('ApiCacheManager', () => { | ||||
|       expect(cache.set.calledOnce).to.be.true | ||||
|       expect(cache.set.calledWith(key, responseData, ttlOptions)).to.be.true | ||||
|       expect(res.originalSend.calledOnce).to.be.true | ||||
|       expect(res.originalSend.calledWith(responseData)).to.be.true | ||||
|       expect(res.originalSend.calledWith(body)).to.be.true | ||||
|     }) | ||||
|   }) | ||||
| }) | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user