Skip to content

deviceshifuhttp: GET/POST instruction handler silently converts upstream non-2xx to 200 OK #1454

@wlw-edgensis

Description

@wlw-edgensis

Background

deviceshifuhttp proxies HTTP requests from the cluster to a device-side host server. When the host server returns a non-2xx response (e.g. 500 from a failed device probe), the proxy should forward the original status code to the caller.

Root Cause

In pkg/deviceshifu/deviceshifuhttp/deviceshifuhttp.go, the GET/POST instruction handler copies response headers and streams the body, but never calls w.WriteHeader before io.Copy:

// ~line 217
utils.CopyHeader(w.Header(), resp.Header)
_, err := io.Copy(w, resp.Body)   // w.WriteHeader never called

Go's net/http implicitly writes 200 OK the first time the ResponseWriter is written to. As a result, any non-2xx status from the upstream host server is silently translated to 200 OK before reaching the caller.

The HTTPCommandline handler in the same file (around line 371) already does this correctly:

utils.CopyHeader(w.Header(), resp.Header)
w.WriteHeader(resp.StatusCode)   // present here
_, err = io.Copy(w, resp.Body)

How to Trigger

  1. Deploy a device via deviceshifu whose host server returns non-2xx on some endpoint (e.g. a /healthz that probes the physical device and returns 500 when the device is unreachable).
  2. Invoke that endpoint through the deviceshifu ClusterIP service.
  3. Observe: the response status is always 200 OK regardless of what the host server actually returned. The body is forwarded correctly, so a caller checking only the status code will incorrectly conclude the device is healthy.

Impact

Any staleness-detection or health-check mechanism that relies on HTTP status codes from deviceshifu endpoints will not work as expected for HTTP-based device drivers. The body may contain {"ok": false} but the status is 200, which is a semantic contradiction that breaks status-code-based probing.

Fix

Add one line before the io.Copy call in the GET/POST handler:

utils.CopyHeader(w.Header(), resp.Header)
w.WriteHeader(resp.StatusCode)   // add this
_, err := io.Copy(w, resp.Body)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions