spdycat: Add -d option to POST data

This commit is contained in:
Tatsuhiro Tsujikawa 2013-01-11 00:15:45 +09:00
parent 28489fd6a8
commit ae8e5b7a95
3 changed files with 94 additions and 11 deletions

View File

@ -25,6 +25,7 @@
#include "spdylay_config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
@ -60,6 +61,10 @@
#include "HtmlParser.h"
#include "util.h"
#ifndef O_BINARY
# define O_BINARY (0)
#endif // O_BINARY
namespace spdylay {
struct Config {
@ -76,6 +81,7 @@ struct Config {
std::string keyfile;
int window_bits;
std::map<std::string,std::string> headers;
std::string datafile;
Config():null_out(false), remote_name(false), verbose(false),
get_assets(false), stat(false), no_tls(false),
spdy_version(-1), timeout(-1), window_bits(-1)
@ -201,13 +207,20 @@ struct Request {
http_parser_url u;
spdylay_gzip *inflater;
HtmlParser *html_parser;
const spdylay_data_provider *data_prd;
int64_t data_length;
int64_t data_offset;
// Recursion level: 0: first entity, 1: entity linked from first entity
int level;
RequestStat stat;
std::string status;
Request(const std::string& uri, const http_parser_url &u, int level = 0)
Request(const std::string& uri, const http_parser_url &u,
const spdylay_data_provider *data_prd, int64_t data_length,
int level = 0)
: uri(uri), u(u),
inflater(0), html_parser(0), level(level)
inflater(0), html_parser(0), data_prd(data_prd),
data_length(data_length),data_offset(0),
level(level)
{}
~Request()
@ -330,13 +343,15 @@ struct SpdySession {
hostport = ss.str();
}
bool add_request(const std::string& uri, const http_parser_url& u,
const spdylay_data_provider *data_prd,
int64_t data_length,
int level = 0)
{
if(path_cache.count(uri)) {
return false;
} else {
path_cache.insert(uri);
reqvec.push_back(new Request(uri, u, level));
reqvec.push_back(new Request(uri, u, data_prd, data_length, level));
return true;
}
}
@ -355,7 +370,8 @@ void submit_request(Spdylay& sc, const std::string& hostport,
{
std::string path = req->make_reqpath();
int r = sc.submit_request(get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA),
hostport, path, headers, 3, req);
hostport, path, headers, 3, req->data_prd,
req->data_length, req);
assert(r == 0);
}
@ -375,7 +391,8 @@ void update_html_parser(SpdySession *spdySession, Request *req,
fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) &&
fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&
porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {
spdySession->add_request(uri, u, req->level+1);
// No POST data for assets
spdySession->add_request(uri, u, 0, 0, req->level+1);
submit_request(*spdySession->sc, spdySession->hostport, config.headers,
spdySession->reqvec.back());
}
@ -717,6 +734,29 @@ int communicate(const std::string& host, uint16_t port,
return ok ? 0 : -1;
}
ssize_t file_read_callback
(spdylay_session *session, int32_t stream_id,
uint8_t *buf, size_t length, int *eof,
spdylay_data_source *source, void *user_data)
{
Request *req = (Request*)spdylay_session_get_stream_user_data
(session, stream_id);
int fd = source->fd;
ssize_t r;
while((r = pread(fd, buf, length, req->data_offset)) == -1 &&
errno == EINTR);
if(r == -1) {
return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
} else {
if(r == 0) {
*eof = 1;
} else {
req->data_offset += r;
}
return r;
}
}
int run(char **uris, int n)
{
spdylay_session_callbacks callbacks;
@ -739,6 +779,25 @@ int run(char **uris, int n)
uint16_t prev_port = 0;
int failures = 0;
SpdySession spdySession;
int data_fd = -1;
spdylay_data_provider data_prd;
struct stat data_stat;
if(!config.datafile.empty()) {
data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY);
if(data_fd == -1) {
std::cerr << "Could not open file " << config.datafile << std::endl;
return 1;
}
if(fstat(data_fd, &data_stat) == -1) {
close(data_fd);
std::cerr << "Could not stat file " << config.datafile << std::endl;
return 1;
}
data_prd.source.fd = data_fd;
data_prd.read_callback = file_read_callback;
}
for(int i = 0; i < n; ++i) {
http_parser_url u;
std::string uri = strip_fragment(uris[i]);
@ -758,7 +817,8 @@ int run(char **uris, int n)
prev_host = get_uri_field(uri.c_str(), u, UF_HOST);
prev_port = port;
}
spdySession.add_request(uri, u);
spdySession.add_request(uri, u, data_fd == -1 ? 0 : &data_prd,
data_stat.st_size);
}
}
if(!spdySession.reqvec.empty()) {
@ -772,7 +832,7 @@ int run(char **uris, int n)
void print_usage(std::ostream& out)
{
out << "Usage: spdycat [-Oansv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n"
out << "Usage: spdycat [-Oadnsv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n"
<< " [--key=<KEY>] [--no-tls] <URI>..."
<< std::endl;
}
@ -807,6 +867,8 @@ void print_help(std::ostream& out)
<< " must be in PEM format.\n"
<< " --no-tls Disable SSL/TLS. Use -2 or -3 to specify\n"
<< " SPDY protocol version to use.\n"
<< " -d, --data=<FILE> Post FILE to server. If - is given, data\n"
<< " will be read from stdin.\n"
<< std::endl;
}
@ -829,10 +891,11 @@ int main(int argc, char **argv)
{"help", no_argument, 0, 'h' },
{"header", required_argument, 0, 'H' },
{"no-tls", no_argument, &flag, 3 },
{"data", required_argument, 0, 'd' },
{0, 0, 0, 0 }
};
int option_index = 0;
int c = getopt_long(argc, argv, "OanhH:v23st:w:", long_options,
int c = getopt_long(argc, argv, "Oad:nhH:v23st:w:", long_options,
&option_index);
if(c == -1) {
break;
@ -906,6 +969,9 @@ int main(int argc, char **argv)
case 's':
config.stat = true;
break;
case 'd':
config.datafile = strcmp("-", optarg) == 0 ? "/dev/stdin" : optarg;
break;
case '?':
exit(EXIT_FAILURE);
case 0:

View File

@ -144,6 +144,8 @@ int Spdylay::submit_request(const std::string& scheme,
const std::string& path,
const std::map<std::string,std::string> &headers,
uint8_t pri,
const spdylay_data_provider *data_prd,
int64_t data_length,
void *stream_user_data)
{
enum eStaticHeaderPosition
@ -158,7 +160,7 @@ int Spdylay::submit_request(const std::string& scheme,
};
const char *static_nv[] = {
":method", "GET",
":method", data_prd ? "POST" : "GET",
":path", path.c_str(),
":version", "HTTP/1.1",
":scheme", scheme.c_str(),
@ -170,6 +172,9 @@ int Spdylay::submit_request(const std::string& scheme,
int hardcoded_entry_count = sizeof(static_nv) / sizeof(*static_nv);
int header_count = headers.size();
int total_entry_count = hardcoded_entry_count + header_count * 2;
if(data_prd) {
++total_entry_count;
}
const char **nv = new const char*[total_entry_count + 1];
@ -180,6 +185,14 @@ int Spdylay::submit_request(const std::string& scheme,
int pos = hardcoded_entry_count;
std::string content_length_str;
if(data_prd) {
std::stringstream ss;
ss << data_length;
content_length_str = ss.str();
nv[pos++] = "content-length";
nv[pos++] = content_length_str.c_str();
}
while( i != end ) {
const char *key = (*i).first.c_str();
const char *value = (*i).second.c_str();
@ -201,7 +214,8 @@ int Spdylay::submit_request(const std::string& scheme,
}
nv[pos] = NULL;
int r = spdylay_submit_request(session_, pri, nv, NULL, stream_user_data);
int r = spdylay_submit_request(session_, pri, nv, data_prd,
stream_user_data);
delete [] nv;

View File

@ -58,7 +58,10 @@ public:
int submit_request(const std::string& scheme,
const std::string& hostport, const std::string& path,
const std::map<std::string,std::string>& headers,
uint8_t pri, void *stream_user_data);
uint8_t pri,
const spdylay_data_provider *data_prd,
int64_t data_length,
void *stream_user_data);
int submit_settings(int flags, spdylay_settings_entry *iv, size_t niv);
bool would_block();
void* user_data();