• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 package software.amazon.awssdk.crt.http;
7 
8 import java.nio.ByteBuffer;
9 import java.nio.charset.Charset;
10 import java.nio.charset.StandardCharsets;
11 import java.util.ArrayList;
12 import java.util.List;
13 
14 /**
15  * A wrapper class for http header key-value pairs
16  */
17 public class HttpHeader {
18     private final static int BUFFER_INT_SIZE = 4;
19     private final static Charset UTF8 = StandardCharsets.UTF_8;
20     private byte[] name;  /* Not final, Native will manually set name after calling empty Constructor. */
21     private byte[] value; /* Not final, Native will manually set value after calling empty Constructor. */
22 
23     /** Called by Native to create a new HttpHeader. This is so that Native doesn't have to worry about UTF8
24      * encoding/decoding issues. The user thread will deal with them when they call getName() or getValue() **/
HttpHeader()25     private HttpHeader() {}
26 
27     /**
28      *
29      * @param name header name
30      * @param value header value
31      */
HttpHeader(String name, String value)32     public HttpHeader(String name, String value){
33         this.name = name.getBytes(UTF8);
34         this.value = value.getBytes(UTF8);
35     }
36 
37     /**
38      *
39      * @param name header name
40      * @param value header value
41      */
HttpHeader(byte[] name, byte[] value)42     public HttpHeader(byte[] name, byte[] value){
43         this.name = name;
44         this.value = value;
45     }
46 
47     /**
48      *
49      * @return the name of the header, converted to a UTF-8 string
50      */
getName()51     public String getName() {
52         if (name == null) {
53             return "";
54         }
55         return new String(name, UTF8);
56     }
57 
58     /**
59      *
60      * @return the name of the header, in raw bytes
61      */
getNameBytes()62     public byte[] getNameBytes() {
63         return name;
64     }
65 
66     /**
67      *
68      * @return the value of the header, converted to a UTF-8 string
69      */
getValue()70     public String getValue() {
71         if (value == null) {
72             return "";
73         }
74         return new String(value, UTF8);
75     }
76 
77     /**
78      *
79      * @return the value of the header, in raw bytes
80      */
getValueBytes()81     public byte[] getValueBytes() {
82         return value;
83     }
84 
85     @Override
toString()86     public String toString() {
87         return getName() + ":" + getValue();
88     }
89 
90     /** Each header is marshalled as
91      * [4-bytes BE name length] [variable length name value] [4-bytes BE value length] [variable length value value]
92      * @param headersBlob Blob of encoded headers
93      * @return array of decoded headers
94      */
loadHeadersListFromMarshalledHeadersBlob(ByteBuffer headersBlob)95     public static List<HttpHeader> loadHeadersListFromMarshalledHeadersBlob(ByteBuffer headersBlob) {
96         List<HttpHeader> headers = new ArrayList<>(16);
97 
98         while(headersBlob.hasRemaining()) {
99             int nameLen = headersBlob.getInt();
100 
101             // we want to protect against 0 length header names, 0 length values are fine.
102             // the marshalling layer will make sure that even if a length is 0, the 0 will
103             // still be stored in the byte array.
104             if (nameLen > 0) {
105                 byte[] nameBuf = new byte[nameLen];
106                 headersBlob.get(nameBuf);
107                 int valLen = headersBlob.getInt();
108                 byte[] valueBuf = new byte[valLen];
109                 headersBlob.get(valueBuf);
110                 headers.add(new HttpHeader(nameBuf, valueBuf));
111             }
112         }
113 
114         return headers;
115     }
116 
117     /**
118      * Lists of headers are marshalled as follows:
119      *
120      * each string field is: [4-bytes BE] [variable length bytes specified by the
121      * previous field]
122      *
123      * @param headers List of header name-value pairs
124      *
125      * @return encoded blob of headers
126      */
marshalHeadersForJni(List<HttpHeader> headers)127     public static byte[] marshalHeadersForJni(List<HttpHeader> headers) {
128         int size = 0;
129 
130         for (HttpHeader header : headers) {
131             if (header.getNameBytes().length > 0) {
132                 size += header.getNameBytes().length + header.getValueBytes().length + (BUFFER_INT_SIZE * 2);
133             }
134         }
135 
136         ByteBuffer buffer = ByteBuffer.allocate(size);
137         for (HttpHeader header : headers) {
138             if (header.getNameBytes().length > 0) {
139                 buffer.putInt(header.getNameBytes().length);
140                 buffer.put(header.getNameBytes());
141                 buffer.putInt(header.getValueBytes().length);
142                 buffer.put(header.getValueBytes());
143             }
144         }
145 
146         return buffer.array();
147     }
148 
149 
150     /**
151      * @param headersBlob encoded headers blob
152      * @return array of headers
153      * @see #loadHeadersListFromMarshalledHeadersBlob
154      */
loadHeadersFromMarshalledHeadersBlob(ByteBuffer headersBlob)155     public static HttpHeader[] loadHeadersFromMarshalledHeadersBlob(ByteBuffer headersBlob) {
156         List<HttpHeader> headers = loadHeadersListFromMarshalledHeadersBlob(headersBlob);
157         HttpHeader[] headersArray = new HttpHeader[headers.size()];
158         return headers.toArray(headersArray);
159     }
160 }
161