• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tomdesair / tus-java-server / 28704942481

04 Jul 2026 11:35AM UTC coverage: 92.355%. First build
28704942481

Pull #105

github

web-flow
Merge 94b11f46d into e8bbe6ae5
Pull Request #105: feat: Implement IETF Resumable Uploads for HTTP (RUFH) Protocol

866 of 1022 branches covered (84.74%)

Branch coverage included in aggregate %.

443 of 496 new or added lines in 24 files covered. (89.31%)

2263 of 2366 relevant lines covered (95.65%)

6.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.77
/src/main/java/me/desair/tus/server/rufh/handler/RufhAppendPatchRequestHandler.java
1
package me.desair.tus.server.rufh.handler;
2

3
import java.io.IOException;
4
import java.io.InputStream;
5
import me.desair.tus.server.HttpHeader;
6
import me.desair.tus.server.HttpMethod;
7
import me.desair.tus.server.exception.TusException;
8
import me.desair.tus.server.upload.UploadInfo;
9
import me.desair.tus.server.upload.UploadLockingService;
10
import me.desair.tus.server.upload.UploadStorageService;
11
import me.desair.tus.server.util.AbstractRequestHandler;
12
import me.desair.tus.server.util.InterruptibleInputStream;
13
import me.desair.tus.server.util.StructuredHeaderUtil;
14
import me.desair.tus.server.util.TusServletRequest;
15
import me.desair.tus.server.util.TusServletResponse;
16

17
/**
18
 * Request handler for data append requests via HTTP PATCH.
19
 *
20
 * <p>Appends request payload bytes to an existing upload resource, registers locking via {@link
21
 * InterruptibleInputStream}, updates completion status, and sets Upload-Offset, Upload-Complete,
22
 * and Upload-Draft response headers.
23
 *
24
 * <p>Reference: Section 4.4.2 (Append Response) & Appendix B (Draft Version Identification) of
25
 * draft-ietf-httpbis-resumable-upload-11.
26
 */
27
public class RufhAppendPatchRequestHandler extends AbstractRequestHandler {
6✔
28

29
  @Override
30
  public boolean supports(HttpMethod method) {
31
    return HttpMethod.PATCH.equals(method);
8✔
32
  }
33

34
  @Override
35
  public void process(
36
      HttpMethod method,
37
      TusServletRequest servletRequest,
38
      TusServletResponse servletResponse,
39
      UploadStorageService uploadStorageService,
40
      String ownerKey)
41
      throws IOException, TusException {
NEW
42
    process(method, servletRequest, servletResponse, uploadStorageService, null, ownerKey);
×
NEW
43
  }
×
44

45
  @Override
46
  public void process(
47
      HttpMethod method,
48
      TusServletRequest servletRequest,
49
      TusServletResponse servletResponse,
50
      UploadStorageService uploadStorageService,
51
      UploadLockingService uploadLockingService,
52
      String ownerKey)
53
      throws IOException, TusException {
54

55
    String requestUri = servletRequest.getRequestURI();
3✔
56
    UploadInfo uploadInfo = uploadStorageService.getUploadInfo(requestUri, ownerKey);
5✔
57

58
    // If upload is null, this is a PATCH creation request and was handled by
59
    // RufhCreationPostRequestHandler
60
    if (uploadInfo == null) {
2!
NEW
61
      return;
×
62
    }
63

64
    String uploadCompleteHeader = servletRequest.getHeader(HttpHeader.UPLOAD_COMPLETE);
4✔
65
    Boolean uploadComplete = StructuredHeaderUtil.parseBoolean(uploadCompleteHeader);
3✔
66

67
    InputStream is = servletRequest.getContentInputStream();
3✔
68
    if (is != null) {
2!
69
      if (uploadLockingService != null) {
2!
70
        InterruptibleInputStream interruptibleStream = new InterruptibleInputStream(is);
5✔
71
        uploadLockingService.registerInputStream(requestUri, interruptibleStream);
4✔
72
        is = interruptibleStream;
2✔
73
      }
74
      UploadInfo appended = uploadStorageService.append(uploadInfo, is);
5✔
75
      if (appended != null) {
2!
76
        uploadInfo = appended;
2✔
77
      }
78
    }
79

80
    boolean isFinished = Boolean.TRUE.equals(uploadComplete) || isUploadCompleted(uploadInfo);
12!
81
    if (isFinished) {
2✔
82
      uploadInfo.setLength(uploadInfo.getOffset());
4✔
83
      uploadStorageService.update(uploadInfo);
3✔
84
    }
85

86
    servletResponse.setHeader(HttpHeader.UPLOAD_DRAFT, "11");
4✔
87
    servletResponse.setHeader(HttpHeader.UPLOAD_OFFSET, String.valueOf(uploadInfo.getOffset()));
6✔
88
    servletResponse.setHeader(
4✔
89
        HttpHeader.UPLOAD_COMPLETE, StructuredHeaderUtil.formatBoolean(isFinished));
1✔
90

91
    if (isFinished) {
2✔
92
      servletResponse.setStatus(200);
4✔
93
    } else {
94
      servletResponse.setStatus(204);
3✔
95
    }
96
  }
1✔
97

98
  private boolean isUploadCompleted(UploadInfo uploadInfo) {
99
    return uploadInfo != null && !uploadInfo.isUploadInProgress();
7!
100
  }
101
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc