def test_manager(self): request1 = http.ClientRequest("http://www.domain1.com/") self.assertTrue(request1.method == "GET") request2 = http.ClientRequest("http://www.domain2.com/", "HEAD") self.assertTrue(request2.method == "HEAD") self.client.queue_request(request1) self.client.queue_request(request2) # thread_loop will process the queue until it blocks for more # than the timeout (default, 60s) self.client.thread_loop(timeout=5) response1 = request1.response self.assertTrue( str(response1.protocol) == "HTTP/1.1", "Protocol in response1: %s" % response1.protocol) self.assertTrue(response1.status == 200, "Status in response1: %i" % response1.status) self.assertTrue(response1.reason == "You got it!", "Reason in response1: %s" % response1.reason) self.assertTrue(request1.res_body == TEST_STRING, "Data in response1: %s" % request1.res_body) response2 = request2.response self.assertTrue( str(response2.protocol) == "HTTP/1.1", "Protocol in response2: %s" % response2.protocol) self.assertTrue(response2.status == 200, "Status in response2: %i" % response2.status) self.assertTrue(response2.reason == "You got it!", "Reason in response2: %s" % response2.reason) self.assertTrue(request2.res_body == "", "Data in response2: %s" % request2.res_body)
def test_continue(self): """RFC2616: If a client will wait for a 100 (Continue) response before sending the request body, it MUST send an Expect request-header field with the "100-continue" expectation.""" request1 = http.ClientRequest("http://www.domain1.com/file", method="PUT", entity_body=TEST_BODY) self.assertTrue(request1.method == "PUT") request2 = http.ClientRequest("http://www.domain1.com/file2", method="PUT", entity_body=TEST_BODY) request2.set_expect_continue() self.client.queue_request(request1) self.assertTrue(request1.get_header('Expect') is None) self.client.queue_request(request2) self.assertTrue(request2.get_header('Expect') == "100-continue") # thread_loop will process the queue until it blocks for more # than the timeout (default, forever) self.client.thread_loop(timeout=5) response1 = request1.response self.assertTrue(response1.status == 200, "Status in response1: %i" % response1.status) self.assertTrue(response1.reason == "OK", "Reason in response1: %s" % response1.reason) self.assertTrue(request1.res_body == '', "Data in response1: %s" % request1.res_body) response2 = request2.response self.assertTrue(response2.status == 200, "Status in response2: %i" % response2.status) self.assertTrue(response2.reason == "OK", "Reason in response2: %s" % response2.reason) self.assertTrue(request2.res_body == "", "Data in response2: %s" % request2.res_body)
def test_streamed_put(self): request = http.ClientRequest("http://www.domain1.com/file2", "PUT", entity_body=StringIO("123456\r\n\r\n")) request.set_expect_continue() self.client.process_request(request) response = request.response self.assertTrue(response.status == 200, "Status in response: %i" % response.status) self.assertTrue(response.reason == "OK", "Reason in response: %s" % response.reason) self.assertTrue(request.res_body == "", "Data in response: %s" % request.res_body) request = http.ClientRequest("http://www.domain1.com/file", "PUT", entity_body=StringIO("123456\r\n\r\n")) request.set_content_length(10) self.client.process_request(request) response = request.response self.assertTrue(response.status == 200, "Status in response: %i" % response.status) self.assertTrue(response.reason == "OK", "Reason in response: %s" % response.reason) self.assertTrue(request.res_body == "", "Data in response: %s" % request.res_body)
def read_stream(self, key, out=None): """Reads a media resource""" if not self.is_medialink_collection(): raise ExpectedMediaLinkCollection streamURL = str(self.baseURI) + core.ODataURI.FormatKeyDict( self.entity_set.GetKeyDict(key)) + "/$value" if out is None: request = http.ClientRequest(streamURL, 'HEAD') else: request = http.ClientRequest(streamURL, 'GET', res_body=out) request.set_accept("*/*") self.client.process_request(request) if request.status == 200: # success, read the entity information back from the response sinfo = core.StreamInfo() sinfo.type = request.response.get_content_type() sinfo.size = request.response.get_content_length() sinfo.modified = request.response.get_last_modified() sinfo.created = sinfo.modified sinfo.md5 = request.response.get_content_md5() return sinfo elif request.status == 404: # sort of success, we return an empty stream sinfo = core.StreamInfo() sinfo.size = 0 return sinfo else: self.RaiseError(request)
def test_async_close(self): """RFC2616: clients, servers, and proxies MUST be able to recover from asynchronous close events Client software SHOULD reopen the transport connection and retransmit the aborted sequence of requests without user interaction so long as the request sequence is idempotent""" request = http.ClientRequest("http://www.domain5.com/unreliable") # this request will timeout the first time before any data # has been sent to the client, it should retry and succeed self.client.process_request(request) response = request.response self.assertFalse(response.status is None, "No response") self.assertTrue(response.status == 200, "Status in response: %i" % response.status) self.assertTrue(response.reason == "Thanks for your patience", "Reason in response: %s" % response.reason) self.assertTrue(response.entity_body.getvalue() == TEST_STRING, "Body in response: %i" % response.status) request = http.ClientRequest("http://www.domain6.com/unreliable", min_retry_time=0.1) # this request will always fail with a broken server self.client.process_request(request) response = request.response self.assertTrue(response.status is None, "No response")
def test_ntlm_negotiate(self): request1 = client.ClientRequest("http://www.domain2.com/") self.client.queue_request(request1) # thread_loop will process the queue until it blocks for more # than the timeout (default, 60s) self.client.thread_loop() response1 = request1.response self.assertTrue(response1.status == 401, "Status in response1: %i" % response1.status) challenges = response1.get_www_authenticate() self.assertTrue(len(challenges) == 2) c = NTLMCredentials() c.set_ntlm_credentials('mydomain\\user', 'Password') c.protectionSpace = "http://www.domain2.com" self.client.add_credentials(c) request2 = client.ClientRequest("http://www.domain2.com/") self.client.queue_request(request2) self.client.thread_loop() response2 = request2.response self.assertTrue( str(response2.protocol) == "HTTP/1.1", "Protocol in response1: %s" % response1.protocol) self.assertTrue(response2.status == 200, "Status in response1: %i" % response1.status) self.assertTrue(response2.reason == "You got it!", "Reason in response1: %s" % response1.reason) self.assertTrue(request2.res_body == TEST_STRING, "Data in response1: %s" % request1.res_body)
def test_401(self): request1 = client.ClientRequest("http://www.domain1.com/") self.client.queue_request(request1) # thread_loop will process the queue until it blocks for more # than the timeout (default, 60s) self.client.thread_loop() response1 = request1.response self.assertTrue(response1.status == 401, "Status in response1: %i" % response1.status) self.assertTrue(response1.reason == "Who are you?", "Reason in response1: %s" % response1.reason) self.assertTrue(request1.res_body == '', "Data in response1: %s" % request1.res_body) challenges = response1.get_www_authenticate() self.assertTrue(len(challenges) == 1 and isinstance( challenges[0], BasicChallenge), "Challenge") c = BasicCredentials() c.protectionSpace = "http://www.domain1.com" c.realm = None # should match all realms! c.userid = 'user' c.password = '******' self.client.add_credentials(c) request2 = client.ClientRequest("http://www.domain1.com/") self.client.queue_request(request2) self.client.thread_loop() response2 = request2.response self.assertTrue(str(response2.protocol) == "HTTP/1.1", "Protocol in response1: %s" % response1.protocol) self.assertTrue(response2.status == 200, "Status in response1: %i" % response1.status) self.assertTrue(response2.reason == "You got it!", "Reason in response1: %s" % response1.reason) self.assertTrue(request2.res_body == TEST_STRING, "Data in response1: %s" % request1.res_body)
def __setitem__(self, key, entity): if not isinstance( entity, edm.Entity) or entity.entity_set is not self.entity_set: raise TypeError if key != entity.key(): raise ValueError if not entity.exists: raise edm.NonExistentEntity(str(entity.get_location())) if not self.isCollection: request = http.ClientRequest(str(self.baseURI), 'GET') self.client.process_request(request) if request.status == 200: # this collection is not empty, which will be an error # unless it already contains entity, in which case it's # a no-op existingEntity = self.new_entity() doc = core.Document() doc.Read(request.res_body) existingEntity.exists = True doc.root.GetValue(existingEntity) if existingEntity.key() == entity.key(): return else: raise edm.NavigationError( "Navigation property %s already points to an entity (use replace to update it)" % self.name) elif request.status != 404: # some type of error self.RaiseError(request) doc = core.Document(root=core.URI) doc.root.SetValue(str(entity.get_location())) data = str(doc) request = http.ClientRequest(str(self.linksURI), 'PUT', entity_body=data) request.set_content_type( params.MediaType.from_str('application/xml')) self.client.process_request(request) if request.status == 204: return else: self.RaiseError(request) else: doc = core.Document(root=core.URI) doc.root.SetValue(str(entity.get_location())) data = str(doc) request = http.ClientRequest(str(self.linksURI), 'POST', entity_body=data) request.set_content_type( params.MediaType.from_str('application/xml')) self.client.process_request(request) if request.status == 204: return else: self.RaiseError(request)
def __delitem__(self, key): if self.isCollection: entity = self.new_entity() entity.set_key(key) request = http.ClientRequest( str(self.linksURI) + core.ODataURI.FormatEntityKey(entity), 'DELETE') else: # danger, how do we know that key really is the right one? request = http.ClientRequest(str(self.linksURI), 'DELETE') self.client.process_request(request) if request.status == 204: # success, nothing to read back return else: self.RaiseError(request)
def __len__(self): if self.isCollection: return super(NavigationCollection, self).__len__() else: # This is clumsy as we grab the entity itself entityURL = str(self.baseURI) sysQueryOptions = {} if self.filter is not None: sysQueryOptions[core.SystemQueryOption.filter] = unicode( self.filter) if sysQueryOptions: entityURL = uri.URI.from_octets( entityURL + "?" + core.ODataURI.FormatSysQueryOptions(sysQueryOptions)) request = http.ClientRequest(str(entityURL)) request.set_header('Accept', 'application/atom+xml;type=entry') self.client.process_request(request) if request.status == 404: # if we got a 404 from the underlying system we're done return 0 elif request.status != 200: raise UnexpectedHTTPResponse( "%i %s" % (request.status, request.response.reason)) doc = core.Document(baseURI=entityURL) doc.Read(request.res_body) if isinstance(doc.root, atom.Entry): entity = core.Entity(self.entity_set) entity.exists = True doc.root.GetValue(entity) return 1 else: raise core.InvalidEntryDocument(str(entityURL))
def update_entity(self, entity): if not entity.exists: raise edm.NonExistentEntity(str(entity.get_location())) doc = core.Document(root=core.Entry) doc.root.SetValue(entity, True) data = str(doc) request = http.ClientRequest(str(entity.get_location()), 'PUT', entity_body=data) request.set_content_type( params.MediaType.from_str(core.ODATA_RELATED_ENTRY_TYPE)) self.client.process_request(request) if request.status == 204: # success, nothing to read back but we're not done # we've only updated links to existing entities on properties with # single cardinality for k, dv in entity.NavigationItems(): if not dv.bindings or dv.isCollection: continue # we need to know the location of the target entity set binding = dv.bindings[-1] if isinstance(binding, edm.Entity) and binding.exists: dv.bindings = [] # now use the default method to finish the job self.update_bindings(entity) return else: self.RaiseError(request)
def new_stream(self, src, sinfo=None, key=None): """Creates a media resource""" if not self.is_medialink_collection(): raise ExpectedMediaLinkCollection if sinfo is None: sinfo = core.StreamInfo() request = http.ClientRequest(str(self.baseURI), 'POST', entity_body=src) request.set_content_type(sinfo.type) if sinfo.size is not None: request.set_content_length(sinfo.size) if sinfo.modified is not None: request.set_last_modified(params.FullDate(src=sinfo.modified)) if key: request.set_header("Slug", str(app.Slug(unicode(key)))) self.client.process_request(request) if request.status == 201: # success, read the entity back from the response doc = core.Document() doc.Read(request.res_body) entity = self.new_entity() entity.exists = True doc.root.GetValue(entity) return entity else: self.RaiseError(request)
def insert_entity(self, entity): if entity.exists: raise edm.EntityExists(str(entity.get_location())) if self.is_medialink_collection(): # insert a blank stream and then update mle = self.new_stream(src=StringIO()) entity.set_key(mle.key()) # 2-way merge mle.merge(entity) entity.merge(mle) entity.exists = True self.update_entity(entity) else: doc = core.Document(root=core.Entry(None, entity)) data = str(doc) request = http.ClientRequest(str(self.baseURI), 'POST', entity_body=data) request.set_content_type( params.MediaType.from_str(core.ODATA_RELATED_ENTRY_TYPE)) self.client.process_request(request) if request.status == 201: # success, read the entity back from the response doc = core.Document() doc.Read(request.res_body) entity.exists = True doc.root.GetValue(entity) # so which bindings got handled? Assume all of them for k, dv in entity.NavigationItems(): dv.bindings = [] else: self.RaiseError(request)
def test_chain(self): try: from OpenSSL import SSL # noqa except ImportError: logging.warning( "Skipping chain test (install pyOpenSSL to activate test)") return client = http.Client() try: client.get_server_certificate_chain( uri.URI.from_octets("http://www.pyslet.org/")) self.fail("Can't get certificate chain from http URL") except ValueError: pass chain = client.get_server_certificate_chain( uri.URI.from_octets("https://code.google.com/p/qtimigration/")) fpath = os.path.join(self.d, 'ca_certs.txt') with open(fpath, 'wb') as f: f.write(chain) client = http.Client(ca_certs=fpath) request = http.ClientRequest("https://code.google.com/p/qtimigration/") try: client.process_request(request) except messages.HTTPException as err: logging.error(err) client.close()
def test_retries(self): request = http.ClientRequest("http://www.domain1.com/", max_retries=10, min_retry_time=4) self.assertTrue(request.max_retries == 10) self.assertTrue(request.nretries == 0) self.assertTrue(request.retry_time == 0) MockTime.now = 10.0 ranges = [ (10.0, 10.0), # 10+0 +-0 (13.0, 15.0), # 10+4 +-1 (13.0, 15.0), # 10+4 +-1 (16.0, 20.0), # 10+8 +-2 (19.0, 25.0), # 10+12 +-3 (25.0, 35.0), # 10+20 +-5 (34.0, 50.0), # 10+32 +-8 (49.0, 75.0), # 10+52 +-13 (73.0, 115.0), # 10+84 +-21 (112.0, 180.0) ] # 10+136 +-34 for i in xrange(10): # simulate a failed send request.connect(None, 0) request.disconnect(1) self.assertTrue(request.can_retry(), "retry %ith time" % i) self.assertTrue( request.retry_time >= ranges[i][0], "%f too small in pair %i" % (request.retry_time, i)) self.assertTrue( request.retry_time <= ranges[i][1], "%f too large in pair %i" % (request.retry_time, i))
def test_async_error(self): request = http.ClientRequest("http://www.domain7.com/", min_retry_time=0.1) self.client.process_request(request) response = request.response self.assertTrue(response.status == 200) self.client.idle_cleanup(0)
def test_google_insecure(self): client = http.Client() request = http.ClientRequest("https://code.google.com/p/qtimigration/") try: client.process_request(request) except messages.HTTPException as err: logging.error(err) client.close()
def domain4_thread_oneshot(self): time.sleep(1) logging.debug("domain4_thread_oneshot starting...") request = http.ClientRequest("http://www.domain4.com/index.txt") try: self.client.process_request(request) except messages.HTTPException as err: logging.error(err)
def __getitem__(self, key): sysQueryOptions = {} if self.filter is not None: sysQueryOptions[core.SystemQueryOption.filter] = "%s and %s" % ( core.ODataURI.key_dict_to_query( self.entity_set.key_dict(key)), unicode(self.filter)) entityURL = str(self.baseURI) else: entityURL = ( str(self.baseURI) + core.ODataURI.FormatKeyDict(self.entity_set.GetKeyDict(key))) if self.expand is not None: sysQueryOptions[core.SystemQueryOption.expand] = core.FormatExpand( self.expand) if self.select is not None: sysQueryOptions[core.SystemQueryOption.select] = core.FormatSelect( self.select) if sysQueryOptions: entityURL = uri.URI.from_octets( entityURL + "?" + core.ODataURI.FormatSysQueryOptions(sysQueryOptions)) request = http.ClientRequest(str(entityURL)) if self.filter: request.set_header('Accept', 'application/atom+xml') else: request.set_header('Accept', 'application/atom+xml;type=entry') self.client.process_request(request) if request.status == 404: raise KeyError(key) elif request.status != 200: raise UnexpectedHTTPResponse( "%i %s" % (request.status, request.response.reason)) doc = core.Document(baseURI=entityURL) doc.Read(request.res_body) if isinstance(doc.root, atom.Entry): entity = core.Entity(self.entity_set) entity.exists = True doc.root.GetValue(entity) return entity elif isinstance(doc.root, atom.Feed): nresults = len(doc.root.Entry) if nresults == 0: raise KeyError(key) elif nresults == 1: e = doc.root.Entry[0] entity = core.Entity(self.entity_set) entity.exists = True e.GetValue(entity) return entity else: raise UnexpectedHTTPResponse( "%i entities returned from %s" % nresults, entityURL) elif isinstance(doc.root, core.Error): raise KeyError(key) else: raise core.InvalidEntryDocument(str(entityURL))
def test_post_after_shutdown(self): request = http.ClientRequest("http://www.domain8.com/", method="POST", entity_body="Hello") self.client.process_request(request) response = request.response self.assertTrue(response.status == 200) # the remote end has shut down the socket, but without telling # us so we'll get an error here. We should be able to detect # that the error happens before we send the request and so # re-establish the connection. A fail is likely though because # the method is POST which we won't resend if the data was # partially sent. request = http.ClientRequest("http://www.domain8.com/", method="POST", entity_body="Hello") self.client.process_request(request) response = request.response self.assertTrue(response.status == 200)
def __delitem__(self, key): entity = self.new_entity() entity.set_key(key) request = http.ClientRequest(str(entity.get_location()), 'DELETE') self.client.process_request(request) if request.status == 204: # success, nothing to read back return else: self.RaiseError(request)
def read_stream_close(self, key): """Creates a generator for a media resource.""" if not self.is_medialink_collection(): raise ExpectedMediaLinkCollection streamURL = str(self.baseURI) + core.ODataURI.FormatKeyDict( self.entity_set.GetKeyDict(key)) + "/$value" swrapper = EntityStream(self) request = http.ClientRequest(streamURL, 'GET', res_body=swrapper) request.set_accept("*/*") swrapper.start_request(request) return swrapper.sinfo, swrapper.data_gen()
def test_simple(self): """simple tests RFC 2616: If the abs_path is not present in the URL, it MUST be given as "/" when used as a Request-URI for a resource""" request = http.ClientRequest("http://www.example.com/") request.start_sending() request_line = request.send_start() self.assertTrue(request_line.startswith("GET / HTTP/1."), request_line)
def test_google_secure(self): client = http.Client( ca_certs=os.path.join(TEST_DATA_DIR, "ca_certs.txt")) request = http.ClientRequest("https://code.google.com/p/qtimigration/") try: client.process_request(request) except messages.HTTPException as err: logging.error(err) client.close() client = http.Client( ca_certs=os.path.join(TEST_DATA_DIR, "no_certs.txt")) request = http.ClientRequest("https://code.google.com/p/qtimigration/") try: client.process_request(request) if request.status != 0: self.fail("Expected status=0 after security failure") if not request.error: self.fail("Expected error after security failure") logging.info(str(request.error)) except messages.HTTPException as err: logging.error(str(err)) client.close()
def test_async_close2(self): """RFC2616: Non-idempotent methods or sequences MUST NOT be automatically retried.""" request = http.ClientRequest("http://www.domain5.com/unreliable", method="POST", entity_body="Hello") # this request will timeout the first time before any data # has been sent to the client, it should retry and fail! self.client.process_request(request) response = request.response self.assertTrue(response.status is None, "No response")
def test_streamed_get(self): buff = StringIO() request = http.ClientRequest("http://www.domain1.com/", "GET", entity_body=None, res_body=buff) self.client.process_request(request) response = request.response self.assertTrue(response.status == 200, "Status in response: %i" % response.status) self.assertTrue(buff.getvalue() == TEST_STRING, "Data in response: %s" % request.res_body) self.assertTrue(request.res_body == "", "Data in streamed response: %s" % request.res_body)
def test_redirect(self): request = http.ClientRequest("http://www.domain2.com/") self.client.queue_request(request) self.client.thread_loop(timeout=5) response = request.response self.assertTrue( str(response.protocol) == "HTTP/1.1", "Protocol in response1: %s" % response.protocol) self.assertTrue(response.status == 200, "Status in response: %i" % response.status) self.assertTrue(response.reason == "You got it!", "Reason in response: %s" % response.reason) self.assertTrue(request.res_body == TEST_STRING, "Data in response: %s" % repr(request.res_body))
def test_nochunked(self): """RFC2616: For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing a message-body MUST include a valid Content-Length header field unless the server is known to be HTTP/1.1 compliant""" t = threading.Thread(target=self.run_legacy, args=(2, )) t.start() request = http.ClientRequest("http://localhost:%i/nochunked" % self.port) self.assertTrue(request.protocol == params.HTTP_1p1) # start off optimistic about keep_alive self.assertTrue(request.keep_alive) self.client.process_request(request) self.assertTrue(request.response.status == 200) self.assertTrue(request.response.protocol == params.HTTP_1p0) # legacy server closes the connection self.assertFalse(request.response.keep_alive) # now try and make a call which would normally default to chunked data = "How long is a piece of string?" bodytext = server.Pipe(rblocking=False, timeout=10) bodytext.write(data) request = http.ClientRequest("http://localhost:%i/nochunked" % self.port, "PUT", entity_body=bodytext) # we should now know that the server is 1.0, so we expect an # error when trying to send an unbounded entity without # content-length self.client.process_request(request) self.assertTrue(request.response.status is None) request.set_content_length(len(data)) self.client.process_request(request) self.assertTrue(request.response.status == 204) self.assertFalse(request.response.keep_alive)
def test_upgrade(self): request = http.ClientRequest("http://www.domain9.com/socket") request.set_upgrade([params.ProductToken("happy")]) self.client.process_request(request) self.assertTrue(request.status == 101) try: self.assertTrue(isinstance(request.send_pipe, Pipe)) self.assertTrue(isinstance(request.recv_pipe, Pipe)) request.send_pipe.write(b'hello\r\n') request.send_pipe.write_eof() output = request.recv_pipe.read() self.assertTrue(output == b'hello\r\n', "Failed echo test on upgrade: %s" % str(output)) finally: request.recv_pipe.close()
def entity_generator(self): feedURL = self.baseURI sysQueryOptions = {} if self.filter is not None: sysQueryOptions[core.SystemQueryOption.filter] = unicode( self.filter) if self.expand is not None: sysQueryOptions[core.SystemQueryOption.expand] = core.FormatExpand( self.expand) if self.select is not None: sysQueryOptions[core.SystemQueryOption.select] = core.FormatSelect( self.select) if self.orderby is not None: sysQueryOptions[core.SystemQueryOption. orderby] = core.CommonExpression.OrderByToString( self.orderby) if sysQueryOptions: feedURL = uri.URI.from_octets( str(feedURL) + "?" + core.ODataURI.FormatSysQueryOptions(sysQueryOptions)) while True: request = http.ClientRequest(str(feedURL)) request.set_header('Accept', 'application/atom+xml') self.client.process_request(request) if request.status != 200: raise UnexpectedHTTPResponse( "%i %s" % (request.status, request.response.reason)) doc = core.Document(baseURI=feedURL) doc.Read(request.res_body) if isinstance(doc.root, atom.Feed): if len(doc.root.Entry): for e in doc.root.Entry: entity = core.Entity(self.entity_set) entity.exists = True e.GetValue(entity) yield entity else: break else: raise core.InvalidFeedDocument(str(feedURL)) feedURL = None for link in doc.root.Link: if link.rel == "next": feedURL = link.ResolveURI(link.href) break if feedURL is None: break