1 /// \file
2 /// \brief Contains HTTPConnection, used to communicate with web servers
3 ///
4 /// This file is part of RakNet Copyright 2008 Kevin Jenkins.
5 ///
6 /// Raknet is available under the terms of the GPLv3 license, see /usr/local/share/licenses/raknet-3.9.2_10,1/GPLv3.
7 /// Creative Commons Licensees are subject to the
8 /// license found at
9 /// http://creativecommons.org/licenses/by-nc/2.5/
10 /// Single application licensees are subject to the license found at
11 /// http://www.jenkinssoftware.com/SingleApplicationLicense.html
12 /// Custom license users are subject to the terms therein.
13 /// GPL license users are subject to the GNU General Public
14 /// License as published by the Free
15 /// Software Foundation; either version 2 of the License, or (at your
16 /// option) any later version.
17
18 #include "NativeFeatureIncludes.h"
19 #if _RAKNET_SUPPORT_HTTPConnection==1
20
21 #include "TCPInterface.h"
22 #include "HTTPConnection.h"
23 #include "RakSleep.h"
24 #include "RakString.h"
25 #include "RakAssert.h"
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 using namespace RakNet;
32
HTTPConnection()33 HTTPConnection::HTTPConnection() : connectionState(CS_NONE)
34 {
35 tcp=0;
36 }
37
Init(TCPInterface * _tcp,const char * _host,unsigned short _port)38 void HTTPConnection::Init(TCPInterface* _tcp, const char *_host, unsigned short _port)
39 {
40 tcp=_tcp;
41 host=_host;
42 port=_port;
43 }
44
Post(const char * remote_path,const char * data,const char * _contentType)45 void HTTPConnection::Post(const char *remote_path, const char *data, const char *_contentType)
46 {
47 OutgoingCommand op;
48 op.contentType=_contentType;
49 op.data=data;
50 op.remotePath=remote_path;
51 op.isPost=true;
52 outgoingCommand.Push(op, __FILE__, __LINE__ );
53 //printf("Adding outgoing post\n");
54 }
55
Get(const char * path)56 void HTTPConnection::Get(const char *path)
57 {
58 OutgoingCommand op;
59 op.remotePath=path;
60 op.isPost=false;
61 outgoingCommand.Push(op, __FILE__, __LINE__ );
62 }
63
HasBadResponse(int * code,RakNet::RakString * data)64 bool HTTPConnection::HasBadResponse(int *code, RakNet::RakString *data)
65 {
66 if(badResponses.IsEmpty())
67 return false;
68
69 if (code)
70 *code = badResponses.Peek().code;
71 if (data)
72 *data = badResponses.Pop().data;
73 return true;
74 }
CloseConnection()75 void HTTPConnection::CloseConnection()
76 {
77 connectionState=CS_DISCONNECTING;
78 }
Update(void)79 void HTTPConnection::Update(void)
80 {
81 SystemAddress sa;
82 sa = tcp->HasCompletedConnectionAttempt();
83 while (sa!=UNASSIGNED_SYSTEM_ADDRESS)
84 {
85 // printf("Connected\n");
86 connectionState=CS_CONNECTED;
87 server=sa;
88 sa = tcp->HasCompletedConnectionAttempt();
89 }
90
91 sa = tcp->HasFailedConnectionAttempt();
92 while (sa!=UNASSIGNED_SYSTEM_ADDRESS)
93 {
94 //printf("Failed connected\n");
95 CloseConnection();
96 sa = tcp->HasFailedConnectionAttempt();
97 }
98
99 sa = tcp->HasLostConnection();
100 while (sa!=UNASSIGNED_SYSTEM_ADDRESS)
101 {
102 //printf("Lost connection\n");
103 CloseConnection();
104 sa = tcp->HasLostConnection();
105 }
106
107
108 switch (connectionState)
109 {
110 case CS_NONE:
111 {
112 if (outgoingCommand.IsEmpty())
113 return;
114
115 //printf("Connecting\n");
116 server = tcp->Connect(host, port, false);
117 connectionState = CS_CONNECTING;
118 }
119 break;
120 case CS_DISCONNECTING:
121 {
122 if (tcp->ReceiveHasPackets()==false)
123 {
124 if (incomingData.IsEmpty()==false)
125 {
126 results.Push(incomingData, __FILE__, __LINE__ );
127 }
128 incomingData.Clear();
129 tcp->CloseConnection(server);
130 connectionState=CS_NONE;
131 }
132 }
133 break;
134 case CS_CONNECTING:
135 {
136 }
137 break;
138 case CS_CONNECTED:
139 {
140 //printf("Connected\n");
141 if (outgoingCommand.IsEmpty())
142 {
143 //printf("Closed connection (nothing to do)\n");
144 CloseConnection();
145 return;
146 }
147
148 #if OPEN_SSL_CLIENT_SUPPORT==1
149 tcp->StartSSLClient(server);
150 #endif
151
152 //printf("Sending request\n");
153 currentProcessingCommand = outgoingCommand.Pop();
154 RakString request;
155 if (currentProcessingCommand.isPost)
156 {
157 request.Set("POST %s HTTP/1.0\r\n"
158 "Host: %s:%i\r\n"
159 "Content-Type: %s\r\n"
160 "Content-Length: %u\r\n"
161 "\r\n"
162 "%s",
163 currentProcessingCommand.remotePath.C_String(),
164 host.C_String(),
165 port,
166 currentProcessingCommand.contentType.C_String(),
167 (unsigned) currentProcessingCommand.data.GetLength(),
168 currentProcessingCommand.data.C_String());
169 }
170 else
171 {
172 request.Set("GET %s\r\n", host.C_String());
173 }
174
175 // request.URLEncode();
176 tcp->Send(request.C_String(), (unsigned int) request.GetLength(), server,false);
177 connectionState=CS_PROCESSING;
178 }
179 break;
180 case CS_PROCESSING:
181 {
182 }
183 }
184
185 // if (connectionState==CS_PROCESSING && currentProcessingCommand.data.IsEmpty()==false)
186 // outgoingCommand.PushAtHead(currentProcessingCommand);
187 }
HasRead(void) const188 bool HTTPConnection::HasRead(void) const
189 {
190 return results.IsEmpty()==false;
191 }
Read(void)192 RakString HTTPConnection::Read(void)
193 {
194 if (results.IsEmpty())
195 return RakString();
196
197 RakNet::RakString resultStr = results.Pop();
198 // const char *start_of_body = strstr(resultStr.C_String(), "\r\n\r\n");
199 const char *start_of_body = strpbrk(resultStr.C_String(), "\001\002\003%");
200
201 if(start_of_body)
202 return RakNet::RakString::NonVariadic(start_of_body);
203 else
204 return resultStr;
205 }
GetServerAddress(void) const206 SystemAddress HTTPConnection::GetServerAddress(void) const
207 {
208 return server;
209 }
ProcessTCPPacket(Packet * packet)210 void HTTPConnection::ProcessTCPPacket(Packet *packet)
211 {
212 RakAssert(packet);
213
214 // read all the packets possible
215 if(packet->systemAddress == server)
216 {
217 if(incomingData.GetLength() == 0)
218 {
219 int response_code = atoi((char *)packet->data + strlen("HTTP/1.0 "));
220
221 if(response_code > 299)
222 {
223 badResponses.Push(BadResponse(packet->data, response_code), __FILE__, __LINE__ );
224 //printf("Closed connection (Bad response 2)\n");
225 CloseConnection();
226 return;
227 }
228 }
229
230 RakNet::RakString incomingTemp = RakNet::RakString::NonVariadic((const char*) packet->data);
231 incomingTemp.URLDecode();
232 incomingData += incomingTemp;
233
234 // printf((const char*) packet->data);
235 // printf("\n");
236
237 RakAssert(strlen((char *)packet->data) == packet->length); // otherwise it contains Null bytes
238
239 const char *start_of_body = strstr(incomingData, "\r\n\r\n");
240
241 // besides having the server close the connection, they may
242 // provide a length header and supply that many bytes
243 if(
244 // Why was start_of_body here? Makes the GET command fail
245 // start_of_body &&
246 connectionState == CS_PROCESSING)
247 {
248 /*
249 // The stupid programmer that wrote this originally didn't think that just because the header contains this value doesn't mean you got the whole message
250 if (strstr((const char*) packet->data, "\r\nConnection: close\r\n"))
251 {
252 CloseConnection();
253 }
254 else
255 {
256 */
257 long length_of_headers;
258 if (start_of_body)
259 {
260 length_of_headers = (long)(start_of_body + 4 - incomingData.C_String());
261 const char *length_header = strstr(incomingData, "\r\nLength: ");
262
263 if(length_header)
264 {
265 long length = atol(length_header + 10) + length_of_headers;
266
267 if((long) incomingData.GetLength() >= length)
268 {
269 //printf("Closed connection (Got all data due to length header)\n");
270 CloseConnection();
271 }
272 }
273 }
274 else
275 {
276 // No processing needed
277 }
278
279
280 //}
281 }
282 }
283 }
284
IsBusy(void) const285 bool HTTPConnection::IsBusy(void) const
286 {
287 return connectionState != CS_NONE;
288 }
289
GetState(void) const290 int HTTPConnection::GetState(void) const
291 {
292 return connectionState;
293 }
294
295
~HTTPConnection(void)296 HTTPConnection::~HTTPConnection(void)
297 {
298 if (tcp)
299 tcp->CloseConnection(server);
300 }
301
302
303 #endif // _RAKNET_SUPPORT_*
304