void RESTRequest::requestHeaderData() { MHD_get_connection_values( connection, MHD_HEADER_KIND, RESTRequest::iterate_headers, this); MHD_get_connection_values( connection, MHD_GET_ARGUMENT_KIND, RESTRequest::iterate_arguments, this); }
/** * @brief get_url_from_query * @param connection * @param redirect_url as plaintext - not url encoded * @param redirect_url_len * @return NULL or redirect url */ static const char *get_redirect_url(struct MHD_Connection *connection) { struct collect_query_key query_key = { .key = "redir" }; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &collect_query_key, &query_key); if (!query_key.value) return NULL; return query_key.value; } /* save the query or empty string into **query. * the call must free query later */ static int get_query(struct MHD_Connection *connection, char **query) { int element_counter; char **elements; struct collect_query collect_query; int i; int j; int length = 0; element_counter = MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, counter_iterator, NULL); if (element_counter == 0) { *query = safe_strdup(""); return 0; } elements = calloc(element_counter, sizeof(char *)); collect_query.i = 0; collect_query.elements = elements; // static int get_host_value_callback(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, collect_query_string, &collect_query); for(i=0; i<element_counter; i++) { if(!elements[i]) continue; length += strlen(elements[i]); if(i >0) /* q=foo&o=bar the '&' need also some space */ length++; } /* don't miss the zero terminator */ *query = calloc(1, length+1); for(i=0, j=0; i<element_counter; i++) { if(!elements[i]) continue; strncpy(*query + j, elements[i], length-j); free(elements[i]); } free(elements); return 0; }
/** * @brief authenticated - called for all request from authenticated clients. * @param connection * @param ip_addr * @param mac * @param url * @param client * @return * * It's unsual to received request from clients which are already authed. * Happens when the user: * - clicked in multiple windows on "accept" -> redirect to origin - no checking * - when the user reloaded a splashpage -> redirect to origin * - when a user calls deny url -> deauth it */ static int authenticated(struct MHD_Connection *connection, const char *ip_addr, const char *mac, const char *url, t_client *client) { s_config *config = config_get_config(); const char *redirect_url; const char *host = NULL; char redirect_to_us[128]; MHD_get_connection_values(connection, MHD_HEADER_KIND, get_host_value_callback, &host); if (is_splashpage(host, url) || check_authdir_match(url, config->authdir)) { redirect_url = get_redirect_url(connection); /* TODO: what should we do when we get such request? */ if (redirect_url == NULL || strlen(redirect_url) == 0) return show_splashpage(connection, client); else return authenticate_client(connection, ip_addr, mac, redirect_url, client); } else if (check_authdir_match(url, config->denydir)) { auth_client_action(ip_addr, mac, AUTH_MAKE_DEAUTHENTICATED); snprintf(redirect_to_us, 128, "http://%s:%u/", config->gw_address, config->gw_port); return send_redirect_temp(connection, redirect_to_us); } /* user doesn't wants the splashpage or tried to auth itself */ return serve_file(connection, client, url); }
//-------------------------------------------------------------------------- // Callback appelée par libmicrohttpd //-------------------------------------------------------------------------- int HTTPDServer::answer (struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { MHD_ValueKind t = MHD_GET_ARGUMENT_KIND; if (0 == strcmp (method, "GET")) t = MHD_GET_ARGUMENT_KIND; else if (0 == strcmp (method, "POST")) t = MHD_POSTDATA_KIND; else { string msg = "Method "; msg += method; msg += " is not supported"; return send (connection, msg.c_str(), 0, MHD_HTTP_BAD_REQUEST); } Message msg (url); MHD_get_connection_values (connection, t, _get_params, &msg); vector<Message*> outMsgs; if (fDebug) { cout << method << ": "; msg.print(cout); cout << endl; } fProcessor->processMessage (&msg, outMsgs); if (outMsgs.size()) send (connection, outMsgs); else page (connection, url); return MHD_YES; }
int CWebServer::GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::multimap<std::string, std::string> &headerValues) { if (connection == NULL) return -1; return MHD_get_connection_values(connection, kind, FillArgumentMultiMap, &headerValues); }
int HTTPRequestHandlerUtils::GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::multimap<std::string, std::string> &headerValues) { if (connection == nullptr) return -1; return MHD_get_connection_values(connection, kind, FillArgumentMultiMap, &headerValues); }
int ahc_echo(void* servctx, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** reqctx) { static int dummy; if (reqctx == NULL) { // The first time only the headers are valid, do not respond in the first round. *reqctx = &dummy; return MHD_YES; } ServerContext* pServCtx = (ServerContext*)servctx; // Not used yet printf("%s %s %s\n", method, url, version); if (strcmp(method, "GET") == 0) { if (*upload_data_size != 0) return MHD_NO; // No upload data expected in HTTP GET method // Parse headers puts("[HEADERS]"); MHD_get_connection_values(connection, MHD_HEADER_KIND, &print_out_key, NULL); // Parse GET parameters puts("[GET PARAMS]"); StringPairList spl; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &print_out_key, &spl); std::string text = "<html><body>\n"; text += "<p>URL="; text += url; text += "</p>\n<p>Parameters</p>\n<ul>\n"; for (StringPairList::iterator it = spl.begin(); it != spl.end(); ++it) { text += "<li>"; text += it->first; text += '='; text += it->second; text += "</li>\n"; } text += "</body></html>\n"; MHD_Response* response = MHD_create_response_from_buffer(text.size(), (void*)text.c_str(), MHD_RESPMEM_MUST_COPY); MHD_add_response_header(response, "Content-Type", "text/html"); int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); MHD_destroy_response(response); *reqctx = NULL; // clear context pointer return ret; } else return MHD_NO; // Not supported yet }
static char *http_get_header(struct MHD_Connection *connection, const char *header) { struct header_finder_t finder; finder.header = header; finder.value = NULL; MHD_get_connection_values (connection, MHD_HEADER_KIND, &get_key, &finder); return finder.value; }
static int handle_request(void* /*cls*/, MHD_Connection* connection, const char* url, const char* method, const char*, const char*, unsigned int*, void** /*con_cls*/) { if (std::string(method) != "GET") { return report_error(connection, MHD_HTTP_NOT_IMPLEMENTED, "501 Not Implemented"); } if (mp3d_debug_httpd) { MHD_get_connection_values(connection, MHD_HEADER_KIND, dump_key, NULL); MHD_get_connection_values(connection, MHD_COOKIE_KIND, dump_key, NULL); MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, dump_key, NULL); std::cout << "*** " << method << " request for '" << url << "'" << std::endl; } const char* q = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "mp3d_q"); const std::string request_url(url); if (request_url == "/") { std::string page_data; make_main_page(page_data, q); return send_string_response(connection, MHD_HTTP_OK, page_data); } else if (boost::starts_with(request_url, "/static/")) { StringStringMap::const_iterator it = static_file_map.find(request_url); if (it == static_file_map.end()) { return report_error(connection, MHD_HTTP_NOT_FOUND, "404 Not Found"); } return send_string_response(connection, MHD_HTTP_OK, it->second); } else if (boost::starts_with(request_url, "/play/")) { const size_t id = strtoul(request_url.substr(6).c_str(), 0, 10); // FIXME: error checking. play_queue.clear(); play_queue.push_back(all_mp3s[id]); // FIXME: lookup by id (don't assume id == index). return see_other(connection, "/"); } else if (boost::starts_with(request_url, "/add/")) { const size_t id = strtoul(request_url.substr(5).c_str(), 0, 10); // FIXME: error checking. play_queue.push_back(all_mp3s[id]); // FIXME: lookup by id (don't assume id == index). return see_other(connection, "/"); } else if (boost::starts_with(request_url, "/remove/")) { const size_t id = strtoul(request_url.substr(8).c_str(), 0, 10); // FIXME: error checking. play_queue.remove(id); // FIXME: is this set-like behavior the behavior we want? return see_other(connection, "/"); } else { return report_error(connection, MHD_HTTP_NOT_FOUND, "404 Not Found"); } }
int QMHDServerPrivate::onRequest(MHD_Connection* mhdConnection, const char* method, const char* path, const char* httpVersion, void** requestPtr) { QMHDRequest* request = new QMHDRequest(mhdConnection); QStringHash query; QStringHash headers; MHD_get_connection_values(mhdConnection, MHD_GET_ARGUMENT_KIND, &header_values_iterator, &query); MHD_get_connection_values(mhdConnection, MHD_HEADER_KIND, &header_values_iterator, &headers); request->setMethod(qmhd_method_from_string(method)); request->setPath(path); request->setQuery(query); request->setHttpVersion(qmhd_http_version_from_string(httpVersion)); request->setHeaders(headers); request->setResponse(new QMHDResponse(mhdConnection)); *requestPtr = request; return MHD_YES; }
static int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { printf ("New %s request for %s using version %s\n", method, url, version); MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key, NULL); return MHD_NO; }
// add security related headers and send the response on the given connection // the reference counter is not touched // this function is a wrapper around MHD_queue_response // MHD_queue_response should be replaced with this function static void secure_queue_response(MHD_Connection *connection, unsigned int status_code, struct MHD_Response* response) { // TODO: protect againts handling untrusted content to the browser // see: // http://www.dotnetnoob.com/2012/09/security-through-http-response-headers.html // http://www.w3.org/TR/CSP2/ // https://code.google.com/p/doctype-mirror/wiki/ArticleContentSniffing // check content type // don't server when no type or no whitelisted type is given // TODO sending invalid mime types is as bad as not sending them TODO /* std::vector<std::string> allowed_types; allowed_types.push_back("text/html"); allowed_types.push_back("application/json"); allowed_types.push_back("image/png"); */ const char* type = MHD_get_response_header(response, "Content-Type"); if(type == 0 /*|| std::find(allowed_types.begin(), allowed_types.end(), std::string(type)) == allowed_types.end()*/) { std::string page; if(type == 0) page = "<html><body><p>Fatal Error: no content type was set on this response. This is a bug.</p></body></html>"; else page = "<html><body><p>Fatal Error: this content type is not allowed. This is a bug.<br/> Content-Type: "+std::string(type)+"</p></body></html>"; struct MHD_Response* resp = MHD_create_response_from_data(page.size(), (void*)page.data(), 0, 1); MHD_add_response_header(resp, "Content-Type", "text/html"); MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, resp); MHD_destroy_response(resp); } // tell Internet Explorer to not do content sniffing MHD_add_response_header(response, "X-Content-Type-Options", "nosniff"); // Content security policy header, its a new technology and not implemented everywhere // get own host name as the browser sees it std::string host; MHD_get_connection_values(connection, MHD_HEADER_KIND, _extract_host_header_it_cb, (void*)&host); std::string csp; csp += "default-src 'none';"; csp += "script-src '"+host+STATIC_FILES_ENTRY_PATH+"';"; csp += "font-src '"+host+STATIC_FILES_ENTRY_PATH+"';"; csp += "img-src 'self';"; // allow images from all paths on this server csp += "media-src 'self';"; // allow media files from all paths on this server MHD_add_response_header(response, "X-Content-Security-Policy", csp.c_str()); MHD_queue_response(connection, status_code, response); }
/** * @brief preauthenticated - called for all request of a client in this state. * @param connection * @param ip_addr * @param mac * @return */ static int preauthenticated(struct MHD_Connection *connection, const char *ip_addr, const char *mac, const char *url, t_client *client) { char *host = NULL; const char *redirect_url; s_config *config = config_get_config(); if (!client) { client = add_client(ip_addr); if (!client) return send_error(connection, 503); } MHD_get_connection_values(connection, MHD_HEADER_KIND, get_host_value_callback, &host); /* check if this is a redirect querty with a foreign host as target */ if(is_foreign_hosts(connection, host)) { return redirect_to_splashpage(connection, client, host, url); } /* request is directed to us */ /* check if client wants to be authenticated */ if(check_authdir_match(url, config->authdir)) { /* Only the first request will redirected to config->redirectURL. * When the client reloads a page when it's authenticated, it should be redirected * to their origin url */ if (config->redirectURL) redirect_url = config->redirectURL; else redirect_url = get_redirect_url(connection); if (try_to_authenticate(connection, client, host, url)) { return authenticate_client(connection, ip_addr, mac, redirect_url, client); } else { /* user used an invalid token, redirect to splashpage but hold query "redir" intact */ return encode_and_redirect_to_splashpage(connection, redirect_url); } } if(is_splashpage(host, url)) { return show_splashpage(connection, client); } /* no special handling left - try to serve static content to the user */ return serve_file(connection, client, url); }
static int check_token_is_valid(struct MHD_Connection *connection, t_client *client) { /* token check */ struct collect_query_key query_key = { .key = "token" }; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &collect_query_key, &query_key); /* token not found in query string */ if (!query_key.value) return 0; /* token doesn't match */ if (strcmp(client->token, query_key.value)) return 0; return 1; } /** * @brief try_to_authenticate * @param connection * @param client * @param host * @param url * @return */ static int try_to_authenticate(struct MHD_Connection *connection, t_client *client, const char *host, const char *url) { /* a successful auth looks like * http://192.168.42.1:2050/nodogsplash_auth/?redir=http%3A%2F%2Fberlin.freifunk.net%2F&tok=94c4cdd2 * when authaction -> http://192.168.42.1:2050/nodogsplash_auth/ */ s_config *config = config_get_config(); /* we are checking here for the second '/' of /denydir/ */ if (check_authdir_match(url, config->authdir)) { /* matched to authdir */ if (check_token_is_valid(connection, client)) { return 1; /* valid token */ } } else if (check_authdir_match(url, config->denydir)) { /* matched to deauth */ /* TODO: do we need denydir? */ return 0; } return 0; }
static int ahc_echo (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **unused) { static int ptr; const char *me = cls; struct MHD_Response *response; int ret; const char *hdr; (void)version;(void)upload_data;(void)upload_data_size; /* Unused. Silent compiler warning. */ if (0 != strcmp (me, method)) return MHD_NO; /* unexpected method */ if (&ptr != *unused) { *unused = &ptr; return MHD_YES; } *unused = NULL; hdr = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "k"); if ((hdr == NULL) || (0 != strcmp (hdr, "v x"))) abort (); hdr = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "hash"); if ((hdr == NULL) || (0 != strcmp (hdr, "#foo"))) abort (); hdr = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "space"); if ((hdr == NULL) || (0 != strcmp (hdr, "\240bar"))) abort (); if (3 != MHD_get_connection_values (connection, MHD_GET_ARGUMENT_KIND, NULL, NULL)) abort (); response = MHD_create_response_from_buffer (strlen (url), (void *) url, MHD_RESPMEM_MUST_COPY); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); if (ret == MHD_NO) abort (); return ret; }
static int answer_to_connection(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { char buf[5],page[REPLY_PAGE_SIZE], *dyn_format_str = NULL; char format_str[50] = "$c:$i:$o:$p0"; struct MHD_Response *response; struct dnetc_values *shmem; int i,ret; if(strcmp(method,"GET") != 0) return MHD_NO; shmem = (struct dnetc_values *) cls; if(!shmem->running) { snprintf(page,REPLY_PAGE_SIZE,"NONE"); } else { MHD_get_connection_values (connection, MHD_GET_ARGUMENT_KIND, parse_http_get_param, &dyn_format_str); if(dyn_format_str == NULL) { for(i=1;i<shmem->n_cpu;i++) { snprintf(buf,5,":$p%d",i); strcat(format_str, buf); } sprint_formated_data(page,REPLY_PAGE_SIZE,format_str,shmem); } else { sprint_formated_data(page,REPLY_PAGE_SIZE,dyn_format_str,shmem); free(dyn_format_str); } } response = MHD_create_response_from_data (strlen(page), (void *) page, MHD_NO, MHD_YES); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return ret; }
/** * @brief authenticated - called for all request from authenticated clients. * @param connection * @param ip_addr * @param mac * @param url * @param client * @return * * It's unsual to received request from clients which are already authed. * Happens when the user: * - clicked in multiple windows on "accept" -> redirect to origin - no checking * - when the user reloaded a splashpage -> redirect to origin * - when a user calls deny url -> deauth it */ static int authenticated(struct MHD_Connection *connection, const char *ip_addr, const char *mac, const char *url, t_client *client) { s_config *config = config_get_config(); const char *redirect_url; const char *host = NULL; char redirect_to_us[128]; MHD_get_connection_values(connection, MHD_HEADER_KIND, get_host_value_callback, &host); if (is_splashpage(host, url) || check_authdir_match(url, config->authdir)) { redirect_url = get_redirect_url(connection); /* TODO: what should we do when we get such request? */ if (redirect_url == NULL || strlen(redirect_url) == 0) return show_splashpage(connection, client); else return authenticate_client(connection, ip_addr, mac, redirect_url, client); } else if (check_authdir_match(url, config->denydir)) { auth_client_action(ip_addr, mac, AUTH_MAKE_DEAUTHENTICATED); snprintf(redirect_to_us, 128, "http://%s:%u/", config->gw_address, config->gw_port); return send_redirect_temp(connection, redirect_to_us); } /* check if this is an late request meaning the user tries to get the internet, but ended up here, * because the iptables rule came to late */ if (is_foreign_hosts(connection, host)) { /* might happen if the firewall rule isn't yet installed */ return send_refresh(connection); } /* user doesn't wants the splashpage or tried to auth itself */ return serve_file(connection, client, url); }
/* **************************************************************************** * * connectionTreat - * * This is the MHD_AccessHandlerCallback function for MHD_start_daemon * This function returns: * o MHD_YES if the connection was handled successfully * o MHD_NO if the socket must be closed due to a serious error * * - This function is called once when the headers are read and the ciP is created. * - Then it is called for data payload and once all the payload an acknowledgement * must be done, setting *upload_data_size to ZERO. * - The last call is made with *upload_data_size == 0 and now is when the connection * is open to send responses. * * Call 1: *con_cls == NULL * Call 2: *con_cls != NULL AND *upload_data_size != 0 * Call 3: *con_cls != NULL AND *upload_data_size == 0 */ static int connectionTreat ( void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ) { ConnectionInfo* ciP = (ConnectionInfo*) *con_cls; size_t dataLen = *upload_data_size; // 1. First call - setup ConnectionInfo and get/check HTTP headers if (ciP == NULL) { // // IP Address and port of caller // char ip[32]; unsigned short port = 0; const union MHD_ConnectionInfo* mciP = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); if (mciP != NULL) { port = (mciP->client_addr->sa_data[0] << 8) + mciP->client_addr->sa_data[1]; snprintf(ip, sizeof(ip), "%d.%d.%d.%d", mciP->client_addr->sa_data[2] & 0xFF, mciP->client_addr->sa_data[3] & 0xFF, mciP->client_addr->sa_data[4] & 0xFF, mciP->client_addr->sa_data[5] & 0xFF); } else { port = 0; snprintf(ip, sizeof(ip), "IP unknown"); } // // ConnectionInfo // if ((ciP = new ConnectionInfo(url, method, version, connection)) == NULL) { LM_E(("Runtime Error (error allocating ConnectionInfo)")); return MHD_NO; } *con_cls = (void*) ciP; // Pointer to ConnectionInfo for subsequent calls ciP->port = port; ciP->ip = ip; // // Transaction starts here // LM_TRANSACTION_START("from", ip, port, url); // Incoming REST request starts // // URI parameters // ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = DEFAULT_PARAM_NOTIFY_FORMAT; ciP->uriParam[URI_PARAM_PAGINATION_OFFSET] = DEFAULT_PAGINATION_OFFSET; ciP->uriParam[URI_PARAM_PAGINATION_LIMIT] = DEFAULT_PAGINATION_LIMIT; ciP->uriParam[URI_PARAM_PAGINATION_DETAILS] = DEFAULT_PAGINATION_DETAILS; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, uriArgumentGet, ciP); if (ciP->httpStatusCode != SccOk) { LM_W(("Bad Input (error in URI parameters)")); restReply(ciP, ciP->answer); return MHD_YES; } LM_T(LmtUriParams, ("notifyFormat: '%s'", ciP->uriParam[URI_PARAM_NOTIFY_FORMAT].c_str())); MHD_get_connection_values(connection, MHD_HEADER_KIND, httpHeaderGet, &ciP->httpHeaders); char tenant[128]; ciP->tenantFromHttpHeader = strToLower(tenant, ciP->httpHeaders.tenant.c_str(), sizeof(tenant)); LM_T(LmtTenant, ("HTTP tenant: '%s'", ciP->httpHeaders.tenant.c_str())); ciP->outFormat = wantedOutputSupported(ciP->httpHeaders.accept, &ciP->charset); if (ciP->outFormat == NOFORMAT) ciP->outFormat = XML; // XML is default output format ciP->servicePath = ciP->httpHeaders.servicePath; if (servicePathSplit(ciP) != 0) { LM_W(("Bad Input (error in ServicePath http-header)")); restReply(ciP, ciP->answer); } if (contentTypeCheck(ciP) != 0) { LM_W(("Bad Input (invalid mime-type in Content-Type http-header)")); restReply(ciP, ciP->answer); } else if (outFormatCheck(ciP) != 0) { LM_W(("Bad Input (invalid mime-type in Accept http-header)")); restReply(ciP, ciP->answer); } else ciP->inFormat = formatParse(ciP->httpHeaders.contentType, NULL); // Set default mime-type for notifications if (ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] == "") { if (ciP->outFormat == XML) ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "XML"; else if (ciP->outFormat == JSON) ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "JSON"; else ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "XML"; LM_T(LmtUriParams, ("'default' value for notifyFormat (ciP->outFormat == %d)): '%s'", ciP->outFormat, ciP->uriParam[URI_PARAM_NOTIFY_FORMAT].c_str())); } return MHD_YES; } // // 2. Data gathering calls // // 2-1. Data gathering calls, just wait // 2-2. Last data gathering call, acknowledge the receipt of data // if (dataLen != 0) { if (dataLen == ciP->httpHeaders.contentLength) { if (ciP->httpHeaders.contentLength <= PAYLOAD_MAX_SIZE) { if (ciP->httpHeaders.contentLength > STATIC_BUFFER_SIZE) ciP->payload = (char*) malloc(ciP->httpHeaders.contentLength + 1); else ciP->payload = static_buffer; ciP->payloadSize = dataLen; memcpy(ciP->payload, upload_data, dataLen); ciP->payload[dataLen] = 0; } else { char details[256]; snprintf(details, sizeof(details), "payload size: %d, max size supported: %d", ciP->httpHeaders.contentLength, PAYLOAD_MAX_SIZE); ciP->answer = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->url, SccRequestEntityTooLarge, details); ciP->httpStatusCode = SccRequestEntityTooLarge; } // All payload received, acknowledge! *upload_data_size = 0; } else LM_T(LmtPartialPayload, ("Got %d of payload of %d bytes", dataLen, ciP->httpHeaders.contentLength)); return MHD_YES; } // 3. Finally, serve the request (unless an error has occurred) if (((ciP->verb == POST) || (ciP->verb == PUT)) && (ciP->httpHeaders.contentLength == 0) && (strncasecmp(ciP->url.c_str(), "/log/", 5) != 0)) { std::string errorMsg = restErrorReplyGet(ciP, ciP->outFormat, "", url, SccLengthRequired, "Zero/No Content-Length in PUT/POST request"); ciP->httpStatusCode = SccLengthRequired; restReply(ciP, errorMsg); } else if (ciP->answer != "") restReply(ciP, ciP->answer); else serveFunction(ciP); return MHD_YES; }
static int write_to_riemann(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { int marker, ret; riemann_client_t *riemann_client = cls; if (*ptr != &marker || *upload_data_size != 0) { *ptr = ▮ *upload_data_size = 0; return MHD_YES; } { riemann_message_t msg = RIEMANN_MSG_INIT; riemann_event_t event = RIEMANN_EVENT_INIT; riemann_event_t *events[] = { NULL }; attribute_list_t *head, *cur; int header_count = 2, i; riemann_attribute_pairs_t *attributes; events[0] = &event; riemann_event_set_host(&event, riemann_field_host); riemann_event_set_service(&event, riemann_field_service); head = push_riemann_attribute_pair(NULL, "Method", method); if (head == NULL) { return MHD_NO; } cur = push_riemann_attribute_pair(head, "URL", url); if (cur == NULL) { free(head->attr); free(head); return MHD_NO; } header_count += MHD_get_connection_values(connection, MHD_HEADER_KIND, headers_to_attributes, &cur); attributes = calloc(header_count, sizeof(riemann_attribute_pairs_t)); if (attributes == NULL) { cur = head; while (cur) { free(cur->attr); head = cur; free(cur); cur = head->next; } return MHD_NO; } cur = head; for (i = 0; i < header_count && cur; ++i) { attributes[i].key = cur->attr->key; attributes[i].value = cur->attr->value; free(cur->attr); head = cur->next; free(cur); cur = head; } riemann_event_set_attributes(&event, attributes, header_count); riemann_message_set_events(&msg, events, 1); ret = riemann_client_send_message(riemann_client, &msg, 0, NULL); if (ret) { fprintf(stderr, "Cannot send message: %s\n", strerror(errno)); } fprintf(stderr, "Notfied riemann about '%s'\n", url); riemann_event_free(&event); free(attributes); } { struct MHD_Response *response; response = MHD_create_response_from_buffer(body_len, (void *)body, MHD_RESPMEM_PERSISTENT); if (response) { MHD_add_response_header(response, "Content-Type", "text/html"); ret = MHD_queue_response(connection, MHD_HTTP_OK, response); MHD_destroy_response(response); } } (void)version; (void)upload_data; return ret; }
static int url_handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { char *me; struct MHD_Response *response; int ret; std::map<std::string, std::string> url_args; std::string respdata; // HTTP access control (CORS) // https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control // some times a preflight check is required which uses the OPTIONS HTTP method to check for permission to // call cross-domain requests if (0 == strcmp(method, MHD_HTTP_METHOD_OPTIONS)) return sendAccessControl(connection, url, method, version); // we only need to deal with GET requests /* FIXME -- don't need this since the HTTP options/preflight will ensure non-supported methods are rejected? if (0 != strcmp(method, MHD_HTTP_METHOD_GET)) return MHD_NO;*/ // set up out connection information on the first pass through. if (NULL == *con_cls) { struct connection_info *con_info; con_info = (connection_info*) malloc (sizeof (struct connection_info)); if (NULL == con_info) return MHD_NO; static int idCounter = 1; con_info->id = idCounter++; // FIXME: for some reason this line causes segfaults under linux? //con_info->url = std::string(url ? url : ""); con_info->data = NULL; con_info->biomapsId = NULL; con_info->dataSize = 0; if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) { std::cout << "Setting up con_cls for POST: " << con_info->id << std::endl; std::cout << " - with url: " << url << std::endl; const char* tmp = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-type"); std::string contentType(tmp ? tmp : ""); if (contentType.find(JSON_CONTENT_TYPE) == std::string::npos) { std::cerr << "Error creating POST processor?! Unhandled content type: " << MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-type") << std::endl; free (con_info); return MHD_NO; } con_info->connectiontype = POST; } else con_info->connectiontype = GET; *con_cls = (void *) con_info; return MHD_YES; } // intercept POST requests for now to test stuff if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) { // post recieved, do stuff. struct connection_info *con_info = (connection_info*)(*con_cls); std::cout << "Received a POST for connection: " << con_info->id << std::endl; if (*upload_data_size != 0) { std::cout << "Processed some data: " << *upload_data_size << std::endl; //std::cout << "Data: " << upload_data << std::endl; con_info->data = (char*)realloc(con_info->data, con_info->dataSize + *upload_data_size); memcpy(con_info->data + con_info->dataSize, upload_data, *upload_data_size); con_info->dataSize += *upload_data_size; //std::string bob(upload_data, *upload_data_size); //con_info->data += bob.c_str(); //std::cout << "con_info->data: " << con_info->data << std::endl; *upload_data_size = 0; // set to 0 to indicate all data considered/handled. return MHD_YES; } else { } } else if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) { if (MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, get_url_args, &url_args) < 0) { return MHD_NO; } API api; respdata = api.executeAPI(url, url_args); } if (respdata == "BAD RESPONSE") { return send_bad_response(connection); } //val = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "q"); me = (char *) malloc(respdata.size() + 1); if (me == 0) return MHD_NO; strncpy(me, respdata.c_str(), respdata.size() + 1); response = MHD_create_response_from_buffer(strlen(me), me, MHD_RESPMEM_MUST_FREE); if (response == 0) { free(me); return MHD_NO; } /*it = url_args.find("type"); if (it != url_args.end() && strcasecmp(it->second.c_str(), "xml") == 0) type = typexml;*/ MHD_add_response_header(response, "Content-Type", "application/json"); MHD_add_response_header(response, "Content-Range", "items 0-5/5"); MHD_add_response_header(response, "Access-Control-Allow-Origin", "*"); // need to make sure we always expose the (non-simple) headers that we use MHD_add_response_header(response, "Access-Control-Expose-Headers", "Content-Range"); //MHD_add_response_header(response, "OurHeader", type); ret = MHD_queue_response(connection, MHD_HTTP_OK, response); MHD_destroy_response(response); return ret; }
int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { str page = {NULL, 0}; struct MHD_Response *response; int ret; void *async_data = NULL; struct httpd_cb *cb; const char *normalised_url; struct post_request *pr; str_str_t *kv; char *p; int cnt_type = HTTPD_STD_CNT_TYPE; int accept_type = HTTPD_STD_CNT_TYPE; int ret_code = MHD_HTTP_OK; LM_DBG("START *** cls=%p, connection=%p, url=%s, method=%s, " "versio=%s, upload_data[%zu]=%p, *con_cls=%p\n", cls, connection, url, method, version, *upload_data_size, upload_data, *con_cls); pr = *con_cls; if(pr == NULL){ pr = pkg_malloc(sizeof(struct post_request)); if(pr==NULL) { LM_ERR("oom while allocating post_request structure\n"); return MHD_NO; } memset(pr, 0, sizeof(struct post_request)); *con_cls = pr; pr = NULL; } if(strncmp(method, "POST", 4)==0) { if(pr == NULL){ pr = *con_cls; pr->p_list = slinkedl_init(&httpd_alloc, &httpd_free); if (pr->p_list==NULL) { LM_ERR("oom while allocating list\n"); return MHD_NO; } LM_DBG("running MHD_create_post_processor\n"); pr->pp = MHD_create_post_processor(connection, post_buf_size, &post_iterator, pr); if(pr->pp==NULL) { if (*upload_data_size == 0) { /* We need to wait for morte data before * handling the POST request */ return MHD_YES; } LM_DBG("NOT a regular POST :o)\n"); if (pr->content_type==0 && pr->content_len==0) MHD_get_connection_values(connection, MHD_HEADER_KIND, &getConnectionHeader, pr); if (pr->content_type<=0 || pr->content_len<=0) { LM_ERR("got a bogus request\n"); return MHD_NO; } if (*upload_data_size != pr->content_len) { /* For now, we don't support large POST with truncated data */ LM_ERR("got a truncated POST request\n"); return MHD_NO; } LM_DBG("got ContentType [%d] with len [%d]: %.*s\\n", pr->content_type, pr->content_len, (int)*upload_data_size, upload_data); /* Here we save data. */ switch (pr->content_type) { case HTTPD_TEXT_XML_CNT_TYPE: case HTTPD_APPLICATION_JSON_CNT_TYPE: /* Save the entire body as 'body' */ kv = (str_str_t*)slinkedl_append(pr->p_list, sizeof(str_str_t) + 1 + *upload_data_size); p = (char*)(kv + 1); kv->key.len = 1; kv->key.s = p; memcpy(p, "1", 1); p += 1; kv->val.len = *upload_data_size; kv->val.s = p; memcpy(p, upload_data, *upload_data_size); break; default: LM_ERR("Unhandled data for ContentType [%d]\n", pr->content_type); return MHD_NO; } /* Mark the fact that we consumed all data */ *upload_data_size = 0; return MHD_YES; } LM_DBG("pr=[%p] pp=[%p] p_list=[%p]\n", pr, pr->pp, pr->p_list); return MHD_YES; } else { if (pr->pp==NULL) { if (*upload_data_size == 0) { if (pr->content_type==HTTPD_TEXT_XML_CNT_TYPE) cnt_type = HTTPD_TEXT_XML_CNT_TYPE; if (pr->content_type==HTTPD_APPLICATION_JSON_CNT_TYPE) cnt_type = HTTPD_APPLICATION_JSON_CNT_TYPE; *con_cls = pr->p_list; cb = get_httpd_cb(url); if (cb) { normalised_url = &url[cb->http_root->len+1]; LM_DBG("normalised_url=[%s]\n", normalised_url); ret_code = cb->callback(cls, (void*)connection, normalised_url, method, version, upload_data, upload_data_size, con_cls, &buffer, &page); } else { page = MI_HTTP_U_URL; ret_code = MHD_HTTP_BAD_REQUEST; } /* slinkedl_traverse(pr->p_list, &httpd_print_data, NULL, NULL); */ slinkedl_list_destroy(*con_cls); pkg_free(pr); *con_cls = pr = NULL; goto send_response; } LM_DBG("NOT a regular POST :o)\n"); if (pr->content_type==0 && pr->content_len==0) MHD_get_connection_values(connection, MHD_HEADER_KIND, &getConnectionHeader, pr); if (pr->content_type<=0 || pr->content_len<=0) { LM_ERR("got a bogus request\n"); return MHD_NO; } if (*upload_data_size != pr->content_len) { /* For now, we don't support large POST with truncated data */ LM_ERR("got a truncated POST request\n"); return MHD_NO; } LM_DBG("got ContentType [%d] with len [%d]: %.*s\\n", pr->content_type, pr->content_len, (int)*upload_data_size, upload_data); /* Here we save data. */ switch (pr->content_type) { case HTTPD_TEXT_XML_CNT_TYPE: case HTTPD_APPLICATION_JSON_CNT_TYPE: /* Save the entire body as 'body' */ kv = (str_str_t*)slinkedl_append(pr->p_list, sizeof(str_str_t) + 1 + *upload_data_size); p = (char*)(kv + 1); kv->key.len = 1; kv->key.s = p; memcpy(p, "1", 1); p += 1; kv->val.len = *upload_data_size; kv->val.s = p; memcpy(p, upload_data, *upload_data_size); break; default: LM_ERR("Unhandled data for ContentType [%d]\n", pr->content_type); return MHD_NO; } /* Mark the fact that we consumed all data */ *upload_data_size = 0; return MHD_YES; } LM_DBG("running MHD_post_process: " "pp=%p status=%d upload_data_size=%zu\n", pr->pp, pr->status, *upload_data_size); if (pr->status<0) { slinkedl_list_destroy(pr->p_list); pr->p_list = NULL; /* FIXME: * It might be better to reply with an error * instead of resetting the connection via MHD_NO */ return MHD_NO; } ret =MHD_post_process(pr->pp, upload_data, *upload_data_size); LM_DBG("ret=%d upload_data_size=%zu\n", ret, *upload_data_size); if(*upload_data_size != 0) { *upload_data_size = 0; return MHD_YES; } LM_DBG("running MHD_destroy_post_processor: " "pr=[%p] pp=[%p] p_list=[%p]\n", pr, pr->pp, pr->p_list); MHD_destroy_post_processor(pr->pp); LM_DBG("done MHD_destroy_post_processor\n"); /* slinkedl_traverse(pr->p_list, &httpd_print_data, NULL, NULL); */ *con_cls = pr->p_list; cb = get_httpd_cb(url); if (cb) { normalised_url = &url[cb->http_root->len+1]; LM_DBG("normalised_url=[%s]\n", normalised_url); ret_code = cb->callback(cls, (void*)connection, normalised_url, method, version, upload_data, upload_data_size, con_cls, &buffer, &page); } else { page = MI_HTTP_U_URL; ret_code = MHD_HTTP_BAD_REQUEST; } /* slinkedl_traverse(pr->p_list, &httpd_print_data, NULL, NULL); */ slinkedl_list_destroy(*con_cls); pkg_free(pr); *con_cls = pr = NULL; } }else if(strncmp(method, "GET", 3)==0) { pr = *con_cls; MHD_get_connection_values(connection, MHD_HEADER_KIND, &getConnectionHeader, pr); accept_type = pr->accept_type; pkg_free(pr); *con_cls = pr = NULL; LM_DBG("accept_type=[%d]\n", accept_type); cb = get_httpd_cb(url); if (cb) { normalised_url = &url[cb->http_root->len+1]; LM_DBG("normalised_url=[%s]\n", normalised_url); ret_code = cb->callback(cls, (void*)connection, normalised_url, method, version, upload_data, upload_data_size, con_cls, &buffer, &page); } else { page = MI_HTTP_U_URL; ret_code = MHD_HTTP_BAD_REQUEST; } }else{ page = MI_HTTP_U_METHOD; ret_code = MHD_HTTP_METHOD_NOT_ACCEPTABLE; } send_response: if (page.s) { LM_DBG("MHD_create_response_from_data [%p:%d]\n", page.s, page.len); response = MHD_create_response_from_data(page.len, (void*)page.s, 0, 1); } else { LM_DBG("MHD_create_response_from_callback\n"); response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, buffer.len, cb->flush_data_callback, (void*)async_data, NULL); } if (cnt_type==HTTPD_TEXT_XML_CNT_TYPE || accept_type==HTTPD_TEXT_XML_CNT_TYPE) MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/xml; charset=utf-8"); if (cnt_type==HTTPD_APPLICATION_JSON_CNT_TYPE || accept_type==HTTPD_APPLICATION_JSON_CNT_TYPE) MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "application/json"); ret = MHD_queue_response (connection, ret_code, response); MHD_destroy_response (response); return ret; }
static int ahc_echo (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **unused) { static int ptr; const char *me = cls; struct MHD_Response *response; int ret; const char *hdr; if (0 != strcmp (me, method)) return MHD_NO; /* unexpected method */ if (&ptr != *unused) { *unused = &ptr; return MHD_YES; } *unused = NULL; ret = 0; MHD_get_connection_values (connection, MHD_HEADER_KIND, &kv_cb, &ret); if (ret != 1) abort (); hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "NotFound"); if (hdr != NULL) abort (); hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); if ((hdr == NULL) || (0 != strcmp (hdr, "*/*"))) abort (); hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST); if ((hdr == NULL) || (0 != strcmp (hdr, "127.0.0.1:21080"))) abort (); MHD_set_connection_value (connection, MHD_HEADER_KIND, "FakeHeader", "NowPresent"); hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "FakeHeader"); if ((hdr == NULL) || (0 != strcmp (hdr, "NowPresent"))) abort (); response = MHD_create_response_from_buffer (strlen (url), (void *) url, MHD_RESPMEM_MUST_COPY); MHD_add_response_header (response, "MyHeader", "MyValue"); hdr = MHD_get_response_header (response, "MyHeader"); if (0 != strcmp ("MyValue", hdr)) abort (); MHD_add_response_header (response, "MyHeader", "MyValueToo"); if (MHD_YES != MHD_del_response_header (response, "MyHeader", "MyValue")) abort (); hdr = MHD_get_response_header (response, "MyHeader"); if (0 != strcmp ("MyValueToo", hdr)) abort (); if (1 != MHD_get_response_headers (response, NULL, NULL)) abort (); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); if (ret == MHD_NO) abort (); return ret; }
int http_controller_request(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { char *page = NULL; if(!strcmp(method, "GET")) { if(!strcmp(url, "/")) page = http_get_index(); else if (!strcmp(url, "/playlist")) page = http_get_playlist(); else if (!strcmp(url, "/status")) page = http_get_status(); else if (!strcmp(url, "/current")) page = http_get_current(); else if (!strncmp(url, "/playlist/", 10)) { char *request = strdup(url); page = http_get_playlist_item(request); free(request); } } else if(!strcmp(method, "POST")) { //Calculate content_length int content_length = 0; MHD_get_connection_values(connection, MHD_HEADER_KIND, http_controller_headers, &content_length); //We need more post data if(content_length > *upload_data_size) return MHD_YES; //Create string from upload_data char *strdata = malloc(content_length + 1); strncpy(strdata, upload_data, content_length); strdata[content_length] = 0; //Parse JSON json_value *data = json_parse(strdata); free(strdata); strdata = NULL; //Ignore malformed JSON if(data) { //Execute commands if(!strcmp(url, "/status")) page = http_post_status(data); else if(!strcmp(url, "/current")) page = http_post_current(data); else if(!strcmp(url, "/playlist")) page = http_post_playlist(data); else if (!strncmp(url, "/playlist/", 10)) { char *request = strdup(url); page = http_post_playlist_item(request, data); free(request); } json_value_free(data); gui_update_playlist(); } } else if(!strcmp(method, "DELETE")) { if(!strcmp(url, "/playlist")) http_delete_playlist(); else if(!strncmp(url, "/playlist/", 10)) { char *request = strdup(url); page = http_delete_playlist_item(request); free(request); } gui_update_playlist(); } if(page == NULL) page = strdup("404 Not Found!"); struct MHD_Response *response; response = MHD_create_response_from_data (strlen (page), (void*) page, true, false); int ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return ret; }
/* **************************************************************************** * * connectionTreat - * * This is the MHD_AccessHandlerCallback function for MHD_start_daemon * This function returns: * o MHD_YES if the connection was handled successfully * o MHD_NO if the socket must be closed due to a serious error * * - This function is called once when the headers are read and the ciP is created. * - Then it is called for data payload and once all the payload an acknowledgement * must be done, setting *upload_data_size to ZERO. * - The last call is made with *upload_data_size == 0 and now is when the connection * is open to send responses. * * Call 1: *con_cls == NULL * Call 2: *con_cls != NULL AND *upload_data_size != 0 * Call 3: *con_cls != NULL AND *upload_data_size == 0 */ static int connectionTreat ( void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ) { ConnectionInfo* ciP = (ConnectionInfo*) *con_cls; size_t dataLen = *upload_data_size; // 1. First call - setup ConnectionInfo and get/check HTTP headers if (ciP == NULL) { if ((ciP = new ConnectionInfo(url, method, version, connection)) == NULL) LM_RE(MHD_NO, ("Error allocating ConnectionInfo")); *con_cls = (void*) ciP; // Pointer to ConnectionInfo for subsequent calls MHD_get_connection_values(connection, MHD_HEADER_KIND, httpHeaderGet, &ciP->httpHeaders); ciP->outFormat = wantedOutputSupported(ciP->httpHeaders.accept, &ciP->charset); if (ciP->outFormat == NOFORMAT) ciP->outFormat = XML; // XML is default output format if (contentTypeCheck(ciP) != 0) { LM_W(("Error in Content-Type")); restReply(ciP, ciP->answer); } else if (outFormatCheck(ciP) != 0) { LM_W(("Bad Accepted Out-Format (in Accept header)")); restReply(ciP, ciP->answer); } else ciP->inFormat = formatParse(ciP->httpHeaders.contentType, NULL); return MHD_YES; } // // 2. Data gathering calls // // 2-1. Data gathering calls, just wait // 2-2. Last data gathering call, acknowledge the receipt of data // if (dataLen != 0) { if (dataLen == ciP->httpHeaders.contentLength) { if (ciP->httpHeaders.contentLength <= PAYLOAD_MAX_SIZE) { if (ciP->httpHeaders.contentLength > STATIC_BUFFER_SIZE) ciP->payload = (char*) malloc(ciP->httpHeaders.contentLength); else ciP->payload = static_buffer; ciP->payloadSize = dataLen; memcpy(ciP->payload, upload_data, dataLen); ciP->payload[dataLen] = 0; } else { char details[256]; snprintf(details, sizeof(details), "payload size: %d, max size supported: %d", ciP->httpHeaders.contentLength, PAYLOAD_MAX_SIZE); ciP->answer = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->url, SccRequestEntityTooLarge, details); ciP->httpStatusCode = SccRequestEntityTooLarge; } // All payload received, acknowledge! *upload_data_size = 0; } else LM_T(LmtPartialPayload, ("Got %d of payload of %d bytes", dataLen, ciP->httpHeaders.contentLength)); return MHD_YES; } // 3. Finally, serve the request (unless an error has occurred) if (((ciP->verb == POST) || (ciP->verb == PUT)) && (ciP->httpHeaders.contentLength == 0)) { std::string errorMsg = restErrorReplyGet(ciP, ciP->outFormat, "", url, SccLengthRequired, "Zero/No Content-Length in PUT/POST request"); ciP->httpStatusCode = SccLengthRequired; restReply(ciP, errorMsg); } else if (ciP->answer != "") restReply(ciP, ciP->answer); else serveFunction(ciP); return MHD_YES; }
static int OnHttpRequest(void* cls, struct MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** ptr) { if (!*ptr) { *ptr = new std::string; return MHD_YES; } std::string* str = (std::string*) *ptr; if (strcmp(method, "POST") == 0 && *upload_data_size) { *upload_data_size = 0; (*str) += upload_data; return MHD_YES; } CLog::Log(LOGDEBUG,"OnHttpRequest - enter function [url=%s][method=%s][version=%s][upload_data=%s][upload_data_size=%lu] (hsrv)",url,method,version,upload_data,*upload_data_size); CHttpServer* httpServer = (CHttpServer*)cls; if (!httpServer) { CLog::Log(LOGERROR,"OnHttpRequest - FAILED to get server context (hsrv)"); return MHD_NO; } MAPHTTPHEADER header; MHD_get_connection_values(connection, MHD_HEADER_KIND, HeadersIterator, &header); #if 0 std::map<char*, char*>::const_iterator end = headerParams.end(); for (std::map<char*, char*>::const_iterator it = headerParams.begin(); it != end; ++it) { std::cout << "Key: " << it->first; std::cout << "Value: " << it->second << '\n'; } #endif CHttpRequest httpRequest((char*) method, (char*) version, (char*) url, header, str->size(), (char*) str->c_str()); CHttpResponse httpResponse; CLog::Log(LOGDEBUG,"OnHttpRequest - BEFORE HandleRequest with [method=%s][version=%s][url=%s][str=%s][strSize=%lu] (hsrv)",method,version,url,str->c_str(),str->size()); httpServer->HandleRequest(httpRequest, httpResponse); CLog::Log(LOGDEBUG,"OnHttpRequest - AFTER HandleRequest with [method=%s][version=%s][url=%s][str=%s][strSize=%lu]. [IsChunked=%d] (hsrv)",method,version,url,str->c_str(),str->size(),httpResponse.IsChunked()); struct MHD_Response* response; if (httpResponse.IsChunked()) { CStdString deviceId = ""; IMAPHTTPHEADER it = header.find("X-Boxee-Device-ID"); if (it == header.end()) { CLog::Log(LOGWARNING,"OnHttpRequest - FAILED to get X-Boxee-Device-ID from header for chunked request (hsrv)"); } else { deviceId = it->second; } CReaderCallback* readerCls = new CReaderCallback(deviceId, CStdString(url)); readerCls->chunkedCB = httpResponse.GetChunkedCallback(); CLog::Log(LOGDEBUG,"OnHttpRequest - going to response_from_callback. [method=%s][version=%s][url=%s][str=%s][strSize=%lu]. [IsChunked=%d] (hsrv)",method,version,url,str->c_str(),str->size(),httpResponse.IsChunked()); response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 65535, readerCallback, readerCls, NULL); if (MHD_add_response_header(response, "Transfer-Encoding", "chunked") == MHD_NO) { CLog::Log(LOGERROR,"OnHttpRequest - FAILED to add server to header"); return MHD_NO; } } else { unsigned char* bodyPtr = &(httpResponse.GetBody()[0]); response = MHD_create_response_from_buffer(httpResponse.GetBody().size(), bodyPtr, MHD_RESPMEM_MUST_COPY); } if (!response) { CLog::Log(LOGERROR,"OnHttpRequest - FAILED to create response. [%s] (hsrv)",method); return MHD_NO; } if (MHD_add_response_header(response, "Server", httpServer->GetServerAgent().c_str()) == MHD_NO) { CLog::Log(LOGERROR,"OnHttpRequest - FAILED to add server to header"); return MHD_NO; } std::map<std::string, std::string>::iterator it = httpResponse.GetHeader().begin(); while (it != httpResponse.GetHeader().end()) { if (MHD_add_response_header(response, it->first.c_str(), it->second.c_str()) == MHD_NO) { CLog::Log(LOGERROR,"OnHttpRequest - FAILED to add response header [%s=%s] (hsrv)", it->first.c_str(), it->second.c_str()); } it++; } CLog::Log(LOGDEBUG,"OnHttpRequest - going to queue response. [code=%d][bodySize=%lu] (hsrv)",httpResponse.GetCode(), httpResponse.GetBody().size()); int retVal = MHD_YES; if (MHD_queue_response(connection, httpResponse.GetCode(), response) != MHD_YES) { CLog::Log(LOGERROR,"OnHttpRequest - FAILED to queue response. [%s] (hsrv)",method); retVal = MHD_NO; } MHD_destroy_response(response); delete str; return retVal; }
/** * Prints everything in the header of a connection * @param connection the connection that we want to print all headers. */ static void print_headers(struct MHD_Connection *connection) { fprintf(stdout, _("Headers for this connection are:\n")); MHD_get_connection_values(connection, MHD_HEADER_KIND, &print_out_key, NULL); }
int httpd_callback (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { struct MHD_Response *response; int ret, timeout; while(! page_delivered) { if (avr_failure == 1) { waiting_for_response = 0; page_delivered = 1; return MHD_NO; } printf("HTTPD_CALLBACK: F*****g waiting for page to be delivered\n"); sleep(1); } printf ("HTTPD_CALLBACK: Starting HTTP Callback for new %s request for %s using version %s\n", method, url, version); memset(&page[0], 0, 65535); if (! MHD_get_connection_values (connection, MHD_GET_ARGUMENT_KIND, &parse_get_args, NULL)) { if (strncmp(url, "/\0", 2) == 0) { url = "/index.html"; } char httpd_file_path[256] = HTTPD_ROOT; strncat(httpd_file_path, url, 256); struct stat sb; if (stat(httpd_file_path, &sb) == -1) { fprintf(stderr, "HTTPD_CALLBACK: stat error: %s\n", httpd_file_path); return MHD_NO; } if ((sb.st_mode & S_IFMT) == S_IFREG) { fprintf(stderr, "HTTPD_CALLBACK: delivering %s\n", httpd_file_path); int pageIdx = 0; char inChar; FILE *fp; fp = fopen(httpd_file_path, "r"); while (inChar != EOF) { inChar = fgetc(fp); if (inChar != EOF) { page[pageIdx++] = inChar; } } fclose(fp); } else { // 404 fprintf(stderr, "HTTPD_CALLBACK: not a regular file: %s 0x%x != 0x%x\n", httpd_file_path, sb.st_mode & S_IFMT, S_IFREG); return MHD_NO; } } printf("HTTPD_CALLBACK: delivering page\n"); response = MHD_create_response_from_data(strlen(page), (void*)page, 0, 0); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); page_delivered = 1; return ret; }
static int connectionTreat ( void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ) { ConnectionInfo* ciP = (ConnectionInfo*) *con_cls; size_t dataLen = *upload_data_size; // 1. First call - setup ConnectionInfo and get/check HTTP headers if (ciP == NULL) { // // First thing to do on a new connection, set correlator to N/A. // After reading HTTP headers, the correlator id either changes due to encountering a // Fiware-Correlator HTTP Header, or, if no HTTP header with Fiware-Correlator is found, // a new correlator is generated. // correlatorIdSet("N/A"); // // IP Address and port of caller // char ip[32]; unsigned short port = 0; const union MHD_ConnectionInfo* mciP = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); if (mciP != NULL) { struct sockaddr* addr = (struct sockaddr*) mciP->client_addr; port = (addr->sa_data[0] << 8) + addr->sa_data[1]; snprintf(ip, sizeof(ip), "%d.%d.%d.%d", addr->sa_data[2] & 0xFF, addr->sa_data[3] & 0xFF, addr->sa_data[4] & 0xFF, addr->sa_data[5] & 0xFF); snprintf(clientIp, sizeof(clientIp), "%s", ip); } else { port = 0; snprintf(ip, sizeof(ip), "IP unknown"); } // // Reset time measuring? // if (timingStatistics) { memset(&threadLastTimeStat, 0, sizeof(threadLastTimeStat)); } // // ConnectionInfo // // FIXME P1: ConnectionInfo could be a thread variable (like the static_buffer), // as long as it is properly cleaned up between calls. // We would save the call to new/free for each and every request. // Once we *really* look to scratch some efficiency, this change should be made. // // Also, is ciP->ip really used? // if ((ciP = new ConnectionInfo(url, method, version, connection)) == NULL) { LM_E(("Runtime Error (error allocating ConnectionInfo)")); return MHD_NO; } if (timingStatistics) { clock_gettime(CLOCK_REALTIME, &ciP->reqStartTime); } LM_T(LmtRequest, ("")); // WARNING: This log message below is crucial for the correct function of the Behave tests - CANNOT BE REMOVED LM_T(LmtRequest, ("--------------------- Serving request %s %s -----------------", method, url)); *con_cls = (void*) ciP; // Pointer to ConnectionInfo for subsequent calls ciP->port = port; ciP->ip = ip; ciP->callNo = reqNo; ++reqNo; // // URI parameters // // FIXME P1: We might not want to do all these assignments, they are not used in all requests ... // Once we *really* look to scratch some efficiency, this change should be made. // ciP->uriParam[URI_PARAM_PAGINATION_OFFSET] = DEFAULT_PAGINATION_OFFSET; ciP->uriParam[URI_PARAM_PAGINATION_LIMIT] = DEFAULT_PAGINATION_LIMIT; ciP->uriParam[URI_PARAM_PAGINATION_DETAILS] = DEFAULT_PAGINATION_DETAILS; MHD_get_connection_values(connection, MHD_HEADER_KIND, httpHeaderGet, &ciP->httpHeaders); char correlator[CORRELATOR_ID_SIZE + 1]; if (ciP->httpHeaders.correlator == "") { correlatorGenerate(correlator); ciP->httpHeaders.correlator = correlator; } correlatorIdSet(ciP->httpHeaders.correlator.c_str()); ciP->httpHeader.push_back("Fiware-Correlator"); ciP->httpHeaderValue.push_back(ciP->httpHeaders.correlator); // // Transaction starts here // lmTransactionStart("from", ip, port, url); // Incoming REST request starts /* X-Forwared-For (used by a potential proxy on top of Orion) overrides ip */ if (ciP->httpHeaders.xforwardedFor == "") { lmTransactionSetFrom(ip); } else { lmTransactionSetFrom(ciP->httpHeaders.xforwardedFor.c_str()); } ciP->apiVersion = apiVersionGet(ciP->url.c_str()); char tenant[SERVICE_NAME_MAX_LEN + 1]; ciP->tenantFromHttpHeader = strToLower(tenant, ciP->httpHeaders.tenant.c_str(), sizeof(tenant)); ciP->outMimeType = wantedOutputSupported(ciP->apiVersion, ciP->httpHeaders.accept, &ciP->charset); if (ciP->outMimeType == NOMIMETYPE) { ciP->outMimeType = JSON; // JSON is default output mimeType } MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, uriArgumentGet, ciP); return MHD_YES; } // // 2. Data gathering calls // // 2-1. Data gathering calls, just wait // 2-2. Last data gathering call, acknowledge the receipt of data // if (dataLen != 0) { // // If the HTTP header says the request is bigger than our PAYLOAD_MAX_SIZE, // just silently "eat" the entire message // if (ciP->httpHeaders.contentLength > PAYLOAD_MAX_SIZE) { *upload_data_size = 0; return MHD_YES; } // // First call with payload - use the thread variable "static_buffer" if possible, // otherwise allocate a bigger buffer // // FIXME P1: This could be done in "Part I" instead, saving an "if" for each "Part II" call // Once we *really* look to scratch some efficiency, this change should be made. // if (ciP->payloadSize == 0) // First call with payload { if (ciP->httpHeaders.contentLength > STATIC_BUFFER_SIZE) { ciP->payload = (char*) malloc(ciP->httpHeaders.contentLength + 1); } else { ciP->payload = static_buffer; } } // Copy the chunk LM_T(LmtPartialPayload, ("Got %d of payload of %d bytes", dataLen, ciP->httpHeaders.contentLength)); memcpy(&ciP->payload[ciP->payloadSize], upload_data, dataLen); // Add to the size of the accumulated read buffer ciP->payloadSize += *upload_data_size; // Zero-terminate the payload ciP->payload[ciP->payloadSize] = 0; // Acknowledge the data and return *upload_data_size = 0; return MHD_YES; } // // 3. Finally, serve the request (unless an error has occurred) // // URL and headers checks are delayed to the "third" MHD call, as no // errors can be sent before all the request has been read // if (urlCheck(ciP, ciP->url) == false) { alarmMgr.badInput(clientIp, "error in URI path"); restReply(ciP, ciP->answer); } ciP->servicePath = ciP->httpHeaders.servicePath; lmTransactionSetSubservice(ciP->servicePath.c_str()); if (servicePathSplit(ciP) != 0) { alarmMgr.badInput(clientIp, "error in ServicePath http-header"); restReply(ciP, ciP->answer); } if (contentTypeCheck(ciP) != 0) { alarmMgr.badInput(clientIp, "invalid mime-type in Content-Type http-header"); restReply(ciP, ciP->answer); } else if (outMimeTypeCheck(ciP) != 0) { alarmMgr.badInput(clientIp, "invalid mime-type in Accept http-header"); restReply(ciP, ciP->answer); } else { ciP->inMimeType = mimeTypeParse(ciP->httpHeaders.contentType, NULL); } if (ciP->httpStatusCode != SccOk) { alarmMgr.badInput(clientIp, "error in URI parameters"); restReply(ciP, ciP->answer); return MHD_YES; } // // Here, if the incoming request was too big, return error about it // if (ciP->httpHeaders.contentLength > PAYLOAD_MAX_SIZE) { char details[256]; snprintf(details, sizeof(details), "payload size: %d, max size supported: %d", ciP->httpHeaders.contentLength, PAYLOAD_MAX_SIZE); alarmMgr.badInput(clientIp, details); ciP->answer = restErrorReplyGet(ciP, "", ciP->url, SccRequestEntityTooLarge, details); ciP->httpStatusCode = SccRequestEntityTooLarge; } // // Requests of verb POST, PUT or PATCH are considered erroneous if no payload is present - with two exceptions. // // - Old log requests (URL contains '/log/') // - New log requests (URL is exactly '/admin/log') // if (((ciP->verb == POST) || (ciP->verb == PUT) || (ciP->verb == PATCH )) && (ciP->httpHeaders.contentLength == 0) && ((strncasecmp(ciP->url.c_str(), "/log/", 5) != 0) && (strncasecmp(ciP->url.c_str(), "/admin/log", 10) != 0))) { std::string errorMsg = restErrorReplyGet(ciP, "", url, SccContentLengthRequired, "Zero/No Content-Length in PUT/POST/PATCH request"); ciP->httpStatusCode = SccContentLengthRequired; restReply(ciP, errorMsg); alarmMgr.badInput(clientIp, errorMsg); } else if (ciP->answer != "") { alarmMgr.badInput(clientIp, ciP->answer); restReply(ciP, ciP->answer); } else { serveFunction(ciP); } return MHD_YES; }
static int check_token_is_valid(struct MHD_Connection *connection, t_client *client) { /* token check */ struct collect_query_key token_key = { .key = "token" }; struct collect_query_key tok_key = { .key = "tok" }; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &collect_query_key, &token_key); MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &collect_query_key, &tok_key); /* token not found in query string */ if (!token_key.value && !tok_key.value) return 0; if (token_key.value && !strcmp(client->token, token_key.value)) return 1; if (tok_key.value && !strcmp(client->token, tok_key.value)) return 1; return 0; } /** * @brief try_to_authenticate * @param connection * @param client * @param host * @param url * @return */ static int try_to_authenticate(struct MHD_Connection *connection, t_client *client, const char *host, const char *url) { /* a successful auth looks like * http://192.168.42.1:2050/nodogsplash_auth/?redir=http%3A%2F%2Fberlin.freifunk.net%2F&tok=94c4cdd2 * when authaction -> http://192.168.42.1:2050/nodogsplash_auth/ */ s_config *config = config_get_config(); /* we are checking here for the second '/' of /denydir/ */ if (check_authdir_match(url, config->authdir)) { /* matched to authdir */ if (check_token_is_valid(connection, client)) { return 1; /* valid token */ } } else if (check_authdir_match(url, config->denydir)) { /* matched to deauth */ /* TODO: do we need denydir? */ return 0; } return 0; } /** * @brief authenticate the client and redirect them * @param connection * @param ip_addr - needs to be freed * @param mac - needs to be freed * @param redirect_url - redirect the client to this url * @return */ static int authenticate_client(struct MHD_Connection *connection, const char *ip_addr, const char *mac, const char *redirect_url, t_client *client) { /* TODO: handle redirect_url == NULL */ auth_client_action(ip_addr, mac, AUTH_MAKE_AUTHENTICATED); if (redirect_url) return send_redirect_temp(connection, redirect_url); else return send_error(connection, 200); }
int http_cb_request (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { (void)cls; (void)url; (void)upload_data; (void)upload_data_size; int ret; struct Proxy *proxy; struct SPDY_Headers spdy_headers; bool with_body = false; struct HTTP_URI *http_uri; const char *header_value; if (NULL == ptr || NULL == *ptr) return MHD_NO; http_uri = (struct HTTP_URI *)*ptr; if(NULL == http_uri->proxy) { //first call for this request if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST)) { free(http_uri->uri); free(http_uri); PRINT_INFO2("unexpected method %s", method); return MHD_NO; } if(NULL == (proxy = au_malloc(sizeof(struct Proxy)))) { free(http_uri->uri); free(http_uri); PRINT_INFO("No memory"); return MHD_NO; } ++glob_opt.responses_pending; proxy->id = rand(); proxy->http_active = true; proxy->http_connection = connection; http_uri->proxy = proxy; return MHD_YES; } proxy = http_uri->proxy; if(proxy->spdy_error || proxy->http_error) return MHD_NO; // handled at different place TODO? leaks? if(proxy->spdy_active) { if(0 == strcmp (method, MHD_HTTP_METHOD_POST)) { PRINT_INFO("POST processing"); int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id); PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id); proxy->spdy_connection->want_io |= WANT_WRITE; if(0 == *upload_data_size) { PRINT_INFO("POST http EOF"); proxy->receiving_done = true; return MHD_YES; } if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size)) { //TODO handle it better? PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); return MHD_NO; } *upload_data_size = 0; return MHD_YES; } //already handled PRINT_INFO("unnecessary call to http_cb_request"); return MHD_YES; } //second call for this request PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version); proxy->url = http_uri->uri; header_value = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST) && (NULL == header_value || 0 != strcmp ("0", header_value)); PRINT_INFO2("body will be sent %i", with_body); ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri); if(ret != 0) DIE("parse_uri failed"); proxy->http_uri = http_uri; spdy_headers.num = MHD_get_connection_values (connection, MHD_HEADER_KIND, NULL, NULL); if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *)))) DIE("no memory"); spdy_headers.nv[0] = ":method"; spdy_headers.nv[1] = method; spdy_headers.nv[2] = ":path"; spdy_headers.nv[3] = proxy->uri->path_and_more; spdy_headers.nv[4] = ":version"; spdy_headers.nv[5] = (char *)version; spdy_headers.nv[6] = ":scheme"; spdy_headers.nv[7] = proxy->uri->scheme; spdy_headers.nv[8] = ":host"; spdy_headers.nv[9] = NULL; //nv[14] = NULL; spdy_headers.cnt = 10; MHD_get_connection_values (connection, MHD_HEADER_KIND, &http_cb_iterate, &spdy_headers); spdy_headers.nv[spdy_headers.cnt] = NULL; if(NULL == spdy_headers.nv[9]) spdy_headers.nv[9] = proxy->uri->host_and_port; if(0 != spdy_request(spdy_headers.nv, proxy, with_body)) { free(spdy_headers.nv); //free_proxy(proxy); return MHD_NO; } free(spdy_headers.nv); proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 4096, &http_cb_response, proxy, &http_cb_response_done); if (NULL == proxy->http_response) DIE("no response"); if(MHD_NO == MHD_add_response_header (proxy->http_response, "Proxy-Connection", "keep-alive")) PRINT_INFO("SPDY_name_value_add failed: "); if(MHD_NO == MHD_add_response_header (proxy->http_response, "Connection", "Keep-Alive")) PRINT_INFO("SPDY_name_value_add failed: "); if(MHD_NO == MHD_add_response_header (proxy->http_response, "Keep-Alive", "timeout=5, max=100")) PRINT_INFO("SPDY_name_value_add failed: "); proxy->spdy_active = true; return MHD_YES; }