1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.codec.http;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.util.Send;
20 import io.netty5.handler.codec.DefaultHeaders.NameValidator;
21 import io.netty5.util.internal.StringUtil;
22
23 import java.util.Map.Entry;
24
25 import static java.util.Objects.requireNonNull;
26
27
28
29
30 public class DefaultLastHttpContent extends DefaultHttpObject implements LastHttpContent<DefaultLastHttpContent> {
31 private final HttpHeaders trailingHeaders;
32 private final Buffer payload;
33
34 public DefaultLastHttpContent(Buffer payload) {
35 this(payload, true);
36 }
37
38 public DefaultLastHttpContent(Buffer payload, boolean validateHeaders) {
39 this(payload, new TrailingHttpHeaders(validateHeaders));
40 }
41
42 public DefaultLastHttpContent(Buffer payload, HttpHeaders trailingHeaders) {
43 this.payload = requireNonNull(payload, "payload");
44 this.trailingHeaders = requireNonNull(trailingHeaders, "trailingHeaders");
45 }
46
47 @Override
48 public HttpHeaders trailingHeaders() {
49 return trailingHeaders;
50 }
51
52 @Override
53 public String toString() {
54 StringBuilder buf = new StringBuilder(super.toString());
55 buf.append(StringUtil.NEWLINE);
56 appendHeaders(buf);
57
58
59 buf.setLength(buf.length() - StringUtil.NEWLINE.length());
60 return buf.toString();
61 }
62
63 private void appendHeaders(StringBuilder buf) {
64 for (Entry<String, String> e : trailingHeaders()) {
65 buf.append(e.getKey());
66 buf.append(": ");
67 buf.append(e.getValue());
68 buf.append(StringUtil.NEWLINE);
69 }
70 }
71
72 @Override
73 public Send<DefaultLastHttpContent> send() {
74 return payload.send().map(DefaultLastHttpContent.class,
75 payload -> new DefaultLastHttpContent(payload, trailingHeaders));
76 }
77
78 @Override
79 public DefaultLastHttpContent copy() {
80 return new DefaultLastHttpContent(payload.copy(), trailingHeaders.copy());
81 }
82
83 @Override
84 public void close() {
85 payload.close();
86 }
87
88 @Override
89 public boolean isAccessible() {
90 return payload.isAccessible();
91 }
92
93 @Override
94 public DefaultLastHttpContent touch(Object hint) {
95 payload.touch(hint);
96 return this;
97 }
98
99 @Override
100 public Buffer payload() {
101 return payload;
102 }
103
104 private static final class TrailingHttpHeaders extends DefaultHttpHeaders {
105 private static final NameValidator<CharSequence> TrailerNameValidator = name -> {
106 DefaultHttpHeaders.HttpNameValidator.validateName(name);
107 if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(name)
108 || HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(name)
109 || HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(name)) {
110 throw new IllegalArgumentException("prohibited trailing header: " + name);
111 }
112 };
113
114 @SuppressWarnings({ "unchecked" })
115 TrailingHttpHeaders(boolean validate) {
116 super(validate, validate ? TrailerNameValidator : NameValidator.NOT_NULL);
117 }
118 }
119 }