Upload Modules
This commit is contained in:
653
ngx_http_flv_module/ngx_rtmp_amf.c
Normal file
653
ngx_http_flv_module/ngx_rtmp_amf.c
Normal file
@@ -0,0 +1,653 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Winshining
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_amf.h"
|
||||
#include "ngx_rtmp.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static ngx_inline void*
|
||||
ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len)
|
||||
{
|
||||
if (dst == NULL || src == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_LITTLE_ENDIAN)
|
||||
ngx_rtmp_rmemcpy(dst, src, len);
|
||||
#else
|
||||
ngx_memcpy(dst, src, len);
|
||||
#endif
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
#define NGX_RTMP_AMF_DEBUG_SIZE 16
|
||||
|
||||
#ifdef NGX_DEBUG
|
||||
static void
|
||||
ngx_rtmp_amf_debug(const char* op, ngx_log_t *log, u_char *p, size_t n)
|
||||
{
|
||||
u_char hstr[3 * NGX_RTMP_AMF_DEBUG_SIZE + 1];
|
||||
u_char str[NGX_RTMP_AMF_DEBUG_SIZE + 1];
|
||||
u_char *hp, *sp;
|
||||
static u_char hex[] = "0123456789ABCDEF";
|
||||
size_t i;
|
||||
|
||||
hp = hstr;
|
||||
sp = str;
|
||||
|
||||
for(i = 0; i < n && i < NGX_RTMP_AMF_DEBUG_SIZE; ++i) {
|
||||
*hp++ = ' ';
|
||||
if (p) {
|
||||
*hp++ = hex[(*p & 0xf0) >> 4];
|
||||
*hp++ = hex[*p & 0x0f];
|
||||
*sp++ = (*p >= 0x20 && *p <= 0x7e) ?
|
||||
*p : (u_char)'?';
|
||||
++p;
|
||||
} else {
|
||||
*hp++ = 'X';
|
||||
*hp++ = 'X';
|
||||
*sp++ = '?';
|
||||
}
|
||||
}
|
||||
*hp = *sp = '\0';
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, log, 0,
|
||||
"AMF %s (%d)%s '%s'", op, n, hstr, str);
|
||||
}
|
||||
#endif
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_get(ngx_rtmp_amf_ctx_t *ctx, void *p, size_t n)
|
||||
{
|
||||
size_t size;
|
||||
ngx_chain_t *l;
|
||||
size_t offset;
|
||||
u_char *pos, *last;
|
||||
#ifdef NGX_DEBUG
|
||||
void *op = p;
|
||||
size_t on = n;
|
||||
#endif
|
||||
|
||||
if (!n)
|
||||
return NGX_OK;
|
||||
|
||||
for(l = ctx->link, offset = ctx->offset; l; l = l->next, offset = 0) {
|
||||
|
||||
pos = l->buf->pos + offset;
|
||||
last = l->buf->last;
|
||||
|
||||
if (last >= pos + n) {
|
||||
if (p) {
|
||||
p = ngx_cpymem(p, pos, n);
|
||||
}
|
||||
ctx->offset = offset + n;
|
||||
ctx->link = l;
|
||||
|
||||
#ifdef NGX_DEBUG
|
||||
ngx_rtmp_amf_debug("read", ctx->log, (u_char*)op, on);
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
size = last - pos;
|
||||
|
||||
if (p) {
|
||||
p = ngx_cpymem(p, pos, size);
|
||||
}
|
||||
|
||||
n -= size;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ctx->log, 0,
|
||||
"AMF read eof (%d)", n);
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_put(ngx_rtmp_amf_ctx_t *ctx, void *p, size_t n)
|
||||
{
|
||||
ngx_buf_t *b;
|
||||
size_t size;
|
||||
ngx_chain_t *l, *ln;
|
||||
|
||||
#ifdef NGX_DEBUG
|
||||
ngx_rtmp_amf_debug("write", ctx->log, (u_char*)p, n);
|
||||
#endif
|
||||
|
||||
l = ctx->link;
|
||||
|
||||
if (ctx->link && ctx->first == NULL) {
|
||||
ctx->first = ctx->link;
|
||||
}
|
||||
|
||||
while(n) {
|
||||
b = l ? l->buf : NULL;
|
||||
|
||||
if (b == NULL || b->last == b->end) {
|
||||
|
||||
ln = ctx->alloc(ctx->arg);
|
||||
if (ln == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->first == NULL) {
|
||||
ctx->first = ln;
|
||||
}
|
||||
|
||||
if (l) {
|
||||
l->next = ln;
|
||||
}
|
||||
|
||||
l = ln;
|
||||
ctx->link = l;
|
||||
b = l->buf;
|
||||
}
|
||||
|
||||
size = b->end - b->last;
|
||||
|
||||
if (size >= n) {
|
||||
b->last = ngx_cpymem(b->last, p, n);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
b->last = ngx_cpymem(b->last, p, size);
|
||||
p = (u_char*)p + size;
|
||||
n -= size;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||||
size_t nelts)
|
||||
{
|
||||
uint8_t type;
|
||||
uint16_t len;
|
||||
size_t n, namelen, maxlen;
|
||||
ngx_int_t rc;
|
||||
u_char buf[2];
|
||||
|
||||
maxlen = 0;
|
||||
for(n = 0; n < nelts; ++n) {
|
||||
namelen = elts[n].name.len;
|
||||
if (namelen > maxlen)
|
||||
maxlen = namelen;
|
||||
}
|
||||
|
||||
for( ;; ) {
|
||||
|
||||
#if !(NGX_WIN32)
|
||||
char name[maxlen];
|
||||
#else
|
||||
char name[1024];
|
||||
if (maxlen > sizeof(name)) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
/* read key */
|
||||
switch (ngx_rtmp_amf_get(ctx, buf, 2)) {
|
||||
case NGX_DONE:
|
||||
/* Envivio sends unfinalized arrays */
|
||||
return NGX_OK;
|
||||
case NGX_OK:
|
||||
break;
|
||||
default:
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_rtmp_amf_reverse_copy(&len, buf, 2);
|
||||
|
||||
if (!len)
|
||||
break;
|
||||
|
||||
if (len <= maxlen) {
|
||||
rc = ngx_rtmp_amf_get(ctx, name, len);
|
||||
|
||||
} else {
|
||||
rc = ngx_rtmp_amf_get(ctx, name, maxlen);
|
||||
if (rc != NGX_OK)
|
||||
return NGX_ERROR;
|
||||
rc = ngx_rtmp_amf_get(ctx, 0, len - maxlen);
|
||||
}
|
||||
|
||||
if (rc != NGX_OK)
|
||||
return NGX_ERROR;
|
||||
|
||||
/* TODO: if we require array to be sorted on name
|
||||
* then we could be able to use binary search */
|
||||
for(n = 0; n < nelts
|
||||
&& (len != elts[n].name.len
|
||||
|| ngx_strncmp(name, elts[n].name.data, len));
|
||||
++n);
|
||||
|
||||
if (ngx_rtmp_amf_read(ctx, n < nelts ? &elts[n] : NULL, 1) != NGX_OK)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_amf_get(ctx, &type, 1) != NGX_OK
|
||||
|| type != NGX_RTMP_AMF_END)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_read_array(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||||
size_t nelts)
|
||||
{
|
||||
uint32_t len;
|
||||
size_t n;
|
||||
u_char buf[4];
|
||||
|
||||
/* read length */
|
||||
if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK)
|
||||
return NGX_ERROR;
|
||||
|
||||
ngx_rtmp_amf_reverse_copy(&len, buf, 4);
|
||||
|
||||
for (n = 0; n < len; ++n) {
|
||||
if (ngx_rtmp_amf_read(ctx, n < nelts ? &elts[n] : NULL, 1) != NGX_OK)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_read_variant(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||||
size_t nelts)
|
||||
{
|
||||
uint8_t type;
|
||||
ngx_int_t rc;
|
||||
size_t n;
|
||||
ngx_rtmp_amf_elt_t elt;
|
||||
|
||||
rc = ngx_rtmp_amf_get(ctx, &type, 1);
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ngx_memzero(&elt, sizeof(elt));
|
||||
for (n = 0; n < nelts; ++n, ++elts) {
|
||||
if (type == elts->type) {
|
||||
elt.data = elts->data;
|
||||
elt.len = elts->len;
|
||||
}
|
||||
}
|
||||
|
||||
elt.type = type | NGX_RTMP_AMF_TYPELESS;
|
||||
|
||||
return ngx_rtmp_amf_read(ctx, &elt, 1);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_is_compatible_type(uint8_t t1, uint8_t t2)
|
||||
{
|
||||
return t1 == t2
|
||||
|| (t1 == NGX_RTMP_AMF_OBJECT && t2 == NGX_RTMP_AMF_MIXED_ARRAY)
|
||||
|| (t2 == NGX_RTMP_AMF_OBJECT && t1 == NGX_RTMP_AMF_MIXED_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||||
size_t nelts)
|
||||
{
|
||||
void *data;
|
||||
ngx_int_t type;
|
||||
uint8_t type8;
|
||||
size_t n;
|
||||
uint16_t len;
|
||||
ngx_int_t rc;
|
||||
u_char buf[8];
|
||||
uint32_t max_index;
|
||||
|
||||
for(n = 0; n < nelts; ++n) {
|
||||
|
||||
if (elts && elts->type & NGX_RTMP_AMF_TYPELESS) {
|
||||
type = elts->type & ~NGX_RTMP_AMF_TYPELESS;
|
||||
data = elts->data;
|
||||
|
||||
} else {
|
||||
switch (ngx_rtmp_amf_get(ctx, &type8, 1)) {
|
||||
case NGX_DONE:
|
||||
if (elts && elts->type & NGX_RTMP_AMF_OPTIONAL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
case NGX_ERROR:
|
||||
return NGX_ERROR;
|
||||
}
|
||||
type = type8;
|
||||
data = (elts &&
|
||||
ngx_rtmp_amf_is_compatible_type(
|
||||
(uint8_t) (elts->type & 0xff), (uint8_t) type))
|
||||
? elts->data
|
||||
: NULL;
|
||||
|
||||
if (elts && (elts->type & NGX_RTMP_AMF_CONTEXT)) {
|
||||
if (data) {
|
||||
*(ngx_rtmp_amf_ctx_t *) data = *ctx;
|
||||
}
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NGX_RTMP_AMF_NUMBER:
|
||||
if (ngx_rtmp_amf_get(ctx, buf, 8) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_rtmp_amf_reverse_copy(data, buf, 8);
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_BOOLEAN:
|
||||
if (ngx_rtmp_amf_get(ctx, data, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_STRING:
|
||||
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_rtmp_amf_reverse_copy(&len, buf, 2);
|
||||
|
||||
if (data == NULL) {
|
||||
rc = ngx_rtmp_amf_get(ctx, data, len);
|
||||
|
||||
} else if (elts && elts->len <= len) {
|
||||
rc = ngx_rtmp_amf_get(ctx, data, elts->len - 1);
|
||||
if (rc != NGX_OK)
|
||||
return NGX_ERROR;
|
||||
((char*)data)[elts->len - 1] = 0;
|
||||
rc = ngx_rtmp_amf_get(ctx, NULL, len - elts->len + 1);
|
||||
|
||||
} else {
|
||||
rc = ngx_rtmp_amf_get(ctx, data, len);
|
||||
((char*)data)[len] = 0;
|
||||
}
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_NULL:
|
||||
case NGX_RTMP_AMF_ARRAY_NULL:
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_MIXED_ARRAY:
|
||||
if (ngx_rtmp_amf_get(ctx, &max_index, 4) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
case NGX_RTMP_AMF_OBJECT:
|
||||
if (ngx_rtmp_amf_read_object(ctx, data,
|
||||
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
|
||||
) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_ARRAY:
|
||||
if (ngx_rtmp_amf_read_array(ctx, data,
|
||||
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
|
||||
) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_VARIANT_:
|
||||
if (ngx_rtmp_amf_read_variant(ctx, data,
|
||||
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
|
||||
) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_INT8:
|
||||
if (ngx_rtmp_amf_get(ctx, data, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_INT16:
|
||||
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_rtmp_amf_reverse_copy(data, buf, 2);
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_INT32:
|
||||
if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_rtmp_amf_reverse_copy(data, buf, 4);
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_END:
|
||||
return NGX_OK;
|
||||
|
||||
default:
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (elts) {
|
||||
++elts;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx,
|
||||
ngx_rtmp_amf_elt_t *elts, size_t nelts)
|
||||
{
|
||||
uint16_t len;
|
||||
size_t n;
|
||||
u_char buf[2];
|
||||
|
||||
for(n = 0; n < nelts; ++n) {
|
||||
|
||||
len = (uint16_t) elts[n].name.len;
|
||||
|
||||
if (ngx_rtmp_amf_put(ctx,
|
||||
ngx_rtmp_amf_reverse_copy(buf,
|
||||
&len, 2), 2) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_amf_put(ctx, elts[n].name.data, len) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_amf_write(ctx, &elts[n], 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_rtmp_amf_put(ctx, "\0\0", 2) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_amf_write_array(ngx_rtmp_amf_ctx_t *ctx,
|
||||
ngx_rtmp_amf_elt_t *elts, size_t nelts)
|
||||
{
|
||||
uint32_t len;
|
||||
size_t n;
|
||||
u_char buf[4];
|
||||
|
||||
len = nelts;
|
||||
if (ngx_rtmp_amf_put(ctx,
|
||||
ngx_rtmp_amf_reverse_copy(buf,
|
||||
&len, 4), 4) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
for(n = 0; n < nelts; ++n) {
|
||||
if (ngx_rtmp_amf_write(ctx, &elts[n], 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
|
||||
ngx_rtmp_amf_elt_t *elts, size_t nelts)
|
||||
{
|
||||
size_t n;
|
||||
ngx_int_t type;
|
||||
uint8_t type8;
|
||||
void *data;
|
||||
uint16_t len;
|
||||
uint32_t max_index;
|
||||
u_char buf[8];
|
||||
|
||||
for(n = 0; n < nelts; ++n) {
|
||||
|
||||
type = elts[n].type;
|
||||
data = elts[n].data;
|
||||
len = (uint16_t) elts[n].len;
|
||||
|
||||
if (type & NGX_RTMP_AMF_TYPELESS) {
|
||||
type &= ~NGX_RTMP_AMF_TYPELESS;
|
||||
} else {
|
||||
type8 = (uint8_t)type;
|
||||
if (ngx_rtmp_amf_put(ctx, &type8, 1) != NGX_OK)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case NGX_RTMP_AMF_NUMBER:
|
||||
if (ngx_rtmp_amf_put(ctx,
|
||||
ngx_rtmp_amf_reverse_copy(buf,
|
||||
data, 8), 8) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_BOOLEAN:
|
||||
if (ngx_rtmp_amf_put(ctx, data, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_STRING:
|
||||
if (len == 0 && data) {
|
||||
len = (uint16_t) ngx_strlen((u_char*) data);
|
||||
}
|
||||
|
||||
if (ngx_rtmp_amf_put(ctx,
|
||||
ngx_rtmp_amf_reverse_copy(buf,
|
||||
&len, 2), 2) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_amf_put(ctx, data, len) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_NULL:
|
||||
case NGX_RTMP_AMF_ARRAY_NULL:
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_MIXED_ARRAY:
|
||||
max_index = 0;
|
||||
if (ngx_rtmp_amf_put(ctx, &max_index, 4) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
case NGX_RTMP_AMF_OBJECT:
|
||||
type8 = NGX_RTMP_AMF_END;
|
||||
if (ngx_rtmp_amf_write_object(ctx, data,
|
||||
elts[n].len / sizeof(ngx_rtmp_amf_elt_t)) != NGX_OK
|
||||
|| ngx_rtmp_amf_put(ctx, &type8, 1) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_ARRAY:
|
||||
if (ngx_rtmp_amf_write_array(ctx, data,
|
||||
elts[n].len / sizeof(ngx_rtmp_amf_elt_t)) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_INT8:
|
||||
if (ngx_rtmp_amf_put(ctx, data, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_INT16:
|
||||
if (ngx_rtmp_amf_put(ctx,
|
||||
ngx_rtmp_amf_reverse_copy(buf,
|
||||
data, 2), 2) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_AMF_INT32:
|
||||
if (ngx_rtmp_amf_put(ctx,
|
||||
ngx_rtmp_amf_reverse_copy(buf,
|
||||
data, 4), 4) != NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user