~ubuntu-branches/ubuntu/maverick/couchdb/maverick

« back to all changes in this revision

Viewing changes to src/couchdb/priv/couch_js/http.c

  • Committer: James Westby
  • Date: 2010-06-15 11:22:10 UTC
  • mfrom: (36.1.4 couchdb)
  • Revision ID: james.westby@linaro.org-20100615112210-s7ibg9pis9ixd7or
Tags: 0.11.0-1ubuntu1
Merge from debian unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
 
2
// use this file except in compliance with the License. You may obtain a copy of
 
3
// the License at
 
4
//
 
5
//   http://www.apache.org/licenses/LICENSE-2.0
 
6
//
 
7
// Unless required by applicable law or agreed to in writing, software
 
8
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
9
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
10
// License for the specific language governing permissions and limitations under
 
11
// the License.
 
12
 
 
13
#include <stdlib.h>
 
14
#include <string.h>
 
15
#include <jsapi.h>
 
16
#include <curl/curl.h>
 
17
 
 
18
#include "utf8.h"
 
19
 
 
20
#ifdef XP_WIN
 
21
// Map some of the string function names to things which exist on Windows
 
22
#define strcasecmp _strcmpi
 
23
#define strncasecmp _strnicmp
 
24
#define snprintf _snprintf
 
25
#endif
 
26
 
 
27
typedef struct curl_slist CurlHeaders;
 
28
 
 
29
typedef struct {
 
30
    int             method;
 
31
    char*           url;
 
32
    CurlHeaders*    req_headers;
 
33
    jsint           last_status;
 
34
} HTTPData;
 
35
 
 
36
char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL};
 
37
 
 
38
#define GET     0
 
39
#define HEAD    1
 
40
#define POST    2
 
41
#define PUT     3
 
42
#define DELETE  4
 
43
#define COPY    5
 
44
 
 
45
static JSBool
 
46
go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen);
 
47
 
 
48
static JSString*
 
49
str_from_binary(JSContext* cx, char* data, size_t length);
 
50
 
 
51
static JSBool
 
52
constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
 
53
{
 
54
    HTTPData* http = NULL;
 
55
    JSBool ret = JS_FALSE;
 
56
 
 
57
    http = (HTTPData*) malloc(sizeof(HTTPData));
 
58
    if(!http)
 
59
    {
 
60
        JS_ReportError(cx, "Failed to create CouchHTTP instance.");
 
61
        goto error;
 
62
    }
 
63
 
 
64
    http->method = -1;
 
65
    http->url = NULL;
 
66
    http->req_headers = NULL;
 
67
    http->last_status = -1;
 
68
 
 
69
    if(!JS_SetPrivate(cx, obj, http))
 
70
    {
 
71
        JS_ReportError(cx, "Failed to set private CouchHTTP data.");
 
72
        goto error;
 
73
    }
 
74
    
 
75
    ret = JS_TRUE;
 
76
    goto success;
 
77
 
 
78
error:
 
79
    if(http) free(http);
 
80
 
 
81
success:
 
82
    return ret;
 
83
}
 
84
 
 
85
static void
 
86
destructor(JSContext* cx, JSObject* obj)
 
87
{
 
88
    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
 
89
    if(!http)
 
90
    {
 
91
        fprintf(stderr, "Unable to destroy invalid CouchHTTP instance.\n");
 
92
    }
 
93
    else
 
94
    {
 
95
        if(http->url) free(http->url);
 
96
        if(http->req_headers) curl_slist_free_all(http->req_headers);
 
97
        free(http);
 
98
    }
 
99
}
 
100
 
 
101
static JSBool
 
102
open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
 
