2012-12-21 09:06:18 +01:00
|
|
|
/*
|
|
|
|
* \brief HTTP client test
|
|
|
|
* \author Ivan Loskutov
|
2018-07-10 19:09:40 +02:00
|
|
|
* \author Martin Stein
|
2012-12-21 09:06:18 +01:00
|
|
|
* \date 2012-12-21
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 Ksys Labs LLC
|
2017-02-20 13:23:52 +01:00
|
|
|
* Copyright (C) 2012-2017 Genode Labs GmbH
|
2012-12-21 09:06:18 +01:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 13:23:52 +01:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2012-12-21 09:06:18 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Genode includes */
|
2017-05-29 14:52:24 +02:00
|
|
|
#include <base/attached_rom_dataspace.h>
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
#include <base/log.h>
|
2017-03-20 12:04:55 +01:00
|
|
|
#include <libc/component.h>
|
2017-05-29 14:52:24 +02:00
|
|
|
#include <nic/packet_allocator.h>
|
|
|
|
#include <timer_session/connection.h>
|
|
|
|
#include <util/string.h>
|
2017-03-20 12:04:55 +01:00
|
|
|
|
2018-07-10 19:09:40 +02:00
|
|
|
/* Libc includes */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2012-12-21 09:06:18 +01:00
|
|
|
|
2018-07-10 19:09:40 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
2012-12-21 09:06:18 +01:00
|
|
|
|
2018-07-10 19:09:40 +02:00
|
|
|
using namespace Genode;
|
2012-12-21 09:06:18 +01:00
|
|
|
|
2018-07-10 19:09:40 +02:00
|
|
|
void close_socket(Libc::Env &env, int sd)
|
2012-12-21 09:06:18 +01:00
|
|
|
{
|
2018-07-10 19:09:40 +02:00
|
|
|
if (::shutdown(sd, SHUT_RDWR)) {
|
|
|
|
error("failed to shutdown");
|
|
|
|
env.parent().exit(-1);
|
2012-12-21 09:06:18 +01:00
|
|
|
}
|
2018-07-10 19:09:40 +02:00
|
|
|
if (::close(sd)) {
|
|
|
|
error("failed to close");
|
|
|
|
env.parent().exit(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-21 09:06:18 +01:00
|
|
|
|
2018-07-10 19:09:40 +02:00
|
|
|
static void test(Libc::Env &env)
|
|
|
|
{
|
|
|
|
using Ipv4_string = String<16>;
|
|
|
|
enum { NR_OF_REPLIES = 5 };
|
|
|
|
enum { NR_OF_TRIALS = 15 };
|
|
|
|
|
|
|
|
/* read component configuration */
|
|
|
|
Attached_rom_dataspace config_rom { env, "config" };
|
|
|
|
Xml_node config_node { config_rom.xml() };
|
|
|
|
Ipv4_string const srv_ip { config_node.attribute_value("server_ip", Ipv4_string("0.0.0.0")) };
|
|
|
|
uint16_t const srv_port { config_node.attribute_value("server_port", (uint16_t)0) };
|
|
|
|
|
|
|
|
/* construct server socket address */
|
|
|
|
struct sockaddr_in srv_addr;
|
|
|
|
srv_addr.sin_port = htons(srv_port);
|
|
|
|
srv_addr.sin_family = AF_INET;
|
|
|
|
srv_addr.sin_addr.s_addr = inet_addr(srv_ip.string());
|
|
|
|
|
|
|
|
/* try several times to request a reply */
|
|
|
|
for (unsigned trial_cnt = 0, reply_cnt = 0; trial_cnt < NR_OF_TRIALS;
|
|
|
|
trial_cnt++)
|
2018-02-09 17:41:16 +01:00
|
|
|
{
|
2018-07-10 19:09:40 +02:00
|
|
|
/* pause a while between each trial */
|
|
|
|
usleep(1000000);
|
2012-12-21 09:06:18 +01:00
|
|
|
|
2018-07-10 19:09:40 +02:00
|
|
|
/* create socket */
|
|
|
|
int sd = ::socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (sd < 0) {
|
|
|
|
error("failed to create socket");
|
2012-12-21 09:06:18 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-07-10 19:09:40 +02:00
|
|
|
/* connect to server */
|
|
|
|
if (::connect(sd, (struct sockaddr *)&srv_addr, sizeof(srv_addr))) {
|
|
|
|
error("Failed to connect to server");
|
|
|
|
close_socket(env, sd);
|
2012-12-21 09:06:18 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-07-10 19:09:40 +02:00
|
|
|
/* send request */
|
|
|
|
char const *req = "GET / HTTP/1.0\r\nHost: localhost:80\r\n\r\n";
|
|
|
|
size_t const req_sz = Genode::strlen(req);
|
|
|
|
if (::send(sd, req, req_sz, 0) != (int)req_sz) {
|
|
|
|
error("failed to send request");
|
|
|
|
close_socket(env, sd);
|
2012-12-21 09:06:18 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-07-10 19:09:40 +02:00
|
|
|
/* receive reply */
|
|
|
|
enum { REPLY_BUF_SZ = 1024 };
|
|
|
|
char reply_buf[REPLY_BUF_SZ];
|
|
|
|
size_t reply_sz = 0;
|
|
|
|
bool reply_failed = false;
|
|
|
|
char const *reply_end = "</html>";
|
|
|
|
size_t const reply_end_sz = Genode::strlen(reply_end);
|
|
|
|
for (; reply_sz <= REPLY_BUF_SZ; ) {
|
|
|
|
char *rcv_buf = &reply_buf[reply_sz];
|
|
|
|
size_t const rcv_buf_sz = REPLY_BUF_SZ - reply_sz;
|
|
|
|
signed long rcv_sz = ::recv(sd, rcv_buf, rcv_buf_sz, 0);
|
|
|
|
if (rcv_sz < 0) {
|
|
|
|
reply_failed = true;
|
2012-12-21 09:06:18 +01:00
|
|
|
break;
|
2018-07-10 19:09:40 +02:00
|
|
|
}
|
|
|
|
reply_sz += rcv_sz;
|
|
|
|
if (reply_sz >= reply_end_sz) {
|
|
|
|
if (!strcmp(&reply_buf[reply_sz - reply_end_sz], reply_end, reply_end_sz)) {
|
|
|
|
break; }
|
|
|
|
}
|
2012-12-21 09:06:18 +01:00
|
|
|
}
|
2018-07-10 19:09:40 +02:00
|
|
|
/* ignore failed replies */
|
|
|
|
if (reply_failed) {
|
|
|
|
error("failed to receive reply");
|
|
|
|
close_socket(env, sd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* handle reply */
|
|
|
|
reply_buf[reply_sz] = 0;
|
|
|
|
log("Received \"", Cstring(reply_buf), "\"");
|
|
|
|
if (++reply_cnt == NR_OF_REPLIES) {
|
|
|
|
log("Test done");
|
|
|
|
env.parent().exit(0);
|
|
|
|
}
|
|
|
|
/* close socket and retry */
|
|
|
|
close_socket(env, sd);
|
2012-12-21 09:06:18 +01:00
|
|
|
}
|
2018-02-09 17:41:16 +01:00
|
|
|
log("Test failed");
|
2018-04-12 12:05:22 +02:00
|
|
|
env.parent().exit(-1);
|
2012-12-21 09:06:18 +01:00
|
|
|
}
|
2018-07-10 19:09:40 +02:00
|
|
|
|
|
|
|
void Libc::Component::construct(Libc::Env &env) { with_libc([&] () { test(env); }); }
|