123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import { AxiosResponse, AxiosRequestConfig } from "axios"
- import { BehaviorSubject, Observable } from "rxjs"
- import cloneDeep from "lodash/cloneDeep"
- import * as T from "fp-ts/Task"
- import * as TE from "fp-ts/TaskEither"
- import { pipe } from "fp-ts/function"
- import AxiosStrategy, {
- cancelRunningAxiosRequest,
- } from "./strategies/AxiosStrategy"
- import ExtensionStrategy, {
- cancelRunningExtensionRequest,
- hasExtensionInstalled,
- } from "./strategies/ExtensionStrategy"
- import { HoppRESTResponse } from "./types/HoppRESTResponse"
- import { EffectiveHoppRESTRequest } from "./utils/EffectiveURL"
- import { settingsStore } from "~/newstore/settings"
- export type NetworkResponse = AxiosResponse<any> & {
- config?: {
- timeData?: {
- startTime: number
- endTime: number
- }
- }
- }
- export type NetworkStrategy = (
- req: AxiosRequestConfig
- ) => TE.TaskEither<any, NetworkResponse>
- export const cancelRunningRequest = () => {
- if (isExtensionsAllowed() && hasExtensionInstalled()) {
- cancelRunningExtensionRequest()
- } else {
- cancelRunningAxiosRequest()
- }
- }
- const isExtensionsAllowed = () => settingsStore.value.EXTENSIONS_ENABLED
- const runAppropriateStrategy = (req: AxiosRequestConfig) => {
- if (isExtensionsAllowed() && hasExtensionInstalled()) {
- return ExtensionStrategy(req)
- }
- return AxiosStrategy(req)
- }
- /**
- * Returns an identifier for how a request will be ran
- * if the system is asked to fire a request
- *
- */
- export function getCurrentStrategyID() {
- if (isExtensionsAllowed() && hasExtensionInstalled()) {
- return "extension" as const
- } else if (settingsStore.value.PROXY_ENABLED) {
- return "proxy" as const
- } else {
- return "normal" as const
- }
- }
- export const sendNetworkRequest = (req: any) =>
- pipe(
- runAppropriateStrategy(req),
- TE.getOrElse((e) => {
- throw e
- })
- )()
- const processResponse = (
- res: NetworkResponse,
- req: EffectiveHoppRESTRequest,
- backupTimeStart: number,
- backupTimeEnd: number,
- successState: HoppRESTResponse["type"]
- ) =>
- pipe(
- TE.Do,
- // Calculate the content length
- TE.bind("contentLength", () =>
- TE.of(
- res.headers["content-length"]
- ? parseInt(res.headers["content-length"])
- : (res.data as ArrayBuffer).byteLength
- )
- ),
- // Building the final response object
- TE.map(
- ({ contentLength }) =>
- <HoppRESTResponse>{
- type: successState,
- statusCode: res.status,
- body: res.data,
- headers: Object.keys(res.headers).map((x) => ({
- key: x,
- value: res.headers[x],
- })),
- meta: {
- responseSize: contentLength,
- responseDuration: backupTimeEnd - backupTimeStart,
- },
- req,
- }
- )
- )
- export function createRESTNetworkRequestStream(
- request: EffectiveHoppRESTRequest
- ): Observable<HoppRESTResponse> {
- const response = new BehaviorSubject<HoppRESTResponse>({
- type: "loading",
- req: request,
- })
- pipe(
- TE.Do,
- // Get a deep clone of the request
- TE.bind("req", () => TE.of(cloneDeep(request))),
- // Assembling headers object
- TE.bind("headers", ({ req }) =>
- TE.of(
- req.effectiveFinalHeaders.reduce((acc, { key, value }) => {
- return Object.assign(acc, { [key]: value })
- }, {})
- )
- ),
- // Assembling params object
- TE.bind("params", ({ req }) => {
- const params = new URLSearchParams()
- req.effectiveFinalParams.forEach((x) => {
- params.append(x.key, x.value)
- })
- return TE.of(params)
- }),
- // Keeping the backup start time
- TE.bind("backupTimeStart", () => TE.of(Date.now())),
- // Running the request and getting the response
- TE.bind("res", ({ req, headers, params }) =>
- runAppropriateStrategy({
- method: req.method as any,
- url: req.effectiveFinalURL.trim(),
- headers,
- params,
- data: req.effectiveFinalBody,
- })
- ),
- // Getting the backup end time
- TE.bind("backupTimeEnd", () => TE.of(Date.now())),
- // Assemble the final response object
- TE.chainW(({ req, res, backupTimeEnd, backupTimeStart }) =>
- processResponse(res, req, backupTimeStart, backupTimeEnd, "success")
- ),
- // Writing success state to the stream
- TE.chain((res) => {
- response.next(res)
- response.complete()
- return TE.of(res)
- }),
- // Package the error type
- TE.getOrElseW((e) => {
- const obj: HoppRESTResponse = {
- type: "network_fail",
- error: e,
- req: request,
- }
- response.next(obj)
- response.complete()
- return T.of(obj)
- })
- )()
- return response
- }
|