103
{    
 
104
    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
 
105
    char* method = NULL;
 
106
    char* url = NULL;
 
107
    JSBool ret = JS_FALSE;
 
108
    int methid;
 
109
 
 
110
    if(!http)
 
111
    {
 
112
        JS_ReportError(cx, "Invalid CouchHTTP instance.");
 
113
        goto done;
 
114
    }
 
115
 
 
116
    if(argv[0] == JSVAL_VOID)
 
117
    {
 
118
        JS_ReportError(cx, "You must specify a method.");
 
119
        goto done;
 
120
    }
 
121
 
 
122
    method = enc_string(cx, argv[0], NULL);
 
123
    if(!method)
 
124
    {
 
125
        JS_ReportError(cx, "Failed to encode method.");
 
126
        goto done;
 
127
    }
 
128
    
 
129
    for(methid = 0; METHODS[methid] != NULL; methid++)
 
130
    {
 
131
        if(strcasecmp(METHODS[methid], method) == 0) break;
 
132
    }
 
133
    
 
134
    if(methid > COPY)
 
135
    {
 
136
        JS_ReportError(cx, "Invalid method specified.");
 
137
        goto done;
 
138
    }
 
139
 
 
140
    http->method = methid;
 
141
 
 
142
    if(argv[1] == JSVAL_VOID)
 
143
    {
 
144
        JS_ReportError(cx, "You must specify a URL.");
 
145
        goto done;
 
146
    }
 
147
 
 
148
    if(http->url)
 
149
    {
 
150
        free(http->url);
 
151
        http->url = NULL;
 
152
    }
 
153
 
 
154
    http->url = enc_string(cx, argv[1], NULL);
 
155
    if(!http->url)
 
156
    {
 
157
        JS_ReportError(cx, "Failed to encode URL.");
 
158
        goto done;
 
159
    }
 
160
    
 
161
    if(argv[2] != JSVAL_VOID && argv[2] != JSVAL_FALSE)
 
162
    {
 
163
        JS_ReportError(cx, "Synchronous flag must be false if specified.");
 
164
        goto done;
 
165
    }
 
166
    
 
167
    if(http->req_headers)
 
168
    {
 
169
        curl_slist_free_all(http->req_headers);
 
170
        http->req_headers = NULL;
 
171
    }
 
172
    
 
173
    // Disable Expect: 100-continue
 
174
    http->req_headers = curl_slist_append(http->req_headers, "Expect:");
 
175
 
 
176
    ret = JS_TRUE;
 
177
 
 
178
done:
 
179
    if(method) free(method);
 
180
    return ret;
 
181
}
 
182
 
 
183
static JSBool
 
184
setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
 
185
{    
 
186
    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
 
187
    char* keystr = NULL;
 
188
    char* valstr = NULL;
 
189
    char* hdrbuf = NULL;
 
190
    size_t hdrlen = -1;
 
191
    JSBool ret = JS_FALSE;
 
192
 
 
193
    if(!http)
 
194
    {
 
195
        JS_ReportError(cx, "Invalid CouchHTTP instance.");
 
196
        goto done;
 
197
    }
 
198
 
 
199
    if(argv[0] == JSVAL_VOID)
 
200
    {
 
201
        JS_ReportError(cx, "You must speciy a header name.");
 
202
        goto done;
 
203
    }
 
204
 
 
205
    keystr = enc_string(cx, argv[0], NULL);
 
206
    if(!keystr)
 
207
    {
 
208
        JS_ReportError(cx, "Failed to encode header name.");
 
209
        goto done;
 
210
    }
 
211
    
 
212
    if(argv[1] == JSVAL_VOID)
 
213
    {
 
214
        JS_ReportError(cx, "You must specify a header value.");
 
215
        goto done;
 
216
    }
 
217
    
 
218
    valstr = enc_string(cx, argv[1], NULL);
 
219
    if(!valstr)
 
220
    {
 
221
        JS_ReportError(cx, "Failed to encode header value.");
 
222
        goto done;
 
223
    }
 
224
    
 
225
    hdrlen = strlen(keystr) + strlen(valstr) + 3;
 
226
    hdrbuf = (char*) malloc(hdrlen * sizeof(char));
 
227
    if(!hdrbuf)
 
228
    {
 
229
        JS_ReportError(cx, "Failed to allocate header buffer.");
 
230
        goto done;
 
231
    }
 
232
    
 
233
    snprintf(hdrbuf, hdrlen, "%s: %s", keystr, valstr);
 
234
    http->req_headers = curl_slist_append(http->req_headers, hdrbuf);
 
235
 
 
236
    ret = JS_TRUE;
 
237
 
 
238
done:
 
239
    if(keystr) free(keystr);
 
240
    if(valstr) free(valstr);
 
241
    if(hdrbuf) free(hdrbuf);
 
242
 
 
243
    return ret;
 
244
}
 
