Statistics
| Branch: | Tag: | Revision:

mongoose / examples / restful_server_s3 / restful_server_s3.c @ eaef5bd1

History | View | Annotate | Download (5.72 KB)

1
/*
2
 * Copyright (c) 2014 Cesanta Software Limited
3
 * All rights reserved
4
 */
5

    
6
#include "mongoose.h"
7

    
8
static const char *s_http_port = "8000";
9
static const char *s_access_key_id = NULL;
10
static const char *s_secret_access_key = NULL;
11
static struct mg_serve_http_opts s_http_server_opts;
12

    
13
static void send_error_result(struct mg_connection *nc, const char *msg) {
14
  mg_printf_http_chunk(nc, "Error: %s", msg);
15
  mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */
16
}
17

    
18
static void link_conns(struct mg_connection *nc1, struct mg_connection *nc2) {
19
  nc1->user_data = nc2;
20
  nc2->user_data = nc1;
21
}
22

    
23
static void unlink_conns(struct mg_connection *nc1) {
24
  struct mg_connection *nc2 = (struct mg_connection *) nc1->user_data;
25
  if (nc1->user_data != NULL) {
26
    nc1->user_data = NULL;
27
    nc2->user_data = NULL;
28
  }
29
}
30

    
31
/* S3 client handler */
32
static void s3_handler(struct mg_connection *nc, int ev, void *ev_data) {
33
  struct http_message *hm = (struct http_message *) ev_data;
34
  struct mg_connection *nc2 = (struct mg_connection *) nc->user_data;
35

    
36
  switch (ev) {
37
    case MG_EV_HTTP_REPLY:
38
      if (nc2 != NULL) {
39
        mg_printf_http_chunk(nc2, "Error: %.*s", (int) hm->message.len,
40
                             hm->message.p);
41
        mg_send_http_chunk(nc2, "", 0);
42
      }
43
      unlink_conns(nc);
44
      nc->flags |= MG_F_SEND_AND_CLOSE;
45
      break;
46
    case MG_EV_CLOSE:
47
      unlink_conns(nc);
48
      break;
49
    default:
50
      break;
51
  }
52
}
53

    
54
static void send_s3_request(struct mg_connection *nc, const char *file_name,
55
                            const char *file_data, const char *host,
56
                            const char *bucket) {
57
  char host_port[100];
58
  struct mg_connection *s3_conn;
59

    
60
  snprintf(host_port, sizeof(host_port), "%s:80", host);
61
  s3_conn = mg_connect(nc->mgr, host_port, s3_handler);
62

    
63
  if (s3_conn == NULL) {
64
    send_error_result(nc, "s3 connection failed");
65
  } else {
66
    const char *content_type = "text/plain", *method = "PUT";
67
    char date[100], to_sign[500], signature[100], sha1[20], req[1000];
68
    time_t now = time(NULL);
69

    
70
    link_conns(nc, s3_conn);
71
    strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
72
    mg_set_protocol_http_websocket(s3_conn);
73

    
74
    /* Prepare S3 authorization header */
75
    snprintf(to_sign, sizeof(to_sign), "%s\n\n%s\n%s\n/%s/%s", method,
76
             content_type, date, bucket, file_name);
77
    cs_hmac_sha1((unsigned char *) s_secret_access_key,
78
                 strlen(s_secret_access_key), (unsigned char *) to_sign,
79
                 strlen(to_sign), (unsigned char *) sha1);
80
    mg_base64_encode((unsigned char *) sha1, sizeof(sha1), signature);
81
    snprintf(req, sizeof(req),
82
             "%s /%s HTTP/1.1\r\n"
83
             "Host: %s.%s\r\n"
84
             "Date: %s\r\n"
85
             "Content-Type: %s\r\n"
86
             "Content-Length: %lu\r\n"
87
             "Authorization: AWS %s:%s\r\n"
88
             "\r\n",
89
             method, file_name, bucket, host, date, content_type,
90
             (unsigned long) strlen(file_data), s_access_key_id, signature);
91
    mg_printf(s3_conn, "%s%s", req, file_data);
92
    /* S3 request sent, wait for a reply */
93
  }
94
}
95

    
96
static void handle_api_call(struct mg_connection *nc, struct http_message *hm) {
97
  char file_name[100], file_data[100], host[100], bucket[100];
98

    
99
  /* Get form variables */
100
  mg_get_http_var(&hm->body, "file_name", file_name, sizeof(file_name));
101
  mg_get_http_var(&hm->body, "file_data", file_data, sizeof(file_data));
102
  mg_get_http_var(&hm->body, "host", host, sizeof(host));
103
  mg_get_http_var(&hm->body, "bucket", bucket, sizeof(bucket));
104

    
105
  /* Send headers */
106
  mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
107

    
108
  /* Send body */
109
  if (file_name[0] == '\0' || file_data[0] == '\0' || bucket[0] == '\0') {
110
    send_error_result(nc, "bad input");
111
  } else {
112
    send_s3_request(nc, file_name, file_data, host, bucket);
113
  }
114
}
115

    
116
/* Server handler */
117
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
118
  struct http_message *hm = (struct http_message *) ev_data;
119

    
120
  switch (ev) {
121
    case MG_EV_HTTP_REQUEST:
122
      if (mg_vcmp(&hm->uri, "/upload") == 0) {
123
        handle_api_call(nc, hm); /* Handle RESTful call */
124
      } else {
125
        mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
126
      }
127
      break;
128
    case MG_EV_CLOSE:
129
      unlink_conns(nc);
130
      break;
131
    default:
132
      break;
133
  }
134
}
135

    
136
int main(int argc, char *argv[]) {
137
  struct mg_mgr mgr;
138
  struct mg_connection *nc;
139
  int i;
140
  char *cp;
141

    
142
  mg_mgr_init(&mgr, NULL);
143
  nc = mg_bind(&mgr, s_http_port, ev_handler);
144
  mg_set_protocol_http_websocket(nc);
145
  s_http_server_opts.document_root = ".";
146
  s_http_server_opts.enable_directory_listing = "yes";
147

    
148
  /* Use current binary directory as document root */
149
  if (argc > 0 && ((cp = strrchr(argv[0], '/')) != NULL ||
150
                   (cp = strrchr(argv[0], '/')) != NULL)) {
151
    *cp = '\0';
152
    s_http_server_opts.document_root = argv[0];
153
  }
154

    
155
  /* Process command line options to customize HTTP server */
156
  for (i = 1; i < argc; i++) {
157
    if (strcmp(argv[i], "-D") == 0 && i + 1 < argc) {
158
      mgr.hexdump_file = argv[++i];
159
    } else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
160
      s_http_port = argv[++i];
161
    } else if (strcmp(argv[i], "-a") == 0 && i + 1 < argc) {
162
      s_access_key_id = argv[++i];
163
    } else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
164
      s_secret_access_key = argv[++i];
165
    }
166
  }
167

    
168
  if (s_access_key_id == NULL || s_secret_access_key == NULL) {
169
    fprintf(stderr,
170
            "Usage: %s -a access_key_id -s s_secret_access_key "
171
            "[-p port] [-D hexdump_file]\n",
172
            argv[0]);
173
    exit(1);
174
  }
175

    
176
  printf("Starting RESTful server on port %s\n", s_http_port);
177
  for (;;) {
178
    mg_mgr_poll(&mgr, 1000);
179
  }
180
  mg_mgr_free(&mgr);
181

    
182
  return 0;
183
}