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