//==========================================================================
|
//==========================================================================
|
//
|
//
|
// net/http_client.c
|
// net/http_client.c
|
//
|
//
|
// Stand-alone HTTP support for RedBoot
|
// Stand-alone HTTP support for RedBoot
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 2002 Gary Thomas
|
// Copyright (C) 2002 Gary Thomas
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// eCos is free software; you can redistribute it and/or modify it under
|
// the terms of the GNU General Public License as published by the Free
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 or (at your option) any later version.
|
// Software Foundation; either version 2 or (at your option) any later version.
|
//
|
//
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// As a special exception, if other files instantiate templates or use macros
|
// or inline functions from this file, or you compile this file and link it
|
// or inline functions from this file, or you compile this file and link it
|
// with other works to produce a work based on this file, this file does not
|
// with other works to produce a work based on this file, this file does not
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// License. However the source code for this file must still be made available
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// in accordance with section (3) of the GNU General Public License.
|
//
|
//
|
// This exception does not invalidate any other reasons why a work based on
|
// This exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): gthomas
|
// Author(s): gthomas
|
// Contributors: gthomas
|
// Contributors: gthomas
|
// Date: 2002-05-22
|
// Date: 2002-05-22
|
// Purpose:
|
// Purpose:
|
// Description:
|
// Description:
|
//
|
//
|
// This code is part of RedBoot (tm).
|
// This code is part of RedBoot (tm).
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
|
|
// HTTP client support
|
// HTTP client support
|
|
|
#include <redboot.h> // have_net
|
#include <redboot.h> // have_net
|
#include <net/net.h>
|
#include <net/net.h>
|
#include <net/http.h>
|
#include <net/http.h>
|
|
|
// So we remember which ports have been used
|
// So we remember which ports have been used
|
static int get_port = 7800;
|
static int get_port = 7800;
|
|
|
static struct _stream{
|
static struct _stream{
|
bool open;
|
bool open;
|
int avail, actual_len, pos, filelen;
|
int avail, actual_len, pos, filelen;
|
char data[4096];
|
char data[4096];
|
char *bufp;
|
char *bufp;
|
tcp_socket_t sock;
|
tcp_socket_t sock;
|
} http_stream;
|
} http_stream;
|
|
|
static __inline__ int
|
static __inline__ int
|
min(int a, int b)
|
min(int a, int b)
|
{
|
{
|
if (a < b)
|
if (a < b)
|
return a;
|
return a;
|
else
|
else
|
return b;
|
return b;
|
}
|
}
|
|
|
int
|
int
|
http_stream_open(connection_info_t *info, int *err)
|
http_stream_open(connection_info_t *info, int *err)
|
{
|
{
|
int res;
|
int res;
|
struct _stream *s = &http_stream;
|
struct _stream *s = &http_stream;
|
|
|
info->server->sin_port = 80; // HTTP port
|
info->server->sin_port = 80; // HTTP port
|
if ((res = __tcp_open(&s->sock, info->server, get_port++, 5000, err)) < 0) {
|
if ((res = __tcp_open(&s->sock, info->server, get_port++, 5000, err)) < 0) {
|
*err = HTTP_OPEN;
|
*err = HTTP_OPEN;
|
return -1;
|
return -1;
|
}
|
}
|
diag_sprintf(s->data, "GET %s HTTP/1.0\r\n\r\n", info->filename);
|
diag_sprintf(s->data, "GET %s HTTP/1.0\r\n\r\n", info->filename);
|
__tcp_write_block(&s->sock, s->data, strlen(s->data));
|
__tcp_write_block(&s->sock, s->data, strlen(s->data));
|
s->avail = 0;
|
s->avail = 0;
|
s->open = true;
|
s->open = true;
|
s->pos = 0;
|
s->pos = 0;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
void
|
void
|
http_stream_close(int *err)
|
http_stream_close(int *err)
|
{
|
{
|
struct _stream *s = &http_stream;
|
struct _stream *s = &http_stream;
|
|
|
if (s->open) {
|
if (s->open) {
|
__tcp_close(&s->sock);
|
__tcp_close(&s->sock);
|
s->open = false;
|
s->open = false;
|
}
|
}
|
}
|
}
|
|
|
int
|
int
|
http_stream_read(char *buf,
|
http_stream_read(char *buf,
|
int len,
|
int len,
|
int *err)
|
int *err)
|
{
|
{
|
struct _stream *s = &http_stream;
|
struct _stream *s = &http_stream;
|
int total = 0;
|
int total = 0;
|
int cnt, code;
|
int cnt, code;
|
|
|
if (!s->open) {
|
if (!s->open) {
|
return -1; // Shouldn't happen, but...
|
return -1; // Shouldn't happen, but...
|
}
|
}
|
while (len) {
|
while (len) {
|
while (s->avail == 0) {
|
while (s->avail == 0) {
|
// Need to wait for some data to arrive
|
// Need to wait for some data to arrive
|
__tcp_poll();
|
__tcp_poll();
|
if (s->sock.state != _ESTABLISHED) {
|
if (s->sock.state != _ESTABLISHED) {
|
if (s->sock.state == _CLOSE_WAIT) {
|
if (s->sock.state == _CLOSE_WAIT) {
|
// This connection is breaking
|
// This connection is breaking
|
if (s->sock.data_bytes == 0 && s->sock.rxcnt == 0) {
|
if (s->sock.data_bytes == 0 && s->sock.rxcnt == 0) {
|
__tcp_close(&s->sock);
|
__tcp_close(&s->sock);
|
return total;
|
return total;
|
}
|
}
|
} else if (s->sock.state == _CLOSED) {
|
} else if (s->sock.state == _CLOSED) {
|
// The connection is gone
|
// The connection is gone
|
s->open = false;
|
s->open = false;
|
return -1;
|
return -1;
|
} else {
|
} else {
|
*err = HTTP_IO;
|
*err = HTTP_IO;
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
s->actual_len = __tcp_read(&s->sock, s->data, sizeof(s->data));
|
s->actual_len = __tcp_read(&s->sock, s->data, sizeof(s->data));
|
if (s->actual_len > 0) {
|
if (s->actual_len > 0) {
|
s->bufp = s->data;
|
s->bufp = s->data;
|
s->avail = s->actual_len;
|
s->avail = s->actual_len;
|
if (s->pos == 0) {
|
if (s->pos == 0) {
|
// First data - need to scan HTTP response header
|
// First data - need to scan HTTP response header
|
if (strncmp(s->bufp, "HTTP/", 5) == 0) {
|
if (strncmp(s->bufp, "HTTP/", 5) == 0) {
|
// Should look like "HTTP/1.1 200 OK"
|
// Should look like "HTTP/1.1 200 OK"
|
s->bufp += 5;
|
s->bufp += 5;
|
s->avail -= 5;
|
s->avail -= 5;
|
// Find first space
|
// Find first space
|
while ((s->avail > 0) && (*s->bufp != ' ')) {
|
while ((s->avail > 0) && (*s->bufp != ' ')) {
|
s->bufp++;
|
s->bufp++;
|
s->avail--;
|
s->avail--;
|
}
|
}
|
// Now the integer response
|
// Now the integer response
|
code = 0;
|
code = 0;
|
while ((s->avail > 0) && (*s->bufp == ' ')) {
|
while ((s->avail > 0) && (*s->bufp == ' ')) {
|
s->bufp++;
|
s->bufp++;
|
s->avail--;
|
s->avail--;
|
}
|
}
|
while ((s->avail > 0) && isdigit(*s->bufp)) {
|
while ((s->avail > 0) && isdigit(*s->bufp)) {
|
code = (code * 10) + (*s->bufp - '0');
|
code = (code * 10) + (*s->bufp - '0');
|
s->bufp++;
|
s->bufp++;
|
s->avail--;
|
s->avail--;
|
}
|
}
|
// Make sure it says OK
|
// Make sure it says OK
|
while ((s->avail > 0) && (*s->bufp == ' ')) {
|
while ((s->avail > 0) && (*s->bufp == ' ')) {
|
s->bufp++;
|
s->bufp++;
|
s->avail--;
|
s->avail--;
|
}
|
}
|
if (strncmp(s->bufp, "OK", 2)) {
|
if (strncmp(s->bufp, "OK", 2)) {
|
*err = HTTP_BADHDR;
|
*err = HTTP_BADHDR;
|
return -1;
|
return -1;
|
}
|
}
|
// Find \r\n\r\n - end of HTTP preamble
|
// Find \r\n\r\n - end of HTTP preamble
|
while (s->avail > 4) {
|
while (s->avail > 4) {
|
// This could be done faster, but not simpler
|
// This could be done faster, but not simpler
|
if (strncmp(s->bufp, "\r\n\r\n", 4) == 0) {
|
if (strncmp(s->bufp, "\r\n\r\n", 4) == 0) {
|
s->bufp += 4;
|
s->bufp += 4;
|
s->avail -= 4;
|
s->avail -= 4;
|
#if 0 // DEBUG - show header
|
#if 0 // DEBUG - show header
|
*(s->bufp-2) = '\0';
|
*(s->bufp-2) = '\0';
|
diag_printf(s->data);
|
diag_printf(s->data);
|
#endif
|
#endif
|
break;
|
break;
|
}
|
}
|
s->avail--;
|
s->avail--;
|
s->bufp++;
|
s->bufp++;
|
}
|
}
|
s->pos++;
|
s->pos++;
|
} else {
|
} else {
|
// Unrecognized response
|
// Unrecognized response
|
*err = HTTP_BADHDR;
|
*err = HTTP_BADHDR;
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
} else if (s->actual_len < 0) {
|
} else if (s->actual_len < 0) {
|
*err = HTTP_IO;
|
*err = HTTP_IO;
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
cnt = min(len, s->avail);
|
cnt = min(len, s->avail);
|
memcpy(buf, s->bufp, cnt);
|
memcpy(buf, s->bufp, cnt);
|
s->avail -= cnt;
|
s->avail -= cnt;
|
s->bufp += cnt;
|
s->bufp += cnt;
|
buf += cnt;
|
buf += cnt;
|
total += cnt;
|
total += cnt;
|
len -= cnt;
|
len -= cnt;
|
}
|
}
|
return total;
|
return total;
|
}
|
}
|
|
|
char *
|
char *
|
http_error(int err)
|
http_error(int err)
|
{
|
{
|
char *errmsg = "Unknown error";
|
char *errmsg = "Unknown error";
|
|
|
switch (err) {
|
switch (err) {
|
case HTTP_NOERR:
|
case HTTP_NOERR:
|
return "";
|
return "";
|
case HTTP_BADHDR:
|
case HTTP_BADHDR:
|
return "Unrecognized HTTP response";
|
return "Unrecognized HTTP response";
|
case HTTP_OPEN:
|
case HTTP_OPEN:
|
return "Can't connect to host";
|
return "Can't connect to host";
|
case HTTP_IO:
|
case HTTP_IO:
|
return "I/O error";
|
return "I/O error";
|
}
|
}
|
return errmsg;
|
return errmsg;
|
}
|
}
|
|
|
//
|
//
|
// RedBoot interface
|
// RedBoot interface
|
//
|
//
|
GETC_IO_FUNCS(http_io, http_stream_open, http_stream_close,
|
GETC_IO_FUNCS(http_io, http_stream_open, http_stream_close,
|
0, http_stream_read, http_error);
|
0, http_stream_read, http_error);
|
RedBoot_load(http, http_io, true, true, 0);
|
RedBoot_load(http, http_io, true, true, 0);
|
|
|