00001
00002
00003
00004
00005
00006
00007
00008
00009
00014 #ifdef ENABLE_NETWORK
00015
00016 #include "../../stdafx.h"
00017 #include "../../debug.h"
00018 #include "../../rev.h"
00019 #include "../network_func.h"
00020
00021 #include "tcp_http.h"
00022
00024 static SmallVector<NetworkHTTPSocketHandler *, 1> _http_connections;
00025
00034 NetworkHTTPSocketHandler::NetworkHTTPSocketHandler(SOCKET s,
00035 HTTPCallback *callback, const char *host, const char *url,
00036 const char *data, int depth) :
00037 NetworkSocketHandler(),
00038 recv_pos(0),
00039 recv_length(0),
00040 callback(callback),
00041 data(data),
00042 redirect_depth(depth),
00043 sock(s)
00044 {
00045 size_t bufferSize = strlen(url) + strlen(host) + strlen(_openttd_revision) + (data == NULL ? 0 : strlen(data)) + 128;
00046 char *buffer = AllocaM(char, bufferSize);
00047
00048 DEBUG(net, 7, "[tcp/http] requesting %s%s", host, url);
00049 if (data != NULL) {
00050 seprintf(buffer, buffer + bufferSize - 1, "POST %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s\r\n", url, host, _openttd_revision, (int)strlen(data), data);
00051 } else {
00052 seprintf(buffer, buffer + bufferSize - 1, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\n\r\n", url, host, _openttd_revision);
00053 }
00054
00055 ssize_t size = strlen(buffer);
00056 ssize_t res = send(this->sock, (const char*)buffer, size, 0);
00057 if (res != size) {
00058
00059
00060 this->callback->OnFailure();
00061 delete this;
00062 return;
00063 }
00064
00065 *_http_connections.Append() = this;
00066 }
00067
00069 NetworkHTTPSocketHandler::~NetworkHTTPSocketHandler()
00070 {
00071 this->CloseConnection();
00072
00073 if (this->sock != INVALID_SOCKET) closesocket(this->sock);
00074 this->sock = INVALID_SOCKET;
00075 free((void*)this->data);
00076 }
00077
00078 NetworkRecvStatus NetworkHTTPSocketHandler::CloseConnection(bool error)
00079 {
00080 NetworkSocketHandler::CloseConnection(error);
00081 return NETWORK_RECV_STATUS_OKAY;
00082 }
00083
00088 #define return_error(msg) { DEBUG(net, 0, msg); return -1; }
00089
00090 static const char * const NEWLINE = "\r\n";
00091 static const char * const END_OF_HEADER = "\r\n\r\n";
00092 static const char * const HTTP_1_0 = "HTTP/1.0 ";
00093 static const char * const HTTP_1_1 = "HTTP/1.1 ";
00094 static const char * const CONTENT_LENGTH = "Content-Length: ";
00095 static const char * const LOCATION = "Location: ";
00096
00107 int NetworkHTTPSocketHandler::HandleHeader()
00108 {
00109 assert(strlen(HTTP_1_0) == strlen(HTTP_1_1));
00110 assert(strstr(this->recv_buffer, END_OF_HEADER) != NULL);
00111
00112
00113 if (strncmp(this->recv_buffer, HTTP_1_0, strlen(HTTP_1_0)) != 0 &&
00114 strncmp(this->recv_buffer, HTTP_1_1, strlen(HTTP_1_1)) != 0) {
00115 return_error("[tcp/http] received invalid HTTP reply");
00116 }
00117
00118 char *status = this->recv_buffer + strlen(HTTP_1_0);
00119 if (strncmp(status, "200", 3) == 0) {
00120
00121
00122
00123 char *length = strcasestr(this->recv_buffer, CONTENT_LENGTH);
00124 if (length == NULL) return_error("[tcp/http] missing 'content-length' header");
00125
00126
00127 length += strlen(CONTENT_LENGTH);
00128
00129
00130
00131 char *end_of_line = strstr(length, NEWLINE);
00132
00133
00134 *end_of_line = '\0';
00135 int len = atoi(length);
00136
00137 *end_of_line = '\r';
00138
00139
00140
00141
00142 if (len == 0) return_error("[tcp/http] refusing to download 0 bytes");
00143
00144 DEBUG(net, 7, "[tcp/http] downloading %i bytes", len);
00145 return len;
00146 }
00147
00148 if (strncmp(status, "301", 3) != 0 &&
00149 strncmp(status, "302", 3) != 0 &&
00150 strncmp(status, "303", 3) != 0 &&
00151 strncmp(status, "307", 3) != 0) {
00152
00153
00154
00155
00156 *strstr(status, NEWLINE) = '\0';
00157 DEBUG(net, 0, "[tcp/http] unhandled status reply %s", status);
00158 return -1;
00159 }
00160
00161 if (this->redirect_depth == 5) return_error("[tcp/http] too many redirects, looping redirects?");
00162
00163
00164 char *uri = strcasestr(this->recv_buffer, LOCATION);
00165 if (uri == NULL) return_error("[tcp/http] missing 'location' header for redirect");
00166
00167 uri += strlen(LOCATION);
00168
00169
00170
00171 char *end_of_line = strstr(uri, NEWLINE);
00172 *end_of_line = '\0';
00173
00174 DEBUG(net, 6, "[tcp/http] redirecting to %s", uri);
00175
00176 int ret = NetworkHTTPSocketHandler::Connect(uri, this->callback, this->data, this->redirect_depth + 1);
00177 if (ret != 0) return ret;
00178
00179
00180 this->data = NULL;
00181
00182
00183 *end_of_line = '\r';
00184 return 0;
00185 }
00186
00194 int NetworkHTTPSocketHandler::Connect(char *uri, HTTPCallback *callback, const char *data, int depth)
00195 {
00196 char *hname = strstr(uri, "://");
00197 if (hname == NULL) return_error("[tcp/http] invalid location");
00198
00199 hname += 3;
00200
00201 char *url = strchr(hname, '/');
00202 if (url == NULL) return_error("[tcp/http] invalid location");
00203
00204 *url = '\0';
00205
00206
00207 const char *company = NULL;
00208 const char *port = NULL;
00209 ParseConnectionString(&company, &port, hname);
00210 if (company != NULL) return_error("[tcp/http] invalid hostname");
00211
00212 NetworkAddress address(hname, port == NULL ? 80 : atoi(port));
00213
00214
00215 *url = '/';
00216 new NetworkHTTPContentConnecter(address, callback, url, data, depth);
00217 return 0;
00218 }
00219
00220 #undef return_error
00221
00229 int NetworkHTTPSocketHandler::Receive()
00230 {
00231 for (;;) {
00232 ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0);
00233 if (res == -1) {
00234 int err = GET_LAST_ERROR();
00235 if (err != EWOULDBLOCK) {
00236
00237 if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
00238 return -1;
00239 }
00240
00241 return 1;
00242 }
00243
00244
00245 if (res == 0) {
00246 if (this->recv_length != 0) return -1;
00247
00248 this->callback->OnReceiveData(NULL, 0);
00249 return 0;
00250 }
00251
00252
00253 if (this->recv_length == 0) {
00254 int read = this->recv_pos + res;
00255 int end = min(read, lengthof(this->recv_buffer) - 1);
00256
00257
00258 char prev = this->recv_buffer[end];
00259 this->recv_buffer[end] = '\0';
00260 char *end_of_header = strstr(this->recv_buffer, END_OF_HEADER);
00261 this->recv_buffer[end] = prev;
00262
00263 if (end_of_header == NULL) {
00264 if (read == lengthof(this->recv_buffer)) {
00265 DEBUG(net, 0, "[tcp/http] header too big");
00266 return -1;
00267 }
00268 this->recv_pos = read;
00269 } else {
00270 int ret = this->HandleHeader();
00271 if (ret <= 0) return ret;
00272
00273 this->recv_length = ret;
00274
00275 end_of_header += strlen(END_OF_HEADER);
00276 int len = min(read - (end_of_header - this->recv_buffer), res);
00277 if (len != 0) {
00278 this->callback->OnReceiveData(end_of_header, len);
00279 this->recv_length -= len;
00280 }
00281
00282 this->recv_pos = 0;
00283 }
00284 } else {
00285 res = min(this->recv_length, res);
00286
00287 this->callback->OnReceiveData(this->recv_buffer, res);
00288 this->recv_length -= res;
00289 }
00290 }
00291 }
00292
00296 void NetworkHTTPSocketHandler::HTTPReceive()
00297 {
00298
00299 if (_http_connections.Length() == 0) return;
00300
00301 fd_set read_fd;
00302 struct timeval tv;
00303
00304 FD_ZERO(&read_fd);
00305 for (NetworkHTTPSocketHandler **iter = _http_connections.Begin(); iter < _http_connections.End(); iter++) {
00306 FD_SET((*iter)->sock, &read_fd);
00307 }
00308
00309 tv.tv_sec = tv.tv_usec = 0;
00310 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
00311 int n = select(FD_SETSIZE, &read_fd, NULL, NULL, &tv);
00312 #else
00313 int n = WaitSelect(FD_SETSIZE, &read_fd, NULL, NULL, &tv, NULL);
00314 #endif
00315 if (n == -1) return;
00316
00317 for (NetworkHTTPSocketHandler **iter = _http_connections.Begin(); iter < _http_connections.End(); ) {
00318 NetworkHTTPSocketHandler *cur = *iter;
00319
00320 if (FD_ISSET(cur->sock, &read_fd)) {
00321 int ret = cur->Receive();
00322
00323 if (ret < 0) cur->callback->OnFailure();
00324 if (ret <= 0) {
00325
00326 cur->CloseConnection();
00327 _http_connections.Erase(iter);
00328 delete cur;
00329 continue;
00330 }
00331 }
00332 iter++;
00333 }
00334 }
00335
00336 #endif