1 /*
2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Class implementing the interface to the "testman" Web Service
5 * COPYRIGHT: Copyright 2009-2020 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 static const WCHAR szHostname[] = L"reactos.org";
11 static const INTERNET_PORT ServerPort = 8443;
12 static const WCHAR szServerFile[] = L"testman/webservice/";
13
14 /**
15 * Constructs a CWebService object and immediately establishes a connection to the "testman" Web Service.
16 */
CWebService()17 CWebService::CWebService()
18 {
19 /* Zero-initialize variables */
20 m_hHTTP = NULL;
21 m_hHTTPRequest = NULL;
22 m_TestID = NULL;
23
24 /* Establish an internet connection to the "testman" server */
25 m_hInet = InternetOpenW(L"rosautotest", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
26
27 if(!m_hInet)
28 FATAL("InternetOpenW failed\n");
29
30 m_hHTTP = InternetConnectW(m_hInet, szHostname, ServerPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
31
32 if(!m_hHTTP)
33 FATAL("InternetConnectW failed\n");
34 }
35
36 /**
37 * Destructs a CWebService object and closes all connections to the Web Service.
38 */
~CWebService()39 CWebService::~CWebService()
40 {
41 if(m_hInet)
42 InternetCloseHandle(m_hInet);
43
44 if(m_hHTTP)
45 InternetCloseHandle(m_hHTTP);
46
47 if(m_hHTTPRequest)
48 InternetCloseHandle(m_hHTTPRequest);
49
50 if(m_TestID)
51 delete m_TestID;
52 }
53
54 /**
55 * Sends data to the Web Service.
56 *
57 * @param InputData
58 * A std::string containing all the data, which is going to be submitted as HTTP POST data.
59 *
60 * @return
61 * Returns a pointer to a char array containing the data received from the Web Service.
62 * The caller needs to free that pointer.
63 */
64 PCHAR
DoRequest(const string & InputData)65 CWebService::DoRequest(const string& InputData)
66 {
67 const WCHAR szHeaders[] = L"Content-Type: application/x-www-form-urlencoded";
68
69 auto_array_ptr<char> Data;
70 DWORD DataLength;
71
72 /* Post our test results to the web service */
73 m_hHTTPRequest = HttpOpenRequestW(m_hHTTP, L"POST", szServerFile, NULL, NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
74
75 if(!m_hHTTPRequest)
76 FATAL("HttpOpenRequestW failed\n");
77
78 Data.reset(new char[InputData.size() + 1]);
79 strcpy(Data, InputData.c_str());
80
81 if(!HttpSendRequestW(m_hHTTPRequest, szHeaders, lstrlenW(szHeaders), Data, (DWORD)InputData.size()))
82 FATAL("HttpSendRequestW failed\n");
83
84 /* Get the response */
85 if(!InternetQueryDataAvailable(m_hHTTPRequest, &DataLength, 0, 0))
86 FATAL("InternetQueryDataAvailable failed\n");
87
88 Data.reset(new char[DataLength + 1]);
89
90 if(!InternetReadFile(m_hHTTPRequest, Data, DataLength, &DataLength))
91 FATAL("InternetReadFile failed\n");
92
93 Data[DataLength] = 0;
94
95 return Data.release();
96 }
97
98 /**
99 * Interface to other classes for finishing this test run
100 *
101 * @param TestType
102 * Constant pointer to a char array containing the test type to be run (i.e. "wine")
103 */
104 void
Finish(const char * TestType)105 CWebService::Finish(const char* TestType)
106 {
107 auto_array_ptr<char> Response;
108 string Data;
109 stringstream ss;
110
111 if (!m_TestID)
112 EXCEPTION("CWebService::Finish was called, but not a single result had been submitted!\n");
113
114 Data = "action=finish";
115 Data += Configuration.GetAuthenticationRequestString();
116 Data += "&testtype=";
117 Data += TestType;
118 Data += "&testid=";
119 Data += m_TestID;
120
121 Response.reset(DoRequest(Data));
122
123 if (strcmp(Response, "OK"))
124 {
125 ss << "When finishing the test run, the server responded:" << endl << Response << endl;
126 SSEXCEPTION;
127 }
128 }
129
130 /**
131 * Requests a Test ID from the Web Service for our test run.
132 *
133 * @param TestType
134 * Constant pointer to a char array containing the test type to be run (i.e. "wine")
135 */
136 void
GetTestID(const char * TestType)137 CWebService::GetTestID(const char* TestType)
138 {
139 string Data;
140
141 Data = "action=gettestid";
142 Data += Configuration.GetAuthenticationRequestString();
143 Data += Configuration.GetSystemInfoRequestString();
144 Data += "&testtype=";
145 Data += TestType;
146
147 if(!Configuration.GetComment().empty())
148 {
149 Data += "&comment=";
150 Data += Configuration.GetComment();
151 }
152
153 m_TestID = DoRequest(Data);
154
155 /* Verify that this is really a number */
156 if(!IsNumber(m_TestID))
157 {
158 stringstream ss;
159
160 ss << "Expected Test ID, but received:" << endl << m_TestID << endl;
161 SSEXCEPTION;
162 }
163 }
164
165 /**
166 * Gets a Suite ID from the Web Service for this module/test combination.
167 *
168 * @param TestType
169 * Constant pointer to a char array containing the test type to be run (i.e. "wine")
170 *
171 * @param TestInfo
172 * Pointer to a CTestInfo object containing information about the test
173 *
174 * @return
175 * Returns a pointer to a char array containing the Suite ID received from the Web Service.
176 * The caller needs to free that pointer.
177 */
178 PCHAR
GetSuiteID(const char * TestType,CTestInfo * TestInfo)179 CWebService::GetSuiteID(const char* TestType, CTestInfo* TestInfo)
180 {
181 auto_array_ptr<char> SuiteID;
182 string Data;
183
184 Data = "action=getsuiteid";
185 Data += Configuration.GetAuthenticationRequestString();
186 Data += "&testtype=";
187 Data += TestType;
188 Data += "&module=";
189 Data += TestInfo->Module;
190 Data += "&test=";
191 Data += TestInfo->Test;
192
193 SuiteID.reset(DoRequest(Data));
194
195 /* Verify that this is really a number */
196 if(!IsNumber(SuiteID))
197 {
198 stringstream ss;
199
200 ss << "Expected Suite ID, but received:" << endl << SuiteID << endl;
201 SSEXCEPTION;
202 }
203
204 return SuiteID.release();
205 }
206
207 /**
208 * Interface to other classes for submitting a result of one test
209 *
210 * @param TestType
211 * Constant pointer to a char array containing the test type to be run (i.e. "wine")
212 *
213 * @param TestInfo
214 * Pointer to a CTestInfo object containing information about the test
215 */
216 void
Submit(const char * TestType,CTestInfo * TestInfo)217 CWebService::Submit(const char* TestType, CTestInfo* TestInfo)
218 {
219 auto_array_ptr<char> Response;
220 auto_array_ptr<char> SuiteID;
221 string Data;
222 stringstream ss;
223
224 if(!m_TestID)
225 GetTestID(TestType);
226
227 SuiteID.reset(GetSuiteID(TestType, TestInfo));
228
229 Data = "action=submit";
230 Data += Configuration.GetAuthenticationRequestString();
231 Data += "&testtype=";
232 Data += TestType;
233 Data += "&testid=";
234 Data += m_TestID;
235 Data += "&suiteid=";
236 Data += SuiteID;
237 Data += "&log=";
238 Data += EscapeString(TestInfo->Log);
239
240 Response.reset(DoRequest(Data));
241
242 if (strcmp(Response, "OK"))
243 {
244 ss << "When submitting the result, the server responded:" << endl << Response << endl;
245 SSEXCEPTION;
246 }
247 }
248