mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-11-06 00:54:23 +00:00
Merge pull request #56 from lordmathis/fix/body-already-read
Fix double read of json response when content-length header is missing
This commit is contained in:
@@ -131,11 +131,16 @@ func (h *Handler) ListInstances() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
// Marshal to bytes first to set Content-Length header
|
||||||
if err := json.NewEncoder(w).Encode(instances); err != nil {
|
data, err := json.Marshal(instances)
|
||||||
|
if err != nil {
|
||||||
http.Error(w, "Failed to encode instances: "+err.Error(), http.StatusInternalServerError)
|
http.Error(w, "Failed to encode instances: "+err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
|
||||||
|
w.Write(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ describe('API Error Handling', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('converts HTTP errors to meaningful messages', async () => {
|
it('converts HTTP errors to meaningful messages', async () => {
|
||||||
mockFetch.mockResolvedValue({
|
const mockResponse = {
|
||||||
ok: false,
|
ok: false,
|
||||||
status: 409,
|
status: 409,
|
||||||
text: () => Promise.resolve('Instance already exists')
|
text: () => Promise.resolve('Instance already exists'),
|
||||||
})
|
clone: function() { return this }
|
||||||
|
}
|
||||||
|
mockFetch.mockResolvedValue(mockResponse)
|
||||||
|
|
||||||
await expect(instancesApi.create('existing', {}))
|
await expect(instancesApi.create('existing', {}))
|
||||||
.rejects
|
.rejects
|
||||||
@@ -23,11 +25,13 @@ describe('API Error Handling', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles empty error responses gracefully', async () => {
|
it('handles empty error responses gracefully', async () => {
|
||||||
mockFetch.mockResolvedValue({
|
const mockResponse = {
|
||||||
ok: false,
|
ok: false,
|
||||||
status: 500,
|
status: 500,
|
||||||
text: () => Promise.resolve('')
|
text: () => Promise.resolve(''),
|
||||||
})
|
clone: function() { return this }
|
||||||
|
}
|
||||||
|
mockFetch.mockResolvedValue(mockResponse)
|
||||||
|
|
||||||
await expect(instancesApi.list())
|
await expect(instancesApi.list())
|
||||||
.rejects
|
.rejects
|
||||||
|
|||||||
@@ -49,11 +49,8 @@ async function apiCall<T>(
|
|||||||
} else {
|
} else {
|
||||||
// Handle empty responses for JSON endpoints
|
// Handle empty responses for JSON endpoints
|
||||||
const contentLength = response.headers.get('content-length');
|
const contentLength = response.headers.get('content-length');
|
||||||
if (contentLength === '0' || contentLength === null) {
|
if (contentLength === '0') {
|
||||||
const text = await response.text();
|
return {} as T; // Return empty object for empty JSON responses
|
||||||
if (text.trim() === '') {
|
|
||||||
return {} as T; // Return empty object for empty JSON responses
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const data = await response.json() as T;
|
const data = await response.json() as T;
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export async function handleApiError(response: Response): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorMessage = await parseErrorResponse(response)
|
// Clone the response before reading to avoid consuming the body stream
|
||||||
|
const errorMessage = await parseErrorResponse(response.clone())
|
||||||
throw new Error(errorMessage)
|
throw new Error(errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user