View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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   * The default {@link LastHttpContent} implementation.
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          // Remove the last newline.
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 }