Skip to content

Commit

Permalink
Merge pull request #1 from softlayer/master-merging
Browse files Browse the repository at this point in the history
Master merging
  • Loading branch information
allmightyspiff authored Nov 5, 2019
2 parents 56d5ec7 + e4186a5 commit 5c4fa72
Show file tree
Hide file tree
Showing 11 changed files with 405 additions and 104 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
*.iml
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ implementation of xmlrpc that works over another protocol. To encode
request body there is EncodeMethodCall function. To decode server
response Response data type can be used.


## Testing

For a full test suite, you need to spin up the testing webserver
`~go/src/github.com/softlayer/xmlrpc $ ruby test_server.rb`

Then run the tests
`go test -v`

## Contribution

See [project status](#status).
Expand Down
76 changes: 49 additions & 27 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package xmlrpc
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/rpc"
"net/url"
"strconv"
"sync"
"time"
)

type Client struct {
Expand All @@ -28,21 +31,20 @@ type clientCodec struct {

// responses presents map of active requests. It is required to return request id, that
// rpc.Client can mark them as done.
responses map[uint64]*http.Response
mutex sync.Mutex

responsesMu sync.RWMutex
responses map[uint64]*http.Response
response Response

// ready presents channel, that is used to link request and it`s response.
ready chan uint64

// close notifies codec is closed.
close chan uint64

}

func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)

if err != nil {
return err
}
Expand All @@ -53,8 +55,8 @@ func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (
}
}

var httpResponse *http.Response
httpResponse, err = codec.httpClient.Do(httpRequest)

httpResponse, err := codec.httpClient.Do(httpRequest)

if err != nil {
return err
Expand All @@ -64,10 +66,9 @@ func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (
codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
}

codec.mutex.Lock()
codec.responsesMu.Lock()
codec.responses[request.Seq] = httpResponse
codec.mutex.Unlock()

codec.responsesMu.Unlock()
codec.ready <- request.Seq

return nil
Expand All @@ -76,37 +77,56 @@ func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (
func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
var seq uint64
select {
case seq = <-codec.ready:
case <-codec.close:
return errors.New("codec is closed")
case seq = <-codec.ready:
case <-codec.close:
return errors.New("codec is closed")
}
response.Seq = seq

codec.mutex.Lock()
codec.responsesMu.RLock()
httpResponse := codec.responses[seq]
delete(codec.responses, seq)
codec.mutex.Unlock()
codec.responsesMu.RUnlock()

defer httpResponse.Body.Close()

if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
response.Error = fmt.Sprintf("request error: bad status code - %d", httpResponse.StatusCode)
return nil
contentLength := httpResponse.ContentLength
if contentLength == -1 {
if ntcoentLengthHeader, ok := httpResponse.Header["Ntcoent-Length"]; ok {
ntcoentLength, err := strconv.ParseInt(ntcoentLengthHeader[0], 10, 64)
if err == nil {
contentLength = ntcoentLength
}
}
}

var respData []byte
if contentLength != -1 {
respData = make([]byte, contentLength)
_, err = io.ReadFull(httpResponse.Body, respData)
} else {
respData, err = ioutil.ReadAll(httpResponse.Body)
}

body, err := ioutil.ReadAll(httpResponse.Body)
if err != nil {
response.Error = err.Error()
return nil
}

resp := Response(body)
if err := resp.Err(); err != nil {
response.Error = err.Error()
return nil

resp := NewResponse(respData, httpResponse.StatusCode)

if resp.Failed() {
err := resp.Err()
response.Error = fmt.Sprintf("%v", err)
return err

}
codec.response = *resp

codec.response = resp
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
return &XmlRpcError{HttpStatusCode: httpResponse.StatusCode}
}

return nil
}
Expand All @@ -124,17 +144,19 @@ func (codec *clientCodec) Close() error {
}

close(codec.close)

return nil
}

// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
func NewClient(requrl string, transport http.RoundTripper, timeout time.Duration) (*Client, error) {
if transport == nil {
transport = http.DefaultTransport
}

httpClient := &http.Client{Transport: transport}
httpClient := &http.Client{
Transport: transport,
Timeout: timeout,
}

jar, err := cookiejar.New(nil)

Expand All @@ -151,8 +173,8 @@ func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
codec := clientCodec{
url: u,
httpClient: httpClient,
close: make(chan uint64),
ready: make(chan uint64),
close: make(chan uint64),
responses: make(map[uint64]*http.Response),
cookies: jar,
}
Expand Down
2 changes: 1 addition & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func Test_BadStatus(t *testing.T) {
}

func newClient(t *testing.T) *Client {
client, err := NewClient("http://localhost:5001", nil)
client, err := NewClient("http://localhost:5001", nil, 5 * time.Second)
if err != nil {
t.Fatalf("Can't create client: %v", err)
}
Expand Down
Loading

0 comments on commit 5c4fa72

Please sign in to comment.