package main import ( "bytes" "fmt" "log" "net" "os" "strings" "time" ) var p4info = []byte{157, 157, 0, 0, 0, 99, 109, 112, 102, 105, 108, 101, 0, 0, 0, 0, 0, 0, 99, 108, 105, 101, 110, 116, 0, 2, 0, 0, 0, 55, 54, 0, 97, 112, 105, 0, 5, 0, 0, 0, 57, 57, 57, 57, 57, 0, 101, 110, 97, 98, 108, 101, 83, 116, 114, 101, 97, 109, 115, 0, 0, 0, 0, 0, 0, 104, 111, 115, 116, 0, 10, 0, 0, 0, 98, 114, 101, 116, 116, 45, 109, 105, 110, 116, 0, 112, 111, 114, 116, 0, 14, 0, 0, 0, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 56, 49, 57, 50, 0, 115, 110, 100, 98, 117, 102, 0, 5, 0, 0, 0, 57, 56, 51, 48, 51, 0, 114, 99, 118, 98, 117, 102, 0, 6, 0, 0, 0, 55, 57, 54, 51, 53, 54, 0, 102, 117, 110, 99, 0, 8, 0, 0, 0, 112, 114, 111, 116, 111, 99, 111, 108, 0, 184, 184, 0, 0, 0, 112, 114, 111, 103, 0, 2, 0, 0, 0, 112, 52, 0, 118, 101, 114, 115, 105, 111, 110, 0, 27, 0, 0, 0, 50, 48, 49, 52, 46, 49, 47, 76, 73, 78, 85, 88, 50, 54, 88, 56, 54, 95, 54, 52, 47, 56, 50, 49, 57, 57, 48, 0, 99, 108, 105, 101, 110, 116, 0, 10, 0, 0, 0, 98, 114, 101, 116, 116, 45, 109, 105, 110, 116, 0, 99, 119, 100, 0, 21, 0, 0, 0, 47, 104, 111, 109, 101, 47, 98, 114, 101, 116, 116, 47, 103, 111, 47, 115, 114, 99, 47, 112, 52, 0, 104, 111, 115, 116, 0, 10, 0, 0, 0, 98, 114, 101, 116, 116, 45, 109, 105, 110, 116, 0, 111, 115, 0, 4, 0, 0, 0, 85, 78, 73, 88, 0, 117, 115, 101, 114, 0, 5, 0, 0, 0, 98, 114, 101, 116, 116, 0, 99, 104, 97, 114, 115, 101, 116, 0, 1, 0, 0, 0, 49, 0, 102, 117, 110, 99, 0, 9, 0, 0, 0, 117, 115, 101, 114, 45, 105, 110, 102, 111, 0} func main() { port := "8192" ips, err := net.LookupIP("localhost") checkError(err) for _, ip := range ips { fmt.Println("Resolved addr ", ip) } addr := strings.Join([]string{ips[0].String(), port}, ":") fmt.Println("Joined addr string ", addr) tcpAddr, err := net.ResolveTCPAddr("tcp", addr) checkError(err) fmt.Println("TCPAddr ", tcpAddr.String()) conn, err := net.DialTCP("tcp", nil, tcpAddr) checkError(err) handleConn(conn) } type message struct { size int data *[]byte } type data struct { header [5]byte value string } func handleConn(c net.Conn) { _, err := c.Write(p4info) checkError(err) msgc := make(chan message) resp := make(chan map[string]data) go translate(msgc, resp) for { c.SetReadDeadline(time.Now().Add(time.Second)) size := getHeader(c) buf := make([]byte, size) n, err := c.Read(buf) checkError(err) if err != nil || n == 0 { c.Close() break } /*Perhaps pointless using channels/goroutine, since we are blocking for the response anyway*/ msgc <- message{size, &buf} kv := <-resp log.Printf("%+v\n\n", kv) // If we get func = release we have finished reading f, ok := kv["func"] if ok && f.value == "release" { log.Println("Finished reading") break } } } func getHeader(c net.Conn) int { header := make([]byte, 5) n, err := c.Read(header) if readError(err, n, 5) { log.Printf("Stopped getting messages\n") c.Close() os.Exit(1) } return int(header[1]) } func translate(m <-chan message, resp chan<- map[string]data) { for msg := range m { buf := bytes.NewBuffer(*msg.data) kv := make(map[string]data) l := buf.Len() i := 0 //Read in the buffer for { if i == l { // We've reached the end break } var key bytes.Buffer for { n := buf.Next(1) if n[0] != 0 { key.Write(n) } else { buf.UnreadByte() break } i++ } var header [5]byte copy(header[:], buf.Next(5)) i += 5 value := buf.Next(int(header[1])) kv[key.String()] = data{header, string(value)} i += int(header[1]) end := buf.Next(1) if int(end[0]) != 0 { log.Panicf("Expected null got %+v", end) } i++ } resp <- kv } } func readError(err error, size int, s_err int) bool { if err != nil || size != s_err { return true } return false } func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 10478 | Brett Bates | Message formatting only handled a single %replacement%, now does all | ||
#3 | 10477 | Brett Bates |
The original point in doing this was to try to retrieve a p4 info, so now formats the output Can get a p4 info request returned :) |
||
#2 | 10476 | Brett Bates |
Still just a thought exercise but now uses the length correctly, and composes the request The encoder and decoder bits don't really mirror eachother, just wrote the quickest bit of code possible For an actual, non-messy, useful, golike api would make the buffer readers and writers implement their io counterparts, should also be able to create multiple connections, which would return a goroutine with channels for encoding decoding etc. |
||
#1 | 10474 | Brett Bates |
The most basic (useful) invoke/dispatch i could fathom for creating a p4 api in golang Currently just sends out the protocol/user-info call as a byte slice, and reads in the response to a map of type map[string]data where data contains the header and the value Poorly written go, with a very dumb implementation of the protocol, using the 5 byte headers first value as our length... |