/** Closure sent to main thread to request a reply to be sent to * a HTTP request. * Replies must be sent in the main loop in the main http thread, * this cannot be done from worker threads. */ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) { assert(!replySent && req); // Send event to main http thread to send reply message struct evbuffer* evb = evhttp_request_get_output_buffer(req); assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); auto req_copy = req; HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{ evhttp_send_reply(req_copy, nStatus, nullptr, nullptr); // Re-enable reading from the socket. This is the second part of the libevent // workaround above. if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { evhttp_connection* conn = evhttp_request_get_connection(req_copy); if (conn) { bufferevent* bev = evhttp_connection_get_bufferevent(conn); if (bev) { bufferevent_enable(bev, EV_READ | EV_WRITE); } } } }); ev->trigger(nullptr); replySent = true; req = nullptr; // transferred back to main thread }
static void stream_end(struct stream_ctx *st, int failed) { struct evhttp_connection *evcon; evcon = evhttp_request_get_connection(st->req); if (evcon) evhttp_connection_set_closecb(evcon, NULL, NULL); if (!failed) evhttp_send_reply_end(st->req); evbuffer_free(st->evbuf); event_free(st->ev); if (st->xcode) transcode_cleanup(st->xcode); else { free(st->buf); close(st->fd); } #ifdef HAVE_LIBEVENT2_OLD if (g_st == st) g_st = NULL; #endif free(st); }
static void HHVM_METHOD(EventHttpRequest, closeConnection) { struct evhttp_connection *conn; EventHttpRequestResourceData *resource_data = FETCH_RESOURCE(this_, EventHttpRequestResourceData, s_event_http_request); conn = evhttp_request_get_connection((evhttp_request_t *) resource_data->getInternalResourceData()); if(conn != NULL){ evhttp_connection_free(conn); } }
static void stream_chunk_raw_cb(int fd, short event, void *arg) { struct stream_ctx *st; size_t chunk_size; int ret; st = (struct stream_ctx *)arg; if (st->end_offset && (st->offset > st->end_offset)) { stream_end(st, 0); return; } if (st->end_offset && ((st->offset + STREAM_CHUNK_SIZE) > (st->end_offset + 1))) chunk_size = st->end_offset + 1 - st->offset; else chunk_size = STREAM_CHUNK_SIZE; ret = read(st->fd, st->buf, chunk_size); if (ret <= 0) { if (ret == 0) DPRINTF(E_LOG, L_HTTPD, "Done streaming file id %d\n", st->id); else DPRINTF(E_LOG, L_HTTPD, "Streaming error, file id %d\n", st->id); stream_end(st, 0); return; } DPRINTF(E_DBG, L_HTTPD, "Read %d bytes; streaming file id %d\n", ret, st->id); evbuffer_add(st->evbuf, st->buf, ret); #ifdef HAVE_LIBEVENT2_OLD evhttp_send_reply_chunk(st->req, st->evbuf); struct evhttp_connection *evcon = evhttp_request_get_connection(st->req); struct bufferevent *bufev = evhttp_connection_get_bufferevent(evcon); g_st = st; // Can't pass st to callback so use global - limits libevent 2.0 to a single stream bufev->writecb = stream_chunk_resched_cb_wrapper; #else evhttp_send_reply_chunk_with_cb(st->req, st->evbuf, stream_chunk_resched_cb, st); #endif st->offset += ret; stream_up_playcount(st); }
static void parseRequest(RequestInfo& requestInfo) { const struct evhttp_uri* uri = evhttp_request_get_evhttp_uri(requestInfo.req); /*const*/ struct evhttp_connection* conn = evhttp_request_get_connection(requestInfo.req); requestInfo.method = evhttp_request_get_command(requestInfo.req); requestInfo.uriHost = evhttp_uri_get_host(uri); requestInfo.uriPath = evhttp_uri_get_path(uri); requestInfo.uriQuery = evhttp_uri_get_query(uri); requestInfo.uriScheme = evhttp_uri_get_scheme(uri); requestInfo.requestHeaders = evhttp_request_get_input_headers(requestInfo.req); requestInfo.requestBody = evhttp_request_get_input_buffer(requestInfo.req); evhttp_connection_get_peer(conn, &requestInfo.remoteAddress, (ev_uint16_t*) &requestInfo.remotePort); }
CService HTTPRequest::GetPeer() { evhttp_connection* con = evhttp_request_get_connection(req); CService peer; if (con) { // evhttp retains ownership over returned address string const char* address = ""; uint16_t port = 0; evhttp_connection_get_peer(con, (char**)&address, &port); peer = LookupNumeric(address, port); } return peer; }
int dps_rest_remote_node_get(struct evhttp_request *req, ip_addr_t *remote_node) { struct evhttp_connection *evcon = NULL; char *remote_host = NULL; char *ptr = NULL; ev_uint16_t remote_port; int ret_val = -1; log_debug(RESTHandlerLogLevel, "Enter"); do { evcon = evhttp_request_get_connection(req); if (evcon == NULL) { log_error(RESTHandlerLogLevel, "ERROR: evhttp_request_get_connection returns NULL"); break; } evhttp_connection_get_peer(evcon, &remote_host, &remote_port); if (remote_host == NULL) { log_error(RESTHandlerLogLevel, "ERROR: evhttp_connection_get_peer returns NULL"); break; } log_debug(RESTHandlerLogLevel, "Remote Host %s", remote_host); remote_node->port_http = remote_port; ptr = strchr(remote_host,':'); if (NULL == ptr) { remote_node->family = AF_INET; ret_val = inet_pton(AF_INET, remote_host, remote_node->ip6); } else { remote_node->family = AF_INET6; ret_val = inet_pton(AF_INET6, remote_host, remote_node->ip6); } if (ret_val != 1) { log_error(RESTHandlerLogLevel, "ERROR: evhttp_request has invalid remote host %s", remote_host); break; } } while (0); log_debug(RESTHandlerLogLevel, "Exit"); return ret_val; }
static void http_router(struct evhttp_request *r, void *which_server) { char *remote_host; ev_uint16_t remote_port; const char *uri = evhttp_request_get_uri(r); struct evhttp_connection *conn = evhttp_request_get_connection(r); evhttp_connection_get_peer(conn, &remote_host, &remote_port); hlog(LOG_DEBUG, "http %s [%s] request %s", (which_server == (void *)1) ? "status" : "upload", remote_host, uri); http_requests++; /* status server routing */ if (which_server == (void *)1) { if (strncmp(uri, "/status.json", 12) == 0) { http_status(r); return; } if (strncmp(uri, "/counterdata?", 13) == 0) { http_counterdata(r, uri); return; } http_route_static(r, uri); return; } /* position upload server routing */ if (which_server == (void *)2) { if (strncmp(uri, "/", 7) == 0) { http_upload_position(r, remote_host); return; } hlog(LOG_DEBUG, "http request on upload server for '%s': 404 not found", uri); evhttp_send_error(r, HTTP_NOTFOUND, "Not found"); return; } hlog(LOG_ERR, "http request on unknown server for '%s': 404 not found", uri); evhttp_send_error(r, HTTP_NOTFOUND, "Server not found"); return; }
static Variant HHVM_METHOD(EventHttpRequest, getEventBufferEvent) { evhttp_connection_t *conn; event_buffer_event_t *bevent; EventHttpRequestResourceData *event_http_request_resource_data = FETCH_RESOURCE(this_, EventHttpRequestResourceData, s_event_http_request); if((conn = evhttp_request_get_connection((evhttp_request_t *) event_http_request_resource_data->getInternalResourceData())) == NULL){ return Variant(); } if((bevent = evhttp_connection_get_bufferevent(conn)) == NULL){ return Variant(); } String ClassName("EventBufferEvent"); Object event_buffer_event = ObjectData::newInstance(Unit::lookupClass(ClassName.get())); Resource resource; resource = Resource(NEWOBJ(EventBufferEventResourceData(bevent, event_buffer_event.get()))); SET_RESOURCE(event_buffer_event, resource, s_event_bufferevent); EventBufferEventResourceData *event_buffer_event_resource_data = FETCH_RESOURCE(event_buffer_event, EventBufferEventResourceData, s_event_bufferevent); event_buffer_event_resource_data->isInternal = true; return event_buffer_event; }
/* Thread: httpd */ void httpd_stream_file(struct evhttp_request *req, int id) { struct media_file_info *mfi; struct stream_ctx *st; void (*stream_cb)(int fd, short event, void *arg); struct stat sb; struct timeval tv; struct evhttp_connection *evcon; struct evkeyvalq *input_headers; struct evkeyvalq *output_headers; const char *param; const char *param_end; const char *ua; const char *client_codecs; char buf[64]; int64_t offset; int64_t end_offset; off_t pos; int transcode; int ret; offset = 0; end_offset = 0; input_headers = evhttp_request_get_input_headers(req); param = evhttp_find_header(input_headers, "Range"); if (param) { DPRINTF(E_DBG, L_HTTPD, "Found Range header: %s\n", param); /* Start offset */ ret = safe_atoi64(param + strlen("bytes="), &offset); if (ret < 0) { DPRINTF(E_LOG, L_HTTPD, "Invalid start offset, will stream whole file (%s)\n", param); offset = 0; } /* End offset, if any */ else { param_end = strchr(param, '-'); if (param_end && (strlen(param_end) > 1)) { ret = safe_atoi64(param_end + 1, &end_offset); if (ret < 0) { DPRINTF(E_LOG, L_HTTPD, "Invalid end offset, will stream to end of file (%s)\n", param); end_offset = 0; } if (end_offset < offset) { DPRINTF(E_LOG, L_HTTPD, "End offset < start offset, will stream to end of file (%" PRIi64 " < %" PRIi64 ")\n", end_offset, offset); end_offset = 0; } } } } mfi = db_file_fetch_byid(id); if (!mfi) { DPRINTF(E_LOG, L_HTTPD, "Item %d not found\n", id); evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } if (mfi->data_kind != DATA_KIND_FILE) { evhttp_send_error(req, 500, "Cannot stream radio station"); goto out_free_mfi; } st = (struct stream_ctx *)malloc(sizeof(struct stream_ctx)); if (!st) { DPRINTF(E_LOG, L_HTTPD, "Out of memory for struct stream_ctx\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_free_mfi; } memset(st, 0, sizeof(struct stream_ctx)); st->fd = -1; ua = evhttp_find_header(input_headers, "User-Agent"); client_codecs = evhttp_find_header(input_headers, "Accept-Codecs"); transcode = transcode_needed(ua, client_codecs, mfi->codectype); output_headers = evhttp_request_get_output_headers(req); if (transcode) { DPRINTF(E_INFO, L_HTTPD, "Preparing to transcode %s\n", mfi->path); stream_cb = stream_chunk_xcode_cb; st->xcode = transcode_setup(mfi, XCODE_PCM16_HEADER, &st->size); if (!st->xcode) { DPRINTF(E_WARN, L_HTTPD, "Transcoding setup failed, aborting streaming\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_free_st; } if (!evhttp_find_header(output_headers, "Content-Type")) evhttp_add_header(output_headers, "Content-Type", "audio/wav"); } else { /* Stream the raw file */ DPRINTF(E_INFO, L_HTTPD, "Preparing to stream %s\n", mfi->path); st->buf = (uint8_t *)malloc(STREAM_CHUNK_SIZE); if (!st->buf) { DPRINTF(E_LOG, L_HTTPD, "Out of memory for raw streaming buffer\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_free_st; } stream_cb = stream_chunk_raw_cb; st->fd = open(mfi->path, O_RDONLY); if (st->fd < 0) { DPRINTF(E_LOG, L_HTTPD, "Could not open %s: %s\n", mfi->path, strerror(errno)); evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); goto out_cleanup; } ret = stat(mfi->path, &sb); if (ret < 0) { DPRINTF(E_LOG, L_HTTPD, "Could not stat() %s: %s\n", mfi->path, strerror(errno)); evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); goto out_cleanup; } st->size = sb.st_size; pos = lseek(st->fd, offset, SEEK_SET); if (pos == (off_t) -1) { DPRINTF(E_LOG, L_HTTPD, "Could not seek into %s: %s\n", mfi->path, strerror(errno)); evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); goto out_cleanup; } st->offset = offset; st->end_offset = end_offset; /* Content-Type for video files is different than for audio files * and overrides whatever may have been set previously, like * application/x-dmap-tagged when we're speaking DAAP. */ if (mfi->has_video) { /* Front Row and others expect video/<type> */ ret = snprintf(buf, sizeof(buf), "video/%s", mfi->type); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n"); else { evhttp_remove_header(output_headers, "Content-Type"); evhttp_add_header(output_headers, "Content-Type", buf); } } /* If no Content-Type has been set and we're streaming audio, add a proper * Content-Type for the file we're streaming. Remember DAAP streams audio * with application/x-dmap-tagged as the Content-Type (ugh!). */ else if (!evhttp_find_header(output_headers, "Content-Type") && mfi->type) { ret = snprintf(buf, sizeof(buf), "audio/%s", mfi->type); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Type", buf); } } st->evbuf = evbuffer_new(); if (!st->evbuf) { DPRINTF(E_LOG, L_HTTPD, "Could not allocate an evbuffer for streaming\n"); evhttp_clear_headers(output_headers); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_cleanup; } ret = evbuffer_expand(st->evbuf, STREAM_CHUNK_SIZE); if (ret != 0) { DPRINTF(E_LOG, L_HTTPD, "Could not expand evbuffer for streaming\n"); evhttp_clear_headers(output_headers); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_cleanup; } st->ev = event_new(evbase_httpd, -1, EV_TIMEOUT, stream_cb, st); evutil_timerclear(&tv); if (!st->ev || (event_add(st->ev, &tv) < 0)) { DPRINTF(E_LOG, L_HTTPD, "Could not add one-shot event for streaming\n"); evhttp_clear_headers(output_headers); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_cleanup; } st->id = mfi->id; st->start_offset = offset; st->stream_size = st->size; st->req = req; if ((offset == 0) && (end_offset == 0)) { /* If we are not decoding, send the Content-Length. We don't do * that if we are decoding because we can only guesstimate the * size in this case and the error margin is unknown and variable. */ if (!transcode) { ret = snprintf(buf, sizeof(buf), "%" PRIi64, (int64_t)st->size); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Length", buf); } evhttp_send_reply_start(req, HTTP_OK, "OK"); } else { if (offset > 0) st->stream_size -= offset; if (end_offset > 0) st->stream_size -= (st->size - end_offset); DPRINTF(E_DBG, L_HTTPD, "Stream request with range %" PRIi64 "-%" PRIi64 "\n", offset, end_offset); ret = snprintf(buf, sizeof(buf), "bytes %" PRIi64 "-%" PRIi64 "/%" PRIi64, offset, (end_offset) ? end_offset : (int64_t)st->size, (int64_t)st->size); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Range too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Range", buf); ret = snprintf(buf, sizeof(buf), "%" PRIi64, ((end_offset) ? end_offset + 1 : (int64_t)st->size) - offset); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Length", buf); evhttp_send_reply_start(req, 206, "Partial Content"); } #ifdef HAVE_POSIX_FADVISE if (!transcode) { /* Hint the OS */ posix_fadvise(st->fd, st->start_offset, st->stream_size, POSIX_FADV_WILLNEED); posix_fadvise(st->fd, st->start_offset, st->stream_size, POSIX_FADV_SEQUENTIAL); posix_fadvise(st->fd, st->start_offset, st->stream_size, POSIX_FADV_NOREUSE); } #endif evcon = evhttp_request_get_connection(req); evhttp_connection_set_closecb(evcon, stream_fail_cb, st); DPRINTF(E_INFO, L_HTTPD, "Kicking off streaming for %s\n", mfi->path); free_mfi(mfi, 0); return; out_cleanup: if (st->evbuf) evbuffer_free(st->evbuf); if (st->xcode) transcode_cleanup(st->xcode); if (st->buf) free(st->buf); if (st->fd > 0) close(st->fd); out_free_st: free(st); out_free_mfi: free_mfi(mfi, 0); }
static void stream_chunk_xcode_cb(int fd, short event, void *arg) { struct stream_ctx *st; struct timeval tv; int xcoded; int ret; int dummy; st = (struct stream_ctx *)arg; xcoded = transcode(st->evbuf, STREAM_CHUNK_SIZE, st->xcode, &dummy); if (xcoded <= 0) { if (xcoded == 0) DPRINTF(E_LOG, L_HTTPD, "Done streaming transcoded file id %d\n", st->id); else DPRINTF(E_LOG, L_HTTPD, "Transcoding error, file id %d\n", st->id); stream_end(st, 0); return; } DPRINTF(E_DBG, L_HTTPD, "Got %d bytes from transcode; streaming file id %d\n", xcoded, st->id); /* Consume transcoded data until we meet start_offset */ if (st->start_offset > st->offset) { ret = st->start_offset - st->offset; if (ret < xcoded) { evbuffer_drain(st->evbuf, ret); st->offset += ret; ret = xcoded - ret; } else { evbuffer_drain(st->evbuf, xcoded); st->offset += xcoded; goto consume; } } else ret = xcoded; #ifdef HAVE_LIBEVENT2_OLD evhttp_send_reply_chunk(st->req, st->evbuf); struct evhttp_connection *evcon = evhttp_request_get_connection(st->req); struct bufferevent *bufev = evhttp_connection_get_bufferevent(evcon); g_st = st; // Can't pass st to callback so use global - limits libevent 2.0 to a single stream bufev->writecb = stream_chunk_resched_cb_wrapper; #else evhttp_send_reply_chunk_with_cb(st->req, st->evbuf, stream_chunk_resched_cb, st); #endif st->offset += ret; stream_up_playcount(st); return; consume: /* reschedule immediately - consume up to start_offset */ evutil_timerclear(&tv); ret = event_add(st->ev, &tv); if (ret < 0) { DPRINTF(E_LOG, L_HTTPD, "Could not re-add one-shot event for streaming (xcode)\n"); stream_end(st, 0); return; } }
static void http_request_done(struct evhttp_request *req, void *ctx) { char buffer[256]; ev_ssize_t nread; struct evkeyval *header; if (req == NULL) { /* If req is NULL, it means an error occurred, but * sadly we are mostly left guessing what the error * might have been. We'll do our best... */ struct bufferevent *bev = (struct bufferevent *) ctx; int errcode = EVUTIL_SOCKET_ERROR(); fprintf(stderr, "some request failed - no idea which one though!\n"); /* Print out the OpenSSL error queue that libevent * squirreled away for us, if any. */ /* If the OpenSSL error queue was empty, maybe it was a * socket error; let's try printing that. */ fprintf(stderr, "socket error = %s (%d)\n", evutil_socket_error_to_string(errcode), errcode); return; } /*fprintf(stderr, "Response line: %d %s\n", evhttp_request_get_response_code(req), evhttp_request_get_response_code_line(req)); struct evkeyvalq *headers = evhttp_request_get_input_headers(req); if (NULL != headers) { fprintf(stderr, "Response headers:\n"); TAILQ_FOREACH(header, headers, next) { fprintf(stderr, "%s: %s\r\n", header->key, header->value); } fprintf(stderr, "\n"); }*/ const int http_code = evhttp_request_get_response_code(req); struct evhttp_connection *evcon = evhttp_request_get_connection(req); if (HTTP_MOVEPERM == http_code || HTTP_MOVETEMP == http_code) { const char *location = evhttp_find_header(evhttp_request_get_input_headers(req), "Location"); if (NULL != location) { //fprintf(stderr, "Location: %s\n", location); create_request(location); } } if (HTTP_OK == http_code) { struct evbuffer *input_buffer = evhttp_request_get_input_buffer(req); int found = 0; while ((nread = evbuffer_remove(input_buffer, buffer, sizeof (buffer))) > 0) { if (0 == search_find(&search_list, buffer, nread)) { found = 1; break; } } fprintf(stderr, "%s: %d\n", evhttp_find_header(evhttp_request_get_output_headers(req), "Host"), found); } if (--n_pending_requests == 0) event_base_loopexit(base, NULL); }
http_delivery_status_t buffer_delivery( struct evhttp_request* req, char* decoded_path) { char* my_decoded_path = NULL; char* my_client_ip = NULL; char* my_client_user_agent = NULL; buffer_xact_ctx* p_ctx = NULL; prmm_req prmm_r = NULL; int content_on_disk = 0; http_delivery_status_t stat; stat = http_delivery_internal_error; channel_info_t* p_channel; p_channel = get_channel(decoded_path); if(!p_channel) { debug_msg("channel not identified for %s", decoded_path); stat = http_delivery_not_found; return stat; /*we don't allocate anything, so just return*/ } /* * doecode_path points to memory owned by libevent, * and will be modified later, so we have keep a copy. */ my_decoded_path = strdup(decoded_path); if(!my_decoded_path) { debug_msg("strdup"); goto error; } char* host; uint16_t port; evhttp_connection_get_peer( evhttp_request_get_connection(req), &host, &port); my_client_ip = strdup(host); if(!my_client_ip) { debug_msg("strdup"); goto error; } struct evkeyvalq* input_headers; input_headers = evhttp_request_get_input_headers(req); const char* user_agent = evhttp_find_header(input_headers, "User-Agent"); if(user_agent) { my_client_user_agent = strdup(user_agent); if(!my_client_user_agent) { debug_msg("strdup"); goto error; } } p_ctx = malloc(sizeof(buffer_xact_ctx)); if(!p_ctx) { debug_msg("malloc"); goto error; } p_ctx->client_req = req; p_ctx->data_sent = 0; p_ctx->data_total = 0; p_ctx->decoded_path = my_decoded_path; p_ctx->client_ip = my_client_ip; p_ctx->client_user_agent = my_client_user_agent; prmm_conn prmm_c; prmm_c = get_prmm_conn( p_channel->channel_id, p_channel->maddress, p_channel->mport); if(!prmm_c) { debug_msg("get_prmm_conn"); goto error; } prmm_r = prmm_request_new( prmm_c, decoded_path, http_prmm_cb, p_ctx); if(!prmm_r) { debug_msg("prmm_request_new"); goto error; } p_ctx->prmm_r = prmm_r; prmm_request_stat_t prmm_stat; prmm_stat = prmm_request_start(prmm_r); switch(prmm_stat) { case prmm_request_success: break; case prmm_request_failed: debug_msg("can't get content via prmm"); stat = http_delivery_not_found; goto error; case prmm_request_on_disk: debug_msg("content has been written to disk just now,"); debug_msg("so use local delivery instead."); content_on_disk = 1; goto error; /*this isn't error actually, we just need to free memory*/ default: debug_msg("?! invalid stat: %d", stat); goto error; } /*for memory leak detect when debugging*/ debug_msg("buffer_xact_ctx[%p] created", p_ctx); return http_delivery_success; error: if(my_decoded_path) free(my_decoded_path); if(my_client_ip) free(my_client_ip); if(my_client_user_agent) free(my_client_user_agent); if(p_ctx) free(p_ctx); if(prmm_r) prmm_request_free(prmm_r); if(content_on_disk) return local_delivery(req, decoded_path); return stat; }
/** HTTP request callback */ static void http_request_cb(struct evhttp_request* req, void* arg) { // Disable reading to work around a libevent bug, fixed in 2.2.0. if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { evhttp_connection* conn = evhttp_request_get_connection(req); if (conn) { bufferevent* bev = evhttp_connection_get_bufferevent(conn); if (bev) { bufferevent_disable(bev, EV_READ); } } } std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req)); // Early address-based allow check if (!ClientAllowed(hreq->GetPeer())) { LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n", hreq->GetPeer().ToString()); hreq->WriteReply(HTTP_FORBIDDEN); return; } // Early reject unknown HTTP methods if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) { LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n", hreq->GetPeer().ToString()); hreq->WriteReply(HTTP_BADMETHOD); return; } LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n", RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToString()); // Find registered handler for prefix std::string strURI = hreq->GetURI(); std::string path; std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin(); std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end(); for (; i != iend; ++i) { bool match = false; if (i->exactMatch) match = (strURI == i->prefix); else match = (strURI.substr(0, i->prefix.size()) == i->prefix); if (match) { path = strURI.substr(i->prefix.size()); break; } } // Dispatch to worker thread if (i != iend) { std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(std::move(hreq), path, i->handler)); assert(workQueue); if (workQueue->Enqueue(item.get())) item.release(); /* if true, queue took ownership */ else { LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n"); item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded"); } } else { hreq->WriteReply(HTTP_NOTFOUND); } }