1 package com.guinetik.rr.http;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 /**
7 * Fluent builder for HTTP request headers with convenience methods for common headers.
8 *
9 * <p>This class provides a type-safe way to build and manage HTTP headers for requests,
10 * with predefined constants for common header names and content types.
11 *
12 * <h2>Building Headers</h2>
13 * <pre class="language-java"><code>
14 * // Create headers with fluent API
15 * RocketHeaders headers = new RocketHeaders()
16 * .contentType(RocketHeaders.ContentTypes.APPLICATION_JSON)
17 * .accept(RocketHeaders.ContentTypes.APPLICATION_JSON)
18 * .set("X-Custom-Header", "custom-value");
19 *
20 * // Use in request
21 * RequestSpec request = RequestBuilder.post("/users")
22 * .headers(headers)
23 * .body(user)
24 * .responseType(User.class)
25 * .build();
26 * </code></pre>
27 *
28 * <h2>Authentication Headers</h2>
29 * <pre class="language-java"><code>
30 * // Bearer token authentication
31 * RocketHeaders headers = new RocketHeaders()
32 * .bearerAuth("my-jwt-token");
33 *
34 * // Basic authentication
35 * RocketHeaders basicAuth = new RocketHeaders()
36 * .basicAuth("username", "password");
37 * </code></pre>
38 *
39 * <h2>Default JSON Headers</h2>
40 * <pre class="language-java"><code>
41 * // Get pre-configured JSON headers
42 * RocketHeaders jsonHeaders = RocketHeaders.defaultJson();
43 * // Equivalent to Content-Type: application/json + Accept: application/json
44 * </code></pre>
45 *
46 * <h2>Merging Headers</h2>
47 * <pre class="language-java"><code>
48 * RocketHeaders base = RocketHeaders.defaultJson();
49 * RocketHeaders auth = new RocketHeaders().bearerAuth("token");
50 *
51 * // Merge headers (auth values take precedence)
52 * RocketHeaders merged = base.merge(auth);
53 * </code></pre>
54 *
55 * @author guinetik <guinetik@gmail.com>
56 * @see com.guinetik.rr.request.RequestBuilder
57 * @since 1.0.0
58 */
59 public class RocketHeaders {
60
61 private final Map<String, String> headers;
62
63 /**
64 * Standard HTTP header names as constants.
65 */
66 public static final class Names {
67 public static final String CONTENT_TYPE = "Content-Type";
68 public static final String ACCEPT = "Accept";
69 public static final String AUTHORIZATION = "Authorization";
70 public static final String USER_AGENT = "User-Agent";
71 public static final String CONTENT_LENGTH = "Content-Length";
72 }
73
74 /**
75 * Common content types as constants.
76 */
77 public static final class ContentTypes {
78 public static final String APPLICATION_JSON = "application/json";
79 public static final String APPLICATION_FORM = "application/x-www-form-urlencoded";
80 public static final String TEXT_PLAIN = "text/plain";
81 public static final String MULTIPART_FORM = "multipart/form-data";
82 }
83
84 /**
85 * Creates a new empty header container.
86 */
87 public RocketHeaders() {
88 this.headers = new HashMap<>();
89 }
90
91 /**
92 * Creates a new header container with the specified headers.
93 *
94 * @param headers The initial headers
95 */
96 public RocketHeaders(Map<String, String> headers) {
97 this.headers = new HashMap<>(headers);
98 }
99
100 /**
101 * Sets a header value.
102 *
103 * @param name The header name
104 * @param value The header value
105 * @return This HttpHeader instance for chaining
106 */
107 public RocketHeaders set(String name, String value) {
108 headers.put(name, value);
109 return this;
110 }
111
112 /**
113 * Gets a header value.
114 *
115 * @param name The header name
116 * @return The header value or null if not present
117 */
118 public String get(String name) {
119 return headers.get(name);
120 }
121
122 /**
123 * Removes a header.
124 *
125 * @param name The header name
126 * @return This HttpHeader instance for chaining
127 */
128 public RocketHeaders remove(String name) {
129 headers.remove(name);
130 return this;
131 }
132
133 /**
134 * Checks if a header is present.
135 *
136 * @param name The header name
137 * @return true if the header is present
138 */
139 public boolean contains(String name) {
140 return headers.containsKey(name);
141 }
142
143 /**
144 * Sets the Content-Type header.
145 *
146 * @param contentType The content type
147 * @return This HttpHeader instance for chaining
148 */
149 public RocketHeaders contentType(String contentType) {
150 return set(Names.CONTENT_TYPE, contentType);
151 }
152
153 /**
154 * Sets the Accept header.
155 *
156 * @param accept The accept value
157 * @return This HttpHeader instance for chaining
158 */
159 public RocketHeaders accept(String accept) {
160 return set(Names.ACCEPT, accept);
161 }
162
163 /**
164 * Sets the Authorization header with a Bearer token.
165 *
166 * @param token The token
167 * @return This HttpHeader instance for chaining
168 */
169 public RocketHeaders bearerAuth(String token) {
170 return set(Names.AUTHORIZATION, "Bearer " + token);
171 }
172
173 /**
174 * Sets the Authorization header with Basic authentication.
175 *
176 * @param username The username
177 * @param password The password
178 * @return This HttpHeader instance for chaining
179 */
180 public RocketHeaders basicAuth(String username, String password) {
181 String auth = java.util.Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
182 return set(Names.AUTHORIZATION, "Basic " + auth);
183 }
184
185 /**
186 * Gets all headers as a Map.
187 *
188 * @return An unmodifiable view of the header map
189 */
190 public Map<String, String> asMap() {
191 return java.util.Collections.unmodifiableMap(headers);
192 }
193
194 /**
195 * Merges this header with another, with values from the other taking precedence.
196 *
197 * @param other The other HttpHeader instance
198 * @return A new HttpHeader instance with merged values
199 */
200 public RocketHeaders merge(RocketHeaders other) {
201 RocketHeaders result = new RocketHeaders(this.headers);
202 result.headers.putAll(other.headers);
203 return result;
204 }
205
206 /**
207 * Creates a set of default headers with JSON content type.
208 *
209 * @return A new HttpHeader with JSON content type and accept headers
210 */
211 public static RocketHeaders defaultJson() {
212 return new RocketHeaders()
213 .contentType(ContentTypes.APPLICATION_JSON)
214 .accept(ContentTypes.APPLICATION_JSON);
215 }
216 }