예제 #1
0
static void processor(const char *tx_addr, int port)
{
    sys_init(1);
    sys_log('E', "started port=%d, tx=%s\n", port, tx_addr);

    /* connect to the control thread */
    char addr[MAX_ADDR];
    sys_address(addr, port);
    IO *io = sys_connect(addr, IO_CHUNK);

    tx_attach(tx_addr);

    /* get env code from the tx */
    char *code = tx_program();
    char *res = mem_alloc(MAX_BLOCK);

    while (!io->stop) {
        sys_iready(io, -1);

        int status = -1;
        long long sid = 0LL, time = sys_millis();

        Env *env = NULL;
        Arg *arg = NULL;
        Vars *v = vars_new(0), *r = NULL, *w = NULL;

        Http_Req *req = http_parse_req(io);
        if (io->stop)
            goto exit;

        if (req == NULL) {
            status = http_400(io);
            goto exit;
        }

        if (req->method == OPTIONS) {
            status = http_opts(io);
            goto exit;
        }

        env = env_new("net", code);

        if (str_idx(req->path, "/fn") == 0) {
            int idx = (req->path[3] == '/') ? 4 : 3;
            int i = 0, len = 1, cnt = 0;
            Func **fns = env_funcs(env, req->path + idx, &cnt);

            status = http_200(io);
            while (status == 200 && len) {
                len = pack_fn2csv(fns, cnt, res, MAX_BLOCK, &i);
                status = http_chunk(io, res, len);
            }

            mem_free(fns);
            goto exit;
        }

        /* compare the request with the function defintion */
        Func *fn = env_func(env, req->path + 1);
        if (fn == NULL) {
            Error *err = error_new("unknown function '%s'", req->path + 1);
            status = http_404(io, err->msg);
            mem_free(err);
            goto exit;
        }

        if (fn->rp.name != NULL && req->method != POST) {
            status = http_405(io, POST);
            goto exit;
        }

        if (fn->rp.name == NULL && req->method == POST) {
            status = http_405(io, GET);
            goto exit;
        }

        /* TODO: think what to do with duplicate parameter values */
        for (int i = 0; i < req->args->len; ++i) {
            char *name = req->args->names[i];
            if (array_freq(req->args->names, req->args->len, name) > 1) {
                Error *err = error_new("duplicate parameter '%s' "
                                       "(not supported)",
                                       name);
                status = http_404(io, err->msg);
                mem_free(err);
                goto exit;
            }
        }

        if (fn->pp.len != req->args->len) {
            Error *err = error_new("expected %d primitive parameters, got %d",
                                   fn->pp.len, req->args->len);
            status = http_404(io, err->msg);
            mem_free(err);
            goto exit;
        }

        arg = mem_alloc(sizeof(Arg));
        for (int i = 0; i < fn->pp.len; ++i) {
            char *name = fn->pp.names[i];
            Type t = fn->pp.types[i];

            int idx = array_scan(req->args->names, req->args->len, name);
            if (idx < 0) {
                Error *err = error_new("unknown parameter '%s'", name);
                status = http_404(io, err->msg);
                mem_free(err);
                goto exit;
            }

            char *val = req->args->vals[idx];
            int error = 0;
            if (t == Int) {
                arg->vals[i].v_int = str_int(val, &error);
            } else if (t == Real)
                arg->vals[i].v_real = str_real(val, &error);
            else if (t == Long)
                arg->vals[i].v_long = str_long(val, &error);
            else if (t == String) {
                error = str_len(val) > MAX_STRING;
                if (!error)
                    str_cpy(arg->vals[i].v_str, val);
            }

            if (error) {
                Error *err = error_new("value '%s' (parameter '%s') "
                                       "is not of type '%s'",
                                       val, name, type_to_str(t));
                status = http_404(io, err->msg);
                mem_free(err);
                goto exit;
            }
        }

        if (fn->rp.name != NULL) {
            TBuf *body = NULL;
            if (req->len > 0) {
                Error *err = pack_csv2rel(req->body, fn->rp.head, &body);
                if (err != NULL) {
                    status = http_404(io, err->msg);
                    mem_free(err);
                    goto exit;
                }
            } else {
                body = tbuf_new();
            }

            vars_add(v, fn->rp.name, 0, body);

            /* project the parameter */
            Rel *param = rel_project(rel_load(fn->rp.head, fn->rp.name),
                                     fn->rp.head->names,
                                     fn->rp.head->len);

            rel_eval(param, v, arg);

            /* clean the previous version */
            tbuf_clean(body);
            tbuf_free(body);

            /* replace with the new body */
            int vpos = array_scan(v->names, v->len, fn->rp.name);
            v->vals[vpos] = param->body;

            param->body = NULL;
            rel_free(param);
        }

        /* start a transaction */
        r = vars_new(fn->r.len);
        w = vars_new(fn->w.len);
        for (int i = 0; i < fn->r.len; ++i)
            vars_add(r, fn->r.names[i], 0, NULL);
        for (int i = 0; i < fn->w.len; ++i)
            vars_add(w, fn->w.names[i], 0, NULL);

        sid = tx_enter(addr, r, w);

        /* prepare variables */
        for (int i = 0; i < r->len; ++i) {
            TBuf *body = vol_read(r->vols[i], r->names[i], r->vers[i]);
            vars_add(v, r->names[i], 0, body);
        }
        for (int i = 0; i < w->len; ++i) {
            int pos = array_scan(v->names, v->len, w->names[i]);
            if (pos < 0)
                vars_add(v, w->names[i], 0, NULL);
        }
        for (int i = 0; i < fn->t.len; ++i)
            vars_add(v, fn->t.names[i], 0, NULL);

        /* evaluate the function body */
        for (int i = 0; i < fn->slen; ++i)
            rel_eval(fn->stmts[i], v, arg);

        /* prepare the return value. note, the resulting relation
           is just a container for the body, so it is not freed */
        Rel *ret = NULL;
        if (fn->ret != NULL)
            ret = fn->stmts[fn->slen - 1];

        /* persist the global variables */
        for (int i = 0; i < w->len; ++i) {
            int idx = array_scan(v->names, v->len, w->names[i]);
            if (idx < 0) {
                status = http_500(io);
                goto exit;
            }

            vol_write(w->vols[i], v->vals[idx], w->names[i], w->vers[i]);
            tbuf_free(v->vals[idx]);
            v->vals[idx] = NULL;
        }

        /* confirm a success and send the result back */
        status = http_200(io);
        if (status != 200)
            goto exit;

        tx_commit(sid);

        /* N.B. there is no explicit revert as the transaction manager handles
           nested tx_enter and a connectivity failure as a rollback */

        int len = 1, i = 0;
        while (status == 200 && len) {
            len = pack_rel2csv(ret, res, MAX_BLOCK, i++);
            status = http_chunk(io, res, len);
        }
exit:
        if (status != -1)
            sys_log('E', "%016llX method %c, path %s, time %lldms - %3d\n",
                         sid,
                         (req == NULL) ? '?' : req->method,
                         (req == NULL) ? "malformed" : req->path,
                         sys_millis() - time,
                         status);


        if (r != NULL)
            vars_free(r);
        if (w != NULL)
            vars_free(w);
        if (arg != NULL)
            mem_free(arg);
        if (req != NULL)
            http_free_req(req);
        if (env != NULL)
            env_free(env);
        for (int i = 0; i < v->len; ++i)
            if (v->vals[i] != NULL) {
                tbuf_clean(v->vals[i]);
                tbuf_free(v->vals[i]);
            }
        vars_free(v);

        sys_term(io);
    }

    mem_free(code);
    mem_free(res);
    tx_detach();
    sys_close(io);
}
예제 #2
0
void http_read(TCP_NODE * n, char *p_cmd, char *p_url, char *p_proto,
	       HASH * p_head)
{

	int code = 0;
	char lastmodified[DATE_SIZE];
	char resource[BUF_SIZE];
	char filename[BUF_SIZE];
	char range[BUF_SIZE];
	char *p_range = range;
	size_t filesize = 0;
	size_t content_length = 0;
	char keepalive[BUF_SIZE];
	const char *mimetype = NULL;

	/* Get protocol */
	if (!http_proto(n, p_proto)) {
		node_status(n, NODE_SHUTDOWN);
		goto END;
	}

	/* Get action */
	if (!http_action(n, p_cmd)) {
		node_status(n, NODE_SHUTDOWN);
		goto END;
	}

	/* Get resource */
	if (!http_resource(n, p_url, resource)) {
		node_status(n, NODE_SHUTDOWN);
		goto END;
	}

	/* Check Keep-Alive */
	n->keepalive = http_keepalive(p_head, keepalive);

	/* Compute filename */
	if (!http_filename(resource, filename)) {
		http_404(n, keepalive);
		info(_log, &n->c_addr, "404 %s", resource);
		goto END;
	}

	/* Compute file size */
	filesize = http_size_simple(filename);

	/* Compute mime type */
	mimetype = mime_find(filename);

	/* Last-Modified. */
	if (!http_resource_modified(filename, p_head, lastmodified)) {
		http_304(n, keepalive);
		info(_log, &n->c_addr, "304 %s", resource);
		goto END;
	}

	/* Range request? */
	if (http_range_detected(p_head, range)) {
		code = 206;
	} else {
		code = 200;
	}

	/* Normal 200-er request */
	if (code == 200) {
		info(_log, &n->c_addr, "200 %s", resource);
		http_200(n, lastmodified, filename, filesize, keepalive,
			 mimetype);
		http_body(n, filename, filesize);
		goto END;
	}

	/* Check for 'bytes=' in range. Fallback to 200 if necessary. */
	if (!http_range_prepare(&p_range)) {
		info(_log, &n->c_addr, "200 %s", resource);
		http_200(n, lastmodified, filename, filesize, keepalive,
			 mimetype);
		http_body(n, filename, filesize);
		goto END;
	}

	/* multipart/byteranges */
	if (!http_range_multipart(p_range)) {
		RESPONSE *r_head = NULL, *r_file = NULL;

		/* Header */
		if ((r_head =
		     resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) {
			node_status(n, NODE_SHUTDOWN);
			goto END;
		}

		/* File */
		if ((r_file =
		     resp_put(n->response, RESPONSE_FROM_FILE)) == NULL) {
			node_status(n, NODE_SHUTDOWN);
			goto END;
		}

		/* Parse range. */
		if (!http_range_simple(n, r_file, filename, filesize, p_range,
				       &content_length)) {
			node_status(n, NODE_SHUTDOWN);
			goto END;
		}

		/* Header with known content_length */
		http_206_simple(n, r_head, r_file, lastmodified, filename,
				filesize, content_length, keepalive, mimetype);

		info(_log, &n->c_addr, "206 %s [%s]", resource, range);
		goto END;
	} else {
		RESPONSE *r_head = NULL, *r_bottom = NULL, *r_zsyncbug = NULL;
		char boundary[12];

		/* Create boundary string */
		http_random(boundary, 12);

		/* Header */
		if ((r_head =
		     resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) {
			node_status(n, NODE_SHUTDOWN);
			goto END;
		}

		/* zsync bug? One more \r\n between header and body. */
		if ((r_zsyncbug =
		     resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) {
			node_status(n, NODE_SHUTDOWN);
			goto END;
		}

		/* Parse range. */
		if (!http_range_complex
		    (n, filename, filesize, mimetype, p_range, &content_length,
		     boundary)) {
			node_status(n, NODE_SHUTDOWN);
			goto END;
		}

		/* Bottom */
		if ((r_bottom =
		     resp_put(n->response, RESPONSE_FROM_MEMORY)) == NULL) {
			node_status(n, NODE_SHUTDOWN);
			goto END;
		}
		http_206_boundary_finish(r_bottom, &content_length, boundary);

		/* Header with known content_length */
		http_206_complex(n, r_head, lastmodified, filename,
				 filesize, content_length, boundary, keepalive);

		/* zsync bug? One more \r\n between header and body. */
		http_newline(r_zsyncbug, &content_length);

		info(_log, &n->c_addr, "206 %s [%s]", resource, range);

		goto END;
	}

	info(_log, &n->c_addr, "FIXME: HTTP parser end reached without result");
	node_status(n, NODE_SHUTDOWN);

 END:

	/* HTTP Pipeline: There is at least one more request to parse.
	 * Recursive request! Limited by the input buffer.
	 */
	if (n->pipeline != NODE_SHUTDOWN && n->recv_size > 0) {
		http_buf(n);
	}
}