summaryrefslogtreecommitdiff
path: root/src/ne_request.h
blob: a0d5b738592d8c31543154e9a8f11827d1e7edb3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/* 
   HTTP Request Handling
   Copyright (C) 1999-2006, 2008, Joe Orton <joe@manyfish.co.uk>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA

*/

#ifndef NE_REQUEST_H
#define NE_REQUEST_H

#include "ne_utils.h" /* For ne_status */
#include "ne_string.h" /* For ne_buffer */
#include "ne_session.h"

NE_BEGIN_DECLS

#define NE_OK (0) /* Success */
#define NE_ERROR (1) /* Generic error; use ne_get_error(session) for message */
#define NE_LOOKUP (2) /* Server or proxy hostname lookup failed */
#define NE_AUTH (3) /* User authentication failed on server */
#define NE_PROXYAUTH (4) /* User authentication failed on proxy */
#define NE_CONNECT (5) /* Could not connect to server */
#define NE_TIMEOUT (6) /* Connection timed out */
#define NE_FAILED (7) /* The precondition failed */
#define NE_RETRY (8) /* Retry request (ne_end_request ONLY) */
#define NE_REDIRECT (9) /* See ne_redirect.h */

/* Opaque object representing a single HTTP request. */
typedef struct ne_request_s ne_request;

/***** Request Handling *****/

/* Create a request in session 'sess', with given method and path.
 * 'path' must conform to the 'abs_path' grammar in RFC2396, with an
 * optional "? query" part, and MUST be URI-escaped by the caller. */
ne_request *ne_request_create(ne_session *sess,
                              const char *method, const char *path)
    ne_attribute((nonnull));

/* The request body will be taken from 'size' bytes of 'buffer'. */
void ne_set_request_body_buffer(ne_request *req, const char *buffer,
                                size_t size)
    ne_attribute((nonnull));

/* The request body will be taken from 'length' bytes read from the
 * file descriptor 'fd', starting from file offset 'offset'. */
void ne_set_request_body_fd(ne_request *req, int fd,
                            ne_off_t offset, ne_off_t length)
    ne_attribute((nonnull));

/* "Pull"-based request body provider: a callback which is invoked to
 * provide blocks of request body on demand.
 *
 * Before each time the body is provided, the callback will be called
 * once with buflen == 0.  The body may have to be provided >1 time
 * per request (for authentication retries etc.).
 *
 * For a call with buflen == 0, the callback must return zero on success
 * or non-zero on error; the session error string must be set on error.
 * For a call with buflen > 0, the callback must return:
 *        <0           : error, abort request; session error string must be set.
 *         0           : ignore 'buffer' contents, end of body.
 *     0 < x <= buflen : buffer contains x bytes of body data.  */
typedef ssize_t (*ne_provide_body)(void *userdata, 
				   char *buffer, size_t buflen);

/* Install a callback which is invoked as needed to provide the
 * request body, a block at a time.  The total size of the request
 * body is 'length'; the callback must ensure that it returns no more
 * than 'length' bytes in total.  If 'length' is set to -1, then the
 * total size of the request is unknown by the caller and chunked 
 * tranfer will be used. */
void ne_set_request_body_provider(ne_request *req, ne_off_t length,
                                  ne_provide_body provider, void *userdata)
    ne_attribute((nonnull (1)));

/* Handling response bodies; two callbacks must be provided:
 *
 * 1) 'acceptance' callback: determines whether you want to handle the
 * response body given the response-status information, e.g., if you
 * only want 2xx responses, say so here.
 *
 * 2) 'reader' callback: passed blocks of the response-body as they
 * arrive, if the acceptance callback returned non-zero.  */

/* 'acceptance' callback type. Return non-zero to accept the response,
 * else zero to ignore it. */
typedef int (*ne_accept_response)(void *userdata, ne_request *req, 
                                  const ne_status *st);

/* An 'acceptance' callback which only accepts 2xx-class responses.
 * Ignores userdata. */
int ne_accept_2xx(void *userdata, ne_request *req, const ne_status *st);

/* An acceptance callback which accepts all responses.  Ignores
 * userdata. */
int ne_accept_always(void *userdata, ne_request *req, const ne_status *st);

/* Callback for reading a block of data.  Returns zero on success, or
 * non-zero on error.  If returning an error, the response will be
 * aborted and the callback will not be invoked again.  The request
 * dispatch (or ne_read_response_block call) will fail with NE_ERROR;
 * the session error string should have been set by the callback. */
