Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/access/http/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 11 kB image not shown  

Quelle  h1conn.c   Sprache: C

 

/*****************************************************************************
 * h1conn.c: HTTP 1.x connection handling
 *****************************************************************************
 * Copyright © 2015 Rémi Denis-Courmont
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vlc_common.h>
#include <vlc_tls.h>
#include <vlc_block.h>

#include "conn.h"
#include "message.h"

static unsigned vlc_http_can_read(const char *buf, size_t len)
{
    static const char end[4] = { '\r', '\n', '\r', '\n' };

    for (unsigned i = 4; i > 0; i--)
        if (len >= i && !memcmp(buf + len - i, end, i))
            return 4 - i;
    return 4;
}

/**
 * Receives HTTP headers.
 *
 * Receives HTTP 1.x response line and headers.
 *
 * @return A heap-allocated null-terminated string contained the full
 * headers, including the final CRLF.
 */

static char *vlc_https_headers_recv(struct vlc_tls *tls, size_t *restrict lenp)
{
    size_t size = 0, len = 0, canread;
    char *buf = NULL;

    while ((canread = vlc_http_can_read(buf, len)) > 0)
    {
        ssize_t val;

        if (len + canread >= size)
        {
            size += 2048;
            if (size > 65536)
                goto fail;

            char *newbuf = realloc(buf, size);
            if (unlikely(newbuf == NULL))
                goto fail;

            buf = newbuf;
        }

        assert(size - len >= canread);
        //vlc_cleanup_push(free, buf);
        val = vlc_tls_Read(tls, buf + len, canread, true);
        //vlc_cleanup_pop();

        if (val != (ssize_t)canread)
            goto fail;

        len += val;
    }

    assert(size - len >= 1);
    buf[len] = '\0'; /* for convenience */
    if (lenp != NULL)
        *lenp = len;
    return buf;
fail:
    free(buf);
    return NULL;
}

/** Gets minor HTTP version */
static int vlc_http_minor(const char *msg)
{
    int minor;

    if (sscanf(msg, "HTTP/1.%1d", &minor) == 1)
        return minor;
    return -1;
}

struct vlc_h1_conn
{
    struct vlc_http_conn conn;
    struct vlc_http_stream stream;
    uintmax_t content_length;
    bool connection_close;
    bool active;
    bool released;
    bool proxy;
    void *opaque;
};

#define CO(conn) ((conn)->opaque)

static void vlc_h1_conn_destroy(struct vlc_h1_conn *conn);

static void *vlc_h1_stream_fatal(struct vlc_h1_conn *conn)
{
    if (conn->conn.tls != NULL)
    {
        vlc_http_dbg(CO(conn), "connection failed");
        vlc_tls_Shutdown(conn->conn.tls, true);
        vlc_tls_Close(conn->conn.tls);
        conn->conn.tls = NULL;
    }
    return NULL;
}

static struct vlc_h1_conn *vlc_h1_stream_conn(struct vlc_http_stream *stream)
{
    return container_of(stream, struct vlc_h1_conn, stream);
}

static struct vlc_http_stream *vlc_h1_stream_open(struct vlc_http_conn *c,
                                 const struct vlc_http_msg *req, bool has_data)
{
    struct vlc_h1_conn *conn = container_of(c, struct vlc_h1_conn, conn);
    size_t len;
    ssize_t val;

    if (conn->active || conn->conn.tls == NULL)
        return NULL;

    char *payload = vlc_http_msg_format(req, &len, conn->proxy, has_data);
    if (unlikely(payload == NULL))
        return NULL;

    vlc_http_dbg(CO(conn), "outgoing request:\n%.*s", (int)len, payload);
    val = vlc_tls_Write(conn->conn.tls, payload, len);
    free(payload);

    if (val < (ssize_t)len)
        return vlc_h1_stream_fatal(conn);

    conn->active = true;
    conn->content_length = 0;
    conn->connection_close = false;
    return &conn->stream;
}

static struct vlc_http_msg *vlc_h1_stream_wait(struct vlc_http_stream *stream)
{
    struct vlc_h1_conn *conn = vlc_h1_stream_conn(stream);
    struct vlc_http_msg *resp;
    const char *str;
    size_t len;
    int minor;

    assert(conn->active);

    if (conn->conn.tls == NULL)
        return NULL;

    char *payload = vlc_https_headers_recv(conn->conn.tls, &len);
    if (payload == NULL)
        return vlc_h1_stream_fatal(conn);

    vlc_http_dbg(CO(conn), "incoming response:\n%.*s", (int)len, payload);

    resp = vlc_http_msg_headers(payload);
    minor = vlc_http_minor(payload);
    free(payload);

    if (resp == NULL)
        return vlc_h1_stream_fatal(conn);

    assert(minor >= 0);

    conn->content_length = vlc_http_msg_get_size(resp);
    conn->connection_close = false;

    if (minor >= 1)
    {
        if (vlc_http_msg_get_token(resp, "Connection", "close") != NULL)
            conn->connection_close = true;

        str = vlc_http_msg_get_token(resp, "Transfer-Encoding", "chunked");
        if (str != NULL)
        {
            if (vlc_http_next_token(str) != NULL)
            {
                vlc_http_msg_destroy(resp);
                return vlc_h1_stream_fatal(conn); /* unsupported TE */
            }

            assert(conn->content_length == UINTMAX_MAX);
            stream = vlc_chunked_open(stream, conn->conn.tls);
            if (unlikely(stream == NULL))
            {
                vlc_http_msg_destroy(resp);
                return vlc_h1_stream_fatal(conn);
            }
        }
    }
    else
        conn->connection_close = true;

