5
0
Fork 0

Initial commit

master
UnicodingUnicorn 2019-02-06 13:26:35 +08:00
parent 02e3605bda
commit 3f8f28dd99
4 changed files with 343 additions and 1 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

View File

@ -1,3 +1,82 @@
# backend-publish # backend-publish
Beep backend accepts PUT requests and publishes them to NAT queue. Beep backend accepts PUT requests and publishes a protobuf-ed version to a [NATS](htts://nats.io) queue, like some sort of weird HTTP/NATS converter. Needless to say, relies on a NATS instance being up.
## Quickstart
```
go build && ./backend-publish
```
## Flags
Flags are supplied to the compiled go program in the form ```-flag=stuff```.
| Flag | Description | Default |
| ---- | ----------- | ------- |
| listen | Port number to listen on | 8080 |
| nats | URL of NATS | nats://localhost:4222 |
## API
### Put Bite
```
PUT /conversation/:key/start/:start
```
TODO: Description of what this does cos honestly I have no idea Ambrose doesn't write documentation
#### URL Params
| Name | Type | Description |
| ---- | ---- | ----------- |
| key | String | Audio bite's conversation's ID. |
| start | Epoch timestamp | Time the audio bite starts. |
#### Body
Raw body of audio data in bytes.
#### Success (200 OK)
Empty body.
#### Errors
| Code | Description |
| ---- | ----------- |
| 400 | start is not an uint/key is not an alphanumeric string/data could not be read from the body |
| 500 | Error serialising data into a protocol buffer. |
---
### Put Bite User
```
PUT /conversation/:key/start/:start/user
```
TODO: Description of what this does cos honestly I have no idea Ambrose doesn't write documentation
#### URL Params
| Name | Type | Description |
| ---- | ---- | ----------- |
| key | String | Audio bite's conversation's ID. |
| start | Epoch timestamp | Time the audio bite starts. |
#### Body
Raw body of audio data in bytes.
#### Success (200 OK)
Empty body.
#### Errors
| Code | Description |
| ---- | ----------- |
| 400 | start is not an uint/key is not an alphanumeric string/data could not be read from the body |
| 500 | Error serialising data into a protocol buffer. |

93
bite.pb.go Normal file
View File

@ -0,0 +1,93 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: bite.proto
package main
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Bite struct {
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Start uint64 `protobuf:"varint,2,opt,name=start,proto3" json:"start,omitempty"`
Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Bite) Reset() { *m = Bite{} }
func (m *Bite) String() string { return proto.CompactTextString(m) }
func (*Bite) ProtoMessage() {}
func (*Bite) Descriptor() ([]byte, []int) {
return fileDescriptor_e1ec993646b17549, []int{0}
}
func (m *Bite) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Bite.Unmarshal(m, b)
}
func (m *Bite) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Bite.Marshal(b, m, deterministic)
}
func (m *Bite) XXX_Merge(src proto.Message) {
xxx_messageInfo_Bite.Merge(m, src)
}
func (m *Bite) XXX_Size() int {
return xxx_messageInfo_Bite.Size(m)
}
func (m *Bite) XXX_DiscardUnknown() {
xxx_messageInfo_Bite.DiscardUnknown(m)
}
var xxx_messageInfo_Bite proto.InternalMessageInfo
func (m *Bite) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *Bite) GetStart() uint64 {
if m != nil {
return m.Start
}
return 0
}
func (m *Bite) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func init() {
proto.RegisterType((*Bite)(nil), "main.Bite")
}
func init() { proto.RegisterFile("bite.proto", fileDescriptor_e1ec993646b17549) }
var fileDescriptor_e1ec993646b17549 = []byte{
// 102 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0xca, 0x2c, 0x49,
0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc, 0x53, 0x72, 0xe2, 0x62,
0x71, 0xca, 0x2c, 0x49, 0x15, 0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, 0x94, 0x60, 0x54, 0x60, 0xd4,
0xe0, 0x09, 0x02, 0x31, 0x85, 0x44, 0xb8, 0x58, 0x8b, 0x4b, 0x12, 0x8b, 0x4a, 0x24, 0x98, 0x14,
0x18, 0x35, 0x58, 0x82, 0x20, 0x1c, 0x21, 0x21, 0x2e, 0x96, 0x94, 0xc4, 0x92, 0x44, 0x09, 0x66,
0xb0, 0x42, 0x30, 0x3b, 0x89, 0x0d, 0x6c, 0xa0, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xa3, 0xe1,
0xe5, 0xfe, 0x5e, 0x00, 0x00, 0x00,
}