245
 
 
246
static JSBool
 
247
sendreq(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
 
248
{
 
249
    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
 
250
    char* body = NULL;
 
251
    size_t bodylen = 0;
 
252
    JSBool ret = JS_FALSE;
 
253
    
 
254
    if(!http)
 
255
    {
 
256
        JS_ReportError(cx, "Invalid CouchHTTP instance.");
 
257
        goto done;
 
258
    }
 
259
 
 
260
    if(argv[0] != JSVAL_VOID && argv[0] != JS_GetEmptyStringValue(cx))
 
261
    {
 
262
        body = enc_string(cx, argv[0], &bodylen);
 
263
        if(!body)
 
264
        {
 
265
            JS_ReportError(cx, "Failed to encode body.");
 
266
            goto done;
 
267
        }
 
268
    }
 
269
 
 
270
    ret = go(cx, obj, http, body, bodylen);
 
271
 
 
272
done:
 
273
    if(body) free(body);
 
274
    return ret;
 
275
}
 
276
 
 
277
static JSBool
 
278
status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 
279
{
 
280
    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
 
281
    
 
282
    if(!http)
 
283
    {
 
284
        JS_ReportError(cx, "Invalid CouchHTTP instance.");
 
285
        return JS_FALSE;
 
286
    }
 
287
    
 
288
    if(INT_FITS_IN_JSVAL(http->last_status))
 
289
    {
 
290
        *vp = INT_TO_JSVAL(http->last_status);
 
291
        return JS_TRUE;
 
292
    }
 
293
    else
 
294
    {
 
295
        JS_ReportError(cx, "INTERNAL: Invalid last_status");
 
296
        return JS_FALSE;
 
297
    }
 
298
}
 
299
 
 
300
JSClass CouchHTTPClass = {
 
301
    "CouchHTTP",
 
302
    JSCLASS_HAS_PRIVATE
 
303
        | JSCLASS_CONSTRUCT_PROTOTYPE
 
304
        | JSCLASS_HAS_RESERVED_SLOTS(2),
 
305
    JS_PropertyStub,
 
306
    JS_PropertyStub,
 
307
    JS_PropertyStub,
 
308
    JS_PropertyStub,
 
309
    JS_EnumerateStub,
 
310
    JS_ResolveStub,
 
311
    JS_ConvertStub,
 
312
    destructor,
 
313
    JSCLASS_NO_OPTIONAL_MEMBERS
 
314
};
 
315
 
 
316
JSPropertySpec CouchHTTPProperties[] = {
 
317
    {"status", 0, JSPROP_READONLY, status, NULL},
 
318
    {0, 0, 0, 0, 0}
 
319
};
 
320
 
 
321
JSFunctionSpec CouchHTTPFunctions[] = {
 
322
    {"_open", open, 3, 0, 0},
 
323
    {"_setRequestHeader", setheader, 2, 0, 0},
 
324
    {"_send", sendreq, 1, 0, 0},
 
325
    {0, 0, 0, 0, 0}
 
326
};
 
327
 
 
328
JSObject*
 
329
install_http(JSContext* cx, JSObject* glbl)
 
330
{
 
331
    JSObject* klass = NULL;
 
332
    HTTPData* http = NULL;
 
333
 
 
334
    klass = JS_InitClass(
 
335
        cx,
 
336
        glbl,
 
337
        NULL,
 
338
        &CouchHTTPClass,
 
339
        constructor,
 
340
        0,
 
341
        CouchHTTPProperties,
 
342
        CouchHTTPFunctions,
 
343
        NULL,
 
344
        NULL
 
345
    );
 
346
 
 
347
    if(!klass)
 
348
    {
 
349
        fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
 
350
        return NULL;
 
351
    }
 
352
    
 
353
    return klass;
 
354
}
 
355
 
 
356
 
 
357
// Curl Helpers
 
358
 
 
359
typedef struct {
 
360
    HTTPData*   http;
 
361
    JSContext*  cx;
 
362
    JSObject*   resp_headers;
 
363
    char*       sendbuf;
 
364
    size_t      sendlen;
 
365
    size_t      sent;
 
366
    char*       recvbuf;
 
367
    size_t      recvlen;
 
368
    size_t      read;
 
369
} CurlState;
 
370
 
 
371
/*
 
372
 * I really hate doing this but this doesn't have to be
 
373
 * uber awesome, it just has to work.
 
374
 */
 
375
CURL*       HTTP_HANDLE = NULL;
 
376
char        ERRBUF[CURL_ERROR_SIZE];
 
377
 
 
378
static size_t send_body(void *ptr, size_t size, size_t nmem, void *data);
 
379
static int seek_body(void *ptr, curl_off_t offset, int origin);
 
380
static size_t recv_body(void *ptr, size_t size, size_t nmem, void *data);
 
381
static size_t recv_header(void *ptr, size_t size, size_t nmem, void *data);
 
382
 
 
383
static JSBool
 
384
go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
 
385
{
 
386
    CurlState state;
 
387
    JSString* jsbody;
 
388
    JSBool ret = JS_FALSE;
 
389
    jsval tmp;
 
390
    
 
391
    state.cx = cx;
 
392
    state.http = http;
 
393
    
 
394
    state.sendbuf = body;
 
395
    state.sendlen = bodylen;
 
396
    state.sent = 0;
 
397
 
 
398
    state.recvbuf = NULL;
 
399
    state.recvlen = 0;
 
400
    state.read = 0;
 
401
 
 
402
    if(HTTP_HANDLE == NULL)
 
403
    {
 
404
        HTTP_HANDLE = curl_easy_init();
 
405
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_READFUNCTION, send_body);
 
406
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKFUNCTION, seek_body);
 
407
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_HEADERFUNCTION, recv_header);
 
