[
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\nGodeps/_workspace\nReadme\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"openssl\"]\n\tpath = openssl\n\turl = https://github.com/openssl/openssl.git\n"
  },
  {
    "path": "Godeps/Godeps.json",
    "content": "{\n\t\"ImportPath\": \"github.com/zubairhamed/canopus\",\n\t\"GoVersion\": \"go1.7\",\n\t\"Deps\": [\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/davecgh/go-spew/spew\",\n\t\t\t\"Rev\": \"5215b55f46b2b919f50a1df0eaa5886afe4e3b3d\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/pmezard/go-difflib/difflib\",\n\t\t\t\"Rev\": \"d8ed2627bdf02c080bf22230dbb337003b7aba2d\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/stretchr/testify/assert\",\n\t\t\t\"Comment\": \"v1.0-80-g67106a5\",\n\t\t\t\"Rev\": \"67106a5111a06241c8d84952c33214675f51a34a\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2015, Zubair Hamed\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Canopus\n\n[![GoDoc](https://godoc.org/github.com/zubairhamed/canopus?status.svg)](https://godoc.org/github.com/zubairhamed/canopus)\n[![Build Status](https://drone.io/github.com/zubairhamed/canopus/status.png?)](https://drone.io/github.com/zubairhamed/canopus/latest)\n[![Coverage Status](https://coveralls.io/repos/zubairhamed/canopus/badge.svg?branch=master)](https://coveralls.io/r/zubairhamed/canopus?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/zubairhamed/canopus)](https://goreportcard.com/report/github.com/zubairhamed/canopus)\n\n#### Canopus is a client/server implementation of the [Constrained Application Protocol (CoAP)][RFC7252]\n[RFC7252]: http://tools.ietf.org/html/rfc7252\n\n## Updates\n#### 25.11.2016\nI've added basic dTLS Support based on [Julien Vermillard's][JVERMILLARD] [implementation][NATIVEDTLS]. Thanks Julien! It should now support PSK-based authentication.\nI've also gone ahead and refactored the APIs to make it that bit more Go idiomatic.\n[JVERMILLARD]: https://github.com/jvermillard\n[NATIVEDTLS]: https://github.com/jvermillard/nativedtls\n\n## Building and running\n1. git submodule update --init --recursive\n2. cd openssl\n3. ./config && make\n4. You should then be able to run the examples in the /examples folder\n\n#### Simple Example\n```go\n\t// Server\n\t// See /examples/simple/server/main.go\n\tserver := canopus.NewServer()\n\n\tserver.Get(\"/hello\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\n\t\tres := canopus.NewResponse(msg, nil)\n\t\treturn res\n\t})\n\n\tserver.ListenAndServe(\":5683\")\n\n\t// Client\n\t// See /examples/simple/client/main.go\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get, canopus.GenerateMessageID()).(*canopus.CoapRequest)\n\treq.SetStringPayload(\"Hello, canopus\")\n\treq.SetRequestURI(\"/hello\")\n\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tfmt.Println(\"Got Response:\" + resp.GetMessage().GetPayload().String())\n```\n\n#### Observe / Notify\n```go\n\t// Server\n\t// See /examples/observe/server/main.go\n\tserver := canopus.NewServer()\n\tserver.Get(\"/watch/this\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.NewMessageOfType(canopus.MessageAcknowledgment, req.GetMessage().GetMessageId(), canopus.NewPlainTextPayload(\"Acknowledged\"))\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\n\tticker := time.NewTicker(3 * time.Second)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\t\tchangeVal := strconv.Itoa(rand.Int())\n\t\t\t\tfmt.Println(\"[SERVER << ] Change of value -->\", changeVal)\n\n\t\t\t\tserver.NotifyChange(\"/watch/this\", changeVal, false)\n\t\t\t}\n\t\t}\n\t}()\n\n\tserver.OnObserve(func(resource string, msg canopus.Message) {\n\t\tfmt.Println(\"[SERVER << ] Observe Requested for \" + resource)\n\t})\n\n\tserver.ListenAndServe(\":5683\")\n\n\t// Client\n\t// See /examples/observe/client/main.go\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\n\ttok, err := conn.ObserveResource(\"/watch/this\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tobsChannel := make(chan canopus.ObserveMessage)\n\tdone := make(chan bool)\n\tgo conn.Observe(obsChannel)\n\n\tnotifyCount := 0\n\tfor {\n\t\tselect {\n\t\tcase obsMsg, _ := <-obsChannel:\n\t\t\tif notifyCount == 5 {\n\t\t\t\tfmt.Println(\"[CLIENT >> ] Canceling observe after 5 notifications..\")\n\t\t\t\tgo conn.CancelObserveResource(\"watch/this\", tok)\n\t\t\t\tgo conn.StopObserve(obsChannel)\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tnotifyCount++\n\t\t\t\t// msg := obsMsg.Msg\\\n\t\t\t\tresource := obsMsg.GetResource()\n\t\t\t\tval := obsMsg.GetValue()\n\n\t\t\t\tfmt.Println(\"[CLIENT >> ] Got Change Notification for resource and value: \", notifyCount, resource, val)\n\t\t\t}\n\t\t}\n\t}\n```\n\n### dTLS with PSK\n```go\n\t// Server\n\t// See /examples/dtls/simple-psk/server/main.go\n\tserver := canopus.NewServer()\n\n\tserver.Get(\"/hello\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\n\tserver.HandlePSK(func(id string) []byte {\n\t\treturn []byte(\"secretPSK\")\n\t})\n\n\tserver.ListenAndServeDTLS(\":5684\")\n\n\t// Client\n\t// See /examples/dtls/simple-psk/client/main.go\n\tconn, err := canopus.DialDTLS(\"localhost:5684\", \"canopus\", \"secretPSK\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get, canopus.GenerateMessageID())\n\treq.SetStringPayload(\"Hello, canopus\")\n\treq.SetRequestURI(\"/hello\")\n\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tfmt.Println(\"Got Response:\" + resp.GetMessage().GetPayload().String())\n```\n\n#### CoAP-CoAP Proxy\n```go\n\t// Server\n\t// See /examples/proxy/coap/server/main.go\n\tserver := canopus.NewServer()\n\n\tserver.Get(\"/proxycall\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Data from :5685 -- \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\tserver.ListenAndServe(\":5685\")\n\n\t// Proxy Server\n\t// See /examples/proxy/coap/proxy/main.go\n\tserver := canopus.NewServer()\n\tserver.ProxyOverCoap(true)\n\n\tserver.Get(\"/proxycall\", func(req canopus.Request) canopus.Response {\n\t\tcanopus.PrintMessage(req.GetMessage())\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\tserver.ListenAndServe(\":5683\")\n\n\t// Client\n\t// See /examples/proxy/coap/client/main.go\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get, canopus.GenerateMessageID())\n\treq.SetProxyURI(\"coap://localhost:5685/proxycall\")\n\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tprintln(\"err\", err)\n\t}\n\tcanopus.PrintMessage(resp.GetMessage())\n```\n\n#### CoAP-HTTP Proxy\n```go\n\t// Server\n\t// See /examples/proxy/http/server/main.go\n\tserver := canopus.NewServer()\n\tserver.ProxyOverHttp(true)\n\n\tserver.ListenAndServe(\":5683\")\n\n\t// Client\n\t// See /examples/proxy/http/client/main.go\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get, canopus.GenerateMessageID())\n\treq.SetProxyURI(\"https://httpbin.org/get\")\n\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tprintln(\"err\", err)\n\t}\n\tcanopus.PrintMessage(resp.GetMessage())\n```\n"
  },
  {
    "path": "bytecontent.go",
    "content": "package canopus\n\n// Represents a message payload containing an array of bytes\nfunc NewBytesPayload(v []byte) MessagePayload {\n\treturn &BytesPayload{\n\t\tcontent: v,\n\t}\n}\n\ntype BytesPayload struct {\n\tcontent []byte\n}\n\nfunc (p *BytesPayload) GetBytes() []byte {\n\treturn p.content\n}\n\nfunc (p *BytesPayload) Length() int {\n\treturn len(p.content)\n}\n\nfunc (p *BytesPayload) String() string {\n\treturn string(p.content)\n}\n"
  },
  {
    "path": "canopus.go",
    "content": "package canopus\n\nimport (\n\t\"errors\"\n\t\"math/rand\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\n// CurrentMessageID stores the current message id used/generated for messages\nvar CurrentMessageID = 0\nvar MESSAGEID_MUTEX *sync.Mutex\n\nfunc init() {\n\trand.Seed(time.Now().UTC().UnixNano())\n\n\tCurrentMessageID = rand.Intn(65535)\n\n\tMESSAGEID_MUTEX = &sync.Mutex{}\n}\n\nvar GENERATE_ID uint16 = 0\n\nconst UDP = \"udp\"\n\n// Types of Messages\nconst (\n\tMessageConfirmable    = 0\n\tMessageNonConfirmable = 1\n\tMessageAcknowledgment = 2\n\tMessageReset          = 3\n)\n\n// Fragments/parts of a CoAP Message packet\nconst (\n\tDataHeader     = 0\n\tDataCode       = 1\n\tDataMsgIDStart = 2\n\tDataMsgIDEnd   = 4\n\tDataTokenStart = 4\n)\n\n// OptionCode type represents a valid CoAP Option Code\ntype OptionCode int\n\nconst (\n\t// OptionIfMatch request-header field is used with a method to make it conditional.\n\t// A client that has one or more entities previously obtained from the resource can verify\n\t// that one of those entities is current by including a list of their associated entity tags\n\t// in the If-Match header field.\n\tOptionIfMatch OptionCode = 1\n\n\tOptionURIHost       OptionCode = 3\n\tOptionEtag          OptionCode = 4\n\tOptionIfNoneMatch   OptionCode = 5\n\tOptionObserve       OptionCode = 6\n\tOptionURIPort       OptionCode = 7\n\tOptionLocationPath  OptionCode = 8\n\tOptionURIPath       OptionCode = 11\n\tOptionContentFormat OptionCode = 12\n\tOptionMaxAge        OptionCode = 14\n\tOptionURIQuery      OptionCode = 15\n\tOptionAccept        OptionCode = 17\n\tOptionLocationQuery OptionCode = 20\n\tOptionBlock2        OptionCode = 23\n\tOptionBlock1        OptionCode = 27\n\tOptionSize2         OptionCode = 28\n\tOptionProxyURI      OptionCode = 35\n\tOptionProxyScheme   OptionCode = 39\n\tOptionSize1         OptionCode = 60\n)\n\n// CoapCode defines a valid CoAP Code Type\ntype CoapCode uint8\n\nconst (\n\tGet    CoapCode = 1\n\tPost   CoapCode = 2\n\tPut    CoapCode = 3\n\tDelete CoapCode = 4\n\n\t// 2.x\n\tCoapCodeEmpty    CoapCode = 0\n\tCoapCodeCreated  CoapCode = 65 // 2.01\n\tCoapCodeDeleted  CoapCode = 66 // 2.02\n\tCoapCodeValid    CoapCode = 67 // 2.03\n\tCoapCodeChanged  CoapCode = 68 // 2.04\n\tCoapCodeContent  CoapCode = 69 // 2.05\n\tCoapCodeContinue CoapCode = 95 // 2.31\n\n\t// 4.x\n\tCoapCodeBadRequest               CoapCode = 128 // 4.00\n\tCoapCodeUnauthorized             CoapCode = 129 // 4.01\n\tCoapCodeBadOption                CoapCode = 130 // 4.02\n\tCoapCodeForbidden                CoapCode = 131 // 4.03\n\tCoapCodeNotFound                 CoapCode = 132 // 4.04\n\tCoapCodeMethodNotAllowed         CoapCode = 133 // 4.05\n\tCoapCodeNotAcceptable            CoapCode = 134 // 4.06\n\tCoapCodeRequestEntityIncomplete  CoapCode = 136 // 4.08\n\tCoapCodeConflict                 CoapCode = 137 // 4.09\n\tCoapCodePreconditionFailed       CoapCode = 140 // 4.12\n\tCoapCodeRequestEntityTooLarge    CoapCode = 141 // 4.13\n\tCoapCodeUnsupportedContentFormat CoapCode = 143 // 4.15\n\n\t// 5.x\n\tCoapCodeInternalServerError  CoapCode = 160 // 5.00\n\tCoapCodeNotImplemented       CoapCode = 161 // 5.01\n\tCoapCodeBadGateway           CoapCode = 162 // 5.02\n\tCoapCodeServiceUnavailable   CoapCode = 163 // 5.03\n\tCoapCodeGatewayTimeout       CoapCode = 164 // 5.04\n\tCoapCodeProxyingNotSupported CoapCode = 165 // 5.05\n)\n\nconst DefaultAckTimeout = 2\nconst DefaultAckRandomFactor = 1.5\nconst DefaultMaxRetransmit = 4\nconst DefaultNStart = 1\nconst DefaultLeisure = 5\nconst DefaultProbingRate = 1\n\nconst CoapDefaultHost = \"\"\nconst CoapDefaultPort = 5683\nconst CoapsDefaultPort = 5684\n\nconst PayloadMarker = 0xff\nconst MaxPacketSize = 1500\n\n// MessageIDPurgeDuration defines the number of seconds before a MessageID Purge is initiated\nconst MessageIDPurgeDuration = 60\n\ntype RouteHandler func(Request) Response\n\n// Proxy Filter\ntype ProxyFilter func(Message, net.Addr) bool\ntype ProxyHandler func(c CoapServer, msg Message, session Session)\n\ntype MediaType int\n\nconst (\n\tMediaTypeTextPlain                  MediaType = 0\n\tMediaTypeTextXML                    MediaType = 1\n\tMediaTypeTextCsv                    MediaType = 2\n\tMediaTypeTextHTML                   MediaType = 3\n\tMediaTypeImageGif                   MediaType = 21\n\tMediaTypeImageJpeg                  MediaType = 22\n\tMediaTypeImagePng                   MediaType = 23\n\tMediaTypeImageTiff                  MediaType = 24\n\tMediaTypeAudioRaw                   MediaType = 25\n\tMediaTypeVideoRaw                   MediaType = 26\n\tMediaTypeApplicationLinkFormat      MediaType = 40\n\tMediaTypeApplicationXML             MediaType = 41\n\tMediaTypeApplicationOctetStream     MediaType = 42\n\tMediaTypeApplicationRdfXML          MediaType = 43\n\tMediaTypeApplicationSoapXML         MediaType = 44\n\tMediaTypeApplicationAtomXML         MediaType = 45\n\tMediaTypeApplicationXmppXML         MediaType = 46\n\tMediaTypeApplicationExi             MediaType = 47\n\tMediaTypeApplicationFastInfoSet     MediaType = 48\n\tMediaTypeApplicationSoapFastInfoSet MediaType = 49\n\tMediaTypeApplicationJSON            MediaType = 50\n\tMediaTypeApplicationXObitBinary     MediaType = 51\n\tMediaTypeTextPlainVndOmaLwm2m       MediaType = 1541\n\tMediaTypeTlvVndOmaLwm2m             MediaType = 1542\n\tMediaTypeJSONVndOmaLwm2m            MediaType = 1543\n\tMediaTypeOpaqueVndOmaLwm2m          MediaType = 1544\n)\n\nconst (\n\tMethodGet     = \"GET\"\n\tMethodPut     = \"PUT\"\n\tMethodPost    = \"POST\"\n\tMethodDelete  = \"DELETE\"\n\tMethodOptions = \"OPTIONS\"\n\tMethodPatch   = \"PATCH\"\n)\n\ntype BlockSizeType byte\n\nconst (\n\tBlockSize16   BlockSizeType = 0\n\tBlockSize32   BlockSizeType = 1\n\tBlockSize64   BlockSizeType = 2\n\tBlockSize128  BlockSizeType = 3\n\tBlockSize256  BlockSizeType = 4\n\tBlockSize512  BlockSizeType = 5\n\tBlockSize1024 BlockSizeType = 6\n)\n\n// Errors\nvar ErrPacketLengthLessThan4 = errors.New(\"Packet length less than 4 bytes\")\nvar ErrInvalidCoapVersion = errors.New(\"Invalid CoAP version. Should be 1.\")\nvar ErrOptionLengthUsesValue15 = errors.New((\"Message format error. Option length has reserved value of 15\"))\nvar ErrOptionDeltaUsesValue15 = errors.New((\"Message format error. Option delta has reserved value of 15\"))\nvar ErrUnknownMessageType = errors.New(\"Unknown message type\")\nvar ErrInvalidTokenLength = errors.New(\"Invalid Token Length ( > 8)\")\nvar ErrUnknownCriticalOption = errors.New(\"Unknown critical option encountered\")\nvar ErrUnsupportedMethod = errors.New(\"Unsupported Method\")\nvar ErrNoMatchingRoute = errors.New(\"No matching route found\")\nvar ErrUnsupportedContentFormat = errors.New(\"Unsupported Content-Format\")\nvar ErrNoMatchingMethod = errors.New(\"No matching method\")\nvar ErrNilMessage = errors.New(\"Message is nil\")\nvar ErrNilConn = errors.New(\"Connection object is nil\")\nvar ErrNilAddr = errors.New(\"Address cannot be nil\")\nvar ErrMessageSizeTooLongBlockOptionValNotSet = errors.New(\"Message is too long, block option or value not set\")\n\n// Security Options\nconst (\n\tSecNoSec        = \"NoSec\"\n\tSecPreSharedKey = \"PreSharedKey\"\n\tSecRawPublicKey = \"RawPublicKey\"\n\tSecCertificate  = \"Certificate\"\n)\n\n// Interfaces\ntype CoapServer interface {\n\tListenAndServe(addr string)\n\tListenAndServeDTLS(addr string)\n\tStop()\n\n\tGet(path string, fn RouteHandler) Route\n\tDelete(path string, fn RouteHandler) Route\n\tPut(path string, fn RouteHandler) Route\n\tPost(path string, fn RouteHandler) Route\n\tOptions(path string, fn RouteHandler) Route\n\tPatch(path string, fn RouteHandler) Route\n\n\tNewRoute(path string, method CoapCode, fn RouteHandler) Route\n\tNotifyChange(resource, value string, confirm bool)\n\n\tOnNotify(fn FnEventNotify)\n\tOnStart(fn FnEventStart)\n\tOnClose(fn FnEventClose)\n\tOnDiscover(fn FnEventDiscover)\n\tOnError(fn FnEventError)\n\tOnObserve(fn FnEventObserve)\n\tOnObserveCancel(fn FnEventObserveCancel)\n\tOnMessage(fn FnEventMessage)\n\tOnBlockMessage(fn FnEventBlockMessage)\n\n\tProxyOverHttp(enabled bool)\n\tProxyOverCoap(enabled bool)\n\n\tGetEvents() Events\n\n\tAllowProxyForwarding(Message, net.Addr) bool\n\tGetRoutes() []Route\n\tForwardCoap(msg Message, session Session)\n\tForwardHTTP(msg Message, session Session)\n\n\tAddObservation(resource, token string, session Session)\n\tHasObservation(resource string, addr net.Addr) bool\n\tRemoveObservation(resource string, addr net.Addr)\n\n\tHandlePSK(func(id string) []byte)\n\n\tGetSession(addr string) Session\n\tDeleteSession(ssn Session)\n\n\tGetCookieSecret() []byte\n}\n\ntype ServerConnection interface {\n\tReadFrom(b []byte) (n int, addr net.Addr, err error)\n\tWriteTo(b []byte, addr net.Addr) (n int, err error)\n\tClose() error\n\tLocalAddr() net.Addr\n\tSetDeadline(t time.Time) error\n\tSetReadDeadline(t time.Time) error\n\tSetWriteDeadline(t time.Time) error\n}\n\ntype Option interface {\n\tName() string\n\tIsElective() bool\n\tIsCritical() bool\n\tStringValue() string\n\tIntValue() int\n\tGetCode() OptionCode\n\tGetValue() interface{}\n}\n\ntype Session interface {\n\tGetConnection() ServerConnection\n\tGetAddress() net.Addr\n\tWrite([]byte) (int, error)\n\tRead([]byte) (n int, err error)\n\tGetServer() CoapServer\n\tWriteBuffer([]byte) int\n}\n\ntype Request interface {\n\tGetAttributes() map[string]string\n\tGetAttribute(o string) string\n\tGetAttributeAsInt(o string) int\n\tGetMessage() Message\n\tGetURIQuery(q string) string\n\n\tSetProxyURI(uri string)\n\tSetMediaType(mt MediaType)\n\tSetPayload([]byte)\n\tSetStringPayload(s string)\n\tSetRequestURI(uri string)\n\tSetConfirmable(con bool)\n\tSetToken(t string)\n\tSetURIQuery(k string, v string)\n}\n\ntype Response interface {\n\tGetMessage() Message\n\tGetError() error\n\tGetPayload() []byte\n\tGetURIQuery(q string) string\n}\n\ntype Connection interface {\n\tObserveResource(resource string) (tok string, err error)\n\tCancelObserveResource(resource string, token string) (err error)\n\tStopObserve(ch chan ObserveMessage)\n\tObserve(ch chan ObserveMessage)\n\tSend(req Request) (resp Response, err error)\n\n\tWrite(b []byte) (n int, err error)\n\tRead(b []byte) (n int, err error)\n\tClose() error\n}\n\n// Represents the payload/content of a CoAP Message\ntype MessagePayload interface {\n\tGetBytes() []byte\n\tLength() int\n\tString() string\n}\n\ntype Message interface {\n\tGetToken() []byte\n\tGetMessageId() uint16\n\tGetMessageType() uint8\n\tGetAcceptedContent() MediaType\n\tGetCodeString() string\n\tGetCode() CoapCode\n\tGetMethod() uint8\n\tGetTokenLength() uint8\n\tGetTokenString() string\n\tGetOptions(id OptionCode) []Option\n\tGetOption(id OptionCode) Option\n\tGetAllOptions() []Option\n\tGetOptionsAsString(id OptionCode) []string\n\tGetLocationPath() string\n\tGetURIPath() string\n\tGetPayload() MessagePayload\n\n\tSetToken([]byte)\n\tSetMessageId(uint16)\n\tSetMessageType(uint8)\n\tSetBlock1Option(opt Option)\n\tSetStringPayload(s string)\n\tSetPayload(MessagePayload)\n\n\tAddOption(code OptionCode, value interface{})\n\tAddOptions(opts []Option)\n\tCloneOptions(cm Message, opts ...OptionCode)\n\tReplaceOptions(code OptionCode, opts []Option)\n\tRemoveOptions(id OptionCode)\n}\n\ntype Route interface {\n\tGetMethod() string\n\tGetMediaTypes() []MediaType\n\tGetConfiguredPath() string\n\n\tMatches(path string) (bool, map[string]string)\n\tAutoAcknowledge() bool\n\tHandle(req Request) Response\n}\n\ntype FnEventNotify func(string, interface{}, Message)\ntype FnEventStart func(CoapServer)\ntype FnEventClose func(CoapServer)\ntype FnEventDiscover func()\ntype FnEventError func(error)\ntype FnEventObserve func(string, Message)\ntype FnEventObserveCancel func(string, Message)\ntype FnEventMessage func(Message, bool)\ntype FnEventBlockMessage func(Message, bool)\n\ntype EventCode int\n\nconst (\n\tEventStart         EventCode = 0\n\tEventClose         EventCode = 1\n\tEventDiscover      EventCode = 2\n\tEventMessage       EventCode = 3\n\tEventError         EventCode = 4\n\tEventObserve       EventCode = 5\n\tEventObserveCancel EventCode = 6\n\tEventNotify        EventCode = 7\n)\n\ntype ObserveMessage interface {\n\tGetResource() string\n\tGetValue() interface{}\n\tGetMessage() Message\n}\n\ntype Events interface {\n\tOnNotify(fn FnEventNotify)\n\tOnStart(fn FnEventStart)\n\tOnClose(fn FnEventClose)\n\tOnDiscover(fn FnEventDiscover)\n\tOnError(fn FnEventError)\n\tOnObserve(fn FnEventObserve)\n\tOnObserveCancel(fn FnEventObserveCancel)\n\tOnMessage(fn FnEventMessage)\n\tOnBlockMessage(fn FnEventBlockMessage)\n\n\tNotify(resource string, value interface{}, msg Message)\n\tStarted(server CoapServer)\n\tClosed(server CoapServer)\n\tDiscover()\n\tError(err error)\n\tObserve(resource string, msg Message)\n\tObserveCancelled(resource string, msg Message)\n\tMessage(msg Message, inbound bool)\n\tBlockMessage(msg Message, inbound bool)\n}\n\ntype BlockMessage interface {\n}\n"
  },
  {
    "path": "client.go",
    "content": "package canopus\n\nimport \"net\"\n\nfunc Dial(address string) (conn Connection, err error) {\n\tudpConn, err := net.Dial(\"udp\", address)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &UDPConnection{\n\t\tconn: udpConn,\n\t}\n\n\treturn\n}\n\nfunc DialDTLS(address, identity, psk string) (conn Connection, err error) {\n\tudpConn, err := net.Dial(\"udp\", address)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn, err = NewDTLSConnection(udpConn, identity, psk)\n\tif err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc NewObserveMessage(r string, val interface{}, msg Message) ObserveMessage {\n\treturn &CoapObserveMessage{\n\t\tResource: r,\n\t\tValue:    val,\n\t\tMsg:      msg,\n\t}\n}\n\ntype CoapObserveMessage struct {\n\tCoapMessage\n\tResource string\n\tValue    interface{}\n\tMsg      Message\n}\n\nfunc (m *CoapObserveMessage) GetResource() string {\n\treturn m.Resource\n}\n\nfunc (m *CoapObserveMessage) GetValue() interface{} {\n\treturn m.Value\n}\n\nfunc (m *CoapObserveMessage) GetMessage() Message {\n\treturn m.GetMessage()\n}\n"
  },
  {
    "path": "conn.go",
    "content": "package canopus\n\nimport (\n\t\"log\"\n\t\"net\"\n\t\"sync\"\n)\n\nfunc MessageSizeAllowed(req Request) bool {\n\tmsg := req.GetMessage()\n\tb, _ := MessageToBytes(msg)\n\n\tif len(b) > 65536 {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\ntype UDPConnection struct {\n\tconn net.Conn\n}\n\nfunc (c *UDPConnection) ObserveResource(resource string) (tok string, err error) {\n\treq := NewRequest(MessageConfirmable, Get)\n\treq.SetRequestURI(resource)\n\treq.GetMessage().AddOption(OptionObserve, 0)\n\n\tresp, err := c.Send(req)\n\ttok = string(resp.GetMessage().GetToken())\n\n\treturn\n}\n\nfunc (c *UDPConnection) CancelObserveResource(resource string, token string) (err error) {\n\treq := NewRequest(MessageConfirmable, Get)\n\treq.SetRequestURI(resource)\n\treq.GetMessage().AddOption(OptionObserve, 1)\n\n\t_, err = c.Send(req)\n\treturn\n}\n\nfunc (c *UDPConnection) StopObserve(ch chan ObserveMessage) {\n\tclose(ch)\n}\n\nfunc (c *UDPConnection) Close() error {\n\treturn c.conn.Close()\n}\n\nfunc (c *UDPConnection) Observe(ch chan ObserveMessage) {\n\n\treadBuf := make([]byte, MaxPacketSize)\n\tfor {\n\t\tlen, err := c.Read(readBuf)\n\t\tif err == nil {\n\t\t\tmsgBuf := make([]byte, len)\n\t\t\tcopy(msgBuf, readBuf)\n\n\t\t\tmsg, err := BytesToMessage(msgBuf)\n\t\t\tif msg.GetOption(OptionObserve) != nil {\n\t\t\t\tch <- NewObserveMessage(msg.GetURIPath(), msg.GetPayload(), msg)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"Error occured reading UDP\", err)\n\t\t\t\tclose(ch)\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Println(\"Error occured reading UDP\", err)\n\t\t\tclose(ch)\n\t\t}\n\t}\n}\n\nfunc (c *UDPConnection) Send(req Request) (resp Response, err error) {\n\tmsg := req.GetMessage()\n\topt := msg.GetOption(OptionBlock1)\n\n\tif opt == nil { // Block1 was not set\n\t\tif MessageSizeAllowed(req) != true {\n\t\t\treturn nil, ErrMessageSizeTooLongBlockOptionValNotSet\n\t\t}\n\t} else { // Block1 was set\n\t\t// log.Println(\"Block 1 was set\")\n\t}\n\n\tif opt != nil {\n\t\tblockOpt := Block1OptionFromOption(opt)\n\t\tif blockOpt.Value == nil {\n\t\t\tif MessageSizeAllowed(req) != true {\n\t\t\t\terr = ErrMessageSizeTooLongBlockOptionValNotSet\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\t// - Block # = one and only block (sz = unspecified), whereas 0 = 16bits\n\t\t\t\t// - MOre bit = 0\n\t\t\t}\n\t\t} else { // BLock transfer request\n\t\t\tpayload := msg.GetPayload().GetBytes()\n\t\t\tpayloadLen := uint32(len(payload))\n\t\t\tblockSize := blockOpt.BlockSizeLength()\n\t\t\tcurrSeq := uint32(0)\n\t\t\ttotalBlocks := uint32(payloadLen / blockSize)\n\t\t\tcompleted := false\n\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(1)\n\n\t\t\tfor completed == false {\n\t\t\t\tif currSeq <= totalBlocks {\n\n\t\t\t\t\tvar blockPayloadStart uint32\n\t\t\t\t\tvar blockPayloadEnd uint32\n\t\t\t\t\tvar blockPayload []byte\n\n\t\t\t\t\tblockPayloadStart = currSeq * uint32(blockSize)\n\n\t\t\t\t\tmore := true\n\t\t\t\t\tif currSeq == totalBlocks {\n\t\t\t\t\t\tmore = false\n\t\t\t\t\t\tblockPayloadEnd = payloadLen\n\t\t\t\t\t} else {\n\t\t\t\t\t\tblockPayloadEnd = blockPayloadStart + uint32(blockSize)\n\t\t\t\t\t}\n\n\t\t\t\t\tblockPayload = payload[blockPayloadStart : blockPayloadEnd+1]\n\n\t\t\t\t\tblockOpt = NewBlock1Option(blockOpt.Size(), more, currSeq)\n\t\t\t\t\tmsg.ReplaceOptions(blockOpt.Code, []Option{blockOpt})\n\t\t\t\t\tmodifiedMsg := msg.(*CoapMessage)\n\t\t\t\t\tmodifiedMsg.SetMessageId(GenerateMessageID())\n\t\t\t\t\tmodifiedMsg.SetPayload(NewBytesPayload(blockPayload))\n\n\t\t\t\t\t// send message\n\t\t\t\t\t_, err2 := c.SendMessage(msg)\n\t\t\t\t\tif err2 != nil {\n\t\t\t\t\t\twg.Done()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcurrSeq = currSeq + 1\n\n\t\t\t\t} else {\n\t\t\t\t\tcompleted = true\n\t\t\t\t\twg.Done()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tresp, err = c.SendMessage(msg)\n\treturn\n}\n\nfunc (c *UDPConnection) SendMessage(msg Message) (resp Response, err error) {\n\tif msg == nil {\n\t\treturn nil, ErrNilMessage\n\t}\n\n\tb, err := MessageToBytes(msg)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif msg.GetMessageType() == MessageNonConfirmable {\n\t\tgo c.Write(b)\n\t\tresp = NewResponse(NewEmptyMessage(msg.GetMessageId()), nil)\n\t\treturn\n\t}\n\n\t_, err = c.Write(b)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmsgBuf := make([]byte, 1500)\n\tif msg.GetMessageType() == MessageAcknowledgment {\n\t\tresp = NewResponse(NewEmptyMessage(msg.GetMessageId()), nil)\n\t\treturn\n\t}\n\n\tn, err := c.Read(msgBuf)\n\tif err != nil {\n\t\treturn\n\t}\n\n\trespMsg, err := BytesToMessage(msgBuf[:n])\n\tif err != nil {\n\t\treturn\n\t}\n\tresp = NewResponse(respMsg, nil)\n\n\tif msg.GetMessageType() == MessageConfirmable {\n\t\t// TODO: Send out message and wait for a confirm. If confirmation not retrieved,\n\t\t// resend (taking into account timeouts and back-off transmissions\n\n\t\t// c.Send(NewRequestFromMessage(msg))\n\t}\n\treturn\n}\n\nfunc (c *UDPConnection) Write(b []byte) (int, error) {\n\treturn c.conn.Write(b)\n}\n\nfunc (c *UDPConnection) Read(b []byte) (int, error) {\n\treturn c.conn.Read(b)\n}\n"
  },
  {
    "path": "corelink.go",
    "content": "package canopus\n\n// Represents a message payload containing core-link format values\ntype CoreLinkFormatPayload struct {\n}\n\nfunc (p *CoreLinkFormatPayload) GetBytes() []byte {\n\treturn make([]byte, 0)\n}\n\nfunc (p *CoreLinkFormatPayload) Length() int {\n\treturn 0\n}\n\nfunc (p *CoreLinkFormatPayload) String() string {\n\treturn \"\"\n}\n"
  },
  {
    "path": "coreresource.go",
    "content": "package canopus\n\n// Instantiates a new core-attribute with a given key/value\nfunc NewCoreAttribute(key string, value interface{}) *CoreAttribute {\n\treturn &CoreAttribute{\n\t\tKey:   key,\n\t\tValue: value,\n\t}\n}\n\n// Instantiates a new Core Resource Object\nfunc NewCoreResource() *CoreResource {\n\tc := &CoreResource{}\n\n\treturn c\n}\n"
  },
  {
    "path": "coreresource_test.go",
    "content": "package canopus\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCoreResourceParsing(t *testing.T) {\n\n\tcases1 := []struct {\n\t\tin         string\n\t\telemCount  int\n\t\ttargets    []string\n\t\tattrCount  []int\n\t\tattributes []map[string]string\n\t}{\n\t\t{\n\t\t\t\"</1>,</2>,</3>,</4>,</5/0>,</5/1>,</5/2>,</5/3>\",\n\t\t\t8,\n\t\t\t[]string{\"/1\", \"/2\", \"/3\", \"/4\", \"/5/0\", \"/5/1\", \"/5/2\", \"/5/3\"},\n\t\t\t[]int{0, 0, 0, 0, 0, 0, 0, 0, 0},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"</sensors>;ct=40;title=\\\"Sensor Index\\\",</sensors/temp>;rt=\\\"temperature-c\\\";if=\\\"sensor\\\",</sensors/light>;rt=\\\"light-lux\\\";if=\\\"sensor\\\",<http://www.example.com/sensors/t123>;anchor=\\\"/sensors/temp\\\";rel=\\\"describedby\\\",</t>;anchor=\\\"/sensors/temp\\\";rel=\\\"alternate\\\"\",\n\t\t\t5,\n\t\t\t[]string{\"/sensors\", \"/sensors/temp\", \"/sensors/light\", \"http://www.example.com/sensors/t123\", \"/t\"},\n\t\t\t[]int{1, 2, 2, 2, 2},\n\t\t\t[]map[string]string{\n\t\t\t\tmap[string]string{\n\t\t\t\t\t\"ct\":    \"40\",\n\t\t\t\t\t\"title\": \"Sensor Index\",\n\t\t\t\t},\n\t\t\t\tmap[string]string{\n\t\t\t\t\t\"rt\": \"temperature-c\",\n\t\t\t\t\t\"if\": \"sensor\",\n\t\t\t\t},\n\t\t\t\tmap[string]string{\n\t\t\t\t\t\"rt\": \"light-lux\",\n\t\t\t\t\t\"if\": \"sensor\",\n\t\t\t\t},\n\t\t\t\tmap[string]string{\n\t\t\t\t\t\"anchor\": \"/sensors/temp\",\n\t\t\t\t\t\"rel\":    \"describedby\",\n\t\t\t\t},\n\t\t\t\tmap[string]string{\n\t\t\t\t\t\"anchor\": \"/sensors/temp\",\n\t\t\t\t\t\"rel\":    \"alternate\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, c := range cases1 {\n\t\tresources := CoreResourcesFromString(c.in)\n\t\tassert.Equal(t, len(resources), c.elemCount)\n\n\t\tfor i, o := range resources {\n\t\t\tassert.Equal(t, o.Target, c.targets[i])\n\n\t\t\tfor _, a := range o.Attributes {\n\t\t\t\tkey := a.Key\n\t\t\t\tval := a.Value\n\n\t\t\t\tassert.Equal(t, c.attributes[i][key], val)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "dtls.go",
    "content": "package canopus\n\n/*\n#cgo LDFLAGS: -L${SRCDIR}/openssl -lssl -lcrypto -ldl\n#cgo CFLAGS: -g -Wno-deprecated -Wno-error -I${SRCDIR}/openssl/include -Wno-incompatible-pointer-types -Wno-return-type\n\n#include <stdlib.h>\n#include <string.h>\n#include <openssl/err.h>\n#include <openssl/ssl.h>\n#include <openssl/bio.h>\n#include <internal/bio.h>\n\nextern int go_session_bio_write(BIO* bio, char* buf, int num);\nextern int go_session_bio_read(BIO* bio, char* buf, int num);\nextern int go_session_bio_free(BIO* bio);\nextern unsigned int go_server_psk_callback(SSL *ssl, char *identity, char *psk, unsigned int max_psk_len);\nextern int generate_cookie_callback(SSL* ssl, unsigned char* cookie, unsigned int *cookie_len);\nextern int verify_cookie_callback(SSL* ssl, unsigned char* cookie, unsigned int cookie_len);\n\nstatic long go_session_bio_ctrl(BIO *bp,int cmd,long larg,void *parg) {\n\treturn 1;\n}\n\nstatic int write_wrapper(BIO* bio, char* data, int n) {\n\treturn go_session_bio_write(bio,data,n);\n}\n\nstatic int go_session_bio_create( BIO *b ) {\n\tBIO_set_init(b,1);\n\tBIO_set_flags(b, BIO_FLAGS_READ | BIO_FLAGS_WRITE);\n\treturn 1;\n}\n\n// a BIO for a client conencted to our server\nstatic BIO_METHOD* go_session_bio_method;\n\nstatic BIO_METHOD* BIO_go_session() {\n\treturn go_session_bio_method;\n}\n\nstatic void set_errno(int e) {\n\terrno = e;\n}\n\nstatic char *getGoData(BIO* bio) {\n\treturn BIO_get_data(bio);\n}\n\nstatic unsigned int server_psk_callback(SSL *ssl, char *identity, unsigned char *psk, unsigned int max_psk_len) {\n\treturn go_server_psk_callback(ssl,identity,(char*)psk,max_psk_len);\n}\n\nstatic void init_lib() {\n\tsetvbuf(stdout, NULL, _IOLBF, 0);\n\tSSL_library_init();\n\tERR_load_BIO_strings();\n\tSSL_load_error_strings();\n}\n\nstatic int init_session_bio_method() {\n\tgo_session_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK,\"go session dtls\");\n\tBIO_meth_set_write(go_session_bio_method,write_wrapper);\n\tBIO_meth_set_read(go_session_bio_method,go_session_bio_read);\n\tBIO_meth_set_ctrl(go_session_bio_method,go_session_bio_ctrl);\n\tBIO_meth_set_create(go_session_bio_method,go_session_bio_create);\n\tBIO_meth_set_destroy(go_session_bio_method,go_session_bio_free);\n}\n\nstatic void init_server_ctx(SSL_CTX *ctx) {\n\tSSL_CTX_set_min_proto_version(ctx, 0xFEFD); // 1.2\n\tSSL_CTX_set_max_proto_version(ctx, 0xFEFD); // 1.2\n\tSSL_CTX_set_read_ahead(ctx, 1);\n\tSSL_CTX_set_cookie_generate_cb(ctx, &generate_cookie_callback);\n\tSSL_CTX_set_cookie_verify_cb(ctx, &verify_cookie_callback);\n}\n\nstatic int get_errno(void) {\n\treturn errno;\n}\n\nstatic void setGoData(BIO* bio, char *data) {\n\tBIO_set_data(bio, data);\n}\n\nstatic void set_cookie_option(SSL *ssl) {\n\tSSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);\n}\n\nstatic void set_psk_callback(SSL *ssl) {\n\tSSL_set_psk_server_callback(ssl, &server_psk_callback);\n}\n\nstatic void setGoSessionId(BIO* bio, unsigned int clientId) {\n\tunsigned int * pId = malloc(sizeof(unsigned int));\n\t*pId = clientId;\n\tBIO_set_data(bio,pId);\n}\n\n// Client\nextern int go_conn_bio_write(BIO* bio, char* buf, int num);\nextern int go_conn_bio_read(BIO* bio, char* buf, int num);\nextern int go_conn_bio_free(BIO* bio);\nextern unsigned int go_psk_callback(SSL *ssl, char *hint, char *identity, unsigned int max_identity_len, char *psk, unsigned int max_psk_len);\n\nstatic long go_bio_ctrl(BIO *bp,int cmd,long larg,void *parg) {\n\t//always return operation not supported\n\t//http://www.openssl.org/docs/crypto/BIO_ctrl.html\n\t//printf(\"go_bio_ctrl %d\\n\", cmd);\n\treturn 1;\n}\n\nstatic int go_bio_create( BIO *b ) {\n\tBIO_set_init(b,1);\n\t//BIO_set_num(b,-1);\n\t//BIO_set_ptr(b,NULL);\n\tBIO_set_flags(b, BIO_FLAGS_READ | BIO_FLAGS_WRITE);\n\treturn 1;\n}\n\nstatic BIO_METHOD go_bio_method = {\n\tBIO_TYPE_SOURCE_SINK,\n\t\"go dtls\",\n\t(int (*)(BIO *, const char *, int))go_conn_bio_write,\n\tgo_conn_bio_read,\n\tNULL,\n\tNULL,\n\tgo_bio_ctrl, // ctrl\n\tgo_bio_create, // new\n\tgo_conn_bio_free // delete\n};\n\nstatic BIO_METHOD* BIO_go() {\n\treturn &go_bio_method;\n}\n\nstatic void set_proto_1_2(SSL_CTX *ctx) {\n\tSSL_CTX_set_min_proto_version(ctx, 0xFEFD); // 1.2\n\tSSL_CTX_set_max_proto_version(ctx, 0xFEFD); // 1.2\n}\n\nstatic unsigned int psk_callback(SSL *ssl, const char *hint,\n        char *identity, unsigned int max_identity_len,\n        unsigned char *psk, unsigned int max_psk_len) {\n\treturn go_psk_callback(ssl,hint,identity,max_identity_len,(char*)psk,max_psk_len);\n}\n\nstatic void init_ctx(SSL_CTX *ctx) {\n\tSSL_CTX_set_read_ahead(ctx, 1);\n\tSSL_CTX_set_psk_client_callback(ctx,&psk_callback);\n}\n\nstatic void setGoClientId(BIO* bio, unsigned int clientId) {\n\tunsigned int * pId = malloc(sizeof(unsigned int));\n\t*pId = clientId;\n\tBIO_set_data(bio,pId);\n}\n\n*/\nimport \"C\"\nimport (\n\t\"bytes\"\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"reflect\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nfunc init() {\n\t// low level init of OpenSSL\n\tC.init_lib()\n\n\t// init server BIO\n\tserver_bio_method_init()\n}\n\nfunc server_bio_method_init() {\n\tC.init_session_bio_method()\n}\n\nfunc NewServerDtlsContext() (ctx *ServerDtlsContext, err error) {\n\tsslCtx := C.SSL_CTX_new(C.DTLSv1_2_server_method())\n\n\tif sslCtx == nil {\n\t\terr = errors.New(\"error creating SSL context\")\n\t\treturn\n\t}\n\n\tC.init_server_ctx(sslCtx)\n\n\tret := int(C.SSL_CTX_set_cipher_list(sslCtx, C.CString(\"PSK-AES256-CCM8:PSK-AES128-CCM8\")))\n\tif ret != 1 {\n\t\terr = errors.New(\"impossible to set cipherlist\")\n\t}\n\n\tctx = &ServerDtlsContext{\n\t\tsslCtx: sslCtx,\n\t}\n\n\treturn\n}\n\ntype ServerDtlsContext struct {\n\tsslCtx *C.SSL_CTX\n}\n\n//export go_session_bio_read\nfunc go_session_bio_read(bio *C.BIO, buf *C.char, num C.int) C.int {\n\tsession := DTLS_SERVER_SESSIONS[*(*int32)(C.BIO_get_data(bio))]\n\tsocketData := <-session.rcvd\n\n\tdata := goSliceFromCString(buf, int(num))\n\tif data == nil {\n\t\treturn 0\n\t}\n\n\twrote := copy(data, socketData)\n\treturn C.int(wrote)\n}\n\n//export go_session_bio_write\nfunc go_session_bio_write(bio *C.BIO, buf *C.char, num C.int) C.int {\n\tsession := DTLS_SERVER_SESSIONS[*(*int32)(C.BIO_get_data(bio))]\n\tdata := goSliceFromCString(buf, int(num))\n\n\tn, err := session.GetConnection().WriteTo(data, session.GetAddress())\n\tif err != nil && err != io.EOF {\n\t\t//We expect either a syscall error\n\t\t//or a netOp error wrapping a syscall error\n\tTESTERR:\n\t\tswitch err.(type) {\n\t\tcase syscall.Errno:\n\t\t\tC.set_errno(C.int(err.(syscall.Errno)))\n\t\tcase *net.OpError:\n\t\t\terr = err.(*net.OpError).Err\n\t\t\tbreak TESTERR\n\t\t}\n\t\treturn C.int(-1)\n\t}\n\treturn C.int(n)\n}\n\n//export go_session_bio_free\nfunc go_session_bio_free(bio *C.BIO) C.int {\n\t// TODO\n\n\t// some flags magic\n\tif C.int(C.BIO_get_shutdown(bio)) != 0 {\n\t\tC.BIO_set_data(bio, nil)\n\t\tC.BIO_set_flags(bio, 0)\n\t\tC.BIO_set_init(bio, 0)\n\t}\n\treturn C.int(1)\n}\n\n//export go_server_psk_callback\nfunc go_server_psk_callback(ssl *C.SSL, identity *C.char, psk *C.char, max_psk_len C.uint) C.uint {\n\tbio := C.SSL_get_rbio(ssl)\n\tsession := DTLS_SERVER_SESSIONS[*(*int32)(C.BIO_get_data(bio))]\n\tserver := session.GetServer().(*DefaultCoapServer)\n\n\tgoPskID := C.GoString(identity)\n\n\tserverPsk := server.fnPskHandler(goPskID)\n\n\tif serverPsk == nil {\n\t\treturn 0\n\t}\n\n\tif len(serverPsk) >= int(max_psk_len) {\n\t\treturn 0\n\t}\n\n\ttargetPsk := goSliceFromCString(psk, int(max_psk_len))\n\treturn C.uint(copy(targetPsk, serverPsk))\n}\n\n//export generate_cookie_callback\nfunc generate_cookie_callback(ssl *C.SSL, cookie *C.uchar, cookie_len *C.uint) C.int {\n\tbio := C.SSL_get_rbio(ssl)\n\tsession := DTLS_SERVER_SESSIONS[*(*int32)(C.BIO_get_data(bio))]\n\n\tmac := hmac.New(sha256.New, session.GetServer().GetCookieSecret())\n\tmac.Write([]byte(session.GetAddress().String()))\n\tcookieValue := mac.Sum(nil)\n\n\tif len(cookieValue) >= int(*cookie_len) {\n\t\tlogMsg(\"Not enough cookie space (should not happen..)\")\n\t\treturn 0\n\t}\n\n\tdata := goSliceFromUCString(cookie, int(*cookie_len))\n\n\t*cookie_len = C.uint(copy(data, cookieValue))\n\treturn 1\n\n}\n\n//export verify_cookie_callback\nfunc verify_cookie_callback(ssl *C.SSL, cookie *C.uchar, cookie_len C.uint) C.int {\n\tbio := C.SSL_get_rbio(ssl)\n\tsession := DTLS_SERVER_SESSIONS[*(*int32)(C.BIO_get_data(bio))]\n\n\tmac := hmac.New(sha256.New, session.GetServer().GetCookieSecret())\n\tmac.Write([]byte(session.GetAddress().String()))\n\tcookieValue := mac.Sum(nil)\n\n\tif len(cookieValue) != int(cookie_len) {\n\t\treturn 0\n\t}\n\n\tdata := goSliceFromUCString(cookie, int(cookie_len))\n\n\tif bytes.Equal(data, cookieValue) {\n\t\treturn 1\n\t} else {\n\t\treturn 0\n\t}\n}\n\n// Provides a zero copy interface for returning a go slice backed by a c array.\nfunc goSliceFromCString(cArray *C.char, size int) (cslice []byte) {\n\t//See http://code.google.com/p/go-wiki/wiki/cgo\n\t//It turns out it's really easy to\n\t//make a string from a *C.char and vise versa.\n\t//not so easy to write to a c array.\n\tsliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&cslice)))\n\tsliceHeader.Cap = size\n\tsliceHeader.Len = size\n\tsliceHeader.Data = uintptr(unsafe.Pointer(cArray))\n\treturn\n}\n\n// Provides a zero copy interface for returning a go slice backed by a c array.\nfunc goSliceFromUCString(cArray *C.uchar, size int) (cslice []byte) {\n\t//See http://code.google.com/p/go-wiki/wiki/cgo\n\t//It turns out it's really easy to\n\t//make a string from a *C.char and vise versa.\n\t//not so easy to write to a c array.\n\tsliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&cslice)))\n\tsliceHeader.Cap = size\n\tsliceHeader.Len = size\n\tsliceHeader.Data = uintptr(unsafe.Pointer(cArray))\n\treturn\n}\n\nfunc getErrorString(code C.ulong) string {\n\tif code == 0 {\n\t\treturn \"\"\n\t}\n\tmsg := fmt.Sprintf(\"%s:%s:%s\\n\",\n\t\tC.GoString(C.ERR_lib_error_string(code)),\n\t\tC.GoString(C.ERR_func_error_string(code)),\n\t\tC.GoString(C.ERR_reason_error_string(code)))\n\tif len(msg) == 4 { //being lazy here, all the strings were empty\n\t\treturn \"\"\n\t}\n\t//Check for extra line data\n\tvar file *C.char\n\tvar line C.int\n\tvar data *C.char\n\tvar flags C.int\n\tif int(C.ERR_get_error_line_data(&file, &line, &data, &flags)) != 0 {\n\t\tmsg += fmt.Sprintf(\"%s:%s\", C.GoString(file), int(line))\n\t\tif flags&C.ERR_TXT_STRING != 0 {\n\t\t\tmsg += \":\" + C.GoString(data)\n\t\t}\n\t\tif flags&C.ERR_TXT_MALLOCED != 0 {\n\t\t\tC.CRYPTO_free(unsafe.Pointer(data), C.CString(\"\"), 0)\n\t\t}\n\t}\n\treturn msg\n}\n\nfunc newSslSession(session *DTLSServerSession, ctx *ServerDtlsContext, pskCallback func(id string) []byte) (err error) {\n\tssl := C.SSL_new(ctx.sslCtx)\n\n\tid := atomic.AddInt32(&NEXT_SESSION_ID, 1)\n\n\tif pskCallback != nil {\n\t\tC.set_psk_callback(ssl)\n\t}\n\n\tbio := C.BIO_new(C.BIO_go_session())\n\n\tif bio == nil {\n\t\terr = errors.New(\"Error creating session: Bio is nil\")\n\t\treturn\n\t}\n\tC.SSL_set_bio(ssl, bio, bio)\n\n\tsession.ssl = ssl\n\tsession.bio = bio\n\n\tDTLS_SERVER_SESSIONS[id] = session\n\n\tC.setGoSessionId(bio, C.uint(id))\n\n\tC.set_cookie_option(ssl)\n\tC.SSL_set_accept_state(ssl)\n\tC.DTLSv1_listen\n\n\treturn\n}\n\ntype DTLSServerSession struct {\n\tUDPServerSession\n\tssl *C.SSL\n\tbio *C.BIO\n}\n\nfunc (s *DTLSServerSession) GetConnection() ServerConnection {\n\treturn s.conn\n}\n\nfunc (s *DTLSServerSession) Write(b []byte) (int, error) {\n\t// TODO test is connected ?\n\tlength := len(b)\n\tret := C.SSL_write(s.ssl, unsafe.Pointer(&b[0]), C.int(length))\n\tif err := s.getError(ret); err != nil {\n\t\treturn 0, err\n\t}\n\treturn int(ret), nil\n}\n\nfunc (s *DTLSServerSession) Read(b []byte) (n int, err error) {\n\t// TODO test if closed?\n\tlength := len(b)\n\t// s.rcvd <- s.buf\n\n\tret := C.SSL_read(s.ssl, unsafe.Pointer(&b[0]), C.int(length))\n\tif err = s.getError(ret); err != nil {\n\t\tn = 0\n\t\treturn\n\t}\n\t// if there's no error, but a return value of 0\n\t// let's say it's an EOF\n\tif ret == 0 {\n\t\tn = 0\n\t\terr = io.EOF\n\t\treturn\n\t}\n\tn = int(ret)\n\treturn\n}\n\nfunc (s *DTLSServerSession) getError(ret C.int) error {\n\terr := C.SSL_get_error(s.ssl, ret)\n\tswitch err {\n\tcase C.SSL_ERROR_NONE:\n\t\treturn nil\n\tcase C.SSL_ERROR_ZERO_RETURN:\n\t\treturn io.EOF\n\tcase C.SSL_ERROR_SYSCALL:\n\t\tif int(C.ERR_peek_error()) != 0 {\n\t\t\treturn syscall.Errno(C.get_errno())\n\t\t}\n\n\tdefault:\n\t\tmsg := \"\"\n\t\tfor {\n\t\t\terrCode := C.ERR_get_error()\n\t\t\tif errCode == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmsg += getErrorString(errCode)\n\t\t}\n\t\tC.ERR_clear_error()\n\t\treturn errors.New(msg)\n\t}\n\treturn nil\n}\n\n// Client DTLS\nfunc NewDTLSConnection(c net.Conn, identity, psk string) (conn Connection, err error) {\n\tsslCtx := C.SSL_CTX_new(C.DTLSv1_2_client_method())\n\n\tC.set_proto_1_2(sslCtx)\n\tC.init_ctx(sslCtx)\n\n\tret := int(C.SSL_CTX_set_cipher_list(sslCtx, C.CString(\"PSK-AES256-CCM8:PSK-AES128-CCM8\")))\n\tif ret != 1 {\n\t\terr = errors.New(\"impossible to set cipherlist\")\n\t\treturn\n\t}\n\n\tssl := C.SSL_new(sslCtx)\n\n\t// self := DTLSClient{false, 0, C.BIO_new(C.BIO_go()), dtlsCtx.ctx, ssl, conn, nil, nil}\n\tbio := C.BIO_new(C.BIO_go())\n\n\tconn = &DTLSConnection{\n\t\tUDPConnection: UDPConnection{\n\t\t\tconn: c,\n\t\t},\n\t\tsslCtx: sslCtx,\n\t\tssl:    ssl,\n\t\tbio:    bio,\n\t\tpsk:    []byte(psk),\n\t\tpskId:  &identity,\n\t}\n\n\tC.SSL_set_bio(ssl, bio, bio)\n\n\tid := atomic.AddInt32(&NEXT_SESSION_ID, 1)\n\tC.setGoClientId(bio, C.uint(id))\n\tDTLS_CLIENT_CONNECTIONS[id] = conn.(*DTLSConnection)\n\n\treturn\n}\n\ntype DTLSConnection struct {\n\tUDPConnection\n\tclosed    bool\n\tconnected int32 // connection handshake was done, atomic (0 false, 1 true)\n\tsslCtx    *C.SSL_CTX\n\tbio       *C.BIO\n\tssl       *C.SSL\n\tpskId     *string\n\tpsk       []byte\n}\n\nfunc (c *DTLSConnection) ObserveResource(resource string) (tok string, err error) {\n\treq := NewRequest(MessageConfirmable, Get)\n\treq.SetRequestURI(resource)\n\treq.GetMessage().AddOption(OptionObserve, 0)\n\n\tresp, err := c.Send(req)\n\ttok = string(resp.GetMessage().GetToken())\n\n\treturn\n}\n\nfunc (c *DTLSConnection) CancelObserveResource(resource string, token string) (err error) {\n\treq := NewRequest(MessageConfirmable, Get)\n\treq.SetRequestURI(resource)\n\treq.GetMessage().AddOption(OptionObserve, 1)\n\n\t_, err = c.Send(req)\n\treturn\n}\n\nfunc (c *DTLSConnection) StopObserve(ch chan ObserveMessage) {\n\tclose(ch)\n}\n\nfunc (c *DTLSConnection) Observe(ch chan ObserveMessage) {\n\n\treadBuf := make([]byte, MaxPacketSize)\n\tfor {\n\t\tlen, err := c.Read(readBuf)\n\t\tif err == nil {\n\t\t\tmsgBuf := make([]byte, len)\n\t\t\tcopy(msgBuf, readBuf)\n\n\t\t\tmsg, err := BytesToMessage(msgBuf)\n\t\t\tif msg.GetOption(OptionObserve) != nil {\n\t\t\t\tch <- NewObserveMessage(msg.GetURIPath(), msg.GetPayload(), msg)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tlogMsg(\"Error occured reading UDP\", err)\n\t\t\t\tclose(ch)\n\t\t\t}\n\t\t} else {\n\t\t\tlogMsg(\"Error occured reading UDP\", err)\n\t\t\tclose(ch)\n\t\t}\n\t}\n}\n\nfunc (c *DTLSConnection) Send(req Request) (resp Response, err error) {\n\tmsg := req.GetMessage()\n\topt := msg.GetOption(OptionBlock1)\n\n\tif opt == nil { // Block1 was not set\n\t\tif MessageSizeAllowed(req) != true {\n\t\t\treturn nil, ErrMessageSizeTooLongBlockOptionValNotSet\n\t\t}\n\n\t} else { // Block1 was set\n\t\t// fmt.Println(\"Block 1 was set\")\n\t}\n\n\tif opt != nil {\n\t\tblockOpt := Block1OptionFromOption(opt)\n\n\t\tif blockOpt.Value == nil {\n\t\t\tif MessageSizeAllowed(req) != true {\n\t\t\t\terr = ErrMessageSizeTooLongBlockOptionValNotSet\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\t// - Block # = one and only block (sz = unspecified), whereas 0 = 16bits\n\t\t\t\t// - MOre bit = 0\n\t\t\t}\n\t\t} else { // BLock transfer request\n\t\t\tpayload := msg.GetPayload().GetBytes()\n\t\t\tpayloadLen := uint32(len(payload))\n\t\t\tblockSize := blockOpt.BlockSizeLength()\n\t\t\tcurrSeq := uint32(0)\n\t\t\ttotalBlocks := uint32(payloadLen / blockSize)\n\t\t\tcompleted := false\n\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(1)\n\n\t\t\tfor completed == false {\n\t\t\t\tif currSeq <= totalBlocks {\n\n\t\t\t\t\tvar blockPayloadStart uint32\n\t\t\t\t\tvar blockPayloadEnd uint32\n\t\t\t\t\tvar blockPayload []byte\n\n\t\t\t\t\tblockPayloadStart = currSeq*uint32(blockSize) + (currSeq * 1)\n\n\t\t\t\t\tmore := true\n\t\t\t\t\tif currSeq == totalBlocks {\n\t\t\t\t\t\tmore = false\n\t\t\t\t\t\tblockPayloadEnd = payloadLen\n\t\t\t\t\t} else {\n\t\t\t\t\t\tblockPayloadEnd = blockPayloadStart + uint32(blockSize)\n\t\t\t\t\t}\n\n\t\t\t\t\tblockPayload = payload[blockPayloadStart:blockPayloadEnd]\n\n\t\t\t\t\tblockOpt = NewBlock1Option(blockOpt.Size(), more, currSeq)\n\t\t\t\t\tmsg.ReplaceOptions(blockOpt.Code, []Option{blockOpt})\n\t\t\t\t\tmodifiedMsg := msg.(*CoapMessage)\n\t\t\t\t\tmodifiedMsg.SetMessageId(GenerateMessageID())\n\t\t\t\t\tmodifiedMsg.SetPayload(NewBytesPayload(blockPayload))\n\n\t\t\t\t\t// send message\n\t\t\t\t\t_, err2 := c.sendMessage(msg)\n\t\t\t\t\tif err2 != nil {\n\t\t\t\t\t\twg.Done()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcurrSeq = currSeq + 1\n\n\t\t\t\t} else {\n\t\t\t\t\tcompleted = true\n\t\t\t\t\twg.Done()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tresp, err = c.sendMessage(msg)\n\treturn\n}\n\nfunc (c *DTLSConnection) sendMessage(msg Message) (resp Response, err error) {\n\n\tif msg == nil {\n\t\treturn nil, ErrNilMessage\n\t}\n\n\tb, err := MessageToBytes(msg)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif msg.GetMessageType() == MessageNonConfirmable {\n\t\tgo c.Write(b)\n\t\tresp = NewResponse(NewEmptyMessage(msg.GetMessageId()), nil)\n\t\treturn\n\t}\n\n\t_, err = c.Write(b)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmsgBuf := make([]byte, 1500)\n\tif msg.GetMessageType() == MessageAcknowledgment {\n\t\tresp = NewResponse(NewEmptyMessage(msg.GetMessageId()), nil)\n\t\treturn\n\t}\n\n\tn, err := c.Read(msgBuf)\n\tif err != nil {\n\t\treturn\n\t}\n\n\trespMsg, err := BytesToMessage(msgBuf[:n])\n\tif err != nil {\n\t\treturn\n\t}\n\n\tresp = NewResponse(respMsg, nil)\n\n\tif msg.GetMessageType() == MessageConfirmable {\n\t\t// TODO: Send out message and wait for a confirm. If confirmation not retrieved,\n\t\t// resend (taking into account timeouts and back-off transmissions\n\n\t\t// c.Send(NewRequestFromMessage(msg))\n\t}\n\treturn\n}\n\nfunc (c *DTLSConnection) Write(b []byte) (int, error) {\n\tif atomic.CompareAndSwapInt32(&c.connected, 0, 1) {\n\t\tif err := c.connect(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\tlength := len(b)\n\tret := C.SSL_write(c.ssl, unsafe.Pointer(&b[0]), C.int(length))\n\tif err := c.getError(ret); err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int(ret), nil\n}\n\nfunc (c *DTLSConnection) Read(b []byte) (int, error) {\n\tif atomic.CompareAndSwapInt32(&c.connected, 0, 1) {\n\t\tif err := c.connect(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\tlength := len(b)\n\tret := C.SSL_read(c.ssl, unsafe.Pointer(&b[0]), C.int(length))\n\tif err := c.getError(ret); err != nil {\n\t\treturn 0, err\n\t}\n\n\t// if there's no error, but a return value of 0\n\t// let's say it's an EOF\n\tif ret == 0 {\n\t\treturn 0, io.EOF\n\t}\n\n\treturn int(ret), nil\n}\n\nfunc (c *DTLSConnection) Close() error {\n\tif c.closed {\n\t\treturn nil\n\t}\n\tc.closed = true\n\tdefer func() {\n\t\tC.SSL_free(c.ssl)\n\t}()\n\n\tret := C.SSL_shutdown(c.ssl)\n\tif int(ret) == 0 {\n\t\tret = C.SSL_shutdown(c.ssl)\n\t\tif int(ret) != 1 {\n\t\t\treturn c.getError(ret)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *DTLSConnection) connect() error {\n\tret := C.SSL_connect(c.ssl)\n\tif err := c.getError(ret); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *DTLSConnection) getError(ret C.int) error {\n\terr := C.SSL_get_error(c.ssl, ret)\n\tswitch err {\n\tcase C.SSL_ERROR_NONE:\n\t\treturn nil\n\tcase C.SSL_ERROR_ZERO_RETURN:\n\t\treturn io.EOF\n\tcase C.SSL_ERROR_SYSCALL:\n\t\tif int(C.ERR_peek_error()) != 0 {\n\t\t\treturn syscall.Errno(C.get_errno())\n\t\t}\n\n\tdefault:\n\t\tmsg := \"\"\n\t\tfor {\n\t\t\terrCode := C.ERR_get_error()\n\t\t\tif errCode == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmsg += getErrorString(errCode)\n\t\t}\n\t\tC.ERR_clear_error()\n\t\treturn errors.New(msg)\n\t}\n\treturn nil\n}\n\n//export go_conn_bio_write\nfunc go_conn_bio_write(bio *C.BIO, buf *C.char, num C.int) C.int {\n\tclient := DTLS_CLIENT_CONNECTIONS[*(*int32)(C.BIO_get_data(bio))]\n\tdata := goSliceFromCString(buf, int(num))\n\tn, err := client.conn.Write(data)\n\tif err != nil && err != io.EOF {\n\t\t//We expect either a syscall error\n\t\t//or a netOp error wrapping a syscall error\n\tTESTERR:\n\t\tswitch err.(type) {\n\t\tcase syscall.Errno:\n\t\t\tC.set_errno(C.int(err.(syscall.Errno)))\n\t\tcase *net.OpError:\n\t\t\terr = err.(*net.OpError).Err\n\t\t\tbreak TESTERR\n\t\t}\n\t\treturn C.int(-1)\n\t}\n\treturn C.int(n)\n}\n\n//export go_conn_bio_read\nfunc go_conn_bio_read(bio *C.BIO, buf *C.char, num C.int) C.int {\n\tclient := DTLS_CLIENT_CONNECTIONS[*(*int32)(C.BIO_get_data(bio))]\n\tdata := goSliceFromCString(buf, int(num))\n\tn, err := client.conn.Read(data)\n\tif err == nil {\n\t\treturn C.int(n)\n\t}\n\n\tif err == io.EOF || err == io.ErrUnexpectedEOF {\n\t\treturn 0\n\t}\n\t//We expect either a syscall error\n\t//or a netOp error wrapping a syscall error\n\nTESTERR:\n\tswitch err.(type) {\n\tcase syscall.Errno:\n\t\tC.set_errno(C.int(err.(syscall.Errno)))\n\tcase *net.OpError:\n\t\terr = err.(*net.OpError).Err\n\t\tbreak TESTERR\n\t}\n\treturn C.int(-1)\n}\n\n//export go_conn_bio_free\nfunc go_conn_bio_free(bio *C.BIO) C.int {\n\tclient := DTLS_CLIENT_CONNECTIONS[*(*int32)(C.BIO_get_data(bio))]\n\tclient.Close()\n\tif C.int(C.BIO_get_shutdown(bio)) != 0 {\n\t\tC.BIO_set_data(bio, nil)\n\t\tC.BIO_set_flags(bio, 0)\n\t\tC.BIO_set_init(bio, 0)\n\t}\n\treturn C.int(1)\n}\n\n//export go_psk_callback\nfunc go_psk_callback(ssl *C.SSL, hint *C.char, identity *C.char, max_identity_len C.uint, psk *C.char, max_psk_len C.uint) C.uint {\n\tbio := C.SSL_get_rbio(ssl)\n\tclient := DTLS_CLIENT_CONNECTIONS[*(*int32)(C.BIO_get_data(bio))]\n\n\tif client.pskId == nil || client.psk == nil {\n\t\treturn 0\n\t}\n\n\tif len(*client.pskId) >= int(max_identity_len) || len(client.psk) >= int(max_psk_len) {\n\t\tlogMsg(\"PSKID or PSK too large\")\n\t\treturn 0\n\t}\n\ttargetId := goSliceFromCString(identity, int(max_identity_len))\n\tcopy(targetId, *client.pskId)\n\ttargetPsk := goSliceFromCString(psk, int(max_psk_len))\n\treturn C.uint(copy(targetPsk, client.psk))\n}\n"
  },
  {
    "path": "empty.go",
    "content": "package canopus\n\nfunc NewEmptyPayload() MessagePayload {\n\treturn &EmptyPayload{}\n}\n\n// Represents an empty message payload\ntype EmptyPayload struct {\n}\n\nfunc (p *EmptyPayload) GetBytes() []byte {\n\treturn []byte{}\n}\n\nfunc (p *EmptyPayload) Length() int {\n\treturn 0\n}\n\nfunc (p *EmptyPayload) String() string {\n\treturn \"\"\n}\n"
  },
  {
    "path": "event.go",
    "content": "package canopus\n\nfunc NewEvents() *ServerEvents {\n\treturn &ServerEvents{\n\t\tevtFnNotify:        []FnEventNotify{},\n\t\tevtFnStart:         []FnEventStart{},\n\t\tevtFnClose:         []FnEventClose{},\n\t\tevtFnDiscover:      []FnEventDiscover{},\n\t\tevtFnError:         []FnEventError{},\n\t\tevtFnObserve:       []FnEventObserve{},\n\t\tevtFnObserveCancel: []FnEventObserveCancel{},\n\t\tevtFnMessage:       []FnEventMessage{},\n\t\tevtFnBlockMessage:  []FnEventBlockMessage{},\n\t}\n}\n\n// This holds the various events which are triggered throughout\n// an application's lifetime\ntype ServerEvents struct {\n\tevtFnNotify        []FnEventNotify\n\tevtFnStart         []FnEventStart\n\tevtFnClose         []FnEventClose\n\tevtFnDiscover      []FnEventDiscover\n\tevtFnError         []FnEventError\n\tevtFnObserve       []FnEventObserve\n\tevtFnObserveCancel []FnEventObserveCancel\n\tevtFnMessage       []FnEventMessage\n\tevtFnBlockMessage  []FnEventBlockMessage\n}\n\n// OnNotify is Fired when an observeed resource is notified\nfunc (ce *ServerEvents) OnNotify(fn FnEventNotify) {\n\tce.evtFnNotify = append(ce.evtFnNotify, fn)\n}\n\n// Fired when the server/client starts up\nfunc (ce *ServerEvents) OnStart(fn FnEventStart) {\n\tce.evtFnStart = append(ce.evtFnStart, fn)\n}\n\n// Fired when the server/client closes\nfunc (ce *ServerEvents) OnClose(fn FnEventClose) {\n\tce.evtFnClose = append(ce.evtFnClose, fn)\n}\n\n// Fired when a discovery request is triggered\nfunc (ce *ServerEvents) OnDiscover(fn FnEventDiscover) {\n\tce.evtFnDiscover = append(ce.evtFnDiscover, fn)\n}\n\n// Catch-all event which is fired when an error occurs\nfunc (ce *ServerEvents) OnError(fn FnEventError) {\n\tce.evtFnError = append(ce.evtFnError, fn)\n}\n\n// Fired when an observe request is triggered for a resource\nfunc (ce *ServerEvents) OnObserve(fn FnEventObserve) {\n\tce.evtFnObserve = append(ce.evtFnObserve, fn)\n}\n\n// Fired when an observe-cancel request is triggered foa r esource\nfunc (ce *ServerEvents) OnObserveCancel(fn FnEventObserveCancel) {\n\tce.evtFnObserveCancel = append(ce.evtFnObserveCancel, fn)\n}\n\n// Fired when a message is received\nfunc (ce *ServerEvents) OnMessage(fn FnEventMessage) {\n\tce.evtFnMessage = append(ce.evtFnMessage, fn)\n}\n\n// Fired when a block messageis received\nfunc (ce *ServerEvents) OnBlockMessage(fn FnEventBlockMessage) {\n\tce.evtFnBlockMessage = append(ce.evtFnBlockMessage, fn)\n}\n\n// Fires the \"OnNotify\" event\nfunc (ce *ServerEvents) Notify(resource string, value interface{}, msg Message) {\n\tfor _, fn := range ce.evtFnNotify {\n\t\tfn(resource, value, msg)\n\t}\n}\n\n// Fires the \"OnStarted\" event\nfunc (ce *ServerEvents) Started(server CoapServer) {\n\tfor _, fn := range ce.evtFnStart {\n\t\tfn(server)\n\t}\n}\n\n// Fires the \"OnClosed\" event\nfunc (ce *ServerEvents) Closed(server CoapServer) {\n\tfor _, fn := range ce.evtFnClose {\n\t\tfn(server)\n\t}\n}\n\n// Fires the \"OnDiscover\" event\nfunc (ce *ServerEvents) Discover() {\n\tfor _, fn := range ce.evtFnDiscover {\n\t\tfn()\n\t}\n}\n\n// Fires the \"OnError\" event given an error object\nfunc (ce *ServerEvents) Error(err error) {\n\tfor _, fn := range ce.evtFnError {\n\t\tfn(err)\n\t}\n}\n\n// Fires the \"OnObserve\" event for a given resource\nfunc (ce *ServerEvents) Observe(resource string, msg Message) {\n\tfor _, fn := range ce.evtFnObserve {\n\t\tfn(resource, msg)\n\t}\n}\n\n// Fires the \"OnObserveCancelled\" event for a given resource\nfunc (ce *ServerEvents) ObserveCancelled(resource string, msg Message) {\n\tfor _, fn := range ce.evtFnObserveCancel {\n\t\tfn(resource, msg)\n\t}\n}\n\n// Fires the \"OnMessage\" event. The 'inbound' variables determines if the\n// message is inbound or outgoing\nfunc (ce *ServerEvents) Message(msg Message, inbound bool) {\n\tfor _, fn := range ce.evtFnMessage {\n\t\tfn(msg, inbound)\n\t}\n}\n\n// Fires the \"OnBlockMessage\" event. The 'inbound' variables determines if the\n// message is inbound or outgoing\nfunc (ce *ServerEvents) BlockMessage(msg Message, inbound bool) {\n\tfor _, fn := range ce.evtFnBlockMessage {\n\t\tfn(msg, inbound)\n\t}\n}\n"
  },
  {
    "path": "event_test.go",
    "content": "package canopus\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestEvents(t *testing.T) {\n\tevents := NewEvents()\n\n\tassert.NotNil(t, events)\n\n\t// OnNotify\n\tevtOnNotifyCalled := false\n\tevents.OnNotify(func(string, interface{}, Message) {\n\t\tevtOnNotifyCalled = true\n\t})\n\tevents.Notify(\"/test\", \"\", nil)\n\n\t// OnStarted\n\tevtOnStartedCalled := false\n\tevents.OnStart(func(CoapServer) {\n\t\tevtOnStartedCalled = true\n\t})\n\tevents.Started(nil)\n\n\t// OnClosed\n\tevtOnClosedCalled := false\n\tevents.OnClose(func(CoapServer) {\n\t\tevtOnClosedCalled = true\n\t})\n\tevents.Closed(nil)\n\n\t// OnDiscover\n\tevtOnDiscoverCalled := false\n\tevents.OnDiscover(func() {\n\t\tevtOnDiscoverCalled = true\n\t})\n\tevents.Discover()\n\n\t// OnError\n\tevtOnErrorCalled := false\n\tevents.OnError(func(error) {\n\t\tevtOnErrorCalled = true\n\t})\n\tevents.Error(errors.New(\"An error occured\"))\n\n\t// OnObserve\n\tevtOnObserveCalled := false\n\tevents.OnObserve(func(string, Message) {\n\t\tevtOnObserveCalled = true\n\t})\n\tevents.Observe(\"/test\", nil)\n\n\t// OnObserveCancelled\n\tevtOnObserveCancelledCalled := false\n\tevents.OnObserveCancel(func(string, Message) {\n\t\tevtOnObserveCancelledCalled = true\n\t})\n\tevents.ObserveCancelled(\"/test\", nil)\n\n\t// OnMessage\n\tevtOnMessageCalled := false\n\tevents.OnMessage(func(Message, bool) {\n\t\tevtOnMessageCalled = true\n\t})\n\tevents.Message(nil, true)\n\n\ttime.Sleep(3000)\n\n\tassert.True(t, evtOnNotifyCalled)\n\tassert.True(t, evtOnStartedCalled)\n\tassert.True(t, evtOnClosedCalled)\n\tassert.True(t, evtOnDiscoverCalled)\n\tassert.True(t, evtOnErrorCalled)\n\tassert.True(t, evtOnObserveCalled)\n\tassert.True(t, evtOnObserveCancelledCalled)\n\tassert.True(t, evtOnMessageCalled)\n}\n"
  },
  {
    "path": "examples/block1/client.go",
    "content": "package main\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\n\tfile, err := ioutil.ReadFile(\"./ietf-block.htm\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Post)\n\tblockOpt := canopus.NewBlock1Option(canopus.BlockSize16, true, 0)\n\n\treq.GetMessage().SetBlock1Option(blockOpt)\n\treq.SetRequestURI(\"/blockupload\")\n\treq.SetPayload(file)\n\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tlog.Println(err)\n\t} else {\n\t\tlog.Println(\"Got Response:\")\n\t\tlog.Println(resp.GetMessage().GetPayload().String())\n\t}\n}\n"
  },
  {
    "path": "examples/block1/ietf-block.htm",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head profile=\"http://dublincore.org/documents/2008/08/04/dc-html/\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <meta name=\"robots\" content=\"index,follow\" />\n    <meta name=\"creator\" content=\"rfcmarkup version 1.118\" />\n    <link rel=\"schema.DC\" href=\"http://purl.org/dc/elements/1.1/\" />\n<meta name=\"DC.Relation.Replaces\" content=\"draft-bormann-core-coap-block\" />\n<meta name=\"DC.Identifier\" content=\"urn:ietf:id:ietf-core-block\" />\n<meta name=\"DC.Date.Issued\" content=\"2016-04-29\" />\n<meta name=\"DC.Creator\" content=\"Shelby, Zach\" />\n<meta name=\"DC.Creator\" content=\"Bormann, Carsten\" />\n<meta name=\"DC.Description.Abstract\" content=\"CoAP is a RESTful transfer protocol for constrained nodes and\nnetworks. Basic CoAP messages work well for the small payloads we\nexpect from temperature sensors, light switches, and similar building-\nautomation devices. Occasionally, however, applications will need to\ntransfer larger payloads -- for instance, for firmware updates. With\nHTTP, TCP does the grunt work of slicing large payloads up into\nmultiple packets and ensuring that they all arrive and are handled in\nthe right order. CoAP is based on datagram transports such as UDP or\nDTLS, which limits the maximum size of resource representations that\ncan be transferred without too much fragmentation. Although UDP\nsupports larger payloads through IP fragmentation, it is limited to 64\nKiB and, more importantly, doesn't really work well for constrained\napplications and networks. Instead of relying on IP fragmentation,\nthis specification extends basic CoAP with a pair of &quot;Block&quot; options,\nfor transferring multiple blocks of information from a resource\nrepresentation in multiple request-response pairs. In many important\ncases, the Block options enable a server to be truly stateless: the\nserver can handle each block transfer separately, with no need for a\nconnection setup or other server-side memory of previous block\ntransfers. In summary, the Block options provide a minimal way to\ntransfer larger representations in a block-wise fashion.\" />\n<meta name=\"DC.Title\" content=\"Block-wise transfers in CoAP\" />\n\n    <link rel=\"icon\" href=\"/images/id.png\" type=\"image/png\" />\n    <link rel=\"shortcut icon\" href=\"/images/id.png\" type=\"image/png\" />\n    <title>draft-ietf-core-block-15 - Block-wise transfers in CoAP</title>\n    \n    \n    <style type=\"text/css\">\n\t@media only screen \n\t  and (min-width: 992px)\n\t  and (max-width: 1199px) {\n\t    body { font-size: 14pt; }\n            div.content { width: 96ex; margin: 0 auto; }\n        }\n\t@media only screen \n\t  and (min-width: 768px)\n\t  and (max-width: 991px) {\n            body { font-size: 14pt; }\n            div.content { width: 96ex; margin: 0 auto; }\n        }\n\t@media only screen \n\t  and (min-width: 480px)\n\t  and (max-width: 767px) {\n            body { font-size: 11pt; }\n            div.content { width: 96ex; margin: 0 auto; }\n        }\n\t@media only screen \n\t  and (max-width: 479px) {\n            body { font-size: 8pt; }\n            div.content { width: 96ex; margin: 0 auto; }\n        }\n\t@media only screen \n\t  and (min-device-width : 375px) \n\t  and (max-device-width : 667px) {\n            body { font-size: 9.5pt; }\n            div.content { width: 96ex; margin: 0 1px; }\n        }\n\t@media only screen \n\t  and (min-device-width: 1200px) {\n            body { font-size: 10pt; margin: 0 4em; }\n            div.content { width: 96ex; margin: 0; }\n        }\n        h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {\n\t    font-weight: bold;\n            line-height: 0pt;\n            display: inline;\n            white-space: pre;\n            font-family: monospace;\n            font-size: 1em;\n\t    font-weight: bold;\n        }\n        pre {\n            font-size: 1em;\n            margin-top: 0px;\n            margin-bottom: 0px;\n        }\n\t.pre {\n\t    white-space: pre;\n\t    font-family: monospace;\n\t}\n\t.header{\n\t    font-weight: bold;\n\t}\n        .newpage {\n            page-break-before: always;\n        }\n        .invisible {\n            text-decoration: none;\n            color: white;\n        }\n        a.selflink {\n          color: black;\n          text-decoration: none;\n        }\n        @media print {\n            body {\n                font-family: monospace;\n                font-size: 10.5pt;\n            }\n            h1, h2, h3, h4, h5, h6 {\n                font-size: 1em;\n            }\n        \n            a:link, a:visited {\n                color: inherit;\n                text-decoration: none;\n            }\n            .noprint {\n                display: none;\n            }\n        }\n\t@media screen {\n\t    .grey, .grey a:link, .grey a:visited {\n\t\tcolor: #777;\n\t    }\n            .docinfo {\n                background-color: #EEE;\n            }\n            .top {\n                border-top: 7px solid #EEE;\n            }\n            .bgwhite  { background-color: white; }\n            .bgred    { background-color: #F44; }\n            .bggrey   { background-color: #666; }\n            .bgbrown  { background-color: #840; }            \n            .bgorange { background-color: #FA0; }\n            .bgyellow { background-color: #EE0; }\n            .bgmagenta{ background-color: #F4F; }\n            .bgblue   { background-color: #66F; }\n            .bgcyan   { background-color: #4DD; }\n            .bggreen  { background-color: #4F4; }\n\n            .legend   { font-size: 90%; }\n            .cplate   { font-size: 70%; border: solid grey 1px; }\n\t}\n    </style>\n    <!--[if IE]>\n    <style>\n    body {\n       font-size: 13px;\n       margin: 10px 10px;\n    }\n    </style>\n    <![endif]-->\n\n    <script type=\"text/javascript\"><!--\n    function addHeaderTags() {\n\tvar spans = document.getElementsByTagName(\"span\");\n\tfor (var i=0; i < spans.length; i++) {\n\t    var elem = spans[i];\n\t    if (elem) {\n\t\tvar level = elem.getAttribute(\"class\");\n                if (level == \"h1\" || level == \"h2\" || level == \"h3\" || level == \"h4\" || level == \"h5\" || level == \"h6\") {\n                    elem.innerHTML = \"<\"+level+\">\"+elem.innerHTML+\"</\"+level+\">\";\t\t\n                }\n\t    }\n\t}\n    }\n    var legend_html = \"Colour legend:<br />      <table>         <tr><td>Unknown:</td>                   <td><span class='cplate bgwhite'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Draft:</td>                     <td><span class='cplate bgred'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Informational:</td>             <td><span class='cplate bgorange'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Experimental:</td>              <td><span class='cplate bgyellow'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Best Common Practice:</td>      <td><span class='cplate bgmagenta'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Proposed Standard:</td>         <td><span class='cplate bgblue'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Draft Standard (old designation):</td> <td><span class='cplate bgcyan'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Internet Standard:</td>         <td><span class='cplate bggreen'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Historic:</td>                  <td><span class='cplate bggrey'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Obsolete:</td>                  <td><span class='cplate bgbrown'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>     </table>\";\n    function showElem(id) {\n        var elem = document.getElementById(id);\n        elem.innerHTML = eval(id+\"_html\");\n        elem.style.visibility='visible';\n    }\n    function hideElem(id) {\n        var elem = document.getElementById(id);\n        elem.style.visibility='hidden';        \n        elem.innerHTML = \"\";\n    }\n    // -->\n    </script>\n</head>\n<body onload=\"addHeaderTags()\">\n  <div class=\"content\">\n   <div style=\"height: 13px;\">\n      <div onmouseover=\"this.style.cursor='pointer';\"\n         onclick=\"showElem('legend');\"\n         onmouseout=\"hideElem('legend')\"\n\t style=\"height: 6px; position: absolute;\"\n         class=\"pre noprint docinfo bgred\"\n         title=\"Click for colour legend.\" >                                                                        </div>\n      <div id=\"legend\"\n           class=\"docinfo noprint pre legend\"\n           style=\"position:absolute; top: 4px; left: 4ex; visibility:hidden; background-color: white; padding: 4px 9px 5px 7px; border: solid #345 1px; \"\n           onmouseover=\"showElem('legend');\"\n           onmouseout=\"hideElem('legend');\">\n      </div>\n   </div>\n<span class=\"pre noprint docinfo top\">[<a href=\"../html/\" title=\"Document search and retrieval page\">Docs</a>] [<a href=\"https://tools.ietf.org/id/draft-ietf-core-block-15.txt\" title=\"Plaintext version of this document\">txt</a>|<a href=\"/pdf/draft-ietf-core-block-15.txt\" title=\"PDF version of this document\">pdf</a>] [<a href='https://datatracker.ietf.org/doc/draft-ietf-core-block' title='IESG Datatracker information for this document'>Tracker</a>] [<a href=\"../wg/core\" title=\"The working group handling this document\">WG</a>] [<a href=\"mailto:draft-ietf-core-block@tools.ietf.org?subject=draft-ietf-core-block%20\" title=\"Send email to the document authors\">Email</a>] [<a href=\"/rfcdiff?difftype=--hwdiff&amp;url2=draft-ietf-core-block-15.txt\" title=\"Inline diff (wdiff)\">Diff1</a>] [<a href=\"/rfcdiff?url2=draft-ietf-core-block-15.txt\" title=\"Side-by-side diff\">Diff2</a>] [<a href=\"/idnits?url=https://tools.ietf.org/id/draft-ietf-core-block-15.txt\" title=\"Run an idnits check of this document\">Nits</a>]          </span><br />\n<span class=\"pre noprint docinfo\">                                                                        </span><br />\n<span class=\"pre noprint docinfo\">Versions: (<a href=\"./draft-bormann-core-coap-block\" title=\"Precursor\">draft-bormann-core-coap-block</a>)  <a href=\"./draft-ietf-core-block-00\">00</a>                           \n          <a href=\"./draft-ietf-core-block-01\">01</a> <a href=\"./draft-ietf-core-block-02\">02</a> <a href=\"./draft-ietf-core-block-03\">03</a> <a href=\"./draft-ietf-core-block-04\">04</a> <a href=\"./draft-ietf-core-block-05\">05</a> <a href=\"./draft-ietf-core-block-06\">06</a> <a href=\"./draft-ietf-core-block-07\">07</a> <a href=\"./draft-ietf-core-block-08\">08</a> <a href=\"./draft-ietf-core-block-09\">09</a> <a href=\"./draft-ietf-core-block-10\">10</a> <a href=\"./draft-ietf-core-block-11\">11</a> <a href=\"./draft-ietf-core-block-12\">12</a>                           \n          <a href=\"./draft-ietf-core-block-13\">13</a> <a href=\"./draft-ietf-core-block-14\">14</a> <a href=\"./draft-ietf-core-block-15\">15</a> <a href=\"./draft-ietf-core-block-16\">16</a> <a href=\"./draft-ietf-core-block-17\">17</a> <a href=\"./draft-ietf-core-block-18\">18</a> <a href=\"./draft-ietf-core-block-19\">19</a> <a href=\"./draft-ietf-core-block-20\">20</a>                                       </span><br />\n<span class=\"pre noprint docinfo\">                                                                        </span><br />\n<pre>\nCoRE Working Group                                            C. Bormann\nInternet-Draft                                   Universitaet Bremen TZI\nIntended status: Standards Track                          Z. Shelby, Ed.\nExpires: January 5, 2015                                             ARM\n                                                           July 04, 2014\n\n\n                      <span class=\"h1\">Blockwise transfers in CoAP</span>\n                        <span class=\"h1\">draft-ietf-core-block-15</span>\n\nAbstract\n\n   CoAP is a RESTful transfer protocol for constrained nodes and\n   networks.  Basic CoAP messages work well for the small payloads we\n   expect from temperature sensors, light switches, and similar\n   building-automation devices.  Occasionally, however, applications\n   will need to transfer larger payloads -- for instance, for firmware\n   updates.  With HTTP, TCP does the grunt work of slicing large\n   payloads up into multiple packets and ensuring that they all arrive\n   and are handled in the right order.\n\n   CoAP is based on datagram transports such as UDP or DTLS, which\n   limits the maximum size of resource representations that can be\n   transferred without too much fragmentation.  Although UDP supports\n   larger payloads through IP fragmentation, it is limited to 64 KiB\n   and, more importantly, doesn't really work well for constrained\n   applications and networks.\n\n   Instead of relying on IP fragmentation, this specification extends\n   basic CoAP with a pair of \"Block\" options, for transferring multiple\n   blocks of information from a resource representation in multiple\n   request-response pairs.  In many important cases, the Block options\n   enable a server to be truly stateless: the server can handle each\n   block transfer separately, with no need for a connection setup or\n   other server-side memory of previous block transfers.\n\n   In summary, the Block options provide a minimal way to transfer\n   larger representations in a block-wise fashion.\n\nStatus of This Memo\n\n   This Internet-Draft is submitted in full conformance with the\n   provisions of <a href=\"./bcp78\">BCP 78</a> and <a href=\"./bcp79\">BCP 79</a>.\n\n   Internet-Drafts are working documents of the Internet Engineering\n   Task Force (IETF).  Note that other groups may also distribute\n   working documents as Internet-Drafts.  The list of current Internet-\n   Drafts is at <a href=\"http://datatracker.ietf.org/drafts/current/\">http://datatracker.ietf.org/drafts/current/</a>.\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 1]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-2\" id=\"page-2\" href=\"#page-2\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   Internet-Drafts are draft documents valid for a maximum of six months\n   and may be updated, replaced, or obsoleted by other documents at any\n   time.  It is inappropriate to use Internet-Drafts as reference\n   material or to cite them other than as \"work in progress.\"\n\n   This Internet-Draft will expire on January 5, 2015.\n\nCopyright Notice\n\n   Copyright (c) 2014 IETF Trust and the persons identified as the\n   document authors.  All rights reserved.\n\n   This document is subject to <a href=\"./bcp78\">BCP 78</a> and the IETF Trust's Legal\n   Provisions Relating to IETF Documents\n   (<a href=\"http://trustee.ietf.org/license-info\">http://trustee.ietf.org/license-info</a>) in effect on the date of\n   publication of this document.  Please review these documents\n   carefully, as they describe your rights and restrictions with respect\n   to this document.  Code Components extracted from this document must\n   include Simplified BSD License text as described in Section 4.e of\n   the Trust Legal Provisions and are provided without warranty as\n   described in the Simplified BSD License.\n\nTable of Contents\n\n   <a href=\"#section-1\">1</a>.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   <a href=\"#page-3\">3</a>\n   <a href=\"#section-2\">2</a>.  Block-wise transfers  . . . . . . . . . . . . . . . . . . . .   <a href=\"#page-5\">5</a>\n     <a href=\"#section-2.1\">2.1</a>.  The Block2 and Block1 Options . . . . . . . . . . . . . .   <a href=\"#page-5\">5</a>\n     <a href=\"#section-2.2\">2.2</a>.  Structure of a Block Option . . . . . . . . . . . . . . .   <a href=\"#page-6\">6</a>\n     <a href=\"#section-2.3\">2.3</a>.  Block Options in Requests and Responses . . . . . . . . .   <a href=\"#page-8\">8</a>\n     <a href=\"#section-2.4\">2.4</a>.  Using the Block2 Option . . . . . . . . . . . . . . . . .  <a href=\"#page-10\">10</a>\n     <a href=\"#section-2.5\">2.5</a>.  Using the Block1 Option . . . . . . . . . . . . . . . . .  <a href=\"#page-11\">11</a>\n     <a href=\"#section-2.6\">2.6</a>.  Combining Blockwise Transfers with the Observe Option . .  <a href=\"#page-12\">12</a>\n     <a href=\"#section-2.7\">2.7</a>.  Combining Block1 and Block2 . . . . . . . . . . . . . . .  <a href=\"#page-13\">13</a>\n     <a href=\"#section-2.8\">2.8</a>.  Combining Block2 with Multicast . . . . . . . . . . . . .  <a href=\"#page-13\">13</a>\n     <a href=\"#section-2.9\">2.9</a>.  Response Codes  . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-14\">14</a>\n       <a href=\"#section-2.9.1\">2.9.1</a>.  2.31 Continue . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-14\">14</a>\n       <a href=\"#section-2.9.2\">2.9.2</a>.  4.08 Request Entity Incomplete  . . . . . . . . . . .  <a href=\"#page-14\">14</a>\n       <a href=\"#section-2.9.3\">2.9.3</a>.  4.13 Request Entity Too Large . . . . . . . . . . . .  <a href=\"#page-14\">14</a>\n   <a href=\"#section-3\">3</a>.  Examples  . . . . . . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-15\">15</a>\n     <a href=\"#section-3.1\">3.1</a>.  Block2 Examples . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-15\">15</a>\n     <a href=\"#section-3.2\">3.2</a>.  Block1 Examples . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-19\">19</a>\n     <a href=\"#section-3.3\">3.3</a>.  Combining Block1 and Block2 . . . . . . . . . . . . . . .  <a href=\"#page-20\">20</a>\n     <a href=\"#section-3.4\">3.4</a>.  Combining Observe and Block2  . . . . . . . . . . . . . .  <a href=\"#page-22\">22</a>\n   <a href=\"#section-4\">4</a>.  The Size2 and Size1 Options . . . . . . . . . . . . . . . . .  <a href=\"#page-25\">25</a>\n   <a href=\"#section-5\">5</a>.  HTTP Mapping Considerations . . . . . . . . . . . . . . . . .  <a href=\"#page-26\">26</a>\n   <a href=\"#section-6\">6</a>.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-27\">27</a>\n   <a href=\"#section-7\">7</a>.  Security Considerations . . . . . . . . . . . . . . . . . . .  <a href=\"#page-28\">28</a>\n     <a href=\"#section-7.1\">7.1</a>.  Mitigating Resource Exhaustion Attacks  . . . . . . . . .  <a href=\"#page-29\">29</a>\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 2]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-3\" id=\"page-3\" href=\"#page-3\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n     <a href=\"#section-7.2\">7.2</a>.  Mitigating Amplification Attacks  . . . . . . . . . . . .  <a href=\"#page-29\">29</a>\n   <a href=\"#section-8\">8</a>.  Acknowledgements  . . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-30\">30</a>\n   <a href=\"#section-9\">9</a>.  References  . . . . . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-30\">30</a>\n     <a href=\"#section-9.1\">9.1</a>.  Normative References  . . . . . . . . . . . . . . . . . .  <a href=\"#page-30\">30</a>\n     <a href=\"#section-9.2\">9.2</a>.  Informative References  . . . . . . . . . . . . . . . . .  <a href=\"#page-30\">30</a>\n   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  <a href=\"#page-31\">31</a>\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-1\" href=\"#section-1\">1</a>.  Introduction</span>\n\n   The work on Constrained RESTful Environments (CoRE) aims at realizing\n   the REST architecture in a suitable form for the most constrained\n   nodes (such as microcontrollers with limited RAM and ROM [<a href=\"./rfc7228\" title=\"&quot;Terminology for Constrained-Node Networks&quot;\">RFC7228</a>])\n   and networks (such as 6LoWPAN, [<a href=\"./rfc4944\" title=\"&quot;Transmission of IPv6 Packets over IEEE 802.15.4 Networks&quot;\">RFC4944</a>]) [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>].  The CoAP\n   protocol is intended to provide RESTful [<a href=\"#ref-REST\" title=\"&quot;Architectural Styles and the Design of Network-based Software Architectures&quot;\">REST</a>] services not unlike\n   HTTP [<a href=\"./rfc7230\" title=\"&quot;Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing&quot;\">RFC7230</a>], while reducing the complexity of implementation as\n   well as the size of packets exchanged in order to make these services\n   useful in a highly constrained network of themselves highly\n   constrained nodes.\n\n   This objective requires restraint in a number of sometimes\n   conflicting ways:\n\n   o  reducing implementation complexity in order to minimize code size,\n\n   o  reducing message sizes in order to minimize the number of\n      fragments needed for each message (in turn to maximize the\n      probability of delivery of the message), the amount of\n      transmission power needed and the loading of the limited-bandwidth\n      channel,\n\n   o  reducing requirements on the environment such as stable storage,\n      good sources of randomness or user interaction capabilities.\n\n   CoAP is based on datagram transports such as UDP, which limit the\n   maximum size of resource representations that can be transferred\n   without creating unreasonable levels of IP fragmentation.  In\n   addition, not all resource representations will fit into a single\n   link layer packet of a constrained network, which may cause\n   adaptation layer fragmentation even if IP layer fragmentation is not\n   required.  Using fragmentation (either at the adaptation layer or at\n   the IP layer) for the transport of larger representations would be\n   possible up to the maximum size of the underlying datagram protocol\n   (such as UDP), but the fragmentation/reassembly process burdens the\n   lower layers with conversation state that is better managed in the\n   application layer.\n\n   The present specification defines a pair of CoAP options to enable\n   _block-wise_ access to resource representations.  The Block options\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 3]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-4\" id=\"page-4\" href=\"#page-4\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   provide a minimal way to transfer larger resource representations in\n   a block-wise fashion.  The overriding objective is to avoid the need\n   for creating conversation state at the server for block-wise GET\n   requests.  (It is impossible to fully avoid creating conversation\n   state for POST/PUT, if the creation/replacement of resources is to be\n   atomic; where that property is not needed, there is no need to create\n   server conversation state in this case, either.)\n\n   In summary, this specification adds a pair of Block options to CoAP\n   that can be used for block-wise transfers.  Benefits of using these\n   options include:\n\n   o  Transfers larger than what can be accommodated in constrained-\n      network link-layer packets can be performed in smaller blocks.\n\n   o  No hard-to-manage conversation state is created at the adaptation\n      layer or IP layer for fragmentation.\n\n   o  The transfer of each block is acknowledged, enabling individual\n      retransmission if required.\n\n   o  Both sides have a say in the block size that actually will be\n      used.\n\n   o  The resulting exchanges are easy to understand using packet\n      analyzer tools and thus quite accessible to debugging.\n\n   o  If needed, the Block options can also be used (without changes) to\n      provide random access to power-of-two sized blocks within a\n      resource representation.\n\n   The key words \"MUST\", \"MUST NOT\", \"REQUIRED\", \"SHALL\", \"SHALL NOT\",\n   \"SHOULD\", \"SHOULD NOT\", \"RECOMMENDED\", \"NOT RECOMMENDED\", \"MAY\", and\n   \"OPTIONAL\" in this document are to be interpreted as described in <a href=\"./rfc2119\">RFC</a>\n   <a href=\"./rfc2119\">2119</a>, <a href=\"./bcp14\">BCP 14</a> [<a href=\"./rfc2119\" title=\"&quot;Key words for use in RFCs to Indicate Requirement Levels&quot;\">RFC2119</a>] and indicate requirement levels for compliant\n   CoAP implementations.\n\n   In this document, the term \"byte\" is used in its now customary sense\n   as a synonym for \"octet\".\n\n   Where bit arithmetic is explained, this document uses the notation\n   familiar from the programming language C, except that the operator\n   \"**\" stands for exponentiation.\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 4]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-5\" id=\"page-5\" href=\"#page-5\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-2\" href=\"#section-2\">2</a>.  Block-wise transfers</span>\n\n   As discussed in the introduction, there are good reasons to limit the\n   size of datagrams in constrained networks:\n\n   o  by the maximum datagram size (~ 64 KiB for UDP)\n\n   o  by the desire to avoid IP fragmentation (MTU of 1280 for IPv6)\n\n   o  by the desire to avoid adaptation layer fragmentation (60-80 bytes\n      for 6LoWPAN [<a href=\"./rfc4919\" title=\"&quot;IPv6 over Low-Power Wireless Personal Area Networks (6LoWPANs): Overview, Assumptions, Problem Statement, and Goals&quot;\">RFC4919</a>])\n\n   When a resource representation is larger than can be comfortably\n   transferred in the payload of a single CoAP datagram, a Block option\n   can be used to indicate a block-wise transfer.  As payloads can be\n   sent both with requests and with responses, this specification\n   provides two separate options for each direction of payload transfer.\n   In identifying these options, we use the number 1 to refer to the\n   transfer of the resource representation that pertains to the request,\n   and the number 2 to refer to the transfer of the resource\n   representation for the response.\n\n   In the following, the term \"payload\" will be used for the actual\n   content of a single CoAP message, i.e. a single block being\n   transferred, while the term \"body\" will be used for the entire\n   resource representation that is being transferred in a block-wise\n   fashion.  The Content-Format option applies to the body, not to the\n   payload, in particular the boundaries between the blocks may be in\n   places that are not separating whole units in terms of the structure,\n   encoding, or content-coding used by the Content-Format.\n\n   In most cases, all blocks being transferred for a body (except for\n   the last one) will be of the same size.  The block size is not fixed\n   by the protocol.  To keep the implementation as simple as possible,\n   the Block options support only a small range of power-of-two block\n   sizes, from 2**4 (16) to 2**10 (1024) bytes.  As bodies often will\n   not evenly divide into the power-of-two block size chosen, the size\n   need not be reached in the final block (but even for the final block,\n   the chosen power-of-two size will still be indicated in the block\n   size field of the Block option).\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.1\" href=\"#section-2.1\">2.1</a>.  The Block2 and Block1 Options</span>\n\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 5]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-6\" id=\"page-6\" href=\"#page-6\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n       +-----+---+---+---+---+--------+--------+--------+---------+\n       | No. | C | U | N | R | Name   | Format | Length | Default |\n       +-----+---+---+---+---+--------+--------+--------+---------+\n       |  23 | C | U | - | - | Block2 | uint   |    0-3 | (none)  |\n       |     |   |   |   |   |        |        |        |         |\n       |  27 | C | U | - | - | Block1 | uint   |    0-3 | (none)  |\n       +-----+---+---+---+---+--------+--------+--------+---------+\n\n                       Table 1: Block Option Numbers\n\n   Both Block1 and Block2 options can be present both in request and\n   response messages.  In either case, the Block1 Option pertains to the\n   request payload, and the Block2 Option pertains to the response\n   payload.\n\n   Hence, for the methods defined in [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>], Block1 is useful with\n   the payload-bearing POST and PUT requests and their responses.\n   Block2 is useful with GET, POST, and PUT requests and their payload-\n   bearing responses (2.01, 2.02, 2.04, 2.05 -- see section \"Payload\" of\n   [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>]).\n\n   Where Block1 is present in a request or Block2 in a response (i.e.,\n   in that message to the payload of which it pertains) it indicates a\n   block-wise transfer and describes how this specific block-wise\n   payload forms part of the entire body being transferred (\"descriptive\n   usage\").  Where it is present in the opposite direction, it provides\n   additional control on how that payload will be formed or was\n   processed (\"control usage\").\n\n   Implementation of either Block option is intended to be optional.\n   However, when it is present in a CoAP message, it MUST be processed\n   (or the message rejected); therefore it is identified as a critical\n   option.  It MUST NOT occur more than once.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.2\" href=\"#section-2.2\">2.2</a>.  Structure of a Block Option</span>\n\n   Three items of information may need to be transferred in a Block\n   (Block1 or Block2) option:\n\n   o  The size of the block (SZX);\n\n   o  whether more blocks are following (M);\n\n   o  the relative number of the block (NUM) within a sequence of blocks\n      with the given size.\n\n   The value of the Block Option is a variable-size (0 to 3 byte)\n   unsigned integer (uint, see <a href=\"./rfc7252#section-3.2\">Section&nbsp;3.2 of [RFC7252]</a>).  This integer\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 6]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-7\" id=\"page-7\" href=\"#page-7\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   value encodes these three fields, see Figure 1.  (Due to the CoAP\n   uint encoding rules, when all of NUM, M, and SZX happen to be zero, a\n   zero-byte integer will be sent.)\n\n           0\n           0 1 2 3 4 5 6 7\n          +-+-+-+-+-+-+-+-+\n          |  NUM  |M| SZX |\n          +-+-+-+-+-+-+-+-+\n\n           0                   1\n           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5\n          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n          |          NUM          |M| SZX |\n          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n           0                   1                   2\n           0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3\n          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n          |                   NUM                 |M| SZX |\n          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n                       Figure 1: Block option value\n\n   The block size is encoded using a three-bit unsigned integer (0 for\n   2**4 to 6 for 2**10 bytes), which we call the \"SZX\" (\"size\n   exponent\"); the actual block size is then \"2**(SZX + 4)\".  SZX is\n   transferred in the three least significant bits of the option value\n   (i.e., \"val &amp; 7\" where \"val\" is the value of the option).\n\n   The fourth least significant bit, the M or \"more\" bit (\"val &amp; 8\"),\n   indicates whether more blocks are following or the current block-wise\n   transfer is the last block being transferred.\n\n   The option value divided by sixteen (the NUM field) is the sequence\n   number of the block currently being transferred, starting from zero.\n   The current transfer is therefore about the \"size\" bytes starting at\n   byte \"NUM &lt;&lt; (SZX + 4)\".\n\n   Implementation note:  As an implementation convenience, \"(val &amp; ~0xF)\n      &lt;&lt; (val &amp; 7)\", i.e., the option value with the last 4 bits masked\n      out, shifted to the left by the value of SZX, gives the byte\n      position of the first byte of the block being transferred.\n\n   More specifically, within the option value of a Block1 or Block2\n   Option, the meaning of the option fields is defined as follows:\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 7]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-8\" id=\"page-8\" href=\"#page-8\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   NUM:  Block Number, indicating the block number being requested or\n      provided.  Block number 0 indicates the first block of a body\n      (i.e., starting with the first byte of the body).\n\n   M: More Flag (\"not last block\").  For descriptive usage, this flag,\n      if unset, indicates that the payload in this message is the last\n      block in the body; when set it indicates that there are one or\n      more additional blocks available.  When a Block2 Option is used in\n      a request to retrieve a specific block number (\"control usage\"),\n      the M bit MUST be sent as zero and ignored on reception.  (In a\n      Block1 Option in a response, the M flag is used to indicate\n      atomicity, see below.)\n\n   SZX:  Block Size.  The block size is represented as three-bit\n      unsigned integer indicating the size of a block to the power of\n      two.  Thus block size = 2**(SZX + 4).  The allowed values of SZX\n      are 0 to 6, i.e., the minimum block size is 2**(0+4) = 16 and the\n      maximum is 2**(6+4) = 1024.  The value 7 for SZX (which would\n      indicate a block size of 2048) is reserved, i.e.  MUST NOT be sent\n      and MUST lead to a 4.00 Bad Request response code upon reception\n      in a request.\n\n   There is no default value for the Block1 and Block2 Options.  Absence\n   of one of these options is equivalent to an option value of 0 with\n   respect to the value of NUM and M that could be given in the option,\n   i.e. it indicates that the current block is the first and only block\n   of the transfer (block number 0, M bit not set).  However, in\n   contrast to the explicit value 0, which would indicate an SZX of 0\n   and thus a size value of 16 bytes, there is no specific explicit size\n   implied by the absence of the option -- the size is left unspecified.\n   (As for any uint, the explicit value 0 is efficiently indicated by a\n   zero-length option; this, therefore, is different in semantics from\n   the absence of the option.)\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.3\" href=\"#section-2.3\">2.3</a>.  Block Options in Requests and Responses</span>\n\n   The Block options are used in one of three roles:\n\n   o  In descriptive usage, i.e., a Block2 Option in a response (such as\n      a 2.05 response for GET), or a Block1 Option in a request (a PUT\n      or POST):\n\n      *  The NUM field in the option value describes what block number\n         is contained in the payload of this message.\n\n      *  The M bit indicates whether further blocks need to be\n         transferred to complete the transfer of that body.\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 8]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-9\" id=\"page-9\" href=\"#page-9\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n      *  The block size implied by SZX MUST match the size of the\n         payload in bytes, if the M bit is set.  (SZX does not govern\n         the payload size if M is unset).  For Block2, if the request\n         suggested a larger value of SZX, the next request MUST move SZX\n         down to the size given in the response.  (The effect is that,\n         if the server uses the smaller of (1) its preferred block size\n         and (2) the block size requested, all blocks for a body use the\n         same block size.)\n\n   o  A Block2 Option in control usage in a request (e.g., GET):\n\n      *  The NUM field in the Block2 Option gives the block number of\n         the payload that is being requested to be returned in the\n         response.\n\n      *  In this case, the M bit has no function and MUST be set to\n         zero.\n\n      *  The block size given (SZX) suggests a block size (in the case\n         of block number 0) or repeats the block size of previous blocks\n         received (in the case of a non-zero block number).\n\n   o  A Block1 Option in control usage in a response (e.g., a 2.xx\n      response for a PUT or POST request):\n\n      *  The NUM field of the Block1 Option indicates what block number\n         is being acknowledged.\n\n      *  If the M bit was set in the request, the server can choose\n         whether to act on each block separately, with no memory, or\n         whether to handle the request for the entire body atomically,\n         or any mix of the two.\n\n         +  If the M bit is also set in the response, it indicates that\n            this response does not carry the final response code to the\n            request, i.e. the server collects further blocks from the\n            same endpoint and plans to implement the request atomically\n            (e.g., acts only upon reception of the last block of\n            payload).  In this case, the response MUST NOT carry a\n            Block2 option.\n\n         +  Conversely, if the M bit is unset even though it was set in\n            the request, it indicates the block-wise request was enacted\n            now specifically for this block, and the response carries\n            the final response to this request (and to any previous ones\n            with the M bit set in the response's Block1 Option in this\n            sequence of block-wise transfers); the client is still\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015                [Page 9]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-10\" id=\"page-10\" href=\"#page-10\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n            expected to continue sending further blocks, the request\n            method for which may or may not also be enacted per-block.\n\n      *  Finally, the SZX block size given in a control Block1 Option\n         indicates the largest block size preferred by the server for\n         transfers toward the resource that is the same or smaller than\n         the one used in the initial exchange; the client SHOULD use\n         this block size or a smaller one in all further requests in the\n         transfer sequence, even if that means changing the block size\n         (and possibly scaling the block number accordingly) from now\n         on.\n\n   Using one or both Block options, a single REST operation can be split\n   into multiple CoAP message exchanges.  As specified in [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>],\n   each of these message exchanges uses their own CoAP Message ID.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.4\" href=\"#section-2.4\">2.4</a>.  Using the Block2 Option</span>\n\n   When a request is answered with a response carrying a Block2 Option\n   with the M bit set, the requester may retrieve additional blocks of\n   the resource representation by sending further requests with the same\n   options as the initial request and a Block2 Option giving the block\n   number and block size desired.  In a request, the client MUST set the\n   M bit of a Block2 Option to zero and the server MUST ignore it on\n   reception.\n\n   To influence the block size used in a response, the requester MAY\n   also use the Block2 Option on the initial request, giving the desired\n   size, a block number of zero and an M bit of zero.  A server MUST use\n   the block size indicated or a smaller size.  Any further block-wise\n   requests for blocks beyond the first one MUST indicate the same block\n   size that was used by the server in the response for the first\n   request that gave a desired size using a Block2 Option.\n\n   Once the Block2 Option is used by the requester and a first response\n   has been received with a possibly adjusted block size, all further\n   requests in a single block-wise transfer SHOULD ultimately use the\n   same size, except that there may not be enough content to fill the\n   last block (the one returned with the M bit not set).  (Note that the\n   client may start using the Block2 Option in a second request after a\n   first request without a Block2 Option resulted in a Block2 option in\n   the response.)  The server SHOULD use the block size indicated in the\n   request option or a smaller size, but the requester MUST take note of\n   the actual block size used in the response it receives to its initial\n   request and proceed to use it in subsequent requests.  The server\n   behavior MUST ensure that this client behavior results in the same\n   block size for all responses in a sequence (except for the last one\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 10]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-11\" id=\"page-11\" href=\"#page-11\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   with the M bit not set, and possibly the first one if the initial\n   request did not contain a Block2 Option).\n\n   Block-wise transfers can be used to GET resources the representations\n   of which are entirely static (not changing over time at all, such as\n   in a schema describing a device), or for dynamically changing\n   resources.  In the latter case, the Block2 Option SHOULD be used in\n   conjunction with the ETag Option, to ensure that the blocks being\n   reassembled are from the same version of the representation: The\n   server SHOULD include an ETag option in each response.  If an ETag\n   option is available, the client's reassembler, when reassembling the\n   representation from the blocks being exchanged, MUST compare ETag\n   Options.  If the ETag Options do not match in a GET transfer, the\n   requester has the option of attempting to retrieve fresh values for\n   the blocks it retrieved first.  To minimize the resulting\n   inefficiency, the server MAY cache the current value of a\n   representation for an ongoing sequence of requests.  (The server may\n   identify the sequence by the combination of the requesting end-point\n   and the URI being the same in each block-wise request.)  Note well\n   that this specification makes no requirement for the server to\n   establish any state; however, servers that offer quickly changing\n   resources may thereby make it impossible for a client to ever\n   retrieve a consistent set of blocks.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.5\" href=\"#section-2.5\">2.5</a>.  Using the Block1 Option</span>\n\n   In a request with a request payload (e.g., PUT or POST), the Block1\n   Option refers to the payload in the request (descriptive usage).\n\n   In response to a request with a payload (e.g., a PUT or POST\n   transfer), the block size given in the Block1 Option indicates the\n   block size preference of the server for this resource (control\n   usage).  Obviously, at this point the first block has already been\n   transferred by the client without benefit of this knowledge.  Still,\n   the client SHOULD heed the preference indicated and, for all further\n   blocks, use the block size preferred by the server or a smaller one.\n   Note that any reduction in the block size may mean that the second\n   request starts with a block number larger than one, as the first\n   request already transferred multiple blocks as counted in the smaller\n   size.\n\n   To counter the effects of adaptation layer fragmentation on packet\n   delivery probability, a client may want to give up retransmitting a\n   request with a relatively large payload even before MAX_RETRANSMIT\n   has been reached, and try restating the request as a block-wise\n   transfer with a smaller payload.  Note that this new attempt is then\n   a new message-layer transaction and requires a new Message ID.\n   (Because of the uncertainty whether the request or the\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 11]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-12\" id=\"page-12\" href=\"#page-12\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   acknowledgement was lost, this strategy is useful mostly for\n   idempotent requests.)\n\n   In a blockwise transfer of a request payload (e.g., a PUT or POST)\n   that is intended to be implemented in an atomic fashion at the\n   server, the actual creation/replacement takes place at the time the\n   final block, i.e. a block with the M bit unset in the Block1 Option,\n   is received.  In this case, all success responses to non-final blocks\n   carry the response code 2.31 (Continue, <a href=\"#section-2.9.1\">Section 2.9.1</a>).  If not all\n   previous blocks are available at the server at the time of processing\n   the final block, the transfer fails and error code 4.08 (Request\n   Entity Incomplete, <a href=\"#section-2.9.2\">Section 2.9.2</a>) MUST be returned.  A server MAY\n   also return a 4.08 error code for any (final or non-final) Block1\n   transfer that is not in sequence; clients that do not have specific\n   mechanisms to handle this case therefore SHOULD always start with\n   block zero and send the following blocks in order.\n\n   The error code 4.13 (Request Entity Too Large) can be returned at any\n   time by a server that does not currently have the resources to store\n   blocks for a block-wise request payload transfer that it would intend\n   to implement in an atomic fashion.  (Note that a 4.13 response to a\n   request that does not employ Block1 is a hint for the client to try\n   sending Block1, and a 4.13 response with a smaller SZX in its Block1\n   option than requested is a hint to try a smaller SZX.)\n\n   The Block1 option provides no way for a single endpoint to perform\n   multiple concurrently proceeding block-wise request payload transfer\n   (e.g., PUT or POST) operations to the same resource.  Starting a new\n   block-wise sequence of requests to the same resource (before an old\n   sequence from the same endpoint was finished) simply overwrites the\n   context the server may still be keeping.  (This is probably exactly\n   what one wants in this case - the client may simply have restarted\n   and lost its knowledge of the previous sequence.)\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.6\" href=\"#section-2.6\">2.6</a>.  Combining Blockwise Transfers with the Observe Option</span>\n\n   The Observe Option provides a way for a client to be notified about\n   changes over time of a resource [<a href=\"#ref-I-D.ietf-core-observe\">I-D.ietf-core-observe</a>].  Resources\n   observed by clients may be larger than can be comfortably processed\n   or transferred in one CoAP message.  The following rules apply to the\n   combination of blockwise transfers with notifications.\n\n   Observation relationships always apply to an entire resource; the\n   Block2 option does not provide a way to observe a single block of a\n   resource.\n\n   As with basic GET transfers, the client can indicate its desired\n   block size in a Block2 Option in the GET request establishing or\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 12]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-13\" id=\"page-13\" href=\"#page-13\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   renewing the observation relationship.  If the server supports\n   blockwise transfers, it SHOULD take note of the block size and apply\n   it as a maximum size to all notifications/responses resulting from\n   the GET request (until the client is removed from the list of\n   observers or the entry in that list is updated by the server\n   receiving a new GET request for the resource from the client).\n\n   When sending a 2.05 (Content) notification, the server only sends the\n   first block of the representation.  The client retrieves the rest of\n   the representation as if it had caused this first response by a GET\n   request, i.e., by using additional GET requests with Block2 options\n   containing NUM values greater than zero.  (This results in the\n   transfer of the entire representation, even if only some of the\n   blocks have changed with respect to a previous notification.)\n\n   As with other dynamically changing resources, to ensure that the\n   blocks being reassembled are from the same version of the\n   representation, the server SHOULD include an ETag option in each\n   response, and the reassembling client MUST compare the ETag options\n   (<a href=\"#section-2.4\">Section 2.4</a>).\n\n   See <a href=\"#section-3.4\">Section 3.4</a> for examples.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.7\" href=\"#section-2.7\">2.7</a>.  Combining Block1 and Block2</span>\n\n   In PUT and particularly in POST exchanges, both the request body and\n   the response body may be large enough to require the use of block-\n   wise transfers.  First, the Block1 transfer of the request body\n   proceeds as usual.  In the exchange of the last slice of this block-\n   wise transfer, the response carries the first slice of the Block2\n   transfer (NUM is zero).  To continue this Block2 transfer, the client\n   continues to send requests similar to the requests in the Block1\n   phase, but leaves out the Block1 options and includes a Block2\n   request option with non-zero NUM.\n\n   Block2 transfers that retrieve the response body for a request that\n   used Block1 MUST be performed in sequential order.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.8\" href=\"#section-2.8\">2.8</a>.  Combining Block2 with Multicast</span>\n\n   A client can use the Block2 option in a multicast GET request with\n   NUM = 0 to aid in limiting the size of the response.\n\n   Similarly, a response to a multicast GET request can use a Block2\n   option with NUM = 0 if the representation is large, or to further\n   limit the size of the response.\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 13]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-14\" id=\"page-14\" href=\"#page-14\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   In both cases, the client retrieves any further blocks using unicast\n   exchanges; in the unicast requests, the client SHOULD heed any block\n   size preferences indicated by the server in the response to the\n   multicast request.\n\n   Other uses of the Block options in conjunction with multicast\n   messages are for further study.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-2.9\" href=\"#section-2.9\">2.9</a>.  Response Codes</span>\n\n   Two response codes are defined by this specification beyond those\n   already defined in [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>], and another response code is extended\n   in its meaning.\n\n<span class=\"h4\"><a class=\"selflink\" name=\"section-2.9.1\" href=\"#section-2.9.1\">2.9.1</a>.  2.31 Continue</span>\n\n   This new success status code indicates that the transfer of this\n   block of the request body was successful and that the server\n   encourages sending further blocks, but that a final outcome of the\n   whole block-wise request cannot yet be determined.  No payload is\n   returned with this response code.\n\n<span class=\"h4\"><a class=\"selflink\" name=\"section-2.9.2\" href=\"#section-2.9.2\">2.9.2</a>.  4.08 Request Entity Incomplete</span>\n\n   This new client error status code indicates that the server has not\n   received the blocks of the request body that it needs to proceed.\n   The client has not sent all blocks, not sent them in the order\n   required by the server, or has sent them long enough ago that the\n   server has already discarded them.\n\n<span class=\"h4\"><a class=\"selflink\" name=\"section-2.9.3\" href=\"#section-2.9.3\">2.9.3</a>.  4.13 Request Entity Too Large</span>\n\n   In <a href=\"./rfc7252#section-5.9.2.9\">[RFC7252], section&nbsp;5.9.2.9</a>, the response code 4.13 (Request Entity\n   Too Large) is defined to be like HTTP 413 \"Request Entity Too Large\".\n   [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>] also recommends that this response SHOULD include a Size1\n   Option (<a href=\"#section-4\">Section 4</a>) to indicate the maximum size of request entity the\n   server is able and willing to handle, unless the server is not in a\n   position to make this information available.\n\n   The present specification allows the server to return this response\n   code at any time during a Block1 transfer to indicate that it does\n   not currently have the resources to store blocks for a transfer that\n   it would intend to implement in an atomic fashion.  It also allows\n   the server to return a 4.13 response to a request that does not\n   employ Block1 as a hint for the client to try sending Block1.\n   Finally, a 4.13 response to a request with a Block1 option (control\n   usage, see <a href=\"#section-2.3\">Section 2.3</a>) where the response carries a smaller SZX in\n   its Block1 option is a hint to try that smaller SZX.\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 14]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-15\" id=\"page-15\" href=\"#page-15\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-3\" href=\"#section-3\">3</a>.  Examples</span>\n\n   This section gives a number of short examples with message flows for\n   a block-wise GET, and for a PUT or POST.  These examples demonstrate\n   the basic operation, the operation in the presence of\n   retransmissions, and examples for the operation of the block size\n   negotiation.\n\n   In all these examples, a Block option is shown in a decomposed way\n   indicating the kind of Block option (1 or 2) followed by a colon, and\n   then the block number (NUM), more bit (M), and block size exponent\n   (2**(SZX+4)) separated by slashes.  E.g., a Block2 Option value of 33\n   would be shown as 2:2/0/32), or a Block1 Option value of 59 would be\n   shown as 1:3/1/128.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-3.1\" href=\"#section-3.1\">3.1</a>.  Block2 Examples</span>\n\n   The first example (Figure 2) shows a GET request that is split into\n   three blocks.  The server proposes a block size of 128, and the\n   client agrees.  The first two ACKs contain 128 bytes of payload each,\n   and third ACK contains between 1 and 128 bytes.\n\n   CLIENT                                                     SERVER\n     |                                                            |\n     | CON [MID=1234], GET, /status                       ------&gt; |\n     |                                                            |\n     | &lt;------   ACK [MID=1234], 2.05 Content, 2:0/1/128          |\n     |                                                            |\n     | CON [MID=1235], GET, /status, 2:1/0/128            ------&gt; |\n     |                                                            |\n     | &lt;------   ACK [MID=1235], 2.05 Content, 2:1/1/128          |\n     |                                                            |\n     | CON [MID=1236], GET, /status, 2:2/0/128            ------&gt; |\n     |                                                            |\n     | &lt;------   ACK [MID=1236], 2.05 Content, 2:2/0/128          |\n\n                      Figure 2: Simple blockwise GET\n\n   In the second example (Figure 3), the client anticipates the\n   blockwise transfer (e.g., because of a size indication in the link-\n   format description [<a href=\"./rfc6690\" title=\"&quot;Constrained RESTful Environments (CoRE) Link Format&quot;\">RFC6690</a>]) and sends a block size proposal.  All\n   ACK messages except for the last carry 64 bytes of payload; the last\n   one carries between 1 and 64 bytes.\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 15]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-16\" id=\"page-16\" href=\"#page-16\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   CLIENT                                                     SERVER\n     |                                                          |\n     | CON [MID=1234], GET, /status, 2:0/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1234], 2.05 Content, 2:0/1/64         |\n     |                                                          |\n     | CON [MID=1235], GET, /status, 2:1/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.05 Content, 2:1/1/64         |\n     :                                                          :\n     :                          ...                             :\n     :                                                          :\n     | CON [MID=1238], GET, /status, 2:4/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1238], 2.05 Content, 2:4/1/64         |\n     |                                                          |\n     | CON [MID=1239], GET, /status, 2:5/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1239], 2.05 Content, 2:5/0/64         |\n\n              Figure 3: Blockwise GET with early negotiation\n\n   In the third example (Figure 4), the client is surprised by the need\n   for a blockwise transfer, and unhappy with the size chosen\n   unilaterally by the server.  As it did not send a size proposal\n   initially, the negotiation only influences the size from the second\n   message exchange onward.  Since the client already obtained both the\n   first and second 64-byte block in the first 128-byte exchange, it\n   goes on requesting the third 64-byte block (\"2/0/64\").  None of this\n   is (or needs to be) understood by the server, which simply responds\n   to the requests as it best can.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 16]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-17\" id=\"page-17\" href=\"#page-17\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   CLIENT                                                     SERVER\n     |                                                          |\n     | CON [MID=1234], GET, /status                     ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1234], 2.05 Content, 2:0/1/128        |\n     |                                                          |\n     | CON [MID=1235], GET, /status, 2:2/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.05 Content, 2:2/1/64         |\n     |                                                          |\n     | CON [MID=1236], GET, /status, 2:3/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1236], 2.05 Content, 2:3/1/64         |\n     |                                                          |\n     | CON [MID=1237], GET, /status, 2:4/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1237], 2.05 Content, 2:4/1/64         |\n     |                                                          |\n     | CON [MID=1238], GET, /status, 2:5/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1238], 2.05 Content, 2:5/0/64         |\n\n               Figure 4: Blockwise GET with late negotiation\n\n   In all these (and the following) cases, retransmissions are handled\n   by the CoAP message exchange layer, so they don't influence the block\n   operations (Figure 5, Figure 6).\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 17]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-18\" id=\"page-18\" href=\"#page-18\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   CLIENT                                                     SERVER\n     |                                                          |\n     | CON [MID=1234], GET, /status                     ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1234], 2.05 Content, 2:0/1/128        |\n     |                                                          |\n     | CON [MID=1235], GE/////////////////////////              |\n     |                                                          |\n     | (timeout)                                                |\n     |                                                          |\n     | CON [MID=1235], GET, /status, 2:2/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.05 Content, 2:2/1/64         |\n     :                                                          :\n     :                          ...                             :\n     :                                                          :\n     | CON [MID=1238], GET, /status, 2:5/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1238], 2.05 Content, 2:5/0/64         |\n\n        Figure 5: Blockwise GET with late negotiation and lost CON\n\n   CLIENT                                                     SERVER\n     |                                                          |\n     | CON [MID=1234], GET, /status                     ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1234], 2.05 Content, 2:0/1/128        |\n     |                                                          |\n     | CON [MID=1235], GET, /status, 2:2/0/64           ------&gt; |\n     |                                                          |\n     | //////////////////////////////////tent, 2:2/1/64         |\n     |                                                          |\n     | (timeout)                                                |\n     |                                                          |\n     | CON [MID=1235], GET, /status, 2:2/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.05 Content, 2:2/1/64         |\n     :                                                          :\n     :                          ...                             :\n     :                                                          :\n     | CON [MID=1238], GET, /status, 2:5/0/64           ------&gt; |\n     |                                                          |\n     | &lt;------   ACK [MID=1238], 2.05 Content, 2:5/0/64         |\n\n        Figure 6: Blockwise GET with late negotiation and lost ACK\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 18]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-19\" id=\"page-19\" href=\"#page-19\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-3.2\" href=\"#section-3.2\">3.2</a>.  Block1 Examples</span>\n\n   The following examples demonstrate a PUT exchange; a POST exchange\n   looks the same, with different requirements on atomicity/idempotence.\n   Note that, similar to GET, the responses to the requests that have a\n   more bit in the request Block1 Option are provisional and carry the\n   response code 2.31 (Continue); only the final response tells the\n   client that the PUT did succeed.\n\n   CLIENT                                                     SERVER\n     |                                                          |\n     | CON [MID=1234], PUT, /options, 1:0/1/128    ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1234], 2.31 Continue, 1:0/1/128       |\n     |                                                          |\n     | CON [MID=1235], PUT, /options, 1:1/1/128    ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.31 Continue, 1:1/1/128       |\n     |                                                          |\n     | CON [MID=1236], PUT, /options, 1:2/0/128    ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1236], 2.04 Changed, 1:2/0/128        |\n\n                   Figure 7: Simple atomic blockwise PUT\n\n   A stateless server that simply builds/updates the resource in place\n   (statelessly) may indicate this by not setting the more bit in the\n   response (Figure 8); in this case, the response codes are valid\n   separately for each block being updated.  This is of course only an\n   acceptable behavior of the server if the potential inconsistency\n   present during the run of the message exchange sequence does not lead\n   to problems, e.g. because the resource being created or changed is\n   not yet or not currently in use.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 19]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-20\" id=\"page-20\" href=\"#page-20\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   CLIENT                                                     SERVER\n     |                                                          |\n     | CON [MID=1234], PUT, /options, 1:0/1/128    ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1234], 2.04 Changed, 1:0/0/128        |\n     |                                                          |\n     | CON [MID=1235], PUT, /options, 1:1/1/128    ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.04 Changed, 1:1/0/128        |\n     |                                                          |\n     | CON [MID=1236], PUT, /options, 1:2/0/128    ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1236], 2.04 Changed, 1:2/0/128        |\n\n                 Figure 8: Simple stateless blockwise PUT\n\n   Finally, a server receiving a blockwise PUT or POST may want to\n   indicate a smaller block size preference (Figure 9).  In this case,\n   the client SHOULD continue with a smaller block size; if it does, it\n   MUST adjust the block number to properly count in that smaller size.\n\n   CLIENT                                                     SERVER\n     |                                                          |\n     | CON [MID=1234], PUT, /options, 1:0/1/128    ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1234], 2.04 Changed, 1:0/1/32         |\n     |                                                          |\n     | CON [MID=1235], PUT, /options, 1:4/1/32     ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.04 Changed, 1:4/1/32         |\n     |                                                          |\n     | CON [MID=1236], PUT, /options, 1:5/1/32     ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1235], 2.04 Changed, 1:5/1/32         |\n     |                                                          |\n     | CON [MID=1237], PUT, /options, 1:6/0/32     ------&gt;      |\n     |                                                          |\n     | &lt;------   ACK [MID=1236], 2.04 Changed, 1:6/0/32         |\n\n          Figure 9: Simple atomic blockwise PUT with negotiation\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-3.3\" href=\"#section-3.3\">3.3</a>.  Combining Block1 and Block2</span>\n\n   Block options may be used in both directions of a single exchange.\n   The following example demonstrates a blockwise POST request,\n   resulting in a separate blockwise response.\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 20]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-21\" id=\"page-21\" href=\"#page-21\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   CLIENT                                                     SERVER\n     |                                                              |\n     | CON [MID=1234], POST, /soap, 1:0/1/128      ------&gt;          |\n     |                                                              |\n     | &lt;------   ACK [MID=1234], 2.31 Continue, 1:0/1/128           |\n     |                                                              |\n     | CON [MID=1235], POST, /soap, 1:1/1/128      ------&gt;          |\n     |                                                              |\n     | &lt;------   ACK [MID=1235], 2.31 Continue, 1:1/1/128           |\n     |                                                              |\n     | CON [MID=1236], POST, /soap, 1:2/0/128      ------&gt;          |\n     |                                                              |\n     | &lt;------   ACK [MID=1236], 2.04 Changed, 2:0/1/128, 1:2/0/128 |\n     |                                                              |\n     | CON [MID=1237], POST, /soap, 2:1/0/128      ------&gt;          |\n     | (no payload for requests with Block2 with NUM != 0)          |\n     | (could also do late negotiation by requesting e.g. 2:2/0/64) |\n     |                                                              |\n     | &lt;------   ACK [MID=1237], 2.04 Changed, 2:1/1/128            |\n     |                                                              |\n     | CON [MID=1238], POST, /soap, 2:2/0/128      ------&gt;          |\n     |                                                              |\n     | &lt;------   ACK [MID=1238], 2.04 Changed, 2:2/1/128            |\n     |                                                              |\n     | CON [MID=1239], POST, /soap, 2:3/0/128      ------&gt;          |\n     |                                                              |\n     | &lt;------   ACK [MID=1239], 2.04 Changed, 2:3/0/128            |\n\n         Figure 10: Atomic blockwise POST with blockwise response\n\n   This model does provide for early negotiation input to the Block2\n   blockwise transfer, as shown below.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 21]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-22\" id=\"page-22\" href=\"#page-22\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   CLIENT                                                     SERVER\n     |                                                              |\n     | CON [MID=1234], POST, /soap, 1:0/1/128 ------&gt;               |\n     |                                                              |\n     | &lt;------   ACK [MID=1234], 2.31 Continue, 1:0/1/128           |\n     |                                                              |\n     | CON [MID=1235], POST, /soap, 1:1/1/128 ------&gt;               |\n     |                                                              |\n     | &lt;------   ACK [MID=1235], 2.31 Continue, 1:1/1/128           |\n     |                                                              |\n     | CON [MID=1236], POST, /soap, 1:2/0/128, 2:0/0/64 ------&gt;     |\n     |                                                              |\n     | &lt;------   ACK [MID=1236], 2.04 Changed, 1:2/0/128, 2:0/1/64 |\n     |                                                              |\n     | CON [MID=1237], POST, /soap, 2:1/0/64      ------&gt;           |\n     | (no payload for requests with Block2 with NUM != 0)          |\n     |                                                              |\n     | &lt;------   ACK [MID=1237], 2.04 Changed, 2:1/1/64             |\n     |                                                              |\n     | CON [MID=1238], POST, /soap, 2:2/0/64      ------&gt;           |\n     |                                                              |\n     | &lt;------   ACK [MID=1238], 2.04 Changed, 2:2/1/64             |\n     |                                                              |\n     | CON [MID=1239], POST, /soap, 2:3/0/64      ------&gt;           |\n     |                                                              |\n     | &lt;------   ACK [MID=1239], 2.04 Changed, 2:3/0/64             |\n\n      Figure 11: Atomic blockwise POST with blockwise response, early\n                                negotiation\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-3.4\" href=\"#section-3.4\">3.4</a>.  Combining Observe and Block2</span>\n\n   In the following example, the server first sends a direct response\n   (Observe sequence number 62350) to the initial GET request (the\n   resulting blockwise transfer is as in Figure 4 and has therefore been\n   left out).  The second transfer is started by a 2.05 notification\n   that contains just the first block (Observe sequence number 62354);\n   the client then goes on to obtain the rest of the blocks.\n\n       CLIENT  SERVER\n         |      |\n         +-----&gt;|     Header: GET 0x41011636\n         | GET  |      Token: 0xfb\n         |      |   Uri-Path: status-icon\n         |      |    Observe: (empty)\n         |      |\n         |&lt;-----+     Header: 2.05 0x61451636\n         | 2.05 |      Token: 0xfb\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 22]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-23\" id=\"page-23\" href=\"#page-23\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n         |      |     Block2: 0/1/128\n         |      |    Observe: 62350\n         |      |       ETag: 6f00f38e\n         |      |    Payload: [128 bytes]\n         |      |\n         |      |  (Usual GET transfer left out)\n           ...\n         |      |  (Notification of first block:)\n         |      |\n         |&lt;-----+     Header: 2.05 0x4145af9c\n         | 2.05 |      Token: 0xfb\n         |      |     Block2: 0/1/128\n         |      |    Observe: 62354\n         |      |       ETag: 6f00f392\n         |      |    Payload: [128 bytes]\n         |      |\n         +- - -&gt;|     Header: 0x6000af9c\n         |      |\n         |      |  (Retrieval of remaining blocks)\n         |      |\n         +-----&gt;|     Header: GET 0x41011637\n         | GET  |      Token: 0xfc\n         |      |   Uri-Path: status-icon\n         |      |     Block2: 1/0/128\n         |      |\n         |&lt;-----+     Header: 2.05 0x61451637\n         | 2.05 |      Token: 0xfc\n         |      |     Block2: 1/1/128\n         |      |       ETag: 6f00f392\n         |      |    Payload: [128 bytes]\n         |      |\n         +-----&gt;|     Header: GET 0x41011638\n         | GET  |      Token: 0xfc\n         |      |   Uri-Path: status-icon\n         |      |     Block2: 2/0/128\n         |      |\n         |&lt;-----+     Header: 2.05 0x61451638\n         | 2.05 |      Token: 0xfc\n         |      |     Block2: 2/0/128\n         |      |       ETag: 6f00f392\n         |      |    Payload: [53 bytes]\n\n\n            Figure 12: Observe sequence with blockwise response\n\n   In the following example, the client also uses early negotiation to\n   limit the block size to 64 bytes.\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 23]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-24\" id=\"page-24\" href=\"#page-24\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n       CLIENT  SERVER\n         |      |\n         +-----&gt;|     Header: GET 0x41011636\n         | GET  |      Token: 0xfb\n         |      |   Uri-Path: status-icon\n         |      |    Observe: (empty)\n         |      |     Block2: 0/0/64\n         |      |\n         |&lt;-----+     Header: 2.05 0x61451636\n         | 2.05 |      Token: 0xfb\n         |      |     Block2: 0/1/64\n         |      |    Observe: 62350\n         |      |       ETag: 6f00f38e\n         |      |    Max-Age: 60\n         |      |    Payload: [64 bytes]\n         |      |\n         |      |  (Usual GET transfer left out)\n           ...\n         |      |  (Notification of first block:)\n         |      |\n         |&lt;-----+     Header: 2.05 0x4145af9c\n         | 2.05 |      Token: 0xfb\n         |      |     Block2: 0/1/64\n         |      |    Observe: 62354\n         |      |       ETag: 6f00f392\n         |      |    Payload: [64 bytes]\n         |      |\n         +- - -&gt;|     Header: 0x6000af9c\n         |      |\n         |      |  (Retrieval of remaining blocks)\n         |      |\n         +-----&gt;|     Header: GET 0x41011637\n         | GET  |      Token: 0xfc\n         |      |   Uri-Path: status-icon\n         |      |     Block2: 1/0/64\n         |      |\n         |&lt;-----+     Header: 2.05 0x61451637\n         | 2.05 |      Token: 0xfc\n         |      |     Block2: 1/1/64\n         |      |       ETag: 6f00f392\n         |      |    Payload: [64 bytes]\n           ....\n         |      |\n         +-----&gt;|     Header: GET 0x41011638\n         | GET  |      Token: 0xfc\n         |      |   Uri-Path: status-icon\n         |      |     Block2: 4/0/64\n         |      |\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 24]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-25\" id=\"page-25\" href=\"#page-25\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n         |&lt;-----+     Header: 2.05 0x61451638\n         | 2.05 |      Token: 0xfc\n         |      |     Block2: 4/0/64\n         |      |       ETag: 6f00f392\n         |      |    Payload: [53 bytes]\n\n            Figure 13: Observe sequence with early negotiation\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-4\" href=\"#section-4\">4</a>.  The Size2 and Size1 Options</span>\n\n   In many cases when transferring a large resource representation block\n   by block, it is advantageous to know the total size early in the\n   process.  Some indication may be available from the maximum size\n   estimate attribute \"sz\" provided in a resource description [<a href=\"./rfc6690\" title=\"&quot;Constrained RESTful Environments (CoRE) Link Format&quot;\">RFC6690</a>].\n   However, the size may vary dynamically, so a more up-to-date\n   indication may be useful.\n\n   This specification defines two CoAP Options, Size1 for indicating the\n   size of the representation transferred in requests, and Size2 for\n   indicating the size of the representation transferred in responses.\n   (Size1 is already defined in [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>] for the narrow case of\n   indicating in 4.13 responses the maximum size of request payload that\n   the server is able and willing to handle.)\n\n   The Size2 Option may be used for two purposes:\n\n   o  in a request, to ask the server to provide a size estimate along\n      with the usual response (\"size request\").  For this usage, the\n      value MUST be set to 0.\n\n   o  in a response carrying a Block2 Option, to indicate the current\n      estimate the server has of the total size of the resource\n      representation, measured in bytes (\"size indication\").\n\n   Similarly, the Size1 Option may be used for two purposes:\n\n   o  in a request carrying a Block1 Option, to indicate the current\n      estimate the client has of the total size of the resource\n      representation, measured in bytes (\"size indication\").\n\n   o  in a 4.13 response, to indicate the maximum size that would have\n      been acceptable [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>], measured in bytes.\n\n   Apart from conveying/asking for size information, the Size options\n   have no other effect on the processing of the request or response.\n   If the client wants to minimize the size of the payload in the\n   resulting response, it should add a Block2 option to the request with\n   a small block size (e.g., setting SZX=0).\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 25]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-26\" id=\"page-26\" href=\"#page-26\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   The Size Options are \"elective\", i.e., a client MUST be prepared for\n   the server to ignore the size estimate request.  The Size Options\n   MUST NOT occur more than once.\n\n        +-----+---+---+---+---+-------+--------+--------+---------+\n        | No. | C | U | N | R | Name  | Format | Length | Default |\n        +-----+---+---+---+---+-------+--------+--------+---------+\n        |  60 |   |   | x |   | Size1 | uint   |    0-4 | (none)  |\n        |     |   |   |   |   |       |        |        |         |\n        |  28 |   |   | x |   | Size2 | uint   |    0-4 | (none)  |\n        +-----+---+---+---+---+-------+--------+--------+---------+\n\n                       Table 2: Size Option Numbers\n\n   Implementation Notes:\n\n   o  As a quality of implementation consideration, blockwise transfers\n      for which the total size considerably exceeds the size of one\n      block are expected to include size indications, whenever those can\n      be provided without undue effort (preferably with the first block\n      exchanged).  If the size estimate does not change, the indication\n      does not need to be repeated for every block.\n\n   o  The end of a blockwise transfer is governed by the M bits in the\n      Block Options, _not_ by exhausting the size estimates exchanged.\n\n   o  As usual for an option of type uint, the value 0 is best expressed\n      as an empty option (0 bytes).  There is no default value for\n      either Size Option.\n\n   o  The Size Options are neither critical nor unsafe, and are marked\n      as No-Cache-Key.\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-5\" href=\"#section-5\">5</a>.  HTTP Mapping Considerations</span>\n\n   In this subsection, we give some brief examples for the influence the\n   Block options might have on intermediaries that map between CoAP and\n   HTTP.\n\n   For mapping CoAP requests to HTTP, the intermediary may want to map\n   the sequence of block-wise transfers into a single HTTP transfer.\n   E.g., for a GET request, the intermediary could perform the HTTP\n   request once the first block has been requested and could then\n   fulfill all further block requests out of its cache.  A constrained\n   implementation may not be able to cache the entire object and may use\n   a combination of TCP flow control and (in particular if timeouts\n   occur) HTTP range requests to obtain the information necessary for\n   the next block transfer at the right time.\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 26]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-27\" id=\"page-27\" href=\"#page-27\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   For PUT or POST requests, historically there was more variation in\n   how HTTP servers might implement ranges; recently, [<a href=\"./rfc7233\" title=\"&quot;Hypertext Transfer Protocol (HTTP/1.1): Range Requests&quot;\">RFC7233</a>] has\n   defined that Range header fields received with a request method other\n   than GET are not to be interpreted.  So, in general, the CoAP-to-HTTP\n   intermediary will have to try sending the payload of all the blocks\n   of a block-wise transfer for these other methods within one HTTP\n   request.  If enough buffering is available, this request can be\n   started when the last CoAP block is received.  A constrained\n   implementation may want to relieve its buffering by already starting\n   to send the HTTP request at the time the first CoAP block is\n   received; any HTTP 408 status code that indicates that the HTTP\n   server became impatient with the resulting transfer can then be\n   mapped into a CoAP 4.08 response code (similarly, 413 maps to 4.13).\n\n   For mapping HTTP to CoAP, the intermediary may want to map a single\n   HTTP transfer into a sequence of block-wise transfers.  If the HTTP\n   client is too slow delivering a request body on a PUT or POST, the\n   CoAP server might time out and return a 4.08 response code, which in\n   turn maps well to an HTTP 408 status code (again, 4.13 maps to 413).\n   HTTP range requests received on the HTTP side may be served out of a\n   cache and/or mapped to GET requests that request a sequence of blocks\n   overlapping the range.\n\n   (Note that, while the semantics of CoAP 4.08 and HTTP 408 differ,\n   this difference is largely due to the different way the two protocols\n   are mapped to transport.  HTTP has an underlying TCP connection,\n   which supplies connection state, so a HTTP 408 status code can\n   immediately be used to indicate that a timeout occurred during\n   transmitting a request through that active TCP connection.  The CoAP\n   4.08 response code indicates one or more missing blocks, which may be\n   due to timeouts or resource constraints; as there is no connection\n   state, there is no way to deliver such a response immediately;\n   instead, it is delivered on the next block transfer.  Still, HTTP 408\n   is probably the best mapping back to HTTP, as the timeout is the most\n   likely cause for a CoAP 4.08.  Note that there is no way to\n   distinguish a timeout from a missing block for a server without\n   creating additional state, the need for which we want to avoid.)\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-6\" href=\"#section-6\">6</a>.  IANA Considerations</span>\n\n   This draft adds the following option numbers to the CoAP Option\n   Numbers registry of [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>]:\n\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 27]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-28\" id=\"page-28\" href=\"#page-28\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n                      +--------+--------+-----------+\n                      | Number | Name   | Reference |\n                      +--------+--------+-----------+\n                      | 23     | Block2 | [RFCXXXX] |\n                      |        |        |           |\n                      | 27     | Block1 | [RFCXXXX] |\n                      |        |        |           |\n                      | 28     | Size2  | [RFCXXXX] |\n                      +--------+--------+-----------+\n\n                       Table 3: CoAP Option Numbers\n\n   This draft adds the following response code to the CoAP Response\n   Codes registry of [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>]:\n\n             +------+---------------------------+-----------+\n             | Code | Description               | Reference |\n             +------+---------------------------+-----------+\n             | 2.31 | Continue                  | [RFCXXXX] |\n             |      |                           |           |\n             | 4.08 | Request Entity Incomplete | [RFCXXXX] |\n             +------+---------------------------+-----------+\n\n                       Table 4: CoAP Response Codes\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-7\" href=\"#section-7\">7</a>.  Security Considerations</span>\n\n   Providing access to blocks within a resource may lead to surprising\n   vulnerabilities.  Where requests are not implemented atomically, an\n   attacker may be able to exploit a race condition or confuse a server\n   by inducing it to use a partially updated resource representation.\n   Partial transfers may also make certain problematic data invisible to\n   intrusion detection systems; it is RECOMMENDED that an intrusion\n   detection system (IDS) that analyzes resource representations\n   transferred by CoAP implement the Block options to gain access to\n   entire resource representations.  Still, approaches such as\n   transferring even-numbered blocks on one path and odd-numbered blocks\n   on another path, or even transferring blocks multiple times with\n   different content and obtaining a different interpretation of\n   temporal order at the IDS than at the server, may prevent an IDS from\n   seeing the whole picture.  These kinds of attacks are well understood\n   from IP fragmentation and TCP segmentation; CoAP does not add\n   fundamentally new considerations.\n\n   Where access to a resource is only granted to clients making use of\n   specific security associations, all blocks of that resource MUST be\n   subject to the same security checks; it MUST NOT be possible for\n   unprotected exchanges to influence blocks of an otherwise protected\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 28]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-29\" id=\"page-29\" href=\"#page-29\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   resource.  As a related consideration, where object security is\n   employed, PUT/POST should be implemented in the atomic fashion,\n   unless the object security operation is performed on each access and\n   the creation of unusable resources can be tolerated.\n\n   A stateless server might be susceptible to an attack where the\n   adversary sends a Block1 (e.g., PUT) block with a high block number:\n   A naive implementation might exhaust its resources by creating a huge\n   resource representation.\n\n   Misleading size indications may be used by an attacker to induce\n   buffer overflows in poor implementations, for which the usual\n   considerations apply.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-7.1\" href=\"#section-7.1\">7.1</a>.  Mitigating Resource Exhaustion Attacks</span>\n\n   Certain blockwise requests may induce the server to create state,\n   e.g. to create a snapshot for the blockwise GET of a fast-changing\n   resource to enable consistent access to the same version of a\n   resource for all blocks, or to create temporary resource\n   representations that are collected until pressed into service by a\n   final PUT or POST with the more bit unset.  All mechanisms that\n   induce a server to create state that cannot simply be cleaned up\n   create opportunities for denial-of-service attacks.  Servers SHOULD\n   avoid being subject to resource exhaustion based on state created by\n   untrusted sources.  But even if this is done, the mitigation may\n   cause a denial-of-service to a legitimate request when it is drowned\n   out by other state-creating requests.  Wherever possible, servers\n   should therefore minimize the opportunities to create state for\n   untrusted sources, e.g. by using stateless approaches.\n\n   Performing segmentation at the application layer is almost always\n   better in this respect than at the transport layer or lower (IP\n   fragmentation, adaptation layer fragmentation), for instance because\n   there is application layer semantics that can be used for mitigation\n   or because lower layers provide security associations that can\n   prevent attacks.  However, it is less common to apply timeouts and\n   keepalive mechanisms at the application layer than at lower layers.\n   Servers MAY want to clean up accumulated state by timing it out (cf.\n   response code 4.08), and clients SHOULD be prepared to run blockwise\n   transfers in an expedient way to minimize the likelihood of running\n   into such a timeout.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-7.2\" href=\"#section-7.2\">7.2</a>.  Mitigating Amplification Attacks</span>\n\n   [<a name=\"ref-RFC7252\" id=\"ref-RFC7252\">RFC7252</a>] discusses the susceptibility of CoAP end-points for use in\n   amplification attacks.\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 29]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-30\" id=\"page-30\" href=\"#page-30\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   A CoAP server can reduce the amount of amplification it provides to\n   an attacker by offering large resource representations only in\n   relatively small blocks.  With this, e.g., for a 1000 byte resource,\n   a 10-byte request might result in an 80-byte response (with a 64-byte\n   block) instead of a 1016-byte response, considerably reducing the\n   amplification provided.\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-8\" href=\"#section-8\">8</a>.  Acknowledgements</span>\n\n   Much of the content of this draft is the result of discussions with\n   the [<a href=\"./rfc7252\" title=\"&quot;The Constrained Application Protocol (CoAP)&quot;\">RFC7252</a>] authors, and via many CoRE WG discussions.\n\n   Charles Palmer provided extensive editorial comments to a previous\n   version of this draft, some of which the authors hope to have covered\n   in this version.  Esko Dijk reviewed a more recent version, leading\n   to a number of further editorial improvements, a solution to the 4.13\n   ambiguity problem, and the section about combining Block and\n   multicast.  Markus Becker proposed getting rid of an ill-conceived\n   default value for the Block2 and Block1 options.  Peter Bigot\n   insisted on a more systematic coverage of the options and response\n   code.\n\n   Kepeng Li, Linyi Tian, and Barry Leiba wrote up an early version of\n   the Size Option, which has informed this draft.  Klaus Hartke wrote\n   some of the text describing the interaction of Block2 with Observe.\n   Matthias Kovatsch provided a number of significant simplifications of\n   the protocol.\n\n<span class=\"h2\"><a class=\"selflink\" name=\"section-9\" href=\"#section-9\">9</a>.  References</span>\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-9.1\" href=\"#section-9.1\">9.1</a>.  Normative References</span>\n\n   [<a name=\"ref-I-D.ietf-core-observe\" id=\"ref-I-D.ietf-core-observe\">I-D.ietf-core-observe</a>]\n              Hartke, K., \"Observing Resources in CoAP\", <a href=\"./draft-ietf-core-observe-14\">draft-ietf-</a>\n              <a href=\"./draft-ietf-core-observe-14\">core-observe-14</a> (work in progress), June 2014.\n\n   [<a name=\"ref-RFC2119\" id=\"ref-RFC2119\">RFC2119</a>]  Bradner, S., \"Key words for use in RFCs to Indicate\n              Requirement Levels\", <a href=\"./bcp14\">BCP 14</a>, <a href=\"./rfc2119\">RFC 2119</a>, March 1997.\n\n   [<a name=\"ref-RFC7252\" id=\"ref-RFC7252\">RFC7252</a>]  Shelby, Z., Hartke, K., and C. Bormann, \"The Constrained\n              Application Protocol (CoAP)\", <a href=\"./rfc7252\">RFC 7252</a>, June 2014.\n\n<span class=\"h3\"><a class=\"selflink\" name=\"section-9.2\" href=\"#section-9.2\">9.2</a>.  Informative References</span>\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 30]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-31\" id=\"page-31\" href=\"#page-31\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   [<a name=\"ref-REST\" id=\"ref-REST\">REST</a>]     Fielding, R., \"Architectural Styles and the Design of\n              Network-based Software Architectures\", Ph.D. Dissertation,\n              University of California, Irvine, 2000,\n              &lt;<a href=\"http://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf\">http://www.ics.uci.edu/~fielding/pubs/dissertation/</a>\n              <a href=\"http://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf\">fielding_dissertation.pdf</a>&gt;.\n\n   [<a name=\"ref-RFC4919\" id=\"ref-RFC4919\">RFC4919</a>]  Kushalnagar, N., Montenegro, G., and C. Schumacher, \"IPv6\n              over Low-Power Wireless Personal Area Networks (6LoWPANs):\n              Overview, Assumptions, Problem Statement, and Goals\", <a href=\"./rfc4919\">RFC</a>\n              <a href=\"./rfc4919\">4919</a>, August 2007.\n\n   [<a name=\"ref-RFC4944\" id=\"ref-RFC4944\">RFC4944</a>]  Montenegro, G., Kushalnagar, N., Hui, J., and D. Culler,\n              \"Transmission of IPv6 Packets over IEEE 802.15.4\n              Networks\", <a href=\"./rfc4944\">RFC 4944</a>, September 2007.\n\n   [<a name=\"ref-RFC6690\" id=\"ref-RFC6690\">RFC6690</a>]  Shelby, Z., \"Constrained RESTful Environments (CoRE) Link\n              Format\", <a href=\"./rfc6690\">RFC 6690</a>, August 2012.\n\n   [<a name=\"ref-RFC7228\" id=\"ref-RFC7228\">RFC7228</a>]  Bormann, C., Ersue, M., and A. Keranen, \"Terminology for\n              Constrained-Node Networks\", <a href=\"./rfc7228\">RFC 7228</a>, May 2014.\n\n   [<a name=\"ref-RFC7230\" id=\"ref-RFC7230\">RFC7230</a>]  Fielding, R. and J. Reschke, \"Hypertext Transfer Protocol\n              (HTTP/1.1): Message Syntax and Routing\", <a href=\"./rfc7230\">RFC 7230</a>, June\n              2014.\n\n   [<a name=\"ref-RFC7233\" id=\"ref-RFC7233\">RFC7233</a>]  Fielding, R., Lafon, Y., and J. Reschke, \"Hypertext\n              Transfer Protocol (HTTP/1.1): Range Requests\", <a href=\"./rfc7233\">RFC 7233</a>,\n              June 2014.\n\nAuthors' Addresses\n\n   Carsten Bormann\n   Universitaet Bremen TZI\n   Postfach 330440\n   Bremen  D-28359\n   Germany\n\n   Phone: +49-421-218-63921\n   Email: cabo@tzi.org\n\n\n\n\n\n\n\n\n\n\n\n\n<span class=\"grey\">Bormann &amp; Shelby         Expires January 5, 2015               [Page 31]</span></pre>\n<hr class='noprint' style='width: 96ex;' align='left'/><!--NewPage--><pre class='newpage'><a name=\"page-32\" id=\"page-32\" href=\"#page-32\" class=\"invisible\"> </a>\n<span class=\"grey\">Internet-Draft         Blockwise transfers in CoAP             July 2014</span>\n\n\n   Zach Shelby (editor)\n   ARM\n   150 Rose Orchard\n   San Jose, CA  95134\n   USA\n\n   Phone: +1-408-203-9434\n   Email: zach.shelby@arm.com\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nBormann &amp; Shelby         Expires January 5, 2015               [Page 32]\n\n</pre><br />\n    <span class=\"noprint\"><small><small>Html markup produced by rfcmarkup 1.118, available from\n      <a href=\"https://tools.ietf.org/tools/rfcmarkup/\">https://tools.ietf.org/tools/rfcmarkup/</a>\n    </small></small></span>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "examples/block1/server.go",
    "content": "package main\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tserver := canopus.NewServer()\n\n\tserver.Get(\"/blockinfo\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\n\tserver.Post(\"/blockupload\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\t// Save to file\n\t\tpayload := req.GetMessage().GetPayload().GetBytes()\n\t\tlog.Println(\"len\", len(payload))\n\t\terr := ioutil.WriteFile(\"output.html\", payload, 0644)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t}\n\n\t\treturn res\n\t})\n\n\tserver.OnBlockMessage(func(msg canopus.Message, inbound bool) {\n\t\t// log.Println(\"Incoming Block Message:\")\n\t\t// canopus.PrintMessage(msg)\n\t})\n\n\tserver.ListenAndServe(\":5683\")\n\t<-make(chan struct{})\n}\n"
  },
  {
    "path": "examples/discovery/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"crypto/rand\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tfmt.Println(\"Starting up\")\n\tserver := canopus.NewServer()\n\n\tserver.ListenAndServeDTLS(\":5682\")\n\n\tfmt.Println(\"New Request..\")\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Post, canopus.GenerateMessageID())\n\t// req.SetStringPayload(BuildModelResourceStringPayload(c.enabledObjects))\n\treq.SetRequestURI(\"/rd\")\n\treq.SetURIQuery(\"ep\", \"name\")\n\n\t// session := canopus.NewUDPServerSession(\"localhost:5684\", server.GetConnection(), server)\n\n\t// DTLS Version\n\tfmt.Println(\"Initializing SSL\")\n\tsecret := make([]byte, 32)\n\tif n, err := rand.Read(secret); n != 32 || err != nil {\n\t\tpanic(err)\n\t}\n\tserver.(*canopus.DefaultCoapServer).SetCookieSecret(secret)\n\tserver.HandlePSK(func(id string) []byte {\n\t\treturn []byte(\"secretPSK\")\n\t})\n\n\tctx, err := canopus.NewServerDtlsContext()\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tsession := canopus.NewDTLSServerSession(\"localhost:5684\", server.GetConnection(), server, ctx)\n\n\tfmt.Println(\"Sending Message\")\n\tresp, err := canopus.SendMessage(req.GetMessage(), session)\n\tfmt.Println(\"Response\", resp, err)\n\n\tcanopus.PrintMessage(resp.GetMessage())\n\n\t<-make(chan struct{})\n}\n"
  },
  {
    "path": "examples/dtls/simple-psk/client.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tconn, err := canopus.DialDTLS(\"localhost:5684\", \"canopus\", \"secretPSK\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get)\n\treq.SetStringPayload(\"Hello, canopus\")\n\treq.SetRequestURI(\"/hello\")\n\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tfmt.Println(\"Got Response:\" + resp.GetMessage().GetPayload().String())\n}\n"
  },
  {
    "path": "examples/dtls/simple-psk/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tserver := canopus.NewServer()\n\n\tserver.Get(\"/hello\", func(req canopus.Request) canopus.Response {\n\t\tfmt.Println(\"Hello Called\")\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\n\tserver.HandlePSK(func(id string) []byte {\n\t\treturn []byte(\"secretPSK\")\n\t})\n\n\tserver.ListenAndServeDTLS(\":5684\")\n\n\t<-make(chan struct{})\n}\n"
  },
  {
    "path": "examples/observe/client.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\n\ttok, err := conn.ObserveResource(\"/watch/this\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tobsChannel := make(chan canopus.ObserveMessage)\n\tdone := make(chan bool)\n\tgo conn.Observe(obsChannel)\n\n\tnotifyCount := 0\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase obsMsg, open := <-obsChannel:\n\t\t\t\tif open {\n\t\t\t\t\tif notifyCount == 5 {\n\t\t\t\t\t\tfmt.Println(\"[CLIENT >> ] Canceling observe after 5 notifications..\")\n\t\t\t\t\t\tgo conn.CancelObserveResource(\"watch/this\", tok)\n\t\t\t\t\t\tgo conn.StopObserve(obsChannel)\n\t\t\t\t\t\tdone <- true\n\t\t\t\t\t\treturn\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnotifyCount++\n\t\t\t\t\t\t// msg := obsMsg.Msg\\\n\t\t\t\t\t\tresource := obsMsg.GetResource()\n\t\t\t\t\t\tval := obsMsg.GetValue()\n\n\t\t\t\t\t\tfmt.Println(\"[CLIENT >> ] Got Change Notification for resource and value: \", notifyCount, resource, val)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tdone <- true\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\t<-done\n\tfmt.Println(\"Done\")\n}\n"
  },
  {
    "path": "examples/observe/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tserver := canopus.NewServer()\n\tserver.Get(\"/watch/this\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.NewMessageOfType(canopus.MessageAcknowledgment, req.GetMessage().GetMessageId(), canopus.NewPlainTextPayload(\"Acknowledged\"))\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\n\tticker := time.NewTicker(3 * time.Second)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\t\tchangeVal := strconv.Itoa(rand.Int())\n\t\t\t\tfmt.Println(\"[SERVER << ] Change of value -->\", changeVal)\n\n\t\t\t\tserver.NotifyChange(\"/watch/this\", changeVal, false)\n\t\t\t}\n\t\t}\n\t}()\n\n\tserver.OnMessage(func(msg canopus.Message, inbound bool) {\n\t\tcanopus.PrintMessage(msg)\n\t})\n\n\tserver.OnObserve(func(resource string, msg canopus.Message) {\n\t\tfmt.Println(\"[SERVER << ] Observe Requested for \" + resource)\n\t})\n\n\tserver.ListenAndServe(\":5683\")\n\t<-make(chan struct{})\n}\n"
  },
  {
    "path": "examples/proxy/coap/client.go",
    "content": "package main\n\nimport \"github.com/zubairhamed/canopus\"\n\nfunc main() {\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get, canopus.GenerateMessageID())\n\treq.SetProxyURI(\"coap://localhost:5685/proxycall\")\n\n\tcanopus.PrintMessage(req.GetMessage())\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tprintln(\"err\", err)\n\t}\n\tcanopus.PrintMessage(resp.GetMessage())\n}\n"
  },
  {
    "path": "examples/proxy/coap/proxy.go",
    "content": "package main\n\nimport \"github.com/zubairhamed/canopus\"\n\nfunc main() {\n\tserver := canopus.NewServer()\n\tserver.ProxyOverCoap(true)\n\n\tserver.Get(\"/proxycall\", func(req canopus.Request) canopus.Response {\n\t\tcanopus.PrintMessage(req.GetMessage())\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\tserver.ListenAndServe(\":5683\")\n\t<-make(chan struct{})\n}\n"
  },
  {
    "path": "examples/proxy/coap/server.go",
    "content": "package main\n\nimport \"github.com/zubairhamed/canopus\"\n\nfunc main() {\n\tserver := canopus.NewServer()\n\n\tserver.Get(\"/proxycall\", func(req canopus.Request) canopus.Response {\n\t\tcanopus.PrintMessage(req.GetMessage())\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Data from :5685 -- \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\tserver.ListenAndServe(\":5685\")\n\t<-make(chan struct{})\n}\n"
  },
  {
    "path": "examples/proxy/http/client.go",
    "content": "package main\n\nimport \"github.com/zubairhamed/canopus\"\n\nfunc main() {\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get, canopus.GenerateMessageID())\n\treq.SetProxyURI(\"https://httpbin.org/get\")\n\n\tcanopus.PrintMessage(req.GetMessage())\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tprintln(\"err\", err)\n\t}\n\tcanopus.PrintMessage(resp.GetMessage())\n}\n"
  },
  {
    "path": "examples/proxy/http/server.go",
    "content": "package main\n\nimport \"github.com/zubairhamed/canopus\"\n\nfunc main() {\n\tserver := canopus.NewServer()\n\tserver.ProxyOverHttp(true)\n\n\tserver.ListenAndServe(\":5683\")\n\t<-make(chan struct{})\n\n}\n"
  },
  {
    "path": "examples/simple/client.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tfmt.Println(\"Connecting to CoAP Server\")\n\tconn, err := canopus.Dial(\"localhost:5683\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treq := canopus.NewRequest(canopus.MessageConfirmable, canopus.Get)\n\treq.SetStringPayload(\"Hello, canopus\")\n\treq.SetRequestURI(\"/hello\")\n\n\tfmt.Println(\"Sending request..\")\n\tresp, err := conn.Send(req)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tfmt.Println(\"Got Response:\" + resp.GetMessage().GetPayload().String())\n}\n"
  },
  {
    "path": "examples/simple/server.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\n\t\"github.com/zubairhamed/canopus\"\n)\n\nfunc main() {\n\tserver := canopus.NewServer()\n\n\tserver.Get(\"/hello\", func(req canopus.Request) canopus.Response {\n\t\tlog.Println(\"Hello Called\")\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged with response : \" + req.GetMessage().GetPayload().String())\n\n\t\tres := canopus.NewResponse(msg, nil)\n\t\treturn res\n\t})\n\n\tserver.Post(\"/hello\", func(req canopus.Request) canopus.Response {\n\t\tlog.Println(\"Hello Called via POST\")\n\n\t\tmsg := canopus.ContentMessage(req.GetMessage().GetMessageId(), canopus.MessageAcknowledgment)\n\t\tmsg.SetStringPayload(\"Acknowledged: \" + req.GetMessage().GetPayload().String())\n\t\tres := canopus.NewResponse(msg, nil)\n\t\treturn res\n\t})\n\n\tserver.Get(\"/basic\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.NewMessageOfType(canopus.MessageAcknowledgment, req.GetMessage().GetMessageId(), canopus.NewPlainTextPayload(\"Acknowledged\"))\n\t\tres := canopus.NewResponse(msg, nil)\n\t\treturn res\n\t})\n\n\tserver.Get(\"/basic/json\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.NewMessageOfType(canopus.MessageAcknowledgment, req.GetMessage().GetMessageId(), nil)\n\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\n\tserver.Get(\"/basic/xml\", func(req canopus.Request) canopus.Response {\n\t\tmsg := canopus.NewMessageOfType(canopus.MessageAcknowledgment, req.GetMessage().GetMessageId(), nil)\n\t\tres := canopus.NewResponse(msg, nil)\n\n\t\treturn res\n\t})\n\n\tserver.OnMessage(func(msg canopus.Message, inbound bool) {\n\t\tcanopus.PrintMessage(msg)\n\t})\n\n\tserver.ListenAndServe(\":5683\")\n\t<-make(chan struct{})\n}\n"
  },
  {
    "path": "init.go",
    "content": "package canopus\n\nfunc init() {\n\n}\n"
  },
  {
    "path": "json.go",
    "content": "package canopus\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n)\n\nfunc NewJSONPayload(obj interface{}) MessagePayload {\n\treturn &JSONPayload{\n\t\tobj: obj,\n\t}\n}\n\n// Represents a message payload containing JSON String\ntype JSONPayload struct {\n\tobj interface{}\n}\n\nfunc (p *JSONPayload) GetBytes() []byte {\n\to, err := json.MarshalIndent(p.obj, \"\", \"   \")\n\n\tif err != nil {\n\t\tlog.Println(err)\n\n\t\treturn []byte{}\n\t}\n\n\treturn []byte(string(o))\n}\n\nfunc (p *JSONPayload) Length() int {\n\treturn 0\n}\n\nfunc (p *JSONPayload) String() string {\n\to, _ := json.Marshal(p.obj)\n\n\treturn string(o)\n}\n"
  },
  {
    "path": "message.go",
    "content": "package canopus\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"log\"\n\t\"sort\"\n\t\"strings\"\n)\n\n// Instantiates a new message object\n// messageType (e.g. Confirm/Non-Confirm)\n// CoAP code\t404 - Not found etc\n// Message ID\tuint16 unique id\nfunc NewMessage(messageType uint8, code CoapCode, messageID uint16) Message {\n\treturn &CoapMessage{\n\t\tMessageType: messageType,\n\t\tMessageID:   messageID,\n\t\tCode:        code,\n\t\tToken:       []byte(GenerateToken(8)),\n\t}\n}\n\n// Instantiates an empty message with a given message id\nfunc NewEmptyMessage(id uint16) Message {\n\tmsg := NewMessageOfType(MessageAcknowledgment, id, nil)\n\n\treturn msg\n}\n\n// Instantiates an empty message of a specific type and message id\nfunc NewMessageOfType(t uint8, id uint16, payload MessagePayload) Message {\n\treturn &CoapMessage{\n\t\tMessageType: t,\n\t\tMessageID:   id,\n\t\tPayload:     payload,\n\t}\n}\n\n/*\n     0                   1                   2                   3\n    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n   |Ver| T |  TKL  |      Code     |          Message ID           |\n   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n   |   Token (if any, TKL bytes) ...\n   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n   |   Options (if any) ...\n   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n   |1 1 1 1 1 1 1 1|    Payload (if any) ...\n   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n*/\n\n// Converts an array of bytes to a Mesasge object.\n// An error is returned if a parsing error occurs\nfunc BytesToMessage(data []byte) (Message, error) {\n\tmsg := &CoapMessage{}\n\n\tdataLen := len(data)\n\tif dataLen < 4 {\n\t\treturn msg, ErrPacketLengthLessThan4\n\t}\n\n\tver := data[DataHeader] >> 6\n\tif ver != 1 {\n\t\treturn nil, ErrInvalidCoapVersion\n\t}\n\n\tmsg.MessageType = data[DataHeader] >> 4 & 0x03\n\ttokenLength := data[DataHeader] & 0x0f\n\tmsg.Code = CoapCode(data[DataCode])\n\n\tmsg.MessageID = binary.BigEndian.Uint16(data[DataMsgIDStart:DataMsgIDEnd])\n\n\t// Token\n\tif tokenLength > 0 {\n\t\tmsg.Token = make([]byte, tokenLength)\n\t\ttoken := data[DataTokenStart : DataTokenStart+tokenLength]\n\t\tcopy(msg.Token, token)\n\t}\n\n\t/*\n\t    0   1   2   3   4   5   6   7\n\t   +---------------+---------------+\n\t   |               |               |\n\t   |  Option Delta | Option Length |   1 byte\n\t   |               |               |\n\t   +---------------+---------------+\n\t   \\                               \\\n\t   /         Option Delta          /   0-2 bytes\n\t   \\          (extended)           \\\n\t   +-------------------------------+\n\t   \\                               \\\n\t   /         Option Length         /   0-2 bytes\n\t   \\          (extended)           \\\n\t   +-------------------------------+\n\t   \\                               \\\n\t   /                               /\n\t   \\                               \\\n\t   /         Option Value          /   0 or more bytes\n\t   \\                               \\\n\t   /                               /\n\t   \\                               \\\n\t   +-------------------------------+\n\t*/\n\n\ttmp := data[DataTokenStart+msg.GetTokenLength():]\n\n\tlastOptionID := uint(0)\n\tfor len(tmp) > 0 {\n\t\tif tmp[0] == PayloadMarker {\n\t\t\ttmp = tmp[1:]\n\t\t\tbreak\n\t\t}\n\n\t\toptionDelta := uint(tmp[0] >> 4)\n\t\toptionLength := uint(tmp[0] & 0x0f)\n\n\t\ttmp = tmp[1:]\n\t\tswitch optionDelta {\n\t\tcase 13:\n\t\t\toptionDeltaExtended := uint(tmp[0])\n\t\t\toptionDelta += optionDeltaExtended\n\t\t\ttmp = tmp[1:]\n\t\t\tbreak\n\n\t\tcase 14:\n\t\t\toptionDeltaExtended := uint(decodeInt(tmp[:2]))\n\t\t\toptionDelta = uint(optionDeltaExtended + 269)\n\t\t\ttmp = tmp[2:]\n\t\t\tbreak\n\n\t\tcase 15:\n\t\t\treturn msg, ErrOptionDeltaUsesValue15\n\t\t}\n\t\tlastOptionID += optionDelta\n\n\t\tswitch optionLength {\n\t\tcase 13:\n\t\t\toptionLengthExtended := uint(tmp[0])\n\t\t\toptionLength += optionLengthExtended\n\t\t\ttmp = tmp[1:]\n\t\t\tbreak\n\n\t\tcase 14:\n\t\t\toptionLengthExtended := uint(decodeInt(tmp[:1]))\n\t\t\toptionLength += uint(optionLengthExtended - uint(269))\n\t\t\ttmp = tmp[2:]\n\t\t\tbreak\n\n\t\tcase 15:\n\t\t\treturn msg, ErrOptionLengthUsesValue15\n\t\t}\n\n\t\toptCode := OptionCode(lastOptionID)\n\t\tif optionLength > 0 {\n\t\t\toptionValue := tmp[:optionLength]\n\n\t\t\tswitch optCode {\n\t\t\tcase OptionURIPort, OptionContentFormat, OptionMaxAge, OptionAccept, OptionSize1,\n\t\t\t\tOptionSize2, OptionBlock1, OptionBlock2:\n\t\t\t\tmsg.Options = append(msg.Options, NewOption(optCode, decodeInt(optionValue)))\n\t\t\t\tbreak\n\n\t\t\tcase OptionURIHost, OptionEtag, OptionLocationPath, OptionURIPath, OptionURIQuery,\n\t\t\t\tOptionLocationQuery, OptionProxyURI, OptionProxyScheme, OptionObserve:\n\t\t\t\tmsg.Options = append(msg.Options, NewOption(optCode, string(optionValue)))\n\t\t\t\tbreak\n\n\t\t\tdefault:\n\t\t\t\tif lastOptionID&0x01 == 1 {\n\t\t\t\t\tlog.Println(\"Unknown Critical Option id \", lastOptionID)\n\t\t\t\t\treturn msg, ErrUnknownCriticalOption\n\t\t\t\t}\n\t\t\t\tlog.Println(\"Warning: Unknown Option id \", optionDelta)\n\t\t\t\tmsg.Options = append(msg.Options, NewOption(optCode, optionValue))\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttmp = tmp[optionLength:]\n\t\t} else {\n\t\t\tmsg.Options = append(msg.Options, NewOption(optCode, nil))\n\t\t}\n\t}\n\tmsg.Payload = NewBytesPayload(tmp)\n\terr := ValidateMessage(msg)\n\n\treturn msg, err\n}\n\n// type to sort the coap options list (which is mandatory) prior to transmission\ntype SortOptions []Option\n\nfunc (opts SortOptions) Len() int {\n\treturn len(opts)\n}\n\nfunc (opts SortOptions) Swap(i, j int) {\n\topts[i], opts[j] = opts[j], opts[i]\n}\n\nfunc (opts SortOptions) Less(i, j int) bool {\n\treturn opts[i].GetCode() < opts[j].GetCode()\n}\n\n// Converts a message object to a byte array. Typically done prior to transmission\nfunc MessageToBytes(msg Message) ([]byte, error) {\n\tmessageID := []byte{0, 0}\n\tbinary.BigEndian.PutUint16(messageID, msg.GetMessageId())\n\n\tbuf := bytes.Buffer{}\n\tbuf.Write([]byte{(1 << 6) | (msg.GetMessageType() << 4) | 0x0f&msg.GetTokenLength()})\n\tbuf.Write([]byte{byte(msg.GetCode())})\n\tbuf.Write([]byte{messageID[0]})\n\tbuf.Write([]byte{messageID[1]})\n\tbuf.Write(msg.GetToken())\n\n\t// Sort Options\n\tsort.Sort(SortOptions(msg.GetAllOptions()))\n\n\tlastOptionCode := 0\n\tfor _, opt := range msg.GetAllOptions() {\n\t\toptCode := int(opt.GetCode())\n\t\toptDelta := optCode - lastOptionCode\n\t\toptDeltaValue, _ := getOptionHeaderValue(optDelta)\n\n\t\tbyteValue := valueToBytes(opt.GetValue())\n\t\tvalueLength := len(byteValue)\n\t\toptLength := valueLength\n\t\toptLengthValue, _ := getOptionHeaderValue(optLength)\n\n\t\tbuf.Write([]byte{byte(optDeltaValue<<4 | optLengthValue)})\n\n\t\tif optDeltaValue == 13 {\n\t\t\tbuf.Write([]byte{byte(optDelta - 13)})\n\t\t} else if optDeltaValue == 14 {\n\t\t\ttmpBuf := new(bytes.Buffer)\n\t\t\tbinary.Write(tmpBuf, binary.BigEndian, uint16(optDelta-269))\n\t\t\tbuf.Write(tmpBuf.Bytes())\n\t\t}\n\n\t\t// TODO: If optDeltaValue == 15, throw error\n\n\t\tif optLengthValue == 13 {\n\t\t\tbuf.Write([]byte{byte(optLength - 13)})\n\t\t} else if optLengthValue == 14 {\n\t\t\ttmpBuf := new(bytes.Buffer)\n\t\t\tbinary.Write(tmpBuf, binary.BigEndian, uint16(optLength-269))\n\t\t\tbuf.Write(tmpBuf.Bytes())\n\t\t}\n\n\t\tbuf.Write(byteValue)\n\t\tlastOptionCode = int(optCode)\n\t}\n\n\tif msg.GetPayload() != nil {\n\t\tif msg.GetPayload().Length() > 0 {\n\t\t\tbuf.Write([]byte{PayloadMarker})\n\t\t}\n\t\tbuf.Write(msg.GetPayload().GetBytes())\n\t}\n\treturn buf.Bytes(), nil\n}\n\nfunc getOptionHeaderValue(optValue int) (int, error) {\n\tswitch true {\n\tcase optValue <= 12:\n\t\treturn optValue, nil\n\n\tcase optValue <= 268:\n\t\treturn 13, nil\n\n\tcase optValue <= 65804:\n\t\treturn 14, nil\n\t}\n\treturn 0, errors.New(\"Invalid Option Delta\")\n}\n\n// Validates a message object and returns any error upon validation failure\nfunc ValidateMessage(msg Message) error {\n\tif msg.GetMessageType() > 3 {\n\t\treturn ErrUnknownMessageType\n\t}\n\n\tif msg.GetTokenLength() > 8 {\n\t\treturn ErrInvalidTokenLength\n\t}\n\n\t// Repeated Unrecognized Options\n\tfor _, opt := range msg.GetAllOptions() {\n\t\topts := msg.GetOptions(opt.GetCode())\n\n\t\tif len(opts) > 1 {\n\t\t\tif !IsRepeatableOption(opts[0]) {\n\t\t\t\tif opts[0].GetCode()&0x01 == 1 {\n\t\t\t\t\treturn ErrUnknownCriticalOption\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc NewBlockMessage() BlockMessage {\n\treturn &CoapBlockMessage{\n\t\tSequence: 0,\n\t}\n}\n\ntype CoapBlockMessage struct {\n\tCoapMessage\n\tMessageBuf []byte\n\tSequence   uint32\n}\n\ntype BySequence []*CoapBlockMessage\n\nfunc (o BySequence) Len() int {\n\treturn len(o)\n}\n\nfunc (o BySequence) Swap(i, j int) {\n\to[i], o[j] = o[j], o[i]\n}\n\nfunc (o BySequence) Less(i, j int) bool {\n\treturn o[i].Sequence < o[j].Sequence\n}\n\n// A Message object represents a CoAP payload\ntype CoapMessage struct {\n\tMessageType uint8\n\tCode        CoapCode\n\tMessageID   uint16\n\tPayload     MessagePayload\n\tToken       []byte\n\tOptions     []Option\n}\n\nfunc (m *CoapMessage) SetMessageType(t uint8) {\n\tm.MessageType = t\n}\n\nfunc (m *CoapMessage) SetToken(t []byte) {\n\tm.Token = t\n}\n\nfunc (m *CoapMessage) SetPayload(p MessagePayload) {\n\tm.Payload = p\n}\n\nfunc (m *CoapMessage) SetMessageId(id uint16) {\n\tm.MessageID = id\n}\n\nfunc (m *CoapMessage) GetToken() []byte {\n\treturn m.Token\n}\n\nfunc (m *CoapMessage) GetPayload() MessagePayload {\n\treturn m.Payload\n}\n\nfunc (m *CoapMessage) GetMessageType() uint8 {\n\treturn m.MessageType\n}\n\nfunc (m *CoapMessage) GetMessageId() uint16 {\n\treturn m.MessageID\n}\n\nfunc (m *CoapMessage) GetCode() CoapCode {\n\treturn m.Code\n}\n\nfunc (m *CoapMessage) GetAllOptions() []Option {\n\treturn m.Options\n}\n\nfunc (m *CoapMessage) GetAcceptedContent() MediaType {\n\tmediaTypeCode := m.GetOption(OptionAccept).IntValue()\n\n\treturn MediaType(mediaTypeCode)\n}\n\nfunc (m *CoapMessage) GetCodeString() string {\n\tcodeClass := string(m.Code >> 5)\n\tcodeDetail := string(m.Code & 0x1f)\n\n\treturn codeClass + \".\" + codeDetail\n}\n\nfunc (m *CoapMessage) GetMethod() uint8 {\n\treturn (byte(m.Code) & 0x1f)\n}\n\nfunc (m *CoapMessage) GetTokenLength() uint8 {\n\treturn uint8(len(m.Token))\n}\n\nfunc (m *CoapMessage) GetTokenString() string {\n\treturn string(m.Token[:len(m.Token)])\n}\n\n// Returns an array of options given an option code\nfunc (m CoapMessage) GetOptions(id OptionCode) []Option {\n\tvar opts []Option\n\tfor _, val := range m.Options {\n\t\tif val.GetCode() == id {\n\t\t\topts = append(opts, val)\n\t\t}\n\t}\n\treturn opts\n}\n\n// Returns the first option found for a given option code\nfunc (m CoapMessage) GetOption(id OptionCode) Option {\n\tfor _, val := range m.Options {\n\t\tif val.GetCode() == id {\n\t\t\treturn val\n\t\t}\n\t}\n\treturn nil\n}\n\n// Attempts to return the string value of an Option\nfunc (m CoapMessage) GetOptionsAsString(id OptionCode) []string {\n\topts := m.GetOptions(id)\n\n\tvar str []string\n\tfor _, o := range opts {\n\t\tif o.GetValue() != nil {\n\t\t\tstr = append(str, o.GetValue().(string))\n\t\t}\n\t}\n\treturn str\n}\n\n// Returns the string value of the Location Path Options by joining and defining a / separator\nfunc (m *CoapMessage) GetLocationPath() string {\n\topts := m.GetOptionsAsString(OptionLocationPath)\n\n\treturn strings.Join(opts, \"/\")\n}\n\n// Returns the string value of the Uri Path Options by joining and defining a / separator\nfunc (m CoapMessage) GetURIPath() string {\n\topts := m.GetOptionsAsString(OptionURIPath)\n\n\treturn \"/\" + strings.Join(opts, \"/\")\n}\n\n// Add an Option to the message. If an option is not repeatable, it will replace\n// any existing defined Option of the same type\nfunc (m *CoapMessage) AddOption(code OptionCode, value interface{}) {\n\topt := NewOption(code, value)\n\tif IsRepeatableOption(opt) {\n\t\tm.Options = append(m.Options, opt)\n\t} else {\n\t\tm.RemoveOptions(code)\n\t\tm.Options = append(m.Options, opt)\n\t}\n}\n\n// Add an array of Options to the message. If an option is not repeatable, it will replace\n// any existing defined Option of the same type\nfunc (m *CoapMessage) AddOptions(opts []Option) {\n\tfor _, opt := range opts {\n\t\tif IsRepeatableOption(opt) {\n\t\t\tm.Options = append(m.Options, opt)\n\t\t} else {\n\t\t\tm.RemoveOptions(opt.GetCode())\n\t\t\tm.Options = append(m.Options, opt)\n\t\t}\n\t}\n}\n\nfunc (c *CoapMessage) SetBlock1Option(opt Option) {\n\tc.AddOption(OptionBlock1, opt.GetValue())\n}\n\n// Copies the given list of options from another message to this one\nfunc (m *CoapMessage) CloneOptions(cm Message, opts ...OptionCode) {\n\tfor _, opt := range opts {\n\t\tm.AddOptions(cm.GetOptions(opt))\n\t}\n}\n\n// Replace an Option\nfunc (m *CoapMessage) ReplaceOptions(code OptionCode, opts []Option) {\n\tm.RemoveOptions(code)\n\n\tm.AddOptions(opts)\n}\n\n// Removes an Option\nfunc (m *CoapMessage) RemoveOptions(id OptionCode) {\n\tvar opts []Option\n\tfor _, opt := range m.Options {\n\t\tif opt.GetCode() != id {\n\t\t\topts = append(opts, opt)\n\t\t}\n\t}\n\tm.Options = opts\n}\n\n// Adds a string payload\nfunc (m *CoapMessage) SetStringPayload(s string) {\n\tm.Payload = NewPlainTextPayload(s)\n}\n\n// Determines if a message contains options for proxying (i.e. Proxy-Scheme or Proxy-Uri)\nfunc IsProxyRequest(msg Message) bool {\n\tif msg.GetOption(OptionProxyScheme) != nil || msg.GetOption(OptionProxyURI) != nil {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc valueToBytes(value interface{}) []byte {\n\tvar v uint32\n\n\tswitch i := value.(type) {\n\tcase string:\n\t\treturn []byte(i)\n\tcase []byte:\n\t\treturn i\n\tcase MediaType:\n\t\tv = uint32(i)\n\tcase byte:\n\t\tv = uint32(i)\n\tcase int:\n\t\tv = uint32(i)\n\tcase int32:\n\t\tv = uint32(i)\n\tcase uint:\n\t\tv = uint32(i)\n\tcase uint32:\n\t\tv = i\n\tdefault:\n\t\tbreak\n\t}\n\n\treturn encodeInt(v)\n}\n\nfunc decodeInt(b []byte) uint32 {\n\ttmp := []byte{0, 0, 0, 0}\n\tcopy(tmp[4-len(b):], b)\n\n\treturn binary.BigEndian.Uint32(tmp)\n}\n\nfunc encodeInt(v uint32) []byte {\n\tswitch {\n\tcase v == 0:\n\t\treturn nil\n\n\tcase v < 256:\n\t\treturn []byte{byte(v)}\n\n\tcase v < 65536:\n\t\trv := []byte{0, 0}\n\t\tbinary.BigEndian.PutUint16(rv, uint16(v))\n\t\treturn rv\n\n\tcase v < 16777216:\n\t\trv := []byte{0, 0, 0, 0}\n\t\tbinary.BigEndian.PutUint32(rv, uint32(v))\n\t\treturn rv[1:]\n\n\tdefault:\n\t\trv := []byte{0, 0, 0, 0}\n\t\tbinary.BigEndian.PutUint32(rv, uint32(v))\n\t\treturn rv\n\t}\n}\n\n// Determines if a message contains URI targeting a CoAP resource\nfunc IsCoapURI(uri string) bool {\n\tif strings.HasPrefix(uri, \"coap\") || strings.HasPrefix(uri, \"coaps\") {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Determines if a message contains URI targeting an HTTP resource\nfunc IsHTTPURI(uri string) bool {\n\tif strings.HasPrefix(uri, \"http\") || strings.HasPrefix(uri, \"https\") {\n\t\treturn true\n\t}\n\treturn false\n\n}\n\n// Gets the string representation of a CoAP Method code (e.g. GET, PUT, DELETE etc)\nfunc MethodString(c CoapCode) string {\n\tswitch c {\n\tcase Get:\n\t\treturn \"GET\"\n\n\tcase Delete:\n\t\treturn \"DELETE\"\n\n\tcase Post:\n\t\treturn \"POST\"\n\n\tcase Put:\n\t\treturn \"PUT\"\n\t}\n\treturn \"\"\n}\n\n// Response Code Messages\n// Creates a Non-Confirmable Empty Message\nfunc EmptyMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeEmpty, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 201 - Created\nfunc CreatedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeCreated, messageID)\n}\n\n// // Creates a Non-Confirmable with CoAP Code 202 - Deleted\nfunc DeletedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeDeleted, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 203 - Valid\nfunc ValidMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeValid, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 204 - Changed\nfunc ChangedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeChanged, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 205 - Content\nfunc ContentMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeContent, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 400 - Bad Request\nfunc BadRequestMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeBadRequest, messageID)\n}\n\nfunc ContinueMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeContinue, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 401 - Unauthorized\nfunc UnauthorizedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeUnauthorized, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 402 - Bad Option\nfunc BadOptionMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeBadOption, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 403 - Forbidden\nfunc ForbiddenMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeForbidden, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 404 - Not Found\nfunc NotFoundMessage(messageID uint16, messageType uint8, token []byte) (m Message) {\n\tm = NewMessage(messageType, CoapCodeNotFound, messageID)\n\tm.SetToken(token)\n\n\treturn\n}\n\n// Creates a Non-Confirmable with CoAP Code 405 - Method Not Allowed\nfunc MethodNotAllowedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeMethodNotAllowed, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 406 - Not Acceptable\nfunc NotAcceptableMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeNotAcceptable, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 409 - Conflict\nfunc ConflictMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeConflict, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 412 - Precondition Failed\nfunc PreconditionFailedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodePreconditionFailed, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 413 - Request Entity Too Large\nfunc RequestEntityTooLargeMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeRequestEntityTooLarge, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 415 - Unsupported Content Format\nfunc UnsupportedContentFormatMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeUnsupportedContentFormat, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 500 - Internal Server Error\nfunc InternalServerErrorMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeInternalServerError, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 501 - Not Implemented\nfunc NotImplementedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeNotImplemented, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 502 - Bad Gateway\nfunc BadGatewayMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeBadGateway, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 503 - Service Unavailable\nfunc ServiceUnavailableMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeServiceUnavailable, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 504 - Gateway Timeout\nfunc GatewayTimeoutMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeGatewayTimeout, messageID)\n}\n\n// Creates a Non-Confirmable with CoAP Code 505 - Proxying Not Supported\nfunc ProxyingNotSupportedMessage(messageID uint16, messageType uint8) Message {\n\treturn NewMessage(messageType, CoapCodeProxyingNotSupported, messageID)\n}\n"
  },
  {
    "path": "message_test.go",
    "content": "package canopus\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMessage(t *testing.T) {\n\tassert.NotNil(t, NewMessage(MessageConfirmable, Get, 12345))\n\tassert.NotNil(t, NewEmptyMessage(12345))\n}\n\nfunc TestInvalidMessage(t *testing.T) {\n\t_, err := BytesToMessage(make([]byte, 0))\n\n\tassert.NotNil(t, err, \"Message should be invalid\")\n\n\t_, err = BytesToMessage(make([]byte, 4))\n\tassert.NotNil(t, err, \"Message should be invalid\")\n}\n\nfunc TestMessagValidation(t *testing.T) {\n\t// ValidateMessage()\n}\n\nfunc TestMessageConversion(t *testing.T) {\n\n\tmsg := NewBasicConfirmableMessage()\n\tmsgId := msg.GetMessageId()\n\n\t// Byte 1\n\tmsg.AddOption(OptionContentFormat, MediaTypeApplicationLinkFormat)\n\n\t// Convert Message to BYte\n\tb, err := MessageToBytes(msg)\n\n\t// Reconvert Back Bytes to Message\n\tnewMsg, err := BytesToMessage(b)\n\tassert.Nil(t, err, \"An error occured converting bytes to message\")\n\n\tassert.Equal(t, 0, int(newMsg.GetMessageType())) // 0x0: Type Confirmable\n\tassert.Equal(t, \"abcd1234\", bytes.NewBuffer(newMsg.GetToken()).String(), \"Token String not the same after reconversion\")\n\tassert.Equal(t, int(msgId), int(newMsg.GetMessageId()), \"Message ID not the same after reconversion\")\n\tassert.NotEqual(t, 0, len(newMsg.GetAllOptions()), \"Options should not be 0\")\n}\n\nfunc TestMessageBadOptions(t *testing.T) {\n\t//\ttestMsg := NewBasicConfirmableMessage()\n\n\t// Unknown Critical Option\n\t//\tunk := OptionCode(99)\n\t//\ttestMsg.AddOption(unk, 0)\n\t//\ttestMsg.AddOption(OPTION_CONTENT_FORMAT, MEDIATYPE_APPLICATION_LINK_FORMAT)\n\t//\t_, err := MessageToBytes(testMsg)\n\t//\tassert.NotNil(t, err, \"Should throw ERR_UNKNOWN_CRITICAL_OPTION\")\n}\n\nfunc TestMessageObject(t *testing.T) {\n\tmsg := &CoapMessage{}\n\n\tassert.Equal(t, 0, len(msg.Options))\n\tmsg.AddOptions(NewPathOptions(\"/example\"))\n\tmsg.AddOption(OptionAccept, MediaTypeApplicationXML)\n\tmsg.AddOption(OptionContentFormat, MediaTypeApplicationJSON)\n\tassert.Equal(t, 3, len(msg.Options))\n\n\topt := msg.GetOption(OptionAccept)\n\tassert.NotNil(t, opt)\n\n\tmsg.RemoveOptions(OptionURIPath)\n\tassert.Equal(t, 2, len(msg.Options))\n}\n\nfunc TestOptionConversion(t *testing.T) {\n\tpreMsg := NewBasicConfirmableMessage()\n\n\tpreMsg.AddOption(OptionIfMatch, \"\")\n\tpreMsg.AddOptions(NewPathOptions(\"/test\"))\n\tpreMsg.AddOption(OptionEtag, \"1234567890\")\n\tpreMsg.AddOption(OptionIfNoneMatch, nil)\n\tpreMsg.AddOption(OptionObserve, 0)\n\tpreMsg.AddOption(OptionURIPort, 1234)\n\tpreMsg.AddOption(OptionLocationPath, \"/aaa\")\n\tpreMsg.AddOption(OptionContentFormat, 1)\n\tpreMsg.AddOption(OptionMaxAge, 1)\n\tpreMsg.AddOption(OptionProxyURI, \"http://www.google.com\")\n\tpreMsg.AddOption(OptionProxyScheme, \"http://proxy.scheme\")\n\n\tconverted, _ := MessageToBytes(preMsg)\n\n\tpostMsg, _ := BytesToMessage(converted)\n\n\tPrintMessage(postMsg)\n}\n\nfunc TestNewMessageHelpers(t *testing.T) {\n\tvar messageID uint16 = 12345\n\n\ttestData := []struct {\n\t\tmsg  Message\n\t\tcode CoapCode\n\t}{\n\t\t{EmptyMessage(messageID, MessageAcknowledgment), CoapCodeEmpty},\n\t\t{CreatedMessage(messageID, MessageAcknowledgment), CoapCodeCreated},\n\t\t{DeletedMessage(messageID, MessageAcknowledgment), CoapCodeDeleted},\n\t\t{ValidMessage(messageID, MessageAcknowledgment), CoapCodeValid},\n\t\t{ChangedMessage(messageID, MessageAcknowledgment), CoapCodeChanged},\n\t\t{ContentMessage(messageID, MessageAcknowledgment), CoapCodeContent},\n\t\t{BadRequestMessage(messageID, MessageAcknowledgment), CoapCodeBadRequest},\n\t\t{UnauthorizedMessage(messageID, MessageAcknowledgment), CoapCodeUnauthorized},\n\t\t{BadOptionMessage(messageID, MessageAcknowledgment), CoapCodeBadOption},\n\t\t{ForbiddenMessage(messageID, MessageAcknowledgment), CoapCodeForbidden},\n\t\t{NotFoundMessage(messageID, MessageAcknowledgment, nil), CoapCodeNotFound},\n\t\t{MethodNotAllowedMessage(messageID, MessageAcknowledgment), CoapCodeMethodNotAllowed},\n\t\t{NotAcceptableMessage(messageID, MessageAcknowledgment), CoapCodeNotAcceptable},\n\t\t{ConflictMessage(messageID, MessageAcknowledgment), CoapCodeConflict},\n\t\t{PreconditionFailedMessage(messageID, MessageAcknowledgment), CoapCodePreconditionFailed},\n\t\t{RequestEntityTooLargeMessage(messageID, MessageAcknowledgment), CoapCodeRequestEntityTooLarge},\n\t\t{UnsupportedContentFormatMessage(messageID, MessageAcknowledgment), CoapCodeUnsupportedContentFormat},\n\t\t{InternalServerErrorMessage(messageID, MessageAcknowledgment), CoapCodeInternalServerError},\n\t\t{NotImplementedMessage(messageID, MessageAcknowledgment), CoapCodeNotImplemented},\n\t\t{BadGatewayMessage(messageID, MessageAcknowledgment), CoapCodeBadGateway},\n\t\t{ServiceUnavailableMessage(messageID, MessageAcknowledgment), CoapCodeServiceUnavailable},\n\t\t{GatewayTimeoutMessage(messageID, MessageAcknowledgment), CoapCodeGatewayTimeout},\n\t\t{ProxyingNotSupportedMessage(messageID, MessageAcknowledgment), CoapCodeProxyingNotSupported},\n\t}\n\n\tfor _, td := range testData {\n\t\tassert.NotNil(t, td.msg)\n\t\tassert.Equal(t, td.code, td.msg.GetCode())\n\t}\n}\n\nfunc NewBasicConfirmableMessage() *CoapMessage {\n\tmsg := NewMessageOfType(MessageConfirmable, GenerateMessageID(), nil).(*CoapMessage)\n\tmsg.Code = Get\n\tmsg.Token = []byte(\"abcd1234\")\n\tmsg.SetStringPayload(\"xxxxx\")\n\n\treturn msg\n}\n"
  },
  {
    "path": "options.go",
    "content": "package canopus\n\nimport (\n\t\"math\"\n\t\"strings\"\n)\n\n// Represents an Option for a CoAP Message\ntype CoapOption struct {\n\tCode  OptionCode\n\tValue interface{}\n}\n\nfunc (o *CoapOption) GetValue() interface{} {\n\treturn o.Value\n}\n\nfunc (o *CoapOption) GetCode() OptionCode {\n\treturn o.Code\n}\n\nfunc (o *CoapOption) Name() string {\n\treturn \"Name of option\"\n}\n\n// Determines if an option is elective\nfunc (o *CoapOption) IsElective() bool {\n\tif (int(o.Code) % 2) != 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Determines if an option is critical\nfunc (o *CoapOption) IsCritical() bool {\n\tif (int(o.Code) % 2) != 0 {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Returns the string value of an option\nfunc (o *CoapOption) StringValue() string {\n\treturn o.Value.(string)\n}\n\nfunc (o *CoapOption) IntValue() int {\n\treturn o.Value.(int)\n}\n\n// Instantiates a New Option\nfunc NewOption(optionNumber OptionCode, optionValue interface{}) *CoapOption {\n\treturn &CoapOption{\n\t\tCode:  optionNumber,\n\t\tValue: optionValue,\n\t}\n}\n\n// Creates an array of options decomposed from a given path\nfunc NewPathOptions(path string) []Option {\n\topts := []Option{}\n\tps := strings.Split(path, \"/\")\n\n\tfor _, p := range ps {\n\t\tif p != \"\" {\n\t\t\topt := NewOption(OptionURIPath, p)\n\t\t\topts = append(opts, opt)\n\t\t}\n\t}\n\treturn opts\n}\n\n// Checks if an option is repeatable\nfunc IsRepeatableOption(opt Option) bool {\n\n\tswitch opt.GetCode() {\n\n\tcase OptionIfMatch, OptionEtag, OptionURIPort, OptionLocationPath, OptionURIPath, OptionURIQuery, OptionLocationQuery,\n\t\tOptionBlock2, OptionBlock1:\n\t\treturn true\n\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// Checks if an option/option code is recognizable/valid\nfunc IsValidOption(opt Option) bool {\n\tswitch opt.GetCode() {\n\n\tcase OptionIfNoneMatch, OptionURIHost,\n\t\tOptionEtag, OptionIfMatch, OptionObserve, OptionURIPort, OptionLocationPath,\n\t\tOptionURIPath, OptionContentFormat, OptionMaxAge, OptionURIQuery, OptionAccept,\n\t\tOptionLocationQuery, OptionBlock2, OptionBlock1, OptionProxyURI, OptionProxyScheme, OptionSize1:\n\t\treturn true\n\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// Determines if an option is elective\nfunc IsElectiveOption(opt Option) bool {\n\ti := int(opt.GetCode())\n\n\tif (i & 1) == 1 {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Determines if an option is critical\nfunc IsCriticalOption(opt Option) bool {\n\treturn !IsElectiveOption(opt)\n}\n\nfunc NewBlock1Option(bs BlockSizeType, more bool, seq uint32) *Block1Option {\n\topt := &Block1Option{}\n\topt.Code = OptionBlock1\n\n\tval := seq\n\n\tval = val << 4\n\tif more {\n\t\tval |= (1 << 3)\n\t}\n\n\tval |= (uint32(bs) << 0)\n\n\topt.Value = val\n\n\t/*\n\t\tBLockSize\n\t\tval := o.Value.(uint)\n\t\texp := val & 0x07\n\n\t\treturn math.Exp2(float64(exp + 4))\n\n\t\tMore\n\t\tval := o.Value.(uint)\n\n\t\treturn ((val >> 3) & 0x01) == 1\n\n\t*/\n\n\treturn opt\n}\n\nfunc Block1OptionFromOption(opt Option) *Block1Option {\n\tblockOpt := &Block1Option{}\n\n\tblockOpt.Value = opt.GetValue()\n\tblockOpt.Code = opt.GetCode()\n\n\treturn blockOpt\n}\n\ntype Block1Option struct {\n\tCoapOption\n}\n\nfunc (o *Block1Option) Sequence() uint32 {\n\tval := o.GetValue().(uint32)\n\n\treturn val >> 4\n}\n\nfunc (o *Block1Option) Exponent() uint32 {\n\tval := o.GetValue().(uint32)\n\n\treturn val & 0x07\n}\n\nfunc (o *Block1Option) BlockSizeLength() uint32 {\n\tsz := uint32(o.Size()) + 4\n\n\treturn sz * sz\n}\n\nfunc (o *Block1Option) Size() BlockSizeType {\n\tval := o.GetValue().(uint32)\n\texp := val & 0x07\n\n\treturn BlockSizeType(byte(math.Exp2(float64(exp + 4))))\n}\n\nfunc (o *Block1Option) HasMore() bool {\n\tval := o.Value.(uint32)\n\n\treturn ((val >> 3) & 0x01) == 1\n}\n"
  },
  {
    "path": "plaintext.go",
    "content": "package canopus\n\nimport \"bytes\"\n\n// Instantiates a new message payload of type string\nfunc NewPlainTextPayload(s string) MessagePayload {\n\treturn &PlainTextPayload{\n\t\tcontent: s,\n\t}\n}\n\n// Represents a message payload containing string value\ntype PlainTextPayload struct {\n\tcontent string\n}\n\nfunc (p *PlainTextPayload) GetBytes() []byte {\n\treturn bytes.NewBufferString(p.content).Bytes()\n}\n\nfunc (p *PlainTextPayload) Length() int {\n\treturn len(p.content)\n}\n\nfunc (p *PlainTextPayload) String() string {\n\treturn p.content\n}\n"
  },
  {
    "path": "proxy.go",
    "content": "package canopus\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\nfunc NullProxyFilter(Message, net.Addr) bool {\n\treturn true\n}\n\n// The default handler when proxying is disabled\nfunc NullProxyHandler(c CoapServer, msg Message, session Session) {\n\tSendMessage(ProxyingNotSupportedMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n}\n\nfunc COAPProxyHandler(c CoapServer, msg Message, session Session) {\n\tproxyURI := msg.GetOption(OptionProxyURI).StringValue()\n\n\tparsedURL, err := url.Parse(proxyURI)\n\tif err != nil {\n\t\tlog.Println(\"Error parsing proxy URI\")\n\t\tSendMessage(BadGatewayMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n\t\treturn\n\t}\n\tclientConn, err := Dial(parsedURL.Host)\n\n\tmsg.RemoveOptions(OptionProxyURI)\n\treq := NewRequestFromMessage(msg).(*CoapRequest)\n\treq.SetRequestURI(parsedURL.RequestURI())\n\n\tresponse, err := clientConn.Send(req)\n\tif err != nil {\n\t\tSendMessage(BadGatewayMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n\t\tclientConn.Close()\n\t\treturn\n\t}\n\n\t_, err = SendMessage(response.GetMessage(), session)\n\tif err != nil {\n\t\tlog.Println(\"Error occured responding to proxy request\")\n\t\tclientConn.Close()\n\t\treturn\n\t}\n\tclientConn.Close()\n}\n\n// Handles requests for proxying from CoAP to HTTP\nfunc HTTPProxyHandler(c CoapServer, msg Message, session Session) {\n\tproxyURI := msg.GetOption(OptionProxyURI).StringValue()\n\trequestMethod := msg.GetCode()\n\n\tclient := &http.Client{}\n\treq, err := http.NewRequest(MethodString(CoapCode(msg.GetMethod())), proxyURI, nil)\n\tif err != nil {\n\t\tSendMessage(BadGatewayMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n\t\treturn\n\t}\n\n\tetag := msg.GetOption(OptionEtag)\n\tif etag != nil {\n\t\treq.Header.Add(\"ETag\", etag.StringValue())\n\t}\n\n\t// TODO: Set timeout handler, and on timeout return 5.04\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tSendMessage(BadGatewayMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n\t\treturn\n\t}\n\n\tdefer resp.Body.Close()\n\n\tcontents, _ := ioutil.ReadAll(resp.Body)\n\tmodifiedMsg := msg.(*CoapMessage)\n\tmodifiedMsg.SetPayload(NewBytesPayload(contents))\n\trespMsg := NewRequestFromMessage(modifiedMsg)\n\n\tif requestMethod == Get {\n\t\tetag := resp.Header.Get(\"ETag\")\n\t\tif etag != \"\" {\n\t\t\tmsg.AddOption(OptionEtag, etag)\n\t\t}\n\t}\n\n\t// TODO: Check payload length against Size1 options\n\tif len(respMsg.GetMessage().GetPayload().String()) > MaxPacketSize {\n\t\tSendMessage(BadGatewayMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n\t\treturn\n\t}\n\n\t_, err = SendMessage(respMsg.GetMessage(), session)\n\tif err != nil {\n\t\tprintln(err.Error())\n\t}\n}\n\n// Handles requests for proxying from HTTP to CoAP\nfunc HTTPCOAPProxyHandler(msg *Message, conn *net.UDPConn, addr net.Addr) {\n\tlog.Println(\"HttpCoapProxyHandler Proxy Handler\")\n}\n"
  },
  {
    "path": "request.go",
    "content": "package canopus\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Creates a New Request Instance\nfunc NewRequest(messageType uint8, messageMethod CoapCode) Request {\n\treturn NewRequestWithMessageId(messageType, messageMethod, GenerateMessageID())\n}\n\nfunc NewRequestWithMessageId(messageType uint8, messageMethod CoapCode, messageID uint16) Request {\n\tmsg := NewMessage(messageType, messageMethod, messageID)\n\treturn &CoapRequest{\n\t\tmsg: msg,\n\t}\n}\n\nfunc NewConfirmableGetRequest() Request {\n\treturn &CoapRequest{\n\t\tmsg: NewMessage(MessageConfirmable, Get, GenerateMessageID()),\n\t}\n}\n\nfunc NewConfirmablePostRequest() Request {\n\treturn &CoapRequest{\n\t\tmsg: NewMessage(MessageConfirmable, Post, GenerateMessageID()),\n\t}\n}\n\nfunc NewConfirmablePutRequest() Request {\n\treturn &CoapRequest{\n\t\tmsg: NewMessage(MessageConfirmable, Put, GenerateMessageID()),\n\t}\n}\n\nfunc NewConfirmableDeleteRequest() Request {\n\treturn &CoapRequest{\n\t\tmsg: NewMessage(MessageConfirmable, Delete, GenerateMessageID()),\n\t}\n}\n\n// Creates a new request messages from a CoAP Message\nfunc NewRequestFromMessage(msg Message) Request {\n\treturn &CoapRequest{\n\t\tmsg: msg,\n\t}\n}\n\nfunc NewClientRequestFromMessage(msg Message, attrs map[string]string, session Session) Request {\n\treturn &CoapRequest{\n\t\tmsg:     msg,\n\t\tattrs:   attrs,\n\t\tsession: session,\n\t}\n}\n\n// Wraps a CoAP Message as a Request\n// Provides various methods which proxies the Message object methods\ntype CoapRequest struct {\n\tmsg     Message\n\tattrs   map[string]string\n\tsession Session\n\tserver  *CoapServer\n}\n\nfunc (c *CoapRequest) SetProxyURI(uri string) {\n\tc.msg.AddOption(OptionProxyURI, uri)\n}\n\nfunc (c *CoapRequest) SetMediaType(mt MediaType) {\n\tc.msg.AddOption(OptionContentFormat, mt)\n}\n\nfunc (c *CoapRequest) GetSession() Session {\n\treturn c.session\n}\n\nfunc (c *CoapRequest) GetAttributes() map[string]string {\n\treturn c.attrs\n}\n\nfunc (c *CoapRequest) GetAttribute(o string) string {\n\treturn c.attrs[o]\n}\n\nfunc (c *CoapRequest) GetAttributeAsInt(o string) int {\n\tattr := c.GetAttribute(o)\n\ti, _ := strconv.Atoi(attr)\n\n\treturn i\n}\n\nfunc (c *CoapRequest) GetMessage() Message {\n\treturn c.msg\n}\n\nfunc (c *CoapRequest) SetStringPayload(s string) {\n\tc.msg.(*CoapMessage).SetPayload(NewPlainTextPayload(s))\n}\n\nfunc (c *CoapRequest) SetPayload(b []byte) {\n\tc.msg.(*CoapMessage).SetPayload(NewBytesPayload(b))\n}\n\nfunc (c *CoapRequest) SetRequestURI(uri string) {\n\tc.msg.AddOptions(NewPathOptions(uri))\n}\n\nfunc (c *CoapRequest) SetConfirmable(con bool) {\n\tif con {\n\t\tc.msg.(*CoapMessage).SetMessageType(MessageConfirmable)\n\t} else {\n\t\tc.msg.(*CoapMessage).SetMessageType(MessageNonConfirmable)\n\t}\n}\n\nfunc (c *CoapRequest) SetToken(t string) {\n\tc.msg.(*CoapMessage).SetToken([]byte(t))\n}\n\nfunc (c *CoapRequest) GetURIQuery(q string) string {\n\tqs := c.GetMessage().GetOptionsAsString(OptionURIQuery)\n\n\tfor _, o := range qs {\n\t\tps := strings.Split(o, \"=\")\n\t\tif len(ps) == 2 {\n\t\t\tif ps[0] == q {\n\t\t\t\treturn ps[1]\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc (c *CoapRequest) SetURIQuery(k string, v string) {\n\tc.GetMessage().AddOption(OptionURIQuery, k+\"=\"+v)\n}\n"
  },
  {
    "path": "request_test.go",
    "content": "package canopus\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRequest(t *testing.T) {\n\tvar req Request\n\tassert.NotNil(t, NewRequest(MessageConfirmable, Get, 12345))\n\n\tmsg := NewMessage(MessageConfirmable, Get, 12345)\n\n\treq = NewRequestFromMessage(msg)\n\tassert.NotNil(t, req)\n\n\tassert.Equal(t, Get, req.GetMessage().GetCode())\n\t// &net.UDPConn{}, &net.UDPAddr{}\n\tassert.NotNil(t, NewClientRequestFromMessage(msg, make(map[string]string), nil))\n\n\treq = NewRequest(MessageConfirmable, Get, 12345)\n\tassert.Equal(t, uint8(0), req.GetMessage().GetMessageType())\n\n\treq.SetConfirmable(false)\n\tassert.Equal(t, uint8(1), req.GetMessage().GetMessageType())\n}\n"
  },
  {
    "path": "response.go",
    "content": "package canopus\n\nimport (\n\t\"strings\"\n)\n\nfunc NoResponse() Response {\n\treturn NilResponse{}\n}\n\ntype NilResponse struct {\n}\n\nfunc (c NilResponse) GetMessage() Message {\n\treturn nil\n}\n\nfunc (c NilResponse) GetError() error {\n\treturn nil\n}\n\nfunc (c NilResponse) GetPayload() []byte {\n\treturn nil\n}\n\nfunc (c NilResponse) GetURIQuery(q string) string {\n\treturn \"\"\n}\n\n// Creates a new Response object with a Message object and any error messages\nfunc NewResponse(msg Message, err error) Response {\n\tresp := &DefaultResponse{\n\t\tmsg: msg,\n\t\terr: err,\n\t}\n\n\treturn resp\n}\n\n// Creates a new response object with a Message object\nfunc NewResponseWithMessage(msg Message) Response {\n\tresp := &DefaultResponse{\n\t\tmsg: msg,\n\t}\n\n\treturn resp\n}\n\ntype DefaultResponse struct {\n\tmsg Message\n\terr error\n}\n\nfunc (c *DefaultResponse) GetMessage() Message {\n\treturn c.msg\n}\n\nfunc (c *DefaultResponse) GetError() error {\n\treturn c.err\n}\n\nfunc (c *DefaultResponse) GetPayload() []byte {\n\treturn c.GetMessage().GetPayload().GetBytes()\n}\n\nfunc (c *DefaultResponse) GetURIQuery(q string) string {\n\tqs := c.GetMessage().GetOptionsAsString(OptionURIQuery)\n\n\tfor _, o := range qs {\n\t\tps := strings.Split(o, \"=\")\n\t\tif len(ps) == 2 {\n\t\t\tif ps[0] == q {\n\t\t\t\treturn ps[1]\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "response_test.go",
    "content": "package canopus\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestResponse(t *testing.T) {\n\tmsg := NewEmptyMessage(12345)\n\tmsg.SetStringPayload(\"hello canopus\")\n\tassert.NotNil(t, NewResponseWithMessage(msg))\n\n\tresponse := NewResponse(msg, ErrUnknownCriticalOption)\n\tassert.NotNil(t, response)\n\tassert.Equal(t, uint16(12345), response.GetMessage().GetMessageId())\n\tassert.Equal(t, ErrUnknownCriticalOption, response.GetError())\n\n}\n"
  },
  {
    "path": "routes.go",
    "content": "package canopus\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n)\n\n// CreateNewRoute creates a new Route object\nfunc CreateNewRegExRoute(path string, method string, fn RouteHandler) Route {\n\tvar re *regexp.Regexp\n\tregexpString := path\n\n\t// Dots\n\tre = regexp.MustCompile(`([^\\\\])\\.`)\n\tregexpString = re.ReplaceAllStringFunc(regexpString, func(m string) string {\n\t\treturn fmt.Sprintf(`%s\\.`, string(m[0]))\n\t})\n\n\t// Wildcard names\n\tre = regexp.MustCompile(`:[^/#?()\\.\\\\]+\\*`)\n\tregexpString = re.ReplaceAllStringFunc(regexpString, func(m string) string {\n\t\treturn fmt.Sprintf(\"(?P<%s>.+)\", m[1:len(m)-1])\n\t})\n\n\tre = regexp.MustCompile(`:[^/#?()\\.\\\\]+`)\n\tregexpString = re.ReplaceAllStringFunc(regexpString, func(m string) string {\n\t\treturn fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:len(m)])\n\t})\n\n\ts := fmt.Sprintf(`\\A%s\\z`, regexpString)\n\n\treturn &RegExRoute{\n\t\tAutoAck: false,\n\t\tPath:    path,\n\t\tMethod:  method,\n\t\tHandler: fn,\n\t\tRegEx:   regexp.MustCompile(s),\n\t}\n}\n\n// Route represents a CoAP Route/Resource\ntype RegExRoute struct {\n\tPath       string\n\tMethod     string\n\tHandler    RouteHandler\n\tRegEx      *regexp.Regexp\n\tAutoAck    bool\n\tMediaTypes []MediaType\n}\n\nfunc (r *RegExRoute) Matches(path string) (bool, map[string]string) {\n\tre := r.RegEx\n\tmatches := re.FindAllStringSubmatch(path, -1)\n\tattrs := make(map[string]string)\n\tif len(matches) > 0 {\n\t\tsubExp := re.SubexpNames()\n\t\tfor idx, exp := range subExp {\n\t\t\tattrs[exp] = matches[0][idx]\n\t\t}\n\t\treturn true, attrs\n\t}\n\treturn false, attrs\n}\n\nfunc (r *RegExRoute) GetMethod() string {\n\treturn r.Method\n}\n\nfunc (r *RegExRoute) GetMediaTypes() []MediaType {\n\treturn r.MediaTypes\n}\n\nfunc (r *RegExRoute) GetConfiguredPath() string {\n\treturn r.Path\n}\n\nfunc (r *RegExRoute) AutoAcknowledge() bool {\n\treturn r.AutoAck\n}\n\nfunc (r *RegExRoute) Handle(req Request) Response {\n\treturn r.Handler(req)\n}\n\n// MatchingRoute checks if a given path matches any defined routes/resources\nfunc MatchingRoute(path string, method string, cf interface{}, routes []Route) (Route, map[string]string, error) {\n\tfor _, route := range routes {\n\t\tif method == route.GetMethod() {\n\t\t\tmatch, attrs := route.Matches(path)\n\n\t\t\tif match {\n\t\t\t\tif len(route.GetMediaTypes()) > 0 {\n\t\t\t\t\tif cf == nil {\n\t\t\t\t\t\treturn route, attrs, ErrUnsupportedContentFormat\n\t\t\t\t\t}\n\n\t\t\t\t\tfoundMediaType := false\n\t\t\t\t\tfor _, o := range route.GetMediaTypes() {\n\t\t\t\t\t\tif uint32(o) == cf {\n\t\t\t\t\t\t\tfoundMediaType = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif !foundMediaType {\n\t\t\t\t\t\treturn route, attrs, ErrUnsupportedContentFormat\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn route, attrs, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil, ErrNoMatchingRoute\n}\n"
  },
  {
    "path": "routes_test.go",
    "content": "package canopus\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRoutes(t *testing.T) {\n\tvar route Route\n\tvar matches bool\n\n\troute = CreateNewRegExRoute(\"/\", \"GET\", nil)\n\tmatches, _ = route.Matches(\"/\")\n\tassert.True(t, matches)\n\n\troute = CreateNewRegExRoute(\"/test\", \"GET\", nil)\n\tmatches, _ = route.Matches(\"/\")\n\tassert.False(t, matches)\n\tmatches, _ = route.Matches(\"/test\")\n\tassert.True(t, matches)\n\n\troute = CreateNewRegExRoute(\"/test/:var\", \"GET\", nil)\n\tmatches, _ = route.Matches(\"/test/abc\")\n\tassert.True(t, matches)\n\tmatches, _ = route.Matches(\"/test/abc/def\")\n\tassert.False(t, matches)\n\n\troute = CreateNewRegExRoute(\"/test/:var/foo\", \"GET\", nil)\n\tmatches, _ = route.Matches(\"/test/abc/foo\")\n\tassert.True(t, matches)\n\tmatches, _ = route.Matches(\"/test/abc\")\n\tassert.False(t, matches)\n\tmatches, _ = route.Matches(\"/test/abc/def\")\n\tassert.False(t, matches)\n\tmatches, _ = route.Matches(\"/test//foo\")\n\tassert.False(t, matches)\n\tmatches, _ = route.Matches(\"/test/foo\")\n\tassert.False(t, matches)\n\n\troute = CreateNewRegExRoute(\"/test.abc/:var\", \"GET\", nil)\n\tmatches, _ = route.Matches(\"/test.abc/abc\")\n\tassert.True(t, matches)\n\tmatches, _ = route.Matches(\"/test.abc/abc/def\")\n\tassert.False(t, matches)\n}\n"
  },
  {
    "path": "server.go",
    "content": "package canopus\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"log\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar DTLS_SERVER_SESSIONS = make(map[int32]*DTLSServerSession)\nvar NEXT_SESSION_ID int32 = 0\nvar DTLS_CLIENT_CONNECTIONS = make(map[int32]*DTLSConnection)\n\ntype ServerConfiguration struct {\n\tEnableResourceDiscovery bool\n}\n\nfunc NewServer() CoapServer {\n\treturn createServer()\n}\n\nfunc createServer() CoapServer {\n\treturn &DefaultCoapServer{\n\t\tevents:                  NewEvents(),\n\t\tobservations:            make(map[string][]*Observation),\n\t\tfnHandleCOAPProxy:       NullProxyHandler,\n\t\tfnHandleHTTPProxy:       NullProxyHandler,\n\t\tfnProxyFilter:           NullProxyFilter,\n\t\tstopChannel:             make(chan int),\n\t\tcoapResponseChannelsMap: make(map[uint16]chan *CoapResponseChannel),\n\t\tmessageIds:              make(map[uint16]time.Time),\n\t\tincomingBlockMessages:   make(map[string]Message),\n\t\toutgoingBlockMessages:   make(map[string]Message),\n\t\tsessions:                make(map[string]Session),\n\t\tcreatedSession:          make(chan Session),\n\t}\n}\n\ntype DefaultCoapServer struct {\n\tmessageIds            map[uint16]time.Time\n\tincomingBlockMessages map[string]Message\n\toutgoingBlockMessages map[string]Message\n\n\troutes       []Route\n\tevents       Events\n\tobservations map[string][]*Observation\n\n\tfnHandleHTTPProxy ProxyHandler\n\tfnHandleCOAPProxy ProxyHandler\n\tfnProxyFilter     ProxyFilter\n\n\tstopChannel chan int\n\n\tcoapResponseChannelsMap map[uint16]chan *CoapResponseChannel\n\n\tsessions       map[string]Session\n\tcreatedSession chan Session\n\tserverConfig   *ServerConfiguration\n\n\tcookieSecret []byte\n\n\tfnPskHandler func(id string) []byte\n}\n\nfunc (s *DefaultCoapServer) DeleteSession(ssn Session) {\n\ts.closeSession(ssn)\n}\n\nfunc (s *DefaultCoapServer) HandlePSK(fn func(id string) []byte) {\n\ts.fnPskHandler = fn\n}\n\nfunc (s *DefaultCoapServer) handleRequest(msg Message, session Session) {\n\tif msg.GetMessageType() != MessageReset {\n\t\t// Unsupported Method\n\t\tif msg.GetCode() != Get && msg.GetCode() != Post && msg.GetCode() != Put && msg.GetCode() != Delete {\n\t\t\ts.handleReqUnsupportedMethodRequest(msg, session)\n\t\t\treturn\n\t\t}\n\n\t\t// Proxy\n\t\tif IsProxyRequest(msg) {\n\t\t\ts.handleReqProxyRequest(msg, session)\n\t\t} else {\n\t\t\troute, attrs, err := MatchingRoute(msg.GetURIPath(), MethodString(msg.GetCode()), msg.GetOptions(OptionContentFormat), s.GetRoutes())\n\t\t\tif err != nil {\n\t\t\t\ts.GetEvents().Error(err)\n\t\t\t\tif err == ErrNoMatchingRoute {\n\t\t\t\t\ts.handleReqNoMatchingRoute(msg, session)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif err == ErrNoMatchingMethod {\n\t\t\t\t\ts.handleReqNoMatchingMethod(msg, session)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif err == ErrUnsupportedContentFormat {\n\t\t\t\t\ts.handleReqUnsupportedContentFormat(msg, session)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tlog.Println(\"Error occured parsing inbound message\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Duplicate Message ID Check\n\t\t\tif s.isDuplicateMessage(msg) {\n\t\t\t\tPrintMessage(msg)\n\t\t\t\tif msg.GetMessageType() == MessageConfirmable {\n\t\t\t\t\tlog.Println(\"Duplicate Message ID \", msg.GetMessageId())\n\t\t\t\t\ts.handleReqDuplicateMessageID(msg, session)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ts.updateMessageTS(msg)\n\n\t\t\t// Auto acknowledge\n\t\t\t// TODO: Necessary?\n\t\t\tif msg.GetMessageType() == MessageConfirmable && route.AutoAcknowledge() {\n\t\t\t\ts.handleRequestAcknowledge(msg, session)\n\t\t\t}\n\t\t\treq := NewClientRequestFromMessage(msg, attrs, session)\n\n\t\t\tif msg.GetMessageType() == MessageConfirmable {\n\t\t\t\t// Observation Request\n\t\t\t\tobsOpt := msg.GetOption(OptionObserve)\n\t\t\t\tif obsOpt != nil {\n\t\t\t\t\ts.handleReqObserve(req, msg, session)\n\t\t\t\t}\n\t\t\t}\n\t\t\topt := req.GetMessage().GetOption(OptionBlock1)\n\t\t\tif opt != nil {\n\t\t\t\tblockOpt := Block1OptionFromOption(opt)\n\n\t\t\t\t// 0000 1 010\n\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t[NUM][M][SZX]\n\t\t\t\t\t\t\t\t\t2 ^ (2 + 4)\n\t\t\t\t\t\t\t\t\t2 ^ 6 = 32\n\t\t\t\t\t\t\t\t\tSize = 2 ^ (SZX + 4)\n\n\t\t\t\t\t\t\t\t\tThe value 7 for SZX (which would\n\t\t\t\t\t      \tindicate a block size of 2048) is reserved, i.e. MUST NOT be sent\n\t\t\t\t\t      \tand MUST lead to a 4.00 Bad Request response code upon reception\n\t\t\t\t\t      \tin a request.\n\t\t\t\t*/\n\n\t\t\t\tif blockOpt.Value != nil {\n\t\t\t\t\tif blockOpt.Code == OptionBlock1 {\n\t\t\t\t\t\texp := blockOpt.Exponent()\n\n\t\t\t\t\t\tif exp == 7 {\n\t\t\t\t\t\t\ts.handleReqBadRequest(msg, session)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// szx := blockOpt.Size()\n\t\t\t\t\t\thasMore := blockOpt.HasMore()\n\t\t\t\t\t\tseqNum := blockOpt.Sequence()\n\t\t\t\t\t\t// fmt.Println(\"Out Values == \", blockOpt.Value, exp, szx, 2, hasMore, seqNum)\n\n\t\t\t\t\t\ts.GetEvents().BlockMessage(msg, true)\n\n\t\t\t\t\t\ts.updateBlockMessageFragment(session.GetAddress().String(), msg, seqNum)\n\n\t\t\t\t\t\tif hasMore {\n\t\t\t\t\t\t\ts.handleReqContinue(msg, session)\n\n\t\t\t\t\t\t\t// Auto Respond to client\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// TODO: Check if message is too large\n\t\t\t\t\t\t\tmsg = NewMessage(msg.GetMessageType(), msg.GetCode(), msg.GetMessageId())\n\t\t\t\t\t\t\tmsg.SetPayload(s.flushBlockMessagePayload(session.GetAddress().String()))\n\t\t\t\t\t\t\treq = NewClientRequestFromMessage(msg, attrs, session)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if blockOpt.Code == OptionBlock2 {\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// TOOO: Invalid Block option Code\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresp := route.Handle(req)\n\t\t\t_, nilresponse := resp.(NilResponse)\n\t\t\tif !nilresponse {\n\t\t\t\trespMsg := resp.GetMessage().(*CoapMessage)\n\t\t\t\trespMsg.SetToken(req.GetMessage().GetToken())\n\n\t\t\t\t// TODO: Validate Message before sending (e.g missing messageId)\n\t\t\t\terr := ValidateMessage(respMsg)\n\t\t\t\tif err == nil {\n\t\t\t\t\ts.GetEvents().Message(respMsg, false)\n\t\t\t\t\tSendMessage(respMsg, session)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (s *DefaultCoapServer) handleReqObserve(req Request, msg Message, session Session) {\n\t// TODO: if server doesn't allow observing, return error\n\taddr := session.GetAddress()\n\n\t// TODO: Check if observation has been registered, if yes, remove it (observation == cancel)\n\tresource := msg.GetURIPath()\n\n\tif s.HasObservation(resource, addr) {\n\t\t// Remove observation of client\n\t\ts.RemoveObservation(resource, addr)\n\n\t\t// Observe Cancel Request & Fire OnObserveCancel Event\n\t\ts.GetEvents().ObserveCancelled(resource, msg)\n\t} else {\n\t\t// Register observation of client\n\t\ts.AddObservation(msg.GetURIPath(), string(msg.GetToken()), session)\n\n\t\t// Observe Request & Fire OnObserve Event\n\t\ts.GetEvents().Observe(resource, msg)\n\t}\n\n\treq.GetMessage().AddOption(OptionObserve, 1)\n}\n\nfunc (s *DefaultCoapServer) handleResponse(msg Message, session Session) {\n\tdefer s.closeSession(session)\n\tif msg.GetOption(OptionObserve) != nil {\n\t\ts.handleAcknowledgeObserveRequest(msg)\n\t\treturn\n\t}\n\n\tch := GetResponseChannel(s, msg.GetMessageId())\n\tif ch != nil {\n\t\tresp := &CoapResponseChannel{\n\t\t\tResponse: NewResponse(msg, nil),\n\t\t}\n\t\tch <- resp\n\t\tDeleteResponseChannel(s, msg.GetMessageId())\n\t}\n}\n\nfunc (s *DefaultCoapServer) GetEvents() Events {\n\treturn s.events\n}\n\nfunc (s *DefaultCoapServer) addDiscoveryRoute() {\n\tvar discoveryRoute RouteHandler = func(req Request) Response {\n\t\tmsg := req.GetMessage()\n\n\t\tvar buf bytes.Buffer\n\t\tfor _, r := range s.routes {\n\t\t\tif r.GetConfiguredPath() != \".well-known/core\" {\n\t\t\t\tbuf.WriteString(\"</\")\n\t\t\t\tbuf.WriteString(r.GetConfiguredPath())\n\t\t\t\tbuf.WriteString(\">\")\n\n\t\t\t\t// Media Types\n\t\t\t\tlenMt := len(r.GetMediaTypes())\n\t\t\t\tif lenMt > 0 {\n\t\t\t\t\tbuf.WriteString(\";ct=\")\n\t\t\t\t\tfor idx, mt := range r.GetMediaTypes() {\n\n\t\t\t\t\t\tbuf.WriteString(strconv.Itoa(int(mt)))\n\t\t\t\t\t\tif idx+1 < lenMt {\n\t\t\t\t\t\t\tbuf.WriteString(\" \")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbuf.WriteString(\",\")\n\t\t\t}\n\t\t}\n\n\t\tack := ContentMessage(msg.GetMessageId(), MessageAcknowledgment)\n\t\tack.SetToken(msg.GetToken())\n\t\tack.SetPayload(NewPlainTextPayload(buf.String()))\n\t\tack.AddOption(OptionContentFormat, MediaTypeApplicationLinkFormat)\n\t\tresp := NewResponseWithMessage(ack)\n\n\t\treturn resp\n\t}\n\ts.NewRoute(\"/.well-known/core\", Get, discoveryRoute)\n}\n\nfunc (s *DefaultCoapServer) ListenAndServeDTLS(addr string) {\n\ts.addDiscoveryRoute()\n\n\tconn := s.createConn(addr)\n\n\tctx, err := NewServerDtlsContext()\n\tif err != nil {\n\t\tpanic(\"Unable to create SSL Context:\" + err.Error())\n\t}\n\n\tif conn == nil {\n\t\tlog.Fatal(\"An error occured starting up CoAPS Server\")\n\t} else {\n\t\tsecret := make([]byte, 32)\n\t\tif n, err := rand.Read(secret); n != 32 || err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\ts.cookieSecret = secret\n\t\tlog.Println(\"Started CoAPS Server \", conn.LocalAddr())\n\t\tgo s.handleIncomingDTLSData(conn, ctx)\n\t\tgo s.events.Started(s)\n\t\tgo s.handleMessageIDPurge()\n\t}\n}\n\nfunc (s *DefaultCoapServer) ListenAndServe(addr string) {\n\ts.addDiscoveryRoute()\n\n\tconn := s.createConn(addr)\n\n\tif conn == nil {\n\t\tlog.Fatal(\"An error occured starting up CoAP Server\")\n\t} else {\n\t\tlog.Println(\"Started CoAP Server \", conn.LocalAddr())\n\t\tgo s.handleIncomingData(conn)\n\t\tgo s.events.Started(s)\n\t\tgo s.handleMessageIDPurge()\n\t}\n}\n\nfunc (s *DefaultCoapServer) createConn(addr string) ServerConnection {\n\tlocalHost := addr\n\tif !strings.Contains(localHost, \":\") {\n\t\tlocalHost = \":\" + localHost\n\t}\n\tlocalAddr, err := net.ResolveUDPAddr(\"udp6\", localHost)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tconn, err := net.ListenUDP(UDP, localAddr)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\treturn &UDPServerConnection{\n\t\tconn: conn,\n\t}\n}\n\nfunc (s *DefaultCoapServer) handleIncomingDTLSData(conn ServerConnection, ctx *ServerDtlsContext) {\n\treadBuf := make([]byte, MaxPacketSize)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-s.stopChannel:\n\t\t\t\treturn\n\n\t\t\tdefault:\n\t\t\t\t// continue\n\t\t\t}\n\n\t\t\tlen, addr, err := conn.ReadFrom(readBuf)\n\t\t\tif err == nil {\n\t\t\t\tmsgBuf := make([]byte, len)\n\t\t\t\tcopy(msgBuf, readBuf[:len])\n\t\t\t\tssn := s.sessions[addr.String()]\n\t\t\t\tif ssn == nil {\n\t\t\t\t\tssn = &DTLSServerSession{\n\t\t\t\t\t\tUDPServerSession: UDPServerSession{\n\t\t\t\t\t\t\taddr:   addr,\n\t\t\t\t\t\t\tconn:   conn,\n\t\t\t\t\t\t\tserver: s,\n\t\t\t\t\t\t\tbuf:    []byte{},\n\t\t\t\t\t\t\trcvd:   make(chan []byte, 1),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t\terr := newSslSession(ssn.(*DTLSServerSession), ctx, s.fnPskHandler)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tpanic(err.Error())\n\t\t\t\t\t}\n\t\t\t\t\ts.sessions[addr.String()] = ssn\n\t\t\t\t\ts.createdSession <- ssn\n\t\t\t\t}\n\n\t\t\t\tssn.(*DTLSServerSession).rcvd <- msgBuf\n\t\t\t} else {\n\t\t\t\tlogMsg(\"Error occured reading UDP\", err)\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\tssn := <-s.createdSession\n\t\t\tgo s.handleSession(ssn)\n\t\t}\n\t}()\n\n}\n\nfunc (s *DefaultCoapServer) handleIncomingData(conn ServerConnection) {\n\treadBuf := make([]byte, MaxPacketSize)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-s.stopChannel:\n\t\t\t\treturn\n\n\t\t\tdefault:\n\t\t\t\t// continue\n\t\t\t}\n\n\t\t\tlen, addr, err := conn.ReadFrom(readBuf)\n\t\t\tif err == nil {\n\t\t\t\tmsgBuf := make([]byte, len)\n\t\t\t\tcopy(msgBuf, readBuf[:len])\n\t\t\t\tssn := s.sessions[addr.String()]\n\t\t\t\tif ssn == nil {\n\t\t\t\t\tssn = &UDPServerSession{\n\t\t\t\t\t\taddr:   addr,\n\t\t\t\t\t\tconn:   conn,\n\t\t\t\t\t\tserver: s,\n\t\t\t\t\t\trcvd:   make(chan []byte),\n\t\t\t\t\t}\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tpanic(err.Error())\n\t\t\t\t\t}\n\t\t\t\t\ts.sessions[addr.String()] = ssn\n\t\t\t\t}\n\t\t\t\tgo func() {\n\t\t\t\t\tssn.(*UDPServerSession).rcvd <- msgBuf\n\t\t\t\t}()\n\t\t\t\tgo s.handleSession(ssn)\n\t\t\t} else {\n\t\t\t\tlogMsg(\"Error occured reading UDP\", err)\n\t\t\t}\n\t\t}\n\t}()\n\n}\n\nfunc (s *DefaultCoapServer) GetSession(addr string) Session {\n\treturn s.sessions[addr]\n}\n\nfunc (s *DefaultCoapServer) Stop() {\n\tclose(s.stopChannel)\n}\n\nfunc (s *DefaultCoapServer) updateBlockMessageFragment(client string, msg Message, seq uint32) {\n\tmsgs := s.incomingBlockMessages[client]\n\n\tif msgs == nil {\n\t\tmsgs = &CoapBlockMessage{\n\t\t\tSequence:   0,\n\t\t\tMessageBuf: []byte{},\n\t\t}\n\t}\n\n\tblockMsgs := msgs.(*CoapBlockMessage)\n\tblockMsgs.Sequence = seq\n\tblockMsgs.MessageBuf = append(blockMsgs.MessageBuf, msg.GetPayload().GetBytes()...)\n\n\ts.incomingBlockMessages[client] = msgs\n}\n\nfunc (s *DefaultCoapServer) flushBlockMessagePayload(origin string) MessagePayload {\n\tmsgs := s.incomingBlockMessages[origin]\n\n\tblockMsg := msgs.(*CoapBlockMessage)\n\tpayload := blockMsg.MessageBuf\n\n\treturn NewBytesPayload(payload)\n}\n\nfunc (s *DefaultCoapServer) handleMessageIDPurge() {\n\t// Routine for clearing up message IDs which has expired\n\tticker := time.NewTicker(MessageIDPurgeDuration * time.Second)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\t\tfor k, v := range s.messageIds {\n\t\t\t\t\telapsed := time.Since(v)\n\t\t\t\t\tif elapsed > MessageIDPurgeDuration {\n\t\t\t\t\t\tdelete(s.messageIds, k)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n}\n\nfunc (s *DefaultCoapServer) SetProxyFilter(fn ProxyFilter) {\n\ts.fnProxyFilter = fn\n}\n\nfunc (s *DefaultCoapServer) GetCookieSecret() []byte {\n\treturn s.cookieSecret\n}\n\nfunc (s *DefaultCoapServer) handleSession(session Session) {\n\tmsgBuf := make([]byte, 1500)\n\tn, _ := session.Read(msgBuf)\n\n\tmsg, err := BytesToMessage(msgBuf[:n])\n\tif err != nil {\n\t\tlogMsg(err.Error())\n\t\ts.handleReqBadRequest(msg, session)\n\t}\n\n\tif msg.GetMessageType() == MessageAcknowledgment {\n\t\ts.handleResponse(msg, session)\n\t} else {\n\t\ts.handleRequest(msg, session)\n\t}\n}\n\nfunc (s *DefaultCoapServer) closeSession(ssn Session) {\n\tdelete(s.sessions, ssn.GetAddress().String())\n}\n\nfunc (s *DefaultCoapServer) Get(path string, fn RouteHandler) Route {\n\treturn s.add(MethodGet, path, fn)\n}\n\nfunc (s *DefaultCoapServer) Delete(path string, fn RouteHandler) Route {\n\treturn s.add(MethodDelete, path, fn)\n}\n\nfunc (s *DefaultCoapServer) Put(path string, fn RouteHandler) Route {\n\treturn s.add(MethodPut, path, fn)\n}\n\nfunc (s *DefaultCoapServer) Post(path string, fn RouteHandler) Route {\n\treturn s.add(MethodPost, path, fn)\n}\n\nfunc (s *DefaultCoapServer) Options(path string, fn RouteHandler) Route {\n\treturn s.add(MethodOptions, path, fn)\n}\n\nfunc (s *DefaultCoapServer) Patch(path string, fn RouteHandler) Route {\n\treturn s.add(MethodPatch, path, fn)\n}\n\nfunc (s *DefaultCoapServer) add(method string, path string, fn RouteHandler) Route {\n\troute := CreateNewRegExRoute(path, method, fn)\n\ts.routes = append(s.routes, route)\n\n\treturn route\n}\n\nfunc (s *DefaultCoapServer) NewRoute(path string, method CoapCode, fn RouteHandler) Route {\n\troute := CreateNewRegExRoute(path, MethodString(method), fn)\n\ts.routes = append(s.routes, route)\n\n\treturn route\n}\n\nfunc (s *DefaultCoapServer) storeNewOutgoingBlockMessage(client string, payload []byte) {\n\tbm := NewBlockMessage().(*CoapBlockMessage)\n\tbm.MessageBuf = payload\n\ts.outgoingBlockMessages[client] = bm\n}\n\nfunc (s *DefaultCoapServer) NotifyChange(resource, value string, confirm bool) {\n\tt := s.observations[resource]\n\n\tif t != nil {\n\t\tvar req Request\n\n\t\tif confirm {\n\t\t\treq = NewRequest(MessageConfirmable, CoapCodeContent)\n\t\t} else {\n\t\t\treq = NewRequest(MessageAcknowledgment, CoapCodeContent)\n\t\t}\n\n\t\tfor _, r := range t {\n\t\t\treq.SetToken(r.Token)\n\t\t\treq.SetStringPayload(value)\n\t\t\treq.SetRequestURI(r.Resource)\n\t\t\tr.NotifyCount++\n\t\t\treq.GetMessage().AddOption(OptionObserve, r.NotifyCount)\n\n\t\t\tgo SendMessage(req.GetMessage(), r.Session)\n\t\t}\n\t}\n}\n\nfunc (s *DefaultCoapServer) AddObservation(resource, token string, session Session) {\n\ts.observations[resource] = append(s.observations[resource], NewObservation(session, token, resource))\n}\n\nfunc (s *DefaultCoapServer) HasObservation(resource string, addr net.Addr) bool {\n\tobs := s.observations[resource]\n\tif obs == nil {\n\t\treturn false\n\t}\n\n\tfor _, o := range obs {\n\t\tif o.Session.GetAddress().String() == addr.String() {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *DefaultCoapServer) RemoveObservation(resource string, addr net.Addr) {\n\tobs := s.observations[resource]\n\tif obs == nil {\n\t\treturn\n\t}\n\n\tfor idx, o := range obs {\n\t\tif o.Session.GetAddress().String() == addr.String() {\n\t\t\ts.observations[resource] = append(obs[:idx], obs[idx+1:]...)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (s *DefaultCoapServer) OnNotify(fn FnEventNotify) {\n\ts.events.OnNotify(fn)\n}\n\nfunc (s *DefaultCoapServer) OnStart(fn FnEventStart) {\n\ts.events.OnStart(fn)\n}\n\nfunc (s *DefaultCoapServer) OnClose(fn FnEventClose) {\n\ts.events.OnClose(fn)\n}\n\nfunc (s *DefaultCoapServer) OnDiscover(fn FnEventDiscover) {\n\ts.events.OnDiscover(fn)\n}\n\nfunc (s *DefaultCoapServer) OnError(fn FnEventError) {\n\ts.events.OnError(fn)\n}\n\nfunc (s *DefaultCoapServer) OnObserve(fn FnEventObserve) {\n\ts.events.OnObserve(fn)\n}\n\nfunc (s *DefaultCoapServer) OnObserveCancel(fn FnEventObserveCancel) {\n\ts.events.OnObserveCancel(fn)\n}\n\nfunc (s *DefaultCoapServer) OnMessage(fn FnEventMessage) {\n\ts.events.OnMessage(fn)\n}\n\nfunc (s *DefaultCoapServer) OnBlockMessage(fn FnEventBlockMessage) {\n\ts.events.OnBlockMessage(fn)\n}\n\nfunc (s *DefaultCoapServer) ProxyOverHttp(enabled bool) {\n\tif enabled {\n\t\ts.fnHandleHTTPProxy = HTTPProxyHandler\n\t} else {\n\t\ts.fnHandleHTTPProxy = NullProxyHandler\n\t}\n}\n\nfunc (s *DefaultCoapServer) ProxyOverCoap(enabled bool) {\n\tif enabled {\n\t\ts.fnHandleCOAPProxy = COAPProxyHandler\n\t} else {\n\t\ts.fnHandleCOAPProxy = NullProxyHandler\n\t}\n}\n\nfunc (s *DefaultCoapServer) AllowProxyForwarding(msg Message, addr net.Addr) bool {\n\treturn s.fnProxyFilter(msg, addr)\n}\n\nfunc (s *DefaultCoapServer) ForwardCoap(msg Message, session Session) {\n\ts.fnHandleCOAPProxy(s, msg, session)\n}\n\nfunc (s *DefaultCoapServer) ForwardHTTP(msg Message, session Session) {\n\ts.fnHandleHTTPProxy(s, msg, session)\n}\n\nfunc (s *DefaultCoapServer) GetRoutes() []Route {\n\treturn s.routes\n}\n\nfunc (s *DefaultCoapServer) isDuplicateMessage(msg Message) bool {\n\t_, ok := s.messageIds[msg.GetMessageId()]\n\n\treturn ok\n}\n\nfunc (s *DefaultCoapServer) updateMessageTS(msg Message) {\n\ts.messageIds[msg.GetMessageId()] = time.Now()\n}\n\nfunc (s *DefaultCoapServer) handleReqUnknownCriticalOption(msg Message, session Session) {\n\tif msg.GetMessageType() == MessageConfirmable {\n\t\tSendMessage(BadOptionMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n\t}\n\treturn\n}\n\nfunc (s *DefaultCoapServer) handleReqBadRequest(msg Message, session Session) {\n\tif msg.GetMessageType() == MessageConfirmable {\n\t\tSendMessage(BadRequestMessage(msg.GetMessageId(), msg.GetMessageType()), session)\n\t}\n\treturn\n}\n\nfunc (s *DefaultCoapServer) handleReqContinue(msg Message, session Session) {\n\tif msg.GetMessageType() == MessageConfirmable {\n\t\tSendMessage(ContinueMessage(msg.GetMessageId(), msg.GetMessageType()), session)\n\t}\n\treturn\n}\n\nfunc (s *DefaultCoapServer) handleReqUnsupportedMethodRequest(msg Message, session Session) {\n\tret := NotImplementedMessage(msg.GetMessageId(), MessageAcknowledgment)\n\tret.CloneOptions(msg, OptionURIPath, OptionContentFormat)\n\n\t// c.GetEvents().Message(ret, false)\n\tSendMessage(ret, session)\n}\n\nfunc (s *DefaultCoapServer) handleReqProxyRequest(msg Message, session Session) {\n\tif !s.AllowProxyForwarding(msg, session.GetAddress()) {\n\t\tSendMessage(ForbiddenMessage(msg.GetMessageId(), MessageAcknowledgment), session)\n\t}\n\n\tproxyURI := msg.GetOption(OptionProxyURI).StringValue()\n\tif IsCoapURI(proxyURI) {\n\t\ts.ForwardCoap(msg, session)\n\t} else if IsHTTPURI(proxyURI) {\n\t\ts.ForwardHTTP(msg, session)\n\t} else {\n\t\t//\n\t}\n}\n\nfunc (s *DefaultCoapServer) handleReqNoMatchingRoute(msg Message, session Session) {\n\tret := NotFoundMessage(msg.GetMessageId(), MessageAcknowledgment, msg.GetToken())\n\tret.CloneOptions(msg, OptionURIPath, OptionContentFormat)\n\n\tSendMessage(ret, session)\n}\n\nfunc (s *DefaultCoapServer) handleReqNoMatchingMethod(msg Message, session Session) {\n\tret := MethodNotAllowedMessage(msg.GetMessageId(), MessageAcknowledgment)\n\tret.CloneOptions(msg, OptionURIPath, OptionContentFormat)\n\n\tSendMessage(ret, session)\n}\n\nfunc (s *DefaultCoapServer) handleReqUnsupportedContentFormat(msg Message, session Session) {\n\tret := UnsupportedContentFormatMessage(msg.GetMessageId(), MessageAcknowledgment)\n\tret.CloneOptions(msg, OptionURIPath, OptionContentFormat)\n\n\t// s.GetEvents().Message(ret, false)\n\tSendMessage(ret, session)\n}\n\nfunc (s *DefaultCoapServer) handleReqDuplicateMessageID(msg Message, session Session) {\n\tret := EmptyMessage(msg.GetMessageId(), MessageReset)\n\tret.CloneOptions(msg, OptionURIPath, OptionContentFormat)\n\n\tSendMessage(ret, session)\n}\n\nfunc (s *DefaultCoapServer) handleRequestAcknowledge(msg Message, session Session) {\n\tack := NewMessageOfType(MessageAcknowledgment, msg.GetMessageId(), nil)\n\n\tSendMessage(ack, session)\n}\n\nfunc (s *DefaultCoapServer) handleAcknowledgeObserveRequest(msg Message) {\n\ts.GetEvents().Notify(msg.GetURIPath(), msg.GetPayload(), msg)\n}\n\nfunc (s *DefaultCoapServer) handleAcknowledgeObserveRequestGetSession(addr string) Session {\n\treturn s.sessions[addr]\n}\n\nfunc NewResponseChannel() (ch chan *CoapResponseChannel) {\n\tch = make(chan *CoapResponseChannel)\n\n\treturn\n}\n\nfunc AddResponseChannel(c CoapServer, msgId uint16, ch chan *CoapResponseChannel) {\n\ts := c.(*DefaultCoapServer)\n\ts.coapResponseChannelsMap[msgId] = ch\n}\n\nfunc DeleteResponseChannel(c CoapServer, msgId uint16) {\n\ts := c.(*DefaultCoapServer)\n\tdelete(s.coapResponseChannelsMap, msgId)\n}\n\nfunc GetResponseChannel(c CoapServer, msgId uint16) (ch chan *CoapResponseChannel) {\n\ts := c.(*DefaultCoapServer)\n\tch = s.coapResponseChannelsMap[msgId]\n\n\treturn\n}\n\nfunc NewObservation(session Session, token string, resource string) *Observation {\n\treturn &Observation{\n\t\tSession:     session,\n\t\tToken:       token,\n\t\tResource:    resource,\n\t\tNotifyCount: 0,\n\t}\n}\n\ntype Observation struct {\n\tSession     Session\n\tToken       string\n\tResource    string\n\tNotifyCount int\n}\n\nfunc _doSendMessage(msg Message, session Session, ch chan *CoapResponseChannel) {\n\tresp := &CoapResponseChannel{}\n\n\tb, err := MessageToBytes(msg)\n\tif err != nil {\n\t\tresp.Error = err\n\t\tch <- resp\n\t}\n\n\t_, err = session.Write(b)\n\tif err != nil {\n\t\tresp.Error = err\n\t\tch <- resp\n\t}\n\n\tif msg.GetMessageType() == MessageNonConfirmable {\n\t\tresp.Response = NewResponse(NewEmptyMessage(msg.GetMessageId()), nil)\n\t\tch <- resp\n\t}\n\tAddResponseChannel(session.GetServer(), msg.GetMessageId(), ch)\n}\n\nfunc SendMessage(msg Message, session Session) (Response, error) {\n\tif session.GetConnection() == nil {\n\t\treturn nil, ErrNilConn\n\t}\n\n\tif msg == nil {\n\t\treturn nil, ErrNilMessage\n\t}\n\n\tif session.GetAddress() == nil {\n\t\treturn nil, ErrNilAddr\n\t}\n\n\tch := NewResponseChannel()\n\tgo _doSendMessage(msg, session, ch)\n\n\tsession.GetServer().DeleteSession(session)\n\trespCh := <-ch\n\treturn respCh.Response, respCh.Error\n}\n\ntype CoapResponseChannel struct {\n\tResponse Response\n\tError    error\n}\n"
  },
  {
    "path": "server_test.go",
    "content": "package canopus\n\nimport \"testing\"\n\n// TODO Redo this entire test suite\nfunc TestServerInstantiate(t *testing.T) {\n\t//var s CoapServer\n\t//s = NewServer()\n\n\t//assert.NotNil(t, s)\n\t//assert.Equal(t, 1000, s.GetLocalAddress().Port)\n\t//assert.Equal(t, \"udp\", s.GetLocalAddress().Network())\n\t//\n\t//s = NewLocalServer(\"TestServer\")\n\t//assert.NotNil(t, s)\n\t//assert.Equal(t, 5683, s.GetLocalAddress().Port)\n\t//assert.Equal(t, \"udp\", s.GetLocalAddress().Network())\n}\n\n//func TestDiscoveryService(t *testing.T) {\n//\tserver := NewCoapServer(\":5684\")\n//\tassert.NotNil(t, server)\n//\tassert.Equal(t, 5684, server.localAddr.Port)\n//\tassert.Equal(t, \"udp\", server.localAddr.Network())\n//\n//\tgo server.Start()\n//\tclient := NewCoapClient()\n//\tclient.OnStart(func(server *CoapServer) {\n//\t\ttok := \"abc123\"\n//\t\tclient.Dial(\"localhost:5684\")\n//\n//\t\treq := NewRequest(TYPE_CONFIRMABLE, GET, GenerateMessageId())\n//\t\treq.SetToken(tok)\n//\t\treq.SetRequestURI(\".well-known/core\")\n//\t\tresp, err := client.Send(req)\n//\t\tassert.Nil(t, err)\n//\n//\t\tassert.Equal(t, tok, resp.GetMessage().GetTokenString())\n//\t\tclient.Stop()\n//\t})\n//\tclient.Start()\n//}\n\n//func TestClientServerRequestResponse(t *testing.T) {\n//\tserver := NewLocalServer()\n//\n//\tserver.Get(\"/ep\", func (req CoapRequest) CoapResponse {\n//\t\tmsg := ContentMessage(req.GetMessage().MessageId, TYPE_ACKNOWLEDGEMENT)\n//\t\tmsg.SetStringPayload(\"ACK GET\")\n//\t\tres := NewResponse(msg, nil)\n//\n//\t\treturn res\n//\t})\n//\n//\tserver.Post(\"/ep\", func (req CoapRequest) CoapResponse {\n//\t\tmsg := ContentMessage(req.GetMessage().MessageId, TYPE_ACKNOWLEDGEMENT)\n//\t\tmsg.SetStringPayload(\"ACK POST\")\n//\t\tres := NewResponse(msg, nil)\n//\n//\t\treturn res\n//\t})\n//\n//\tserver.Put(\"/ep\", func (req CoapRequest) CoapResponse {\n//\t\tmsg := ContentMessage(req.GetMessage().MessageId, TYPE_ACKNOWLEDGEMENT)\n//\t\tmsg.SetStringPayload(\"ACK PUT\")\n//\t\tres := NewResponse(msg, nil)\n//\n//\t\treturn res\n//\t})\n//\n//\tserver.Delete(\"/ep\", func (req CoapRequest) CoapResponse {\n//\t\tmsg := ContentMessage(req.GetMessage().MessageId, TYPE_ACKNOWLEDGEMENT)\n//\t\tmsg.SetStringPayload(\"ACK DELETE\")\n//\t\tres := NewResponse(msg, nil)\n//\n//\t\treturn res\n//\t})\n//\n//\tgo server.Start()\n//\n//\tclient := NewCoapClient()\n//\n//\tclient.OnStart(func(server *CoapServer) {\n//\t\tclient.Dial(\"localhost:5683\")\n//\t\ttoken := \"tok1234\"\n//\n//\t\tvar req CoapRequest\n//\t\tvar resp CoapResponse\n//\t\tvar err error\n//\n//\t\t// 404 Test\n//\t\treq = NewConfirmableGetRequest()\n//\t\treq.SetToken(token)\n//\t\treq.SetRequestURI(\"ep-404\")\n//\t\tresp, err = client.Send(req)\n//\t\tassert.Equal(t, COAPCODE_404_NOT_FOUND, resp.GetMessage().Code)\n//\n//\t\t// GET\n//\t\treq = NewConfirmableGetRequest()\n//\t\treq.SetToken(token)\n//\t\treq.SetRequestURI(\"ep\")\n//\t\tresp, err = client.Send(req)\n//\n//\t\tassert.Nil(t, err)\n//\t\tassert.Equal(t, \"ACK GET\", resp.GetMessage().Payload.String())\n//\t\tassert.Equal(t, token, resp.GetMessage().GetTokenString())\n//\n//\t\t// POST\n//\t\treq = NewConfirmablePostRequest()\n//\t\treq.SetToken(token)\n//\t\treq.SetRequestURI(\"ep\")\n//\t\tresp, err = client.Send(req)\n//\n//\t\tassert.Nil(t, err)\n//\t\tassert.Equal(t, \"ACK POST\", resp.GetMessage().Payload.String())\n//\t\tassert.Equal(t, token, resp.GetMessage().GetTokenString())\n//\n//\t\t// PUT\n//\t\treq = NewConfirmablePutRequest()\n//\t\treq.SetToken(token)\n//\t\treq.SetRequestURI(\"ep\")\n//\t\tresp, err = client.Send(req)\n//\n//\t\tassert.Nil(t, err)\n//\t\tassert.Equal(t, \"ACK PUT\", resp.GetMessage().Payload.String())\n//\t\tassert.Equal(t, token, resp.GetMessage().GetTokenString())\n//\n//\t\t// DELETE\n//\t\treq = NewConfirmableDeleteRequest()\n//\t\treq.SetToken(token)\n//\t\treq.SetRequestURI(\"ep\")\n//\t\tresp, err = client.Send(req)\n//\n//\t\tassert.Nil(t, err)\n//\t\tassert.Equal(t, \"ACK DELETE\", resp.GetMessage().Payload.String())\n//\t\tassert.Equal(t, token, resp.GetMessage().GetTokenString())\n//\n//\t\t// Test default token set\n//\t\treq = NewConfirmableGetRequest()\n//\t\treq.SetRequestURI(\"ep\")\n//\t\tresp, err = client.Send(req)\n//\n//\t\tassert.Nil(t, err)\n//\t\tassert.Equal(t, \"ACK GET\", resp.GetMessage().Payload.String())\n//\t\tassert.NotEmpty(t, resp.GetMessage().GetTokenString())\n//\n//\t\tclient.Stop()\n//\t})\n//\tclient.Start()\n//}\n"
  },
  {
    "path": "serverconn.go",
    "content": "package canopus\n\nimport (\n\t\"net\"\n\t\"time\"\n)\n\ntype UDPServerConnection struct {\n\tconn net.PacketConn\n}\n\nfunc (uc *UDPServerConnection) ReadFrom(b []byte) (n int, addr net.Addr, err error) {\n\treturn uc.conn.ReadFrom(b)\n}\n\nfunc (uc *UDPServerConnection) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\treturn uc.conn.WriteTo(b, addr)\n}\n\nfunc (uc *UDPServerConnection) Close() error {\n\treturn uc.conn.Close()\n}\n\nfunc (uc *UDPServerConnection) LocalAddr() net.Addr {\n\treturn uc.conn.LocalAddr()\n}\n\nfunc (uc *UDPServerConnection) SetDeadline(t time.Time) error {\n\treturn uc.conn.SetDeadline(t)\n}\n\nfunc (uc *UDPServerConnection) SetReadDeadline(t time.Time) error {\n\treturn uc.conn.SetReadDeadline(t)\n}\n\nfunc (uc *UDPServerConnection) SetWriteDeadline(t time.Time) error {\n\treturn uc.conn.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "session.go",
    "content": "package canopus\n\nimport \"net\"\n\ntype UDPServerSession struct {\n\taddr   net.Addr\n\tconn   ServerConnection\n\tserver CoapServer\n\trcvd   chan []byte\n\tbuf    []byte\n}\n\nfunc (s *UDPServerSession) GetConnection() ServerConnection {\n\treturn s.conn\n}\n\nfunc (s *UDPServerSession) GetAddress() net.Addr {\n\treturn s.addr\n}\n\nfunc (s *UDPServerSession) WriteBuffer(b []byte) (n int) {\n\tl := len(b)\n\ts.buf = append(s.buf, b...)\n\treturn l\n}\n\nfunc (s *UDPServerSession) Write(b []byte) (n int, err error) {\n\tn, err = s.conn.WriteTo(b, s.GetAddress())\n\n\treturn\n}\n\nfunc (s *UDPServerSession) Read(b []byte) (n int, err error) {\n\tdata := <-s.rcvd\n\tcopy(b, data)\n\treturn len(data), nil\n}\n\nfunc (s *UDPServerSession) GetServer() CoapServer {\n\treturn s.server\n}\n"
  },
  {
    "path": "test-coverage.sh",
    "content": "#!/bin/bash\n\necho \"mode: set\" > acc.out\nfail=0\n\n# Standard go tooling behavior is to ignore dirs with leading underscors\nfor dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d);\ndo\n  if ls $dir/*.go &> /dev/null; then\n    go test -coverprofile=profile.out $dir || fail=1\n    if [ -f profile.out ]\n    then\n      cat profile.out | grep -v \"mode: set\" >> acc.out\n      rm profile.out\n    fi\n  fi\ndone\n\n# Failures have incomplete results, so don't send\nif [ -n \"$COVERALLS\" ] && [ \"$fail\" -eq 0 ]\nthen\n  goveralls -v -coverprofile=acc.out $COVERALLS\nfi\n\nrm -f acc.out\n\nexit $fail"
  },
  {
    "path": "types.go",
    "content": "package canopus\n\ntype CoreAttributes []*CoreAttribute\n\ntype CoreResource struct {\n\tTarget     string\n\tAttributes CoreAttributes\n}\n\ntype CoreAttribute struct {\n\tKey   string\n\tValue interface{}\n}\n\n// Adds an attribute (key/value) for a given core resource\nfunc (c *CoreResource) AddAttribute(key string, value interface{}) {\n\tc.Attributes = append(c.Attributes, NewCoreAttribute(key, value))\n}\n\n// Gets an attribute for a core resource\nfunc (c *CoreResource) GetAttribute(key string) *CoreAttribute {\n\tfor _, attr := range c.Attributes {\n\t\tif attr.Key == key {\n\t\t\treturn attr\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "utilconn_test.go",
    "content": "package canopus\n\nimport \"testing\"\n\nfunc TestSendMessages(t *testing.T) {\n\t//var conn Connection\n\t//var s CoapServer\n\t//_, err := SendMessageTo(s, nil, conn, nil)\n\t//assert.NotNil(t, err)\n\t//assert.Equal(t, ErrNilConn, err)\n\t//\n\t//conn = NewUDPConnection(nil)\n\t//SendMessageTo(s, nil, conn, nil)\n\t//_, err = SendMessageTo(s, nil, conn, nil)\n\t//assert.NotNil(t, err)\n\t//assert.Equal(t, ErrNilMessage, err)\n\t//\n\t//_, err = SendMessageTo(s, NewEmptyMessage(12345), conn, nil)\n\t//assert.NotNil(t, err)\n\t//assert.Equal(t, ErrNilAddr, err)\n\t//\n\t//addr := &net.UDPAddr{}\n\t//conn = NewMockCanopusUDPConnection(CoapCodeCreated, false, false)\n\t//msg := NewBasicConfirmableMessage()\n\t//_, err = SendMessageTo(s, msg, conn, addr)\n\t//assert.Nil(t, err)\n\t//\n\t//msg.MessageType = MessageNonConfirmable\n\t//_, err = SendMessageTo(s, msg, conn, addr)\n\t//assert.Nil(t, err)\n\t//\n\t//conn = NewMockCanopusUDPConnection(CoapCodeCreated, false, true)\n\t//msg.MessageType = MessageConfirmable\n\t//_, err = SendMessageTo(s, msg, conn, addr)\n\t//assert.NotNil(t, err)\n}\n"
  },
  {
    "path": "utildebug.go",
    "content": "package canopus\n\n// PrintOptions pretty prints out a given Message's options\nfunc PrintOptions(msg Message) {\n\topts := msg.GetAllOptions()\n\tlogMsg(\" - - - OPTIONS - - - \")\n\tif len(opts) > 0 {\n\t\tfor _, opts := range msg.GetAllOptions() {\n\t\t\tlogMsg(\"Code/Number: \", opts.GetCode(), \", Name: \", OptionNumberToString(opts.GetCode()), \", Value: \", opts.GetValue())\n\t\t}\n\t} else {\n\t\tlogMsg(\"None\")\n\t}\n}\n\n// PrintMessage pretty prints out a given Message\nfunc PrintMessage(msg Message) {\n\tlogMsg(\"= = = = = = = = = = = = = = = = \")\n\tlogMsg(\"Code: \", msg.GetCode())\n\tlogMsg(\"Code String: \", CoapCodeToString(msg.GetCode()))\n\tlogMsg(\"MessageId: \", msg.GetMessageId())\n\tlogMsg(\"MessageType: \", msg.GetMessageType())\n\tlogMsg(\"Token: \", string(msg.GetToken()))\n\tlogMsg(\"Token Length: \", msg.GetTokenLength())\n\tlogMsg(\"Payload: \", PayloadAsString(msg.GetPayload()))\n\tPrintOptions(msg)\n\tlogMsg(\"= = = = = = = = = = = = = = = = \")\n}\n\n// OptionNumberToString returns the string representation of a given Option Code\nfunc OptionNumberToString(o OptionCode) string {\n\tswitch o {\n\tcase OptionIfMatch:\n\t\treturn \"If-Match\"\n\n\tcase OptionURIHost:\n\t\treturn \"Uri-Host\"\n\n\tcase OptionEtag:\n\t\treturn \"ETag\"\n\n\tcase OptionIfNoneMatch:\n\t\treturn \"If-None-Match\"\n\n\tcase OptionURIPort:\n\t\treturn \"Uri-Port\"\n\n\tcase OptionLocationPath:\n\t\treturn \"Location-Path\"\n\n\tcase OptionURIPath:\n\t\treturn \"Uri-Path\"\n\n\tcase OptionContentFormat:\n\t\treturn \"Content-Format\"\n\n\tcase OptionMaxAge:\n\t\treturn \"Max-Age\"\n\n\tcase OptionURIQuery:\n\t\treturn \"Uri-Query\"\n\n\tcase OptionAccept:\n\t\treturn \"Accept\"\n\n\tcase OptionLocationQuery:\n\t\treturn \"Location-Query\"\n\n\tcase OptionBlock2:\n\t\treturn \"Block2\"\n\n\tcase OptionBlock1:\n\t\treturn \"Block1\"\n\n\tcase OptionProxyURI:\n\t\treturn \"Proxy-Uri\"\n\n\tcase OptionProxyScheme:\n\t\treturn \"Proxy-Scheme\"\n\n\tcase OptionSize1:\n\t\treturn \"Size1\"\n\n\tcase OptionSize2:\n\t\treturn \"Size2\"\n\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "utils.go",
    "content": "package canopus\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Returns the string value for a Message Payload\nfunc PayloadAsString(p MessagePayload) string {\n\tif p == nil {\n\t\treturn \"\"\n\t}\n\treturn p.String()\n}\n\n// GenerateMessageId generate a uint16 Message ID\nfunc GenerateMessageID() uint16 {\n\tMESSAGEID_MUTEX.Lock()\n\tif CurrentMessageID != 65535 {\n\t\tCurrentMessageID++\n\t} else {\n\t\tCurrentMessageID = 1\n\t}\n\tMESSAGEID_MUTEX.Unlock()\n\n\treturn uint16(CurrentMessageID)\n}\n\nvar genChars = []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\")\n\n// GenerateToken generates a random token by a given length\nfunc GenerateToken(l int) string {\n\trand.Seed(time.Now().UTC().UnixNano())\n\ttoken := make([]rune, l)\n\tfor i := range token {\n\t\ttoken[i] = genChars[rand.Intn(len(genChars))]\n\t}\n\treturn string(token)\n}\n\n// CoreResourcesFromString Converts to CoRE Resources Object from a CoRE String\nfunc CoreResourcesFromString(str string) []*CoreResource {\n\tvar re = regexp.MustCompile(`(<[^>]+>\\s*(;\\s*\\w+\\s*(=\\s*(\\w+|\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\")\\s*)?)*)`)\n\tvar elemRe = regexp.MustCompile(`<[^>]*>`)\n\n\tvar resources []*CoreResource\n\tm := re.FindAllString(str, -1)\n\n\tfor _, match := range m {\n\t\telemMatch := elemRe.FindString(match)\n\t\ttarget := elemMatch[1 : len(elemMatch)-1]\n\n\t\tresource := NewCoreResource()\n\t\tresource.Target = target\n\n\t\tif len(match) > len(elemMatch) {\n\t\t\tattrs := strings.Split(match[len(elemMatch)+1:], \";\")\n\n\t\t\tfor _, attr := range attrs {\n\t\t\t\tpair := strings.Split(attr, \"=\")\n\n\t\t\t\tresource.AddAttribute(pair[0], strings.Replace(pair[1], \"\\\"\", \"\", -1))\n\t\t\t}\n\t\t}\n\t\tresources = append(resources, resource)\n\t}\n\treturn resources\n}\n\n// CoapCodeToString returns the string representation of a CoapCode\nfunc CoapCodeToString(code CoapCode) string {\n\tswitch code {\n\tcase Get:\n\t\treturn \"GET\"\n\n\tcase Post:\n\t\treturn \"POST\"\n\n\tcase Put:\n\t\treturn \"PUT\"\n\n\tcase Delete:\n\t\treturn \"DELETE\"\n\n\tcase CoapCodeEmpty:\n\t\treturn \"0 Empty\"\n\n\tcase CoapCodeCreated:\n\t\treturn \"201 Created\"\n\n\tcase CoapCodeDeleted:\n\t\treturn \"202 Deleted\"\n\n\tcase CoapCodeValid:\n\t\treturn \"203 Valid\"\n\n\tcase CoapCodeChanged:\n\t\treturn \"204 Changed\"\n\n\tcase CoapCodeContent:\n\t\treturn \"205 Content\"\n\n\tcase CoapCodeBadRequest:\n\t\treturn \"400 Bad Request\"\n\n\tcase CoapCodeUnauthorized:\n\t\treturn \"401 Unauthorized\"\n\n\tcase CoapCodeBadOption:\n\t\treturn \"402 Bad Option\"\n\n\tcase CoapCodeForbidden:\n\t\treturn \"403 Forbidden\"\n\n\tcase CoapCodeNotFound:\n\t\treturn \"404 Not Found\"\n\n\tcase CoapCodeMethodNotAllowed:\n\t\treturn \"405 Method Not Allowed\"\n\n\tcase CoapCodeNotAcceptable:\n\t\treturn \"406 Not Acceptable\"\n\n\tcase CoapCodePreconditionFailed:\n\t\treturn \"412 Precondition Failed\"\n\n\tcase CoapCodeRequestEntityTooLarge:\n\t\treturn \"413 Request Entity Too Large\"\n\n\tcase CoapCodeUnsupportedContentFormat:\n\t\treturn \"415 Unsupported Content Format\"\n\n\tcase CoapCodeInternalServerError:\n\t\treturn \"500 Internal Server Error\"\n\n\tcase CoapCodeNotImplemented:\n\t\treturn \"501 Not Implemented\"\n\n\tcase CoapCodeBadGateway:\n\t\treturn \"502 Bad Gateway\"\n\n\tcase CoapCodeServiceUnavailable:\n\t\treturn \"503 Service Unavailable\"\n\n\tcase CoapCodeGatewayTimeout:\n\t\treturn \"504 Gateway Timeout\"\n\n\tcase CoapCodeProxyingNotSupported:\n\t\treturn \"505 Proxying Not Supported\"\n\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\n// ValidCoapMediaTypeCode Checks if a MediaType is of a valid code\nfunc ValidCoapMediaTypeCode(mt MediaType) bool {\n\tswitch mt {\n\tcase MediaTypeTextPlain, MediaTypeTextXML, MediaTypeTextCsv, MediaTypeTextHTML, MediaTypeImageGif,\n\t\tMediaTypeImageJpeg, MediaTypeImagePng, MediaTypeImageTiff, MediaTypeAudioRaw, MediaTypeVideoRaw,\n\t\tMediaTypeApplicationLinkFormat, MediaTypeApplicationXML, MediaTypeApplicationOctetStream, MediaTypeApplicationRdfXML,\n\t\tMediaTypeApplicationSoapXML, MediaTypeApplicationAtomXML, MediaTypeApplicationXmppXML, MediaTypeApplicationExi,\n\t\tMediaTypeApplicationFastInfoSet, MediaTypeApplicationSoapFastInfoSet, MediaTypeApplicationJSON,\n\t\tMediaTypeApplicationXObitBinary, MediaTypeTextPlainVndOmaLwm2m, MediaTypeTlvVndOmaLwm2m,\n\t\tMediaTypeJSONVndOmaLwm2m, MediaTypeOpaqueVndOmaLwm2m:\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc logMsg(a ...interface{}) (n int, err error) {\n\treturn fmt.Println(a)\n}\n"
  },
  {
    "path": "utils_test.go",
    "content": "package canopus\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGenerateMessageId(t *testing.T) {\n\n\tvar id, id2 uint16\n\tid = GenerateMessageID()\n\tfor i := 0; i < 100; i++ {\n\t\tid2 = id + 1\n\t\tid = GenerateMessageID()\n\t\tassert.NotEqual(t, 65535, id)\n\t\tassert.Equal(t, id2, id)\n\t}\n\n\tCurrentMessageID = 65535\n\tid = GenerateMessageID()\n\tassert.Equal(t, uint16(1), id)\n}\n\nfunc TestGenerateToken(t *testing.T) {\n\tassert.Equal(t, \"\", GenerateToken(0))\n\n\tfor i := 1; i < 10; i++ {\n\t\ttok := GenerateToken(i)\n\t\tassert.NotEqual(t, \"\", tok)\n\t\tassert.Equal(t, i, len(tok))\n\t}\n}\n\nfunc TestCoreResourceUtil(t *testing.T) {\n\tvar resources []*CoreResource\n\n\tresources = CoreResourcesFromString(\"\")\n\n\tassert.Equal(t, 0, len(resources))\n\n\tresources = CoreResourcesFromString(\"</sensors/temp>;ct=41;rt=\\\"temperature-c\\\";if=\\\"sensor\\\", </sensors/light>;ct=41;rt=\\\"light-lux\\\";if=\\\"sensor\\\"\")\n\n\tassert.Equal(t, 2, len(resources))\n\tresource1 := resources[0]\n\n\tassert.Equal(t, \"/sensors/temp\", resource1.Target)\n\tassert.Equal(t, 3, len(resource1.Attributes))\n\n\tassert.Nil(t, resource1.GetAttribute(\"invalid_attr\"))\n\n\tassert.NotNil(t, resource1.GetAttribute(\"ct\"))\n\tassert.Equal(t, \"ct\", resource1.GetAttribute(\"ct\").Key)\n\tassert.Equal(t, \"41\", resource1.GetAttribute(\"ct\").Value)\n\n\tassert.NotNil(t, resource1.GetAttribute(\"rt\"))\n\tassert.Equal(t, \"rt\", resource1.GetAttribute(\"rt\").Key)\n\tassert.Equal(t, \"temperature-c\", resource1.GetAttribute(\"rt\").Value)\n\n\tassert.NotNil(t, resource1.GetAttribute(\"if\"))\n\tassert.Equal(t, \"if\", resource1.GetAttribute(\"if\").Key)\n\tassert.Equal(t, \"sensor\", resource1.GetAttribute(\"if\").Value)\n\n\tresource2 := resources[1]\n\tassert.Equal(t, \"/sensors/light\", resource2.Target)\n\tassert.Equal(t, 3, len(resource2.Attributes))\n\n\tassert.NotNil(t, resource2.GetAttribute(\"ct\"))\n\tassert.Equal(t, \"ct\", resource2.GetAttribute(\"ct\").Key)\n\tassert.Equal(t, \"41\", resource2.GetAttribute(\"ct\").Value)\n\n\tassert.NotNil(t, resource2.GetAttribute(\"rt\"))\n\tassert.Equal(t, \"rt\", resource2.GetAttribute(\"rt\").Key)\n\tassert.Equal(t, \"light-lux\", resource2.GetAttribute(\"rt\").Value)\n\n\tassert.NotNil(t, resource2.GetAttribute(\"if\"))\n\tassert.Equal(t, \"if\", resource2.GetAttribute(\"if\").Key)\n\tassert.Equal(t, \"sensor\", resource2.GetAttribute(\"if\").Value)\n}\n\nfunc TestCoapCodeToString(t *testing.T) {\n\ttestData := []struct {\n\t\tcoapCode   CoapCode\n\t\tcodeString string\n\t}{\n\t\t{Get, \"GET\"},\n\t\t{Post, \"POST\"},\n\t\t{Put, \"PUT\"},\n\t\t{Delete, \"DELETE\"},\n\t\t{CoapCodeEmpty, \"0 Empty\"},\n\t\t{CoapCodeCreated, \"201 Created\"},\n\t\t{CoapCodeDeleted, \"202 Deleted\"},\n\t\t{CoapCodeValid, \"203 Valid\"},\n\t\t{CoapCodeChanged, \"204 Changed\"},\n\t\t{CoapCodeContent, \"205 Content\"},\n\t\t{CoapCodeBadRequest, \"400 Bad Request\"},\n\t\t{CoapCodeUnauthorized, \"401 Unauthorized\"},\n\t\t{CoapCodeBadOption, \"402 Bad Option\"},\n\t\t{CoapCodeForbidden, \"403 Forbidden\"},\n\t\t{CoapCodeNotFound, \"404 Not Found\"},\n\t\t{CoapCodeMethodNotAllowed, \"405 Method Not Allowed\"},\n\t\t{CoapCodeNotAcceptable, \"406 Not Acceptable\"},\n\t\t{CoapCodePreconditionFailed, \"412 Precondition Failed\"},\n\t\t{CoapCodeRequestEntityTooLarge, \"413 Request Entity Too Large\"},\n\t\t{CoapCodeUnsupportedContentFormat, \"415 Unsupported Content Format\"},\n\t\t{CoapCodeInternalServerError, \"500 Internal Server Error\"},\n\t\t{CoapCodeNotImplemented, \"501 Not Implemented\"},\n\t\t{CoapCodeBadGateway, \"502 Bad Gateway\"},\n\t\t{CoapCodeServiceUnavailable, \"503 Service Unavailable\"},\n\t\t{CoapCodeGatewayTimeout, \"504 Gateway Timeout\"},\n\t\t{CoapCodeProxyingNotSupported, \"505 Proxying Not Supported\"},\n\t\t{CoapCode(255), \"Unknown\"},\n\t}\n\n\tfor _, td := range testData {\n\t\tassert.Equal(t, td.codeString, CoapCodeToString(td.coapCode))\n\t}\n}\n\nfunc TestRouteMatching(t *testing.T) {\n\n}\n\nfunc TestMediaTypeUtils(t *testing.T) {\n\tassert.True(t, ValidCoapMediaTypeCode(MediaTypeTextPlain))\n\tassert.True(t, ValidCoapMediaTypeCode(MediaTypeOpaqueVndOmaLwm2m))\n\n\tassert.False(t, ValidCoapMediaTypeCode(MediaType(9999)))\n}\n"
  },
  {
    "path": "xml.go",
    "content": "package canopus\n\n// Represents a message payload containing XML String\ntype XMLPayload struct {\n}\n"
  }
]