happy-dom 8.8.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of happy-dom might be problematic. Click here for more details.
- package/README.md +2 -2
- package/lib/cookie/CookieJar.js +1 -1
- package/lib/cookie/CookieJar.js.map +1 -1
- package/lib/dom-token-list/DOMTokenList.js +24 -20
- package/lib/dom-token-list/DOMTokenList.js.map +1 -1
- package/lib/event/EventTarget.js +6 -3
- package/lib/event/EventTarget.js.map +1 -1
- package/lib/exception/DOMExceptionNameEnum.d.ts +5 -1
- package/lib/exception/DOMExceptionNameEnum.js +4 -0
- package/lib/exception/DOMExceptionNameEnum.js.map +1 -1
- package/lib/fetch/AbortController.d.ts +19 -0
- package/lib/fetch/AbortController.js +30 -0
- package/lib/fetch/AbortController.js.map +1 -0
- package/lib/fetch/AbortSignal.d.ts +25 -0
- package/lib/fetch/AbortSignal.js +51 -0
- package/lib/fetch/AbortSignal.js.map +1 -0
- package/lib/fetch/Fetch.d.ts +117 -0
- package/lib/fetch/Fetch.js +530 -0
- package/lib/fetch/Fetch.js.map +1 -0
- package/lib/fetch/Headers.d.ts +81 -3
- package/lib/fetch/Headers.js +139 -24
- package/lib/fetch/Headers.js.map +1 -1
- package/lib/fetch/Request.d.ts +56 -32
- package/lib/fetch/Request.js +165 -110
- package/lib/fetch/Request.js.map +1 -1
- package/lib/fetch/{ResourceFetchHandler.d.ts → ResourceFetch.d.ts} +1 -1
- package/lib/fetch/{ResourceFetchHandler.js → ResourceFetch.js} +5 -5
- package/lib/fetch/ResourceFetch.js.map +1 -0
- package/lib/fetch/Response.d.ts +60 -26
- package/lib/fetch/Response.js +196 -104
- package/lib/fetch/Response.js.map +1 -1
- package/lib/fetch/data-uri/DataURIParser.d.ts +20 -0
- package/lib/fetch/data-uri/DataURIParser.js +60 -0
- package/lib/fetch/data-uri/DataURIParser.js.map +1 -0
- package/lib/fetch/multipart/MultipartFormDataParser.d.ts +40 -0
- package/lib/fetch/multipart/MultipartFormDataParser.js +81 -0
- package/lib/fetch/multipart/MultipartFormDataParser.js.map +1 -0
- package/lib/fetch/multipart/MultipartReader.d.ts +49 -0
- package/lib/fetch/multipart/MultipartReader.js +178 -0
- package/lib/fetch/multipart/MultipartReader.js.map +1 -0
- package/lib/fetch/types/IHeaders.d.ts +71 -0
- package/lib/fetch/{IHeaders.js.map → types/IHeaders.js.map} +1 -1
- package/lib/fetch/types/IHeadersInit.js.map +1 -0
- package/lib/fetch/types/IRequest.d.ts +60 -0
- package/lib/fetch/{IRequest.js.map → types/IRequest.js.map} +1 -1
- package/lib/fetch/types/IRequestBody.d.ts +7 -0
- package/lib/fetch/{IBody.js → types/IRequestBody.js} +1 -1
- package/lib/fetch/types/IRequestBody.js.map +1 -0
- package/lib/fetch/types/IRequestCredentials.d.ts +2 -0
- package/lib/fetch/types/IRequestCredentials.js +3 -0
- package/lib/fetch/types/IRequestCredentials.js.map +1 -0
- package/lib/fetch/{RequestInfo.d.ts → types/IRequestInfo.d.ts} +2 -2
- package/lib/fetch/types/IRequestInfo.js +3 -0
- package/lib/fetch/types/IRequestInfo.js.map +1 -0
- package/lib/fetch/types/IRequestInit.d.ts +21 -0
- package/lib/fetch/types/IRequestInit.js.map +1 -0
- package/lib/fetch/types/IRequestRedirect.d.ts +2 -0
- package/lib/fetch/types/IRequestRedirect.js +3 -0
- package/lib/fetch/types/IRequestRedirect.js.map +1 -0
- package/lib/fetch/types/IRequestReferrerPolicy.d.ts +2 -0
- package/lib/fetch/types/IRequestReferrerPolicy.js +3 -0
- package/lib/fetch/types/IRequestReferrerPolicy.js.map +1 -0
- package/lib/fetch/types/IResponse.d.ts +27 -0
- package/lib/fetch/{IResponse.js.map → types/IResponse.js.map} +1 -1
- package/lib/fetch/types/IResponseBody.d.ts +7 -0
- package/lib/fetch/types/IResponseBody.js +3 -0
- package/lib/fetch/types/IResponseBody.js.map +1 -0
- package/lib/fetch/types/IResponseInit.d.ts +9 -0
- package/lib/fetch/types/IResponseInit.js.map +1 -0
- package/lib/fetch/utilities/FetchBodyUtility.d.ts +44 -0
- package/lib/fetch/utilities/FetchBodyUtility.js +156 -0
- package/lib/fetch/utilities/FetchBodyUtility.js.map +1 -0
- package/lib/fetch/utilities/FetchCORSUtility.d.ts +12 -0
- package/lib/fetch/utilities/FetchCORSUtility.js +20 -0
- package/lib/fetch/utilities/FetchCORSUtility.js.map +1 -0
- package/lib/fetch/utilities/FetchRequestHeaderUtility.d.ts +12 -0
- package/lib/fetch/utilities/FetchRequestHeaderUtility.js +45 -0
- package/lib/fetch/utilities/FetchRequestHeaderUtility.js.map +1 -0
- package/lib/fetch/utilities/FetchRequestReferrerUtility.d.ts +55 -0
- package/lib/fetch/utilities/FetchRequestReferrerUtility.js +194 -0
- package/lib/fetch/utilities/FetchRequestReferrerUtility.js.map +1 -0
- package/lib/fetch/utilities/FetchRequestValidationUtility.d.ts +38 -0
- package/lib/fetch/utilities/FetchRequestValidationUtility.js +70 -0
- package/lib/fetch/utilities/FetchRequestValidationUtility.js.map +1 -0
- package/lib/file/Blob.d.ts +2 -2
- package/lib/file/Blob.js +1 -1
- package/lib/file/Blob.js.map +1 -1
- package/lib/file/File.d.ts +2 -1
- package/lib/file/File.js.map +1 -1
- package/lib/form-data/FormData.d.ts +98 -0
- package/lib/form-data/FormData.js +212 -0
- package/lib/form-data/FormData.js.map +1 -0
- package/lib/index.d.ts +14 -1
- package/lib/index.js +20 -2
- package/lib/index.js.map +1 -1
- package/lib/nodes/document/Document.d.ts +5 -17
- package/lib/nodes/document/Document.js +11 -54
- package/lib/nodes/document/Document.js.map +1 -1
- package/lib/nodes/document/IDocument.d.ts +1 -1
- package/lib/nodes/document-fragment/DocumentFragment.d.ts +3 -14
- package/lib/nodes/document-fragment/DocumentFragment.js +7 -51
- package/lib/nodes/document-fragment/DocumentFragment.js.map +1 -1
- package/lib/nodes/document-fragment/IDocumentFragment.d.ts +2 -1
- package/lib/nodes/element/Element.d.ts +8 -8
- package/lib/nodes/element/Element.js +28 -45
- package/lib/nodes/element/Element.js.map +1 -1
- package/lib/nodes/element/ElementUtility.d.ts +32 -0
- package/lib/nodes/element/ElementUtility.js +105 -0
- package/lib/nodes/element/ElementUtility.js.map +1 -0
- package/lib/nodes/element/HTMLCollection.d.ts +26 -3
- package/lib/nodes/element/HTMLCollection.js +51 -0
- package/lib/nodes/element/HTMLCollection.js.map +1 -1
- package/lib/nodes/element/IHTMLCollection.d.ts +9 -2
- package/lib/nodes/html-anchor-element/HTMLAnchorElement.js +24 -24
- package/lib/nodes/html-anchor-element/HTMLAnchorElement.js.map +1 -1
- package/lib/nodes/html-audio-element/Audio.d.ts +15 -0
- package/lib/nodes/html-audio-element/Audio.js +27 -0
- package/lib/nodes/html-audio-element/Audio.js.map +1 -0
- package/lib/nodes/html-base-element/HTMLBaseElement.js +4 -4
- package/lib/nodes/html-base-element/HTMLBaseElement.js.map +1 -1
- package/lib/nodes/html-button-element/HTMLButtonElement.d.ts +79 -3
- package/lib/nodes/html-button-element/HTMLButtonElement.js +158 -10
- package/lib/nodes/html-button-element/HTMLButtonElement.js.map +1 -1
- package/lib/nodes/html-button-element/IHTMLButtonElement.d.ts +28 -2
- package/lib/nodes/html-dialog-element/HTMLDialogElement.js +3 -3
- package/lib/nodes/html-dialog-element/HTMLDialogElement.js.map +1 -1
- package/lib/nodes/html-element/HTMLElement.js +12 -12
- package/lib/nodes/html-element/HTMLElement.js.map +1 -1
- package/lib/nodes/html-form-element/HTMLFormControlsCollection.d.ts +32 -0
- package/lib/nodes/html-form-element/HTMLFormControlsCollection.js +58 -0
- package/lib/nodes/html-form-element/HTMLFormControlsCollection.js.map +1 -0
- package/lib/nodes/html-form-element/HTMLFormElement.d.ts +37 -17
- package/lib/nodes/html-form-element/HTMLFormElement.js +128 -37
- package/lib/nodes/html-form-element/HTMLFormElement.js.map +1 -1
- package/lib/nodes/html-form-element/IHTMLFormControlsCollection.d.ts +25 -0
- package/lib/nodes/html-form-element/IHTMLFormControlsCollection.js +3 -0
- package/lib/nodes/html-form-element/IHTMLFormControlsCollection.js.map +1 -0
- package/lib/nodes/html-form-element/IHTMLFormElement.d.ts +16 -6
- package/lib/nodes/html-form-element/IRadioNodeList.d.ts +15 -0
- package/lib/nodes/html-form-element/IRadioNodeList.js +3 -0
- package/lib/nodes/html-form-element/IRadioNodeList.js.map +1 -0
- package/lib/nodes/html-form-element/RadioNodeList.d.ts +16 -0
- package/lib/nodes/html-form-element/RadioNodeList.js +28 -0
- package/lib/nodes/html-form-element/RadioNodeList.js.map +1 -0
- package/lib/nodes/html-image-element/HTMLImageElement.js +19 -19
- package/lib/nodes/html-image-element/HTMLImageElement.js.map +1 -1
- package/lib/nodes/html-input-element/HTMLInputElement.d.ts +43 -19
- package/lib/nodes/html-input-element/HTMLInputElement.js +206 -111
- package/lib/nodes/html-input-element/HTMLInputElement.js.map +1 -1
- package/lib/nodes/html-input-element/IHTMLInputElement.d.ts +14 -1
- package/lib/nodes/html-label-element/HTMLLabelElement.js +3 -7
- package/lib/nodes/html-label-element/HTMLLabelElement.js.map +1 -1
- package/lib/nodes/html-link-element/HTMLLinkElement.js +21 -21
- package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
- package/lib/nodes/html-media-element/HTMLMediaElement.js +21 -21
- package/lib/nodes/html-media-element/HTMLMediaElement.js.map +1 -1
- package/lib/nodes/html-meta-element/HTMLMetaElement.js +8 -8
- package/lib/nodes/html-meta-element/HTMLMetaElement.js.map +1 -1
- package/lib/nodes/html-opt-group-element/HTMLOptGroupElement.js +6 -6
- package/lib/nodes/html-opt-group-element/HTMLOptGroupElement.js.map +1 -1
- package/lib/nodes/html-option-element/HTMLOptionElement.d.ts +3 -4
- package/lib/nodes/html-option-element/HTMLOptionElement.js +26 -29
- package/lib/nodes/html-option-element/HTMLOptionElement.js.map +1 -1
- package/lib/nodes/html-script-element/HTMLScriptElement.js +16 -16
- package/lib/nodes/html-script-element/HTMLScriptElement.js.map +1 -1
- package/lib/nodes/html-script-element/ScriptUtility.js +5 -5
- package/lib/nodes/html-script-element/ScriptUtility.js.map +1 -1
- package/lib/nodes/{html-option-element → html-select-element}/HTMLOptionsCollection.d.ts +5 -6
- package/lib/nodes/{html-option-element → html-select-element}/HTMLOptionsCollection.js.map +1 -1
- package/lib/nodes/html-select-element/HTMLSelectElement.d.ts +42 -41
- package/lib/nodes/html-select-element/HTMLSelectElement.js +134 -138
- package/lib/nodes/html-select-element/HTMLSelectElement.js.map +1 -1
- package/lib/nodes/{html-option-element → html-select-element}/IHTMLOptionsCollection.d.ts +4 -5
- package/lib/nodes/html-select-element/IHTMLOptionsCollection.js.map +1 -0
- package/lib/nodes/html-select-element/IHTMLSelectElement.d.ts +22 -2
- package/lib/nodes/html-slot-element/HTMLSlotElement.js +2 -2
- package/lib/nodes/html-slot-element/HTMLSlotElement.js.map +1 -1
- package/lib/nodes/html-style-element/HTMLStyleElement.js +7 -7
- package/lib/nodes/html-style-element/HTMLStyleElement.js.map +1 -1
- package/lib/nodes/html-text-area-element/HTMLTextAreaElement.d.ts +55 -2
- package/lib/nodes/html-text-area-element/HTMLTextAreaElement.js +150 -40
- package/lib/nodes/html-text-area-element/HTMLTextAreaElement.js.map +1 -1
- package/lib/nodes/html-text-area-element/IHTMLTextAreaElement.d.ts +16 -0
- package/lib/nodes/node/Node.d.ts +12 -1
- package/lib/nodes/node/Node.js +50 -4
- package/lib/nodes/node/Node.js.map +1 -1
- package/lib/nodes/node/NodeList.d.ts +2 -3
- package/lib/nodes/node/NodeList.js.map +1 -1
- package/lib/nodes/parent-node/IParentNode.d.ts +4 -4
- package/lib/nodes/parent-node/ParentNodeUtility.d.ts +3 -3
- package/lib/nodes/parent-node/ParentNodeUtility.js +13 -11
- package/lib/nodes/parent-node/ParentNodeUtility.js.map +1 -1
- package/lib/nodes/text/Text.d.ts +13 -0
- package/lib/nodes/text/Text.js +30 -0
- package/lib/nodes/text/Text.js.map +1 -1
- package/lib/query-selector/QuerySelector.js +2 -2
- package/lib/query-selector/QuerySelector.js.map +1 -1
- package/lib/query-selector/SelectorItem.js +2 -0
- package/lib/query-selector/SelectorItem.js.map +1 -1
- package/lib/validity-state/ValidityState.d.ts +87 -0
- package/lib/validity-state/ValidityState.js +171 -0
- package/lib/validity-state/ValidityState.js.map +1 -0
- package/lib/window/GlobalWindow.d.ts +0 -8
- package/lib/window/GlobalWindow.js +0 -2
- package/lib/window/GlobalWindow.js.map +1 -1
- package/lib/window/INodeJSGlobal.d.ts +71 -0
- package/lib/window/INodeJSGlobal.js +2 -0
- package/lib/window/INodeJSGlobal.js.map +1 -0
- package/lib/window/IWindow.d.ts +30 -10
- package/lib/window/Window.d.ts +37 -20
- package/lib/window/Window.js +22 -10
- package/lib/window/Window.js.map +1 -1
- package/lib/xml-http-request/XMLHttpRequest.js +20 -12
- package/lib/xml-http-request/XMLHttpRequest.js.map +1 -1
- package/lib/xml-parser/XMLParser.js +1 -1
- package/lib/xml-parser/XMLParser.js.map +1 -1
- package/package.json +2 -3
- package/src/cookie/CookieJar.ts +1 -1
- package/src/dom-token-list/DOMTokenList.ts +24 -20
- package/src/event/EventTarget.ts +7 -3
- package/src/exception/DOMExceptionNameEnum.ts +5 -1
- package/src/fetch/AbortController.ts +26 -0
- package/src/fetch/AbortSignal.ts +44 -0
- package/src/fetch/Fetch.ts +666 -0
- package/src/fetch/Headers.ts +151 -3
- package/src/fetch/Request.ts +226 -94
- package/src/fetch/{ResourceFetchHandler.ts → ResourceFetch.ts} +3 -3
- package/src/fetch/Response.ts +252 -89
- package/src/fetch/data-uri/DataURIParser.ts +66 -0
- package/src/fetch/multipart/MultipartFormDataParser.ts +113 -0
- package/src/fetch/multipart/MultipartReader.ts +217 -0
- package/src/fetch/types/IHeaders.ts +80 -0
- package/src/fetch/types/IRequest.ts +65 -0
- package/src/fetch/types/IRequestBody.ts +15 -0
- package/src/fetch/types/IRequestCredentials.ts +2 -0
- package/src/fetch/types/IRequestInfo.ts +6 -0
- package/src/fetch/types/IRequestInit.ts +21 -0
- package/src/fetch/types/IRequestRedirect.ts +3 -0
- package/src/fetch/types/IRequestReferrerPolicy.ts +11 -0
- package/src/fetch/types/IResponse.ts +27 -0
- package/src/fetch/types/IResponseBody.ts +15 -0
- package/src/fetch/types/IResponseInit.ts +10 -0
- package/src/fetch/utilities/FetchBodyUtility.ts +175 -0
- package/src/fetch/utilities/FetchCORSUtility.ts +18 -0
- package/src/fetch/utilities/FetchRequestHeaderUtility.ts +47 -0
- package/src/fetch/utilities/FetchRequestReferrerUtility.ts +241 -0
- package/src/fetch/utilities/FetchRequestValidationUtility.ts +82 -0
- package/src/file/Blob.ts +2 -2
- package/src/file/File.ts +5 -1
- package/src/form-data/FormData.ts +237 -0
- package/src/index.ts +27 -1
- package/src/nodes/document/Document.ts +23 -64
- package/src/nodes/document/IDocument.ts +1 -1
- package/src/nodes/document-fragment/DocumentFragment.ts +9 -59
- package/src/nodes/document-fragment/IDocumentFragment.ts +2 -1
- package/src/nodes/element/Element.ts +43 -56
- package/src/nodes/element/ElementUtility.ts +137 -0
- package/src/nodes/element/HTMLCollection.ts +60 -3
- package/src/nodes/element/IHTMLCollection.ts +10 -2
- package/src/nodes/html-anchor-element/HTMLAnchorElement.ts +24 -24
- package/src/nodes/html-audio-element/Audio.ts +22 -0
- package/src/nodes/html-base-element/HTMLBaseElement.ts +4 -4
- package/src/nodes/html-button-element/HTMLButtonElement.ts +184 -10
- package/src/nodes/html-button-element/IHTMLButtonElement.ts +31 -2
- package/src/nodes/html-dialog-element/HTMLDialogElement.ts +3 -3
- package/src/nodes/html-element/HTMLElement.ts +12 -12
- package/src/nodes/html-form-element/HTMLFormControlsCollection.ts +76 -0
- package/src/nodes/html-form-element/HTMLFormElement.ts +147 -41
- package/src/nodes/html-form-element/IHTMLFormControlsCollection.ts +45 -0
- package/src/nodes/html-form-element/IHTMLFormElement.ts +17 -6
- package/src/nodes/html-form-element/IRadioNodeList.ts +16 -0
- package/src/nodes/html-form-element/RadioNodeList.ts +24 -0
- package/src/nodes/html-image-element/HTMLImageElement.ts +19 -19
- package/src/nodes/html-input-element/HTMLInputElement.ts +232 -113
- package/src/nodes/html-input-element/IHTMLInputElement.ts +20 -1
- package/src/nodes/html-label-element/HTMLLabelElement.ts +3 -7
- package/src/nodes/html-link-element/HTMLLinkElement.ts +21 -21
- package/src/nodes/html-media-element/HTMLMediaElement.ts +21 -21
- package/src/nodes/html-meta-element/HTMLMetaElement.ts +8 -8
- package/src/nodes/html-opt-group-element/HTMLOptGroupElement.ts +6 -6
- package/src/nodes/html-option-element/HTMLOptionElement.ts +29 -30
- package/src/nodes/html-script-element/HTMLScriptElement.ts +16 -16
- package/src/nodes/html-script-element/ScriptUtility.ts +5 -5
- package/src/nodes/{html-option-element → html-select-element}/HTMLOptionsCollection.ts +5 -9
- package/src/nodes/html-select-element/HTMLSelectElement.ts +159 -170
- package/src/nodes/{html-option-element → html-select-element}/IHTMLOptionsCollection.ts +4 -8
- package/src/nodes/html-select-element/IHTMLSelectElement.ts +25 -2
- package/src/nodes/html-slot-element/HTMLSlotElement.ts +2 -2
- package/src/nodes/html-style-element/HTMLStyleElement.ts +7 -7
- package/src/nodes/html-text-area-element/HTMLTextAreaElement.ts +174 -41
- package/src/nodes/html-text-area-element/IHTMLTextAreaElement.ts +18 -0
- package/src/nodes/node/Node.ts +61 -6
- package/src/nodes/node/NodeList.ts +2 -3
- package/src/nodes/parent-node/IParentNode.ts +7 -4
- package/src/nodes/parent-node/ParentNodeUtility.ts +25 -15
- package/src/nodes/text/Text.ts +38 -0
- package/src/query-selector/QuerySelector.ts +2 -2
- package/src/query-selector/SelectorItem.ts +3 -0
- package/src/validity-state/ValidityState.ts +211 -0
- package/src/window/GlobalWindow.ts +0 -2
- package/src/window/INodeJSGlobal.ts +70 -0
- package/src/window/IWindow.ts +32 -10
- package/src/window/Window.ts +43 -21
- package/src/xml-http-request/XMLHttpRequest.ts +27 -26
- package/src/xml-parser/XMLParser.ts +1 -1
- package/lib/fetch/FetchHandler.d.ts +0 -18
- package/lib/fetch/FetchHandler.js +0 -83
- package/lib/fetch/FetchHandler.js.map +0 -1
- package/lib/fetch/IAbortSignal.d.ts +0 -16
- package/lib/fetch/IAbortSignal.js +0 -4
- package/lib/fetch/IAbortSignal.js.map +0 -1
- package/lib/fetch/IBody.d.ts +0 -17
- package/lib/fetch/IBody.js.map +0 -1
- package/lib/fetch/IHeaders.d.ts +0 -18
- package/lib/fetch/IHeadersInit.js.map +0 -1
- package/lib/fetch/IRequest.d.ts +0 -18
- package/lib/fetch/IRequestInit.d.ts +0 -16
- package/lib/fetch/IRequestInit.js.map +0 -1
- package/lib/fetch/IResponse.d.ts +0 -20
- package/lib/fetch/IResponseInit.d.ts +0 -9
- package/lib/fetch/IResponseInit.js.map +0 -1
- package/lib/fetch/RequestInfo.js +0 -3
- package/lib/fetch/RequestInfo.js.map +0 -1
- package/lib/fetch/ResourceFetchHandler.js.map +0 -1
- package/lib/form-data/IFormData.d.ts +0 -32
- package/lib/form-data/IFormData.js +0 -3
- package/lib/form-data/IFormData.js.map +0 -1
- package/lib/location/RelativeURL.d.ts +0 -15
- package/lib/location/RelativeURL.js +0 -19
- package/lib/location/RelativeURL.js.map +0 -1
- package/lib/nodes/element/HTMLCollectionFactory.d.ts +0 -21
- package/lib/nodes/element/HTMLCollectionFactory.js +0 -31
- package/lib/nodes/element/HTMLCollectionFactory.js.map +0 -1
- package/lib/nodes/html-option-element/IHTMLOptionsCollection.js.map +0 -1
- package/lib/nodes/node/NodeListFactory.d.ts +0 -21
- package/lib/nodes/node/NodeListFactory.js +0 -31
- package/lib/nodes/node/NodeListFactory.js.map +0 -1
- package/lib/nodes/validity-state/ValidityState.d.ts +0 -52
- package/lib/nodes/validity-state/ValidityState.js +0 -81
- package/lib/nodes/validity-state/ValidityState.js.map +0 -1
- package/src/fetch/FetchHandler.ts +0 -90
- package/src/fetch/IAbortSignal.ts +0 -34
- package/src/fetch/IBody.ts +0 -18
- package/src/fetch/IHeaders.ts +0 -18
- package/src/fetch/IRequest.ts +0 -59
- package/src/fetch/IRequestInit.ts +0 -41
- package/src/fetch/IResponse.ts +0 -22
- package/src/fetch/IResponseInit.ts +0 -10
- package/src/fetch/RequestInfo.ts +0 -6
- package/src/form-data/IFormData.ts +0 -33
- package/src/location/RelativeURL.ts +0 -17
- package/src/nodes/element/HTMLCollectionFactory.ts +0 -31
- package/src/nodes/node/NodeListFactory.ts +0 -31
- package/src/nodes/validity-state/ValidityState.ts +0 -84
- /package/lib/fetch/{IHeaders.js → types/IHeaders.js} +0 -0
- /package/lib/fetch/{IHeadersInit.d.ts → types/IHeadersInit.d.ts} +0 -0
- /package/lib/fetch/{IHeadersInit.js → types/IHeadersInit.js} +0 -0
- /package/lib/fetch/{IRequest.js → types/IRequest.js} +0 -0
- /package/lib/fetch/{IRequestInit.js → types/IRequestInit.js} +0 -0
- /package/lib/fetch/{IResponse.js → types/IResponse.js} +0 -0
- /package/lib/fetch/{IResponseInit.js → types/IResponseInit.js} +0 -0
- /package/lib/nodes/{html-option-element → html-select-element}/HTMLOptionsCollection.js +0 -0
- /package/lib/nodes/{html-option-element → html-select-element}/IHTMLOptionsCollection.js +0 -0
- /package/src/fetch/{IHeadersInit.ts → types/IHeadersInit.ts} +0 -0
@@ -0,0 +1,666 @@
|
|
1
|
+
import IRequestInit from './types/IRequestInit';
|
2
|
+
import IDocument from '../nodes/document/IDocument';
|
3
|
+
import IResponse from './types/IResponse';
|
4
|
+
import Request from './Request';
|
5
|
+
import IRequestInfo from './types/IRequestInfo';
|
6
|
+
import Headers from './Headers';
|
7
|
+
import FetchRequestReferrerUtility from './utilities/FetchRequestReferrerUtility';
|
8
|
+
import DOMException from '../exception/DOMException';
|
9
|
+
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum';
|
10
|
+
import Response from './Response';
|
11
|
+
import HTTP, { IncomingMessage } from 'http';
|
12
|
+
import HTTPS from 'https';
|
13
|
+
import Zlib from 'zlib';
|
14
|
+
import { URL } from 'url';
|
15
|
+
import { Socket } from 'net';
|
16
|
+
import Stream from 'stream';
|
17
|
+
import DataURIParser from './data-uri/DataURIParser';
|
18
|
+
import FetchCORSUtility from './utilities/FetchCORSUtility';
|
19
|
+
|
20
|
+
const SUPPORTED_SCHEMAS = ['data:', 'http:', 'https:'];
|
21
|
+
const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308];
|
22
|
+
const LAST_CHUNK = Buffer.from('0\r\n\r\n');
|
23
|
+
const MAX_REDIRECT_COUNT = 20;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Handles fetch requests.
|
27
|
+
*
|
28
|
+
* Based on:
|
29
|
+
* https://github.com/node-fetch/node-fetch/blob/main/src/index.js
|
30
|
+
*
|
31
|
+
* @see https://fetch.spec.whatwg.org/#http-network-fetch
|
32
|
+
*/
|
33
|
+
export default class Fetch {
|
34
|
+
private reject: (reason: Error) => void | null = null;
|
35
|
+
private resolve: (value: IResponse | Promise<IResponse>) => void | null = null;
|
36
|
+
private listeners = {
|
37
|
+
onSignalAbort: this.onSignalAbort.bind(this)
|
38
|
+
};
|
39
|
+
private isChunkedTransfer = false;
|
40
|
+
private isProperLastChunkReceived = false;
|
41
|
+
private previousChunk: Buffer | null = null;
|
42
|
+
private nodeRequest: HTTP.ClientRequest | null = null;
|
43
|
+
private response: Response | null = null;
|
44
|
+
private ownerDocument: IDocument;
|
45
|
+
private request: Request;
|
46
|
+
private redirectCount = 0;
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Constructor.
|
50
|
+
*
|
51
|
+
* @param options Options.
|
52
|
+
* @param options.document
|
53
|
+
* @param options.url URL.
|
54
|
+
* @param [options.init] Init.
|
55
|
+
* @param [options.ownerDocument] Owner document.
|
56
|
+
* @param [options.redirectCount] Redirect count.
|
57
|
+
* @param [options.contentType] Content Type.
|
58
|
+
*/
|
59
|
+
constructor(options: {
|
60
|
+
ownerDocument: IDocument;
|
61
|
+
url: IRequestInfo;
|
62
|
+
init?: IRequestInit;
|
63
|
+
redirectCount?: number;
|
64
|
+
contentType?: string;
|
65
|
+
}) {
|
66
|
+
const url = options.url;
|
67
|
+
|
68
|
+
this.ownerDocument = options.ownerDocument;
|
69
|
+
this.request =
|
70
|
+
typeof options.url === 'string' || options.url instanceof URL
|
71
|
+
? new Request(options.url, options.init)
|
72
|
+
: <Request>url;
|
73
|
+
if (options.contentType) {
|
74
|
+
(<string>this.request._contentType) = options.contentType;
|
75
|
+
}
|
76
|
+
this.redirectCount = options.redirectCount || 0;
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Sends request.
|
81
|
+
*
|
82
|
+
* @returns Response.
|
83
|
+
*/
|
84
|
+
public send(): Promise<IResponse> {
|
85
|
+
return new Promise((resolve, reject) => {
|
86
|
+
const taskManager = this.ownerDocument.defaultView.happyDOM.asyncTaskManager;
|
87
|
+
const taskID = taskManager.startTask(() => this.abort());
|
88
|
+
|
89
|
+
if (this.resolve) {
|
90
|
+
throw new Error('Fetch already sent.');
|
91
|
+
}
|
92
|
+
|
93
|
+
this.resolve = (response: IResponse | Promise<IResponse>): void => {
|
94
|
+
resolve(response);
|
95
|
+
taskManager.endTask(taskID);
|
96
|
+
};
|
97
|
+
this.reject = (error: Error): void => {
|
98
|
+
reject(error);
|
99
|
+
taskManager.endTask(taskID);
|
100
|
+
};
|
101
|
+
|
102
|
+
this.prepareRequest();
|
103
|
+
this.validateRequest();
|
104
|
+
|
105
|
+
if (this.request._url.protocol === 'data:') {
|
106
|
+
const result = DataURIParser.parse(this.request.url);
|
107
|
+
this.response = new Response(result.buffer, {
|
108
|
+
headers: { 'Content-Type': result.type }
|
109
|
+
});
|
110
|
+
resolve(this.response);
|
111
|
+
return;
|
112
|
+
}
|
113
|
+
|
114
|
+
if (this.request.signal.aborted) {
|
115
|
+
this.abort();
|
116
|
+
return;
|
117
|
+
}
|
118
|
+
|
119
|
+
this.request.signal.addEventListener('abort', this.listeners.onSignalAbort);
|
120
|
+
|
121
|
+
const send = (this.request._url.protocol === 'https:' ? HTTPS : HTTP).request;
|
122
|
+
|
123
|
+
this.nodeRequest = send(this.request._url.href, {
|
124
|
+
method: this.request.method,
|
125
|
+
headers: this.getRequestHeaders()
|
126
|
+
});
|
127
|
+
|
128
|
+
this.nodeRequest.on('error', this.onError.bind(this));
|
129
|
+
this.nodeRequest.on('socket', this.onSocket.bind(this));
|
130
|
+
this.nodeRequest.on('response', this.onResponse.bind(this));
|
131
|
+
|
132
|
+
if (this.request.body === null) {
|
133
|
+
this.nodeRequest.end();
|
134
|
+
} else {
|
135
|
+
Stream.pipeline(this.request.body, this.nodeRequest, (error) => {
|
136
|
+
if (error) {
|
137
|
+
this.onError(error);
|
138
|
+
}
|
139
|
+
});
|
140
|
+
}
|
141
|
+
});
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* Event listener for "socket" event.
|
146
|
+
*
|
147
|
+
* @param socket Socket.
|
148
|
+
*/
|
149
|
+
private onSocket(socket: Socket): void {
|
150
|
+
const onSocketClose = (): void => {
|
151
|
+
if (this.isChunkedTransfer && !this.isProperLastChunkReceived) {
|
152
|
+
const error = new DOMException('Premature close.', DOMExceptionNameEnum.networkError);
|
153
|
+
|
154
|
+
if (this.response && this.response.body) {
|
155
|
+
this.response.body.destroy(error);
|
156
|
+
}
|
157
|
+
}
|
158
|
+
};
|
159
|
+
|
160
|
+
const onData = (buffer: Buffer): void => {
|
161
|
+
this.isProperLastChunkReceived = Buffer.compare(buffer.slice(-5), LAST_CHUNK) === 0;
|
162
|
+
|
163
|
+
// Sometimes final 0-length chunk and end of message code are in separate packets.
|
164
|
+
if (!this.isProperLastChunkReceived && this.previousChunk) {
|
165
|
+
this.isProperLastChunkReceived =
|
166
|
+
Buffer.compare(this.previousChunk.slice(-3), LAST_CHUNK.slice(0, 3)) === 0 &&
|
167
|
+
Buffer.compare(buffer.slice(-2), LAST_CHUNK.slice(3)) === 0;
|
168
|
+
}
|
169
|
+
|
170
|
+
this.previousChunk = buffer;
|
171
|
+
};
|
172
|
+
|
173
|
+
socket.prependListener('close', onSocketClose);
|
174
|
+
socket.on('data', onData);
|
175
|
+
|
176
|
+
this.nodeRequest.on('close', () => {
|
177
|
+
socket.removeListener('close', onSocketClose);
|
178
|
+
socket.removeListener('data', onData);
|
179
|
+
});
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Event listener for signal "abort" event.
|
184
|
+
*/
|
185
|
+
private onSignalAbort(): void {
|
186
|
+
this.finalizeRequest();
|
187
|
+
this.abort();
|
188
|
+
}
|
189
|
+
|
190
|
+
/**
|
191
|
+
* Event listener for request "error" event.
|
192
|
+
*
|
193
|
+
* @param error Error.
|
194
|
+
*/
|
195
|
+
private onError(error: Error): void {
|
196
|
+
this.finalizeRequest();
|
197
|
+
this.reject(
|
198
|
+
new DOMException(
|
199
|
+
`Fetch to "${this.request.url}" failed. Error: ${error.message}`,
|
200
|
+
DOMExceptionNameEnum.networkError
|
201
|
+
)
|
202
|
+
);
|
203
|
+
}
|
204
|
+
|
205
|
+
/**
|
206
|
+
* Event listener for request "response" event.
|
207
|
+
*
|
208
|
+
* @param nodeResponse Node response.
|
209
|
+
*/
|
210
|
+
private onResponse(nodeResponse: IncomingMessage): void {
|
211
|
+
// Needed for handling bad endings of chunked transfer.
|
212
|
+
this.isChunkedTransfer =
|
213
|
+
nodeResponse.headers['transfer-encoding'] === 'chunked' &&
|
214
|
+
!nodeResponse.headers['content-length'];
|
215
|
+
|
216
|
+
this.nodeRequest.setTimeout(0);
|
217
|
+
|
218
|
+
const headers = this.getResponseHeaders(nodeResponse);
|
219
|
+
|
220
|
+
if (this.handleRedirectResponse(nodeResponse, headers)) {
|
221
|
+
return;
|
222
|
+
}
|
223
|
+
|
224
|
+
nodeResponse.once('end', () =>
|
225
|
+
this.request.signal.removeEventListener('abort', this.listeners.onSignalAbort)
|
226
|
+
);
|
227
|
+
|
228
|
+
let body = Stream.pipeline(nodeResponse, new Stream.PassThrough(), (error: Error) => {
|
229
|
+
if (error) {
|
230
|
+
// Ignore error as it is forwarded to the response body.
|
231
|
+
}
|
232
|
+
});
|
233
|
+
|
234
|
+
const responseOptions = {
|
235
|
+
status: nodeResponse.statusCode,
|
236
|
+
statusText: nodeResponse.statusMessage,
|
237
|
+
headers
|
238
|
+
};
|
239
|
+
|
240
|
+
const contentEncodingHeader = headers.get('Content-Encoding');
|
241
|
+
|
242
|
+
if (
|
243
|
+
this.request.method === 'HEAD' ||
|
244
|
+
contentEncodingHeader === null ||
|
245
|
+
nodeResponse.statusCode === 204 ||
|
246
|
+
nodeResponse.statusCode === 304
|
247
|
+
) {
|
248
|
+
this.response = new Response(body, responseOptions);
|
249
|
+
(<boolean>this.response.redirected) = this.redirectCount > 0;
|
250
|
+
(<string>this.response.url) = this.request.url;
|
251
|
+
this.resolve(this.response);
|
252
|
+
return;
|
253
|
+
}
|
254
|
+
|
255
|
+
// Be less strict when decoding compressed responses.
|
256
|
+
// Sometimes servers send slightly invalid responses that are still accepted by common browsers.
|
257
|
+
// "cURL" always uses Z_SYNC_FLUSH.
|
258
|
+
const zlibOptions = {
|
259
|
+
flush: Zlib.constants.Z_SYNC_FLUSH,
|
260
|
+
finishFlush: Zlib.constants.Z_SYNC_FLUSH
|
261
|
+
};
|
262
|
+
|
263
|
+
// For GZip
|
264
|
+
if (contentEncodingHeader === 'gzip' || contentEncodingHeader === 'x-gzip') {
|
265
|
+
body = Stream.pipeline(body, Zlib.createGunzip(zlibOptions), (error: Error) => {
|
266
|
+
if (error) {
|
267
|
+
// Ignore error as it is forwarded to the response body.
|
268
|
+
}
|
269
|
+
});
|
270
|
+
this.response = new Response(body, responseOptions);
|
271
|
+
(<boolean>this.response.redirected) = this.redirectCount > 0;
|
272
|
+
(<string>this.response.url) = this.request.url;
|
273
|
+
this.resolve(this.response);
|
274
|
+
return;
|
275
|
+
}
|
276
|
+
|
277
|
+
// For Deflate
|
278
|
+
if (contentEncodingHeader === 'deflate' || contentEncodingHeader === 'x-deflate') {
|
279
|
+
// Handle the infamous raw deflate response from old servers
|
280
|
+
// A hack for old IIS and Apache servers
|
281
|
+
const raw = Stream.pipeline(nodeResponse, new Stream.PassThrough(), (error) => {
|
282
|
+
if (error) {
|
283
|
+
// Ignore error as it is forwarded to the response body.
|
284
|
+
}
|
285
|
+
});
|
286
|
+
raw.on('data', (chunk) => {
|
287
|
+
// See http://stackoverflow.com/questions/37519828
|
288
|
+
if ((chunk[0] & 0x0f) === 0x08) {
|
289
|
+
body = Stream.pipeline(body, Zlib.createInflate(), (error) => {
|
290
|
+
if (error) {
|
291
|
+
// Ignore error as the fetch() promise has already been resolved.
|
292
|
+
}
|
293
|
+
});
|
294
|
+
} else {
|
295
|
+
body = Stream.pipeline(body, Zlib.createInflateRaw(), (error) => {
|
296
|
+
if (error) {
|
297
|
+
// Ignore error as it is forwarded to the response body.
|
298
|
+
}
|
299
|
+
});
|
300
|
+
}
|
301
|
+
|
302
|
+
this.response = new Response(body, responseOptions);
|
303
|
+
(<boolean>this.response.redirected) = this.redirectCount > 0;
|
304
|
+
(<string>this.response.url) = this.request.url;
|
305
|
+
this.resolve(this.response);
|
306
|
+
});
|
307
|
+
raw.on('end', () => {
|
308
|
+
// Some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted.
|
309
|
+
if (!this.response) {
|
310
|
+
this.response = new Response(body, responseOptions);
|
311
|
+
(<boolean>this.response.redirected) = this.redirectCount > 0;
|
312
|
+
(<string>this.response.url) = this.request.url;
|
313
|
+
this.resolve(this.response);
|
314
|
+
}
|
315
|
+
});
|
316
|
+
return;
|
317
|
+
}
|
318
|
+
|
319
|
+
// For BR
|
320
|
+
if (contentEncodingHeader === 'br') {
|
321
|
+
body = Stream.pipeline(body, Zlib.createBrotliDecompress(), (error) => {
|
322
|
+
if (error) {
|
323
|
+
// Ignore error as it is forwarded to the response body.
|
324
|
+
}
|
325
|
+
});
|
326
|
+
this.response = new Response(body, responseOptions);
|
327
|
+
(<boolean>this.response.redirected) = this.redirectCount > 0;
|
328
|
+
(<string>this.response.url) = this.request.url;
|
329
|
+
this.resolve(this.response);
|
330
|
+
return;
|
331
|
+
}
|
332
|
+
|
333
|
+
// Otherwise, use response as is
|
334
|
+
this.response = new Response(body, responseOptions);
|
335
|
+
(<boolean>this.response.redirected) = this.redirectCount > 0;
|
336
|
+
(<string>this.response.url) = this.request.url;
|
337
|
+
this.resolve(this.response);
|
338
|
+
}
|
339
|
+
|
340
|
+
/**
|
341
|
+
* Handles redirect response.
|
342
|
+
*
|
343
|
+
* @param nodeResponse Node response.
|
344
|
+
* @param responseHeaders Headers.
|
345
|
+
* @returns True if redirect response was handled, false otherwise.
|
346
|
+
*/
|
347
|
+
private handleRedirectResponse(nodeResponse: IncomingMessage, responseHeaders: Headers): boolean {
|
348
|
+
if (!this.isRedirect(nodeResponse.statusCode)) {
|
349
|
+
return false;
|
350
|
+
}
|
351
|
+
|
352
|
+
switch (this.request.redirect) {
|
353
|
+
case 'error':
|
354
|
+
this.finalizeRequest();
|
355
|
+
this.reject(
|
356
|
+
new DOMException(
|
357
|
+
`URI requested responds with a redirect, redirect mode is set to "error": ${this.request.url}`,
|
358
|
+
DOMExceptionNameEnum.abortError
|
359
|
+
)
|
360
|
+
);
|
361
|
+
return true;
|
362
|
+
case 'manual':
|
363
|
+
// Nothing to do
|
364
|
+
return false;
|
365
|
+
case 'follow':
|
366
|
+
const locationHeader = responseHeaders.get('Location');
|
367
|
+
const shouldBecomeGetRequest =
|
368
|
+
nodeResponse.statusCode === 303 ||
|
369
|
+
((nodeResponse.statusCode === 301 || nodeResponse.statusCode === 302) &&
|
370
|
+
this.request.method === 'POST');
|
371
|
+
let locationURL: URL = null;
|
372
|
+
|
373
|
+
if (locationHeader !== null) {
|
374
|
+
try {
|
375
|
+
locationURL = new URL(locationHeader, this.request.url);
|
376
|
+
} catch {
|
377
|
+
this.finalizeRequest();
|
378
|
+
this.reject(
|
379
|
+
new DOMException(
|
380
|
+
`URI requested responds with an invalid redirect URL: ${locationHeader}`,
|
381
|
+
DOMExceptionNameEnum.uriMismatchError
|
382
|
+
)
|
383
|
+
);
|
384
|
+
return true;
|
385
|
+
}
|
386
|
+
}
|
387
|
+
|
388
|
+
if (locationURL === null) {
|
389
|
+
return false;
|
390
|
+
}
|
391
|
+
|
392
|
+
if (this.redirectCount >= MAX_REDIRECT_COUNT) {
|
393
|
+
this.finalizeRequest();
|
394
|
+
this.reject(
|
395
|
+
new DOMException(
|
396
|
+
`Maximum redirects reached at: ${this.request.url}`,
|
397
|
+
DOMExceptionNameEnum.networkError
|
398
|
+
)
|
399
|
+
);
|
400
|
+
return true;
|
401
|
+
}
|
402
|
+
|
403
|
+
const headers = new Headers(this.request.headers);
|
404
|
+
let body: Stream.Readable | Buffer | null = this.request._bodyBuffer;
|
405
|
+
|
406
|
+
if (!body && this.request.body) {
|
407
|
+
// Piping a used request body is not possible.
|
408
|
+
if (this.request.bodyUsed) {
|
409
|
+
throw new DOMException(
|
410
|
+
'It is not possible to pipe a body after it is used.',
|
411
|
+
DOMExceptionNameEnum.networkError
|
412
|
+
);
|
413
|
+
}
|
414
|
+
|
415
|
+
body = new Stream.PassThrough();
|
416
|
+
this.request.body.pipe(<Stream.PassThrough>body);
|
417
|
+
}
|
418
|
+
|
419
|
+
const requestInit: IRequestInit = {
|
420
|
+
method: this.request.method,
|
421
|
+
signal: this.request.signal,
|
422
|
+
referrer: this.request.referrer,
|
423
|
+
referrerPolicy: this.request.referrerPolicy,
|
424
|
+
credentials: this.request.credentials,
|
425
|
+
headers,
|
426
|
+
body
|
427
|
+
};
|
428
|
+
|
429
|
+
// TODO: Maybe we need to add support for OPTIONS request with 'Access-Control-Allow-*' headers?
|
430
|
+
if (
|
431
|
+
this.request.credentials === 'omit' ||
|
432
|
+
(this.request.credentials === 'same-origin' &&
|
433
|
+
FetchCORSUtility.isCORS(this.ownerDocument.location, locationURL))
|
434
|
+
) {
|
435
|
+
headers.delete('authorization');
|
436
|
+
headers.delete('www-authenticate');
|
437
|
+
headers.delete('cookie');
|
438
|
+
headers.delete('cookie2');
|
439
|
+
}
|
440
|
+
|
441
|
+
if (nodeResponse.statusCode !== 303 && this.request.body && !this.request._bodyBuffer) {
|
442
|
+
this.finalizeRequest();
|
443
|
+
this.reject(
|
444
|
+
new DOMException(
|
445
|
+
'Cannot follow redirect with body being a readable stream.',
|
446
|
+
DOMExceptionNameEnum.networkError
|
447
|
+
)
|
448
|
+
);
|
449
|
+
return true;
|
450
|
+
}
|
451
|
+
|
452
|
+
if (this.request.signal.aborted) {
|
453
|
+
this.abort();
|
454
|
+
return true;
|
455
|
+
}
|
456
|
+
|
457
|
+
if (shouldBecomeGetRequest) {
|
458
|
+
requestInit.method = 'GET';
|
459
|
+
requestInit.body = undefined;
|
460
|
+
headers.delete('Content-Length');
|
461
|
+
headers.delete('Content-Type');
|
462
|
+
}
|
463
|
+
|
464
|
+
const responseReferrerPolicy =
|
465
|
+
FetchRequestReferrerUtility.getReferrerPolicyFromHeader(headers);
|
466
|
+
if (responseReferrerPolicy) {
|
467
|
+
requestInit.referrerPolicy = responseReferrerPolicy;
|
468
|
+
}
|
469
|
+
|
470
|
+
const fetch = new (<typeof Fetch>this.constructor)({
|
471
|
+
ownerDocument: this.ownerDocument,
|
472
|
+
url: locationURL,
|
473
|
+
init: requestInit,
|
474
|
+
redirectCount: this.redirectCount + 1,
|
475
|
+
contentType: !shouldBecomeGetRequest ? this.request._contentType : undefined
|
476
|
+
});
|
477
|
+
|
478
|
+
this.finalizeRequest();
|
479
|
+
this.resolve(fetch.send());
|
480
|
+
return true;
|
481
|
+
default:
|
482
|
+
this.finalizeRequest();
|
483
|
+
this.reject(
|
484
|
+
new DOMException(
|
485
|
+
`Redirect option '${this.request.redirect}' is not a valid value of RequestRedirect`
|
486
|
+
)
|
487
|
+
);
|
488
|
+
return true;
|
489
|
+
}
|
490
|
+
}
|
491
|
+
|
492
|
+
/**
|
493
|
+
* Prepares the request before being sent.
|
494
|
+
*/
|
495
|
+
private prepareRequest(): void {
|
496
|
+
if (!this.request.referrerPolicy) {
|
497
|
+
(<string>this.request.referrerPolicy) = 'strict-origin-when-cross-origin';
|
498
|
+
}
|
499
|
+
|
500
|
+
if (this.request.referrer && this.request.referrer !== 'no-referrer') {
|
501
|
+
this.request._referrer = FetchRequestReferrerUtility.getSentReferrer(
|
502
|
+
this.ownerDocument,
|
503
|
+
this.request
|
504
|
+
);
|
505
|
+
} else {
|
506
|
+
this.request._referrer = 'no-referrer';
|
507
|
+
}
|
508
|
+
}
|
509
|
+
|
510
|
+
/**
|
511
|
+
* Validates the request.
|
512
|
+
*
|
513
|
+
* @throws {Error} Throws an error if the request is invalid.
|
514
|
+
*/
|
515
|
+
private validateRequest(): void {
|
516
|
+
if (!SUPPORTED_SCHEMAS.includes(this.request._url.protocol)) {
|
517
|
+
throw new DOMException(
|
518
|
+
`Failed to fetch from "${
|
519
|
+
this.request.url
|
520
|
+
}": URL scheme "${this.request._url.protocol.replace(/:$/, '')}" is not supported.`,
|
521
|
+
DOMExceptionNameEnum.notSupportedError
|
522
|
+
);
|
523
|
+
}
|
524
|
+
}
|
525
|
+
|
526
|
+
/**
|
527
|
+
* Returns request headers.
|
528
|
+
*
|
529
|
+
* @returns Headers.
|
530
|
+
*/
|
531
|
+
private getRequestHeaders(): { [key: string]: string } {
|
532
|
+
const headers = new Headers(this.request.headers);
|
533
|
+
const document = this.ownerDocument;
|
534
|
+
const isCORS = FetchCORSUtility.isCORS(document.location, this.request._url);
|
535
|
+
|
536
|
+
// TODO: Maybe we need to add support for OPTIONS request with 'Access-Control-Allow-*' headers?
|
537
|
+
if (
|
538
|
+
this.request.credentials === 'omit' ||
|
539
|
+
(this.request.credentials === 'same-origin' && isCORS)
|
540
|
+
) {
|
541
|
+
headers.delete('authorization');
|
542
|
+
headers.delete('www-authenticate');
|
543
|
+
}
|
544
|
+
|
545
|
+
headers.set('Accept-Encoding', 'gzip, deflate, br');
|
546
|
+
headers.set('Connection', 'close');
|
547
|
+
|
548
|
+
if (!headers.has('User-Agent')) {
|
549
|
+
headers.set('User-Agent', document.defaultView.navigator.userAgent);
|
550
|
+
}
|
551
|
+
|
552
|
+
if (this.request._referrer instanceof URL) {
|
553
|
+
headers.set('Referer', this.request._referrer.href);
|
554
|
+
}
|
555
|
+
|
556
|
+
if (
|
557
|
+
this.request.credentials === 'include' ||
|
558
|
+
(this.request.credentials === 'same-origin' && !isCORS)
|
559
|
+
) {
|
560
|
+
const cookie = document.defaultView.document.cookie;
|
561
|
+
if (cookie) {
|
562
|
+
headers.set('Cookie', cookie);
|
563
|
+
}
|
564
|
+
}
|
565
|
+
|
566
|
+
if (!headers.has('Accept')) {
|
567
|
+
headers.set('Accept', '*/*');
|
568
|
+
}
|
569
|
+
|
570
|
+
if (!headers.has('Content-Length') && this.request._contentLength !== null) {
|
571
|
+
headers.set('Content-Length', String(this.request._contentLength));
|
572
|
+
}
|
573
|
+
|
574
|
+
if (!headers.has('Content-Type') && this.request._contentType) {
|
575
|
+
headers.set('Content-Type', this.request._contentType);
|
576
|
+
}
|
577
|
+
|
578
|
+
// We need to convert the headers to Node request headers.
|
579
|
+
const httpRequestHeaders = {};
|
580
|
+
|
581
|
+
for (const header of Object.values(headers._entries)) {
|
582
|
+
httpRequestHeaders[header.name] = header.value;
|
583
|
+
}
|
584
|
+
|
585
|
+
return httpRequestHeaders;
|
586
|
+
}
|
587
|
+
|
588
|
+
/**
|
589
|
+
* Returns "true" if redirect.
|
590
|
+
*
|
591
|
+
* @param statusCode Status code.
|
592
|
+
* @returns "true" if redirect.
|
593
|
+
*/
|
594
|
+
private isRedirect(statusCode: number): boolean {
|
595
|
+
return REDIRECT_STATUS_CODES.includes(statusCode);
|
596
|
+
}
|
597
|
+
|
598
|
+
/**
|
599
|
+
* Appends headers to response.
|
600
|
+
*
|
601
|
+
* @param nodeResponse HTTP request.
|
602
|
+
* @returns Headers.
|
603
|
+
*/
|
604
|
+
private getResponseHeaders(nodeResponse: IncomingMessage): Headers {
|
605
|
+
const headers = new Headers();
|
606
|
+
let key = null;
|
607
|
+
|
608
|
+
for (const header of nodeResponse.rawHeaders) {
|
609
|
+
if (!key) {
|
610
|
+
key = header;
|
611
|
+
} else {
|
612
|
+
const lowerKey = key.toLowerCase();
|
613
|
+
|
614
|
+
// Handles setting cookie headers to the document.
|
615
|
+
// "set-cookie" and "set-cookie2" are not allowed in response headers according to spec.
|
616
|
+
if (lowerKey === 'set-cookie' || lowerKey === 'set-cookie2') {
|
617
|
+
const isCORS = FetchCORSUtility.isCORS(this.ownerDocument.location, this.request._url);
|
618
|
+
if (
|
619
|
+
this.request.credentials === 'include' ||
|
620
|
+
(this.request.credentials === 'same-origin' && !isCORS)
|
621
|
+
) {
|
622
|
+
this.ownerDocument.cookie = header;
|
623
|
+
}
|
624
|
+
} else {
|
625
|
+
headers.append(key, header);
|
626
|
+
}
|
627
|
+
|
628
|
+
key = null;
|
629
|
+
}
|
630
|
+
}
|
631
|
+
|
632
|
+
return headers;
|
633
|
+
}
|
634
|
+
|
635
|
+
/**
|
636
|
+
* Finalizes the request.
|
637
|
+
*/
|
638
|
+
private finalizeRequest(): void {
|
639
|
+
this.request.signal.removeEventListener('abort', this.listeners.onSignalAbort);
|
640
|
+
this.nodeRequest.destroy();
|
641
|
+
}
|
642
|
+
|
643
|
+
/**
|
644
|
+
* Aborts the request.
|
645
|
+
*/
|
646
|
+
private abort(): void {
|
647
|
+
const error = new DOMException('The operation was aborted.', DOMExceptionNameEnum.abortError);
|
648
|
+
|
649
|
+
if (this.request.body) {
|
650
|
+
this.request.body.destroy(error);
|
651
|
+
}
|
652
|
+
|
653
|
+
if (!this.response || !this.response.body) {
|
654
|
+
if (this.reject) {
|
655
|
+
this.reject(error);
|
656
|
+
}
|
657
|
+
return;
|
658
|
+
}
|
659
|
+
|
660
|
+
this.response.body.emit('error', error);
|
661
|
+
|
662
|
+
if (this.reject) {
|
663
|
+
this.reject(error);
|
664
|
+
}
|
665
|
+
}
|
666
|
+
}
|