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 com.nimbusds.jwt.JWT; 026import com.nimbusds.jwt.JWTParser; 027import com.nimbusds.oauth2.sdk.ParseException; 028import com.nimbusds.oauth2.sdk.util.JSONArrayUtils; 029import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 030import net.jcip.annotations.ThreadSafe; 031import net.minidev.json.JSONArray; 032import net.minidev.json.JSONObject; 033 034 035/** 036 * HTTP response with support for the parameters required to construct an 037 * {@link com.nimbusds.oauth2.sdk.Response OAuth 2.0 response message}. 038 * 039 * <p>Provided HTTP status code constants: 040 * 041 * <ul> 042 * <li>{@link #SC_OK HTTP 200 OK} 043 * <li>{@link #SC_CREATED HTTP 201 Created} 044 * <li>{@link #SC_FOUND HTTP 302 Redirect} 045 * <li>{@link #SC_BAD_REQUEST HTTP 400 Bad request} 046 * <li>{@link #SC_UNAUTHORIZED HTTP 401 Unauthorized} 047 * <li>{@link #SC_FORBIDDEN HTTP 403 Forbidden} 048 * <li>{@link #SC_SERVER_ERROR HTTP 500 Server error} 049 * </ul> 050 * 051 * <p>Supported response headers: 052 * 053 * <ul> 054 * <li>Location 055 * <li>Content-Type 056 * <li>Cache-Control 057 * <li>Pragma 058 * <li>Www-Authenticate 059 * </ul> 060 */ 061@ThreadSafe 062public class HTTPResponse extends HTTPMessage { 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 (500) indicating an internal server error. 107 */ 108 public static final int SC_SERVER_ERROR = 500; 109 110 111 /** 112 * HTTP status code (503) indicating the server is unavailable. 113 */ 114 public static final int SC_SERVICE_UNAVAILABLE = 503; 115 116 117 /** 118 * The HTTP status code. 119 */ 120 private final int statusCode; 121 122 123 /** 124 * The HTTP status message, {@code null} if not specified. 125 */ 126 private String statusMessage; 127 128 129 /** 130 * The raw response content. 131 */ 132 private String content = null; 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 /** 210 * Gets the HTTP status message. 211 * 212 * @return The HTTP status message, {@code null} if not specified. 213 */ 214 public String getStatusMessage() { 215 216 return statusMessage; 217 } 218 219 220 /** 221 * Sets the HTTP status message. 222 * 223 * @param message The HTTP status message, {@code null} if not 224 * specified. 225 */ 226 public void setStatusMessage(final String message) { 227 228 this.statusMessage = message; 229 } 230 231 232 /** 233 * Gets the {@code Location} header value (for redirects). 234 * 235 * @return The header value, {@code null} if not specified. 236 */ 237 public URI getLocation() { 238 239 String value = getHeaderValue("Location"); 240 241 if (value == null) { 242 return null; 243 } 244 245 try { 246 return new URI(value); 247 248 } catch (URISyntaxException e) { 249 return null; 250 } 251 } 252 253 254 /** 255 * Sets the {@code Location} header value (for redirects). 256 * 257 * @param location The header value, {@code null} if not specified. 258 */ 259 public void setLocation(final URI location) { 260 261 setHeader("Location", location != null ? location.toString() : null); 262 } 263 264 265 /** 266 * Gets the {@code Cache-Control} header value. 267 * 268 * @return The header value, {@code null} if not specified. 269 */ 270 public String getCacheControl() { 271 272 return getHeaderValue("Cache-Control"); 273 } 274 275 276 /** 277 * Sets the {@code Cache-Control} header value. 278 * 279 * @param cacheControl The header value, {@code null} if not specified. 280 */ 281 public void setCacheControl(final String cacheControl) { 282 283 setHeader("Cache-Control", cacheControl); 284 } 285 286 287 /** 288 * Gets the {@code Pragma} header value. 289 * 290 * @return The header value, {@code null} if not specified. 291 */ 292 public String getPragma() { 293 294 return getHeaderValue("Pragma"); 295 } 296 297 298 /** 299 * Sets the {@code Pragma} header value. 300 * 301 * @param pragma The header value, {@code null} if not specified. 302 */ 303 public void setPragma(final String pragma) { 304 305 setHeader("Pragma", pragma); 306 } 307 308 309 /** 310 * Gets the {@code WWW-Authenticate} header value. 311 * 312 * @return The header value, {@code null} if not specified. 313 */ 314 public String getWWWAuthenticate() { 315 316 return getHeaderValue("WWW-Authenticate"); 317 } 318 319 320 /** 321 * Sets the {@code WWW-Authenticate} header value. 322 * 323 * @param wwwAuthenticate The header value, {@code null} if not 324 * specified. 325 */ 326 public void setWWWAuthenticate(final String wwwAuthenticate) { 327 328 setHeader("WWW-Authenticate", wwwAuthenticate); 329 } 330 331 332 /** 333 * Ensures this HTTP response has a specified content body. 334 * 335 * @throws ParseException If the content body is missing or empty. 336 */ 337 private void ensureContent() 338 throws ParseException { 339 340 if (content == null || content.isEmpty()) 341 throw new ParseException("Missing or empty HTTP response body"); 342 } 343 344 345 /** 346 * Gets the raw response content. 347 * 348 * @return The raw response content, {@code null} if none. 349 */ 350 public String getContent() { 351 352 return content; 353 } 354 355 356 /** 357 * Gets the response content as a JSON object. 358 * 359 * @return The response content as a JSON object. 360 * 361 * @throws ParseException If the Content-Type header isn't 362 * {@code application/json}, the response 363 * content is {@code null}, empty or couldn't be 364 * parsed to a valid JSON object. 365 */ 366 public JSONObject getContentAsJSONObject() 367 throws ParseException { 368 369 ensureContentType(CommonContentTypes.APPLICATION_JSON); 370 371 ensureContent(); 372 373 return JSONObjectUtils.parse(content); 374 } 375 376 377 /** 378 * Gets the response content as a JSON array. 379 * 380 * @return The response content as a JSON array. 381 * 382 * @throws ParseException If the Content-Type header isn't 383 * {@code application/json}, the response 384 * content is {@code null}, empty or couldn't be 385 * parsed to a valid JSON array. 386 */ 387 public JSONArray getContentAsJSONArray() 388 throws ParseException { 389 390 ensureContentType(CommonContentTypes.APPLICATION_JSON); 391 392 ensureContent(); 393 394 return JSONArrayUtils.parse(content); 395 } 396 397 398 /** 399 * Gets the response content as a JSON Web Token (JWT). 400 * 401 * @return The response content as a JSON Web Token (JWT). 402 * 403 * @throws ParseException If the Content-Type header isn't 404 * {@code application/jwt}, the response content 405 * is {@code null}, empty or couldn't be parsed 406 * to a valid JSON Web Token (JWT). 407 */ 408 public JWT getContentAsJWT() 409 throws ParseException { 410 411 ensureContentType(CommonContentTypes.APPLICATION_JWT); 412 413 ensureContent(); 414 415 try { 416 return JWTParser.parse(content); 417 418 } catch (java.text.ParseException e) { 419 420 throw new ParseException(e.getMessage(), e); 421 } 422 } 423 424 425 /** 426 * Sets the raw response content. 427 * 428 * @param content The raw response content, {@code null} if none. 429 */ 430 public void setContent(final String content) { 431 432 this.content = content; 433 } 434}