We need to take into account the size of the nxt_unit_response_t structure itself when calculating where to start appending data to in memory. Closes: <https://github.com/nginx/unit/issues/923> Reported-by: Alejandro Colomar <alx@kernel.org> Reviewed-by: Andrew Clayton <a.clayton@nginx.org> Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
1107 lines
29 KiB
C
1107 lines
29 KiB
C
|
|
/*
|
|
* Copyright (C) NGINX, Inc.
|
|
*/
|
|
|
|
#include <nxt_auto_config.h>
|
|
|
|
#include <nxt_unit.h>
|
|
#include <nxt_unit_response.h>
|
|
#include <jni.h>
|
|
#include <stdio.h>
|
|
|
|
#include "nxt_jni.h"
|
|
#include "nxt_jni_Response.h"
|
|
#include "nxt_jni_HeadersEnumeration.h"
|
|
#include "nxt_jni_HeaderNamesEnumeration.h"
|
|
#include "nxt_jni_OutputStream.h"
|
|
#include "nxt_jni_URLClassLoader.h"
|
|
|
|
|
|
static jclass nxt_java_Response_class;
|
|
static jmethodID nxt_java_Response_ctor;
|
|
|
|
|
|
static void JNICALL nxt_java_Response_addHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name, jarray value);
|
|
|
|
static nxt_unit_request_info_t *nxt_java_get_response_info(
|
|
jlong req_info_ptr, uint32_t extra_fields, uint32_t extra_data);
|
|
|
|
static void JNICALL nxt_java_Response_addIntHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name, jint value);
|
|
|
|
static void nxt_java_add_int_header(nxt_unit_request_info_t *req,
|
|
const char *name, uint8_t name_len, int value);
|
|
|
|
static jboolean JNICALL nxt_java_Response_containsHeader(JNIEnv *env,
|
|
jclass cls, jlong req_info_ptr, jarray name);
|
|
|
|
static jstring JNICALL nxt_java_Response_getHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name);
|
|
|
|
static jobject JNICALL nxt_java_Response_getHeaderNames(JNIEnv *env,
|
|
jclass cls, jlong req_info_ptr);
|
|
|
|
static jobject JNICALL nxt_java_Response_getHeaders(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name);
|
|
|
|
static jint JNICALL nxt_java_Response_getStatus(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static jobject JNICALL nxt_java_Response_getRequest(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static void JNICALL nxt_java_Response_commit(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static void JNICALL nxt_java_Response_sendRedirect(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray loc);
|
|
|
|
static int nxt_java_response_set_header(jlong req_info_ptr,
|
|
const char *name, jint name_len, const char *value, jint value_len);
|
|
|
|
static void JNICALL nxt_java_Response_setHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name, jarray value);
|
|
|
|
static void JNICALL nxt_java_Response_removeHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name);
|
|
|
|
static int nxt_java_response_remove_header(jlong req_info_ptr,
|
|
const char *name, jint name_len);
|
|
|
|
static void JNICALL nxt_java_Response_setIntHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name, jint value);
|
|
|
|
static void JNICALL nxt_java_Response_setStatus(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jint sc);
|
|
|
|
static jstring JNICALL nxt_java_Response_getContentType(JNIEnv *env,
|
|
jclass cls, jlong req_info_ptr);
|
|
|
|
static jboolean JNICALL nxt_java_Response_isCommitted(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static void JNICALL nxt_java_Response_reset(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static void JNICALL nxt_java_Response_resetBuffer(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static void JNICALL nxt_java_Response_setBufferSize(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jint size);
|
|
|
|
static jint JNICALL nxt_java_Response_getBufferSize(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static void JNICALL nxt_java_Response_setContentLength(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jlong len);
|
|
|
|
static void JNICALL nxt_java_Response_setContentType(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray type);
|
|
|
|
static void JNICALL nxt_java_Response_removeContentType(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr);
|
|
|
|
static void JNICALL nxt_java_Response_log(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray msg);
|
|
|
|
static void JNICALL nxt_java_Response_trace(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray msg);
|
|
|
|
int
|
|
nxt_java_initResponse(JNIEnv *env, jobject cl)
|
|
{
|
|
int res;
|
|
jclass cls;
|
|
|
|
cls = nxt_java_loadClass(env, cl, "nginx.unit.Response");
|
|
if (cls == NULL) {
|
|
return NXT_UNIT_ERROR;
|
|
}
|
|
|
|
nxt_java_Response_class = (*env)->NewGlobalRef(env, cls);
|
|
(*env)->DeleteLocalRef(env, cls);
|
|
cls = nxt_java_Response_class;
|
|
|
|
nxt_java_Response_ctor = (*env)->GetMethodID(env, cls, "<init>", "(J)V");
|
|
if (nxt_java_Response_ctor == NULL) {
|
|
(*env)->DeleteGlobalRef(env, cls);
|
|
return NXT_UNIT_ERROR;
|
|
}
|
|
|
|
JNINativeMethod resp_methods[] = {
|
|
{ (char *) "addHeader",
|
|
(char *) "(J[B[B)V",
|
|
nxt_java_Response_addHeader },
|
|
|
|
{ (char *) "addIntHeader",
|
|
(char *) "(J[BI)V",
|
|
nxt_java_Response_addIntHeader },
|
|
|
|
{ (char *) "containsHeader",
|
|
(char *) "(J[B)Z",
|
|
nxt_java_Response_containsHeader },
|
|
|
|
{ (char *) "getHeader",
|
|
(char *) "(J[B)Ljava/lang/String;",
|
|
nxt_java_Response_getHeader },
|
|
|
|
{ (char *) "getHeaderNames",
|
|
(char *) "(J)Ljava/util/Enumeration;",
|
|
nxt_java_Response_getHeaderNames },
|
|
|
|
{ (char *) "getHeaders",
|
|
(char *) "(J[B)Ljava/util/Enumeration;",
|
|
nxt_java_Response_getHeaders },
|
|
|
|
{ (char *) "getStatus",
|
|
(char *) "(J)I",
|
|
nxt_java_Response_getStatus },
|
|
|
|
{ (char *) "getRequest",
|
|
(char *) "(J)Lnginx/unit/Request;",
|
|
nxt_java_Response_getRequest },
|
|
|
|
{ (char *) "commit",
|
|
(char *) "(J)V",
|
|
nxt_java_Response_commit },
|
|
|
|
{ (char *) "sendRedirect",
|
|
(char *) "(J[B)V",
|
|
nxt_java_Response_sendRedirect },
|
|
|
|
{ (char *) "setHeader",
|
|
(char *) "(J[B[B)V",
|
|
nxt_java_Response_setHeader },
|
|
|
|
{ (char *) "removeHeader",
|
|
(char *) "(J[B)V",
|
|
nxt_java_Response_removeHeader },
|
|
|
|
{ (char *) "setIntHeader",
|
|
(char *) "(J[BI)V",
|
|
nxt_java_Response_setIntHeader },
|
|
|
|
{ (char *) "setStatus",
|
|
(char *) "(JI)V",
|
|
nxt_java_Response_setStatus },
|
|
|
|
{ (char *) "getContentType",
|
|
(char *) "(J)Ljava/lang/String;",
|
|
nxt_java_Response_getContentType },
|
|
|
|
{ (char *) "isCommitted",
|
|
(char *) "(J)Z",
|
|
nxt_java_Response_isCommitted },
|
|
|
|
{ (char *) "reset",
|
|
(char *) "(J)V",
|
|
nxt_java_Response_reset },
|
|
|
|
{ (char *) "resetBuffer",
|
|
(char *) "(J)V",
|
|
nxt_java_Response_resetBuffer },
|
|
|
|
{ (char *) "setBufferSize",
|
|
(char *) "(JI)V",
|
|
nxt_java_Response_setBufferSize },
|
|
|
|
{ (char *) "getBufferSize",
|
|
(char *) "(J)I",
|
|
nxt_java_Response_getBufferSize },
|
|
|
|
{ (char *) "setContentLength",
|
|
(char *) "(JJ)V",
|
|
nxt_java_Response_setContentLength },
|
|
|
|
{ (char *) "setContentType",
|
|
(char *) "(J[B)V",
|
|
nxt_java_Response_setContentType },
|
|
|
|
{ (char *) "removeContentType",
|
|
(char *) "(J)V",
|
|
nxt_java_Response_removeContentType },
|
|
|
|
{ (char *) "log",
|
|
(char *) "(J[B)V",
|
|
nxt_java_Response_log },
|
|
|
|
{ (char *) "trace",
|
|
(char *) "(J[B)V",
|
|
nxt_java_Response_trace },
|
|
|
|
};
|
|
|
|
res = (*env)->RegisterNatives(env, nxt_java_Response_class,
|
|
resp_methods,
|
|
sizeof(resp_methods)
|
|
/ sizeof(resp_methods[0]));
|
|
|
|
nxt_unit_debug(NULL, "registered Response methods: %d", res);
|
|
|
|
if (res != 0) {
|
|
(*env)->DeleteGlobalRef(env, cls);
|
|
return NXT_UNIT_ERROR;
|
|
}
|
|
|
|
return NXT_UNIT_OK;
|
|
}
|
|
|
|
|
|
jobject
|
|
nxt_java_newResponse(JNIEnv *env, nxt_unit_request_info_t *req)
|
|
{
|
|
return (*env)->NewObject(env, nxt_java_Response_class,
|
|
nxt_java_Response_ctor, nxt_ptr2jlong(req));
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_addHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
|
|
jarray name, jarray value)
|
|
{
|
|
int rc;
|
|
char *name_str, *value_str;
|
|
jsize name_len, value_len;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
value_len = (*env)->GetArrayLength(env, value);
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 1, name_len + value_len + 2);
|
|
if (req == NULL) {
|
|
return;
|
|
}
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
nxt_unit_req_warn(req, "addHeader: failed to get name content");
|
|
return;
|
|
}
|
|
|
|
value_str = (*env)->GetPrimitiveArrayCritical(env, value, NULL);
|
|
if (value_str == NULL) {
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
nxt_unit_req_warn(req, "addHeader: failed to get value content");
|
|
|
|
return;
|
|
}
|
|
|
|
rc = nxt_unit_response_add_field(req, name_str, name_len,
|
|
value_str, value_len);
|
|
if (rc != NXT_UNIT_OK) {
|
|
// throw
|
|
}
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, value, value_str, 0);
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
}
|
|
|
|
|
|
static nxt_unit_request_info_t *
|
|
nxt_java_get_response_info(jlong req_info_ptr, uint32_t extra_fields,
|
|
uint32_t extra_data)
|
|
{
|
|
int rc;
|
|
char *p;
|
|
uint32_t max_size;
|
|
nxt_unit_buf_t *buf;
|
|
nxt_unit_request_info_t *req;
|
|
nxt_java_request_data_t *data;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
return NULL;
|
|
}
|
|
|
|
data = req->data;
|
|
|
|
if (!nxt_unit_response_is_init(req)) {
|
|
max_size = nxt_unit_buf_max();
|
|
max_size = max_size < data->header_size ? max_size : data->header_size;
|
|
|
|
rc = nxt_unit_response_init(req, 200, 16, max_size);
|
|
if (rc != NXT_UNIT_OK) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
buf = req->response_buf;
|
|
|
|
if (extra_fields > req->response_max_fields
|
|
- req->response->fields_count
|
|
|| extra_data > (uint32_t) (buf->end - buf->free))
|
|
{
|
|
p = buf->start + sizeof(nxt_unit_response_t)
|
|
+ req->response_max_fields * sizeof(nxt_unit_field_t);
|
|
|
|
max_size = 2 * (buf->end - p);
|
|
if (max_size > nxt_unit_buf_max()) {
|
|
nxt_unit_req_warn(req, "required max_size is too big: %"PRIu32,
|
|
max_size);
|
|
return NULL;
|
|
}
|
|
|
|
rc = nxt_unit_response_realloc(req, 2 * req->response_max_fields,
|
|
max_size);
|
|
if (rc != NXT_UNIT_OK) {
|
|
nxt_unit_req_warn(req, "reallocation failed: %"PRIu32", %"PRIu32,
|
|
2 * req->response_max_fields, max_size);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return req;
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_addIntHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
|
|
jarray name, jint value)
|
|
{
|
|
char *name_str;
|
|
jsize name_len;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 1, name_len + 40);
|
|
if (req == NULL) {
|
|
return;
|
|
}
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
nxt_unit_req_warn(req, "addIntHeader: failed to get name content");
|
|
return;
|
|
}
|
|
|
|
nxt_java_add_int_header(req, name_str, name_len, value);
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
}
|
|
|
|
|
|
static void
|
|
nxt_java_add_int_header(nxt_unit_request_info_t *req, const char *name,
|
|
uint8_t name_len, int value)
|
|
{
|
|
char *p;
|
|
nxt_unit_field_t *f;
|
|
nxt_unit_response_t *resp;
|
|
|
|
resp = req->response;
|
|
|
|
f = resp->fields + resp->fields_count;
|
|
p = req->response_buf->free;
|
|
|
|
f->hash = nxt_unit_field_hash(name, name_len);
|
|
f->skip = 0;
|
|
f->name_length = name_len;
|
|
|
|
nxt_unit_sptr_set(&f->name, p);
|
|
memcpy(p, name, name_len);
|
|
p += name_len;
|
|
|
|
nxt_unit_sptr_set(&f->value, p);
|
|
f->value_length = snprintf(p, 40, "%d", (int) value);
|
|
p += f->value_length + 1;
|
|
|
|
resp->fields_count++;
|
|
req->response_buf->free = p;
|
|
|
|
}
|
|
|
|
|
|
static jboolean JNICALL
|
|
nxt_java_Response_containsHeader(JNIEnv *env,
|
|
jclass cls, jlong req_info_ptr, jarray name)
|
|
{
|
|
jboolean res;
|
|
char *name_str;
|
|
jsize name_len;
|
|
nxt_unit_response_t *resp;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (!nxt_unit_response_is_init(req)) {
|
|
nxt_unit_req_debug(req, "containsHeader: response is not initialized");
|
|
return 0;
|
|
}
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_unit_req_debug(req, "containsHeader: response already sent");
|
|
return 0;
|
|
}
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
nxt_unit_req_warn(req, "containsHeader: failed to get name content");
|
|
return 0;
|
|
}
|
|
|
|
resp = req->response;
|
|
|
|
res = nxt_java_findHeader(resp->fields,
|
|
resp->fields + resp->fields_count,
|
|
name_str, name_len) != NULL;
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static jstring JNICALL
|
|
nxt_java_Response_getHeader(JNIEnv *env, jclass cls, jlong req_info_ptr,
|
|
jarray name)
|
|
{
|
|
char *name_str;
|
|
jsize name_len;
|
|
nxt_unit_field_t *f;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (!nxt_unit_response_is_init(req)) {
|
|
nxt_unit_req_debug(req, "getHeader: response is not initialized");
|
|
return NULL;
|
|
}
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_unit_req_debug(req, "getHeader: response already sent");
|
|
return NULL;
|
|
}
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
nxt_unit_req_warn(req, "getHeader: failed to get name content");
|
|
return NULL;
|
|
}
|
|
|
|
f = nxt_java_findHeader(req->response->fields,
|
|
req->response->fields + req->response->fields_count,
|
|
name_str, name_len);
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
|
|
if (f == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return nxt_java_newString(env, nxt_unit_sptr_get(&f->value),
|
|
f->value_length);
|
|
}
|
|
|
|
|
|
static jobject JNICALL
|
|
nxt_java_Response_getHeaderNames(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (!nxt_unit_response_is_init(req)) {
|
|
nxt_unit_req_debug(req, "getHeaderNames: response is not initialized");
|
|
return NULL;
|
|
}
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_unit_req_debug(req, "getHeaderNames: response already sent");
|
|
return NULL;
|
|
}
|
|
|
|
return nxt_java_newHeaderNamesEnumeration(env, req->response->fields,
|
|
req->response->fields_count);
|
|
}
|
|
|
|
|
|
static jobject JNICALL
|
|
nxt_java_Response_getHeaders(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name)
|
|
{
|
|
char *name_str;
|
|
jsize name_len;
|
|
nxt_unit_field_t *f;
|
|
nxt_unit_response_t *resp;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (!nxt_unit_response_is_init(req)) {
|
|
nxt_unit_req_debug(req, "getHeaders: response is not initialized");
|
|
return NULL;
|
|
}
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_unit_req_debug(req, "getHeaders: response already sent");
|
|
return NULL;
|
|
}
|
|
|
|
resp = req->response;
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
nxt_unit_req_warn(req, "getHeaders: failed to get name content");
|
|
return NULL;
|
|
}
|
|
|
|
f = nxt_java_findHeader(resp->fields, resp->fields + resp->fields_count,
|
|
name_str, name_len);
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
|
|
if (f == NULL) {
|
|
f = resp->fields + resp->fields_count;
|
|
}
|
|
|
|
return nxt_java_newHeadersEnumeration(env, resp->fields, resp->fields_count,
|
|
f - resp->fields);
|
|
}
|
|
|
|
|
|
static jint JNICALL
|
|
nxt_java_Response_getStatus(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (!nxt_unit_response_is_init(req)) {
|
|
nxt_unit_req_debug(req, "getStatus: response is not initialized");
|
|
return 200;
|
|
}
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_unit_req_debug(req, "getStatus: response already sent");
|
|
return 200;
|
|
}
|
|
|
|
return req->response->status;
|
|
}
|
|
|
|
|
|
static jobject JNICALL
|
|
nxt_java_Response_getRequest(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
nxt_java_request_data_t *data;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
data = req->data;
|
|
|
|
return data->jreq;
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_commit(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
nxt_java_OutputStream_flush_buf(env, req);
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_sendRedirect(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray loc)
|
|
{
|
|
int rc;
|
|
char *loc_str;
|
|
jsize loc_len;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
static const char location[] = "Location";
|
|
static const uint32_t location_len = sizeof(location) - 1;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_java_throw_IllegalStateException(env, "Response already sent");
|
|
|
|
return;
|
|
}
|
|
|
|
loc_len = (*env)->GetArrayLength(env, loc);
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 1,
|
|
location_len + loc_len + 2);
|
|
if (req == NULL) {
|
|
return;
|
|
}
|
|
|
|
loc_str = (*env)->GetPrimitiveArrayCritical(env, loc, NULL);
|
|
if (loc_str == NULL) {
|
|
nxt_unit_req_warn(req, "sendRedirect: failed to get loc content");
|
|
return;
|
|
}
|
|
|
|
req->response->status = 302;
|
|
|
|
rc = nxt_java_response_set_header(req_info_ptr, location, location_len,
|
|
loc_str, loc_len);
|
|
if (rc != NXT_UNIT_OK) {
|
|
// throw
|
|
}
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, loc, loc_str, 0);
|
|
|
|
nxt_unit_response_send(req);
|
|
}
|
|
|
|
|
|
static int
|
|
nxt_java_response_set_header(jlong req_info_ptr,
|
|
const char *name, jint name_len, const char *value, jint value_len)
|
|
{
|
|
int add_field;
|
|
char *dst;
|
|
nxt_unit_field_t *f, *e;
|
|
nxt_unit_response_t *resp;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 0, 0);
|
|
if (req == NULL) {
|
|
return NXT_UNIT_ERROR;
|
|
}
|
|
|
|
resp = req->response;
|
|
|
|
f = resp->fields;
|
|
e = f + resp->fields_count;
|
|
|
|
add_field = 1;
|
|
|
|
for ( ;; ) {
|
|
f = nxt_java_findHeader(f, e, name, name_len);
|
|
if (f == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (add_field && f->value_length >= (uint32_t) value_len) {
|
|
dst = nxt_unit_sptr_get(&f->value);
|
|
memcpy(dst, value, value_len);
|
|
dst[value_len] = '\0';
|
|
f->value_length = value_len;
|
|
|
|
add_field = 0;
|
|
f->skip = 0;
|
|
|
|
} else {
|
|
f->skip = 1;
|
|
}
|
|
|
|
++f;
|
|
}
|
|
|
|
if (!add_field) {
|
|
return NXT_UNIT_OK;
|
|
}
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 1, name_len + value_len + 2);
|
|
if (req == NULL) {
|
|
return NXT_UNIT_ERROR;
|
|
}
|
|
|
|
return nxt_unit_response_add_field(req, name, name_len, value, value_len);
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_setHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name, jarray value)
|
|
{
|
|
int rc;
|
|
char *name_str, *value_str;
|
|
jsize name_len, value_len;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
nxt_unit_req_warn(req, "setHeader: failed to get name content");
|
|
return;
|
|
}
|
|
|
|
value_str = (*env)->GetPrimitiveArrayCritical(env, value, NULL);
|
|
if (value_str == NULL) {
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
nxt_unit_req_warn(req, "setHeader: failed to get value content");
|
|
|
|
return;
|
|
}
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
value_len = (*env)->GetArrayLength(env, value);
|
|
|
|
rc = nxt_java_response_set_header(req_info_ptr, name_str, name_len,
|
|
value_str, value_len);
|
|
if (rc != NXT_UNIT_OK) {
|
|
// throw
|
|
}
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, value, value_str, 0);
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_removeHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name)
|
|
{
|
|
int rc;
|
|
char *name_str;
|
|
jsize name_len;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
nxt_unit_req_warn(req, "setHeader: failed to get name content");
|
|
return;
|
|
}
|
|
|
|
rc = nxt_java_response_remove_header(req_info_ptr, name_str, name_len);
|
|
if (rc != NXT_UNIT_OK) {
|
|
// throw
|
|
}
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
nxt_java_response_remove_header(jlong req_info_ptr,
|
|
const char *name, jint name_len)
|
|
{
|
|
nxt_unit_field_t *f, *e;
|
|
nxt_unit_response_t *resp;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 0, 0);
|
|
if (req == NULL) {
|
|
return NXT_UNIT_ERROR;
|
|
}
|
|
|
|
resp = req->response;
|
|
|
|
f = resp->fields;
|
|
e = f + resp->fields_count;
|
|
|
|
for ( ;; ) {
|
|
f = nxt_java_findHeader(f, e, name, name_len);
|
|
if (f == NULL) {
|
|
break;
|
|
}
|
|
|
|
f->skip = 1;
|
|
|
|
++f;
|
|
}
|
|
|
|
return NXT_UNIT_OK;
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_setIntHeader(JNIEnv *env, jclass cls,
|
|
jlong req_info_ptr, jarray name, jint value)
|
|
{
|
|
int value_len, rc;
|
|
char value_str[40];
|
|
char *name_str;
|
|
jsize name_len;
|
|
|
|
value_len = snprintf(value_str, sizeof(value_str), "%d", (int) value);
|
|
|
|
name_len = (*env)->GetArrayLength(env, name);
|
|
|
|
name_str = (*env)->GetPrimitiveArrayCritical(env, name, NULL);
|
|
if (name_str == NULL) {
|
|
nxt_unit_req_warn(nxt_jlong2ptr(req_info_ptr),
|
|
"setIntHeader: failed to get name content");
|
|
return;
|
|
}
|
|
|
|
rc = nxt_java_response_set_header(req_info_ptr, name_str, name_len,
|
|
value_str, value_len);
|
|
if (rc != NXT_UNIT_OK) {
|
|
// throw
|
|
}
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, name, name_str, 0);
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_setStatus(JNIEnv *env, jclass cls, jlong req_info_ptr,
|
|
jint sc)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 0, 0);
|
|
if (req == NULL) {
|
|
return;
|
|
}
|
|
|
|
req->response->status = sc;
|
|
}
|
|
|
|
|
|
static jstring JNICALL
|
|
nxt_java_Response_getContentType(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_field_t *f;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (!nxt_unit_response_is_init(req)) {
|
|
nxt_unit_req_debug(req, "getContentType: response is not initialized");
|
|
return NULL;
|
|
}
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_unit_req_debug(req, "getContentType: response already sent");
|
|
return NULL;
|
|
}
|
|
|
|
f = nxt_java_findHeader(req->response->fields,
|
|
req->response->fields + req->response->fields_count,
|
|
"Content-Type", sizeof("Content-Type") - 1);
|
|
|
|
if (f == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return nxt_java_newString(env, nxt_unit_sptr_get(&f->value),
|
|
f->value_length);
|
|
}
|
|
|
|
|
|
static jboolean JNICALL
|
|
nxt_java_Response_isCommitted(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_reset(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_buf_t *buf;
|
|
nxt_unit_request_info_t *req;
|
|
nxt_java_request_data_t *data;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
|
|
if (nxt_unit_response_is_sent(req)) {
|
|
nxt_java_throw_IllegalStateException(env, "Response already sent");
|
|
|
|
return;
|
|
}
|
|
|
|
data = req->data;
|
|
|
|
if (data->buf != NULL && data->buf->free > data->buf->start) {
|
|
data->buf->free = data->buf->start;
|
|
}
|
|
|
|
if (nxt_unit_response_is_init(req)) {
|
|
req->response->status = 200;
|
|
req->response->fields_count = 0;
|
|
|
|
buf = req->response_buf;
|
|
|
|
buf->free = buf->start + sizeof(nxt_unit_response_t)
|
|
+ req->response_max_fields * sizeof(nxt_unit_field_t);
|
|
}
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_resetBuffer(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
nxt_java_request_data_t *data;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
data = req->data;
|
|
|
|
if (data->buf != NULL && data->buf->free > data->buf->start) {
|
|
data->buf->free = data->buf->start;
|
|
}
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_setBufferSize(JNIEnv *env, jclass cls, jlong req_info_ptr,
|
|
jint size)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
nxt_java_request_data_t *data;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
data = req->data;
|
|
|
|
if (data->buf_size == (uint32_t) size) {
|
|
return;
|
|
}
|
|
|
|
if (data->buf != NULL && data->buf->free > data->buf->start) {
|
|
nxt_java_throw_IllegalStateException(env, "Buffer is not empty");
|
|
|
|
return;
|
|
}
|
|
|
|
data->buf_size = size;
|
|
|
|
if (data->buf_size > nxt_unit_buf_max()) {
|
|
data->buf_size = nxt_unit_buf_max();
|
|
}
|
|
|
|
if (data->buf != NULL
|
|
&& (uint32_t) (data->buf->end - data->buf->start) < data->buf_size)
|
|
{
|
|
nxt_unit_buf_free(data->buf);
|
|
|
|
data->buf = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static jint JNICALL
|
|
nxt_java_Response_getBufferSize(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
nxt_java_request_data_t *data;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
data = req->data;
|
|
|
|
return data->buf_size;
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_setContentLength(JNIEnv *env, jclass cls, jlong req_info_ptr,
|
|
jlong len)
|
|
{
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_java_get_response_info(req_info_ptr, 0, 0);
|
|
if (req == NULL) {
|
|
return;
|
|
}
|
|
|
|
req->response->content_length = len;
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_setContentType(JNIEnv *env, jclass cls, jlong req_info_ptr,
|
|
jarray type)
|
|
{
|
|
int rc;
|
|
char *type_str;
|
|
jsize type_len;
|
|
|
|
static const char content_type[] = "Content-Type";
|
|
static const uint32_t content_type_len = sizeof(content_type) - 1;
|
|
|
|
type_len = (*env)->GetArrayLength(env, type);
|
|
|
|
type_str = (*env)->GetPrimitiveArrayCritical(env, type, NULL);
|
|
if (type_str == NULL) {
|
|
return;
|
|
}
|
|
|
|
rc = nxt_java_response_set_header(req_info_ptr,
|
|
content_type, content_type_len,
|
|
type_str, type_len);
|
|
if (rc != NXT_UNIT_OK) {
|
|
// throw
|
|
}
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, type, type_str, 0);
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_removeContentType(JNIEnv *env, jclass cls, jlong req_info_ptr)
|
|
{
|
|
nxt_java_response_remove_header(req_info_ptr, "Content-Type",
|
|
sizeof("Content-Type") - 1);
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_log(JNIEnv *env, jclass cls, jlong req_info_ptr, jarray msg)
|
|
{
|
|
char *msg_str;
|
|
jsize msg_len;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
msg_len = (*env)->GetArrayLength(env, msg);
|
|
|
|
msg_str = (*env)->GetPrimitiveArrayCritical(env, msg, NULL);
|
|
if (msg_str == NULL) {
|
|
nxt_unit_req_warn(req, "log: failed to get msg content");
|
|
return;
|
|
}
|
|
|
|
nxt_unit_req_log(req, NXT_UNIT_LOG_INFO, "%.*s", msg_len, msg_str);
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, msg, msg_str, 0);
|
|
}
|
|
|
|
|
|
static void JNICALL
|
|
nxt_java_Response_trace(JNIEnv *env, jclass cls, jlong req_info_ptr, jarray msg)
|
|
{
|
|
#if (NXT_DEBUG)
|
|
char *msg_str;
|
|
jsize msg_len;
|
|
nxt_unit_request_info_t *req;
|
|
|
|
req = nxt_jlong2ptr(req_info_ptr);
|
|
msg_len = (*env)->GetArrayLength(env, msg);
|
|
|
|
msg_str = (*env)->GetPrimitiveArrayCritical(env, msg, NULL);
|
|
if (msg_str == NULL) {
|
|
nxt_unit_req_warn(req, "trace: failed to get msg content");
|
|
return;
|
|
}
|
|
|
|
nxt_unit_req_debug(req, "%.*s", msg_len, msg_str);
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, msg, msg_str, 0);
|
|
#endif
|
|
}
|
|
|