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