engines/http: silence openssl 3.0 deprecation warnings
[fio.git] / engines / http.c
CommitLineData
c2f6a13d
LMB
1/*
2 * HTTP GET/PUT IO engine
3 *
4 * IO engine to perform HTTP(S) GET/PUT requests via libcurl-easy.
5 *
6 * Copyright (C) 2018 SUSE LLC
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License,
10 * version 2 as published by the Free Software Foundation..
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <pthread.h>
24#include <time.h>
25#include <curl/curl.h>
26#include <openssl/hmac.h>
27#include <openssl/sha.h>
09fd2966 28#include <openssl/md5.h>
c2f6a13d
LMB
29#include "fio.h"
30#include "../optgroup.h"
31
9c1c1a8d
JA
32/*
33 * Silence OpenSSL 3.0 deprecated function warnings
34 */
35#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
c2f6a13d 36
09fd2966
LMB
37enum {
38 FIO_HTTP_WEBDAV = 0,
39 FIO_HTTP_S3 = 1,
40 FIO_HTTP_SWIFT = 2,
41
42 FIO_HTTPS_OFF = 0,
43 FIO_HTTPS_ON = 1,
44 FIO_HTTPS_INSECURE = 2,
45};
46
c2f6a13d
LMB
47struct http_data {
48 CURL *curl;
49};
50
51struct http_options {
52 void *pad;
09fd2966 53 unsigned int https;
c2f6a13d
LMB
54 char *host;
55 char *user;
56 char *pass;
57 char *s3_key;
58 char *s3_keyid;
59 char *s3_region;
09fd2966 60 char *swift_auth_token;
c2f6a13d 61 int verbose;
09fd2966 62 unsigned int mode;
c2f6a13d
LMB
63};
64
65struct http_curl_stream {
66 char *buf;
67 size_t pos;
68 size_t max;
69};
70
71static struct fio_option options[] = {
72 {
73 .name = "https",
74 .lname = "https",
09fd2966 75 .type = FIO_OPT_STR,
c2f6a13d
LMB
76 .help = "Enable https",
77 .off1 = offsetof(struct http_options, https),
09fd2966
LMB
78 .def = "off",
79 .posval = {
80 { .ival = "off",
81 .oval = FIO_HTTPS_OFF,
82 .help = "No HTTPS",
83 },
84 { .ival = "on",
85 .oval = FIO_HTTPS_ON,
86 .help = "Enable HTTPS",
87 },
88 { .ival = "insecure",
89 .oval = FIO_HTTPS_INSECURE,
90 .help = "Enable HTTPS, disable peer verification",
91 },
92 },
c2f6a13d
LMB
93 .category = FIO_OPT_C_ENGINE,
94 .group = FIO_OPT_G_HTTP,
95 },
96 {
97 .name = "http_host",
98 .lname = "http_host",
99 .type = FIO_OPT_STR_STORE,
100 .help = "Hostname (S3 bucket)",
101 .off1 = offsetof(struct http_options, host),
102 .def = "localhost",
103 .category = FIO_OPT_C_ENGINE,
104 .group = FIO_OPT_G_HTTP,
105 },
106 {
107 .name = "http_user",
108 .lname = "http_user",
109 .type = FIO_OPT_STR_STORE,
110 .help = "HTTP user name",
111 .off1 = offsetof(struct http_options, user),
112 .category = FIO_OPT_C_ENGINE,
113 .group = FIO_OPT_G_HTTP,
114 },
115 {
116 .name = "http_pass",
117 .lname = "http_pass",
118 .type = FIO_OPT_STR_STORE,
119 .help = "HTTP password",
120 .off1 = offsetof(struct http_options, pass),
121 .category = FIO_OPT_C_ENGINE,
122 .group = FIO_OPT_G_HTTP,
123 },
124 {
125 .name = "http_s3_key",
126 .lname = "S3 secret key",
127 .type = FIO_OPT_STR_STORE,
128 .help = "S3 secret key",
129 .off1 = offsetof(struct http_options, s3_key),
130 .def = "",
131 .category = FIO_OPT_C_ENGINE,
132 .group = FIO_OPT_G_HTTP,
133 },
134 {
135 .name = "http_s3_keyid",
136 .lname = "S3 key id",
137 .type = FIO_OPT_STR_STORE,
138 .help = "S3 key id",
139 .off1 = offsetof(struct http_options, s3_keyid),
140 .def = "",
141 .category = FIO_OPT_C_ENGINE,
142 .group = FIO_OPT_G_HTTP,
143 },
09fd2966
LMB
144 {
145 .name = "http_swift_auth_token",
146 .lname = "Swift auth token",
147 .type = FIO_OPT_STR_STORE,
148 .help = "OpenStack Swift auth token",
149 .off1 = offsetof(struct http_options, swift_auth_token),
150 .def = "",
151 .category = FIO_OPT_C_ENGINE,
152 .group = FIO_OPT_G_HTTP,
153 },
c2f6a13d
LMB
154 {
155 .name = "http_s3_region",
156 .lname = "S3 region",
157 .type = FIO_OPT_STR_STORE,
158 .help = "S3 region",
159 .off1 = offsetof(struct http_options, s3_region),
160 .def = "us-east-1",
161 .category = FIO_OPT_C_ENGINE,
162 .group = FIO_OPT_G_HTTP,
163 },
164 {
09fd2966
LMB
165 .name = "http_mode",
166 .lname = "Request mode to use",
167 .type = FIO_OPT_STR,
168 .help = "Whether to use WebDAV, Swift, or S3",
169 .off1 = offsetof(struct http_options, mode),
170 .def = "webdav",
171 .posval = {
172 { .ival = "webdav",
173 .oval = FIO_HTTP_WEBDAV,
174 .help = "WebDAV server",
175 },
176 { .ival = "s3",
177 .oval = FIO_HTTP_S3,
178 .help = "S3 storage backend",
179 },
180 { .ival = "swift",
181 .oval = FIO_HTTP_SWIFT,
182 .help = "OpenStack Swift storage",
183 },
184 },
c2f6a13d
LMB
185 .category = FIO_OPT_C_ENGINE,
186 .group = FIO_OPT_G_HTTP,
187 },
188 {
189 .name = "http_verbose",
09fd2966 190 .lname = "HTTP verbosity level",
c2f6a13d
LMB
191 .type = FIO_OPT_INT,
192 .help = "increase http engine verbosity",
193 .off1 = offsetof(struct http_options, verbose),
194 .def = "0",
195 .category = FIO_OPT_C_ENGINE,
196 .group = FIO_OPT_G_HTTP,
197 },
198 {
199 .name = NULL,
200 },
201};
202
203static char *_aws_uriencode(const char *uri)
204{
205 size_t bufsize = 1024;
206 char *r = malloc(bufsize);
207 char c;
208 int i, n;
209 const char *hex = "0123456789ABCDEF";
210
211 if (!r) {
212 log_err("malloc failed\n");
213 return NULL;
214 }
215
216 n = 0;
217 for (i = 0; (c = uri[i]); i++) {
218 if (n > bufsize-5) {
219 log_err("encoding the URL failed\n");
220 return NULL;
221 }
222
223 if ( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
224 || (c >= '0' && c <= '9') || c == '_' || c == '-'
225 || c == '~' || c == '.' || c == '/')
226 r[n++] = c;
227 else {
228 r[n++] = '%';
229 r[n++] = hex[(c >> 4 ) & 0xF];
230 r[n++] = hex[c & 0xF];
231 }
232 }
233 r[n++] = 0;
234 return r;
235}
236
237static char *_conv_hex(const unsigned char *p, size_t len)
238{
239 char *r;
240 int i,n;
241 const char *hex = "0123456789abcdef";
242 r = malloc(len * 2 + 1);
243 n = 0;
244 for (i = 0; i < len; i++) {
245 r[n++] = hex[(p[i] >> 4 ) & 0xF];
246 r[n++] = hex[p[i] & 0xF];
247 }
248 r[n] = 0;
249
250 return r;
251}
252
253static char *_gen_hex_sha256(const char *p, size_t len)
254{
255 unsigned char hash[SHA256_DIGEST_LENGTH];
256
257 SHA256((unsigned char*)p, len, hash);
258 return _conv_hex(hash, SHA256_DIGEST_LENGTH);
259}
260
09fd2966
LMB
261static char *_gen_hex_md5(const char *p, size_t len)
262{
263 unsigned char hash[MD5_DIGEST_LENGTH];
264
265 MD5((unsigned char*)p, len, hash);
266 return _conv_hex(hash, MD5_DIGEST_LENGTH);
267}
268
c2f6a13d 269static void _hmac(unsigned char *md, void *key, int key_len, char *data) {
b61a5f46
DD
270#ifndef CONFIG_HAVE_OPAQUE_HMAC_CTX
271 HMAC_CTX _ctx;
272#endif
c2f6a13d
LMB
273 HMAC_CTX *ctx;
274 unsigned int hmac_len;
275
b61a5f46 276#ifdef CONFIG_HAVE_OPAQUE_HMAC_CTX
c2f6a13d 277 ctx = HMAC_CTX_new();
b61a5f46
DD
278#else
279 ctx = &_ctx;
e8aaa776
JA
280 /* work-around crash in certain versions of libssl */
281 HMAC_CTX_init(ctx);
b61a5f46 282#endif
c2f6a13d
LMB
283 HMAC_Init_ex(ctx, key, key_len, EVP_sha256(), NULL);
284 HMAC_Update(ctx, (unsigned char*)data, strlen(data));
285 HMAC_Final(ctx, md, &hmac_len);
b61a5f46 286#ifdef CONFIG_HAVE_OPAQUE_HMAC_CTX
c2f6a13d 287 HMAC_CTX_free(ctx);
b61a5f46
DD
288#else
289 HMAC_CTX_cleanup(ctx);
290#endif
c2f6a13d
LMB
291}
292
293static int _curl_trace(CURL *handle, curl_infotype type,
294 char *data, size_t size,
295 void *userp)
296{
297 const char *text;
298 (void)handle; /* prevent compiler warning */
299 (void)userp;
300
301 switch (type) {
302 case CURLINFO_TEXT:
24010223 303 fprintf(stderr, "== Info: %s", data);
87933e32 304 fio_fallthrough;
c2f6a13d
LMB
305 default:
306 case CURLINFO_SSL_DATA_OUT:
307 case CURLINFO_SSL_DATA_IN:
308 return 0;
309
310 case CURLINFO_HEADER_OUT:
311 text = "=> Send header";
312 break;
313 case CURLINFO_DATA_OUT:
314 text = "=> Send data";
315 break;
316 case CURLINFO_HEADER_IN:
317 text = "<= Recv header";
318 break;
319 case CURLINFO_DATA_IN:
320 text = "<= Recv data";
321 break;
322 }
323
324 log_info("%s: %s", text, data);
325 return 0;
326}
327
328/* https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
329 * https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html#signing-request-intro
330 */
331static void _add_aws_auth_header(CURL *curl, struct curl_slist *slist, struct http_options *o,
332 int op, const char *uri, char *buf, size_t len)
333{
334 char date_short[16];
335 char date_iso[32];
336 char method[8];
337 char dkey[128];
338 char creq[512];
339 char sts[256];
340 char s[512];
341 char *uri_encoded = NULL;
342 char *dsha = NULL;
343 char *csha = NULL;
344 char *signature = NULL;
345 const char *service = "s3";
346 const char *aws = "aws4_request";
347 unsigned char md[SHA256_DIGEST_LENGTH];
348
349 time_t t = time(NULL);
350 struct tm *gtm = gmtime(&t);
351
352 strftime (date_short, sizeof(date_short), "%Y%m%d", gtm);
353 strftime (date_iso, sizeof(date_iso), "%Y%m%dT%H%M%SZ", gtm);
354 uri_encoded = _aws_uriencode(uri);
355
356 if (op == DDIR_WRITE) {
357 dsha = _gen_hex_sha256(buf, len);
358 sprintf(method, "PUT");
359 } else {
360 /* DDIR_READ && DDIR_TRIM supply an empty body */
361 if (op == DDIR_READ)
362 sprintf(method, "GET");
363 else
364 sprintf(method, "DELETE");
365 dsha = _gen_hex_sha256("", 0);
366 }
367
368 /* Create the canonical request first */
369 snprintf(creq, sizeof(creq),
370 "%s\n"
371 "%s\n"
372 "\n"
373 "host:%s\n"
374 "x-amz-content-sha256:%s\n"
375 "x-amz-date:%s\n"
376 "\n"
377 "host;x-amz-content-sha256;x-amz-date\n"
378 "%s"
379 , method
380 , uri_encoded, o->host, dsha, date_iso, dsha);
381
382 csha = _gen_hex_sha256(creq, strlen(creq));
383 snprintf(sts, sizeof(sts), "AWS4-HMAC-SHA256\n%s\n%s/%s/%s/%s\n%s",
384 date_iso, date_short, o->s3_region, service, aws, csha);
385
386 snprintf((char *)dkey, sizeof(dkey), "AWS4%s", o->s3_key);
387 _hmac(md, dkey, strlen(dkey), date_short);
388 _hmac(md, md, SHA256_DIGEST_LENGTH, o->s3_region);
389 _hmac(md, md, SHA256_DIGEST_LENGTH, (char*) service);
390 _hmac(md, md, SHA256_DIGEST_LENGTH, (char*) aws);
391 _hmac(md, md, SHA256_DIGEST_LENGTH, sts);
392
393 signature = _conv_hex(md, SHA256_DIGEST_LENGTH);
394
fc002f14 395 /* Suppress automatic Accept: header */
c2f6a13d
LMB
396 slist = curl_slist_append(slist, "Accept:");
397
398 snprintf(s, sizeof(s), "x-amz-content-sha256: %s", dsha);
399 slist = curl_slist_append(slist, s);
400
401 snprintf(s, sizeof(s), "x-amz-date: %s", date_iso);
402 slist = curl_slist_append(slist, s);
403
404 snprintf(s, sizeof(s), "Authorization: AWS4-HMAC-SHA256 Credential=%s/%s/%s/s3/aws4_request,"
405 "SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=%s",
406 o->s3_keyid, date_short, o->s3_region, signature);
407 slist = curl_slist_append(slist, s);
408
409 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
410
411 free(uri_encoded);
412 free(csha);
413 free(dsha);
414 free(signature);
415}
416
09fd2966
LMB
417static void _add_swift_header(CURL *curl, struct curl_slist *slist, struct http_options *o,
418 int op, const char *uri, char *buf, size_t len)
419{
420 char *dsha = NULL;
421 char s[512];
422
423 if (op == DDIR_WRITE) {
424 dsha = _gen_hex_md5(buf, len);
425 }
fc002f14 426 /* Suppress automatic Accept: header */
09fd2966
LMB
427 slist = curl_slist_append(slist, "Accept:");
428
429 snprintf(s, sizeof(s), "etag: %s", dsha);
430 slist = curl_slist_append(slist, s);
431
432 snprintf(s, sizeof(s), "x-auth-token: %s", o->swift_auth_token);
433 slist = curl_slist_append(slist, s);
434
435 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
436
437 free(dsha);
438}
439
c2f6a13d
LMB
440static void fio_http_cleanup(struct thread_data *td)
441{
442 struct http_data *http = td->io_ops_data;
443
444 if (http) {
445 curl_easy_cleanup(http->curl);
446 free(http);
447 }
448}
449
450static size_t _http_read(void *ptr, size_t size, size_t nmemb, void *stream)
451{
452 struct http_curl_stream *state = stream;
453 size_t len = size * nmemb;
454 /* We're retrieving; nothing is supposed to be read locally */
455 if (!stream)
456 return 0;
457 if (len+state->pos > state->max)
458 len = state->max - state->pos;
459 memcpy(ptr, &state->buf[state->pos], len);
460 state->pos += len;
461 return len;
462}
463
464static size_t _http_write(void *ptr, size_t size, size_t nmemb, void *stream)
465{
466 struct http_curl_stream *state = stream;
467 /* We're just discarding the returned body after a PUT */
468 if (!stream)
469 return nmemb;
470 if (size != 1)
471 return CURLE_WRITE_ERROR;
472 if (nmemb + state->pos > state->max)
473 return CURLE_WRITE_ERROR;
474 memcpy(&state->buf[state->pos], ptr, nmemb);
475 state->pos += nmemb;
476 return nmemb;
477}
478
479static int _http_seek(void *stream, curl_off_t offset, int origin)
480{
481 struct http_curl_stream *state = stream;
482 if (offset < state->max && origin == SEEK_SET) {
483 state->pos = offset;
484 return CURL_SEEKFUNC_OK;
485 } else
486 return CURL_SEEKFUNC_FAIL;
487}
488
489static enum fio_q_status fio_http_queue(struct thread_data *td,
490 struct io_u *io_u)
491{
492 struct http_data *http = td->io_ops_data;
493 struct http_options *o = td->eo;
494 struct http_curl_stream _curl_stream;
495 struct curl_slist *slist = NULL;
496 char object[512];
497 char url[1024];
498 long status;
499 CURLcode res;
500 int r = -1;
501
502 fio_ro_check(td, io_u);
503 memset(&_curl_stream, 0, sizeof(_curl_stream));
09fd2966
LMB
504 snprintf(object, sizeof(object), "%s_%llu_%llu", td->files[0]->file_name,
505 io_u->offset, io_u->xfer_buflen);
506 if (o->https == FIO_HTTPS_OFF)
507 snprintf(url, sizeof(url), "http://%s%s", o->host, object);
508 else
509 snprintf(url, sizeof(url), "https://%s%s", o->host, object);
c2f6a13d
LMB
510 curl_easy_setopt(http->curl, CURLOPT_URL, url);
511 _curl_stream.buf = io_u->xfer_buf;
512 _curl_stream.max = io_u->xfer_buflen;
513 curl_easy_setopt(http->curl, CURLOPT_SEEKDATA, &_curl_stream);
514 curl_easy_setopt(http->curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)io_u->xfer_buflen);
515
09fd2966 516 if (o->mode == FIO_HTTP_S3)
c2f6a13d
LMB
517 _add_aws_auth_header(http->curl, slist, o, io_u->ddir, object,
518 io_u->xfer_buf, io_u->xfer_buflen);
09fd2966
LMB
519 else if (o->mode == FIO_HTTP_SWIFT)
520 _add_swift_header(http->curl, slist, o, io_u->ddir, object,
521 io_u->xfer_buf, io_u->xfer_buflen);
c2f6a13d
LMB
522
523 if (io_u->ddir == DDIR_WRITE) {
524 curl_easy_setopt(http->curl, CURLOPT_READDATA, &_curl_stream);
525 curl_easy_setopt(http->curl, CURLOPT_WRITEDATA, NULL);
526 curl_easy_setopt(http->curl, CURLOPT_UPLOAD, 1L);
527 res = curl_easy_perform(http->curl);
528 if (res == CURLE_OK) {
529 curl_easy_getinfo(http->curl, CURLINFO_RESPONSE_CODE, &status);
530 if (status == 100 || (status >= 200 && status <= 204))
531 goto out;
532 log_err("DDIR_WRITE failed with HTTP status code %ld\n", status);
c2f6a13d 533 }
4a4c21bf 534 goto err;
c2f6a13d
LMB
535 } else if (io_u->ddir == DDIR_READ) {
536 curl_easy_setopt(http->curl, CURLOPT_READDATA, NULL);
537 curl_easy_setopt(http->curl, CURLOPT_WRITEDATA, &_curl_stream);
538 curl_easy_setopt(http->curl, CURLOPT_HTTPGET, 1L);
539 res = curl_easy_perform(http->curl);
540 if (res == CURLE_OK) {
541 curl_easy_getinfo(http->curl, CURLINFO_RESPONSE_CODE, &status);
542 if (status == 200)
543 goto out;
544 else if (status == 404) {
545 /* Object doesn't exist. Pretend we read
546 * zeroes */
547 memset(io_u->xfer_buf, 0, io_u->xfer_buflen);
548 goto out;
549 }
550 log_err("DDIR_READ failed with HTTP status code %ld\n", status);
551 }
552 goto err;
553 } else if (io_u->ddir == DDIR_TRIM) {
554 curl_easy_setopt(http->curl, CURLOPT_HTTPGET, 1L);
555 curl_easy_setopt(http->curl, CURLOPT_CUSTOMREQUEST, "DELETE");
bd915a6b 556 curl_easy_setopt(http->curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)0);
c2f6a13d
LMB
557 curl_easy_setopt(http->curl, CURLOPT_READDATA, NULL);
558 curl_easy_setopt(http->curl, CURLOPT_WRITEDATA, NULL);
559 res = curl_easy_perform(http->curl);
560 if (res == CURLE_OK) {
561 curl_easy_getinfo(http->curl, CURLINFO_RESPONSE_CODE, &status);
562 if (status == 200 || status == 202 || status == 204 || status == 404)
563 goto out;
564 log_err("DDIR_TRIM failed with HTTP status code %ld\n", status);
565 }
566 goto err;
567 }
568
569 log_err("WARNING: Only DDIR_READ/DDIR_WRITE/DDIR_TRIM are supported!\n");
570
571err:
572 io_u->error = r;
573 td_verror(td, io_u->error, "transfer");
574out:
575 curl_slist_free_all(slist);
576 return FIO_Q_COMPLETED;
577}
578
579static struct io_u *fio_http_event(struct thread_data *td, int event)
580{
581 /* sync IO engine - never any outstanding events */
582 return NULL;
583}
584
585int fio_http_getevents(struct thread_data *td, unsigned int min,
586 unsigned int max, const struct timespec *t)
587{
588 /* sync IO engine - never any outstanding events */
589 return 0;
590}
591
592static int fio_http_setup(struct thread_data *td)
593{
594 struct http_data *http = NULL;
595 struct http_options *o = td->eo;
c65f3435 596
c2f6a13d
LMB
597 /* allocate engine specific structure to deal with libhttp. */
598 http = calloc(1, sizeof(*http));
599 if (!http) {
600 log_err("calloc failed.\n");
601 goto cleanup;
602 }
603
604 http->curl = curl_easy_init();
605 if (o->verbose)
606 curl_easy_setopt(http->curl, CURLOPT_VERBOSE, 1L);
607 if (o->verbose > 1)
608 curl_easy_setopt(http->curl, CURLOPT_DEBUGFUNCTION, &_curl_trace);
609 curl_easy_setopt(http->curl, CURLOPT_NOPROGRESS, 1L);
610 curl_easy_setopt(http->curl, CURLOPT_FOLLOWLOCATION, 1L);
611 curl_easy_setopt(http->curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
09fd2966
LMB
612 if (o->https == FIO_HTTPS_INSECURE) {
613 curl_easy_setopt(http->curl, CURLOPT_SSL_VERIFYPEER, 0L);
614 curl_easy_setopt(http->curl, CURLOPT_SSL_VERIFYHOST, 0L);
615 }
c2f6a13d
LMB
616 curl_easy_setopt(http->curl, CURLOPT_READFUNCTION, _http_read);
617 curl_easy_setopt(http->curl, CURLOPT_WRITEFUNCTION, _http_write);
bd915a6b 618 curl_easy_setopt(http->curl, CURLOPT_SEEKFUNCTION, &_http_seek);
c2f6a13d
LMB
619 if (o->user && o->pass) {
620 curl_easy_setopt(http->curl, CURLOPT_USERNAME, o->user);
621 curl_easy_setopt(http->curl, CURLOPT_PASSWORD, o->pass);
622 curl_easy_setopt(http->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
623 }
624
625 td->io_ops_data = http;
626
627 /* Force single process mode. */
628 td->o.use_thread = 1;
629
630 return 0;
631cleanup:
632 fio_http_cleanup(td);
c65f3435 633 return 1;
c2f6a13d
LMB
634}
635
636static int fio_http_open(struct thread_data *td, struct fio_file *f)
637{
638 return 0;
639}
640static int fio_http_invalidate(struct thread_data *td, struct fio_file *f)
641{
642 return 0;
643}
644
5a8a6a03 645FIO_STATIC struct ioengine_ops ioengine = {
c2f6a13d
LMB
646 .name = "http",
647 .version = FIO_IOOPS_VERSION,
f32a30d4 648 .flags = FIO_DISKLESSIO | FIO_SYNCIO,
c2f6a13d
LMB
649 .setup = fio_http_setup,
650 .queue = fio_http_queue,
651 .getevents = fio_http_getevents,
652 .event = fio_http_event,
653 .cleanup = fio_http_cleanup,
654 .open_file = fio_http_open,
655 .invalidate = fio_http_invalidate,
656 .options = options,
657 .option_struct_size = sizeof(struct http_options),
658};
659
660static void fio_init fio_http_register(void)
661{
662 register_ioengine(&ioengine);
663}
664
665static void fio_exit fio_http_unregister(void)
666{
667 unregister_ioengine(&ioengine);
668}