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