func (pc ProposerClient) FastForwardIncrement(ctx context.Context, key string, tombstone protocol.Ballot) (protocol.Age, error) {
	u := *pc.URL
	u.Path = fmt.Sprintf("/fast-forward-increment/%s", url.PathEscape(key))
	req, _ := http.NewRequest("POST", u.String(), nil)
	setBallot(req.Header, tombstone)
	resp, err := pc.httpClient().Do(req)
	if err != nil {
		return protocol.Age{}, err
	}
	if resp.StatusCode != http.StatusOK {
		buf, _ := ioutil.ReadAll(resp.Body)
		return protocol.Age{}, errors.New(strings.TrimSpace(string(buf)))
	}
	return getAge(resp.Header), nil
}
FastForwardIncrement implements extension.Proposer.
package httpapi
import (
	"bufio"
	"bytes"
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"github.com/gorilla/mux"
	"github.com/peterbourgon/caspaxos/extension"
	"github.com/peterbourgon/caspaxos/protocol"
)
type ProposerServer struct {
	proposer protocol.Proposer
	*mux.Router
}
var _ http.Handler = (*ProposerServer)(nil)
func NewProposerServer(proposer protocol.Proposer) *ProposerServer {
	ps := &ProposerServer{
		proposer: proposer,
	}
	r := mux.NewRouter()
	{
		r.StrictSlash(true)
		r.Methods("POST").Path("/read/{key}").HandlerFunc(ps.handleRead)
		r.Methods("POST").Path("/cas/{key}").HandlerFunc(ps.handleCAS)
		r.Methods("POST").Path("/identity-read/{key}").HandlerFunc(ps.handleIdentityRead)
		r.Methods("POST").Path("/add-accepter").HandlerFunc(ps.handleAddAccepter)
		r.Methods("POST").Path("/add-preparer").HandlerFunc(ps.handleAddPreparer)
		r.Methods("POST").Path("/remove-preparer").HandlerFunc(ps.handleRemovePreparer)
		r.Methods("POST").Path("/remove-accepter").HandlerFunc(ps.handleRemoveAccepter)
		r.Methods("POST").Path("/full-identity-read/{key}").HandlerFunc(ps.handleFullIdentityRead)
		r.Methods("POST").Path("/fast-forward-increment/{key}").HandlerFunc(ps.handleFastForwardIncrement)
		r.Methods("GET").Path("/list-preparers").HandlerFunc(ps.handleListPreparers)
		r.Methods("GET").Path("/list-accepters").HandlerFunc(ps.handleListAccepters)
		r.Methods("POST").Path("/watch/{key}").HandlerFunc(ps.handleWatch)
	}
	ps.Router = r
	return ps
}
func (ps *ProposerServer) handleRead(w http.ResponseWriter, r *http.Request) {
	var (
		key, _ = url.PathUnescape(mux.Vars(r)["key"])
		read   = func(x []byte) []byte { return x }
	)
	state, b, err := ps.proposer.Propose(r.Context(), key, read)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	version, value, err := parseVersionValue(state)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	setBallot(w.Header(), b)
	setVersion(w.Header(), version)
	w.Write(value)
}
func (ps *ProposerServer) handleCAS(w http.ResponseWriter, r *http.Request) {
	var (
		key, _         = url.PathUnescape(mux.Vars(r)["key"])
		currentVersion = getVersion(r.Header)
		nextValue, _   = ioutil.ReadAll(r.Body)
	)
	cas := func(x []byte) []byte {
		if version, _, err := parseVersionValue(x); err == nil && version == currentVersion {
			return makeVersionValue(version+1, nextValue)
		}
		return x
	}
	state, _, err := ps.proposer.Propose(r.Context(), key, cas)
	if _, ok := err.(protocol.ConflictError); ok {
		http.Error(w, err.Error(), http.StatusPreconditionFailed)  
		return
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	version, value, err := parseVersionValue(state)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	setVersion(w.Header(), version)
	w.Write(value)
}
func (ps *ProposerServer) handleIdentityRead(w http.ResponseWriter, r *http.Request) {
	ps.handleRead(w, r)
}
func (ps *ProposerServer) handleAddAccepter(w http.ResponseWriter, r *http.Request) {
	buf, _ := ioutil.ReadAll(r.Body)
	u, err := url.Parse(string(buf))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	target := AcceptorClient{URL: u}
	if err := ps.proposer.AddAccepter(target); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintln(w, "OK")
}
func (ps *ProposerServer) handleAddPreparer(w http.ResponseWriter, r *http.Request) {
	buf, _ := ioutil.ReadAll(r.Body)
	u, err := url.Parse(string(buf))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	target := AcceptorClient{URL: u}
	if err := ps.proposer.AddPreparer(target); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintln(w, "OK")
}
func (ps *ProposerServer) handleRemovePreparer(w http.ResponseWriter, r *http.Request) {
	buf, _ := ioutil.ReadAll(r.Body)
	u, err := url.Parse(string(buf))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	target := AcceptorClient{URL: u}
	if err := ps.proposer.RemovePreparer(target); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintln(w, "OK")
}
func (ps *ProposerServer) handleRemoveAccepter(w http.ResponseWriter, r *http.Request) {
	buf, _ := ioutil.ReadAll(r.Body)
	u, err := url.Parse(string(buf))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	target := AcceptorClient{URL: u}
	if err := ps.proposer.RemoveAccepter(target); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintln(w, "OK")
}
func (ps *ProposerServer) handleFullIdentityRead(w http.ResponseWriter, r *http.Request) {
	key, _ := url.PathUnescape(mux.Vars(r)["key"])
	state, b, err := ps.proposer.FullIdentityRead(r.Context(), key)
	setBallot(w.Header(), b)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(state)
}
func (ps *ProposerServer) handleFastForwardIncrement(w http.ResponseWriter, r *http.Request) {
	var (
		key, _    = url.PathUnescape(mux.Vars(r)["key"])
		tombstone = getBallot(r.Header)
	)
	age, err := ps.proposer.FastForwardIncrement(r.Context(), key, tombstone)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	setAge(w.Header(), age)
	fmt.Fprintln(w, "OK")
}
func (ps *ProposerServer) handleListPreparers(w http.ResponseWriter, r *http.Request) {
	addrs, err := ps.proposer.ListPreparers()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	for _, addr := range addrs {
		fmt.Fprintln(w, addr)
	}
}
func (ps *ProposerServer) handleListAccepters(w http.ResponseWriter, r *http.Request) {
	addrs, err := ps.proposer.ListAccepters()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	for _, addr := range addrs {
		fmt.Fprintln(w, addr)
	}
}
func (ps *ProposerServer) handleWatch(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "ProposerServer handleWatch not yet implemented", http.StatusNotImplemented)
}
type ProposerClient struct {
	Client interface {
		Do(*http.Request) (*http.Response, error)
	}
	URL *url.URL
}
var _ extension.Proposer = (*ProposerClient)(nil)
func (pc ProposerClient) Address() string {
	return pc.URL.String()
}
func (pc ProposerClient) Read(ctx context.Context, key string) (version uint64, value []byte, err error) {
	u := *pc.URL
	u.Path = fmt.Sprintf("/read/%s", url.PathEscape(key))
	req, _ := http.NewRequest("POST", u.String(), nil)
	req = req.WithContext(ctx)
	resp, err := pc.httpClient().Do(req)
	if err != nil {
		return 0, nil, err
	}
	if resp.StatusCode != http.StatusOK {
		buf, _ := ioutil.ReadAll(resp.Body)
		return 0, nil, errors.New(strings.TrimSpace(string(buf)))
	}
	version = getVersion(resp.Header)
	value, _ = ioutil.ReadAll(resp.Body)
	return version, value, nil
}
func (pc ProposerClient) CAS(ctx context.Context, key string, currentVersion uint64, nextValue []byte) (version uint64, value []byte, err error) {
	u := *pc.URL
	u.Path = fmt.Sprintf("/cas/%s", url.PathEscape(key))
	req, _ := http.NewRequest("POST", u.String(), bytes.NewReader(nextValue))
	setVersion(req.Header, currentVersion)
	req = req.WithContext(ctx)
	resp, err := pc.httpClient().Do(req)
	if err != nil {
		return version, value, err
	}
	switch {
	case resp.StatusCode == http.StatusPreconditionFailed:  
		buf, _ := ioutil.ReadAll(resp.Body)
		return version, value, extension.CASError{Err: errors.New(strings.TrimSpace(string(buf)))}
	case resp.StatusCode != http.StatusOK:
		buf, _ := ioutil.ReadAll(resp.Body)
		return version, value, errors.New(strings.TrimSpace(string(buf)))
	}
	version = getVersion(resp.Header)
	value, _ = ioutil.ReadAll(resp.Body)
	return version, value, nil
}
func (pc ProposerClient) IdentityRead(ctx context.Context, key string) error {
	_, _, err := pc.readVia(ctx, fmt.Sprintf("/identity-read/%s", url.PathEscape(key)))
	return err
}
func (pc ProposerClient) AddAccepter(target protocol.Acceptor) error {
	return pc.postString("/add-accepter", target.Address())
}
func (pc ProposerClient) AddPreparer(target protocol.Acceptor) error {
	return pc.postString("/add-preparer", target.Address())
}
func (pc ProposerClient) RemovePreparer(target protocol.Acceptor) error {
	return pc.postString("/remove-preparer", target.Address())
}
func (pc ProposerClient) RemoveAccepter(target protocol.Acceptor) error {
	return pc.postString("/remove-accepter", target.Address())
}
func (pc ProposerClient) FullIdentityRead(ctx context.Context, key string) (state []byte, b protocol.Ballot, err error) {
	return pc.readVia(ctx, fmt.Sprintf("/full-identity-read/%s", url.PathEscape(key)))
}