def get_session_token(self, duration_seconds=None, callback=None): #logging.info('retreiving session token') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) stream = tornado.iostream.SSLIOStream(s) stream._always_callback = True # get callbacks on stream.connect addr = (self.DefaultRegionEndpoint,443) yield gen.Task( stream.connect, addr ) if stream.error: callback(ErrorResponse('error connecting to %s to get token' % str(addr) ) ) raise StopIteration data = {'Action':'GetSessionToken'} if duration_seconds: data['DurationSeconds'] = duration_seconds request = AWSRequest('POST', self.DefaultRegionEndpoint, data) request.set_parameter('Version', self.APIVersion) request.sign_request(self.aws_key, self.aws_secret) body = request.to_postdata() request.headers['Content-Length'] = str(len(body)) towrite = request.make_request_headers() + body #logging.info('writing %s' % towrite) yield gen.Task( stream.write, towrite ) rawheaders = yield gen.Task( stream.read_until, '\r\n\r\n' ) if not rawheaders: logging.error('read until headers returned null, likely socket closed...') callback( ErrorResponse('socket closed') ) raise StopIteration code, headers = parse_headers(rawheaders) if code != 200: logging.error('get token: got error response %s, %s' % (code, headers)) callback( ErrorResponse('non 200 response %s' % code ) ) stream.close() raise StopIteration if 'Content-Length' in headers: body = yield gen.Task( stream.read_bytes, int(headers['Content-Length']) ) if not body: logging.error('conn closed reading for body?') callback( ErrorResponse('socket closed') ) raise StopIteration #logging.info('got body %s' % body) response = Response( code, headers, body ) callback( response ) else: logging.error('chunked encoding response?') callback( ErrorResponse('unable to parse chunked encoding') ) if not stream.closed(): stream.close()
def do_request(self, target, params, retry_if_invalid_stream=True, retry_on_expired_token=True, callback=None): if 'verbose' in options: if options.verbose > 0: logging.info('request %s %s' % (target, params)) elif options.verbose > 0: logging.info('request %s' % (target)) if not self.session_token: response = yield gen.Task( self.get_session_token, duration_seconds=3600 ) if response.error: callback(response) raise StopIteration self.session_token = response.meta self.update_secret( str(response.meta['SecretAccessKey']) ) #logging.info('got session token') stream_tries = 0 while stream_tries <= 2: stream_tries += 1 stream = yield gen.Task( self.get_stream ) if stream.error: logging.error('error getting stream %s' % stream) callback( ErrorResponse('unable to get stream') ) raise StopIteration request = AWSRequest('POST', self.DefaultHost, params) body = request.to_json_postdata() request.body = body request.headers['X-Amz-Target'] = 'DynamoDB_20111205.%s' % target if not self.session_token: err = 'session token was invalidated while getting a db connection' logging.error(err) callback( ErrorResponse(err) ) raise StopIteration self.add_auth(request, security_token = self.session_token['SessionToken'], access_key = self.session_token['AccessKeyId']) request.headers['Content-Length'] = str(len(body)) towrite = request.make_request_headers() + body stream._current_request = request #request.callback = callback # we're handling error states ourselves #logging.info('writing to stream..') yield gen.Task( stream.write, towrite ) if stream.error: logging.error('connection died while writing to it') callback( ErrorResponse('connection died while writing') ) raise StopIteration #yield gen.Task( asyncsleep, 10 )# for testing #logging.info('reading from stream') rawheaders = yield gen.Task( stream.read_until, '\r\n\r\n' ) if not rawheaders or stream.error: logging.error('connection seems to have closed..') # if error is read error connection reset by peer, the request probably never made it in... (dead stream)... # if so, perhaps re-try. #callback( ErrorResponse('connection closed on reading for headers') ) #raise StopIteration else: break code, headers = parse_headers(rawheaders) if 'Content-Length' in headers: body = yield gen.Task( stream.read_bytes, int(headers['Content-Length']) ) if code != 200: logging.error('got error response %s, %s, %s' % (code, headers, body)) #logging.info('GOT BODY %s' % body) if not body: callback( ErrorResponse('connection closed on reading for body') ) raise StopIteration request.callback = None stream._current_request = None response = JSONResponse( code, headers, body ) if 'ConsumedCapacityUnits' in response.attributes: self.register_usage( target, params, response.attributes['ConsumedCapacityUnits'] ) if 'verbose' in options and options.verbose > 1 and code == 200: logging.info('got response :%s, %s' % (response, response.attributes)) if code == 400 and response.attributes and '__type' in response.attributes and response.attributes['__type'].endswith('ProvisionedThroughputExceededException'): logging.error( 'throughput exceeded' ) if 'TableName' in params: if self.Statsd: self.Statsd.increment('dynamo.throughput_exceeded.%s' % params['TableName']) elif code == 400 and response.attributes and '__type' in response.attributes and response.attributes['__type'].endswith('ExpiredTokenException'): self.session_token = None # re-do this request... if retry_on_expired_token: result = yield gen.Task( self.do_request, target, params, retry_if_invalid_stream=True, retry_on_expired_token=False ) callback(result) raise StopIteration callback( response ) else: logging.error('chunked encoding response?') callback( ErrorResponse('no content length header') ) if len(self.streams) > 25: logging.warn('too many db connections %s -- closing one' % len(self.streams)) self.remove_connection(stream)