michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TestCommon.h" michael@0: #include "TestHarness.h" michael@0: #include "nsIUDPSocket.h" michael@0: #include "nsISocketTransportService.h" michael@0: #include "nsISocketTransport.h" michael@0: #include "nsIOutputStream.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsINetAddr.h" michael@0: #include "mozilla/net/DNS.h" michael@0: #include "prerror.h" michael@0: michael@0: #define REQUEST 0x68656c6f michael@0: #define RESPONSE 0x6f6c6568 michael@0: michael@0: #define EXPECT_SUCCESS(rv, ...) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (NS_FAILED(rv)) { \ michael@0: fail(__VA_ARGS__); \ michael@0: return false; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: michael@0: #define EXPECT_FAILURE(rv, ...) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (NS_SUCCEEDED(rv)) { \ michael@0: fail(__VA_ARGS__); \ michael@0: return false; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define REQUIRE_EQUAL(a, b, ...) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (a != b) { \ michael@0: fail(__VA_ARGS__); \ michael@0: return false; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: enum TestPhase { michael@0: TEST_OUTPUT_STREAM, michael@0: TEST_SEND_API, michael@0: TEST_NONE michael@0: }; michael@0: michael@0: static TestPhase phase = TEST_NONE; michael@0: michael@0: static bool CheckMessageContent(nsIUDPMessage *aMessage, uint32_t aExpectedContent) michael@0: { michael@0: nsCString data; michael@0: aMessage->GetData(data); michael@0: michael@0: const char* buffer = data.get(); michael@0: uint32_t len = data.Length(); michael@0: michael@0: FallibleTArray& rawData = aMessage->GetDataAsTArray(); michael@0: uint32_t rawLen = rawData.Length(); michael@0: michael@0: if (len != rawLen) { michael@0: fail("Raw data length(%d) do not matches String data length(%d).", rawLen, len); michael@0: return false; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < len; i++) { michael@0: if (buffer[i] != rawData[i]) { michael@0: fail("Raw data(%s) do not matches String data(%s)", rawData.Elements() ,buffer); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: uint32_t input = 0; michael@0: for (uint32_t i = 0; i < len; i++) { michael@0: input += buffer[i] << (8 * i); michael@0: } michael@0: michael@0: if (len != sizeof(uint32_t) || input != aExpectedContent) michael@0: { michael@0: fail("Request 0x%x received, expected 0x%x", input, aExpectedContent); michael@0: return false; michael@0: } else { michael@0: passed("Request 0x%x received as expected", input); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * UDPClientListener: listens for incomming UDP packets michael@0: */ michael@0: class UDPClientListener : public nsIUDPSocketListener michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIUDPSOCKETLISTENER michael@0: virtual ~UDPClientListener(); michael@0: nsresult mResult; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(UDPClientListener, nsIUDPSocketListener) michael@0: michael@0: UDPClientListener::~UDPClientListener() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UDPClientListener::OnPacketReceived(nsIUDPSocket* socket, nsIUDPMessage* message) michael@0: { michael@0: mResult = NS_OK; michael@0: michael@0: uint16_t port; michael@0: nsCString ip; michael@0: nsCOMPtr fromAddr; michael@0: message->GetFromAddr(getter_AddRefs(fromAddr)); michael@0: fromAddr->GetPort(&port); michael@0: fromAddr->GetAddress(ip); michael@0: passed("Packet received on client from %s:%d", ip.get(), port); michael@0: michael@0: if (TEST_SEND_API == phase && CheckMessageContent(message, REQUEST)) { michael@0: uint32_t count; michael@0: const uint32_t data = RESPONSE; michael@0: printf("*** Attempting to write response 0x%x to server by SendWithAddr...\n", RESPONSE); michael@0: mResult = socket->SendWithAddr(fromAddr, (const uint8_t*)&data, michael@0: sizeof(uint32_t), &count); michael@0: if (mResult == NS_OK && count == sizeof(uint32_t)) { michael@0: passed("Response written"); michael@0: } else { michael@0: fail("Response written"); michael@0: } michael@0: return NS_OK; michael@0: } else if (TEST_OUTPUT_STREAM != phase || !CheckMessageContent(message, RESPONSE)) { michael@0: mResult = NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Notify thread michael@0: QuitPumpingEvents(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UDPClientListener::OnStopListening(nsIUDPSocket*, nsresult) michael@0: { michael@0: QuitPumpingEvents(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * UDPServerListener: listens for incomming UDP packets michael@0: */ michael@0: class UDPServerListener : public nsIUDPSocketListener michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIUDPSOCKETLISTENER michael@0: michael@0: virtual ~UDPServerListener(); michael@0: michael@0: nsresult mResult; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(UDPServerListener, nsIUDPSocketListener) michael@0: michael@0: UDPServerListener::~UDPServerListener() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UDPServerListener::OnPacketReceived(nsIUDPSocket* socket, nsIUDPMessage* message) michael@0: { michael@0: mResult = NS_OK; michael@0: michael@0: uint16_t port; michael@0: nsCString ip; michael@0: nsCOMPtr fromAddr; michael@0: message->GetFromAddr(getter_AddRefs(fromAddr)); michael@0: fromAddr->GetPort(&port); michael@0: fromAddr->GetAddress(ip); michael@0: passed("Packet received on server from %s:%d", ip.get(), port); michael@0: michael@0: if (TEST_OUTPUT_STREAM == phase && CheckMessageContent(message, REQUEST)) michael@0: { michael@0: nsCOMPtr outstream; michael@0: message->GetOutputStream(getter_AddRefs(outstream)); michael@0: michael@0: uint32_t count; michael@0: const uint32_t data = RESPONSE; michael@0: printf("*** Attempting to write response 0x%x to client by OutputStream...\n", RESPONSE); michael@0: mResult = outstream->Write((const char*)&data, sizeof(uint32_t), &count); michael@0: michael@0: if (mResult == NS_OK && count == sizeof(uint32_t)) { michael@0: passed("Response written"); michael@0: } else { michael@0: fail("Response written"); michael@0: } michael@0: return NS_OK; michael@0: } else if (TEST_SEND_API != phase || !CheckMessageContent(message, RESPONSE)) { michael@0: mResult = NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Notify thread michael@0: QuitPumpingEvents(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UDPServerListener::OnStopListening(nsIUDPSocket*, nsresult) michael@0: { michael@0: QuitPumpingEvents(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /**** Main ****/ michael@0: int michael@0: main(int32_t argc, char *argv[]) michael@0: { michael@0: nsresult rv; michael@0: ScopedXPCOM xpcom("UDP ServerSocket"); michael@0: if (xpcom.failed()) michael@0: return -1; michael@0: michael@0: // Create UDPSocket michael@0: nsCOMPtr server, client; michael@0: server = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, -1); michael@0: client = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, -1); michael@0: michael@0: // Create UDPServerListener to process UDP packets michael@0: nsRefPtr serverListener = new UDPServerListener(); michael@0: michael@0: // Bind server socket to 127.0.0.1 michael@0: rv = server->Init(0, true); michael@0: NS_ENSURE_SUCCESS(rv, -1); michael@0: int32_t serverPort; michael@0: server->GetPort(&serverPort); michael@0: server->AsyncListen(serverListener); michael@0: michael@0: // Bind clinet on arbitrary port michael@0: nsRefPtr clientListener = new UDPClientListener(); michael@0: client->Init(0, true); michael@0: client->AsyncListen(clientListener); michael@0: michael@0: // Write data to server michael@0: uint32_t count; michael@0: const uint32_t data = REQUEST; michael@0: michael@0: phase = TEST_OUTPUT_STREAM; michael@0: rv = client->Send(NS_LITERAL_CSTRING("127.0.0.1"), serverPort, (uint8_t*)&data, sizeof(uint32_t), &count); michael@0: NS_ENSURE_SUCCESS(rv, -1); michael@0: REQUIRE_EQUAL(count, sizeof(uint32_t), "Error"); michael@0: passed("Request written by Send"); michael@0: michael@0: // Wait for server michael@0: PumpEvents(); michael@0: NS_ENSURE_SUCCESS(serverListener->mResult, -1); michael@0: michael@0: // Read response from server michael@0: NS_ENSURE_SUCCESS(clientListener->mResult, -1); michael@0: michael@0: mozilla::net::NetAddr clientAddr; michael@0: rv = client->GetAddress(&clientAddr); michael@0: NS_ENSURE_SUCCESS(rv, -1); michael@0: michael@0: phase = TEST_SEND_API; michael@0: rv = server->SendWithAddress(&clientAddr, (uint8_t*)&data, sizeof(uint32_t), &count); michael@0: NS_ENSURE_SUCCESS(rv, -1); michael@0: REQUIRE_EQUAL(count, sizeof(uint32_t), "Error"); michael@0: passed("Request written by SendWithAddress"); michael@0: michael@0: // Wait for server michael@0: PumpEvents(); michael@0: NS_ENSURE_SUCCESS(serverListener->mResult, -1); michael@0: michael@0: // Read response from server michael@0: NS_ENSURE_SUCCESS(clientListener->mResult, -1); michael@0: michael@0: // Close server michael@0: printf("*** Attempting to close server ...\n"); michael@0: server->Close(); michael@0: client->Close(); michael@0: PumpEvents(); michael@0: passed("Server closed"); michael@0: michael@0: return 0; // failure is a non-zero return michael@0: }