| /* |
| * libjingle |
| * Copyright 2004--2011, Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <string> |
| #include "talk/base/asynchttprequest.h" |
| #include "talk/base/gunit.h" |
| #include "talk/base/httpserver.h" |
| #include "talk/base/socketstream.h" |
| #include "talk/base/thread.h" |
| |
| namespace talk_base { |
| |
| static const SocketAddress kServerAddr("127.0.0.1", 0); |
| static const SocketAddress kServerHostnameAddr("localhost", 0); |
| static const char kServerGetPath[] = "/get"; |
| static const char kServerPostPath[] = "/post"; |
| static const char kServerResponse[] = "This is a test"; |
| |
| class TestHttpServer : public HttpServer, public sigslot::has_slots<> { |
| public: |
| TestHttpServer(Thread* thread, const SocketAddress& addr) |
| : socket_(thread->socketserver()->CreateAsyncSocket(SOCK_STREAM)) { |
| socket_->Bind(addr); |
| socket_->Listen(5); |
| socket_->SignalReadEvent.connect(this, &TestHttpServer::OnAccept); |
| } |
| |
| SocketAddress address() const { return socket_->GetLocalAddress(); } |
| |
| private: |
| void OnAccept(AsyncSocket* socket) { |
| AsyncSocket* new_socket = socket_->Accept(NULL); |
| if (new_socket) { |
| HandleConnection(new SocketStream(new_socket)); |
| } |
| } |
| talk_base::scoped_ptr<AsyncSocket> socket_; |
| }; |
| |
| class AsyncHttpRequestTest : public testing::Test, |
| public sigslot::has_slots<> { |
| public: |
| AsyncHttpRequestTest() |
| : started_(false), |
| done_(false), |
| server_(Thread::Current(), kServerAddr) { |
| server_.SignalHttpRequest.connect(this, &AsyncHttpRequestTest::OnRequest); |
| } |
| |
| bool started() const { return started_; } |
| bool done() const { return done_; } |
| |
| AsyncHttpRequest* CreateGetRequest(const std::string& host, int port, |
| const std::string& path) { |
| talk_base::AsyncHttpRequest* request = |
| new talk_base::AsyncHttpRequest("unittest"); |
| request->SignalWorkDone.connect(this, |
| &AsyncHttpRequestTest::OnRequestDone); |
| request->request().verb = talk_base::HV_GET; |
| request->set_host(host); |
| request->set_port(port); |
| request->request().path = path; |
| request->response().document.reset(new MemoryStream()); |
| return request; |
| } |
| AsyncHttpRequest* CreatePostRequest(const std::string& host, int port, |
| const std::string& path, |
| const std::string content_type, |
| StreamInterface* content) { |
| talk_base::AsyncHttpRequest* request = |
| new talk_base::AsyncHttpRequest("unittest"); |
| request->SignalWorkDone.connect(this, |
| &AsyncHttpRequestTest::OnRequestDone); |
| request->request().verb = talk_base::HV_POST; |
| request->set_host(host); |
| request->set_port(port); |
| request->request().path = path; |
| request->request().setContent(content_type, content); |
| request->response().document.reset(new MemoryStream()); |
| return request; |
| } |
| |
| const TestHttpServer& server() const { return server_; } |
| |
| protected: |
| void OnRequest(HttpServer* server, HttpServerTransaction* t) { |
| started_ = true; |
| |
| if (t->request.path == kServerGetPath) { |
| t->response.set_success("text/plain", new MemoryStream(kServerResponse)); |
| } else if (t->request.path == kServerPostPath) { |
| // reverse the data and reply |
| size_t size; |
| StreamInterface* in = t->request.document.get(); |
| StreamInterface* out = new MemoryStream(); |
| in->GetSize(&size); |
| for (size_t i = 0; i < size; ++i) { |
| char ch; |
| in->SetPosition(size - i - 1); |
| in->Read(&ch, 1, NULL, NULL); |
| out->Write(&ch, 1, NULL, NULL); |
| } |
| out->Rewind(); |
| t->response.set_success("text/plain", out); |
| } else { |
| t->response.set_error(404); |
| } |
| server_.Respond(t); |
| } |
| void OnRequestDone(SignalThread* thread) { |
| done_ = true; |
| } |
| |
| private: |
| bool started_; |
| bool done_; |
| TestHttpServer server_; |
| }; |
| |
| TEST_F(AsyncHttpRequestTest, TestGetSuccess) { |
| AsyncHttpRequest* req = CreateGetRequest( |
| kServerHostnameAddr.hostname(), server().address().port(), |
| kServerGetPath); |
| EXPECT_FALSE(started()); |
| req->Start(); |
| EXPECT_TRUE_WAIT(started(), 100); // Should have started by now. |
| EXPECT_TRUE_WAIT(done(), 5000); |
| std::string response; |
| EXPECT_EQ(200U, req->response().scode); |
| ASSERT_TRUE(req->response().document.get() != NULL); |
| req->response().document->Rewind(); |
| req->response().document->ReadLine(&response); |
| EXPECT_EQ(kServerResponse, response); |
| req->Release(); |
| } |
| |
| TEST_F(AsyncHttpRequestTest, TestGetNotFound) { |
| AsyncHttpRequest* req = CreateGetRequest( |
| kServerHostnameAddr.hostname(), server().address().port(), |
| "/bad"); |
| req->Start(); |
| EXPECT_TRUE_WAIT(done(), 5000); |
| size_t size; |
| EXPECT_EQ(404U, req->response().scode); |
| ASSERT_TRUE(req->response().document.get() != NULL); |
| req->response().document->GetSize(&size); |
| EXPECT_EQ(0U, size); |
| req->Release(); |
| } |
| |
| TEST_F(AsyncHttpRequestTest, TestPostSuccess) { |
| AsyncHttpRequest* req = CreatePostRequest( |
| kServerHostnameAddr.hostname(), server().address().port(), |
| kServerPostPath, "text/plain", new MemoryStream("abcd1234")); |
| req->Start(); |
| EXPECT_TRUE_WAIT(done(), 5000); |
| std::string response; |
| EXPECT_EQ(200U, req->response().scode); |
| ASSERT_TRUE(req->response().document.get() != NULL); |
| req->response().document->Rewind(); |
| req->response().document->ReadLine(&response); |
| EXPECT_EQ("4321dcba", response); |
| req->Release(); |
| } |
| |
| // Ensure that we shut down properly even if work is outstanding. |
| TEST_F(AsyncHttpRequestTest, TestCancel) { |
| AsyncHttpRequest* req = CreateGetRequest( |
| kServerHostnameAddr.hostname(), server().address().port(), |
| kServerGetPath); |
| req->Start(); |
| req->Destroy(true); |
| } |
| |
| TEST_F(AsyncHttpRequestTest, TestGetSuccessDelay) { |
| AsyncHttpRequest* req = CreateGetRequest( |
| kServerHostnameAddr.hostname(), server().address().port(), |
| kServerGetPath); |
| req->set_start_delay(10); // Delay 10ms. |
| req->Start(); |
| Thread::SleepMs(5); |
| EXPECT_FALSE(started()); // Should not have started immediately. |
| EXPECT_TRUE_WAIT(started(), 200); // Should have started by now. |
| EXPECT_TRUE_WAIT(done(), 5000); |
| std::string response; |
| EXPECT_EQ(200U, req->response().scode); |
| ASSERT_TRUE(req->response().document.get() != NULL); |
| req->response().document->Rewind(); |
| req->response().document->ReadLine(&response); |
| EXPECT_EQ(kServerResponse, response); |
| req->Release(); |
| } |
| |
| } // namespace talk_base |