408
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEFUNCTION, recv_body);
 
409
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOPROGRESS, 1);
 
410
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
 
411
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_ERRORBUFFER, ERRBUF);
 
412
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_COOKIEFILE, "");
 
413
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_USERAGENT,
 
414
                                            "CouchHTTP Client - Relax");
 
415
    }
 
416
    
 
417
    if(!HTTP_HANDLE)
 
418
    {
 
419
        JS_ReportError(cx, "Failed to initialize cURL handle.");
 
420
        goto done;
 
421
    }
 
422
 
 
423
    if(http->method < 0 || http->method > COPY)
 
424
    {
 
425
        JS_ReportError(cx, "INTERNAL: Unknown method.");
 
426
        goto done;
 
427
    }
 
428
 
 
429
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_CUSTOMREQUEST, METHODS[http->method]);
 
430
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 0);
 
431
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1);
 
432
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0);
 
433
    
 
434
    if(http->method == HEAD)
 
435
    {
 
436
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 1);
 
437
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
 
438
    }
 
439
    else if(http->method == POST || http->method == PUT)
 
440
    {
 
441
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 1);
 
442
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
 
443
    }
 
444
    
 
445
    if(body && bodylen)
 
446
    {
 
447
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen);        
 
448
    }
 
449
    else
 
