#include "sentry_transport.h" #include "sentry_alloc.h" #include "sentry_envelope.h" #include "sentry_options.h" #include "sentry_ratelimiter.h" #include "sentry_string.h" #define ENVELOPE_MIME "application/x-sentry-envelope" // The headers we use are: `x-sentry-auth`, `content-type`, `content-length` #define MAX_HTTP_HEADERS 3 typedef struct sentry_transport_s { void (*send_envelope_func)(sentry_envelope_t *envelope, void *state); int (*startup_func)(const sentry_options_t *options, void *state); int (*shutdown_func)(uint64_t timeout, void *state); int (*flush_func)(uint64_t timeout, void *state); void (*free_func)(void *state); size_t (*dump_func)(sentry_run_t *run, void *state); void *state; bool running; } sentry_transport_t; sentry_transport_t * sentry_transport_new( void (*send_func)(sentry_envelope_t *envelope, void *state)) { sentry_transport_t *transport = SENTRY_MAKE(sentry_transport_t); if (!transport) { return NULL; } memset(transport, 0, sizeof(sentry_transport_t)); transport->send_envelope_func = send_func; return transport; } void sentry_transport_set_state(sentry_transport_t *transport, void *state) { transport->state = state; } void sentry_transport_set_free_func( sentry_transport_t *transport, void (*free_func)(void *state)) { transport->free_func = free_func; } void sentry_transport_set_startup_func(sentry_transport_t *transport, int (*startup_func)(const sentry_options_t *options, void *state)) { transport->startup_func = startup_func; } void sentry_transport_set_shutdown_func(sentry_transport_t *transport, int (*shutdown_func)(uint64_t timeout, void *state)) { transport->shutdown_func = shutdown_func; } void sentry_transport_set_flush_func(sentry_transport_t *transport, int (*flush_func)(uint64_t timeout, void *state)) { transport->flush_func = flush_func; } void sentry__transport_send_envelope( sentry_transport_t *transport, sentry_envelope_t *envelope) { if (!envelope) { return; } if (!transport) { SENTRY_TRACE("discarding envelope due to invalid transport"); sentry_envelope_free(envelope); return; } SENTRY_TRACE("sending envelope"); transport->send_envelope_func(envelope, transport->state); } int sentry__transport_startup( sentry_transport_t *transport, const sentry_options_t *options) { if (transport->startup_func) { SENTRY_TRACE("starting transport"); int rv = transport->startup_func(options, transport->state); transport->running = rv == 0; return rv; } return 0; } int sentry__transport_flush(sentry_transport_t *transport, uint64_t timeout) { if (transport->flush_func && transport->running) { SENTRY_TRACE("flushing transport"); return transport->flush_func(timeout, transport->state); } return 0; } int sentry__transport_shutdown(sentry_transport_t *transport, uint64_t timeout) { if (transport->shutdown_func && transport->running) { SENTRY_TRACE("shutting down transport"); transport->running = false; return transport->shutdown_func(timeout, transport->state); } return 0; } void sentry__transport_set_dump_func(sentry_transport_t *transport, size_t (*dump_func)(sentry_run_t *run, void *state)) { transport->dump_func = dump_func; } size_t sentry__transport_dump_queue(sentry_transport_t *transport, sentry_run_t *run) { if (!transport || !transport->dump_func) { return 0; } size_t dumped = transport->dump_func(run, transport->state); if (dumped) { SENTRY_TRACEF("dumped %zu in-flight envelopes to disk", dumped); } return dumped; } void sentry_transport_free(sentry_transport_t *transport) { if (!transport) { return; } if (transport->free_func) { transport->free_func(transport->state); } sentry_free(transport); } sentry_prepared_http_request_t * sentry__prepare_http_request(sentry_envelope_t *envelope, const sentry_dsn_t *dsn, const sentry_rate_limiter_t *rl) { if (!dsn || !dsn->is_valid) { return NULL; } size_t body_len = 0; bool body_owned = true; char *body = sentry_envelope_serialize_ratelimited( envelope, rl, &body_len, &body_owned); if (!body) { return NULL; } sentry_prepared_http_request_t *req = SENTRY_MAKE(sentry_prepared_http_request_t); if (!req) { if (body_owned) { sentry_free(body); } return NULL; } req->headers = sentry_malloc( sizeof(sentry_prepared_http_header_t) * MAX_HTTP_HEADERS); if (!req->headers) { sentry_free(req); if (body_owned) { sentry_free(body); } return NULL; } req->headers_len = 0; req->method = "POST"; req->url = sentry__dsn_get_envelope_url(dsn); sentry_prepared_http_header_t *h; h = &req->headers[req->headers_len++]; h->key = "x-sentry-auth"; h->value = sentry__dsn_get_auth_header(dsn); h = &req->headers[req->headers_len++]; h->key = "content-type"; h->value = sentry__string_clone(ENVELOPE_MIME); h = &req->headers[req->headers_len++]; h->key = "content-length"; h->value = sentry__int64_to_string((int64_t)body_len); req->body = body; req->body_len = body_len; req->body_owned = body_owned; return req; } void sentry__prepared_http_request_free(sentry_prepared_http_request_t *req) { if (!req) { return; } sentry_free(req->url); for (size_t i = 0; i < req->headers_len; i++) { sentry_free(req->headers[i].value); } sentry_free(req->headers); if (req->body_owned) { sentry_free(req->body); } sentry_free(req); }