aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2015-01-23 12:45:51 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2015-01-23 13:08:19 +0200
commit0ed8a2275a3a6cda553b82e9e0222b9d3b8b3ff2 (patch)
tree2b617611ddeb6f97d9f316750496ac13c5ce59ad
parentb1824338b366e25756e4c64f04e535684529832d (diff)
downloadeclat-0ed8a2275a3a6cda553b82e9e0222b9d3b8b3ff2.tar.gz
eclat-0ed8a2275a3a6cda553b82e9e0222b9d3b8b3ff2.tar.bz2
Implement HTTP POST
* NEWS: Update. * doc/eclat.conf.5: Document http-method. Reorganize description of endpoints and regions. * lib/libeclat.h (ec2_request) <postdata>: New member (eclat_request_finalize): New proto. * lib/req2url.c (eclat_request_to_url): Remove second argument. All uses changed. (eclat_request_finalize): New function. * lib/reqfree.c: Free postdata. * lib/reqsign.c (requestsign4): Implement post. * src/config.c: New configuration statement http-method. * src/eclat.c (use_post): New variable. * src/eclat.h (use_post): New extern. * src/util.c (eclat_send_request): Implement post.
-rw-r--r--NEWS9
-rw-r--r--doc/eclat.conf.5113
-rw-r--r--lib/libeclat.h12
-rw-r--r--lib/req2url.c52
-rw-r--r--lib/reqfree.c1
-rw-r--r--lib/reqsign.c4
-rw-r--r--src/config.c33
-rw-r--r--src/eclat.c14
-rw-r--r--src/eclat.h1
-rw-r--r--src/util.c12
10 files changed, 163 insertions, 88 deletions
diff --git a/NEWS b/NEWS
index 208ed04..d498bf7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Eclat NEWS -- history of user-visible changes. 2015-01-22
+Eclat NEWS -- history of user-visible changes. 2015-01-23
Copyright (C) 2012-2015 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -19,6 +19,13 @@ following statement in the eclat configuration file:
signature-version 4;
+* POST support
+
+POST HTTP method is supported. It is enabled by the following
+configuration statement:
+
+ http-method post;
+
* If availability region is not supplied, it is read from the instance store.
* Authentication providers
diff --git a/doc/eclat.conf.5 b/doc/eclat.conf.5
index 4fa7102..32147e8 100644
--- a/doc/eclat.conf.5
+++ b/doc/eclat.conf.5
@@ -13,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with Eclat. If not, see <http://www.gnu.org/licenses/>.
-.TH ECLAT.CONF 5 "January 22, 2015" "ECLAT" "Eclat User Reference"
+.TH ECLAT.CONF 5 "January 23, 2015" "ECLAT" "Eclat User Reference"
.SH NAME
eclat.conf \- configuration file for
.BR eclat (1).
@@ -341,10 +341,23 @@ listed in the corresponding sections below.
.SH EC2 CONFIGURATION
The statements in this group configure EC2 endpoints and regions.
.PP
-An \fBendpoint\fR is a URI of the Amazon server. When selecting the
-endpoint to send the query to
-.B eclat
-uses the following algorithm:
+A \fIregion\fR determines physical location of servers supporting
+given AWS resources.
+.PP
+An \fIendpoint\fR is a URI of the Amazon server. The endpoint to
+use is selected based on the region name.
+.TP
+\fBdefault\-endpoint\fR \fIhostname\fR;
+Defines the endpoint to use if no region-specific endpoint is
+configured.
+.TP
+\fBdefault\-region\fR \fIname\fR;
+Defines the name of the default region.
+.TP
+\fBregion\fR \fIname\fR \fIendpoint\fR;
+Declares a region and the corresponding endpoint.
+.PP
+The region is determined by the following algorithm:
.nr step 1 1
.IP \n[step].
If the \fB\-\-region\fR option is given, take its argument as the
@@ -353,19 +366,17 @@ region name.
If the \fBdefault\-region\fR statement is defined in the configuration
file, use its argument as the region name.
.IP \n+[step].
-If a region name is defined in one of the previous steps, select the
-endpoint defined in the \fBregion\fR statement with the corresponding
-name.
-.IP \n+[step].
-Otherwise, use the endpoint specified by the \fBdefault\-endpoint\fR
-statement.
-.TP
-\fBdefault\-endpoint\fR \fIhostname\fR;
-Defines the endpoint to use in the absence of the \fB\-\-region\fR
-option and \fBdefault\-region\fR statement.
-.TP
-\fBregion\fR \fIname\fR \fIendpoint\fR;
-Declares a region and the corresponding endpoint.
+Otherwise, attempt to obtain region from the \fIinstance store\fR.
+Obviously, this step can succeed only if \fBeclat\fR is run on an
+EC2 instance.
+.PP
+If none of these steps succeed, the program aborts.
+.PP
+Endpoint is selected by looking up a \fBregion\fR statement with
+the \fIname\fR argument matching the currently selected region.
+If such a statement is found, its \fIendpoint\fR argument defines
+the endpoint to use. Otherwise, endpoint is taken from the
+\fBdefault\-endpoint\fR statement.
.PP
An example of the EC2 endpoint configuration follows:
.PP
@@ -378,37 +389,12 @@ region us\-east\-1 ec2.us\-east\-1.amazonaws.com;
# US West (Oregon) Region
region us\-west\-2 ec2.us\-west\-2.amazonaws.com;
.EE
-.SS AUTHENTICATION
+.SS HTTP METHOD
.TP
-\fBauthentication\-provider\fR \fITYPE\fR [\fIARG\fR]\fB;\fR
-Defines authentication provider to use. \fIAuthentication provider\fR
-is a service that supplies AWS access key ID and secret key. See
-.BR eclat (1),
-section
-.BR AUTHENTICATION ,
-for a detailed description.
-
-The \fITYPE\fR argument defines the provider. Allowed values are
-.BR file ,
-and
-.BR instance\-store .
-
-If \fITYPE\fR is \fBfile\fR, the \fIARG\fR parameter is treated as a
-shell globbing pattern: all files matching this pattern are attempted
-in turn, until a keypair is found in one of them.
-
-If \fITYPE\fR is \fBinstance\-store\fR, credentials will be obtained
-from the instance store. \fIARG\fR is optional. If supplied, it
-should be the name of the IAM role this instance is launched with.
-At the time of this writing, an instance can be associated with a
-single role, which will be used by default.
-.TP
-\fBaccess\-file\fR \fIname\fR;
-This is a shortcut for \fBauthentication\-provider file \fIname\fR.
-.TP
-\fBsignature\-version\fR \fIN\fR;
-Declares the signature version. Valid values for \fIN\fR are \fB2\fR,
-which is the default, and \fB4\fR, which provides a better security.
+\fBhttp\-method\fR \fIARG\fR;
+Configures HTTP method. Allowed values for \fIARG\fR are \fBGET\fR
+(the default), and \fBPOST\fR. \fIARG\fR is case-insensitive. The
+\fBPOST\fR method implies using signature version 4.
.SS SSL CONFIGURATION
The \fBssl\fR statement has two forms, and can be used as scalar or as
a block statement. In scalar form it is used to enable SSL:
@@ -448,6 +434,37 @@ By default the CA certificates shipped with
.BR libcurl (3)
will be used. You would rarely need to use \fBca\-file\fR or
\fBca\-path\fR statements.
+.SS AUTHENTICATION
+.TP
+\fBauthentication\-provider\fR \fITYPE\fR [\fIARG\fR]\fB;\fR
+Defines authentication provider to use. \fIAuthentication provider\fR
+is a service that supplies AWS access key ID and secret key. See
+.BR eclat (1),
+section
+.BR AUTHENTICATION ,
+for a detailed description.
+
+The \fITYPE\fR argument defines the provider. Allowed values are
+.BR file ,
+and
+.BR instance\-store .
+
+If \fITYPE\fR is \fBfile\fR, the \fIARG\fR parameter is treated as a
+shell globbing pattern: all files matching this pattern are attempted
+in turn, until a keypair is found in one of them.
+
+If \fITYPE\fR is \fBinstance\-store\fR, credentials will be obtained
+from the instance store. \fIARG\fR is optional. If supplied, it
+should be the name of the IAM role this instance is launched with.
+At the time of this writing, an instance can be associated with a
+single role, which will be used by default.
+.TP
+\fBaccess\-file\fR \fIname\fR;
+This is a shortcut for \fBauthentication\-provider file \fIname\fR.
+.TP
+\fBsignature\-version\fR \fIN\fR;
+Declares the signature version. Valid values for \fIN\fR are \fB2\fR,
+which is the default, and \fB4\fR, which provides a better security.
.SH INSTANCE STORE CONFIGURATION
The \fBinstance\-store\fR compound statement configures HTTP access to
the instance store. By default, \fBeclat\fR uses standard AWS values.
diff --git a/lib/libeclat.h b/lib/libeclat.h
index a5c8c79..b33f0a2 100644
--- a/lib/libeclat.h
+++ b/lib/libeclat.h
@@ -94,6 +94,7 @@ struct ec2_request {
char *uri; /* URI without parameters */
struct grecs_symtab *params; /* Query parameters */
struct grecs_list *headers; /* Query headers */
+ char *postdata;
char *region;
char *access_key;
char *token;
@@ -104,18 +105,19 @@ struct ec2_request *eclat_request_create(int flags, const char *endpoint,
const char *uri, char const *region,
char const *access_key, char const *token);
void eclat_request_free(struct ec2_request *);
-void eclat_request_add_param(struct ec2_request *q, const char *name,
+void eclat_request_add_param(struct ec2_request *req, const char *name,
const char *value);
-void eclat_request_add_param_encoded(struct ec2_request *q, const char *name,
+void eclat_request_add_param_encoded(struct ec2_request *req, const char *name,
const char *value);
-void eclat_request_add_header(struct ec2_request *q, const char *name,
+void eclat_request_add_header(struct ec2_request *req, const char *name,
const char *value);
void eclat_request_sign(struct ec2_request *req, char *secret, char *version);
-char *eclat_request_to_url(struct ec2_request *req, char **post_params);
+char *eclat_request_to_url(struct ec2_request *req);
-void eclat_request_encode(struct ec2_request *q);
+void eclat_request_encode(struct ec2_request *req);
+void eclat_request_finalize(struct ec2_request *req);
typedef struct eclat_partial_tree *eclat_partial_tree_t;
diff --git a/lib/req2url.c b/lib/req2url.c
index 25277a2..b9d7512 100644
--- a/lib/req2url.c
+++ b/lib/req2url.c
@@ -21,7 +21,7 @@
struct param_closure {
struct grecs_txtacc *acc;
- int count;
+ int delim;
};
static int
@@ -30,23 +30,22 @@ add_param(void *sym, void *data)
struct ec2_param *p = sym;
struct param_closure *pc = data;
- if (pc->count)
- grecs_txtacc_grow_char(pc->acc, '&');
- ++pc->count;
+ if (pc->delim)
+ grecs_txtacc_grow_char(pc->acc, pc->delim);
+ pc->delim = '&';
grecs_txtacc_grow_string(pc->acc, p->name);
if (p->value) {
grecs_txtacc_grow_char(pc->acc, '=');
grecs_txtacc_grow_string(pc->acc, p->value);
- }
-
+ }
return 0;
}
char *
-eclat_request_to_url(struct ec2_request *req, char **post_params)
+eclat_request_to_url(struct ec2_request *req)
{
struct grecs_txtacc *acc;
- char *ret = NULL, *p;
+ char *url = NULL;
struct param_closure pc;
acc = grecs_txtacc_create();
@@ -58,27 +57,32 @@ eclat_request_to_url(struct ec2_request *req, char **post_params)
grecs_txtacc_grow(acc, req->endpoint, strlen(req->endpoint));
grecs_txtacc_grow(acc, req->uri, strlen(req->uri));
- if (req->flags & EC2_RF_POST) {
- grecs_txtacc_grow_char(acc, 0);
- ret = grecs_txtacc_finish(acc, 1);
- } else {
- grecs_txtacc_grow_char(acc, '?');
- }
-
- /* Add other parameters */
+ /* Add parameters */
pc.acc = acc;
- pc.count = 0;
+ pc.delim = '?';
grecs_symtab_enumerate(req->params, add_param, &pc);
grecs_txtacc_grow_char(acc, 0);
-
- if (!ret)
- ret = grecs_txtacc_finish(acc, 1);
- else
- *post_params = grecs_txtacc_finish(acc, 1);
-
+ url = grecs_txtacc_finish(acc, 1);
grecs_txtacc_free(acc);
- return ret;
+ return url;
}
+void
+eclat_request_finalize(struct ec2_request *req)
+{
+ struct param_closure pc;
+
+ if (!(req->flags & EC2_RF_POST) || req->postdata)
+ return;
+
+ eclat_request_encode(req);
+ pc.acc = grecs_txtacc_create();
+ pc.delim = 0;
+ grecs_symtab_enumerate(req->params, add_param, &pc);
+ grecs_txtacc_grow_char(pc.acc, 0);
+ req->postdata = grecs_txtacc_finish(pc.acc, 1);
+ grecs_txtacc_free(pc.acc);
+ grecs_symtab_clear(req->params);
+}
diff --git a/lib/reqfree.c b/lib/reqfree.c
index f5145a4..ee0611e 100644
--- a/lib/reqfree.c
+++ b/lib/reqfree.c
@@ -23,6 +23,7 @@ eclat_request_free(struct ec2_request *req)
{
free(req->endpoint);
free(req->uri);
+ free(req->postdata);
grecs_symtab_free(req->params);
free(req);
}
diff --git a/lib/reqsign.c b/lib/reqsign.c
index f3083c6..9572961 100644
--- a/lib/reqsign.c
+++ b/lib/reqsign.c
@@ -252,9 +252,7 @@ requestsign4(struct ec2_request *req, char *secret)
grecs_txtacc_grow_char(acc, '\n');
/* Payload hash */
if (req->flags & EC2_RF_POST) {
- /* FIXME: payload = req->request */
- err("%s:%d: POST is not yet implemented", __FILE__, __LINE__);
- abort();
+ payload = req->postdata;
} else
payload = "";
diff --git a/src/config.c b/src/config.c
index 548b11a..49b47f9 100644
--- a/src/config.c
+++ b/src/config.c
@@ -296,8 +296,8 @@ cb_authentication_provider(enum grecs_callback_command cmd,
}
if (strcmp(type, "file") == 0) {
- if (arg) {
- grecs_error(locus, 0, "requered argument missing");
+ if (!arg) {
+ grecs_error(locus, 0, "required argument missing");
return 1;
}
authentication_provider = authp_file;
@@ -337,6 +337,29 @@ cb_access_file(enum grecs_callback_command cmd,
*(char**)varptr = grecs_strdup(value->v.string);
return 0;
}
+
+static int
+cb_http_method(enum grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ if (cmd != grecs_callback_set_value) {
+ grecs_error(locus, 0, "Unexpected block statement");
+ return 1;
+ }
+ if (!value || value->type != GRECS_TYPE_STRING) {
+ grecs_error(locus, 0, "expected string value");
+ return 1;
+ }
+ if (strcasecmp(value->v.string, "post") == 0)
+ use_post = 1;
+ else if (strcasecmp(value->v.string, "get"))
+ grecs_error(&value->locus, 0, "invalid http method");
+ return 0;
+}
+
static struct grecs_keyword instance_store_kw[] = {
{ "base-url", "URL",
@@ -387,6 +410,9 @@ static struct grecs_keyword eclat_kw[] = {
grecs_type_section, GRECS_DFLT,
NULL, 0,
cb_ssl, NULL, ssl_kw },
+ { "http-method", "arg: post|get",
+ "Define HTTP method to use",
+ grecs_type_string, GRECS_DFLT, NULL, 0, cb_http_method },
{ "format", "<command: string> <format: string>",
"Set default format for <command>",
grecs_type_string, GRECS_MULT, NULL, 0, cb_format },
@@ -465,6 +491,9 @@ config_finish(struct grecs_node *tree)
exit(EX_CONFIG);
if (grecs_error_count || run_config_finish_hooks())
exit(EX_CONFIG);
+
+ if (use_post)
+ signature_version = "4";
}
diff --git a/src/eclat.c b/src/eclat.c
index beaa934..2e46930 100644
--- a/src/eclat.c
+++ b/src/eclat.c
@@ -31,6 +31,7 @@ char *secret_key;
char *security_token;
char *region_name;
int use_ssl;
+int use_post;
int ssl_verify_peer = 1;
char *ssl_ca_file;
char *ssl_ca_path;
@@ -646,10 +647,15 @@ eclat_do_command(eclat_command_env_t *env, struct eclat_command *command,
int rc;
if (!(command->flags & CMD_NOQRY)) {
- env->request = eclat_request_create(use_ssl ? EC2_RF_HTTPS : 0,
- endpoint, "/",
- region_name, access_key,
- security_token);
+ int flags = 0;
+ if (use_ssl)
+ flags |= EC2_RF_HTTPS;
+ if (use_post)
+ flags |= EC2_RF_POST;
+ env->request = eclat_request_create(flags,
+ endpoint, "/",
+ region_name, access_key,
+ security_token);
}
if (command->tag)
diff --git a/src/eclat.h b/src/eclat.h
index a8ac2a0..604ffdc 100644
--- a/src/eclat.h
+++ b/src/eclat.h
@@ -49,6 +49,7 @@ enum authentication_provider {
extern char *endpoint;
extern char *signature_version;
extern int use_ssl;
+extern int use_post;
extern int ssl_verify_peer;
extern char *ssl_ca_file;
extern char *ssl_ca_path;
diff --git a/src/util.c b/src/util.c
index 92532f0..79a78ff 100644
--- a/src/util.c
+++ b/src/util.c
@@ -227,8 +227,15 @@ eclat_send_request(CURL *curl, struct ec2_request *req)
struct curl_slist *headers = NULL;
/* Prepare the request */
+ if (req->flags & EC2_RF_POST) {
+ eclat_request_finalize(req);
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->postdata);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
+ strlen(req->postdata));
+ }
eclat_request_sign(req, secret_key, signature_version);
- url = eclat_request_to_url(req, NULL);
+ url = eclat_request_to_url(req);
curl_easy_setopt(curl, CURLOPT_URL, url);
debug(ECLAT_DEBCAT_MAIN, 1, ("using URL: %s", url));
free(url);
@@ -263,6 +270,9 @@ eclat_send_request(CURL *curl, struct ec2_request *req)
curl_easy_strerror(rc));
}
+ if (req->flags & EC2_RF_POST)
+ debug(ECLAT_DEBCAT_MAIN, 1, ("DATA: %s", req->postdata));
+
if (dry_run_mode)
debug(ECLAT_DEBCAT_MAIN, 1, ("not sending request"));
else {

Return to:

Send suggestions and report system problems to the System administrator.