# How to Use the HTTP Plugin for API Testing in Probe: A Complete Guide

> Master API testing with the Probe HTTP plugin. Send requests, check responses, and validate results using intuitive YAML workflows. Learn how to normalize parameters and merge headers for efficient testing.

- Repository: [Tomohisa Oda/probe](https://github.com/linyows/probe)
- Tags: how-to-guide
- Published: 2026-03-06

---

**The HTTP plugin in linyows/probe enables comprehensive API testing by sending HTTP requests, inspecting responses, and asserting conditions through YAML-based workflows that normalize parameters, merge headers case-insensitively, and return structured results for validation.**

The `linyows/probe` repository provides a flexible workflow engine designed for automated testing scenarios, with its built-in HTTP plugin serving as the primary mechanism for API testing. This plugin allows you to configure requests using intuitive YAML syntax while handling complex operations like JSON serialization, header management, and binary response handling behind the scenes.

## How the HTTP Plugin Works in Probe

The HTTP plugin operates as a HashiCorp go-plugin process, enabling dynamic loading and execution separate from the main Probe binary. When invoked via `uses: http` in a workflow step, the plugin executes a sophisticated request lifecycle defined in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) and [`actions/http/main.go`](https://github.com/linyows/probe/blob/main/actions/http/main.go).

### Request Parameter Normalization

In [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go), the `ResolveMethodAndURL` function (lines 92-124) scans the `with` configuration map for shortcut method fields including `get`, `post`, `put`, `delete`, and `patch`. It resolves these shortcuts into a proper HTTP `method` and fully-qualified `url`, handling relative routes and query strings automatically. This allows concise YAML syntax like `get: /health` instead of explicitly defining `method: GET` and constructing the full URL.

### Header Processing and JSON Serialization

The `NewReq` function in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) (lines 59-90) creates default headers including `Accept` and `User-Agent`, then merges custom headers from the workflow configuration using case-insensitive matching to prevent duplicates. When `Content-Type` is set to `application/json`, the `ProcessHttpBody` function (lines 47-73) automatically marshals map or array bodies into JSON strings before transmission.

### Response Handling and Result Mapping

After execution via `Req.Do` (lines 94-140), the response is wrapped in a `Result` struct containing `status`, `code`, `headers`, `body`, and `filepath` (for binary data). The `StructToMapByTags` function from [`probe.go`](https://github.com/linyows/probe/blob/main/probe.go) (referenced in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) lines 63-71) converts this struct into a generic `map[string]any` that becomes the step's `res` object. This structured data is accessible in `test` expressions and `outputs` for downstream workflow steps.

## HTTP Plugin Configuration Examples

The HTTP plugin supports diverse API testing scenarios through flexible YAML configuration. These examples demonstrate practical implementations ranging from simple health checks to complex authenticated requests and binary file handling.

### Simple Health Check with GET Requests

```yaml
name: API Health Check
jobs:
  - name: Check API Status
    steps:
      - name: Ping API
        uses: http
        with:
          url: https://api.example.com
          get: /health
        test: res.code == 200

```

The `get` shortcut resolves to `method: GET` and combines with the `url` base to form `https://api.example.com/health`. The `test` expression validates that the HTTP status code equals `200`, marking the step as failed if the assertion fails.

### POST Requests with JSON Body and Authentication

```yaml
- name: Create User
  uses: http
  with:
    url: https://api.example.com
    post: /users
    headers:
      content-type: application/json
      authorization: Bearer {{vars.api_token}}
    body:
      name: "{{vars.new_user_name}}"
      email: "{{vars.new_user_email}}"
  test: |
    res.code == 201 &&
    match_json(res.body, {"id": "[0-9]+"})
  outputs:
    user_id: res.body.id

```

Custom headers merge with defaults case-insensitively. Setting `content-type: application/json` triggers automatic JSON serialization of the `body` map. The `match_json` function (provided by the expression engine in [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go)) validates the response format using regex patterns, while `outputs` captures the created user ID for subsequent steps.

### Binary File Downloads

```yaml
- name: Download Report
  uses: http
  with:
    url: https://reports.example.com
    get: /monthly/report.pdf
    timeout: 60s
  test: res.code == 200
  outputs:
    report_path: res.filepath

```

When the response `Content-Type` indicates binary data, the `ProcessHttpBody` function in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) writes the content to a temporary file rather than storing it in memory. The file path is returned via `res.filepath`, allowing downstream steps to access the downloaded PDF without loading the entire binary into the workflow state.

