static void conn_send(struct vlc_h2_frame *f) { assert(f != NULL); size_t len = vlc_h2_frame_size(f); ssize_t val = write(external_fd, f->data, len); assert((size_t)val == len); free(f); }
/** * Receives stream data. * * Dequeues pending incoming data for an HTTP/2 stream. If there is currently * no data block, wait for one. * * \return a VLC data block, or NULL on stream error or end of stream */ static block_t *vlc_h2_stream_read(struct vlc_http_stream *stream) { struct vlc_h2_stream *s = (struct vlc_h2_stream *)stream; struct vlc_h2_conn *conn = s->conn; struct vlc_h2_frame *f; vlc_h2_stream_lock(s); while ((f = s->recv_head) == NULL && !s->recv_end && !s->interrupted) { mutex_cleanup_push(&conn->lock); vlc_cond_wait(&s->recv_wait, &conn->lock); vlc_cleanup_pop(); } if (f == NULL) { vlc_h2_stream_unlock(s); return NULL; } s->recv_head = f->next; if (f->next == NULL) { assert(s->recv_tailp == &f->next); s->recv_tailp = &s->recv_head; } /* Credit the receive window if missing credit exceeds 50%. */ uint_fast32_t credit = VLC_H2_INIT_WINDOW - s->recv_cwnd; if (credit >= (VLC_H2_INIT_WINDOW / 2) && !vlc_h2_output_send(conn->out, vlc_h2_frame_window_update(s->id, credit))) s->recv_cwnd += credit; vlc_h2_stream_unlock(s); /* This, err, unconventional code to avoid copying data. */ block_t *block = block_heap_Alloc(f, sizeof (*f) + vlc_h2_frame_size(f)); if (unlikely(block == NULL)) { free(f); vlc_h2_stream_error(conn, s->id, VLC_H2_INTERNAL_ERROR); return NULL; } size_t len; uint8_t *buf = vlc_h2_frame_data_get(f, &len); assert(block->i_buffer >= len); assert(block->p_buffer <= buf); assert(block->p_buffer + block->i_buffer >= buf + len); block->p_buffer = buf; block->i_buffer = len; return block; }
/** * Sends one HTTP/2 frame through TLS. * * This function sends a whole HTTP/2 frame through a TLS session, then * releases the memory used by the frame. * * The caller must "own" the write side of the TLS session. * * @note This is a blocking function and may be a thread cancellation point. * * @return 0 on success, -1 if the connection failed */ static int vlc_h2_frame_send(struct vlc_tls *tls, struct vlc_h2_frame *f) { size_t len = vlc_h2_frame_size(f); ssize_t val; vlc_cleanup_push(free, f); val = vlc_https_send(tls, f->data, len); vlc_cleanup_pop(); free(f); return ((size_t)val == len) ? 0 : -1; }
/** Queues one outgoing HTTP/2. */ static int vlc_h2_output_queue(struct vlc_h2_output *out, struct vlc_h2_queue *q, struct vlc_h2_frame *f) { if (unlikely(f == NULL)) return -1; /* memory error */ /* Iterate the list to count size and find tail pointer */ struct vlc_h2_frame **lastp = &f; size_t len = 0; do { struct vlc_h2_frame *n = *lastp; len += vlc_h2_frame_size(n); lastp = &n->next; } while (*lastp != NULL); vlc_mutex_lock(&out->lock); if (out->failed) goto error; out->size += len; if (out->size >= VLC_H2_MAX_QUEUE) { /* The queue is full. This should never happen but it can be triggered * by an evil peer at the other end (e.g. sending a lot of pings and * never receiving pongs. Returning an error is better than filling * all memory. */ out->size -= len; goto error; } assert(*(q->last) == NULL); *(q->last) = f; q->last = lastp; vlc_cond_signal(&out->wait); vlc_mutex_unlock(&out->lock); return 0; error: vlc_mutex_unlock(&out->lock); while (f != NULL) { struct vlc_h2_frame *n = f->next; free(f); f = n; } return -1; }
/** Dequeues one outgoing HTTP/2. */ static struct vlc_h2_frame *vlc_h2_output_dequeue(struct vlc_h2_output *out) { struct vlc_h2_queue *q; struct vlc_h2_frame *frame; size_t len; vlc_mutex_lock(&out->lock); for (;;) { q = &out->prio; if (q->first != NULL) break; q = &out->queue; if (q->first != NULL) break; if (unlikely(out->closing)) { vlc_mutex_unlock(&out->lock); return NULL; } int canc = vlc_savecancel(); vlc_cond_wait(&out->wait, &out->lock); vlc_restorecancel(canc); } frame = q->first; q->first = frame->next; if (frame->next == NULL) { assert(q->last == &frame->next); q->last = &q->first; } assert(q->last != &frame->next); len = vlc_h2_frame_size(frame); assert(out->size >= len); out->size -= len; vlc_mutex_unlock(&out->lock); frame->next = NULL; return frame; }