func (node *Node) httpPublish(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ns := vars["namespace"]
	if !nsrx.Match([]byte(ns)) {
		apiError(w, http.StatusBadRequest, BadNamespace)
		return
	}
	dec := json.NewDecoder(r.Body)
	stmts := make([]interface{}, 0, 1024)
loop:
	for {
		sbody := new(pb.SimpleStatement)
		err := dec.Decode(sbody)
		switch {
		case err == io.EOF:
			break loop
		case err != nil:
			apiError(w, http.StatusBadRequest, err)
			return
		default:
			stmts = append(stmts, sbody)
		}
	}
	if len(stmts) == 0 {
		return
	}
	sids, err := node.doPublishBatch(ns, stmts)
	if err != nil {
		apiError(w, http.StatusInternalServerError, err)
		return
	}
	for _, sid := range sids {
		fmt.Fprintln(w, sid)
	}
}
POST /publish/{namespace}
DATA: A stream of json-encoded pb.SimpleStatements
Publishes a batch of statements to the specified namespace.
Returns the statement ids as a newline delimited stream.
package main
import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	mux "github.com/gorilla/mux"
	p2p_crypto "github.com/libp2p/go-libp2p-crypto"
	p2p_peer "github.com/libp2p/go-libp2p-peer"
	p2p_pstore "github.com/libp2p/go-libp2p-peerstore"
	mc "github.com/mediachain/concat/mc"
	mcq "github.com/mediachain/concat/mc/query"
	pb "github.com/mediachain/concat/proto"
	multihash "github.com/multiformats/go-multihash"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)
func apiError(w http.ResponseWriter, status int, err error) {
	w.WriteHeader(status)
	fmt.Fprintf(w, "Error: %s\n", err.Error())
}
func apiNetError(w http.ResponseWriter, err error) {
	switch err {
	case UnknownPeer:
		apiError(w, http.StatusNotFound, err)
	default:
		apiError(w, http.StatusInternalServerError, err)
	}
}
func (node *Node) httpId(w http.ResponseWriter, r *http.Request) {
	ninfo := NodeInfo{node.PeerIdentity.Pretty(), node.publisher.Pretty(), node.info}
	err := json.NewEncoder(w).Encode(ninfo)
	if err != nil {
		log.Printf("Error writing response body: %s", err.Error())
	}
}
func (node *Node) httpRemoteId(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		apiError(w, http.StatusBadRequest, err)
		return
	}
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	ninfo, err := node.doRemoteId(ctx, pid)
	if err != nil {
		apiNetError(w, err)
		return
	}
	err = json.NewEncoder(w).Encode(ninfo)
	if err != nil {
		log.Printf("Error writing response body: %s", err.Error())
	}
}
func (node *Node) httpNetAddr(w http.ResponseWriter, r *http.Request) {
	addrs := node.netAddrs()
	for _, addr := range addrs {
		fmt.Fprintln(w, addr.String())
	}
}
func (node *Node) httpNetPeerAddr(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		apiError(w, http.StatusBadRequest, err)
		return
	}
	addrs := node.netPeerAddrs(pid)
	for _, addr := range addrs {
		fmt.Fprintln(w, addr.String())
	}
}
func (node *Node) httpNetConns(w http.ResponseWriter, r *http.Request) {
	peers := node.netConns()
	for _, peer := range peers {
		fmt.Fprintln(w, mc.FormatHandle(peer))
	}
}
func (node *Node) httpNetLookup(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		apiError(w, http.StatusBadRequest, err)
		return
	}
	pinfo, err := node.doLookup(r.Context(), pid)
	if err != nil {
		apiNetError(w, err)
		return
	}
	for _, addr := range pinfo.Addrs {
		fmt.Fprintln(w, addr.String())
	}
}
func (node *Node) httpNetIdentify(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		apiError(w, http.StatusBadRequest, err)
		return
	}
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	nid, err := node.netIdentify(ctx, pid)
	if err != nil {
		apiNetError(w, err)
		return
	}
	err = json.NewEncoder(w).Encode(nid)
	if err != nil {
		log.Printf("Error writing response body: %s", err.Error())
	}
}
func (node *Node) httpNetPing(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		apiError(w, http.StatusBadRequest, err)
		return
	}
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	dt, err := node.netPing(ctx, pid)
	if err != nil {
		apiNetError(w, err)
		return
	}
	fmt.Fprintln(w, dt)
}
func (node *Node) httpNetFindPeers(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	ch, err := node.netFindPeers(ctx)
	if err != nil {
		apiNetError(w, err)
		return
	}
	for pinfo := range ch {
		fmt.Fprintln(w, pinfo.ID.Pretty())
	}
}
func (node *Node) httpPing(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		apiError(w, http.StatusBadRequest, err)
		return
	}
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	err = node.doPing(ctx, pid)
	if err != nil {
		apiNetError(w, err)
		return
	}
	fmt.Fprintln(w, "OK")
}
func (node *Node) httpDirList(w http.ResponseWriter, r *http.Request) {
	node.httpDirListImpl(w, r, false)
}
func (node *Node) httpDirListAll(w http.ResponseWriter, r *http.Request) {
	node.httpDirListImpl(w, r, true)
}
func (node *Node) httpDirListImpl(w http.ResponseWriter, r *http.Request, inclSelf bool) {
	vars := mux.Vars(r)
	ns := vars["namespace"]
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	peers, err := node.doDirList(ctx, ns)
	if err != nil {
		apiError(w, http.StatusInternalServerError, err)
		return
	}
	mypid := node.PeerIdentity.Pretty()
	for _, peer := range peers {
		if peer != mypid || inclSelf {
			fmt.Fprintln(w, peer)
		}
	}
}
func (node *Node) httpDirListNS(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	nss, err := node.doDirListNS(ctx)
	if err != nil {
		apiError(w, http.StatusInternalServerError, err)
		return
	}
	for _, ns := range nss {
		fmt.Fprintln(w, ns)
	}
}
func (node *Node) httpDirListMF(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	entity := vars["entity"]
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()
	mfs, err := node.doDirListMF(ctx, entity)
	if err != nil {
		apiError(w, http.StatusInternalServerError, err)
		return
	}
	enc := json.NewEncoder(w)
	for _, mf := range mfs {
		err = enc.Encode(mf)
		if err != nil {
			log.Printf("Error encoding manifest result: %s", err.Error())
			return
		}
	}
}
var nsrx *regexp.Regexp
func init() {
	rx, err := regexp.Compile("^[a-zA-Z0-9-]+([.][a-zA-Z0-9-]+)*$")
	if err != nil {
		log.Fatal(err)
	}
	nsrx = rx
}