daemon

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package daemon speaks JSON lines on a pipe, so a program in any language can borrow a browser's fingerprint without reimplementing one.

The protocol is one JSON object per line in, one per line out:

in : {"id":7,"url":"https://…","headers":{"a":"b"},"order":["a"]}
out: {"id":7,"status":200,"url":"…","body":"…","headers":{…}}

The process is long-lived and holds one client, which means one TLS fingerprint, one cookie jar and one exit IP for its whole life. That is not a simplification — it is the point. A fresh handshake and an empty jar for every request is itself a signal, and no browser produces it.

Why every response carries an id

The protocol is strictly one request at a time, so an id looks redundant. It is not, and the failure it prevents is silent.

A caller that gives up on a slow request will typically kill this process and start another. But the abandoned process can already have a complete answer on its way up the pipe, and that answer arrives after the caller has moved on to the next request. Without an id it is indistinguishable from the new request's answer — and the response URL cannot stand in for one, because it is the post-redirect URL. The result is one page filed under another page's request: well-formed, plausible and wrong.

So the id is echoed on EVERY response, including every error path, and a caller is expected to drop any line whose id it is not waiting for.

Index

Constants

View Source
const (
	BodyUTF8   = "utf8"
	BodyBase64 = "base64"
)

How a body is spelled on the wire.

Variables

This section is empty.

Functions

func Serve

func Serve(in io.Reader, out io.Writer, client Client) error

Serve reads requests until the input ends.

It returns an error only for a broken pipe. Everything a request can do wrong comes back as a response with an Error field, attached to that request's id — a daemon that exited on a bad URL would take the session's cookie jar with it.

Types

type Client

type Client interface {
	Do(*tlsforge.Request) (*tlsforge.Response, error)
	Headers() tlsforge.Header
}

Client is the part of *tlsforge.Client the daemon needs, named so tests can substitute one without opening a socket.

type Request

type Request struct {
	// ID is caller-assigned and echoed on the response. See the package comment.
	ID uint64 `json:"id"`

	Method  string            `json:"method,omitempty"`
	URL     string            `json:"url"`
	Headers map[string]string `json:"headers,omitempty"`

	// Order is the header order to send. JSON objects have no order, so a caller
	// that cares must say so here. When omitted, the profile's own order is
	// used, and headers the profile does not name go last.
	Order []string `json:"order,omitempty"`

	Body string `json:"body,omitempty"`

	// BodyEncoding says how to read Body. Absent or "utf8" means Body is the
	// bytes themselves; "base64" means it is standard base64.
	//
	// A JSON string cannot hold arbitrary bytes: encoding/json replaces every
	// byte that is not valid UTF-8 with U+FFFD, silently and without an error.
	// So a body that is not text has to travel as base64 or not at all.
	BodyEncoding string `json:"bodyEncoding,omitempty"`

	// SetCookie seeds the jar with "name=value" pairs before the request.
	SetCookie []string `json:"setCookie,omitempty"`
}

Request is one line of input.

type Response

type Response struct {
	// Never omitempty: a caller has to be able to tell "id 0" from "no id at
	// all", and read the latter as "this binary is older than the code driving
	// it" rather than letting every request time out unexplained.
	ID     uint64 `json:"id"`
	Status int    `json:"status"`
	URL    string `json:"url"`
	Body   string `json:"body"`

	// BodyEncoding is absent when Body is the response text, and "base64" when
	// the response was not valid UTF-8 and could not be carried as a JSON
	// string. Absent rather than "utf8" for the common case, so a reader that
	// predates this field is unaffected by it for every text response.
	BodyEncoding string              `json:"bodyEncoding,omitempty"`
	Headers      map[string][]string `json:"headers"`
	Cookies      []string            `json:"cookies"`
	Error        string              `json:"error,omitempty"`
}

Response is one line of output.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL