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
36namespace android {
37
38// static
39const char *HTTPStream::kStatusKey = ":status:";
40
41HTTPStream::HTTPStream()
42    : mState(READY),
43      mSocket(-1) {
44}
45
46HTTPStream::~HTTPStream() {
47    disconnect();
48}
49
50status_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(5);  // Time out reads after 5 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
106status_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
122status_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
151status_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       0
162
163status_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
213status_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
288ssize_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
317bool 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
329void 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