static liHandlerResult progress_show(liVRequest *vr, gpointer param, gpointer *context) { mod_progress_show_param *psp = (mod_progress_show_param*) param; gboolean debug = _OPTION(vr, psp->p, 0).boolean; gchar *id; guint id_len; liCollectInfo *ci; mod_progress_job *job; if (*context) return LI_HANDLER_WAIT_FOR_EVENT; if (li_vrequest_is_handled(vr)) return LI_HANDLER_GO_ON; if (!li_querystring_find(vr->request.uri.query, CONST_STR_LEN("X-Progress-Id"), &id, &id_len) || id_len == 0 || id_len > 128) { if (debug) VR_DEBUG(vr, "%s", "progress.show: X-Progress-Id not specified"); return LI_HANDLER_GO_ON; } /* start collect job */ job = g_slice_new(mod_progress_job); job->vr = vr; job->context = context; job->format = psp->format; job->debug = debug; job->p = psp->p; job->id = g_strndup(id, id_len); ci = li_collect_start(vr->wrk, progress_collect_func, job, progress_collect_cb, NULL); *context = ci; /* may be NULL */ return ci ? LI_HANDLER_WAIT_FOR_EVENT : LI_HANDLER_GO_ON; }
static liHandlerResult progress_track(liVRequest *vr, gpointer param, gpointer *context) { gchar *id; guint id_len; liPlugin *p = (liPlugin*) param; gboolean debug = _OPTION(vr, p, 0).boolean; gint methods = _OPTION(vr, p, 1).number; mod_progress_data *pd = p->data; UNUSED(context); if (!(methods & (1 << vr->request.http_method))) { /* method not tracked */ } else if (g_ptr_array_index(vr->plugin_ctx, p->id)) { /* already tracked */ VR_WARNING(vr, "%s", "progress.track: already tracking request"); } else if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("X-Progress-Id"), &id, &id_len) && id_len <= 128) { /* progress id found, start tracking of connection */ mod_progress_node *node = g_slice_new0(mod_progress_node); node->timeout_queue_elem.data = node; node->id = g_strndup(id, id_len); node->worker_data = &pd->worker_data[vr->wrk->ndx]; node->vr = vr; g_ptr_array_index(vr->plugin_ctx, pd->p->id) = node; g_hash_table_replace(node->worker_data->hash_table, node->id, node); if (debug) VR_DEBUG(vr, "progress.track: tracking progress with id \"%s\"", node->id); } else if (debug) { VR_DEBUG(vr, "%s", "progress.track: X-Progress-Id parameter not found, cannot track request"); } return LI_HANDLER_GO_ON; }
/* the CollectCallback */ static void progress_collect_cb(gpointer cbdata, gpointer fdata, GPtrArray *result, gboolean complete) { guint i; GString *output; mod_progress_node *node = NULL; mod_progress_job *job = fdata; liVRequest *vr = job->vr; gboolean debug = job->debug; mod_progress_format format = job->format; UNUSED(cbdata); if (complete) { /* clear context so it doesn't get cleaned up anymore */ *(job->context) = NULL; for (i = 0; i < result->len; i++) { node = g_ptr_array_index(result, i); if (node) break; } output = g_string_sized_new(128); /* send mime-type. there seems to be no standard for javascript... using the most commong */ li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/x-javascript")); if (format == PROGRESS_FORMAT_LEGACY) { g_string_append_len(output, CONST_STR_LEN("new Object(")); } else if (format == PROGRESS_FORMAT_JSONP) { gchar *val; guint len; if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("X-Progress-Callback"), &val, &len)) { /* X-Progress-Callback specified, need to check for xss */ gchar *c; for (c = val; c != val+len; c++) { if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '.' || *c == '_') continue; break; } /* was there a bad char? */ if (c != val+len) { g_string_append_len(output, CONST_STR_LEN("progress(")); } else { g_string_append_len(output,val, len); g_string_append_c(output, '('); } } else { g_string_append_len(output, CONST_STR_LEN("progress(")); } } if (!node) { /* progress id not known */ if (debug) VR_DEBUG(vr, "progress.show: progress id \"%s\" unknown", job->id); g_string_append_len(output, CONST_STR_LEN("{\"state\": \"unknown\"}")); } else { if (debug) VR_DEBUG(vr, "progress.show: progress id \"%s\" found", job->id); if (node->vr) { /* still in progress */ g_string_append_printf(output, "{\"state\": \"running\", \"received\": %"G_GUINT64_FORMAT", \"sent\": %"G_GUINT64_FORMAT", \"request_size\": %"G_GUINT64_FORMAT", \"response_size\": %"G_GUINT64_FORMAT"}", node->bytes_in, node->bytes_out, node->request_size, node->response_size ); } else if (node->status_code == 200) { /* done, success */ g_string_append_printf(output, "{\"state\": \"done\", \"received\": %"G_GUINT64_FORMAT", \"sent\": %"G_GUINT64_FORMAT", \"request_size\": %"G_GUINT64_FORMAT", \"response_size\": %"G_GUINT64_FORMAT"}", node->bytes_in, node->bytes_out, node->request_size, node->response_size ); } else { /* done, error */ g_string_append_printf(output, "{\"state\": \"error\", \"status\": %d}", node->status_code ); } } if (format == PROGRESS_FORMAT_LEGACY || format == PROGRESS_FORMAT_JSONP) { g_string_append_c(output, ')'); } vr->response.http_status = 200; li_chunkqueue_append_string(vr->out, output); li_vrequest_handle_direct(vr); li_vrequest_joblist_append(vr); } /* free results */ for (i = 0; i < result->len; i++) { if (g_ptr_array_index(result, i)) g_slice_free(mod_progress_node, g_ptr_array_index(result, i)); } g_free(job->id); g_slice_free(mod_progress_job, job); }
static liHandlerResult flv(liVRequest *vr, gpointer param, gpointer *context) { gchar *start; guint len; goffset pos; liHandlerResult res; gboolean cachable; struct stat st; int err; int fd = -1; UNUSED(context); UNUSED(param); if (li_vrequest_is_handled(vr)) return LI_HANDLER_GO_ON; res = li_stat_cache_get(vr, vr->physical.path, &st, &err, &fd); if (res == LI_HANDLER_WAIT_FOR_EVENT) return res; if (res == LI_HANDLER_ERROR) { /* open or fstat failed */ if (fd != -1) close(fd); if (!li_vrequest_handle_direct(vr)) return LI_HANDLER_ERROR; switch (err) { case ENOENT: case ENOTDIR: vr->response.http_status = 404; return LI_HANDLER_GO_ON; case EACCES: vr->response.http_status = 403; return LI_HANDLER_GO_ON; default: VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err)); return LI_HANDLER_ERROR; } } else if (S_ISDIR(st.st_mode)) { if (fd != -1) close(fd); return LI_HANDLER_GO_ON; } else if (!S_ISREG(st.st_mode)) { if (fd != -1) close(fd); if (!li_vrequest_handle_direct(vr)) return LI_HANDLER_ERROR; vr->response.http_status = 403; } else { liChunkFile *cf; #ifdef FD_CLOEXEC fcntl(fd, F_SETFD, FD_CLOEXEC); #endif if (!li_vrequest_handle_direct(vr)) { close(fd); return LI_HANDLER_ERROR; } if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("start"), &start, &len)) { guint i; pos = 0; for (i = 0; i < len; i++) { if (start[i] >= '0' && start[i] <= '9') { pos *= 10; pos += start[i] - '0'; } } } else { pos = 0; } li_etag_set_header(vr, &st, &cachable); if (cachable) { vr->response.http_status = 304; close(fd); return LI_HANDLER_GO_ON; } vr->response.http_status = 200; li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv")); if (pos < 0 || pos > st.st_size) pos = 0; if (pos != 0) li_chunkqueue_append_mem(vr->direct_out, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9")); cf = li_chunkfile_new(NULL, fd, FALSE); li_chunkqueue_append_chunkfile(vr->direct_out, cf, pos, st.st_size - pos); li_chunkfile_release(cf); } return LI_HANDLER_GO_ON; }