### Advanced Callbacks for Logging

For scenarios requiring custom pre- or post-processing, the plugin supports `before` and `after` callbacks. While these are configured in Go code rather than YAML, they are available through the plugin API:

```go
http.WithBefore(func(req *http.Request) {
    log.Info("Sending request", "url", req.URL.String())
})

http.WithAfter(func(res *http.Response) {
    log.Info("Received response", "status", res.Status)
})

```

These callbacks are invoked by the `Run` method in [`actions/http/main.go`](https://github.com/linyows/probe/blob/main/actions/http/main.go) (lines 31-38) during the request lifecycle, enabling custom logging, metrics collection, or request modification.

## Key Source Files and Implementation Details

| File | Role | Key Functions |
|------|------|---------------|
| [`actions/http/main.go`](https://github.com/linyows/probe/blob/main/actions/http/main.go) | Plugin entry point and workflow integration | `Run`, `Serve` |
| [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) | Core HTTP client logic | `ResolveMethodAndURL`, `NewReq`, `ProcessHttpBody`, `Req.Do` |
| [`probe.go`](https://github.com/linyows/probe/blob/main/probe.go) | Struct mapping utilities | `StructToMapByTags` |
| [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go) | Expression evaluation engine | `match_json` and assertion functions |
| [`README.md`](https://github.com/linyows/probe/blob/main/README.md) | User documentation | HTTP action configuration reference |

The plugin architecture utilizes the HashiCorp go-plugin protocol, enabling dynamic loading as a separate process (implemented in [`actions/http/main.go`](https://github.com/linyows/probe/blob/main/actions/http/main.go) lines 53-69). This separation ensures that HTTP request execution does not block the main Probe workflow engine and allows for isolated plugin lifecycle management.

## Summary

- The HTTP plugin in `linyows/probe` provides comprehensive API testing capabilities through YAML configuration, supporting shortcut methods (`get`, `post`, `put`, `delete`, `patch`) and automatic URL resolution via `ResolveMethodAndURL`.
- Request processing in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) handles case-insensitive header merging, automatic JSON serialization for `application/json` content types, and binary file downloads via temporary file paths returned in `res.filepath`.
- Test assertions leverage the `res` object containing `code`, `body`, `headers`, and `filepath`, evaluated through the expression engine in [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go) with support for functions like `match_json`.
- The plugin operates as a HashiCorp go-plugin process ([`actions/http/main.go`](https://github.com/linyows/probe/blob/main/actions/http/main.go)), enabling dynamic loading and supporting advanced features like `before` and `after` callbacks for custom logging and request modification.

## Frequently Asked Questions

### How do I set custom headers in the Probe HTTP plugin?

Custom headers are defined in the `headers` map within the `with` block of your workflow step. The `NewReq` function in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) (lines 59-90) merges these with default headers (`Accept`, `User-Agent`) using case-insensitive matching to prevent duplicates. You can override defaults like `content-type` or add authorization headers such as `authorization: Bearer {{vars.token}}`.

### Can the HTTP plugin handle file uploads or binary data?

Yes. For binary responses, the `ProcessHttpBody` function in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) detects non-text content types and writes the response to a temporary file, returning the path via `res.filepath`. For uploads, you would typically read a file in a previous step and pass its content in the `body` field, or use the `before` callback to modify the request for multipart uploads.

### What expression functions are available for testing HTTP responses?

The expression engine defined in [`expr.go`](https://github.com/linyows/probe/blob/main/expr.go) provides built-in functions for assertions. Common functions include `match_json` for regex-based JSON validation, standard comparison operators for `res.code`, and path accessors for `res.body` and `res.headers`. The `test` field supports complex boolean expressions using `&&` and `||` operators to validate multiple response conditions simultaneously.

### How does the plugin handle HTTP redirects?

The HTTP plugin uses Go's standard `net/http` client with default redirect handling. As implemented in [`http/client.go`](https://github.com/linyows/probe/blob/main/http/client.go) (lines 94-140), the `Req.Do` method follows redirects automatically up to the default maximum of 10 consecutive redirects. If you need to disable redirects or customize the behavior, you would currently need to use the `before` callback to modify the underlying `http.Client` configuration, though the default behavior suits most API testing scenarios.