Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 1
# encoding: ascii-8bit
2
# 
3
# Licensed to the Apache Software Foundation (ASF) under one
4
# or more contributor license agreements. See the NOTICE file
5
# distributed with this work for additional information
6
# regarding copyright ownership. The ASF licenses this file
7
# to you under the Apache License, Version 2.0 (the
8
# "License"); you may not use this file except in compliance
9
# with the License. You may obtain a copy of the License at
10
# 
11
#   http://www.apache.org/licenses/LICENSE-2.0
12
# 
13
# Unless required by applicable law or agreed to in writing,
14
# software distributed under the License is distributed on an
15
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
# KIND, either express or implied. See the License for the
17
# specific language governing permissions and limitations
18
# under the License.
19
# 
20
 
21
require 'socket'
22
 
23
module Thrift
24
  class Socket < BaseTransport
25
    def initialize(host='localhost', port=9090, timeout=nil)
26
      @host = host
27
      @port = port
28
      @timeout = timeout
29
      @desc = "#{host}:#{port}"
30
      @handle = nil
31
    end
32
 
33
    attr_accessor :handle, :timeout
34
 
35
    def open
36
      begin
37
        addrinfo = ::Socket::getaddrinfo(@host, @port).first
38
        @handle = ::Socket.new(addrinfo[4], ::Socket::SOCK_STREAM, 0)
39
        sockaddr = ::Socket.sockaddr_in(addrinfo[1], addrinfo[3])
40
        begin
41
          @handle.connect_nonblock(sockaddr)
42
        rescue Errno::EINPROGRESS
43
          unless IO.select(nil, [ @handle ], nil, @timeout)
44
            raise TransportException.new(TransportException::NOT_OPEN, "Connection timeout to #{@desc}")
45
          end
46
          begin
47
            @handle.connect_nonblock(sockaddr)
48
          rescue Errno::EISCONN
49
          end
50
        end
51
        @handle
52
      rescue StandardError => e
53
        raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
54
      end
55
    end
56
 
57
    def open?
58
      !@handle.nil? and !@handle.closed?
59
    end
60
 
61
    def write(str)
62
      raise IOError, "closed stream" unless open?
63
      begin
64
        if @timeout.nil? or @timeout == 0
65
          @handle.write(str)
66
        else
67
          len = 0
68
          start = Time.now
69
          while Time.now - start < @timeout
70
            rd, wr, = IO.select(nil, [@handle], nil, @timeout)
71
            if wr and not wr.empty?
72
              len += @handle.write_nonblock(str[len..-1])
73
              break if len >= str.length
74
            end
75
          end
76
          if len < str.length
77
            raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out writing #{str.length} bytes to #{@desc}")
78
          else
79
            len
80
          end
81
        end
82
      rescue TransportException => e
83
        # pass this on
84
        raise e
85
      rescue StandardError => e
86
        @handle.close
87
        @handle = nil
88
        raise TransportException.new(TransportException::NOT_OPEN, e.message)
89
      end
90
    end
91
 
92
    def read(sz)
93
      raise IOError, "closed stream" unless open?
94
 
95
      begin
96
        if @timeout.nil? or @timeout == 0
97
          data = @handle.readpartial(sz)
98
        else
99
          # it's possible to interrupt select for something other than the timeout
100
          # so we need to ensure we've waited long enough
101
          start = Time.now
102
          rd = nil # scoping
103
          loop do
104
            rd, = IO.select([@handle], nil, nil, @timeout)
105
            break if (rd and not rd.empty?) or Time.now - start >= @timeout
106
          end
107
          if rd.nil? or rd.empty?
108
            raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out reading #{sz} bytes from #{@desc}")
109
          else
110
            data = @handle.readpartial(sz)
111
          end
112
        end
113
      rescue TransportException => e
114
        # don't let this get caught by the StandardError handler
115
        raise e
116
      rescue StandardError => e
117
        @handle.close unless @handle.closed?
118
        @handle = nil
119
        raise TransportException.new(TransportException::NOT_OPEN, e.message)
120
      end
121
      if (data.nil? or data.length == 0)
122
        raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}")
123
      end
124
      data
125
    end
126
 
127
    def close
128
      @handle.close unless @handle.nil? or @handle.closed?
129
      @handle = nil
130
    end
131
 
132
    def to_io
133
      @handle
134
    end
135
  end
136
end