~fnux/yggdrasil-go-coap

b39d71afd59b59350db48e82c94188e89053abc1 — Jozef Kralik 2 years ago cf6d606
add New{GET,POST,PUT,DELETE}Request and NewResponse
M README.md => README.md +6 -12
@@ 32,18 32,12 @@ Fork of https://github.com/dustin/go-coap
	// See /examples/simple/server/main.go
	func handleA(w coap.ResponseWriter, req *coap.Request) {
		log.Printf("Got message in handleA: path=%q: %#v from %v", req.Msg.Path(), req.Msg, req.Client.RemoteAddr())
		if req.Msg.IsConfirmable() {
			res := req.Client.NewMessage(coap.MessageParams{
				Type:      coap.Acknowledgement,
				Code:      coap.Content,
				MessageID: req.Msg.MessageID(),
				Token:     req.Msg.Token(),
				Payload:   []byte("hello to you!"),
			})
			res.SetOption(coap.ContentFormat, coap.TextPlain)

			log.Printf("Transmitting from A %#v", res)
			w.Write(res)
		resp := w.NewResponse(coap.Content)
		resp.SetOption(coap.ContentFormat, coap.TextPlain)
		resp.SetPayload([]byte("hello world"))
		log.Printf("Transmitting from A %#v", resp)
		if err := w.Write(resp); err != nil {
			log.Printf("Cannot send response: %v", err)
		}
	}	


M blockwise.go => blockwise.go +14 -11
@@ 1,6 1,7 @@
package coap

import (
	"bytes"
	"fmt"
	"log"
	"time"


@@ 352,7 353,7 @@ type blockWiseReceiver struct {
	currentMore  bool
	payloadSize  uint32

	payload []byte
	payload *bytes.Buffer
}

func (r *blockWiseReceiver) sizeType() OptionID {


@@ 389,7 390,7 @@ func (r *blockWiseReceiver) createReq(b *blockWiseSession, resp Message) (Messag
		}
	}

	if len(r.payload) > 0 {
	if r.payload.Len() > 0 {
		block, err := MarshalBlockOption(r.currentSzx, r.nextNum, r.currentMore)
		if err != nil {
			return nil, err


@@ 406,13 407,14 @@ func newReceiver(b *blockWiseSession, peerDrive bool, origin Message, resp Messa
		origin:     origin,
		blockType:  blockType,
		currentSzx: b.networkSession.blockWiseSzx(),
		payload:    []byte{},
		payload:    bytes.NewBuffer(make([]byte, 0)),
	}

	if resp != nil {
		var ok bool
		if r.payloadSize, ok = resp.Option(r.sizeType()).(uint32); ok {
			//try to get Size
			r.payload.Grow(int(r.payloadSize))
		}
		if respBlock, ok := resp.Option(blockType).(uint32); ok {
			//contains block


@@ 441,7 443,7 @@ func newReceiver(b *blockWiseSession, peerDrive bool, origin Message, resp Messa
			return r, resp, nil
		}
		//append payload and set block
		r.payload = append(r.payload, resp.Payload()...)
		r.payload.Write(resp.Payload())
	}

	if peerDrive {


@@ 463,7 465,7 @@ func newReceiver(b *blockWiseSession, peerDrive bool, origin Message, resp Messa
			r.nextNum = num
			r.currentMore = more
		}
		r.payload = append(r.payload, origin.Payload()...)
		r.payload.Write(origin.Payload())
	}

	return r, nil, nil


@@ 498,7 500,7 @@ func (r *blockWiseReceiver) processResp(b *blockWiseSession, req Message, resp M
			return nil, ErrInvalidBlockSzx
		}
		startOffset := calcStartOffset(num, szx)
		if len(r.payload) < startOffset {
		if r.payload.Len() < startOffset {
			return nil, ErrRequestEntityIncomplete
		}
		if more == true && len(resp.Payload())%SZXVal[szx] != 0 {


@@ 508,14 510,15 @@ func (r *blockWiseReceiver) processResp(b *blockWiseSession, req Message, resp M
			//reagain
			r.nextNum = num
		} else {
			r.payload = append(r.payload[:startOffset], resp.Payload()...)
			r.payload.Truncate(startOffset)
			r.payload.Write(resp.Payload())
			if r.peerDrive {
				r.nextNum = num
			} else {
				if szx > b.blockWiseSzx() {
					num = 0
					szx = b.blockWiseSzx()
					r.nextNum = calcNextNum(num, szx, len(r.payload))
					r.nextNum = calcNextNum(num, szx, r.payload.Len())
				} else {
					r.nextNum = calcNextNum(num, szx, len(resp.Payload()))
				}


@@ 523,11 526,11 @@ func (r *blockWiseReceiver) processResp(b *blockWiseSession, req Message, resp M
		}

		if more == false {
			if r.payloadSize != 0 && int(r.payloadSize) != len(r.payload) {
			if r.payloadSize != 0 && int(r.payloadSize) != r.payload.Len() {
				return nil, ErrInvalidPayloadSize
			}
			if len(r.payload) > 0 {
				resp.SetPayload(r.payload)
			if r.payload.Len() > 0 {
				resp.SetPayload(r.payload.Bytes())
			}
			// remove block used by blockWise
			resp.RemoveOption(r.sizeType())

M client.go => client.go +20 -0
@@ 254,6 254,26 @@ func (co *ClientConn) NewMessage(p MessageParams) Message {
	return co.commander.NewMessage(p)
}

// NewGetRequest creates get request
func (co *ClientConn) NewGetRequest(path string) (Message, error) {
	return co.commander.NewGetRequest(path)
}

// NewPostRequest creates post request
func (co *ClientConn) NewPostRequest(path string, contentType MediaType, body io.Reader) (Message, error) {
	return co.commander.NewPostRequest(path, contentType, body)
}

// NewPutRequest creates put request
func (co *ClientConn) NewPutRequest(path string, contentType MediaType, body io.Reader) (Message, error) {
	return co.commander.NewPutRequest(path, contentType, body)
}

// NewDeleteRequest creates delete request
func (co *ClientConn) NewDeleteRequest(path string) (Message, error) {
	return co.commander.NewDeleteRequest(path)
}

// Write sends direct a message through the connection
func (co *ClientConn) Write(m Message) error {
	return co.commander.Write(m)

M clientcommander.go => clientcommander.go +88 -32
@@ 14,11 14,67 @@ type ClientCommander struct {
	networkSession networkSession
}

// NewMessage Create message for request
// NewMessage creates message for request
func (cc *ClientCommander) NewMessage(p MessageParams) Message {
	return cc.networkSession.NewMessage(p)
}

func (cc *ClientCommander) newGetDeleteRequest(path string, code COAPCode) (Message, error) {
	token, err := GenerateToken()
	if err != nil {
		return nil, err
	}
	req := cc.NewMessage(MessageParams{
		Type:      NonConfirmable,
		Code:      code,
		MessageID: GenerateMessageID(),
		Token:     token,
	})
	req.SetPathString(path)
	return req, nil
}

func (cc *ClientCommander) newPostPutRequest(path string, contentType MediaType, body io.Reader, code COAPCode) (Message, error) {
	token, err := GenerateToken()
	if err != nil {
		return nil, err
	}
	req := cc.networkSession.NewMessage(MessageParams{
		Type:      Confirmable,
		Code:      code,
		MessageID: GenerateMessageID(),
		Token:     token,
	})
	req.SetPathString(path)
	req.SetOption(ContentFormat, contentType)
	payload, err := ioutil.ReadAll(body)
	if err != nil {
		return nil, err
	}
	req.SetPayload(payload)
	return req, nil
}

// NewGetRequest creates get request
func (cc *ClientCommander) NewGetRequest(path string) (Message, error) {
	return cc.newGetDeleteRequest(path, GET)
}

// NewPostRequest creates post request
func (cc *ClientCommander) NewPostRequest(path string, contentType MediaType, body io.Reader) (Message, error) {
	return cc.newPostPutRequest(path, contentType, body, POST)
}

// NewPutRequest creates put request
func (cc *ClientCommander) NewPutRequest(path string, contentType MediaType, body io.Reader) (Message, error) {
	return cc.newPostPutRequest(path, contentType, body, PUT)
}

// NewDeleteRequest creates delete request
func (cc *ClientCommander) NewDeleteRequest(path string) (Message, error) {
	return cc.newGetDeleteRequest(path, DELETE)
}

// LocalAddr implements the networkSession.LocalAddr method.
func (cc *ClientCommander) LocalAddr() net.Addr {
	return cc.networkSession.LocalAddr()


@@ 57,57 113,37 @@ func (cc *ClientCommander) Ping(timeout time.Duration) error {

// Get retrieve the resource identified by the request path
func (cc *ClientCommander) Get(path string) (Message, error) {
	req, err := createGetReq(cc, path)
	req, err := cc.NewGetRequest(path)
	if err != nil {
		return nil, err
	}
	return cc.networkSession.Exchange(req)
}

func (cc *ClientCommander) putPostHelper(code COAPCode, path string, contentType MediaType, body io.Reader) (Message, error) {
	token, err := GenerateToken()
	if err != nil {
		return nil, err
	}
	req := cc.networkSession.NewMessage(MessageParams{
		Type:      Confirmable,
		Code:      POST,
		MessageID: GenerateMessageID(),
		Token:     token,
	})
	req.SetPathString(path)
	req.SetOption(ContentFormat, contentType)
	payload, err := ioutil.ReadAll(body)
// Post update the resource identified by the request path
func (cc *ClientCommander) Post(path string, contentType MediaType, body io.Reader) (Message, error) {
	req, err := cc.NewPostRequest(path, contentType, body)
	if err != nil {
		return nil, err
	}
	req.SetPayload(payload)
	return cc.networkSession.Exchange(req)
}

// Post update the resource identified by the request path
func (cc *ClientCommander) Post(path string, contentType MediaType, body io.Reader) (Message, error) {
	return cc.putPostHelper(POST, path, contentType, body)
}

// Put create the resource identified by the request path
func (cc *ClientCommander) Put(path string, contentType MediaType, body io.Reader) (Message, error) {
	return cc.putPostHelper(PUT, path, contentType, body)
	req, err := cc.NewPutRequest(path, contentType, body)
	if err != nil {
		return nil, err
	}
	return cc.networkSession.Exchange(req)
}

// Delete delete the resource identified by the request path
func (cc *ClientCommander) Delete(path string) (Message, error) {
	token, err := GenerateToken()
	req, err := cc.NewDeleteRequest(path)
	if err != nil {
		return nil, err
	}
	req := cc.networkSession.NewMessage(MessageParams{
		Type:      Confirmable,
		Code:      DELETE,
		MessageID: GenerateMessageID(),
		Token:     token,
	})
	req.SetPathString(path)
	return cc.networkSession.Exchange(req)
}



@@ 140,7 176,7 @@ func (o *Observation) Cancel() error {
// Observe subscribe to severon path. After subscription and every change on path,
// server sends immediately response
func (cc *ClientCommander) Observe(path string, observeFunc func(req *Request)) (*Observation, error) {
	req, err := createGetReq(cc, path)
	req, err := cc.NewGetRequest(path)
	if err != nil {
		return nil, err
	}


@@ 228,3 264,23 @@ func (cc *ClientCommander) Observe(path string, observeFunc func(req *Request)) 
func (cc *ClientCommander) Close() error {
	return cc.networkSession.Close()
}

// SetReadDeadline set read deadline for timeout for Exchange
func (cc *ClientCommander) SetReadDeadline(timeout time.Duration) {
	cc.networkSession.SetReadDeadline(timeout)
}

// SetWriteDeadline set write deadline for timeout for Exchange and Write
func (cc *ClientCommander) SetWriteDeadline(timeout time.Duration) {
	cc.networkSession.SetWriteDeadline(timeout)
}

// ReadDeadline get read deadline
func (cc *ClientCommander) ReadDeadline() time.Duration {
	return cc.networkSession.ReadDeadline()
}

// WriteDeadline get read writeline
func (cc *ClientCommander) WriteDeadline() time.Duration {
	return cc.networkSession.WriteDeadline()
}

M examples/mcast/server/main.go => examples/mcast/server/main.go +4 -10
@@ 8,16 8,10 @@ import (

func handleMcast(w coap.ResponseWriter, r *coap.Request) {
	log.Printf("Got message in handleA: path=%q: %#v from %v", r.Msg.Path(), r.Msg, r.Client.RemoteAddr())
	res := r.Client.NewMessage(coap.MessageParams{
		Type:      coap.Acknowledgement,
		Code:      coap.Content,
		MessageID: r.Msg.MessageID(),
		Token:     r.Msg.Token(),
		Payload:   []byte("hello to you!"),
	})
	res.SetOption(coap.ContentFormat, coap.TextPlain)

	if err := w.Write(res); err != nil {
	resp := w.NewResponse(coap.Content)
	resp.SetOption(coap.ContentFormat, coap.TextPlain)
	resp.SetPayload([]byte("hello to you!"))
	if err := w.Write(resp); err != nil {
		log.Printf("Cannot write resp %v", err)
	}
}

M examples/observe/server/main.go => examples/observe/server/main.go +2 -8
@@ 9,15 9,9 @@ import (
)

func sendResponse(w coap.ResponseWriter, req *coap.Request, subded time.Time) error {
	resp := req.Client.NewMessage(coap.MessageParams{
		Type:      coap.Acknowledgement,
		Code:      coap.Content,
		MessageID: req.Msg.MessageID(),
		Payload:   []byte(fmt.Sprintf("Been running for %v", time.Since(subded))),
		Token:     req.Msg.Token(),
	})

	resp := w.NewResponse(coap.Content)
	resp.SetOption(coap.ContentFormat, coap.TextPlain)
	resp.SetPayload([]byte(fmt.Sprintf("Been running for %v", time.Since(subded))))
	return w.Write(resp)
}


M examples/simple/server/main.go => examples/simple/server/main.go +11 -25
@@ 8,36 8,22 @@ import (

func handleA(w coap.ResponseWriter, req *coap.Request) {
	log.Printf("Got message in handleA: path=%q: %#v from %v", req.Msg.Path(), req.Msg, req.Client.RemoteAddr())
	if req.Msg.IsConfirmable() {
		res := req.Client.NewMessage(coap.MessageParams{
			Type:      coap.Acknowledgement,
			Code:      coap.Content,
			MessageID: req.Msg.MessageID(),
			Token:     req.Msg.Token(),
			Payload:   []byte("hello to you!"),
		})
		res.SetOption(coap.ContentFormat, coap.TextPlain)

		log.Printf("Transmitting from A %#v", res)
		w.Write(res)
	resp := w.NewResponse(coap.Content)
	resp.SetOption(coap.ContentFormat, coap.TextPlain)
	resp.SetPayload([]byte("hello world"))
	log.Printf("Transmitting from A %#v", resp)
	if err := w.Write(resp); err != nil {
		log.Printf("Cannot send response: %v", err)
	}
}

func handleB(w coap.ResponseWriter, req *coap.Request) {
	log.Printf("Got message in handleB: path=%q: %#v from %v", req.Msg.Path(), req.Msg, req.Client.RemoteAddr())
	if req.Msg.IsConfirmable() {
		res := req.Client.NewMessage(coap.MessageParams{
			Type:      coap.Acknowledgement,
			Code:      coap.Content,
			MessageID: req.Msg.MessageID(),
			Token:     req.Msg.Token(),
			Payload:   []byte("good bye!"),
		})
		res.SetOption(coap.ContentFormat, coap.TextPlain)

		log.Printf("Transmitting from B %#v", res)
		w.Write(res)
	}
	resp := w.NewResponse(coap.Content)
	resp.SetOption(coap.ContentFormat, coap.TextPlain)
	resp.SetPayload([]byte("good bye!"))
	log.Printf("Transmitting from B %#v", resp)
	w.Write(resp)
}

func main() {

M getresponsewriter.go => getresponsewriter.go +6 -0
@@ 4,6 4,12 @@ type getResponseWriter struct {
	w ResponseWriter
}

// NewResponse creates reponse for request
func (r *getResponseWriter) NewResponse(code COAPCode) Message {
	return r.w.NewResponse(code)
}

// Write send response to peer
func (r *getResponseWriter) Write(msg Message) error {
	if msg.Payload() != nil && msg.Option(ETag) == nil {
		msg.SetOption(ETag, CalcETag(msg.Payload()))

M multicastClient.go => multicastClient.go +8 -21
@@ 94,6 94,11 @@ func (mconn *MulticastClientConn) NewMessage(p MessageParams) Message {
	return mconn.conn.NewMessage(p)
}

// NewGetRequest creates get request
func (mconn *MulticastClientConn) NewGetRequest(path string) (Message, error) {
	return mconn.conn.NewGetRequest(path)
}

// WriteMsg sends a message through the connection co.
func (mconn *MulticastClientConn) Write(m Message) error {
	return mconn.conn.Write(m)


@@ 109,11 114,12 @@ func (mconn *MulticastClientConn) SetWriteDeadline(timeout time.Duration) {
	mconn.conn.SetWriteDeadline(timeout)
}

// Close close connection
func (mconn *MulticastClientConn) Close() {
	mconn.conn.Close()
}

//Observation represents subscription to resource on the server
//ResponseWaiter represents subscription to resource on the server
type ResponseWaiter struct {
	token []byte
	path  string


@@ 125,29 131,10 @@ func (r *ResponseWaiter) Cancel() error {
	return r.conn.client.multicastHandler.Remove(r.token)
}

type messageNewer interface {
	NewMessage(MessageParams) Message
}

func createGetReq(m messageNewer, path string) (Message, error) {
	token, err := GenerateToken()
	if err != nil {
		return nil, err
	}
	req := m.NewMessage(MessageParams{
		Type:      NonConfirmable,
		Code:      GET,
		MessageID: GenerateMessageID(),
		Token:     token,
	})
	req.SetPathString(path)
	return req, nil
}

// Publish subscribe to sever on path. After subscription and every change on path,
// server sends immediately response
func (mconn *MulticastClientConn) Publish(path string, responseHandler func(req *Request)) (*ResponseWaiter, error) {
	req, err := createGetReq(mconn, path)
	req, err := mconn.conn.NewGetRequest(path)
	if err != nil {
		return nil, err
	}

R sessionnet.go => networksession.go +12 -12
@@ 118,11 118,10 @@ func newSessionTCP(connection Conn, srv *Server) (networkSession, error) {
			blockWiseTransferSzx: BlockWiseTransferSzx,
		},
	}
	/*
		if err := s.sendCSM(); err != nil {
			return nil, err
		}
	*/

	if err := s.sendCSM(); err != nil {
		return nil, err
	}

	return s, nil
}


@@ 220,7 219,7 @@ func (s *sessionTCP) blockWiseMaxPayloadSize(peer BlockSzx) int {
	if s.blockWiseTransferSzx == BlockSzxBERT && peer == BlockSzxBERT {
		m := atomic.LoadUint32(&s.peerMaxMessageSize)
		if m == 0 {
			m = maxMessageSize
			m = uint32(s.srv.MaxMessageSize)
		}
		return int(m - (m % 1024))
	}


@@ 500,7 499,7 @@ func (s *sessionTCP) sendCSM() error {
	})
	req.AddOption(MaxMessageSize, uint32(s.srv.MaxMessageSize))
	if s.blockWiseEnabled() {
		req.AddOption(BlockWiseTransfer, nil)
		req.AddOption(BlockWiseTransfer, []byte{})
	}
	return s.Write(req)
}


@@ 549,13 548,14 @@ func (s *sessionTCP) handleSignals(w ResponseWriter, r *Request) bool {
			case BlockSzxBERT:
				if SZXVal[BlockSzx1024] < int(maxmsgsize) {
					s.sessionBase.blockWiseTransferSzx = BlockSzxBERT
				}
				for i := BlockSzx512; i > BlockSzx16; i-- {
					if SZXVal[i] < int(maxmsgsize) {
						s.sessionBase.blockWiseTransferSzx = i
				} else {
					for i := BlockSzx512; i > BlockSzx16; i-- {
						if SZXVal[i] < int(maxmsgsize) {
							s.sessionBase.blockWiseTransferSzx = i
						}
					}
					s.sessionBase.blockWiseTransferSzx = BlockSzx16
				}
				s.sessionBase.blockWiseTransferSzx = BlockSzx16
			default:
				for i := s.blockWiseSzx(); i > BlockSzx16; i-- {
					if SZXVal[i] < int(maxmsgsize) {

M responsewriter.go => responsewriter.go +17 -0
@@ 3,12 3,29 @@ package coap
//A ResponseWriter interface is used by an CAOP handler to construct an COAP response.
type ResponseWriter interface {
	Write(Message) error
	NewResponse(code COAPCode) Message
}

type responseWriter struct {
	req *Request
}

// NewResponse creates reponse for request
func (r *responseWriter) NewResponse(code COAPCode) Message {
	typ := NonConfirmable
	if r.req.Msg.Type() == Confirmable {
		typ = Acknowledgement
	}
	resp := r.req.Client.NewMessage(MessageParams{
		Type:      typ,
		Code:      code,
		MessageID: r.req.Msg.MessageID(),
		Token:     r.req.Msg.Token(),
	})
	return resp
}

// Write send response to peer
func (r *responseWriter) Write(msg Message) error {
	switch msg.Code() {
	case GET, POST, PUT, DELETE:

M server_test.go => server_test.go +101 -60
@@ 5,6 5,7 @@ import (
	"crypto/tls"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
	"strings"


@@ 121,6 122,7 @@ func RunLocalServerTCPWithHandler(laddr string, BlockWiseTransfer bool, BlockWis
		}, Handler: handler,
		BlockWiseTransfer:    &BlockWiseTransfer,
		BlockWiseTransferSzx: &BlockWiseTransferSzx,
		MaxMessageSize:       ^uint16(0),
	}

	waitLock := sync.Mutex{}


@@ 229,16 231,10 @@ func testServingTCPWithMsgWithObserver(t *testing.T, net string, BlockWiseTransf
}

func simpleMsg(t *testing.T, payload []byte, co *ClientConn) {
	req := co.NewMessage(MessageParams{
		Type:      Confirmable,
		Code:      POST,
		MessageID: 1234,
		Payload:   payload,
		Token:     []byte("abcd"),
	},
	)
	req.SetOption(ContentFormat, TextPlain)
	req.SetPathString("/test")
	req, err := co.NewPostRequest("/test", TextPlain, bytes.NewBuffer(payload))
	if err != nil {
		t.Fatal("cannot create request", err)
	}

	res := CreateRespMessageByReq(co.commander.networkSession.IsTCP(), Valid, req)



@@ 297,14 293,7 @@ func TestServingTLSBigMsg(t *testing.T) {
}

func ChallegingServer(w ResponseWriter, r *Request) {
	req := r.Client.NewMessage(MessageParams{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Payload:   []byte("hello, world!"),
		Token:     []byte("abcd"),
	})
	_, err := r.Client.Exchange(req)
	_, err := r.Client.Post("/test", TextPlain, bytes.NewBuffer([]byte("hello, world!")))
	if err != nil {
		panic(err.Error())
	}


@@ 414,13 403,8 @@ func testServingMCast(t *testing.T, lnet, laddr string, BlockWiseTransfer bool, 
		BlockWiseTransfer:    &BlockWiseTransfer,
		BlockWiseTransferSzx: &BlockWiseTransferSzx,
		Handler: func(w ResponseWriter, r *Request) {
			resp := r.Client.NewMessage(MessageParams{
				Type:      Acknowledgement,
				Code:      Content,
				MessageID: r.Msg.MessageID(),
				Payload:   make([]byte, payloadLen),
				Token:     r.Msg.Token(),
			})
			resp := w.NewResponse(Content)
			resp.SetPayload(make([]byte, payloadLen))
			err := w.Write(resp)
			if err != nil {
				t.Fatalf("cannot send response %v", err)


@@ 429,13 413,8 @@ func testServingMCast(t *testing.T, lnet, laddr string, BlockWiseTransfer bool, 
	}

	s, _, fin, err := RunLocalServerUDPWithHandler(lnet, addrMcast, BlockWiseTransfer, BlockWiseTransferSzx, func(w ResponseWriter, r *Request) {
		resp := r.Client.NewMessage(MessageParams{
			Type:      Acknowledgement,
			Code:      Content,
			MessageID: r.Msg.MessageID(),
			Payload:   make([]byte, payloadLen),
			Token:     r.Msg.Token(),
		})
		resp := w.NewResponse(Content)
		resp.SetPayload(make([]byte, payloadLen))
		conn, err := responseServer.Dial(r.Client.RemoteAddr().String())
		if err != nil {
			t.Fatalf("cannot create connection %v", err)


@@ 492,6 471,25 @@ func TestServingIPv6MCast(t *testing.T) {
	testServingMCast(t, "udp6-mcast", "[ff03::158]:11111", false, BlockSzx16, 16)
}

type dataReader struct {
	data   []byte
	offset int
}

func (r *dataReader) Read(p []byte) (n int, err error) {
	l := len(p)
	if (len(r.data) - r.offset) < l {
		l = len(r.data) - r.offset
	}
	if l != 0 {
		copy(p, r.data[r.offset:r.offset+l])
		r.offset += l
	} else {
		return 0, io.EOF
	}
	return l, nil
}

func BenchmarkServe(b *testing.B) {
	b.StopTimer()
	HandleFunc("/test", EchoServer)


@@ 512,23 510,40 @@ func BenchmarkServe(b *testing.B) {
	}
	defer co.Close()

	req := &DgramMessage{
		MessageBase{
			typ:       Confirmable,
			code:      POST,
			messageID: 1234,
			payload:   []byte("Content sent by client"),
		}}
	req.SetOption(ContentFormat, TextPlain)
	req.SetPathString("/test")
	data := []byte("Content sent by client")
	b.StartTimer()
	for i := uint32(0); i < uint32(b.N); i++ {
		_, err := co.Post("/test", TextPlain, &dataReader{data: data})
		if err != nil {
			b.Fatalf("unable to read msg from server: %v", err)
		}
	}
}

func BenchmarkServeBlockWise(b *testing.B) {
	b.StopTimer()
	HandleFunc("/test", EchoServer)
	defer HandleRemove("/test")

	s, addrstr, fin, err := RunLocalUDPServer("udp", ":0", true, BlockSzx1024)
	if err != nil {
		b.Fatalf("unable to run test server: %v", err)
	}
	defer func() {
		s.Shutdown()
		<-fin
	}()

	co, err := Dial("udp", addrstr)
	if err != nil {
		b.Fatalf("unable to dialing: %v", err)
	}
	defer co.Close()

	data := make([]byte, 65000)
	b.StartTimer()
	for i := uint32(0); i < uint32(b.N); i++ {
		abc := *req
		token := make([]byte, 8)
		binary.LittleEndian.PutUint32(token, i)
		abc.SetToken(token)
		_, err = co.Exchange(&abc)
		_, err := co.Post("/test", TextPlain, &dataReader{data: data})
		if err != nil {
			b.Fatalf("unable to read msg from server: %v", err)
		}


@@ 555,23 570,49 @@ func BenchmarkServeTCP(b *testing.B) {
	}
	defer co.Close()

	req := &TcpMessage{
		MessageBase{
			typ:       Confirmable,
			code:      POST,
			messageID: 1234,
			payload:   []byte("Content sent by client"),
		}}
	req.SetOption(ContentFormat, TextPlain)
	req.SetPathString("/test")
	data := make([]byte, 128)
	b.StartTimer()
	for i := uint32(0); i < uint32(b.N); i++ {
		_, err := co.Post("/test", TextPlain, &dataReader{data: data})
		if err != nil {
			b.Fatalf("unable to read msg from server: %v", err)
		}
	}
}

func BenchmarkServeTCPBlockwise(b *testing.B) {
	b.StopTimer()
	HandleFunc("/test", EchoServer)
	defer HandleRemove("/test")

	BlockWiseTransfer := true
	BlockWiseTransferSzx := BlockSzx1024

	s, addrstr, fin, err := RunLocalTCPServer(":0", BlockWiseTransfer, BlockWiseTransferSzx)
	if err != nil {
		b.Fatalf("unable to run test server: %v", err)
	}
	defer func() {
		s.Shutdown()
		<-fin
	}()

	client := Client{
		Net:                  "tcp",
		BlockWiseTransfer:    &BlockWiseTransfer,
		BlockWiseTransferSzx: &BlockWiseTransferSzx,
		MaxMessageSize:       65000,
	}
	co, err := client.Dial(addrstr)
	if err != nil {
		b.Fatalf("unable dialing: %v", err)
	}
	defer co.Close()

	data := make([]byte, 128)
	b.StartTimer()
	for i := uint32(0); i < uint32(b.N); i++ {
		abc := *req
		token := make([]byte, 8)
		binary.LittleEndian.PutUint32(token, i)
		abc.SetToken(token)
		_, err = co.Exchange(&abc)
		_, err := co.Post("/test", TextPlain, &dataReader{data: data})
		if err != nil {
			b.Fatalf("unable to read msg from server: %v", err)
		}