Просмотр исходного кода

Add timeouts to file-view-text fetch requests (#15690)

* Add timeouts to file-view-text fetch requests with useAbortController for graceful cleanup

* Add context to error log for file-view-text content fetching

GitOrigin-RevId: de50e5d99f22b219eb31425ab39ca35d84300625
Thomas 2 лет назад
Родитель
Сommit
3ff58aa318

+ 35 - 13
services/web/frontend/js/features/file-view/components/file-view-text.jsx

@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
 import PropTypes from 'prop-types'
 import { useProjectContext } from '../../../shared/context/project-context'
 import { debugConsole } from '@/utils/debugging'
+import useAbortController from '../../../shared/hooks/use-abort-controller'
 
 const MAX_FILE_SIZE = 2 * 1024 * 1024
 
@@ -14,12 +15,20 @@ export default function FileViewText({ file, onLoad, onError }) {
   const [shouldShowDots, setShouldShowDots] = useState(false)
   const [inFlight, setInFlight] = useState(false)
 
+  const fetchContentLengthController = useAbortController()
+  const fetchDataController = useAbortController()
+
   useEffect(() => {
     if (inFlight) {
       return
     }
     let path = `/project/${projectId}/file/${file.id}`
-    fetch(path, { method: 'HEAD' })
+    const fetchContentLengthTimeout = setTimeout(
+      () => fetchContentLengthController.abort(),
+      10000
+    )
+    let fetchDataTimeout
+    fetch(path, { method: 'HEAD', signal: fetchContentLengthController.signal })
       .then(response => {
         if (!response.ok) throw new Error('HTTP Error Code: ' + response.status)
         return response.headers.get('Content-Length')
@@ -35,26 +44,39 @@ export default function FileViewText({ file, onLoad, onError }) {
         if (maxSize != null) {
           path += `?range=0-${maxSize}`
         }
-        return fetch(path).then(response => {
-          return response.text().then(text => {
-            if (truncated) {
-              text = text.replace(/\n.*$/, '')
-            }
+        fetchDataTimeout = setTimeout(() => fetchDataController.abort(), 60000)
+        return fetch(path, { signal: fetchDataController.signal }).then(
+          response => {
+            return response.text().then(text => {
+              if (truncated) {
+                text = text.replace(/\n.*$/, '')
+              }
 
-            setTextPreview(text)
-            onLoad()
-            setShouldShowDots(truncated)
-          })
-        })
+              setTextPreview(text)
+              onLoad()
+              setShouldShowDots(truncated)
+            })
+          }
+        )
       })
       .catch(err => {
-        debugConsole.error(err)
+        debugConsole.error('Error fetching file contents', err)
         onError()
       })
       .finally(() => {
         setInFlight(false)
+        clearTimeout(fetchContentLengthTimeout)
+        clearTimeout(fetchDataTimeout)
       })
-  }, [projectId, file.id, onError, onLoad, inFlight])
+  }, [
+    projectId,
+    file.id,
+    onError,
+    onLoad,
+    inFlight,
+    fetchContentLengthController,
+    fetchDataController,
+  ])
   return (
     <div>
       {textPreview && (