450
    {
 
451
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0);
 
452
    }
 
453
 
 
454
    //curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1);
 
455
 
 
456
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url);
 
457
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_HTTPHEADER, http->req_headers);
 
458
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_READDATA, &state);
 
459
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKDATA, &state);
 
460
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state);
 
461
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state);
 
462
 
 
463
    if(curl_easy_perform(HTTP_HANDLE) != 0)
 
464
    {
 
465
        JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF);
 
466
        goto done;
 
467
    }
 
468
    
 
469
    if(!state.resp_headers)
 
470
    {
 
471
        JS_ReportError(cx, "Failed to recieve HTTP headers.");
 
472
        goto done;
 
473
    }
 
474
 
 
475
    tmp = OBJECT_TO_JSVAL(state.resp_headers);
 
476
    if(!JS_DefineProperty(
 
477
        cx,
 
478
        obj,
 
479
        "_headers",
 
480
        tmp,
 
481
        NULL,
 
482
        NULL,
 
483
        JSPROP_READONLY
 
484
    ))
 
485
    {
 
486
        JS_ReportError(cx, "INTERNAL: Failed to set response headers.");
 
487
        goto done;
 
488
    }
 
489
    
 
490
    if(state.recvbuf) // Is good enough?
 
491
    {
 
492
        state.recvbuf[state.read] = '\0';
 
493
        jsbody = dec_string(cx, state.recvbuf, state.read+1);
 
494
        if(!jsbody)
 
495
        {
 
496
            // If we can't decode the body as UTF-8 we forcefully
 
497
            // convert it to a string by just forcing each byte
 
498
            // to a jschar.
 
499
            jsbody = str_from_binary(cx, state.recvbuf, state.read);
 
500
            if(!jsbody) {
 
501
                if(!JS_IsExceptionPending(cx)) {
 
502
                    JS_ReportError(cx, "INTERNAL: Failed to decode body.");
 
503
                }
 
504
                goto done;
 
505
            }
 
506
        }
 
507
        tmp = STRING_TO_JSVAL(jsbody);
 
508
    }
 
509
    else
 
510
    {
 
511
        tmp = JS_GetEmptyStringValue(cx);
 
512
    }
 
513
    
 
514
    if(!JS_DefineProperty(
 
515
        cx,
 
516
        obj,
 
517
        "responseText",
 
518
        tmp,
 
519
        NULL,
 
520
        NULL,
 
521
        JSPROP_READONLY
 
522
    ))
 
523
    {
 
524
        JS_ReportError(cx, "INTERNAL: Failed to set responseText.");
 
525
        goto done;
 
526
    }
 
527
    
 
528
    ret = JS_TRUE;
 
529
 
 
530
done:
 
531
    if(state.recvbuf) JS_free(cx, state.recvbuf);
 
532
    return ret;
 
533
}
 
534
 
 
535
static size_t
 
536
send_body(void *ptr, size_t size, size_t nmem, void *data)
 
537
{
 
538
    CurlState* state = (CurlState*) data;
 
539
    size_t length = size * nmem;
 
540
    size_t towrite = state->sendlen - state->sent;
 
541
    if(towrite == 0)
 
542
    {
 
543
        return 0;
 
544
    }
 
545
 
 
546
    if(length < towrite) towrite = length;
 
547
 
 
548
    //fprintf(stderr, "%lu %lu %lu %lu\n", state->bodyused, state->bodyread, length, towrite);
 
549
 
 
550
    memcpy(ptr, state->sendbuf + state->sent, towrite);
 
551
    state->sent += towrite;
 
552
 
 
553
    return towrite;
 
554
}
 
555
 
 
556
static int
 
557
seek_body(void* ptr, curl_off_t offset, int origin)
 
558
{
 
559
    CurlState* state = (CurlState*) ptr;
 
560
    if(origin != SEEK_SET) return -1;
 
561
 
 
562
    state->sent = (size_t) offset;
 
563
    return (int) state->sent;
 
564
}
 
