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 (500) indicating an internal server error. 109 */ 110 public static final int SC_SERVER_ERROR = 500; 111 112 113 /** 114 * HTTP status code (503) indicating the server is unavailable. 115 */ 116 public static final int SC_SERVICE_UNAVAILABLE = 503; 117 118 119 /** 120 * The HTTP status code. 121 */ 122 private final int statusCode; 123 124 125 /** 126 * The HTTP status message, {@code null} if not specified. 127 */ 128 private String statusMessage; 129 130 131 /** 132 * The raw response content. 133 */ 134 private String content = null; 135 136 137 /** 138 * Creates a new minimal HTTP response with the specified status code. 139 * 140 * @param statusCode The HTTP status code. 141 */ 142 public HTTPResponse(final int statusCode) { 143 144 this.statusCode = statusCode; 145 } 146 147 148 /** 149 * Gets the HTTP status code. 150 * 151 * @return The HTTP status code. 152 */ 153 public int getStatusCode() { 154 155 return statusCode; 156 } 157 158 159 /** 160 * Returns {@code true} if the HTTP status code indicates success 161 * (2xx). 162 * 163 * @return {@code true} if the HTTP status code indicates success, else 164 * {@code false}. 165 */ 166 public boolean indicatesSuccess() { 167 168 return statusCode >= 200 && statusCode < 300; 169 } 170 171 172 /** 173 * Ensures this HTTP response has the specified status code. 174 * 175 * @param expectedStatusCode The expected status code(s). 176 * 177 * @throws ParseException If the status code of this HTTP response 178 * doesn't match the expected. 179 */ 180 public void ensureStatusCode(final int ... expectedStatusCode) 181 throws ParseException { 182 183 for (int c: expectedStatusCode) { 184 185 if (this.statusCode == c) 186 return; 187 } 188 189 throw new ParseException("Unexpected HTTP status code " + 190 this.statusCode + 191 ", must be " + 192 Arrays.toString(expectedStatusCode)); 193 } 194 195 196 /** 197 * Ensures this HTTP response does not have a {@link #SC_OK 200 OK} 198 * status code. 199 * 200 * @throws ParseException If the status code of this HTTP response is 201 * 200 OK. 202 */ 203 public void ensureStatusCodeNotOK() 204 throws ParseException { 205 206 if (statusCode == SC_OK) 207 throw new ParseException("Unexpected HTTP status code, must not be 200 (OK)"); 208 } 209 210 211 /** 212 * Gets the HTTP status message. 213 * 214 * @return The HTTP status message, {@code null} if not specified. 215 */ 216 public String getStatusMessage() { 217 218 return statusMessage; 219 } 220 221 222 /** 223 * Sets the HTTP status message. 224 * 225 * @param message The HTTP status message, {@code null} if not 226 * specified. 227 */ 228 public void setStatusMessage(final String message) { 229 230 this.statusMessage = message; 231 } 232 233 234 /** 235 * Gets the {@code Location} header value (for redirects). 236 * 237 * @return The header value, {@code null} if not specified. 238 */ 239 public URI getLocation() { 240 241 String value = getHeaderValue("Location"); 242 243 if (value == null) { 244 return null; 245 } 246 247 try { 248 return new URI(value); 249 250 } catch (URISyntaxException e) { 251 return null; 252 } 253 } 254 255 256 /** 257 * Sets the {@code Location} header value (for redirects). 258 * 259 * @param location The header value, {@code null} if not specified. 260 */ 261 public void setLocation(final URI location) { 262 263 setHeader("Location", location != null ? location.toString() : null); 264 } 265 266 267 /** 268 * Gets the {@code Cache-Control} header value. 269 * 270 * @return The header value, {@code null} if not specified. 271 */ 272 public String getCacheControl() { 273 274 return getHeaderValue("Cache-Control"); 275 } 276 277 278 /** 279 * Sets the {@code Cache-Control} header value. 280 * 281 * @param cacheControl The header value, {@code null} if not specified. 282 */ 283 public void setCacheControl(final String cacheControl) { 284 285 setHeader("Cache-Control", cacheControl); 286 } 287 288 289 /** 290 * Gets the {@code Pragma} header value. 291 * 292 * @return The header value, {@code null} if not specified. 293 */ 294 public String getPragma() { 295 296 return getHeaderValue("Pragma"); 297 } 298 299 300 /** 301 * Sets the {@code Pragma} header value. 302 * 303 * @param pragma The header value, {@code null} if not specified. 304 */ 305 public void setPragma(final String pragma) { 306 307 setHeader("Pragma", pragma); 308 } 309 310 311 /** 312 * Gets the {@code WWW-Authenticate} header value. 313 * 314 * @return The header value, {@code null} if not specified. 315 */ 316 public String getWWWAuthenticate() { 317 318 return getHeaderValue("WWW-Authenticate"); 319 } 320 321 322 /** 323 * Sets the {@code WWW-Authenticate} header value. 324 * 325 * @param wwwAuthenticate The header value, {@code null} if not 326 * specified. 327 */ 328 public void setWWWAuthenticate(final String wwwAuthenticate) { 329 330 setHeader("WWW-Authenticate", wwwAuthenticate); 331 } 332 333 334 /** 335 * Ensures this HTTP response has a specified content body. 336 * 337 * @throws ParseException If the content body is missing or empty. 338 */ 339 private void ensureContent() 340 throws ParseException { 341 342 if (content == null || content.isEmpty()) 343 throw new ParseException("Missing or empty HTTP response body"); 344 } 345 346 347 /** 348 * Gets the raw response content. 349 * 350 * @return The raw response content, {@code null} if none. 351 */ 352 public String getContent() { 353 354 return content; 355 } 356 357 358 /** 359 * Gets the response content as a JSON object. 360 * 361 * @return The response content as a JSON object. 362 * 363 * @throws ParseException If the Content-Type header isn't 364 * {@code application/json}, the response 365 * content is {@code null}, empty or couldn't be 366 * parsed to a valid JSON object. 367 */ 368 public JSONObject getContentAsJSONObject() 369 throws ParseException { 370 371 ensureEntityContentType(ContentType.APPLICATION_JSON); 372 373 ensureContent(); 374 375 return JSONObjectUtils.parse(content); 376 } 377 378 379 /** 380 * Gets the response content as a JSON array. 381 * 382 * @return The response content as a JSON array. 383 * 384 * @throws ParseException If the Content-Type header isn't 385 * {@code application/json}, the response 386 * content is {@code null}, empty or couldn't be 387 * parsed to a valid JSON array. 388 */ 389 public JSONArray getContentAsJSONArray() 390 throws ParseException { 391 392 ensureEntityContentType(ContentType.APPLICATION_JSON); 393 394 ensureContent(); 395 396 return JSONArrayUtils.parse(content); 397 } 398 399 400 /** 401 * Gets the response content as a JSON Web Token (JWT). 402 * 403 * @return The response content as a JSON Web Token (JWT). 404 * 405 * @throws ParseException If the Content-Type header isn't 406 * {@code application/jwt}, the response content 407 * is {@code null}, empty or couldn't be parsed 408 * to a valid JSON Web Token (JWT). 409 */ 410 public JWT getContentAsJWT() 411 throws ParseException { 412 413 ensureEntityContentType(ContentType.APPLICATION_JWT); 414 415 ensureContent(); 416 417 try { 418 return JWTParser.parse(content); 419 420 } catch (java.text.ParseException e) { 421 422 throw new ParseException(e.getMessage(), e); 423 } 424 } 425 426 427 /** 428 * Sets the raw response content. 429 * 430 * @param content The raw response content, {@code null} if none. 431 */ 432 public void setContent(final String content) { 433 434 this.content = content; 435 } 436}