/* * Send back a simple error page */ void http_response_send_error(struct http_response *resp, int code, const char *fmt, ...) { struct http_request *const req = resp->msg->conn->req; const char *ua; FILE *fp; int i; /* Check headers already sent */ if (resp->msg->hdrs_sent) return; /* Set response line info */ http_response_set_header(resp, 0, HDR_REPLY_STATUS, "%d", code); http_response_set_header(resp, 0, HDR_REPLY_REASON, "%s", http_response_status_msg(code)); /* Set additional headers */ http_response_set_header(resp, 0, HTTP_HEADER_CONTENT_TYPE, "text/html; charset=iso-8859-1"); /* Close connection for real errors */ if (code >= 400) { http_response_set_header(resp, 0, _http_message_connection_header(resp->msg), "close"); } /* Send error page body */ if ((fp = http_response_get_output(resp, 1)) == NULL) return; fprintf(fp, "<HTML>\n<HEAD>\n<TITLE>%d %s</TITLE></HEAD>\n", code, http_response_status_msg(code)); fprintf(fp, "<BODY BGCOLOR=\"#FFFFFF\">\n<H3>%d %s</H3>\n", code, http_response_status_msg(code)); if (fmt != NULL) { va_list args; fprintf(fp, "<B>"); va_start(args, fmt); vfprintf(fp, fmt, args); va_end(args); fprintf(fp, "</B>\n"); } #if 0 fprintf(fp, "<P></P>\n<HR>\n"); fprintf(fp, "<FONT SIZE=\"-1\"><EM>%s</EM></FONT>\n", serv->server_name); #endif /* Add fillter for IE */ if ((ua = http_request_get_header(req, HTTP_HEADER_USER_AGENT)) != NULL && strstr(ua, "IE") != NULL) { for (i = 0; i < 20; i++) { fprintf(fp, "<!-- FILLER TO MAKE INTERNET EXPLORER SHOW" " THIS PAGE INSTEAD OF ITS OWN PAGE -->\n"); } } fprintf(fp, "</BODY>\n</HTML>\n"); }
/** * Set Connection field, either Keep-Alive or Close. */ void http_response_set_keep_alive(struct http_response *r, int enabled) { if(enabled) { http_response_set_header(r, "Connection", "Keep-Alive"); } else { http_response_set_header(r, "Connection", "Close"); } }
/* * Send an HTTP redirect. */ void http_response_send_redirect(struct http_response *resp, const char *url) { const char *name; const char *value; while (_http_head_get_by_index(resp->msg->head, 0, &name, &value) == 0) _http_head_remove(resp->msg->head, name); http_response_set_header(resp, 0, HDR_REPLY_STATUS, "%d", HTTP_STATUS_MOVED_PERMANENTLY); http_response_set_header(resp, 0, HDR_REPLY_REASON, "%s", http_response_status_msg(HTTP_STATUS_MOVED_PERMANENTLY)); http_response_set_header(resp, 0, "Location", "%s", url); }
/* Response to HTTP OPTIONS */ void http_send_options(struct http_client *c) { struct http_response resp; http_response_init(&resp, 200, "OK"); resp.http_version = c->http_version; http_response_set_connection_header(c, &resp); http_response_set_header(&resp, "Content-Type", "text/html"); http_response_set_header(&resp, "Content-Length", "0"); http_response_write(&resp, c->fd); http_client_reset(c); }
void http_response_init(struct http_response *r, int code, const char *msg) { /* remove any old data */ memset(r, 0, sizeof(struct http_response)); r->code = code; r->msg = msg; http_response_set_header(r, "Server", "Webdis"); /* Cross-Origin Resource Sharing, CORS. */ http_response_set_header(r, "Allow", "GET,POST,PUT,OPTIONS"); http_response_set_header(r, "Access-Control-Allow-Origin", "*"); }
/* * Send an HTTP authorization failed response. */ void http_response_send_basic_auth(struct http_response *resp, const char *realm) { http_response_set_header(resp, 0, HTTP_HEADER_WWW_AUTHENTICATE, "Basic realm=\"%s\"", realm); _http_head_remove(resp->msg->head, HTTP_HEADER_LAST_MODIFIED); http_response_send_error(resp, HTTP_STATUS_UNAUTHORIZED, NULL); }
char * http_servlet_tmpl_func_set_header(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av) { struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx); const char *const mtype = tmpl_ctx_get_mtype(ctx); if (ac != 3) { errno = EINVAL; return (NULL); } if (http_response_set_header(arg->resp, 0, av[1], "%s", av[2]) == -1) return (NULL); return (STRDUP(mtype, "")); }
char * http_servlet_tmpl_func_redirect(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av) { struct http_servlet_tmpl_arg *const arg = tmpl_ctx_get_arg(ctx); const char *const mtype = tmpl_ctx_get_mtype(ctx); if (ac != 2) { errno = EINVAL; return (NULL); } if (http_response_set_header(arg->resp, 0, HTTP_HEADER_LOCATION, "%s", av[1]) == -1) return (NULL); http_response_send_error(arg->resp, HTTP_STATUS_FOUND, NULL); return (STRDUP(mtype, "")); }
void custom_type_reply(redisAsyncContext *c, void *r, void *privdata) { redisReply *reply = r; struct cmd *cmd = privdata; (void)c; char int_buffer[50]; int int_len; struct http_response resp; if (reply == NULL) { /* broken Redis link */ format_send_error(cmd, 503, "Service Unavailable"); return; } if(cmd->mime) { /* use the given content-type, but only for strings */ switch(reply->type) { case REDIS_REPLY_NIL: /* or nil values */ format_send_reply(cmd, "", 0, cmd->mime); return; case REDIS_REPLY_STRING: format_send_reply(cmd, reply->str, reply->len, cmd->mime); return; case REDIS_REPLY_INTEGER: int_len = sprintf(int_buffer, "%lld", reply->integer); format_send_reply(cmd, int_buffer, int_len, cmd->mime); return; case REDIS_REPLY_ARRAY: # TODO: Avoid assuming the command is BLPOP. format_send_reply(cmd, reply->element[1]->str, reply->element[1]->len, cmd->mime); return; } } /* couldn't make sense of what the client wanted. */ http_response_init(&resp, 401, "Bad Request"); http_response_set_header(&resp, "Content-Length", "0"); http_response_set_keep_alive(&resp, cmd->keep_alive); http_response_write(&resp, cmd->fd); cmd_free(cmd); }
void http_response_finalize(struct http_response *response) { char date[HTTP_RFC1123_DATE_BUFSZ]; time_t now; /* Version */ if (response->request) response->version = response->request->version; /* Date */ now = time(NULL); http_format_timestamp(date, HTTP_RFC1123_DATE_BUFSZ, now); http_response_set_header(response, "Date", date); /* Content-Length */ http_response_set_header_printf(response, "Content-Length", "%zu", response->body_sz); }
/* Adobe flash cross-domain request */ void http_crossdomain(struct http_client *c) { struct http_response resp; char out[] = "<?xml version=\"1.0\"?>\n" "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n" "<cross-domain-policy>\n" "<allow-access-from domain=\"*\" />\n" "</cross-domain-policy>\n"; http_response_init(&resp, 200, "OK"); resp.http_version = c->http_version; http_response_set_connection_header(c, &resp); http_response_set_header(&resp, "Content-Type", "application/xml"); http_response_set_body(&resp, out, sizeof(out)-1); http_response_write(&resp, c->fd); http_client_reset(c); }
void format_send_reply(struct cmd *cmd, const char *p, size_t sz, const char *content_type) { int free_cmd = 1; const char *ct = cmd->mime?cmd->mime:content_type; struct http_response *resp; if(cmd->is_websocket) { ws_reply(cmd, p, sz); /* If it's a subscribe command, there'll be more responses */ if(!cmd_is_subscribe(cmd)) cmd_free(cmd); return; } if(cmd_is_subscribe(cmd)) { free_cmd = 0; /* start streaming */ if(cmd->started_responding == 0) { cmd->started_responding = 1; resp = http_response_init(cmd->w, 200, "OK"); resp->http_version = cmd->http_version; if(cmd->filename) { http_response_set_header(resp, "Content-Disposition", cmd->filename); } http_response_set_header(resp, "Content-Type", ct); http_response_set_keep_alive(resp, 1); http_response_set_header(resp, "Transfer-Encoding", "chunked"); http_response_set_body(resp, p, sz); http_response_write(resp, cmd->fd); } else { /* Asynchronous chunk write. */ http_response_write_chunk(cmd->fd, cmd->w, p, sz); } } else { /* compute ETag */ char *etag = etag_new(p, sz); if(etag) { /* check If-None-Match */ if(cmd->if_none_match && strcmp(cmd->if_none_match, etag) == 0) { /* SAME! send 304. */ resp = http_response_init(cmd->w, 304, "Not Modified"); } else { resp = http_response_init(cmd->w, 200, "OK"); if(cmd->filename) { http_response_set_header(resp, "Content-Disposition", cmd->filename); } http_response_set_header(resp, "Content-Type", ct); http_response_set_header(resp, "ETag", etag); http_response_set_body(resp, p, sz); } resp->http_version = cmd->http_version; http_response_set_keep_alive(resp, cmd->keep_alive); http_response_write(resp, cmd->fd); free(etag); } else { format_send_error(cmd, 503, "Service Unavailable"); } } /* cleanup */ if(free_cmd) { cmd_free(cmd); } }
int http_response_write(struct http_response *r, int fd) { char *s = NULL, *p; size_t sz = 0; int i, ret, keep_alive = 0; sz = sizeof("HTTP/1.x xxx ")-1 + strlen(r->msg) + 2; s = calloc(sz + 1, 1); ret = sprintf(s, "HTTP/1.%d %d %s\r\n", (r->http_version?1:0), r->code, r->msg); p = s; if(r->code == 200 && r->body) { char content_length[10]; sprintf(content_length, "%zd", r->body_len); http_response_set_header(r, "Content-Length", content_length); } else if(!r->chunked) { http_response_set_header(r, "Content-Length", "0"); } for(i = 0; i < r->header_count; ++i) { /* "Key: Value\r\n" */ size_t header_sz = r->headers[i].key_sz + 2 + r->headers[i].val_sz + 2; s = realloc(s, sz + header_sz); p = s + sz; /* add key */ memcpy(p, r->headers[i].key, r->headers[i].key_sz); p += r->headers[i].key_sz; /* add ": " */ *(p++) = ':'; *(p++) = ' '; /* add value */ memcpy(p, r->headers[i].val, r->headers[i].val_sz); p += r->headers[i].val_sz; /* add "\r\n" */ *(p++) = '\r'; *(p++) = '\n'; sz += header_sz; if(strncasecmp("Connection", r->headers[i].key, r->headers[i].key_sz) == 0 && strncasecmp("Keep-Alive", r->headers[i].val, r->headers[i].val_sz) == 0) { keep_alive = 1; } } /* end of headers */ s = realloc(s, sz + 2); memcpy(s + sz, "\r\n", 2); sz += 2; /* append body if there is one. */ if(r->body && r->body_len) { s = realloc(s, sz + r->body_len); memcpy(s + sz, r->body, r->body_len); sz += r->body_len; } /* send buffer to client */ ret = write(fd, s, sz); /* cleanup buffer */ free(s); if(!keep_alive && (size_t)ret == sz) { /* Close fd is client doesn't support Keep-Alive. */ close(fd); } /* cleanup response object */ for(i = 0; i < r->header_count; ++i) { free(r->headers[i].key); free(r->headers[i].val); } free(r->headers); return ret == (int)sz ? 0 : 1; }
static int WebServletRun(struct http_servlet *servlet, struct http_request *req, struct http_response *resp) { FILE *f; const char *path; const char *query; int priv = 0; if (Enabled(&gWeb.options, WEB_AUTH)) { const char *username; const char *password; ConsoleUser u; struct console_user iu; /* Get username and password */ if ((username = http_request_get_username(req)) == NULL) username = ""; if ((password = http_request_get_password(req)) == NULL) password = ""; strlcpy(iu.username, username, sizeof(iu.username)); RWLOCK_RDLOCK(gUsersLock); u = ghash_get(gUsers, &iu); RWLOCK_UNLOCK(gUsersLock); if ((u == NULL) || strcmp(u->password, password)) { http_response_send_basic_auth(resp, "Access Restricted"); return (1); } priv = u->priv; } if (!(f = http_response_get_output(resp, 1))) { return 0; } if (!(path = http_request_get_path(req))) return 0; if (!(query = http_request_get_query_string(req))) return 0; if (!strcmp(path,"/mpd.css")) { http_response_set_header(resp, 0, "Content-Type", "text/css"); WebShowCSS(f); } else if (!strcmp(path,"/bincmd")) { http_response_set_header(resp, 0, "Content-Type", "text/plain"); http_response_set_header(resp, 1, "Pragma", "no-cache"); http_response_set_header(resp, 1, "Cache-Control", "no-cache, must-revalidate"); pthread_cleanup_push(WebServletRunCleanup, NULL); GIANT_MUTEX_LOCK(); WebRunBinCmd(f, query, priv); GIANT_MUTEX_UNLOCK(); pthread_cleanup_pop(0); } else if (!strcmp(path,"/") || !strcmp(path,"/cmd")) { http_response_set_header(resp, 0, "Content-Type", "text/html"); http_response_set_header(resp, 1, "Pragma", "no-cache"); http_response_set_header(resp, 1, "Cache-Control", "no-cache, must-revalidate"); pthread_cleanup_push(WebServletRunCleanup, NULL); GIANT_MUTEX_LOCK(); fprintf(f, "<!DOCTYPE HTML " "PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " "\"http://www.w3.org/TR/html4/strict.dtd\">\n"); fprintf(f, "<HTML>\n"); fprintf(f, "<HEAD><TITLE>Multi-link PPP Daemon for FreeBSD (mpd)</TITLE>\n"); fprintf(f, "<LINK rel='stylesheet' href='/mpd.css' type='text/css'>\n"); fprintf(f, "</HEAD>\n<BODY>\n"); fprintf(f, "<H1>Multi-link PPP Daemon for FreeBSD</H1>\n"); if (!strcmp(path,"/")) WebShowSummary(f, priv); else if (!strcmp(path,"/cmd")) WebRunCmd(f, query, priv); GIANT_MUTEX_UNLOCK(); pthread_cleanup_pop(0); fprintf(f, "</BODY>\n</HTML>\n"); } else { http_response_send_error(resp, 404, NULL); } return 1; }
/* * Run template servlet */ static int http_servlet_tmpl_run(struct http_servlet *servlet, struct http_request *req, struct http_response *resp) { struct tmpl_private *const priv = servlet->arg; struct http_servlet_tmpl_info *const info = &priv->info; struct http_servlet_tmpl_tinfo *const tinfo = &priv->info.tinfo; struct tmpl_instance *this = NULL; FILE *output = NULL; const char *hval; struct stat sb; int num_errors; int r; /* Construct per-instance state */ if ((this = MALLOC(MEM_TYPE, sizeof(*this))) == NULL) { (*info->logger)(LOG_ERR, "%s: %s: %s", __FUNCTION__, "malloc", strerror(errno)); return (-1); } memset(this, 0, sizeof(*this)); this->priv = priv; /* Grab lock to avoid race with http_servlet_tmpl_destroy() */ r = pthread_rwlock_rdlock(&priv->lock); assert(r == 0); /* Push cleanup hook in case thread gets canceled */ pthread_cleanup_push(http_servlet_tmpl_run_cleanup, this); /* Get servlet output stream (buffered) */ if ((output = http_response_get_output(resp, 1)) == NULL) { (*info->logger)(LOG_ERR, "can't get template output: %s", strerror(errno)); goto fail_errno; } /* Set MIME type */ if (info->mime_type == NULL) { http_response_set_header(resp, 0, HTTP_HEADER_CONTENT_TYPE, "text/html; charset=iso-8859-1"); } else { http_response_set_header(resp, 0, HTTP_HEADER_CONTENT_TYPE, "%s", info->mime_type); if (info->mime_encoding != NULL) { http_response_set_header(resp, 0, HTTP_HEADER_CONTENT_ENCODING, "%s", info->mime_encoding); } } /* Assume servlet output is not cachable */ http_response_set_header(resp, 1, HTTP_HEADER_PRAGMA, "no-cache"); http_response_set_header(resp, 0, HTTP_HEADER_CACHE_CONTROL, "no-cache"); /* Get modification timestamp of the template file */ if (stat(info->path, &sb) == -1) { (*info->logger)(LOG_ERR, "%s: %s: %s", __FUNCTION__, info->path, strerror(errno)); memset(&sb.st_mtime, 0, sizeof(sb.st_mtime)); } /* Invalidate cached template if template file has changed */ if (priv->tmpl != NULL && memcmp(&sb.st_mtime, &priv->mtime, sizeof(priv->mtime)) != 0) { (*info->logger)(LOG_INFO, "template \"%s\" was updated", info->path); tmpl_destroy(&priv->tmpl); } /* Do we need to (re)parse the template? */ if (priv->tmpl == NULL) { /* Parse template file */ if ((priv->tmpl = tmpl_create_mmap(info->path, &num_errors, tinfo->mtype)) == NULL) { (*info->logger)(LOG_ERR, "can't create template from \"%s\": %s", info->path, strerror(errno)); goto fail_errno; } /* Check for an error from tmpl_create() */ if (priv->tmpl == NULL) { (*info->logger)(LOG_ERR, "can't create \"%s\" template: %s", info->path, strerror(errno)); goto fail_errno; } /* Warn if there were any parse errors */ if (num_errors != 0) { (*info->logger)(LOG_WARNING, "%d parse error%s in template \"%s\"", num_errors, num_errors == 1 ? "" : "s", info->path); } /* Update last modified time */ memcpy(&priv->mtime, &sb.st_mtime, sizeof(priv->mtime)); } /* Read URL-encoded form data if this is a normal POST */ if (strcmp(http_request_get_method(req), HTTP_METHOD_POST) == 0 && (hval = http_request_get_header(req, HTTP_HEADER_CONTENT_TYPE)) != NULL && strcasecmp(hval, HTTP_CTYPE_FORM_URLENCODED) == 0) { if (http_request_read_url_encoded_values(req) == -1) { (*info->logger)(LOG_ERR, "error reading %s data for \"%s\" template: %s", HTTP_METHOD_POST, info->path, strerror(errno)); goto fail_errno; } } /* Fill in handler function cookie */ this->targ.arg = info->tinfo.arg; this->targ.req = req; this->targ.resp = resp; /* Create tmpl execution context */ if ((this->ctx = tmpl_ctx_create(&this->targ, tinfo->mtype, tinfo->handler, tinfo->errfmtr)) == NULL) { (*info->logger)(LOG_ERR, "%s: %s: %s", __FUNCTION__, "tmpl_ctx_create", strerror(errno)); goto fail_errno; } /* Execute template */ if (tmpl_execute(priv->tmpl, this->ctx, output, tinfo->flags) == -1) { (*info->logger)(LOG_ERR, "can't execute \"%s\" template: %s", info->path, strerror(errno)); goto fail_errno; } /* OK */ goto done; fail_errno: /* Fail with appropriate error response */ http_response_send_errno_error(resp); done: /* Done */ if (output != NULL) fclose(output); pthread_cleanup_pop(1); return (1); }