158
main.go Normal file
View File

@ -0,0 +1,158 @@
package main;
import (
"encoding/binary"
"errors"
"flag"
"io/ioutil"
"net/http"
"log"
"regexp"
"strconv"
"github.com/golang/protobuf/proto"
"github.com/julienschmidt/httprouter"
"github.com/nats-io/go-nats"
)
const MaxBiteSize = 1024 * 1024 * 10
var listen string
var natsHost string
var nats_conn *nats.Conn
func main() {
// Parse flags
flag.StringVar(&listen, "listen", ":8080", "host and port to listen on")
flag.StringVar(&natsHost, "nats", "nats://localhost:4222", "host and port of NATS")
flag.Parse()
//NATS
n, err := nats.Connect(natsHost)
if err != nil {
log.Fatal(err)
return
}
nats_conn = n
// Routes
router := httprouter.New()
router.PUT("/conversation/:key/start/:start", PutBite) // bites
router.PUT("/conversation/:key/start/:start/user", PutBiteUser) // bite_users
// Start server
log.Printf("starting server on %s", listen)
log.Fatal(http.ListenAndServe(listen, router))
}
// Marshalling keys and assorted helper functions
func validObj(obj string) bool {
return obj == "bite" || obj == "user"
}
// TODO: ensure security of regexp
var validConversationRegexp = regexp.MustCompile(`^[a-zA-Z0-9-]+$`)
func validConversation(conversation string) bool {
return validConversationRegexp.MatchString(conversation)
}
const conversationSeprator = '@'
const objSeprator = '+'
func MarshalKey(obj, conversation string, start uint64) ([]byte, error) {
prefixBytes, err := MarshalKeyPrefix(obj, conversation)
if err != nil {
return nil, err
}
startBytes := make([]byte, 8)
binary.BigEndian.PutUint64(startBytes, start)
return append(prefixBytes, startBytes...), nil
}
func MarshalKeyPrefix(obj, conversation string) ([]byte, error) {
if !validObj(obj) || !validConversation(conversation) {
return nil, errors.New("main: FormatKey: bad obj or conversation")
}
return []byte(obj + string(objSeprator) + conversation + string(conversationSeprator)), nil
}
func ParseStartString(start string) (uint64, error) {
return strconv.ParseUint(start, 10, 64)
}
// Route handlers
func PutBite(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
start, err := ParseStartString(p.ByName("start"))
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
key, err := MarshalKey("bite", p.ByName("key"), start)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
reader := http.MaxBytesReader(w, r.Body, MaxBiteSize)
body, err := ioutil.ReadAll(reader)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
b := Bite {
Start: start,
Key: key,
Data: body,
}
out, err := proto.Marshal(&b)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
log.Print(err)
return
}
nats_conn.Publish("new_bite", out)
w.WriteHeader(200)
}
func PutBiteUser(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
start, err := ParseStartString(p.ByName("start"))
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
key, err := MarshalKey("user", p.ByName("key"), start)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
reader := http.MaxBytesReader(w, r.Body, MaxBiteSize)
body, err := ioutil.ReadAll(reader)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
b := Bite {
Start: start,
Key: key,
Data: body,
}
out, err := proto.Marshal(&b)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
log.Print(err)
return
}
nats_conn.Publish("new_bite_user", out)
w.WriteHeader(200)
}