565
 
 
566
static size_t
 
567
recv_header(void *ptr, size_t size, size_t nmem, void *data)
 
568
{
 
569
    CurlState* state = (CurlState*) data;
 
570
    char code[4];
 
571
    char* header = (char*) ptr;
 
572
    size_t length = size * nmem;
 
573
    size_t index = 0;
 
574
    JSString* hdr = NULL;
 
575
    jsuint hdrlen;
 
576
    jsval hdrval;
 
577
    
 
578
    if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0)
 
579
    {
 
580
        if(length < 12)
 
581
        {
 
582
            return CURLE_WRITE_ERROR;
 
583
        }
 
584
 
 
585
        memcpy(code, header+9, 3*sizeof(char));
 
586
        code[3] = '\0';
 
587
        state->http->last_status = atoi(code);
 
588
 
 
589
        state->resp_headers = JS_NewArrayObject(state->cx, 0, NULL);
 
590
        if(!state->resp_headers)
 
591
        {
 
592
            return CURLE_WRITE_ERROR;
 
593
        }
 
594
 
 
595
        return length;
 
596
    }
 
597
 
 
598
    // We get a notice at the \r\n\r\n after headers.
 
599
    if(length <= 2)
 
600
    {
 
601
        return length;
 
602
    }
 
603
 
 
604
    // Append the new header to our array.
 
605
    hdr = dec_string(state->cx, header, length);
 
606
    if(!hdr)
 
607
    {
 
608
        return CURLE_WRITE_ERROR;
 
609
    }
 
610
 
 
611
    if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen))
 
612
    {
 
613
        return CURLE_WRITE_ERROR;
 
614
    }
 
615
 
 
616
    hdrval = STRING_TO_JSVAL(hdr);
 
617
    if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval))
 
618
    {
 
619
        return CURLE_WRITE_ERROR;
 
620
    }
 
621
 
 
622
    return length;
 
623
}
 
624
 
 
625
static size_t
 
626
recv_body(void *ptr, size_t size, size_t nmem, void *data)
 
627
{
 
628
    CurlState* state = (CurlState*) data;
 
629
    size_t length = size * nmem;
 
630
    char* tmp = NULL;
 
631
    
 
632
    if(!state->recvbuf)
 
633
    {
 
634
        state->recvlen = 4096;
 
635
        state->read = 0;
 
636
        state->recvbuf = JS_malloc(state->cx, state->recvlen);
 
637
    }
 
638
    
 
639
    if(!state->recvbuf)
 
640
    {
 
641
        return CURLE_WRITE_ERROR;
 
642
    }
 
643
 
 
644
    // +1 so we can add '\0' back up in the go function.
 
645
    while(length+1 > state->recvlen - state->read) state->recvlen *= 2;
 
646
    tmp = JS_realloc(state->cx, state->recvbuf, state->recvlen);
 
647
    if(!tmp) return CURLE_WRITE_ERROR;
 
648
    state->recvbuf = tmp;
 
649
   
 
650
    memcpy(state->recvbuf + state->read, ptr, length);
 
651
    state->read += length;
 
652
    return length;
 
653
}
 
654
 
 
655
JSString*
 
656
str_from_binary(JSContext* cx, char* data, size_t length)
 
657
{
 
658
    jschar* conv = (jschar*) JS_malloc(cx, length * sizeof(jschar));
 
659
    JSString* ret = NULL;
 
660
    size_t i;
 
661
 
 
662
    if(!conv) return NULL;
 
663
 
 
664
    for(i = 0; i < length; i++)
 
665
    {
 
666
        conv[i] = (jschar) data[i];
 
667
    }
 
668
 
 
669
    ret = JS_NewUCString(cx, conv, length);
 
670
    if(!ret) JS_free(cx, conv);
 
671
 
 
672
    return ret;
 
673
}