Skip to content
Branch: master
Find file History
bedis and Aestek MAJOR: set a channel per engine-id
Each HAProxy process owns an engine ID. When you reload HAProxy, it will
compute a new engine ID.
The ASYNC mode of SPOE applies to all connections pointing to a same
Since the current implementation does not take into account the
engine-id when sending an ACK, it may be possible that an ACK is sent to
the wrong HAProxy client.

This patch fixes this issue by creating one channel per engine id, so
there is no chance to send an ACK to a wrong connection.
Latest commit c9bc8a2 May 13, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information. SPOE: README: example code updated Apr 29, 2019
actions.go First commit Apr 29, 2019
disconnect.go First commit Apr 29, 2019
encoding.go First commit Apr 29, 2019
frame.go SPOE: MINOR: frame: check frame length against maxFrameSize Apr 29, 2019
frame_test.go First commit Apr 29, 2019
hello.go MAJOR: set a channel per engine-id May 17, 2019
hello_test.go First commit Apr 29, 2019
notify.go First commit Apr 29, 2019
spoe.go MAJOR: set a channel per engine-id May 17, 2019

SPOE in Go

An implementation of the SPOP protocol in Go. (


From Haproxy's documentation :

SPOE is a feature introduced in HAProxy 1.7. It makes possible the communication with external components to retrieve some info. The idea started with the problems caused by most ldap libs not working fine in event-driven systems (often at least the connect() is blocking). So, it is hard to properly implement Single Sign On solution (SSO) in HAProxy. The SPOE will ease this kind of processing, or we hope so.

Now, the aim of SPOE is to allow any kind of offloading on the streams. First releases, besides being experimental, won't do lot of things. As we will see, there are few handled events and even less actions supported. Actually, for now, the SPOE can offload the processing before "tcp-request content", "tcp-response content", "http-request" and "http-response" rules. And it only supports variables definition. But, in spite of these limited features, we can easily imagine to implement SSO solution, ip reputation or ip geolocation services.

How to use

package main

import (

        log ""

func getReputation(ip net.IP) (float64, error) {
	// implement IP reputation code here
        return 1.0, nil

func main() {
	agent := spoe.New(func(messages []spoe.Message) ([]spoe.Action, error) {
		reputation := 0.0

		for _, msg := range messages {
			if msg.Name != "ip-rep" {

			ip, ok := msg.Args["ip"].(net.IP)
			if !ok {
				return nil, fmt.Errorf("spoe handler: expected ip in message")

			reputation, err = getReputation(ip)
			if err != nil {
				return nil, fmt.Errorf("spoe handler: error processing request: %s", err)

		return []spoe.Action{
				Name:  "reputation",
				Scope: spoe.VarScopeSession,
				Value: reputation,
		}, nil

	if err := agent.ListenAndServe(":9000"); err != nil {
You can’t perform that action at this time.