1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 The TestProtocols tests the basic protocols architecture and can
9 be used to test individual protocols as well. If this grows too
10 big then we should split it to individual protocols.
12 -Gagan Saksena 04/29/99
15 #include "TestCommon.h"
29 #include "nsIIOService.h"
30 #include "nsIServiceManager.h"
31 #include "nsIStreamListener.h"
32 #include "nsIInputStream.h"
33 #include "nsIInputStream.h"
35 #include "nsIChannel.h"
36 #include "nsIResumableChannel.h"
38 #include "nsIHttpChannel.h"
39 #include "nsIHttpChannelInternal.h"
40 #include "nsIHttpHeaderVisitor.h"
41 #include "nsIChannelEventSink.h"
42 #include "nsIAsyncVerifyRedirectCallback.h"
43 #include "nsIInterfaceRequestor.h"
44 #include "nsIInterfaceRequestorUtils.h"
45 #include "nsIDNSService.h"
46 #include "nsIAuthPrompt.h"
47 #include "nsIPrefService.h"
48 #include "nsIPrefBranch.h"
49 #include "nsIPropertyBag2.h"
50 #include "nsIWritablePropertyBag2.h"
51 #include "nsITimedChannel.h"
52 #include "nsChannelProperties.h"
53 #include "mozilla/Attributes.h"
54 #include "mozilla/unused.h"
56 #include "nsISimpleEnumerator.h"
57 #include "nsStringAPI.h"
58 #include "nsNetUtil.h"
61 using namespace mozilla
;
63 namespace TestProtocols
{
65 #if defined(PR_LOGGING)
67 // set NSPR_LOG_MODULES=Test:5
69 static PRLogModuleInfo
*gTestLog
= nullptr;
71 #define LOG(args) PR_LOG(gTestLog, PR_LOG_DEBUG, args)
73 static NS_DEFINE_CID(kIOServiceCID
, NS_IOSERVICE_CID
);
75 //static PRTime gElapsedTime; // enable when we time it...
76 static int gKeepRunning
= 0;
77 static bool gVerbose
= false;
78 static bool gAskUserForInput
= false;
79 static bool gResume
= false;
80 static uint64_t gStartAt
= 0;
82 static const char* gEntityID
;
84 //-----------------------------------------------------------------------------
85 // Set proxy preferences for testing
86 //-----------------------------------------------------------------------------
89 SetHttpProxy(const char *proxy
)
91 const char *colon
= strchr(proxy
, ':');
94 NS_WARNING("invalid proxy token; use host:port");
95 return NS_ERROR_UNEXPECTED
;
97 int port
= atoi(colon
+ 1);
100 NS_WARNING("invalid proxy port; must be an integer");
101 return NS_ERROR_UNEXPECTED
;
103 nsAutoCString proxyHost
;
104 proxyHost
= Substring(proxy
, colon
);
106 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
109 prefs
->SetCharPref("network.proxy.http", proxyHost
.get());
110 prefs
->SetIntPref("network.proxy.http_port", port
);
111 prefs
->SetIntPref("network.proxy.type", 1); // manual proxy config
113 LOG(("connecting via proxy=%s:%d\n", proxyHost
.get(), port
));
118 SetPACFile(const char* pacURL
)
120 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
123 prefs
->SetCharPref("network.proxy.autoconfig_url", pacURL
);
124 prefs
->SetIntPref("network.proxy.type", 2); // PAC file
126 LOG(("connecting using PAC file %s\n", pacURL
));
130 //-----------------------------------------------------------------------------
131 // Timing information
132 //-----------------------------------------------------------------------------
134 void PrintTimingInformation(nsITimedChannel
* channel
) {
135 #define PRINT_VALUE(property) \
138 channel->Get##property(&value); \
140 PRExplodedTime exploded; \
141 PR_ExplodeTime(value, PR_LocalTimeParameters, &exploded); \
143 PR_FormatTime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &exploded); \
144 LOG((" " #property ":\t%s (%i usec)", buf, exploded.tm_usec)); \
146 LOG((" " #property ":\t0")); \
149 LOG(("Timing data:"));
150 PRINT_VALUE(ChannelCreationTime
)
151 PRINT_VALUE(AsyncOpenTime
)
152 PRINT_VALUE(DomainLookupStartTime
)
153 PRINT_VALUE(DomainLookupEndTime
)
154 PRINT_VALUE(ConnectStartTime
)
155 PRINT_VALUE(ConnectEndTime
)
156 PRINT_VALUE(RequestStartTime
)
157 PRINT_VALUE(ResponseStartTime
)
158 PRINT_VALUE(ResponseEndTime
)
159 PRINT_VALUE(CacheReadStartTime
)
160 PRINT_VALUE(CacheReadEndTime
)
163 //-----------------------------------------------------------------------------
165 //-----------------------------------------------------------------------------
167 class HeaderVisitor
: public nsIHttpHeaderVisitor
169 virtual ~HeaderVisitor() {}
172 NS_DECL_NSIHTTPHEADERVISITOR
176 NS_IMPL_ISUPPORTS(HeaderVisitor
, nsIHttpHeaderVisitor
)
179 HeaderVisitor::VisitHeader(const nsACString
&header
, const nsACString
&value
)
182 PromiseFlatCString(header
).get(),
183 PromiseFlatCString(value
).get()));
187 //-----------------------------------------------------------------------------
189 //-----------------------------------------------------------------------------
191 class URLLoadInfo
: public nsISupports
193 virtual ~URLLoadInfo();
197 URLLoadInfo(const char* aUrl
);
199 // ISupports interface...
200 NS_DECL_THREADSAFE_ISUPPORTS
202 const char* Name() { return mURLString
.get(); }
206 nsCString mURLString
;
209 URLLoadInfo::URLLoadInfo(const char *aUrl
) : mURLString(aUrl
)
212 mConnectTime
= mTotalTime
= PR_Now();
215 URLLoadInfo::~URLLoadInfo()
220 NS_IMPL_ISUPPORTS0(URLLoadInfo
)
222 //-----------------------------------------------------------------------------
223 // TestChannelEventSink
224 //-----------------------------------------------------------------------------
226 class TestChannelEventSink
: public nsIChannelEventSink
228 virtual ~TestChannelEventSink();
232 NS_DECL_NSICHANNELEVENTSINK
234 TestChannelEventSink();
237 TestChannelEventSink::TestChannelEventSink()
241 TestChannelEventSink::~TestChannelEventSink()
246 NS_IMPL_ISUPPORTS(TestChannelEventSink
, nsIChannelEventSink
)
249 TestChannelEventSink::AsyncOnChannelRedirect(nsIChannel
*channel
,
250 nsIChannel
*newChannel
,
252 nsIAsyncVerifyRedirectCallback
*callback
)
254 LOG(("\n+++ TestChannelEventSink::OnChannelRedirect (with flags %x) +++\n",
256 callback
->OnRedirectVerifyCallback(NS_OK
);
260 //-----------------------------------------------------------------------------
262 //-----------------------------------------------------------------------------
264 class TestAuthPrompt
: public nsIAuthPrompt
266 virtual ~TestAuthPrompt();
270 NS_DECL_NSIAUTHPROMPT
275 NS_IMPL_ISUPPORTS(TestAuthPrompt
, nsIAuthPrompt
)
277 TestAuthPrompt::TestAuthPrompt()
281 TestAuthPrompt::~TestAuthPrompt()
286 TestAuthPrompt::Prompt(const char16_t
*dialogTitle
,
287 const char16_t
*text
,
288 const char16_t
*passwordRealm
,
289 uint32_t savePassword
,
290 const char16_t
*defaultText
,
295 return NS_ERROR_NOT_IMPLEMENTED
;
299 TestAuthPrompt::PromptUsernameAndPassword(const char16_t
*dialogTitle
,
300 const char16_t
*dialogText
,
301 const char16_t
*passwordRealm
,
302 uint32_t savePassword
,
307 NS_ConvertUTF16toUTF8
text(passwordRealm
);
308 printf("* --------------------------------------------------------------------------- *\n");
309 printf("* Authentication Required [%s]\n", text
.get());
310 printf("* --------------------------------------------------------------------------- *\n");
315 printf("Enter username: ");
316 unused
<< fgets(buf
, sizeof(buf
), stdin
);
318 buf
[n
-1] = '\0'; // trim trailing newline
319 *user
= NS_StringCloneData(NS_ConvertUTF8toUTF16(buf
));
322 #if defined(XP_UNIX) && !defined(ANDROID)
323 p
= getpass("Enter password: ");
325 printf("Enter password: ");
326 fgets(buf
, sizeof(buf
), stdin
);
328 buf
[n
-1] = '\0'; // trim trailing newline
331 *pwd
= NS_StringCloneData(NS_ConvertUTF8toUTF16(p
));
334 memset(buf
, 0, sizeof(buf
));
341 TestAuthPrompt::PromptPassword(const char16_t
*dialogTitle
,
342 const char16_t
*text
,
343 const char16_t
*passwordRealm
,
344 uint32_t savePassword
,
349 return NS_ERROR_NOT_IMPLEMENTED
;
352 //-----------------------------------------------------------------------------
354 //-----------------------------------------------------------------------------
356 class InputTestConsumer
: public nsIStreamListener
358 virtual ~InputTestConsumer();
365 NS_DECL_NSIREQUESTOBSERVER
366 NS_DECL_NSISTREAMLISTENER
369 InputTestConsumer::InputTestConsumer()
373 InputTestConsumer::~InputTestConsumer()
377 NS_IMPL_ISUPPORTS(InputTestConsumer
, nsIStreamListener
, nsIRequestObserver
)
380 InputTestConsumer::OnStartRequest(nsIRequest
*request
, nsISupports
* context
)
382 LOG(("InputTestConsumer::OnStartRequest\n"));
384 URLLoadInfo
* info
= (URLLoadInfo
*)context
;
386 info
->mConnectTime
= PR_Now() - info
->mConnectTime
;
389 LOG(("\nStarted loading: %s\n", info
? info
->Name() : "UNKNOWN URL"));
394 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
397 channel
->GetStatus(&status
);
398 LOG(("Channel Status: %08x\n", status
));
399 if (NS_SUCCEEDED(status
)) {
400 LOG(("Channel Info:\n"));
402 channel
->GetName(value
);
403 LOG(("\tName: %s\n", value
.get()));
405 channel
->GetContentType(value
);
406 LOG(("\tContent-Type: %s\n", value
.get()));
408 channel
->GetContentCharset(value
);
409 LOG(("\tContent-Charset: %s\n", value
.get()));
412 if (NS_SUCCEEDED(channel
->GetContentLength(&length
))) {
413 LOG(("\tContent-Length: %lld\n", length
));
415 LOG(("\tContent-Length: Unknown\n"));
419 nsCOMPtr
<nsISupports
> owner
;
420 channel
->GetOwner(getter_AddRefs(owner
));
421 LOG(("\tChannel Owner: %x\n", owner
.get()));
424 nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(request
);
426 nsCOMPtr
<nsIURI
> foo
;
427 props
->GetPropertyAsInterface(NS_LITERAL_STRING("test.foo"),
429 getter_AddRefs(foo
));
433 LOG(("\ttest.foo: %s\n", spec
.get()));
437 nsCOMPtr
<nsIHttpChannelInternal
> httpChannelInt(do_QueryInterface(request
));
438 if (httpChannelInt
) {
439 uint32_t majorVer
, minorVer
;
440 nsresult rv
= httpChannelInt
->GetResponseVersion(&majorVer
, &minorVer
);
441 if (NS_SUCCEEDED(rv
)) {
442 LOG(("HTTP Response version: %u.%u\n", majorVer
, minorVer
));
445 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(request
));
447 HeaderVisitor
*visitor
= new HeaderVisitor();
449 return NS_ERROR_OUT_OF_MEMORY
;
452 LOG(("HTTP request headers:\n"));
453 httpChannel
->VisitRequestHeaders(visitor
);
455 LOG(("HTTP response headers:\n"));
456 httpChannel
->VisitResponseHeaders(visitor
);
461 nsCOMPtr
<nsIResumableChannel
> resChannel
= do_QueryInterface(request
);
463 LOG(("Resumable entity identification:\n"));
464 nsAutoCString entityID
;
465 nsresult rv
= resChannel
->GetEntityID(entityID
);
466 if (NS_SUCCEEDED(rv
)) {
467 LOG(("\t|%s|\n", entityID
.get()));
478 InputTestConsumer::OnDataAvailable(nsIRequest
*request
,
479 nsISupports
* context
,
480 nsIInputStream
*aIStream
,
481 uint64_t aSourceOffset
,
487 URLLoadInfo
* info
= (URLLoadInfo
*)context
;
490 size
= std::min
<uint32_t>(aLength
, sizeof(buf
));
492 rv
= aIStream
->Read(buf
, size
, &amt
);
494 NS_ASSERTION((NS_BASE_STREAM_WOULD_BLOCK
!= rv
),
495 "The stream should never block.");
503 info
->mBytesRead
+= amt
;
512 InputTestConsumer::OnStopRequest(nsIRequest
*request
, nsISupports
* context
,
515 LOG(("InputTestConsumer::OnStopRequest [status=%x]\n", aStatus
));
517 URLLoadInfo
* info
= (URLLoadInfo
*)context
;
521 bool bHTTPURL
= false;
523 info
->mTotalTime
= PR_Now() - info
->mTotalTime
;
525 double readTime
= ((info
->mTotalTime
-info
->mConnectTime
)/1000.0)/1000.0;
527 nsCOMPtr
<nsIHttpChannel
> pHTTPCon(do_QueryInterface(request
));
529 pHTTPCon
->GetResponseStatus(&httpStatus
);
533 LOG(("\nFinished loading: %s Status Code: %x\n", info
->Name(), aStatus
));
535 LOG(("\tHTTP Status: %u\n", httpStatus
));
537 if (NS_ERROR_UNKNOWN_HOST
== aStatus
||
538 NS_ERROR_UNKNOWN_PROXY_HOST
== aStatus
) {
539 LOG(("\tDNS lookup failed.\n"));
541 LOG(("\tTime to connect: %.3f seconds\n", (info
->mConnectTime
/1000.0)/1000.0));
542 LOG(("\tTime to read: %.3f seconds.\n", readTime
));
543 LOG(("\tRead: %lld bytes.\n", info
->mBytesRead
));
544 if (info
->mBytesRead
== int64_t(0)) {
545 } else if (readTime
> 0.0) {
546 LOG(("\tThroughput: %.0f bps.\n", (double)(info
->mBytesRead
*int64_t(8))/readTime
));
548 LOG(("\tThroughput: REAL FAST!!\n"));
551 nsCOMPtr
<nsITimedChannel
> timed(do_QueryInterface(request
));
553 PrintTimingInformation(timed
);
555 LOG(("\nFinished loading: UNKNOWN URL. Status Code: %x\n", aStatus
));
558 if (--gKeepRunning
== 0)
563 //-----------------------------------------------------------------------------
564 // NotificationCallbacks
565 //-----------------------------------------------------------------------------
567 class NotificationCallbacks MOZ_FINAL
: public nsIInterfaceRequestor
{
569 ~NotificationCallbacks() {}
574 NotificationCallbacks() {
577 NS_IMETHOD
GetInterface(const nsIID
& iid
, void* *result
) {
578 nsresult rv
= NS_ERROR_FAILURE
;
580 if (iid
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
581 TestChannelEventSink
*sink
;
583 sink
= new TestChannelEventSink();
585 return NS_ERROR_OUT_OF_MEMORY
;
587 rv
= sink
->QueryInterface(iid
, result
);
591 if (iid
.Equals(NS_GET_IID(nsIAuthPrompt
))) {
592 TestAuthPrompt
*prompt
;
594 prompt
= new TestAuthPrompt();
595 if (prompt
== nullptr)
596 return NS_ERROR_OUT_OF_MEMORY
;
598 rv
= prompt
->QueryInterface(iid
, result
);
605 NS_IMPL_ISUPPORTS(NotificationCallbacks
, nsIInterfaceRequestor
)
607 //-----------------------------------------------------------------------------
609 //-----------------------------------------------------------------------------
611 nsresult
StartLoadingURL(const char* aUrlString
)
615 nsCOMPtr
<nsIIOService
> pService(do_GetService(kIOServiceCID
, &rv
));
617 nsCOMPtr
<nsIURI
> pURL
;
619 rv
= pService
->NewURI(nsDependentCString(aUrlString
), nullptr, nullptr, getter_AddRefs(pURL
));
621 LOG(("ERROR: NewURI failed for %s [rv=%x]\n", aUrlString
));
624 nsCOMPtr
<nsIChannel
> pChannel
;
626 NotificationCallbacks
* callbacks
= new NotificationCallbacks();
628 LOG(("Failed to create a new consumer!"));
629 return NS_ERROR_OUT_OF_MEMORY
;;
631 NS_ADDREF(callbacks
);
633 // Async reading thru the calls of the event sink interface
634 rv
= NS_NewChannel(getter_AddRefs(pChannel
), pURL
, pService
,
635 nullptr, // loadGroup
636 callbacks
); // notificationCallbacks
637 NS_RELEASE(callbacks
);
639 LOG(("ERROR: NS_OpenURI failed for %s [rv=%x]\n", aUrlString
, rv
));
643 nsCOMPtr
<nsITimedChannel
> timed(do_QueryInterface(pChannel
));
645 timed
->SetTimingEnabled(true);
647 nsCOMPtr
<nsIWritablePropertyBag2
> props
= do_QueryInterface(pChannel
);
649 rv
= props
->SetPropertyAsInterface(NS_LITERAL_STRING("test.foo"),
651 if (NS_SUCCEEDED(rv
)) {
652 LOG(("set prop 'test.foo'\n"));
657 You may optionally add/set other headers on this
658 request object. This is done by QI for the specific
661 nsCOMPtr
<nsIHttpChannel
> pHTTPCon(do_QueryInterface(pChannel
));
664 // Setting a sample header.
665 rv
= pHTTPCon
->SetRequestHeader(NS_LITERAL_CSTRING("sample-header"),
666 NS_LITERAL_CSTRING("Sample-Value"),
668 if (NS_FAILED(rv
)) return rv
;
670 InputTestConsumer
* listener
;
672 listener
= new InputTestConsumer
;
673 NS_IF_ADDREF(listener
);
675 NS_ERROR("Failed to create a new stream listener!");
676 return NS_ERROR_OUT_OF_MEMORY
;;
680 info
= new URLLoadInfo(aUrlString
);
683 NS_ERROR("Failed to create a load info!");
684 return NS_ERROR_OUT_OF_MEMORY
;
688 nsCOMPtr
<nsIResumableChannel
> res
= do_QueryInterface(pChannel
);
690 NS_ERROR("Channel is not resumable!");
691 return NS_ERROR_UNEXPECTED
;
696 LOG(("* resuming at %llu bytes, with entity id |%s|\n", gStartAt
, id
.get()));
697 res
->ResumeAt(gStartAt
, id
);
699 rv
= pChannel
->AsyncOpen(listener
, // IStreamListener consumer
702 if (NS_SUCCEEDED(rv
)) {
706 LOG(("ERROR: AsyncOpen failed [rv=%x]\n", rv
));
708 NS_RELEASE(listener
);
716 FindChar(nsCString
& buffer
, char c
)
719 int32_t len
= NS_CStringGetData(buffer
, &b
);
721 for (int32_t offset
= 0; offset
< len
; ++offset
) {
731 StripChar(nsCString
& buffer
, char c
)
734 uint32_t len
= NS_CStringGetData(buffer
, &b
) - 1;
736 for (; len
> 0; --len
) {
739 NS_CStringGetData(buffer
, &b
);
744 nsresult
LoadURLsFromFile(char *aFileName
)
750 nsCString fileBuffer
;
753 fd
= PR_Open(aFileName
, PR_RDONLY
, 777);
755 return NS_ERROR_FAILURE
;
758 // Keep reading the file until EOF (or an error) is reached...
760 len
= PR_Read(fd
, buffer
, sizeof(buffer
));
762 fileBuffer
.Append(buffer
, len
);
763 // Treat each line as a URL...
764 while ((offset
= FindChar(fileBuffer
, '\n')) != -1) {
765 urlString
= StringHead(fileBuffer
, offset
);
766 fileBuffer
.Cut(0, offset
+1);
768 StripChar(urlString
, '\r');
769 if (urlString
.Length()) {
770 LOG(("\t%s\n", urlString
.get()));
771 rv
= StartLoadingURL(urlString
.get());
773 // No need to log an error -- StartLoadingURL already
774 // did that for us, probably.
783 // If anything is left in the fileBuffer, treat it as a URL...
784 StripChar(fileBuffer
, '\r');
785 if (fileBuffer
.Length()) {
786 LOG(("\t%s\n", fileBuffer
.get()));
787 StartLoadingURL(fileBuffer
.get());
795 nsresult
LoadURLFromConsole()
798 printf("Enter URL (\"q\" to start): ");
799 unused
<< scanf("%s", buffer
);
801 gAskUserForInput
= false;
803 StartLoadingURL(buffer
);
809 using namespace TestProtocols
;
812 main(int argc
, char* argv
[])
814 if (test_common_init(&argc
, &argv
) != 0)
817 nsresult rv
= (nsresult
)-1;
819 printf("usage: %s [-verbose] [-file <name>] [-resume <startoffset>"
820 "[-entityid <entityid>]] [-proxy <proxy>] [-pac <pacURL>]"
821 "[-console] <url> <url> ... \n", argv
[0]);
825 #if defined(PR_LOGGING)
826 gTestLog
= PR_NewLogModule("Test");
830 The following code only deals with XPCOM registration stuff. and setting
831 up the event queues. Copied from TestSocketIO.cpp
834 rv
= NS_InitXPCOM2(nullptr, nullptr, nullptr);
835 if (NS_FAILED(rv
)) return -1;
839 LOG(("Trying to load:\n"));
840 for (i
=1; i
<argc
; i
++) {
841 // Turn on verbose printing...
842 if (PL_strcasecmp(argv
[i
], "-verbose") == 0) {
847 // Turn on netlib tracing...
848 if (PL_strcasecmp(argv
[i
], "-file") == 0) {
849 LoadURLsFromFile(argv
[++i
]);
853 if (PL_strcasecmp(argv
[i
], "-console") == 0) {
854 gAskUserForInput
= true;
858 if (PL_strcasecmp(argv
[i
], "-resume") == 0) {
860 PR_sscanf(argv
[++i
], "%llu", &gStartAt
);
864 if (PL_strcasecmp(argv
[i
], "-entityid") == 0) {
865 gEntityID
= argv
[++i
];
869 if (PL_strcasecmp(argv
[i
], "-proxy") == 0) {
870 SetHttpProxy(argv
[++i
]);
874 if (PL_strcasecmp(argv
[i
], "-pac") == 0) {
875 SetPACFile(argv
[++i
]);
879 LOG(("\t%s\n", argv
[i
]));
880 rv
= StartLoadingURL(argv
[i
]);
882 // Enter the message pump to allow the URL load to proceed.
884 } // this scopes the nsCOMPtrs
885 // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
886 NS_ShutdownXPCOM(nullptr);
887 return NS_FAILED(rv
) ? -1 : 0;