899 lines
42 KiB
Markdown
899 lines
42 KiB
Markdown
# VD-14: Resumable download와 background download 경계
|
|
|
|
- 상태: Accepted — production design complete, implementation pending
|
|
- 결정일: 2026-07-28
|
|
- catalog recipe availability: `RECIPE_AVAILABLE` (primary status/selection과 별도)
|
|
- Range resumable download primary status: `DESIGNED_NOT_IMPLEMENTED`
|
|
- app-managed background download primary status: `NOT_SELECTED`
|
|
- cross-browser app-managed background download guarantee: `PLATFORM_LIMITED`
|
|
- 관련 결정: VD-10, VD-11, VD-12
|
|
- current status ledger:
|
|
`docs/architecture/browser-data-capability-completion-ledger.md`
|
|
- 재검토: 제품이 Range 재개, 탭 종료 뒤 전달 또는 대용량 Safari fallback을
|
|
선택할 때
|
|
|
|
## 1. 배경과 현재 사실
|
|
|
|
현재 reference runtime은 whole-object `200` response를 bounded stream으로 읽어
|
|
foreground destination에 저장하거나 browser download manager에 handoff한다. 이
|
|
경로는 전체 payload를 하나의 `Blob`으로 만들지 않고 byte length와 SHA-256을
|
|
검증하지만, 네트워크나 탭이 중단되면 다음 실행은 byte 0부터 다시 시작한다.
|
|
|
|
Range resume는 기존 stream에 `Range` header 하나를 추가하는 기능이 아니다.
|
|
representation identity, exact `206 Content-Range`, durable partial destination,
|
|
checkpoint CAS, `200/412/416` reconciliation과 마지막 whole-object integrity가
|
|
하나의 protocol이어야 한다. background download도 Range resume와 동일하지 않다.
|
|
브라우저 download manager에 넘기는 것과 애플리케이션이 Service Worker에서
|
|
전송을 계속 관리하는 것은 완료 증거와 상호운용성이 전혀 다르다.
|
|
|
|
이 ADR은 목표 계약을 정의한다. 이 문서가 존재한다는 사실은 runtime, endpoint,
|
|
worker 또는 제품 UX가 구현·조합되었다는 뜻이 아니다.
|
|
|
|
## 2. 표준 capability 상태
|
|
|
|
설계, source 존재, 제품 조합과 플랫폼 한계를 하나의 `enabled` boolean으로 합치지
|
|
않는다. primary current status는 다음 다섯 값 중 정확히 하나다. 이 taxonomy는
|
|
선형 maturity model이 아니며 상태 이름만으로 rollout 또는 production readiness를
|
|
추론하지 않는다.
|
|
|
|
| primary status | 의미 |
|
|
| --- | --- |
|
|
| `NOT_SELECTED` | 제품 요구, owner, policy 또는 구현 범위가 아직 선택되지 않음 |
|
|
| `DESIGNED_NOT_IMPLEMENTED` | versioned contract와 불변조건은 승인됐지만 reference source가 없음 |
|
|
| `AVAILABLE_NOT_COMPOSED` | 검증 가능한 reference source가 있지만 제품 bootstrap/endpoint에는 연결되지 않음 |
|
|
| `COMPOSED` | 특정 제품 facade, config와 dependency에 실제로 조합됨 |
|
|
| `PLATFORM_LIMITED` | 요구 semantics를 target browser/platform 전체에서 보장할 수 없음 |
|
|
|
|
production readiness와 traffic admission은 primary status와 독립된 축이다.
|
|
운영 상태는 completion ledger가 정의한 네 canonical 축만 사용한다.
|
|
|
|
```text
|
|
Selection =
|
|
NOT_SELECTED | SELECTED | REMOVING
|
|
TrafficAdmission =
|
|
DISABLED | SHADOW | CANARY | ENABLED
|
|
RuntimeHealth =
|
|
UNKNOWN | AVAILABLE | DEGRADED | UNAVAILABLE | INCOMPATIBLE
|
|
PromotionEvidence =
|
|
MISSING | PARTIAL | COMPLETE | EXPIRED
|
|
```
|
|
|
|
아래 `source evidence`는 ADR과 reference source의 존재를 설명하는 문서 표기일
|
|
뿐 canonical readiness 축이 아니다. `DESIGN_REVIEWED`는 native browser나
|
|
provider 증거가 아니고, `REFERENCE_TESTED`도 ledger의 browser component를
|
|
`PROMOTABLE` 또는 `PromotionEvidence=COMPLETE`로 만들지 않는다.
|
|
|
|
현재 capability별 판정:
|
|
|
|
| capability | primary status | source evidence | 비고 |
|
|
| --- | --- | --- | --- |
|
|
| whole-object foreground streaming | `AVAILABLE_NOT_COMPOSED` | `REFERENCE_TESTED` | 기존 VD-12 범위 |
|
|
| browser-managed handoff mechanism | `AVAILABLE_NOT_COMPOSED` | `REFERENCE_TESTED` | 실제 capability issuer는 제품 연결 시 필요 |
|
|
| Range resumable download | `DESIGNED_NOT_IMPLEMENTED` | `DESIGN_REVIEWED` | 이 ADR의 구현 대상 |
|
|
| app-managed background download | `NOT_SELECTED` | `DESIGN_REVIEWED` | 제품 요구가 선택될 때만 별도 구현 |
|
|
| cross-browser app-managed background download guarantee | `PLATFORM_LIMITED` | `DESIGN_REVIEWED` | 공통 baseline으로 promotion 불가 |
|
|
|
|
기존 foreground stream과 browser-managed handoff의 source/evidence를 Range나
|
|
app-managed background download 구현 증거로 재사용하지 않는다.
|
|
|
|
## 3. 결정 요약
|
|
|
|
1. whole-object foreground streaming, Range resumable download,
|
|
browser-managed handoff와 app-managed background download를 서로 다른
|
|
capability와 결과 타입으로 유지한다.
|
|
2. Range protocol literal은 `RANGE_RESUMABLE_DOWNLOAD_V1`로 고정한다. 기존
|
|
whole-object presigned contract에 암묵적으로 섞지 않는다.
|
|
3. resume의 authority는 server-owned immutable generation과 strong validator다.
|
|
local offset, file name, timestamp 또는 partial byte 존재는 authority가 아니다.
|
|
4. 각 data-plane capability는 exact representation, start/end range, method,
|
|
response status/header/length와 expiry를 묶고 한 번만 사용한다.
|
|
5. checkpoint는 비권한성 recovery metadata만 account-partitioned storage에
|
|
보관한다. URL, signed query/header, raw ETag, bearer token과 file path는
|
|
저장하지 않는다.
|
|
6. destination은 seek/truncate 가능한 명시적 port 또는 owned OPFS staging이다.
|
|
순차 writable에 검증되지 않은 partial bytes를 append하지 않는다.
|
|
7. checkpoint offset은 destination segment가 durable하게 commit되고 exact length가
|
|
재확인된 뒤에만 CAS로 전진한다.
|
|
8. final success는 destination 전체를 처음부터 다시 읽어 whole-object SHA-256을
|
|
검증하고 final commit을 마친 경우만 `SAVED_VERIFIED`다.
|
|
9. browser-managed handoff는 탭 종료 뒤 계속될 수 있는 기본 server-file
|
|
fallback이지만 결과는 계속 `BROWSER_HANDOFF`다.
|
|
10. app-managed background download는 cross-browser baseline이 아니다. 별도
|
|
optional protocol, platform probe, worker control plane과 owned staging이 모두
|
|
승인된 환경에서만 progressive enhancement로 조합한다.
|
|
11. browser 차이는 user-agent 문자열이 아니라 capability probe와 정책으로
|
|
결정한다.
|
|
|
|
## 4. Topology와 책임
|
|
|
|
```text
|
|
product download use case
|
|
-> product-owned download facade
|
|
-> DownloadStrategySelector
|
|
-> WHOLE_OBJECT_PICKER_STREAM
|
|
-> RANGE_RESUMABLE_FOREGROUND
|
|
-> BROWSER_MANAGED_HANDOFF
|
|
-> BOUNDED_OBJECT_URL
|
|
-> APP_MANAGED_BACKGROUND_DOWNLOAD (optional)
|
|
|
|
RANGE_RESUMABLE_FOREGROUND
|
|
-> BFF control plane
|
|
authorization
|
|
immutable representation lookup
|
|
range capability issuance/reissue
|
|
-> browser RangeDownloadRuntime
|
|
checkpoint + mutation lock
|
|
exact HTTP state machine
|
|
seekable destination or OPFS staging
|
|
whole-object verification
|
|
-> object store/BFF byte plane
|
|
|
|
APP_MANAGED_BACKGROUND_DOWNLOAD
|
|
-> window-owned admission and user intent
|
|
-> worker-specific control plane
|
|
-> owned OPFS staging
|
|
-> later foreground export
|
|
```
|
|
|
|
브라우저는 bucket, object key, provider generation locator, signing key 또는 cloud
|
|
credential을 소유하지 않는다. BFF가 logical resource를 exact immutable
|
|
representation에 binding한다. direct object-store Range가 해당 binding과
|
|
capability의 `preconditionMode`가 선택한 exact `If-Range` 또는
|
|
immutable-generation precondition을 실제로 강제하지 못하면 BFF proxy/relay를
|
|
사용한다.
|
|
|
|
## 5. Versioned Range capability
|
|
|
|
### 5.1 Application-visible handle
|
|
|
|
application에는 raw URL이나 validator를 노출하지 않는다.
|
|
|
|
```text
|
|
RangeDownloadCapability
|
|
protocol = RANGE_RESUMABLE_DOWNLOAD_V1
|
|
opaque identity
|
|
safe receipt
|
|
resourceId
|
|
representationBindingSha256
|
|
totalByteLength
|
|
mediaType
|
|
wholeObjectSha256
|
|
requestedStart
|
|
requestedEndExclusive
|
|
preconditionMode = STRONG_IF_RANGE | IMMUTABLE_GENERATION_PRECONDITION
|
|
allowWholeObjectFallback
|
|
expiresAtEpochMs
|
|
```
|
|
|
|
adapter-owned identity vault에는 다음 data-plane binding을 함께 둔다.
|
|
|
|
```text
|
|
exact HTTPS URL/query
|
|
exact GET method
|
|
exact origin/path
|
|
exact Range header
|
|
exact precondition header/value selected by preconditionMode
|
|
required response headers
|
|
allowed statuses = policy-derived exact subset of 200 | 206 | 412 | 416
|
|
expected representation binding
|
|
maximum response bytes
|
|
single-use receipt
|
|
```
|
|
|
|
`representationBindingSha256`는 protocol/version, logical resource, immutable
|
|
generation, precondition mode별 normalized strong validator 또는 generation
|
|
binding, exact total length, media type와 expected whole-object digest의 canonical
|
|
binding이다. 이것은 authorization proof가 아니다. BFF는 client 값을 echo하지
|
|
않고 registry snapshot에서 직접 재계산한다.
|
|
|
|
### 5.2 Strong validator
|
|
|
|
resume에는 다음 중 하나가 필요하다.
|
|
|
|
- server registry가 소유하는 immutable object generation과 그 generation에 pin된
|
|
proxy/direct request
|
|
- RFC semantics를 만족하는 strong ETag와 exact `If-Range`
|
|
|
|
weak ETag(`W/`), `Last-Modified`만 있는 representation, multipart ETag를 whole
|
|
digest로 해석한 값과 CDN이 임의로 다시 쓴 validator는 resume authority로
|
|
사용하지 않는다. provider가 strong validator를 제공하지 못하면 BFF가 immutable
|
|
generation을 pin하거나 Range resume를 `UNSUPPORTED`로 닫는다.
|
|
|
|
raw ETag와 provider generation locator는 application, checkpoint, diagnostics와
|
|
telemetry에 노출하지 않는다. reload 뒤에는 BFF가 새 capability를 발급하고,
|
|
runtime은 새 capability의 `representationBindingSha256`가 checkpoint와 같은지
|
|
확인한 뒤 vault 안의 exact precondition만 사용한다.
|
|
|
|
`STRONG_IF_RANGE` mode는 exact `If-Range`를 보내고 `206`, Range-ignore 또는
|
|
validator mismatch의 full `200`과 해당 `416`만 계약한다.
|
|
`IMMUTABLE_GENERATION_PRECONDITION` mode는 BFF/provider가 정한 exact `If-Match`
|
|
또는 generation precondition을 보내며 `412`를 계약할 수 있다.
|
|
`allowWholeObjectFallback`과 `allowedStatuses`는 mode, requested start와 provider
|
|
topology에서 capability 발급 시 닫히며 executor가 임의로 넓히지 않는다.
|
|
|
|
### 5.3 Capability 재발급
|
|
|
|
capability expiry, data-plane `401/403/410` 또는 최소 잔여 lifetime 부족은 같은
|
|
URL의 무조건 retry가 아니다.
|
|
|
|
1. 현재 response reader를 cancel하고 capability를 consume한다.
|
|
2. control plane에 `downloadKey`, resource와 expected representation binding,
|
|
exact next range를 전달한다.
|
|
3. BFF가 authorization와 current generation을 다시 읽는다.
|
|
4. binding이 같을 때만 새 capability로 같은 range를 재시도한다.
|
|
5. binding이 바뀌었으면 partial destination을 append하지 않고
|
|
`REPRESENTATION_CHANGED/RESTART`로 닫는다.
|
|
|
|
재발급 횟수, 전체 operation deadline과 retry backoff는 composition hard ceiling
|
|
안에 둔다. capability를 durable queue나 worker message에 저장하지 않는다.
|
|
|
|
## 6. Durable checkpoint
|
|
|
|
### 6.1 Schema
|
|
|
|
```text
|
|
RangeDownloadCheckpointV1
|
|
schemaVersion = 1
|
|
protocol = RANGE_RESUMABLE_DOWNLOAD_V1
|
|
revision
|
|
state = ACTIVE | PAUSED | FINALIZING | CLEANUP_PENDING
|
|
downloadKey
|
|
resourceBindingSha256
|
|
representationBindingSha256
|
|
totalByteLength
|
|
nextOffset
|
|
committedSegmentCount
|
|
destination
|
|
kind = OPFS_STAGING | SEEKABLE_FILE
|
|
opaqueDestinationBinding
|
|
createdAtEpochMs
|
|
updatedAtEpochMs
|
|
retentionExpiresAtEpochMs
|
|
```
|
|
|
|
`downloadKey`, destination binding과 physical database/OPFS namespace는
|
|
composition-issued opaque token이다. 사용자 file name, resource ID, account ID,
|
|
tenant ID 또는 local path를 넣지 않는다.
|
|
|
|
checkpoint에 금지하는 값:
|
|
|
|
- presigned URL, query와 signed request/response header
|
|
- bearer/session/auth/CSRF token
|
|
- raw ETag, provider object key/generation locator
|
|
- file name, user path와 native exception
|
|
- incremental hash 내부 state
|
|
- raw backend response나 retry body
|
|
|
|
허용된 digest binding과 offset은 비권한성 recovery metadata다. account partition,
|
|
retention, count/byte budget과 logout deletion을 적용하며 log/analytics/ticket에는
|
|
내보내지 않는다.
|
|
|
|
### 6.2 CAS와 durable offset
|
|
|
|
한 `downloadKey`는 cross-context exclusive mutation lock으로 직렬화한다. lock은
|
|
correctness의 유일한 authority가 아니며 checkpoint revision CAS와 exact
|
|
destination binding이 최종 local authority다.
|
|
|
|
`nextOffset`은 다음 순서가 모두 성공한 뒤에만 전진한다.
|
|
|
|
1. exact `206` range를 bounded stream으로 읽는다.
|
|
2. expected start 위치에만 쓴다.
|
|
3. writer close/segment commit을 완료한다.
|
|
4. destination의 committed length가 expected end 이상인지 확인한다.
|
|
5. unexpected tail이 있으면 authorized `truncate(expectedEnd)`를 완료한다.
|
|
6. checkpoint를 `revision + 1`, `nextOffset = expectedEnd`로 CAS한다.
|
|
|
|
response가 성공했지만 destination commit 전에 crash하면 checkpoint는 이전
|
|
offset에 머문다. 재시작은 destination을 checkpoint offset으로 truncate하고 같은
|
|
range를 다시 요청한다. destination commit 뒤 checkpoint CAS가 유실된 경우도
|
|
동일하게 checkpoint offset까지 truncate한 뒤 재전송한다. 따라서 중복 byte를
|
|
append하지 않는다.
|
|
|
|
### 6.3 Inventory와 retention
|
|
|
|
checkpoint store는 단일 key read 외에 bounded admin operation을 제공해야 한다.
|
|
|
|
- account partition 안의 safe summary를 cursor page로 list
|
|
- expired/terminal checkpoint를 bounded batch로 classify
|
|
- destination binding과 함께 exact owned staging을 cleanup
|
|
- active lock/lease가 있는 항목은 건너뜀
|
|
- count, logical bytes, maximum age와 cleanup retry budget 강제
|
|
- cleanup receipt를 durable하게 남기고 response 유실을 reconcile
|
|
|
|
inventory에는 resource ID, file name, digest, raw validator와 path를 반환하지
|
|
않는다. 제품 resume UI가 필요한 경우 제품 database/query가 별도 safe display
|
|
metadata를 소유하고 opaque `downloadKey`로만 연결한다.
|
|
|
|
## 7. Destination 계약
|
|
|
|
### 7.1 공통 port
|
|
|
|
```text
|
|
ResumableDownloadDestinationPort
|
|
inspect(binding) -> committedLength, readable, writable, permissionState
|
|
openWriter(binding, keepExistingData=true)
|
|
seek(offset)
|
|
write(chunk)
|
|
truncate(length)
|
|
commitSegment()
|
|
openReader(start=0)
|
|
finalize()
|
|
abortAttempt()
|
|
cleanup(authority)
|
|
```
|
|
|
|
native handle, OPFS handle와 path는 adapter 밖으로 노출하지 않는다. 모든 method는
|
|
bounded deadline, AbortSignal과 closed failure를 사용한다.
|
|
|
|
### 7.2 Seekable external file
|
|
|
|
직접 외부 파일에 resume하려면 browser가 기존 data 보존, seek, truncate,
|
|
재읽기와 permission 재확인을 실제로 지원해야 한다.
|
|
|
|
- picker와 permission request는 Window의 명시적 user activation에서만 실행한다.
|
|
- structured-cloned handle을 보존하는 경우 별도 privacy/retention 승인이 필요하다.
|
|
- reopen 뒤 `queryPermission`/`requestPermission`을 거치며 denied면
|
|
`PERMISSION_DENIED/RESELECT`다.
|
|
- writer가 temporary-file commit semantics를 쓰면 segment마다 close한 뒤
|
|
committed file size를 다시 확인한다.
|
|
- checkpoint보다 큰 tail은 검증하지 않고 사용하지 않으며 exact checkpoint
|
|
offset으로 truncate한다.
|
|
- checkpoint보다 파일이 작거나 다른 handle이면 `CONFLICT/RESTART`다.
|
|
|
|
브라우저가 이 계약을 만족하지 못하면 external-file resume를 흉내 내지 않고 OPFS
|
|
staging 또는 browser-managed handoff로 전환한다.
|
|
|
|
### 7.3 OPFS staging
|
|
|
|
cross-browser app-controlled resume의 우선 destination은 policy-owned OPFS
|
|
staging이다.
|
|
|
|
- physical path는 기존 OPFS authority/namespace/partition registry가 발급한다.
|
|
- checkpoint와 OPFS object는 immutable binding과 generation journal로 연결한다.
|
|
- quota estimate는 admission hint일 뿐이며 write 중 quota failure도 처리한다.
|
|
- download 완료 뒤 staging 전체를 다시 읽어 SHA-256을 검증한다.
|
|
- foreground user activation에서 새 외부 destination을 열고 staging을 stream
|
|
export한다.
|
|
- 외부 export close가 성공하기 전 staging을 삭제하지 않는다.
|
|
- export 결과가 유실되면 staging을 유지하고 user에게 retry 가능한 상태를
|
|
반환한다.
|
|
|
|
OPFS 저장 성공은 사용자가 접근 가능한 파일 저장 완료가 아니다. 결과를
|
|
`STAGED_VERIFIED`와 `SAVED_VERIFIED`로 구분한다. OPFS는 큰 파일에서 storage와
|
|
I/O를 한 번 더 요구하므로 quota/retention owner 없는 기본 fallback이 아니다.
|
|
|
|
## 8. HTTP 상태 머신
|
|
|
|
### 8.1 요청 전
|
|
|
|
1. checkpoint와 destination binding을 exact하게 읽는다.
|
|
2. destination length를 검사하고 checkpoint보다 큰 tail을 truncate한다.
|
|
3. checkpoint보다 작으면 partial을 신뢰하지 않고 restart/cleanup으로 닫는다.
|
|
4. 새 capability의 representation binding과 exact range를 검증한다.
|
|
5. `Range: bytes=S-E`와 capability의 `preconditionMode`가 정한 exact
|
|
`If-Range` 또는 immutable-generation precondition을 vault binding 그대로
|
|
보낸다.
|
|
6. `credentials: omit`, `redirect: error`, `no-referrer`, `no-store`,
|
|
identity content encoding을 강제한다.
|
|
|
|
한 request의 range 크기와 exact `S/E`는 capability 발급 **전에** composition
|
|
maximum 안에서 계산한다. executor는 capability의 `requestedStart`,
|
|
`requestedEndExclusive`와 exact Range header가 일치하는지 검증하고 그대로
|
|
전송하며 다시 줄이거나 늘리지 않는다. ceiling을 넘는 capability는 사용 전에
|
|
거절한다. 기본 protocol은 sequential range만 허용한다. parallel range와 sparse
|
|
destination은 별도 protocol/version 없이는 사용하지 않는다.
|
|
zero-byte representation은 유효하지 않은 byte range를 만들지 않는다. exact
|
|
length가 0이고 empty-object SHA-256 binding이 일치하는 whole-object `200` 경로로
|
|
body/length를 확인한 뒤 바로 final verification으로 이동한다.
|
|
|
|
response body를 읽거나 destination writer를 열기 전에 `200`, `206`, `412`,
|
|
`416` 중 수신한 status가 capability vault의 exact `allowedStatuses` member인지
|
|
검사한다. 해당 네 값 중 허용되지 않은 status는 body를 cancel하고 capability를
|
|
consume하며 destination과 checkpoint를 변경하지 않은 채
|
|
`CONTRACT_MISMATCH`로 fail-closed한다. 아래 네 분기는 이 공통 admission gate를
|
|
통과한 경우에만 실행한다. 그 밖의 status는 §8.6의 별도 failure/reissue 규칙으로
|
|
처리한다.
|
|
|
|
### 8.2 `206 Partial Content`
|
|
|
|
성공 조건:
|
|
|
|
- `206`이 capability의 `allowedStatuses` member
|
|
- final response URL이 capability URL과 exact match
|
|
- `Content-Range: bytes S-E/T`가 하나만 존재하고 parse가 엄격함
|
|
- `S`가 requested start, `E + 1`이 requested end exclusive
|
|
- `T`가 checkpoint total과 같음
|
|
- `Content-Length = E - S + 1`
|
|
- strong validator/immutable generation binding 일치
|
|
- media type과 identity encoding 일치
|
|
- body 실제 bytes가 exact content length
|
|
|
|
하나라도 다르면 reader와 current destination attempt를 abort하고 checkpoint를
|
|
전진시키지 않는다. 정상인 경우에만 앞 절의 durable offset 순서로 commit한다.
|
|
|
|
### 8.3 `200 OK`
|
|
|
|
`200`은 capability의 `allowedStatuses` member인 경우에만 이 분기로 들어온다.
|
|
body를 destination에 쓰기 전에 final response URL, required response header,
|
|
media type, identity encoding과 mode별 strong validator 또는 immutable-generation
|
|
evidence가 capability의 exact representation binding과 일치하는지 검증한다.
|
|
|
|
다음 순서로 배타적으로 처리한다.
|
|
|
|
1. validator/generation evidence가 없거나 binding이 다르면 body를 cancel하고
|
|
capability를 consume한다. 기존 checkpoint와 partial은 append하지 않고
|
|
quarantine/retention policy로 전환한 뒤 control plane에서 current
|
|
representation을 다시 확인한다. 결과는
|
|
`REPRESENTATION_CHANGED/RESTART`이며 byte 0의 새 operation만 허용한다.
|
|
2. binding은 같지만 requested start가 `0`이고
|
|
`allowWholeObjectFallback=true`이면 fresh whole-object destination에서 기존
|
|
whole-object stream 계약으로 처리한다. exact total length와 final
|
|
whole-object digest를 검증하기 전에는 success나 final commit을 반환하지 않는다.
|
|
3. binding은 같고 requested start가 `0`이지만
|
|
`allowWholeObjectFallback=false`이면 body를 한 byte도 쓰지 않고 cancel한다.
|
|
결과는 `WHOLE_OBJECT_FALLBACK_NOT_ALLOWED`이며 policy가 허용한 새 Range
|
|
capability, browser handoff 또는 explicit unsupported만 선택한다.
|
|
4. binding은 같고 requested start가 `0`보다 크면 server가 Range를 무시한
|
|
것이다. body를 한 byte도 쓰지 않고 cancel하며 기존 partial을 같은 writer에서
|
|
덮어쓰지 않는다. control plane reconcile 뒤 같은 representation의 byte 0
|
|
restart operation, browser handoff 또는 explicit unsupported만 선택한다.
|
|
|
|
### 8.4 `412 Precondition Failed`
|
|
|
|
`412`가 capability의 `allowedStatuses` member이고
|
|
`preconditionMode=IMMUTABLE_GENERATION_PRECONDITION`인 경우에만 이 분기로 들어온다.
|
|
representation precondition 실패다. body를 cancel하고 checkpoint를 유지한 채
|
|
control plane에서 current generation을 확인한다. 같은 binding을 다시 발급하지
|
|
못하면 partial은 cleanup policy에 따라 폐기하고 byte 0부터 새 operation을
|
|
시작한다.
|
|
|
|
RFC `If-Range` validator mismatch 자체의 정상 응답은 `200`이다. `412`는 BFF나
|
|
provider가 immutable generation을 pin하기 위해 별도 `If-Match` 계열 precondition을
|
|
함께 강제하는 topology에서만 이 상태 머신에 들어온다. topology가 `412`를 계약하지
|
|
않았다면 unknown status로 fail-closed한다.
|
|
|
|
### 8.5 `416 Range Not Satisfiable`
|
|
|
|
`416`이 capability의 `allowedStatuses` member인 경우에만 이 분기로 들어온다.
|
|
response body는 download data로 소비하지 않고 cancel한다.
|
|
`Content-Range: bytes */T`를 strict하게 검사하며 final response URL, required
|
|
headers와 mode별 validator/generation binding도 확인한다. provider의 `416`이
|
|
binding evidence를 반환할 수 없는 topology라면 BFF control plane reconcile이
|
|
exact immutable generation을 다시 증명하기 전에는 EOF나 missing-range 분기로
|
|
진행하지 않는다.
|
|
|
|
다음 순서를 사용하며 한 분기를 처리한 뒤 아래 분기로 fall through하지 않는다.
|
|
|
|
1. malformed/missing `T`, final URL/header mismatch 또는 증명되지 않은
|
|
representation binding은 `CONTRACT_MISMATCH`로 fail-closed한다.
|
|
2. `T != expected total`이면 representation changed다. local bytes를 `T`에 맞춰
|
|
자동 truncate하거나 append하지 않고 capability를 consume한 뒤 partial을
|
|
quarantine/restart한다.
|
|
3. `nextOffset > T`이면 checkpoint 자체가 corrupt/stale이다. 잘못된 offset으로
|
|
truncate하지 않고 checkpoint와 partial을 quarantine한 뒤 restart/recovery로
|
|
닫는다.
|
|
4. `nextOffset <= T`이지만 `local committed length != nextOffset`이면 먼저 local
|
|
state를 reconcile한다.
|
|
- local length가 더 크면 exact `nextOffset`까지만 uncommitted tail을
|
|
authorized truncate하고 durable length를 다시 확인한다.
|
|
- local length가 더 작으면 journal이 증명하는 마지막 confirmed segment로
|
|
destination과 checkpoint를 함께 CAS rollback할 수 있을 때만 복구한다.
|
|
그렇지 않으면 quarantine/restart한다.
|
|
이 분기는 reconcile 결과를 새 state-machine invocation에서 다시 평가하며 바로
|
|
finalization이나 missing-range request로 진행하지 않는다.
|
|
5. `local committed length == nextOffset == T`이면 data transfer가 끝난 후보로
|
|
보고 `FINALIZING` whole-object verification으로 이동한다.
|
|
6. `local committed length == nextOffset < T`이면 local missing range가 남아 있다.
|
|
새 capability로 exact `nextOffset` range를 재발급한다. 같은 total에 대해
|
|
satisfiable range가 다시 `416`이면 bounded retry하지 않고
|
|
`CONTRACT_MISMATCH`로 fail-closed한다.
|
|
|
|
`416` 자체를 다운로드 성공으로 간주하지 않는다.
|
|
|
|
### 8.6 나머지 상태와 network failure
|
|
|
|
| 조건 | 처리 |
|
|
| --- | --- |
|
|
| `401/403/410` | bounded capability reissue; binding mismatch면 restart |
|
|
| `404` | existence-hiding policy에 따라 unavailable/not-found, partial cleanup 예약 |
|
|
| `409` | server representation/session reconcile |
|
|
| `429`/모든 `5xx`/network | 동일 exact range만 bounded retry |
|
|
| redirect/opaque response | policy rejection |
|
|
| timeout/cancel | reader와 writer attempt abort, checkpoint 유지 |
|
|
| overrun/truncation | integrity failure, checkpoint 유지 |
|
|
|
|
retry는 destination commit 여부를 먼저 판단한다. effect가 ambiguous하면
|
|
checkpoint와 destination length를 reconcile하기 전 새 offset으로 이동하지 않는다.
|
|
|
|
## 9. Whole-object integrity와 final commit
|
|
|
|
Range별 transport 검증은 whole-object 무결성 증거가 아니다. 모든 bytes가
|
|
수신되면 checkpoint를 `FINALIZING`으로 CAS하고 다음을 수행한다.
|
|
|
|
1. destination length가 exact total과 같은지 확인한다.
|
|
2. destination을 byte 0부터 bounded chunk로 다시 읽는다.
|
|
3. vetted incremental SHA-256으로 whole-object digest를 계산한다.
|
|
4. capability/representation binding의 expected digest와 constant-time 비교한다.
|
|
5. mismatch면 사용자 destination을 성공으로 표시하지 않고 staging을 격리하거나
|
|
authorized cleanup한다.
|
|
6. OPFS staging이면 foreground external export와 destination close를 완료한다.
|
|
7. final destination commit truth를 확인한 뒤만 `SAVED_VERIFIED`를 반환한다.
|
|
8. checkpoint와 staging cleanup을 exact revision/receipt로 완료한다.
|
|
|
|
portable하지 않은 incremental hash 내부 state를 checkpoint에 serialize하지 않는다.
|
|
마지막 full reread 비용을 피하려면 chunk digest/Merkle manifest를 별도 protocol로
|
|
설계하고 server가 exact proof를 제공해야 한다.
|
|
|
|
## 10. Pause, cancel, crash와 account lifecycle
|
|
|
|
### 10.1 Pause
|
|
|
|
`pause(downloadKey)`는 browser work 중단이며 server resource/capability revoke가
|
|
아니다.
|
|
|
|
- 같은 runtime의 read/write/backoff를 AbortSignal로 중단한다.
|
|
- same-origin context에는 opaque key만 담은 versioned ephemeral pause event를
|
|
보낸다.
|
|
- mutation lock 안에서 checkpoint를 `PAUSED`로 CAS한다.
|
|
- in-memory URL/header/capability는 즉시 retire한다.
|
|
- committed segment는 유지하고 ambiguous writer attempt는 checkpoint offset으로
|
|
reconcile한다.
|
|
|
|
### 10.2 Cancel과 discard
|
|
|
|
cancel은 transfer 중단만 의미할 수 있고, discard는 local partial 삭제다. 제품
|
|
facade가 두 의도를 구분해야 한다. discard는 exact partition/destination binding과
|
|
short-lived maintenance authority를 요구하며 checkpoint와 OPFS staging을 하나의
|
|
cleanup journal로 처리한다.
|
|
|
|
### 10.3 Crash/reload
|
|
|
|
reload 후 runtime은:
|
|
|
|
1. account partition과 governance binding을 검증한다.
|
|
2. checkpoint schema/protocol/revision을 검증한다.
|
|
3. destination을 reopen하고 permission/length를 검사한다.
|
|
4. server에서 새 capability를 발급받아 representation binding을 대조한다.
|
|
5. exact checkpoint offset부터 resume한다.
|
|
|
|
source가 같은지 사용자에게 묻는 file-name 기반 확인은 사용하지 않는다.
|
|
|
|
### 10.4 Logout/account/tenant switch
|
|
|
|
- 신규 capability 발급과 resume admission을 먼저 닫는다.
|
|
- active foreground operation을 abort하고 writer를 정리한다.
|
|
- vault와 worker channel을 close한다.
|
|
- account partition의 checkpoint와 owned staging을 maintenance-authorized bounded
|
|
cleanup으로 제거한다.
|
|
- blocked deletion을 성공으로 보고하지 않는다.
|
|
- 이전 account handle/reference를 새 runtime에서 resolve하지 않는다.
|
|
|
|
retention/legal-hold 정책이 local partial 보존을 요구하는 특별한 제품이 아니라면
|
|
logout에서 partial을 제거하는 것이 기본이다.
|
|
|
|
## 11. Download strategy selector
|
|
|
|
selector는 presentation의 임의 조건문이 아니라 composition-owned immutable
|
|
policy와 runtime probe를 받는 공통 application service다.
|
|
|
|
입력:
|
|
|
|
- source가 server resource인지 client-generated artifact인지
|
|
- exact 또는 maximum byte length
|
|
- verified integrity 필요 여부
|
|
- resume/background 요구
|
|
- system save picker, seek/truncate, OPFS와 worker capability
|
|
- user activation
|
|
- storage quota admission
|
|
- browser-managed capability availability
|
|
- data classification와 retention policy
|
|
|
|
결과:
|
|
|
|
| 조건 | 선택 |
|
|
| --- | --- |
|
|
| server file, 탭 종료 뒤 계속 필요 | `BROWSER_MANAGED_HANDOFF` |
|
|
| server file, verified foreground save, picker 지원 | `WHOLE_OBJECT_PICKER_STREAM` |
|
|
| server file, resume 필수, destination 계약 충족 | `RANGE_RESUMABLE_FOREGROUND` |
|
|
| 작은 generated artifact | `BOUNDED_OBJECT_URL` |
|
|
| 큰 generated artifact, picker 지원 | `WHOLE_OBJECT_PICKER_STREAM` |
|
|
| 큰 generated artifact, picker 미지원 | `SERVER_GENERATION_REQUIRED` 또는 `UNSUPPORTED` |
|
|
| app background download가 승인·지원되고 OPFS quota 확보 | `APP_MANAGED_BACKGROUND_DOWNLOAD` |
|
|
|
|
selector는 fallback으로 byte/memory/security ceiling을 올리지 않는다. integrity가
|
|
필수인데 browser handoff만 가능하면 “검증된 저장”으로 downgrade하지 않고 제품이
|
|
handoff 또는 unsupported 중 하나를 명시적으로 선택한다.
|
|
|
|
### Safari와 picker 미지원 환경
|
|
|
|
user-agent 문자열로 Safari를 판별하지 않는다. 필요한 API와 실제 semantics를
|
|
capability probe로 확인한다.
|
|
|
|
- 대용량 server file: authorized `Content-Disposition` browser handoff
|
|
- 작은 generated file: bounded Blob/object URL
|
|
- 대용량 generated file: server-side generation 또는 unsupported
|
|
- OPFS: app-private staging일 뿐 Finder/Files 저장 완료로 표시하지 않음
|
|
- system save picker 미지원: unbounded Blob으로 자동 전환하지 않음
|
|
- seek/truncate/permission semantics 미충족: external Range resume 비활성화
|
|
|
|
browser-managed handoff endpoint는 cross-origin `download` attribute에 의존하지
|
|
않고 server가 safe `Content-Disposition`, media type, byte/generation policy를
|
|
실제 response에서 강제한다.
|
|
|
|
## 12. Background download 전달의 세 의미
|
|
|
|
### 12.1 Foreground app-managed
|
|
|
|
page가 열린 동안 runtime이 fetch, progress, integrity와 destination을 모두
|
|
관리한다. 현재 whole-object stream과 목표 Range resume가 이 범주다. page lifecycle
|
|
종료 뒤 지속을 보장하지 않는다.
|
|
|
|
### 12.2 Browser-managed handoff
|
|
|
|
navigation/download manager에 authorized endpoint를 넘긴다.
|
|
|
|
- page 종료 뒤 계속될 수 있는 가장 넓은 fallback
|
|
- application은 실제 disk write, 저장 위치와 final digest를 관찰하지 못함
|
|
- 결과는 `BROWSER_HANDOFF`, `SAVED`나 `VERIFIED`가 아님
|
|
- pause/resume UI와 retry semantics는 browser가 소유
|
|
|
|
### 12.3 App-managed background download
|
|
|
|
Service Worker/Background Fetch 등에서 application이 progress/retry/staging을
|
|
관리하려는 별도 optional capability다.
|
|
|
|
필수 조건:
|
|
|
|
- target browser/deployment의 explicit support matrix
|
|
- worker-safe authenticated control plane
|
|
- worker가 매 range마다 새 short-lived capability를 발급받는 계약
|
|
- capability/URL/header를 IDB, OPFS, Cache Storage와 message에 저장하지 않음
|
|
- private bytes는 Cache Storage가 아니라 policy-owned OPFS staging 사용
|
|
- worker termination을 정상 상태로 보고 checkpoint에서 재개
|
|
- concurrency, battery/network, quota와 retention ceiling
|
|
- logout/revocation event와 worker admission fence
|
|
- client/worker version compatibility와 upgrade drain
|
|
- notification/foreground export UX
|
|
|
|
일반 Service Worker의 수명이나 background execution 시간을 correctness 근거로
|
|
삼지 않는다. Background Fetch가 없는 환경에서 timer/keepalive로 장기 download를
|
|
흉내 내지 않는다. user-visible external save picker는 worker에서 호출하지 않고
|
|
완료된 OPFS staging을 다음 foreground user gesture에서 export한다.
|
|
|
|
따라서 app-managed background download가 향후 `AVAILABLE_NOT_COMPOSED` 또는 `COMPOSED`가
|
|
되더라도 지원 browser의 progressive enhancement일 뿐이다. cross-browser 보장
|
|
자체의 primary status는 계속 `PLATFORM_LIMITED`다.
|
|
|
|
## 13. Security, privacy와 observability
|
|
|
|
- URL/query/header, validator와 capability는 bearer 또는 sensitive metadata로
|
|
취급한다.
|
|
- `Range`와 `preconditionMode`가 선택한 exact `If-Range` 또는
|
|
immutable-generation precondition은 adapter vault가 binding 그대로 생성한다.
|
|
- caller는 offset을 늘리거나 arbitrary range를 요청하지 못한다.
|
|
- account partition과 resource authorization을 매 capability reissue에서 검사한다.
|
|
- partial bytes는 원본과 같은 data classification, retention, encryption-at-rest와
|
|
deletion policy를 적용한다.
|
|
- OPFS quota pressure가 다른 account partial을 제거할 권한을 주지 않는다.
|
|
- preview, execution 또는 Cache Storage promotion은 final verification 전 금지한다.
|
|
- high-cardinality ID, file name, path, URL, raw ETag와 digest를 metric label/log에
|
|
넣지 않는다.
|
|
|
|
허용된 aggregate observation:
|
|
|
|
- strategy와 destination kind
|
|
- response state bucket
|
|
- expected/committed byte bucket
|
|
- retry/reissue/resume count bucket
|
|
- duration, pause, restart, integrity와 cleanup outcome
|
|
- browser capability support reason code
|
|
|
|
## 14. Composition과 operational admission
|
|
|
|
`createBrowserTransferRuntime`에 해당하는 미래 composition owner만 다음을 조합한다.
|
|
|
|
- versioned wire codecs와 fixed BFF endpoint
|
|
- capability vault/provider/executor
|
|
- Range checkpoint store, destination registry와 mutation lock
|
|
- selector policy와 browser capability probe
|
|
- presigned, upload, image와 Range lifecycle
|
|
- account/logout cleanup authority
|
|
- safe observer
|
|
- traffic admission과 kill switch
|
|
|
|
독립적인 canonical readiness 상태:
|
|
|
|
```text
|
|
Selection = NOT_SELECTED | SELECTED | REMOVING
|
|
TrafficAdmission = DISABLED | SHADOW | CANARY | ENABLED
|
|
RuntimeHealth = UNKNOWN | AVAILABLE | DEGRADED | UNAVAILABLE | INCOMPATIBLE
|
|
PromotionEvidence = MISSING | PARTIAL | COMPLETE | EXPIRED
|
|
```
|
|
|
|
primary status가 `COMPOSED`여도 `TrafficAdmission` 기본값은 `DISABLED`다.
|
|
필수 config, strong validator/provider conformance, destination semantics, cleanup
|
|
owner 또는 valid evidence가 없으면 `TrafficAdmission=DISABLED`,
|
|
`RuntimeHealth=UNKNOWN | UNAVAILABLE`,
|
|
`PromotionEvidence=MISSING | PARTIAL | EXPIRED`로 readiness를 fail-closed한다.
|
|
이미 승인된 product selection 자체를 provider evidence 부족만으로 되돌리지
|
|
않는다.
|
|
|
|
Kill switch:
|
|
|
|
- 신규 Range capability issuance off
|
|
- Range resume off → whole-object restart 또는 browser handoff
|
|
- direct object-store Range off → BFF proxy
|
|
- external seek destination off → OPFS staging 또는 handoff
|
|
- app-managed background download off → foreground/browser handoff
|
|
- final export off → verified staging 유지
|
|
|
|
kill switch는 partial을 자동 삭제하거나 handoff를 saved/verified로 바꾸지 않는다.
|
|
|
|
## 15. Test와 conformance matrix
|
|
|
|
### 15.1 Deterministic runtime
|
|
|
|
- checkpoint CAS conflict와 corrupt/unknown field
|
|
- exact segment commit 전/후 crash
|
|
- destination larger/smaller/different binding
|
|
- pause/resume/cancel/discard races
|
|
- capability expiry/reissue와 representation change
|
|
- `200/206/412/416` 모든 분기
|
|
- malformed/multiple/overflow `Content-Range`
|
|
- weak/missing/mismatched validator
|
|
- overrun, truncation, stalled body와 abort
|
|
- final whole-object digest mismatch
|
|
- cleanup response loss와 replay
|
|
- count/byte/age retention sweep
|
|
|
|
### 15.2 Browser matrix
|
|
|
|
- system picker 지원/미지원
|
|
- seek/truncate/keep-existing-data semantics
|
|
- OPFS quota, eviction, reload와 worker termination
|
|
- cross-tab lock/pause delivery
|
|
- user activation과 permission denied/revoked
|
|
- large server handoff
|
|
- foreground export close/abort
|
|
- Chromium, Firefox와 WebKit 동일 필수 case set
|
|
|
|
지원하지 않는 API는 skip이 아니라 selector의 expected fallback/`UNSUPPORTED` 결과로
|
|
검증한다.
|
|
|
|
### 15.3 BFF/object provider contract
|
|
|
|
- immutable generation pin
|
|
- capability `preconditionMode`에 따른 strong `If-Range` 또는 immutable
|
|
generation precondition
|
|
- beginning/middle/end/empty/invalid range
|
|
- exact `206 Content-Range`와 length
|
|
- deliberate Range ignore `200`
|
|
- mode가 계약한 경우의 precondition `412`, EOF/invalid `416`
|
|
- mid-transfer capability expiry/revocation
|
|
- redirect/CORS/exposed-header/identity-encoding
|
|
- object replacement race
|
|
- direct provider와 proxy 결과 동등성
|
|
- URL/header/log redaction
|
|
|
|
fake와 route interception은 actual provider conformance를 대체하지 않는다.
|
|
|
|
### 15.4 Background-download-specific fault
|
|
|
|
- worker가 range commit 전/후 종료
|
|
- worker/client version 교체
|
|
- logout과 capability revocation
|
|
- offline/online 반복, quota exhaustion과 battery/network policy
|
|
- notification 유실과 foreground export replay
|
|
- unsupported browser가 foreground/handoff로 정확히 fallback
|
|
|
|
## 16. Rollout과 promotion
|
|
|
|
Primary status 변경과 readiness/traffic promotion은 별도로 승인한다.
|
|
|
|
1. Range의 `DESIGNED_NOT_IMPLEMENTED`와 ADR-local
|
|
`sourceEvidence=DESIGN_REVIEWED`를 확인한다.
|
|
2. provider-neutral ports/runtime, deterministic fake, negative fixture와 browser
|
|
test를 완성한 경우에만 Range primary status를
|
|
`AVAILABLE_NOT_COMPOSED`, source evidence를 `REFERENCE_TESTED`로 변경한다.
|
|
deterministic/reference test만으로 canonical `PromotionEvidence`를
|
|
`COMPLETE`로 바꾸지 않는다.
|
|
3. 제품 요구, owner, data class와 fallback이 선택되지 않은 app-managed
|
|
background download는 계속 `NOT_SELECTED`로 둔다. cross-browser 보장은
|
|
`PLATFORM_LIMITED`다.
|
|
4. fixed staging BFF/provider, actual config, account lifecycle와 runbook을 설치한
|
|
capability만 `COMPOSED`로 기록한다. 이때도
|
|
`TrafficAdmission=DISABLED`, `RuntimeHealth=UNKNOWN`,
|
|
`PromotionEvidence=PARTIAL`이다.
|
|
5. operator probe와 shadow에서 contract evidence를 수집한다.
|
|
6. internal cohort에서 BFF proxy Range를 먼저 canary한다.
|
|
7. direct provider Range와 external seek destination은 각각 별도 canary한다.
|
|
8. app-managed background download를 실제로 선택했다면 지원 browser cohort에서만 별도
|
|
opt-in canary한다.
|
|
9. contract/provider/browser/operations component gate, SLO, cleanup drill,
|
|
rollback과 evidence freshness가 모두 충족된 승인 범위만
|
|
`PromotionEvidence=COMPLETE`, `RuntimeHealth=AVAILABLE`,
|
|
`TrafficAdmission=ENABLED`로 promotion한다.
|
|
|
|
provider, endpoint, validator semantics, browser major behavior, destination adapter,
|
|
wire protocol 또는 security policy가 바뀌면 relevant evidence를 만료시키고
|
|
재승인한다.
|
|
|
|
## 17. Rollback과 제거
|
|
|
|
운영 rollback 순서:
|
|
|
|
1. 신규 Range/background-download admission과 capability 발급을 중지한다.
|
|
2. active writer/worker를 abort하고 checkpoint offset으로 reconcile한다.
|
|
3. app background download를 foreground/browser handoff로 낮춘다.
|
|
4. direct Range를 BFF proxy 또는 whole-object restart로 낮춘다.
|
|
5. verified OPFS staging은 retention window 안에서 foreground export 가능 상태로
|
|
유지한다.
|
|
6. ambiguous partial은 성공으로 표시하지 않고 cleanup queue로 넘긴다.
|
|
7. provider/signing credential 노출이 원인이면 backend revoke와 key rotation을
|
|
수행한다.
|
|
|
|
완전 제거:
|
|
|
|
1. pending checkpoint/staging inventory를 bounded하게 drain, export 또는 discard한다.
|
|
2. worker, channel, lock과 runtime을 close한다.
|
|
3. account-partition checkpoint/OPFS namespace를 maintenance-authorized cleanup한다.
|
|
4. endpoint, worker registration, config, policy와 feature facade를 제거한다.
|
|
5. production bundle/module inventory와 removal test로 source 부재를 증명한다.
|
|
|
|
rollback은 unbounded Blob fallback, validator 완화, digest 생략 또는 partial 자동
|
|
append를 허용하지 않는다.
|
|
|
|
## 18. 완료 기준
|
|
|
|
Range resumable download는 다음이 모두 참일 때만 구현 완료다.
|
|
|
|
- `RANGE_RESUMABLE_DOWNLOAD_V1` port와 strict wire codec이 있음
|
|
- capability mode별 exact allowed-status subset과 `200/206/412/416` 처리
|
|
상태 머신이 실행 가능하게 검증됨
|
|
- strong validator/immutable generation이 실제 provider에서 강제됨
|
|
- checkpoint CAS, inventory, retention과 account cleanup이 구현됨
|
|
- seek/truncate 또는 OPFS staging destination이 crash fault를 통과함
|
|
- capability reissue가 representation mismatch를 fail-closed함
|
|
- final whole-object SHA-256 뒤에만 verified success를 반환함
|
|
- selector가 picker/seek 미지원과 대용량 fallback을 안전하게 결정함
|
|
- actual BFF/provider와 Chromium/Firefox/WebKit evidence가 유효함
|
|
- SLO, alert, runbook, kill switch, rollback과 cleanup drill이 승인됨
|
|
|
|
app-managed background download는 위 항목에 더해 다음이 필요하다.
|
|
|
|
- 지원 browser/deployment 범위가 명시됨
|
|
- worker lifecycle 종료를 checkpoint로 복구함
|
|
- worker control plane이 durable capability 저장 없이 동작함
|
|
- logout/revocation/version upgrade fault가 통과함
|
|
- 미지원 browser fallback이 동일 제품 요구를 안전하게 만족하거나 명시적
|
|
unsupported UX를 가짐
|
|
|
|
이 기준 전에는 기존 foreground streaming 또는 browser handoff의 성공을 Range나
|
|
background download 구현 완료 증거로 사용하지 않는다.
|
|
|
|
## 19. 선택하지 않은 대안
|
|
|
|
- `Range` header만 추가하고 기존 sequential writable에 append
|
|
- weak ETag나 file name/lastModified를 representation identity로 사용
|
|
- serialized incremental hash state를 검증 없이 checkpoint
|
|
- `200` response를 기존 partial 뒤에 append
|
|
- `416`을 곧바로 success로 해석
|
|
- Service Worker keepalive를 cross-browser background 보장으로 간주
|
|
- picker 미지원 대용량 파일을 unbounded Blob으로 fallback
|
|
- OPFS staging을 사용자 파일 저장 완료로 표시
|
|
- browser-managed handoff를 application-verified save로 표시
|
|
- user-agent 문자열 기반 Safari 분기
|
|
|
|
## 20. 참고
|
|
|
|
- [Browser data capability completion ledger](../browser-data-capability-completion-ledger.md)
|
|
- [VD-16 Browser transfer composition과 Image delivery](./VD-16-browser-transfer-composition-and-image-delivery.md)
|
|
- [Browser transfer recovery](../../operations/browser-transfer-recovery.md)
|
|
- [RFC 9110 HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
|
|
- [Fetch Standard](https://fetch.spec.whatwg.org/)
|
|
- [File System Standard](https://fs.spec.whatwg.org/)
|
|
- [Service Workers](https://w3c.github.io/ServiceWorker/)
|
|
- [Background Fetch draft](https://wicg.github.io/background-fetch/)
|
|
- [기존 transfer 설계](../presigned-transfer-and-image-cdn.md)
|
|
- [browser file/origin storage 설계](../browser-file-and-origin-storage.md)
|