1 /*****************************************************************
2 |
3 |      HTTP Client Test Program 2
4 |
5 |      (c) 2001-2011 Gilles Boccon-Gibod
6 |      Author: Gilles Boccon-Gibod (bok@bok.net)
7 |
8  ****************************************************************/
9 
10 /*----------------------------------------------------------------------
11 |       includes
12 +---------------------------------------------------------------------*/
13 #include "Neptune.h"
14 #include "NptDebug.h"
15 
16 #define LOG_FORMAT "%30s,%3d,%8d, %8d, %8d, [%30s], %s\n"
17 
18 static NPT_HttpClient::Connector* HttpConnector = NULL;
19 static NPT_TlsContext* TlsContext = NULL;
20 
21 /*----------------------------------------------------------------------
22 |       TestHttpGet
23 +---------------------------------------------------------------------*/
24 static void
TestHttpGet(const char * arg,bool use_http_1_1,int verbosity)25 TestHttpGet(const char* arg, bool use_http_1_1, int verbosity)
26 {
27     const char* method = NPT_HTTP_METHOD_GET;
28     if (arg && arg[0] == '@') {
29         method = NPT_HTTP_METHOD_HEAD;
30         ++arg;
31     }
32     NPT_HttpUrl url(arg);
33     NPT_HttpRequest request(url, method);
34     NPT_HttpClient client;
35     NPT_HttpResponse* response;
36 
37     if (!url.IsValid()) return;
38     if (use_http_1_1) request.SetProtocol(NPT_HTTP_PROTOCOL_1_1);
39     if (HttpConnector) client.SetConnector(HttpConnector);
40 
41     NPT_TimeStamp before;
42     NPT_System::GetCurrentTimeStamp(before);
43     NPT_Result result = client.SendRequest(request, response);
44     NPT_TimeStamp after;
45     NPT_System::GetCurrentTimeStamp(after);
46     NPT_UInt64 elapsed = (after-before).ToMillis();
47     if (NPT_FAILED(result)) {
48         if (verbosity >= 1) printf(LOG_FORMAT, NPT_ResultText(result), 0, 0, 0, (int)elapsed, "", arg);
49         return;
50     }
51     int loaded = -1;
52     if (!NPT_StringsEqual(method, NPT_HTTP_METHOD_HEAD)) {
53         NPT_DataBuffer payload;
54         result = response->GetEntity()->Load(payload);
55         if (NPT_SUCCEEDED(result))  {
56             loaded = (int)payload.GetDataSize();
57         }
58     } else {
59         loaded = 0;
60     }
61     const NPT_String* server = response->GetHeaders().GetHeaderValue("Server");
62     if (verbosity >= 1) {
63         NPT_LargeSize entity_size = response->GetEntity()?response->GetEntity()->GetContentLength():0;
64         printf(LOG_FORMAT, "NPT_SUCCESS", response->GetStatusCode(), loaded, (int)entity_size, (int)elapsed, server?server->GetChars():"", arg);
65     }
66 
67     delete response;
68 }
69 
70 /*----------------------------------------------------------------------
71 |       TestHttpPost
72 +---------------------------------------------------------------------*/
73 static void
TestHttpPost(const char * arg,bool use_http_1_1,unsigned int verbosity)74 TestHttpPost(const char* arg, bool use_http_1_1, unsigned int verbosity)
75 {
76     NPT_HttpUrl url(arg);
77     NPT_HttpRequest request(url, NPT_HTTP_METHOD_POST);
78     NPT_HttpClient client;
79     NPT_HttpResponse* response;
80 
81     if (!url.IsValid()) return;
82     if (use_http_1_1) request.SetProtocol(NPT_HTTP_PROTOCOL_1_1);
83 
84     NPT_HttpEntity* entity = new NPT_HttpEntity();
85     entity->SetInputStream("blabla");
86     request.SetEntity(entity);
87     request.GetHeaders().SetHeader("Expect", "100-continue");
88 
89     NPT_TimeStamp before;
90     NPT_System::GetCurrentTimeStamp(before);
91     NPT_Result result = client.SendRequest(request, response);
92     NPT_TimeStamp after;
93     NPT_System::GetCurrentTimeStamp(after);
94     NPT_UInt64 elapsed = (after-before).ToMillis();
95     if (NPT_FAILED(result)) {
96         if (verbosity >= 1) printf(LOG_FORMAT, NPT_ResultText(result), 0, 0, 0, (int)elapsed, "", arg);
97         return;
98     }
99     NPT_DataBuffer payload;
100     result = response->GetEntity()->Load(payload);
101     int loaded = -1;
102     if (NPT_SUCCEEDED(result))  {
103         loaded = (int)payload.GetDataSize();
104     }
105     const NPT_String* server = response->GetHeaders().GetHeaderValue("Server");
106     if (verbosity >= 1) printf(LOG_FORMAT, "NPT_SUCCESS", response->GetStatusCode(), loaded, (int)response->GetEntity()->GetContentLength(), (int)elapsed, server?server->GetChars():"", arg);
107 
108     delete response;
109 }
110 
111 /*----------------------------------------------------------------------
112 |   ClientThread
113 +---------------------------------------------------------------------*/
114 class ClientThread : public NPT_Thread
115 {
116 public:
ClientThread(const char * name,const char * playlist,bool use_http_1_1,unsigned int loops,bool random,bool post,unsigned int sleep,unsigned int verbosity)117     ClientThread(const char*  name,
118                  const char*  playlist,
119                  bool         use_http_1_1,
120                  unsigned int loops,
121                  bool         random,
122                  bool         post,
123                  unsigned int sleep,
124                  unsigned int verbosity) :
125         m_Name(name),
126         m_Playlist(playlist),
127         m_UseHttp_1_1(use_http_1_1),
128         m_Loops(loops),
129         m_Random(random),
130         m_Post(post),
131         m_Sleep(sleep),
132         m_Verbosity(verbosity) {}
133 
134     virtual void Run();
135 
136 private:
137     NPT_String   m_Name;
138     NPT_String   m_Playlist;
139     bool         m_UseHttp_1_1;
140     unsigned int m_Loops;
141     bool         m_Random;
142     bool         m_Post;
143     unsigned int m_Sleep;
144     unsigned int m_Verbosity;
145 };
146 
147 /*----------------------------------------------------------------------
148 |   ClientThread::Run
149 +---------------------------------------------------------------------*/
150 void
Run()151 ClientThread::Run()
152 {
153     NPT_DataBuffer list_buffer;
154     NPT_String list_string;
155     if (NPT_File::Exists(m_Playlist)) {
156         NPT_File::Load(m_Playlist, list_buffer);
157         list_string.Assign((const char*)list_buffer.GetData(), list_buffer.GetDataSize());
158     } else {
159         list_string = m_Playlist;
160     }
161     NPT_List<NPT_String> urls = list_string.Split("\n");
162     if (m_Verbosity >= 2) printf("urls: %d\n", urls.GetItemCount());
163     if (m_Verbosity >= 2) printf("loops: %d, random: %s, sleep: %d ms\n", m_Loops, m_Random?"true":"false", m_Sleep);
164     for (unsigned int i=0; i<m_Loops; i++) {
165         NPT_TimeStamp before;
166         NPT_System::GetCurrentTimeStamp(before);
167         for (unsigned int j=0; j<urls.GetItemCount(); j++) {
168             unsigned int choice = j;
169             if (m_Random) {
170                 choice = NPT_System::GetRandomInteger()%urls.GetItemCount();
171             }
172             if (m_Post) {
173                 TestHttpPost((*urls.GetItem(choice)).GetChars(), m_UseHttp_1_1, m_Verbosity);
174             } else {
175                 TestHttpGet((*urls.GetItem(choice)).GetChars(), m_UseHttp_1_1, m_Verbosity);
176             }
177 
178             if (m_Sleep) {
179                 NPT_System::Sleep(NPT_TimeStamp(((float)m_Sleep)/1000.0f));
180             }
181         }
182         NPT_TimeStamp after;
183         NPT_System::GetCurrentTimeStamp(after);
184         float elapsed = (float)(after-before);
185         if (m_Verbosity >= 1) printf("%s [%04d] TOTAL time elapsed = %d ms\n", m_Name.GetChars(), i, (int)(elapsed*1000.0));
186     }
187 }
188 
189 /*----------------------------------------------------------------------
190 |   main
191 +---------------------------------------------------------------------*/
192 int
main(int argc,char ** argv)193 main(int argc, char** argv)
194 {
195     // parse args
196     --argc; ++argv;
197     bool         use_http_1_1 = false;
198     unsigned int loops        = 1;
199     bool         random       = false;
200     bool         post         = false;
201     unsigned int sleep        = 0;
202     unsigned int threads      = 1;
203     unsigned int verbosity    = 1;
204     while (*argv) {
205         if (NPT_StringsEqual(*argv, "--http-1-1")) {
206             use_http_1_1 = true;
207         } else if (NPT_StringsEqual(*argv, "--loops")) {
208             NPT_ParseInteger(*++argv, loops);
209         } else if (NPT_StringsEqual(*argv, "--post")) {
210             post = true;
211         } else if (NPT_StringsEqual(*argv, "--random")) {
212             random = true;
213         } else if (NPT_StringsEqual(*argv, "--sleep")) {
214             NPT_ParseInteger(*++argv, sleep);
215         } else if (NPT_StringsEqual(*argv, "--verbosity")) {
216             NPT_ParseInteger(*++argv, verbosity);
217         } else if (NPT_StringsEqual(*argv, "--threads")) {
218             NPT_ParseInteger(*++argv, threads);
219 #if defined(NPT_CONFIG_ENABLE_TLS)
220         } else if (NPT_StringsEqual(*argv, "--no-cert-check")) {
221             TlsContext = new NPT_TlsContext(NPT_TlsContext::OPTION_VERIFY_LATER | NPT_TlsContext::OPTION_ADD_DEFAULT_TRUST_ANCHORS);
222             HttpConnector = new NPT_HttpTlsConnector(*TlsContext, NPT_HttpTlsConnector::OPTION_ACCEPT_SELF_SIGNED_CERTS | NPT_HttpTlsConnector::OPTION_ACCEPT_HOSTNAME_MISMATCH);
223 #endif
224         } else {
225             break;
226         }
227         ++argv;
228     }
229     if (*argv == NULL) {
230         fprintf(stderr, "ERROR: missing URL or list filename\n");
231         return 1;
232     }
233 
234     NPT_Array<ClientThread*> cthreads;
235     cthreads.Resize(threads);
236     for (unsigned int i=0; i<threads; i++) {
237         NPT_String name = "THREAD ";
238         name += NPT_String::FromInteger(i);
239         ClientThread* thread = new ClientThread(name, *argv, use_http_1_1, loops, random, post, sleep, verbosity);
240         cthreads[i] = thread;
241         thread->Start();
242     }
243 
244     for (unsigned int i=0; i<threads; i++) {
245         cthreads[i]->Wait();
246         delete cthreads[i];
247     }
248 
249     delete TlsContext;
250     delete HttpConnector;
251 
252     return 0;
253 }
254