typedef int (*ne_block_reader)(void *userdata, const char *buf, size_t len);

/* Add a response reader for the given request, with the given
 * acceptance function. userdata is passed as the first argument to
 * the acceptance + reader callbacks. 
 *
 * The acceptance callback is called once each time the request is
 * sent: it may be sent >1 time because of authentication retries etc.
 * For each time the acceptance callback is called, if it returns
 * non-zero, blocks of the response body will be passed to the reader
 * callback as the response is read.  After all the response body has
 * been read, the callback will be called with a 'len' argument of
 * zero.  */
void ne_add_response_body_reader(ne_request *req, ne_accept_response accpt,
				 ne_block_reader reader, void *userdata);

/* Retrieve the value of the response header field with given name;
 * returns NULL if no response header with given name was found.  The
 * return value is valid only until the next call to either
 * ne_request_destroy or ne_begin_request for this request. */
const char *ne_get_response_header(ne_request *req, const char *name);

/* Iterator interface for response headers: if passed a NULL cursor,
 * returns the first header; if passed a non-NULL cursor pointer,
 * returns the next header.  The return value is a cursor pointer: if
 * it is non-NULL, *name and *value are set to the name and value of
 * the header field.  If the return value is NULL, no more headers are
 * found, *name and *value are undefined.
 *
 * The order in which response headers is returned is undefined.  Both
 * the cursor and name/value pointers are valid only until the next
 * call to either ne_request_destroy or ne_begin_request for this
 * request. */
void *ne_response_header_iterate(ne_request *req, void *cursor,
                                 const char **name, const char **value);

/* Adds a header to the request with given name and value. */
void ne_add_request_header(ne_request *req, const char *name, 
			   const char *value);
/* Adds a header to the request with given name, using printf-like
 * format arguments for the value. */
void ne_print_request_header(ne_request *req, const char *name,
			     const char *format, ...) 
    ne_attribute((format(printf, 3, 4)));

/* ne_request_dispatch: Sends the given request, and reads the
 * response.  Returns:
 *  - NE_OK if the request was sent and response read successfully
 *  - NE_AUTH, NE_PROXYAUTH for a server or proxy server authentication error
 *  - NE_CONNECT if connection could not be established
 *  - NE_TIMEOUT if an timeout occurred sending or reading from the server
 *  - NE_ERROR for other fatal dispatch errors
 * On any error, the session error string is set.  On success or
 * authentication error, the actual response-status can be retrieved using
 * ne_get_status(). */
int ne_request_dispatch(ne_request *req);

/* Returns a pointer to the response status information for the given
 * request; pointer is valid until request object is destroyed. */
const ne_status *ne_get_status(const ne_request *req) ne_attribute((const));

/* Returns pointer to session associated with request. */
ne_session *ne_get_session(const ne_request *req) ne_attribute((const));

/* Destroy memory associated with request pointer */
void ne_request_destroy(ne_request *req);

/* "Caller-pulls" request interface.  This is an ALTERNATIVE interface
 * to ne_request_dispatch: either use that, or do all this yourself:
 *
 * caller must call:
 *  1. ne_begin_request (fail if returns non-NE_OK)
 *  2. while(ne_read_response_block(...) > 0) ... loop ...;
 *     (fail if ne_read_response_block returns <0)
 *  3. ne_end_request
 *
 * ne_end_request and ne_begin_request both return an NE_* code; if
 * ne_end_request returns NE_RETRY, you must restart the loop from (1)
 * above. */
int ne_begin_request(ne_request *req);
int ne_end_request(ne_request *req);

/* Read a block of the response into the passed buffer of size 'buflen'.
 *
 * Returns:
 *  <0 - error, stop reading.
 *   0 - end of response
 *  >0 - number of bytes read into buffer.
 */
ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen);

/* Read response blocks until end of response; exactly equivalent to
 * calling ne_read_response_block() until it returns 0.  Returns
 * non-zero on error. */
int ne_discard_response(ne_request *req);

/* Read response blocks until end of response, writing content to the
 * given file descriptor.  Returns NE_ERROR on error. */
int ne_read_response_to_fd(ne_request *req, int fd);

