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 <netdb.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include <media/stagefright/MediaDebug.h> 35 36 namespace android { 37 38 // static 39 const char *HTTPStream::kStatusKey = ":status:"; 40 41 HTTPStream::HTTPStream() 42 : mState(READY), 43 mSocket(-1) { 44 } 45 46 HTTPStream::~HTTPStream() { 47 disconnect(); 48 } 49 50 status_t HTTPStream::connect(const char *server, int port) { 51 Mutex::Autolock autoLock(mLock); 52 53 status_t err = OK; 54 55 if (mState == CONNECTED) { 56 return ERROR_ALREADY_CONNECTED; 57 } 58 59 struct hostent *ent = gethostbyname(server); 60 if (ent == NULL) { 61 return ERROR_UNKNOWN_HOST; 62 } 63 64 CHECK_EQ(mSocket, -1); 65 mSocket = socket(AF_INET, SOCK_STREAM, 0); 66 67 if (mSocket < 0) { 68 return UNKNOWN_ERROR; 69 } 70 71 setReceiveTimeout(30); // Time out reads after 30 secs by default 72 73 mState = CONNECTING; 74 75 int s = mSocket; 76 77 mLock.unlock(); 78 79 struct sockaddr_in addr; 80 addr.sin_family = AF_INET; 81 addr.sin_port = htons(port); 82 addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; 83 memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); 84 85 int res = ::connect(s, (const struct sockaddr *)&addr, sizeof(addr)); 86 87 mLock.lock(); 88 89 if (mState != CONNECTING) { 90 return UNKNOWN_ERROR; 91 } 92 93 if (res < 0) { 94 close(mSocket); 95 mSocket = -1; 96 97 mState = READY; 98 return UNKNOWN_ERROR; 99 } 100 101 mState = CONNECTED; 102 103 return OK; 104 } 105 106 status_t HTTPStream::disconnect() { 107 Mutex::Autolock autoLock(mLock); 108 109 if (mState != CONNECTED && mState != CONNECTING) { 110 return ERROR_NOT_CONNECTED; 111 } 112 113 CHECK(mSocket >= 0); 114 close(mSocket); 115 mSocket = -1; 116 117 mState = READY; 118 119 return OK; 120 } 121 122 status_t HTTPStream::send(const char *data, size_t size) { 123 if (mState != CONNECTED) { 124 return ERROR_NOT_CONNECTED; 125 } 126 127 while (size > 0) { 128 ssize_t n = ::send(mSocket, data, size, 0); 129 130 if (n < 0) { 131 if (errno == EINTR) { 132 continue; 133 } 134 135 disconnect(); 136 137 return ERROR_IO; 138 } else if (n == 0) { 139 disconnect(); 140 141 return ERROR_CONNECTION_LOST; 142 } 143 144 size -= (size_t)n; 145 data += (size_t)n; 146 } 147 148 return OK; 149 } 150 151 status_t HTTPStream::send(const char *data) { 152 return send(data, strlen(data)); 153 } 154 155 // A certain application spawns a local webserver that sends invalid responses, 156 // specifically it terminates header line with only a newline instead of the 157 // CRLF (carriage-return followed by newline) required by the HTTP specs. 158 // The workaround accepts both behaviours but could potentially break 159 // legitimate responses that use a single newline to "fold" headers, which is 160 // why it's not yet on by default. 161 #define WORKAROUND_FOR_MISSING_CR 1 162 163 status_t HTTPStream::receive_line(char *line, size_t size) { 164 if (mState != CONNECTED) { 165 return ERROR_NOT_CONNECTED; 166 } 167 168 bool saw_CR = false; 169 size_t length = 0; 170 171 for (;;) { 172 char c; 173 ssize_t n = recv(mSocket, &c, 1, 0); 174 if (n < 0) { 175 if (errno == EINTR) { 176 continue; 177 } 178 179 disconnect(); 180 181 return ERROR_IO; 182 } else if (n == 0) { 183 disconnect(); 184 185 return ERROR_CONNECTION_LOST; 186 } 187 188 #if WORKAROUND_FOR_MISSING_CR 189 if (c == '\n') { 190 // We have a complete line. 191 192 line[saw_CR ? length - 1 : length] = '\0'; 193 return OK; 194 } 195 #else 196 if (saw_CR && c == '\n') { 197 // We have a complete line. 198 199 line[length - 1] = '\0'; 200 return OK; 201 } 202 #endif 203 204 saw_CR = (c == '\r'); 205 206 if (length + 1 >= size) { 207 return ERROR_MALFORMED; 208 } 209 line[length++] = c; 210 } 211 } 212 213 status_t HTTPStream::receive_header(int *http_status) { 214 *http_status = -1; 215 mHeaders.clear(); 216 217 char line[2048]; 218 status_t err = receive_line(line, sizeof(line)); 219 if (err != OK) { 220 return err; 221 } 222 223 mHeaders.add(string(kStatusKey), string(line)); 224 225 char *spacePos = strchr(line, ' '); 226 if (spacePos == NULL) { 227 // Malformed response? 228 return UNKNOWN_ERROR; 229 } 230 231 char *status_start = spacePos + 1; 232 char *status_end = status_start; 233 while (isdigit(*status_end)) { 234 ++status_end; 235 } 236 237 if (status_end == status_start) { 238 // Malformed response, status missing? 239 return UNKNOWN_ERROR; 240 } 241 242 memmove(line, status_start, status_end - status_start); 243 line[status_end - status_start] = '\0'; 244 245 long tmp = strtol(line, NULL, 10); 246 if (tmp < 0 || tmp > 999) { 247 return UNKNOWN_ERROR; 248 } 249 250 *http_status = (int)tmp; 251 252 for (;;) { 253 err = receive_line(line, sizeof(line)); 254 if (err != OK) { 255 return err; 256 } 257 258 if (*line == '\0') { 259 // Empty line signals the end of the header. 260 break; 261 } 262 263 // puts(line); 264 265 char *colonPos = strchr(line, ':'); 266 if (colonPos == NULL) { 267 mHeaders.add(string(line), string()); 268 } else { 269 char *end_of_key = colonPos; 270 while (end_of_key > line && isspace(end_of_key[-1])) { 271 --end_of_key; 272 } 273 274 char *start_of_value = colonPos + 1; 275 while (isspace(*start_of_value)) { 276 ++start_of_value; 277 } 278 279 *end_of_key = '\0'; 280 281 mHeaders.add(string(line), string(start_of_value)); 282 } 283 } 284 285 return OK; 286 } 287 288 ssize_t HTTPStream::receive(void *data, size_t size) { 289 size_t total = 0; 290 while (total < size) { 291 ssize_t n = recv(mSocket, (char *)data + total, size - total, 0); 292 293 if (n < 0) { 294 if (errno == EINTR) { 295 continue; 296 } 297 298 LOGE("recv failed, errno = %d (%s)", errno, strerror(errno)); 299 300 disconnect(); 301 return (ssize_t)ERROR_IO; 302 } else if (n == 0) { 303 disconnect(); 304 305 LOGE("recv failed, server is gone, total received: %d bytes", 306 total); 307 308 return total == 0 ? (ssize_t)ERROR_CONNECTION_LOST : total; 309 } 310 311 total += (size_t)n; 312 } 313 314 return (ssize_t)total; 315 } 316 317 bool HTTPStream::find_header_value(const string &key, string *value) const { 318 ssize_t index = mHeaders.indexOfKey(key); 319 if (index < 0) { 320 value->clear(); 321 return false; 322 } 323 324 *value = mHeaders.valueAt(index); 325 326 return true; 327 } 328 329 void HTTPStream::setReceiveTimeout(int seconds) { 330 if (seconds < 0) { 331 // Disable the timeout. 332 seconds = 0; 333 } 334 335 struct timeval tv; 336 tv.tv_usec = 0; 337 tv.tv_sec = seconds; 338 CHECK_EQ(0, setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))); 339 } 340 341 } // namespace android 342 343