001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.oauth2.sdk.http; 019 020 021import com.nimbusds.jwt.JWT; 022import com.nimbusds.oauth2.sdk.ParseException; 023import com.nimbusds.openid.connect.sdk.Nonce; 024import net.jcip.annotations.ThreadSafe; 025import net.minidev.json.JSONArray; 026import net.minidev.json.JSONObject; 027 028import java.net.URI; 029import java.net.URISyntaxException; 030import java.util.Arrays; 031 032 033/** 034 * HTTP response with support for the parameters required to construct an 035 * {@link com.nimbusds.oauth2.sdk.Response OAuth 2.0 response message}. 036 * 037 * <p>Provided HTTP status code constants: 038 * 039 * <ul> 040 * <li>{@link #SC_OK HTTP 200 OK} 041 * <li>{@link #SC_CREATED HTTP 201 Created} 042 * <li>{@link #SC_FOUND HTTP 302 Redirect} 043 * <li>{@link #SC_BAD_REQUEST HTTP 400 Bad request} 044 * <li>{@link #SC_UNAUTHORIZED HTTP 401 Unauthorized} 045 * <li>{@link #SC_FORBIDDEN HTTP 403 Forbidden} 046 * <li>{@link #SC_SERVER_ERROR HTTP 500 Server error} 047 * </ul> 048 * 049 * <p>Supported response headers: 050 * 051 * <ul> 052 * <li>Location 053 * <li>Content-Type 054 * <li>Cache-Control 055 * <li>Pragma 056 * <li>Www-Authenticate 057 * <li>DPoP-Nonce 058 * <li>Etc. 059 * </ul> 060 */ 061@ThreadSafe 062public class HTTPResponse extends HTTPMessage implements ReadOnlyHTTPResponse { 063 064 065 /** 066 * HTTP status code (200) indicating the request succeeded. 067 */ 068 public static final int SC_OK = 200; 069 070 071 /** 072 * HTTP status code (201) indicating the request succeeded with a new 073 * resource being created. 074 */ 075 public static final int SC_CREATED = 201; 076 077 078 /** 079 * HTTP status code (302) indicating that the resource resides 080 * temporarily under a different URI (redirect). 081 */ 082 public static final int SC_FOUND = 302; 083 084 085 /** 086 * HTTP status code (400) indicating a bad request. 087 */ 088 public static final int SC_BAD_REQUEST = 400; 089 090 091 /** 092 * HTTP status code (401) indicating that the request requires HTTP 093 * authentication. 094 */ 095 public static final int SC_UNAUTHORIZED = 401; 096 097 098 /** 099 * HTTP status code (403) indicating that access to the resource was 100 * forbidden. 101 */ 102 public static final int SC_FORBIDDEN = 403; 103 104 105 /** 106 * HTTP status code (404) indicating the resource was not found. 107 */ 108 public static final int SC_NOT_FOUND = 404; 109 110 111 /** 112 * HTTP status code (500) indicating an internal server error. 113 */ 114 public static final int SC_SERVER_ERROR = 500; 115 116 117 /** 118 * HTTP status code (503) indicating the server is unavailable. 119 */ 120 public static final int SC_SERVICE_UNAVAILABLE = 503; 121 122 123 /** 124 * The HTTP status code. 125 */ 126 private final int statusCode; 127 128 129 /** 130 * The HTTP status message, {@code null} if not specified. 131 */ 132 private String statusMessage; 133 134 135 /** 136 * Creates a new minimal HTTP response with the specified status code. 137 * 138 * @param statusCode The HTTP status code. 139 */ 140 public HTTPResponse(final int statusCode) { 141 142 this.statusCode = statusCode; 143 } 144 145 146 /** 147 * Gets the HTTP status code. 148 * 149 * @return The HTTP status code. 150 */ 151 public int getStatusCode() { 152 153 return statusCode; 154 } 155 156 157 /** 158 * Returns {@code true} if the HTTP status code indicates success 159 * (2xx). 160 * 161 * @return {@code true} if the HTTP status code indicates success, else 162 * {@code false}. 163 */ 164 public boolean indicatesSuccess() { 165 166 return statusCode >= 200 && statusCode < 300; 167 } 168 169 170 /** 171 * Ensures this HTTP response has the specified status code. 172 * 173 * @param expectedStatusCode The expected status code(s). 174 * 175 * @throws ParseException If the status code of this HTTP response 176 * doesn't match the expected. 177 */ 178 public void ensureStatusCode(final int ... expectedStatusCode) 179 throws ParseException { 180 181 for (int c: expectedStatusCode) { 182 183 if (this.statusCode == c) 184 return; 185 } 186 187 throw new ParseException("Unexpected HTTP status code " + 188 this.statusCode + 189 ", must be " + 190 Arrays.toString(expectedStatusCode)); 191 } 192 193 194 /** 195 * Ensures this HTTP response does not have a {@link #SC_OK 200 OK} 196 * status code. 197 * 198 * @throws ParseException If the status code of this HTTP response is 199 * 200 OK. 200 */ 201 public void ensureStatusCodeNotOK() 202 throws ParseException { 203 204 if (statusCode == SC_OK) 205 throw new ParseException("Unexpected HTTP status code, must not be 200 (OK)"); 206 } 207 208 209 @Override 210 public String getStatusMessage() { 211 212 return statusMessage; 213 } 214 215 216 /** 217 * Sets the HTTP status message. 218 * 219 * @param message The HTTP status message, {@code null} if not 220 * specified. 221 */ 222 public void setStatusMessage(final String message) { 223 224 this.statusMessage = message; 225 } 226 227 228 /** 229 * Gets the {@code Location} header value (for redirects). 230 * 231 * @return The header value, {@code null} if not specified. 232 */ 233 public URI getLocation() { 234 235 String value = getHeaderValue("Location"); 236 237 if (value == null) { 238 return null; 239 } 240 241 try { 242 return new URI(value); 243 244 } catch (URISyntaxException e) { 245 return null; 246 } 247 } 248 249 250 /** 251 * Sets the {@code Location} header value (for redirects). 252 * 253 * @param location The header value, {@code null} if not specified. 254 */ 255 public void setLocation(final URI location) { 256 257 setHeader("Location", location != null ? location.toString() : null); 258 } 259 260 261 /** 262 * Gets the {@code Cache-Control} header value. 263 * 264 * @return The header value, {@code null} if not specified. 265 */ 266 public String getCacheControl() { 267 268 return getHeaderValue("Cache-Control"); 269 } 270 271 272 /** 273 * Sets the {@code Cache-Control} header value. 274 * 275 * @param cacheControl The header value, {@code null} if not specified. 276 */ 277 public void setCacheControl(final String cacheControl) { 278 279 setHeader("Cache-Control", cacheControl); 280 } 281 282 283 /** 284 * Gets the {@code Pragma} header value. 285 * 286 * @return The header value, {@code null} if not specified. 287 */ 288 public String getPragma() { 289 290 return getHeaderValue("Pragma"); 291 } 292 293 294 /** 295 * Sets the {@code Pragma} header value. 296 * 297 * @param pragma The header value, {@code null} if not specified. 298 */ 299 public void setPragma(final String pragma) { 300 301 setHeader("Pragma", pragma); 302 } 303 304 305 /** 306 * Gets the {@code WWW-Authenticate} header value. 307 * 308 * @return The header value, {@code null} if not specified. 309 */ 310 public String getWWWAuthenticate() { 311 312 return getHeaderValue("WWW-Authenticate"); 313 } 314 315 316 /** 317 * Sets the {@code WWW-Authenticate} header value. 318 * 319 * @param wwwAuthenticate The header value, {@code null} if not 320 * specified. 321 */ 322 public void setWWWAuthenticate(final String wwwAuthenticate) { 323 324 setHeader("WWW-Authenticate", wwwAuthenticate); 325 } 326 327 328 /** 329 * Gets the {@code DPoP-Nonce} header value. 330 * 331 * @return The {@code DPoP-Nonce} header value, {@code null} if not 332 * specified or parsing failed. 333 */ 334 public Nonce getDPoPNonce() { 335 336 String nonce = getHeaderValue("DPoP-Nonce"); 337 if (nonce == null) { 338 return null; 339 } 340 341 try { 342 return new Nonce(nonce); 343 } catch (IllegalArgumentException e) { 344 return null; 345 } 346 } 347 348 349 /** 350 * Sets the {@code DPoP-Nonce} header value. 351 * 352 * @param nonce The {@code DPoP-Nonce} header value, {@code null} if 353 * not specified. 354 */ 355 public void setDPoPNonce(final Nonce nonce) { 356 357 if (nonce != null) { 358 setHeader("DPoP-Nonce", nonce.getValue()); 359 } else { 360 setHeader("DPoP-Nonce", (String[]) null); 361 } 362 } 363 364 365 /** 366 * Gets the raw response content. 367 * 368 * @deprecated Use {@link #getBody()}. 369 * 370 * @return The raw response content, {@code null} if none. 371 */ 372 @Deprecated 373 public String getContent() { 374 375 return getBody(); 376 } 377 378 379 /** 380 * Gets the response content as a JSON object. 381 * 382 * @deprecated Use {@link #getBodyAsJSONObject()}. 383 * 384 * @return The response content as a JSON object. 385 * 386 * @throws ParseException If the Content-Type header isn't 387 * {@code application/json}, the response 388 * content is {@code null}, empty or couldn't be 389 * parsed to a valid JSON object. 390 */ 391 @Deprecated 392 public JSONObject getContentAsJSONObject() 393 throws ParseException { 394 395 return getBodyAsJSONObject(); 396 } 397 398 399 /** 400 * Gets the response content as a JSON array. 401 * 402 * @deprecated Use {@link #getBodyAsJSONArray()}. 403 * 404 * @return The response content as a JSON array. 405 * 406 * @throws ParseException If the Content-Type header isn't 407 * {@code application/json}, the response 408 * content is {@code null}, empty or couldn't be 409 * parsed to a valid JSON array. 410 */ 411 @Deprecated 412 public JSONArray getContentAsJSONArray() 413 throws ParseException { 414 415 return getBodyAsJSONArray(); 416 } 417 418 419 /** 420 * Gets the response content as a JSON Web Token (JWT). 421 * 422 * @deprecated Use {@link #getBodyAsJWT()}. 423 * 424 * @return The response content as a JSON Web Token (JWT). 425 * 426 * @throws ParseException If the Content-Type header isn't 427 * {@code application/jwt}, the response content 428 * is {@code null}, empty or couldn't be parsed 429 * to a valid JSON Web Token (JWT). 430 */ 431 @Deprecated 432 public JWT getContentAsJWT() 433 throws ParseException { 434 435 return getBodyAsJWT(); 436 } 437 438 439 /** 440 * Sets the raw response content. 441 * 442 * @deprecated Use {@link #setBody(String)}. 443 * 444 * @param content The raw response content, {@code null} if none. 445 */ 446 @Deprecated 447 public void setContent(final String content) { 448 449 setBody(content); 450 } 451}