diff --git a/packages/nephele/src/Methods/COPY.ts b/packages/nephele/src/Methods/COPY.ts index d07c220..857de2d 100644 --- a/packages/nephele/src/Methods/COPY.ts +++ b/packages/nephele/src/Methods/COPY.ts @@ -41,6 +41,12 @@ export class COPY extends Method { response.locals.baseUrl ); + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + if (!destination) { throw new BadRequestError('Destination header is required.'); } diff --git a/packages/nephele/src/Methods/GET_HEAD.ts b/packages/nephele/src/Methods/GET_HEAD.ts index c1cb673..feed470 100644 --- a/packages/nephele/src/Methods/GET_HEAD.ts +++ b/packages/nephele/src/Methods/GET_HEAD.ts @@ -25,6 +25,13 @@ export class GET_HEAD extends Method { url, response.locals.baseUrl ); + + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + const properties = await resource.getProperties(); const etagPromise = resource.getEtag(); const lastModifiedPromise = properties.get('getlastmodified'); diff --git a/packages/nephele/src/Methods/LOCK.ts b/packages/nephele/src/Methods/LOCK.ts index ea86af6..ef24d8d 100644 --- a/packages/nephele/src/Methods/LOCK.ts +++ b/packages/nephele/src/Methods/LOCK.ts @@ -48,6 +48,12 @@ export class LOCK extends Method { } } + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + const timeoutRequests = timeoutHeader.split(/,\s+/); let timeout = Infinity; for (let curTreq of timeoutRequests) { diff --git a/packages/nephele/src/Methods/MKCOL.ts b/packages/nephele/src/Methods/MKCOL.ts index ecd459d..3160d42 100644 --- a/packages/nephele/src/Methods/MKCOL.ts +++ b/packages/nephele/src/Methods/MKCOL.ts @@ -5,6 +5,7 @@ import { ForbiddenError, LockedError, MediaTypeNotSupportedError, + ResourceExistsError, ResourceNotFoundError, ResourceTreeNotCompleteError, } from '../Errors/index.js'; @@ -22,6 +23,12 @@ export class MKCOL extends Method { response.locals.baseUrl ); + if (!url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + try { const parent = await this.getParentResource(request, response, resource); if (!(await parent?.isCollection())) { @@ -80,7 +87,14 @@ export class MKCOL extends Method { Date: new Date().toUTCString(), }); - await resource.create(response.locals.user); + try { + await resource.create(response.locals.user); + } catch (e: any) { + if (e instanceof ResourceExistsError) { + response.removeHeader('Content-Location'); + } + throw e; + } response.status(201); // Created response.set({ diff --git a/packages/nephele/src/Methods/MOVE.ts b/packages/nephele/src/Methods/MOVE.ts index 9a1c3dc..a4a61b5 100644 --- a/packages/nephele/src/Methods/MOVE.ts +++ b/packages/nephele/src/Methods/MOVE.ts @@ -44,6 +44,12 @@ export class MOVE extends Method { response.locals.baseUrl ); + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + if (!destination) { throw new BadRequestError('Destination header is required.'); } diff --git a/packages/nephele/src/Methods/PROPFIND.ts b/packages/nephele/src/Methods/PROPFIND.ts index 4754951..eca90c7 100644 --- a/packages/nephele/src/Methods/PROPFIND.ts +++ b/packages/nephele/src/Methods/PROPFIND.ts @@ -29,6 +29,12 @@ export class PROPFIND extends Method { response.locals.baseUrl ); + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + if (!['0', '1', 'infinity'].includes(depth)) { throw new BadRequestError( 'Depth header must be one of "0", "1", or "infinity".' diff --git a/packages/nephele/src/Methods/PROPPATCH.ts b/packages/nephele/src/Methods/PROPPATCH.ts index fe2f4b5..28e4e14 100644 --- a/packages/nephele/src/Methods/PROPPATCH.ts +++ b/packages/nephele/src/Methods/PROPPATCH.ts @@ -35,6 +35,13 @@ export class PROPPATCH extends Method { url, response.locals.baseUrl ); + + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + const props = await resource.getProperties(); const xmlBody = await this.getBodyXML(request, response); diff --git a/packages/nephele/src/Methods/PUT.ts b/packages/nephele/src/Methods/PUT.ts index 0bcbcef..7773f70 100644 --- a/packages/nephele/src/Methods/PUT.ts +++ b/packages/nephele/src/Methods/PUT.ts @@ -35,6 +35,12 @@ export class PUT extends Method { } } + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + try { const parent = await this.getParentResource(request, response, resource); if (!(await parent?.isCollection())) { diff --git a/packages/nephele/src/Methods/UNLOCK.ts b/packages/nephele/src/Methods/UNLOCK.ts index ce96d2c..b9fc973 100644 --- a/packages/nephele/src/Methods/UNLOCK.ts +++ b/packages/nephele/src/Methods/UNLOCK.ts @@ -26,6 +26,12 @@ export class UNLOCK extends Method { response.locals.baseUrl ); + if ((await resource.isCollection()) && !url.toString().endsWith('/')) { + response.set({ + 'Content-Location': `${url}/`, + }); + } + if (lockTokenHeader == null || lockTokenHeader.trim() === '') { throw new BadRequestError( 'UNLOCK method must include a lock token in the Lock-Token header.'