    vlc_http_msg_attach(resp, stream);
    return resp;
}

static ssize_t vlc_h1_stream_write(struct vlc_http_stream *stream,
                                   const void *base, size_t length, bool eos)
{
    struct vlc_h1_conn *conn = vlc_h1_stream_conn(stream);

    assert(conn->active);

    if (conn->conn.tls == NULL)
    {
        errno = EPIPE;
        return -1;
    }

    return vlc_https_chunked_write(conn->conn.tls, base, length, eos);
}

static block_t *vlc_h1_stream_read(struct vlc_http_stream *stream)
{
    struct vlc_h1_conn *conn = vlc_h1_stream_conn(stream);
    size_t size = 2048;

    assert(conn->active);

    if (conn->conn.tls == NULL)
        return vlc_http_error;

    if (size > conn->content_length)
        size = conn->content_length;
    if (size == 0)
        return NULL;

    block_t *block = block_Alloc(size);
    if (unlikely(block == NULL))
        return vlc_http_error;

    ssize_t val = vlc_tls_Read(conn->conn.tls, block->p_buffer, size, false);
    if (val <= 0)
    {
        block_Release(block);
        if (val < 0)
            return vlc_http_error;
        if (conn->content_length != UINTMAX_MAX)
        {
            errno = ECONNRESET;
            return vlc_http_error;
        }
        return NULL;
    }

    block->i_buffer = val;
    if (conn->content_length != UINTMAX_MAX)
        conn->content_length -= val;

    return block;
}

static void vlc_h1_stream_close(struct vlc_http_stream *stream, bool abort)
{
    struct vlc_h1_conn *conn = vlc_h1_stream_conn(stream);

    assert(conn->active);

    if (conn->connection_close)
        /* Server requested closing the connection after this stream. */
        abort = true;

    if (conn->content_length > 0 && conn->content_length != UINTMAX_MAX)
        /* Client left some data to be read, so pipelining is impossible. */
        abort = true;

    if (abort)
        /* Shut the underlying connection down and prevent reuse. */
        vlc_h1_stream_fatal(conn);

    conn->active = false;

    if (conn->released)
        vlc_h1_conn_destroy(conn);
}

static const struct vlc_http_stream_cbs vlc_h1_stream_callbacks =
{
    vlc_h1_stream_wait,
    vlc_h1_stream_write,
    vlc_h1_stream_read,
    vlc_h1_stream_close,
};

static void vlc_h1_conn_destroy(struct vlc_h1_conn *conn)
{
    assert(!conn->active);
    assert(conn->released);

    if (conn->conn.tls != NULL)
    {
        vlc_tls_Shutdown(conn->conn.tls, true);
        vlc_tls_Close(conn->conn.tls);
    }
    free(conn);
}

static void vlc_h1_conn_release(struct vlc_http_conn *c)
{
    struct vlc_h1_conn *conn = container_of(c, struct vlc_h1_conn, conn);

    assert(!conn->released);
    conn->released = true;

    if (!conn->active)
        vlc_h1_conn_destroy(conn);
}

static const struct vlc_http_conn_cbs vlc_h1_conn_callbacks =
{
    vlc_h1_stream_open,
    vlc_h1_conn_release,
};

struct vlc_http_conn *vlc_h1_conn_create(void *ctx, vlc_tls_t *tls, bool proxy)
{
    struct vlc_h1_conn *conn = malloc(sizeof (*conn));
    if (unlikely(conn == NULL))
        return NULL;

    conn->conn.cbs = &vlc_h1_conn_callbacks;
    conn->conn.tls = tls;
    conn->stream.cbs = &vlc_h1_stream_callbacks;
    conn->active = false;
    conn->released = false;
    conn->proxy = proxy;
    conn->opaque = ctx;

    return &conn->conn;
}

struct vlc_http_stream *vlc_h1_request(void *ctx, const char *hostname,
                                       unsigned port, bool proxy,
                                       const struct vlc_http_msg *req,
                                       bool idempotent, bool has_data,
                                       struct vlc_http_conn **restrict connp)
{
    struct addrinfo hints =
    {
        .ai_socktype = SOCK_STREAM,
        .ai_protocol = IPPROTO_TCP,
    }, *res;

    vlc_http_dbg(ctx, "resolving %s ...", hostname);

    int val = vlc_getaddrinfo_i11e(hostname, port, &hints, &res);
    if (val != 0)
    {   /* TODO: C locale for gai_strerror() */
        vlc_http_err(ctx, "cannot resolve %s: %s", hostname,
                     gai_strerror(val));
        return NULL;
    }

    for (const struct addrinfo *p = res; p != NULL; p = p->ai_next)
    {
        vlc_tls_t *tcp = vlc_tls_SocketOpenAddrInfo(p, idempotent);
        if (tcp == NULL)
        {
            vlc_http_err(ctx, "socket error: %s", vlc_strerror_c(errno));
            continue;
        }

        struct vlc_http_conn *conn = vlc_h1_conn_create(ctx, tcp, proxy);
        if (unlikely(conn == NULL))
        {
            vlc_tls_SessionDelete(tcp);
            continue;
        }

        /* Send the HTTP request */
        struct vlc_http_stream *stream = vlc_http_stream_open(conn, req,
                                                              has_data);

        if (stream != NULL)
        {
            if (connp != NULL)
                *connp = conn;
            else
                vlc_http_conn_release(conn);

            freeaddrinfo(res);
            return stream;
        }

        vlc_http_conn_release(conn);

        if (!idempotent)
            break; /* If the request is nonidempotent, it cannot be resent. */
    }

    /* All address info failed. */
    freeaddrinfo(res);
    return NULL;
}

Messung
CHZG
86979993

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.