Source: client/http-response-handler.js

  1. /*
  2. * Copyright 2023, TeamDev. All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Redistribution and use in source and/or binary forms, with or without
  11. * modification, must retain the above copyright notice and the following
  12. * disclaimer.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  17. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  18. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  20. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. "use strict";
  27. import {ClientError, ConnectionError, ServerError, SpineError} from "./errors";
  28. /**
  29. * Receives the HTTP response, and turns it into a JS object.
  30. *
  31. * Handles the response failures as well, in which case a corresponding error object
  32. * is returned via a `Promise`.
  33. *
  34. * Only `2xx` response codes count as successful. All other response
  35. * codes are considered erroneous.
  36. *
  37. * By default, expects the input to be a JSON string. Users may choose to customize the behavior
  38. * by extending this type, and supplying the custom implementation via {@link ClientOptions}.
  39. */
  40. export class HttpResponseHandler {
  41. /**
  42. * Retrieves the JS object by transforming the contents
  43. * of the given HTTP response if it was successful,
  44. * rejects with a respective error otherwise.
  45. *
  46. * @param {!Response} response an HTTP request response
  47. * @return {Promise<Object|SpineError>} a promise of a successful server response data,
  48. * rejected if the client response is not `2xx`,
  49. * or if the transformation-to-object fails
  50. * for the response contents.
  51. * @see parse
  52. */
  53. handle(response) {
  54. const statusCode = response.status;
  55. if (HttpResponseHandler._isSuccessfulResponse(statusCode)) {
  56. return this.parse(response);
  57. } else if (HttpResponseHandler._isClientErrorResponse(statusCode)) {
  58. return Promise.reject(new ClientError(response.statusText, response));
  59. } else if (HttpResponseHandler._isServerErrorResponse(statusCode)) {
  60. return Promise.reject(new ServerError(response));
  61. }
  62. }
  63. /**
  64. * Transforms the response into JS object by parsing the response contents.
  65. *
  66. * This implementation expects the response to contain JSON data.
  67. *
  68. * @param response an HTTP response
  69. * @return {Promise<Object|SpineError>} a promise of JS object,
  70. * or a rejection with the corresponding `SpineError`
  71. * @protected
  72. */
  73. parse(response) {
  74. return response.json()
  75. .then(json => Promise.resolve(json))
  76. .catch(error =>
  77. Promise.reject(new SpineError('Failed to parse response JSON', error))
  78. );
  79. }
  80. /**
  81. * Obtains the error caught from and erroneous HTTP request, and returns
  82. * a rejected promise with a given error wrapped into {@link ConnectionError}.
  83. *
  84. * This handling method differs from others, since it is designed to handle the issues
  85. * which were caused by an inability to send the HTTP request itself — so in this case
  86. * there is no HTTP response. Note, that {@link handle} is designed
  87. * to process the HTTP response, including erroneous responses.
  88. *
  89. * @param {!Error} error an error which occurred upon sending an HTTP request
  90. * @return {Promise<ConnectionError>} a rejected promise with a `ConnectionError`
  91. */
  92. onConnectionError(error) {
  93. return Promise.reject(new ConnectionError(error));
  94. }
  95. /**
  96. * @param {!number} statusCode an HTTP request response status code
  97. * @return {boolean} `true` if the response status code is from 200 to 299,
  98. * `false` otherwise
  99. * @protected
  100. */
  101. static _isSuccessfulResponse(statusCode) {
  102. return 200 <= statusCode && statusCode < 300;
  103. }
  104. /**
  105. * @param {!number} statusCode an HTTP request response status code
  106. * @return {boolean} `true` if the response status code is from 400 to 499,
  107. * `false` otherwise
  108. * @protected
  109. */
  110. static _isClientErrorResponse(statusCode) {
  111. return 400 <= statusCode && statusCode < 500;
  112. }
  113. /**
  114. * @param {!number} statusCode an HTTP request response status code
  115. * @return {boolean} `true` if the response status code is from 500,
  116. * `false` otherwise
  117. * @protected
  118. */
  119. static _isServerErrorResponse(statusCode) {
  120. return 500 <= statusCode;
  121. }
  122. }