1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "HTTPStream" 19#include <utils/Log.h> 20 21#include "include/HTTPStream.h" 22 23#include <sys/socket.h> 24 25#include <arpa/inet.h> 26#include <ctype.h> 27#include <errno.h> 28#include <fcntl.h> 29#include <netdb.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <unistd.h> 34 35#include <media/stagefright/foundation/ADebug.h> 36 37namespace android { 38 39// static 40const char *HTTPStream::kStatusKey = ":status:"; 41 42HTTPStream::HTTPStream() 43 : mState(READY), 44 mSocket(-1) { 45} 46 47HTTPStream::~HTTPStream() { 48 disconnect(); 49} 50 51static bool MakeSocketBlocking(int s, bool blocking) { 52 // Make socket non-blocking. 53 int flags = fcntl(s, F_GETFL, 0); 54 if (flags == -1) { 55 return false; 56 } 57 58 if (blocking) { 59 flags &= ~O_NONBLOCK; 60 } else { 61 flags |= O_NONBLOCK; 62 } 63 64 return fcntl(s, F_SETFL, flags) != -1; 65} 66 67static status_t MyConnect( 68 int s, const struct sockaddr *addr, socklen_t addrlen) { 69 status_t result = UNKNOWN_ERROR; 70 71 MakeSocketBlocking(s, false); 72 73 if (connect(s, addr, addrlen) == 0) { 74 result = OK; 75 } else if (errno != EINPROGRESS) { 76 result = -errno; 77 } else { 78 for (;;) { 79 fd_set rs, ws; 80 FD_ZERO(&rs); 81 FD_ZERO(&ws); 82 FD_SET(s, &rs); 83 FD_SET(s, &ws); 84 85 struct timeval tv; 86 tv.tv_sec = 0; 87 tv.tv_usec = 100000ll; 88 89 int nfds = ::select(s + 1, &rs, &ws, NULL, &tv); 90 91 if (nfds < 0) { 92 if (errno == EINTR) { 93 continue; 94 } 95 96 result = -errno; 97 break; 98 } 99 100 if (FD_ISSET(s, &ws) && !FD_ISSET(s, &rs)) { 101 result = OK; 102 break; 103 } 104 105 if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) { 106 // Get the pending error. 107 int error = 0; 108 socklen_t errorLen = sizeof(error); 109 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) { 110 // Couldn't get the real error, so report why not. 111 result = -errno; 112 } else { 113 result = -error; 114 } 115 break; 116 } 117 118 // Timeout expired. Try again. 119 } 120 } 121 122 MakeSocketBlocking(s, true); 123 124 return result; 125} 126 127// Apparently under our linux closing a socket descriptor from one thread 128// will not unblock a pending send/recv on that socket on another thread. 129static ssize_t MySendReceive( 130 int s, void *data, size_t size, int flags, bool sendData) { 131 ssize_t result = 0; 132 133 if (s < 0) { 134 return -1; 135 } 136 while (size > 0) { 137 fd_set rs, ws, es; 138 FD_ZERO(&rs); 139 FD_ZERO(&ws); 140 FD_ZERO(&es); 141 FD_SET(s, sendData ? &ws : &rs); 142 FD_SET(s, &es); 143 144 struct timeval tv; 145 tv.tv_sec = 0; 146 tv.tv_usec = 100000ll; 147 148 int nfds = ::select( 149 s + 1, 150 sendData ? NULL : &rs, 151 sendData ? &ws : NULL, 152 &es, 153 &tv); 154 155 if (nfds < 0) { 156 if (errno == EINTR) { 157 continue; 158 } 159 160 result = -errno; 161 break; 162 } else if (nfds == 0) { 163 // timeout 164 165 continue; 166 } 167 168 CHECK_EQ(nfds, 1); 169 170 ssize_t nbytes = 171 sendData ? send(s, data, size, flags) : recv(s, data, size, flags); 172 173 if (nbytes < 0) { 174 if (errno == EINTR) { 175 continue; 176 } 177 178 result = -errno; 179 break; 180 } else if (nbytes == 0) { 181 result = 0; 182 break; 183 } 184 185 data = (uint8_t *)data + nbytes; 186 size -= nbytes; 187 188 result = nbytes; 189 break; 190 } 191 192 return result; 193} 194 195static ssize_t MySend(int s, const void *data, size_t size, int flags) { 196 return MySendReceive( 197 s, const_cast<void *>(data), size, flags, true /* sendData */); 198} 199 200static ssize_t MyReceive(int s, void *data, size_t size, int flags) { 201 return MySendReceive(s, data, size, flags, false /* sendData */); 202} 203 204status_t HTTPStream::connect(const char *server, int port) { 205 Mutex::Autolock autoLock(mLock); 206 207 status_t err = OK; 208 209 if (mState == CONNECTED) { 210 return ERROR_ALREADY_CONNECTED; 211 } 212 213 if (port < 0 || port > (int) USHRT_MAX) { 214 return UNKNOWN_ERROR; 215 } 216 217 char service[sizeof("65536")]; 218 sprintf(service, "%d", port); 219 struct addrinfo hints, *ai; 220 memset(&hints, 0, sizeof(hints)); 221 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 222 hints.ai_socktype = SOCK_STREAM; 223 224 int ret = getaddrinfo(server, service, &hints, &ai); 225 if (ret) { 226 return ERROR_UNKNOWN_HOST; 227 } 228 229 CHECK_EQ(mSocket, -1); 230 231 mState = CONNECTING; 232 status_t res = -1; 233 struct addrinfo *tmp; 234 for (tmp = ai; tmp; tmp = tmp->ai_next) { 235 mSocket = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol); 236 if (mSocket < 0) { 237 continue; 238 } 239 240 setReceiveTimeout(30); // Time out reads after 30 secs by default. 241 242 int s = mSocket; 243 244 mLock.unlock(); 245 246 res = MyConnect(s, tmp->ai_addr, tmp->ai_addrlen); 247 248 mLock.lock(); 249 250 if (mState != CONNECTING) { 251 close(s); 252 freeaddrinfo(ai); 253 return UNKNOWN_ERROR; 254 } 255 256 if (res == OK) { 257 break; 258 } 259 260 close(s); 261 } 262 263 freeaddrinfo(ai); 264 265 if (res != OK) { 266 close(mSocket); 267 mSocket = -1; 268 269 mState = READY; 270 return res; 271 } 272 273 mState = CONNECTED; 274 275 return OK; 276} 277 278status_t HTTPStream::disconnect() { 279 Mutex::Autolock autoLock(mLock); 280 281 if (mState != CONNECTED && mState != CONNECTING) { 282 return ERROR_NOT_CONNECTED; 283 } 284 285 CHECK(mSocket >= 0); 286 close(mSocket); 287 mSocket = -1; 288 289 mState = READY; 290 291 return OK; 292} 293 294status_t HTTPStream::send(const char *data, size_t size) { 295 if (mState != CONNECTED) { 296 return ERROR_NOT_CONNECTED; 297 } 298 299 while (size > 0) { 300 ssize_t n = MySend(mSocket, data, size, 0); 301 302 if (n < 0) { 303 disconnect(); 304 305 return n; 306 } else if (n == 0) { 307 disconnect(); 308 309 return ERROR_CONNECTION_LOST; 310 } 311 312 size -= (size_t)n; 313 data += (size_t)n; 314 } 315 316 return OK; 317} 318 319status_t HTTPStream::send(const char *data) { 320 return send(data, strlen(data)); 321} 322 323// A certain application spawns a local webserver that sends invalid responses, 324// specifically it terminates header line with only a newline instead of the 325// CRLF (carriage-return followed by newline) required by the HTTP specs. 326// The workaround accepts both behaviours but could potentially break 327// legitimate responses that use a single newline to "fold" headers, which is 328// why it's not yet on by default. 329#define WORKAROUND_FOR_MISSING_CR 1 330 331status_t HTTPStream::receive_line(char *line, size_t size) { 332 if (mState != CONNECTED) { 333 return ERROR_NOT_CONNECTED; 334 } 335 336 bool saw_CR = false; 337 size_t length = 0; 338 339 for (;;) { 340 char c; 341 ssize_t n = MyReceive(mSocket, &c, 1, 0); 342 if (n < 0) { 343 disconnect(); 344 345 return ERROR_IO; 346 } else if (n == 0) { 347 disconnect(); 348 349 return ERROR_CONNECTION_LOST; 350 } 351 352#if WORKAROUND_FOR_MISSING_CR 353 if (c == '\n') { 354 // We have a complete line. 355 356 line[saw_CR ? length - 1 : length] = '\0'; 357 return OK; 358 } 359#else 360 if (saw_CR && c == '\n') { 361 // We have a complete line. 362 363 line[length - 1] = '\0'; 364 return OK; 365 } 366#endif 367 368 saw_CR = (c == '\r'); 369 370 if (length + 1 >= size) { 371 return ERROR_MALFORMED; 372 } 373 line[length++] = c; 374 } 375} 376 377status_t HTTPStream::receive_header(int *http_status) { 378 *http_status = -1; 379 mHeaders.clear(); 380 381 char line[2048]; 382 status_t err = receive_line(line, sizeof(line)); 383 if (err != OK) { 384 return err; 385 } 386 387 mHeaders.add(string(kStatusKey), string(line)); 388 389 char *spacePos = strchr(line, ' '); 390 if (spacePos == NULL) { 391 // Malformed response? 392 return UNKNOWN_ERROR; 393 } 394 395 char *status_start = spacePos + 1; 396 char *status_end = status_start; 397 while (isdigit(*status_end)) { 398 ++status_end; 399 } 400 401 if (status_end == status_start) { 402 // Malformed response, status missing? 403 return UNKNOWN_ERROR; 404 } 405 406 memmove(line, status_start, status_end - status_start); 407 line[status_end - status_start] = '\0'; 408 409 long tmp = strtol(line, NULL, 10); 410 if (tmp < 0 || tmp > 999) { 411 return UNKNOWN_ERROR; 412 } 413 414 *http_status = (int)tmp; 415 416 for (;;) { 417 err = receive_line(line, sizeof(line)); 418 if (err != OK) { 419 return err; 420 } 421 422 if (*line == '\0') { 423 // Empty line signals the end of the header. 424 break; 425 } 426 427 // puts(line); 428 429 char *colonPos = strchr(line, ':'); 430 if (colonPos == NULL) { 431 mHeaders.add(string(line), string()); 432 } else { 433 char *end_of_key = colonPos; 434 while (end_of_key > line && isspace(end_of_key[-1])) { 435 --end_of_key; 436 } 437 438 char *start_of_value = colonPos + 1; 439 while (isspace(*start_of_value)) { 440 ++start_of_value; 441 } 442 443 *end_of_key = '\0'; 444 445 mHeaders.add(string(line), string(start_of_value)); 446 } 447 } 448 449 return OK; 450} 451 452ssize_t HTTPStream::receive(void *data, size_t size) { 453 size_t total = 0; 454 while (total < size) { 455 ssize_t n = MyReceive(mSocket, (char *)data + total, size - total, 0); 456 457 if (n < 0) { 458 LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n)); 459 460 disconnect(); 461 return (ssize_t)ERROR_IO; 462 } else if (n == 0) { 463 disconnect(); 464 465 LOGE("recv failed, server is gone, total received: %d bytes", 466 total); 467 468 return total == 0 ? (ssize_t)ERROR_CONNECTION_LOST : total; 469 } 470 471 total += (size_t)n; 472 } 473 474 return (ssize_t)total; 475} 476 477bool HTTPStream::find_header_value(const string &key, string *value) const { 478 ssize_t index = mHeaders.indexOfKey(key); 479 if (index < 0) { 480 value->clear(); 481 return false; 482 } 483 484 *value = mHeaders.valueAt(index); 485 486 return true; 487} 488 489void HTTPStream::setReceiveTimeout(int seconds) { 490 if (seconds < 0) { 491 // Disable the timeout. 492 seconds = 0; 493 } 494 495 struct timeval tv; 496 tv.tv_usec = 0; 497 tv.tv_sec = seconds; 498 CHECK_EQ(0, setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))); 499} 500 501} // namespace android 502 503