/* Defined request flags: */
typedef enum ne_request_flag_e {
    NE_REQFLAG_EXPECT100 = 0, /* enable this flag to enable use of the
                               * "Expect: 100-continue" for the
                               * request. */

    NE_REQFLAG_IDEMPOTENT, /* disable this flag if the request uses a
                            * non-idempotent method such as POST. */

    NE_REQFLAG_LAST /* enum sentinel value */
} ne_request_flag;

/* Set a new value for a particular request flag. */
void ne_set_request_flag(ne_request *req, ne_request_flag flag, int value);

/* Return 0 if the given flag is not set, >0 it is set, or -1 if the
 * flag is not supported. */
int ne_get_request_flag(ne_request *req, ne_request_flag flag);

/**** Request hooks handling *****/

typedef void (*ne_free_hooks)(void *cookie);

/* Hook called when a request is created; passed the request method,
 * and the string used as the Request-URI (note that this may be a
 * absolute URI if a proxy is in use, an absolute path, a "*", etc).
 * A create_request hook is called exactly once per request. */
typedef void (*ne_create_request_fn)(ne_request *req, void *userdata,
				     const char *method, const char *requri);
void ne_hook_create_request(ne_session *sess, 
			    ne_create_request_fn fn, void *userdata);

/* Hook called before the request is sent.  'header' is the raw HTTP
 * header before the trailing CRLF is added; more headers can be added
 * here.  A pre_send hook may be called >1 time per request if the
 * request is retried due to a post_send hook returning NE_RETRY. */
typedef void (*ne_pre_send_fn)(ne_request *req, void *userdata, 
			       ne_buffer *header);
void ne_hook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);

/* Hook called directly after the response headers have been read, but
 * before the resposnse body has been read.  'status' is the response
 * status-code.  A post_header hook may be called >1 time per request
 * if the request is retried due to a post_send hook returning
 * NE_RETRY. */
typedef void (*ne_post_headers_fn)(ne_request *req, void *userdata,
                                   const ne_status *status);
void ne_hook_post_headers(ne_session *sess, 
                          ne_post_headers_fn fn, void *userdata);

/* Hook called after the request is dispatched (request sent, and
 * the entire response read).  If an error occurred reading the response,
 * this hook will not run.  May return:
 *  NE_OK     everything is okay
 *  NE_RETRY  try sending the request again.
 * anything else signifies an error, and the request is failed. The return
 * code is passed back the _dispatch caller, so the session error must
 * also be set appropriately (ne_set_error).
 */
typedef int (*ne_post_send_fn)(ne_request *req, void *userdata,
			       const ne_status *status);
void ne_hook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata);

/* Hook called when the function is destroyed. */
typedef void (*ne_destroy_req_fn)(ne_request *req, void *userdata);
void ne_hook_destroy_request(ne_session *sess,
			     ne_destroy_req_fn fn, void *userdata);

typedef void (*ne_destroy_sess_fn)(void *userdata);
/* Hook called when the session is about to be destroyed. */
void ne_hook_destroy_session(ne_session *sess,
			     ne_destroy_sess_fn fn, void *userdata);

typedef void (*ne_close_conn_fn)(void *userdata);
/* Hook called when the connection is closed; note that this hook
 * may be called *AFTER* the destroy_session hook. */
void ne_hook_close_conn(ne_session *sess, ne_close_conn_fn fn, void *userdata);

/* The ne_unhook_* functions remove a hook registered with the given
 * session.  If a hook is found which was registered with a given
 * function 'fn', and userdata pointer 'userdata', then it will be
 * removed from the hooks list.
 *
 * It is unsafe to use any of these functions from a hook function to
 * unregister itself, except for ne_unhook_destroy_request. */
void ne_unhook_create_request(ne_session *sess, 
                              ne_create_request_fn fn, void *userdata);
void ne_unhook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata);
void ne_unhook_post_headers(ne_session *sess, ne_post_headers_fn fn, void *userdata);
void ne_unhook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata);
void ne_unhook_destroy_request(ne_session *sess,
                               ne_destroy_req_fn fn, void *userdata);
void ne_unhook_destroy_session(ne_session *sess,
                               ne_destroy_sess_fn fn, void *userdata);
void ne_unhook_close_conn(ne_session *sess, 
                          ne_close_conn_fn fn, void *userdata);

/* Store an opaque context for the request, 'priv' is returned by a
 * call to ne_request_get_private with the same ID. */
void ne_set_request_private(ne_request *req, const char *id, void *priv);
void *ne_get_request_private(ne_request *req, const char *id);

NE_END_DECLS

#endif /* NE_REQUEST_H */