Пример #1
0
/*
 * 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");
}
Пример #2
0
/**
 * 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");
	}
}
Пример #3
0
/*
 * 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);
}
Пример #4
0
/* 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);
}
Пример #5
0
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", "*");
}
Пример #6
0
/*
 * 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, ""));
}
Пример #9
0
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);
}
Пример #10
0
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);
}
Пример #11
0
/* 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);
}
Пример #12
0
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);
	}
}
Пример #13
0
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;
}
Пример #14
0
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);
}