Trying to implement Dial for usage with http.Transport

This commit is contained in:
Henry
2014-02-10 21:01:55 +01:00
parent a534c1afaa
commit ce26df96ac
5 changed files with 194 additions and 39 deletions

View File

@ -3,12 +3,11 @@ package goSam
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io"
"net" "net"
) )
type Client struct { type Client struct {
samConn io.ReadWriteCloser samConn net.Conn
fromSam *bufio.Reader fromSam *bufio.Reader
toSam *bufio.Writer toSam *bufio.Writer

42
dial.go Normal file
View File

@ -0,0 +1,42 @@
package goSam
import (
"fmt"
"net"
"strings"
)
// implements the net.Dial function to be used as http.Transport
func (c *Client) Dial(network, addr string) (net.Conn, error) {
addr = addr[:strings.Index(addr, ":")]
addr, err := c.Lookup(addr)
if err != nil {
return nil, err
}
fmt.Println("Dial Lookup:", addr)
id, _, err := c.createStreamSession("")
if err != nil {
return nil, err
}
newC, err := NewDefaultClient()
if err != nil {
return nil, err
}
if newC.Hello() != nil {
return nil, err
}
fmt.Println("newC Hello OK")
if newC.StreamConnect(id, addr) != nil {
return nil, err
}
fmt.Println("StreamConnect OK")
return newC.samConn, nil
}

View File

@ -4,26 +4,25 @@ import (
"fmt" "fmt"
) )
type Result int
const ( const (
ResultOk Result = iota //Operation completed successfully ResultOk = "OK" //Operation completed successfully
ResultCantReachPeer //The peer exists, but cannot be reached ResultCantReachPeer = "CANT_REACH_PEER" //The peer exists, but cannot be reached
ResultDuplicatedDest //The specified Destination is already in use ResultDuplicatedId = "DUPLICATED_ID" //If the nickname is already associated with a session :
ResultI2PError //A generic I2P error (e.g. I2CP disconnection, etc.) ResultDuplicatedDest = "DUPLICATED_DEST" //The specified Destination is already in use
ResultInvalidKey //The specified key is not valid (bad format, etc.) ResultI2PError = "I2P_ERROR" //A generic I2P error (e.g. I2CP disconnection, etc.)
ResultKeyNotFound //The naming system can't resolve the given name ResultInvalidKey = "INVALID_KEY" //The specified key is not valid (bad format, etc.)
ResultPeerNotFound //The peer cannot be found on the network ResultKeyNotFound = "KEY_NOT_FOUND" //The naming system can't resolve the given name
ResultTimeout // Timeout while waiting for an event (e.g. peer answer) ResultPeerNotFound = "PEER_NOT_FOUND" //The peer cannot be found on the network
ResultTimeout = "TIMEOUT" // Timeout while waiting for an event (e.g. peer answer)
) )
type ReplyError struct { type ReplyError struct {
Result Result Result string
Reply *Reply Reply *Reply
} }
func (r ReplyError) Error() string { func (r ReplyError) Error() string {
return fmt.Sprintf("ReplyError: Result:%d - Reply:%+v", r.Reply) return fmt.Sprintf("ReplyError: Result:%s - Reply:%+v", r.Result, r.Reply)
} }
func (c *Client) Lookup(name string) (addr string, err error) { func (c *Client) Lookup(name string) (addr string, err error) {
@ -40,7 +39,7 @@ func (c *Client) Lookup(name string) (addr string, err error) {
line string line string
r *Reply r *Reply
) )
for {
line, err = c.fromSam.ReadString('\n') line, err = c.fromSam.ReadString('\n')
if err != nil { if err != nil {
return return
@ -48,12 +47,12 @@ func (c *Client) Lookup(name string) (addr string, err error) {
r, err = parseReply(line) r, err = parseReply(line)
if err != nil { if err != nil {
break return
} }
if r.Topic != "NAMING" || r.Type != "REPLY" { if r.Topic != "NAMING" || r.Type != "REPLY" {
err = fmt.Errorf("Unknown Reply: %+v\n", r) err = fmt.Errorf("Unknown Reply: %+v\n", r)
break return
} }
switch r.Pairs["RESULT"] { switch r.Pairs["RESULT"] {
@ -62,14 +61,11 @@ func (c *Client) Lookup(name string) (addr string, err error) {
return return
case "KEY_NOT_FOUND": case "KEY_NOT_FOUND":
err = ReplyError{ResultKeyNotFound, r} err = ReplyError{ResultKeyNotFound, r}
return
} }
if r.Pairs["NAME"] != name { if r.Pairs["NAME"] != name {
err = fmt.Errorf("i2p Replyied with: %+v\n", r) err = fmt.Errorf("i2p Replyied with: %+v\n", r)
break
}
break
} }
return return

66
sessions.go Normal file
View File

@ -0,0 +1,66 @@
package goSam
import (
"fmt"
"math"
"math/rand"
)
func (c *Client) createStreamSession(dest string) (id int32, newDest string, err error) {
if dest == "" {
dest = "TRANSIENT"
}
id = rand.Int31n(math.MaxInt32)
createCmd := fmt.Sprintf("SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s\n", id, dest)
_, err = c.toSam.WriteString(createCmd)
if err != nil {
return
}
if err = c.toSam.Flush(); err != nil {
return
}
var (
line string
r *Reply
)
line, err = c.fromSam.ReadString('\n')
if err != nil {
return
}
fmt.Println("createStreamSession line:", line)
r, err = parseReply(line)
if err != nil {
return
}
if r.Topic != "SESSION" || r.Type != "STATUS" {
err = fmt.Errorf("Unknown Reply: %+v\n", r)
return
}
switch r.Pairs["RESULT"] {
case ResultOk:
fmt.Println("createStreamSession created")
newDest = r.Pairs["DESTINATION"]
return
case ResultDuplicatedId:
err = ReplyError{ResultDuplicatedId, r}
return
case ResultDuplicatedDest:
err = ReplyError{ResultDuplicatedDest, r}
return
case ResultInvalidKey:
err = ReplyError{ResultInvalidKey, r}
return
case ResultI2PError:
err = ReplyError{ResultKeyNotFound, r}
return
}
return
}

52
stream.go Normal file
View File

@ -0,0 +1,52 @@
package goSam
import (
"fmt"
)
func (c *Client) StreamConnect(id int32, dest string) error {
connectCmd := fmt.Sprintf("STREAM CONNECT ID=%d DESTINATION=%s\n", id, dest)
_, err := c.toSam.WriteString(connectCmd)
if err != nil {
return err
}
if err = c.toSam.Flush(); err != nil {
return err
}
var (
line string
r *Reply
)
line, err = c.fromSam.ReadString('\n')
if err != nil {
return err
}
r, err = parseReply(line)
if err != nil {
return err
}
if r.Topic != "STREAM" || r.Type != "STATUS" {
return fmt.Errorf("Unknown Reply: %+v\n", r)
}
switch r.Pairs["RESULT"] {
case ResultOk:
fmt.Println("StreamConnect OK")
return nil
case ResultDuplicatedId:
return ReplyError{ResultDuplicatedId, r}
case ResultDuplicatedDest:
return ReplyError{ResultDuplicatedDest, r}
case ResultInvalidKey:
return ReplyError{ResultInvalidKey, r}
case ResultI2PError:
return ReplyError{ResultKeyNotFound, r}
}
return nil
}