static ngx_int_t process_status_line(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_upstream_t *u; passenger_context_t *context; context = ngx_http_get_module_ctx(r, ngx_http_passenger_module); if (context == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rc = parse_status_line(r, context); if (rc == NGX_AGAIN) { return rc; } u = r->upstream; if (rc == NGX_HTTP_SCGI_PARSE_NO_HEADER) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream sent no valid HTTP/1.0 header"); #if 0 if (u->accel) { return NGX_HTTP_UPSTREAM_INVALID_HEADER; } #endif r->http_version = NGX_HTTP_VERSION_9; u->headers_in.status_n = NGX_HTTP_OK; u->state->status = NGX_HTTP_OK; return NGX_OK; } u->headers_in.status_n = context->status; u->state->status = context->status; u->headers_in.status_line.len = context->status_end - context->status_start; u->headers_in.status_line.data = ngx_palloc(r->pool, u->headers_in.status_line.len); if (u->headers_in.status_line.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_memcpy(u->headers_in.status_line.data, context->status_start, u->headers_in.status_line.len); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http scgi status %ui \"%V\"", u->headers_in.status_n, &u->headers_in.status_line); u->process_header = process_header; return process_header(r); }
void http_process_reply_bytes (Call *c, char **bufp, size_t *buf_lenp) { Conn *s = c->conn; struct iovec iov; Any_Type arg; iov.iov_base = *bufp; iov.iov_len = *buf_lenp; arg.vp = &iov; event_signal (EV_CALL_RECV_RAW_DATA, (Object *) c, arg); do { switch (s->state) { case S_REPLY_STATUS: parse_status_line (c, bufp, buf_lenp); break; case S_REPLY_HEADER: parse_headers (c, bufp, buf_lenp); break; case S_REPLY_FOOTER: parse_footers (c, bufp, buf_lenp); break; case S_REPLY_DATA: if (parse_data (c, bufp, buf_lenp) && s->state < S_CLOSING) s->state = S_REPLY_DONE; break; case S_REPLY_CONTINUE: parse_headers (c, bufp, buf_lenp); break; case S_REPLY_CHUNKED: xfer_chunked (c, bufp, buf_lenp); break; case S_REPLY_DONE: return; default: fprintf (stderr, "%s.http_process_reply_bytes: bad state %d\n", prog_name, s->state); exit (1); } } while (*buf_lenp > 0 && s->state < S_CLOSING); }
bool proxy_connection::read_status_line(unsigned fd, size_t& total) { io_result res; do { if ((res = read(fd, _M_client->_M_body, total)) == IO_ERROR) { _M_client->_M_error = http_error::GATEWAY_TIMEOUT; return false; } else if (res == IO_NO_DATA_READ) { return true; } _M_client->_M_timestamp = now::_M_time; parse_result parse_result = parse_status_line(fd); if (parse_result == PARSE_ERROR) { _M_client->_M_error = http_error::BAD_GATEWAY; return false; } else if (parse_result == PARSING_COMPLETED) { // Reuse client's headers. _M_client->_M_headers.reset(); parse_result = parse_headers(fd); if (parse_result == PARSE_ERROR) { _M_client->_M_error = http_error::BAD_GATEWAY; return false; } else if (parse_result == PARSING_NOT_COMPLETED) { _M_state = READING_HEADERS_STATE; } else { _M_state = PROCESSING_RESPONSE_STATE; } return true; } } while (res == IO_SUCCESS); return true; }
struct status_line * ini_load_status_line(const char *inifile) { static const char * const system = SYSCONFDIR "/i3blocks.conf"; const char * const home = getenv("HOME"); char buf[PATH_MAX]; FILE *fp; struct status_line *status; struct status_line *parse(void) { status = calloc(1, sizeof(struct status_line)); if (status && parse_status_line(fp, status)) { free(status->blocks); free(status->updated_blocks); free(status); status = NULL; } if (fclose(fp)) errorx("fclose"); return status; }
utility_retcode_t rtsp_parse_response(struct rtsp_response *response) { utility_retcode_t ret; FUNC_ENTER; ret = parse_status_line(response); if (UTILITY_SUCCESS != ret) { ERRR("Failed to parse status line\n"); goto out; } ret = get_header_list(response); if (UTILITY_SUCCESS != ret) { ERRR("Failed to parse headers\n"); goto out; } /* Here we will need to parse the message body, if any. */ out: FUNC_RETURN; return ret; }
static svn_error_t * multistatus_closed(svn_ra_serf__xml_estate_t *xes, void *baton, int leaving_state, const svn_string_t *cdata, apr_hash_t *attrs, apr_pool_t *scratch_pool) { struct svn_ra_serf__server_error_t *server_error = baton; const char *errcode; const char *status; switch (leaving_state) { case MS_RESPONSE_HREF: { apr_status_t result; apr_uri_t uri; result = apr_uri_parse(scratch_pool, cdata->data, &uri); if (result) return svn_ra_serf__wrap_err(result, NULL); svn_ra_serf__xml_note(xes, MS_RESPONSE, "path", svn_urlpath__canonicalize(uri.path, scratch_pool)); } break; case MS_RESPONSE_STATUS: svn_ra_serf__xml_note(xes, MS_RESPONSE, "status", cdata->data); break; case MS_RESPONSE_ERROR_HUMANREADABLE: svn_ra_serf__xml_note(xes, MS_RESPONSE, "human-readable", cdata->data); errcode = svn_hash_gets(attrs, "errcode"); if (errcode) svn_ra_serf__xml_note(xes, MS_RESPONSE, "errcode", errcode); break; case MS_RESPONSE: if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL) { error_item_t *item; item = apr_pcalloc(server_error->pool, sizeof(*item)); item->path = apr_pstrdup(server_error->pool, svn_hash_gets(attrs, "path")); SVN_ERR(parse_status_line(&item->http_status, &item->http_reason, status, server_error->pool, scratch_pool)); /* Do we have a mod_dav specific message? */ item->message = svn_hash_gets(attrs, "human-readable"); if (item->message) { if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL) { apr_int64_t val; SVN_ERR(svn_cstring_atoi64(&val, errcode)); item->apr_err = (apr_status_t)val; } item->message = apr_pstrdup(server_error->pool, item->message); } else item->message = apr_pstrdup(server_error->pool, svn_hash_gets(attrs, "description")); APR_ARRAY_PUSH(server_error->items, error_item_t *) = item; } break; case MS_PROPSTAT_STATUS: svn_ra_serf__xml_note(xes, MS_PROPSTAT, "status", cdata->data); break; case MS_PROPSTAT_ERROR_HUMANREADABLE: svn_ra_serf__xml_note(xes, MS_PROPSTAT, "human-readable", cdata->data); errcode = svn_hash_gets(attrs, "errcode"); if (errcode) svn_ra_serf__xml_note(xes, MS_PROPSTAT, "errcode", errcode); break; case MS_PROPSTAT_RESPONSEDESCRIPTION: svn_ra_serf__xml_note(xes, MS_PROPSTAT, "description", cdata->data); break; case MS_PROPSTAT: if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL) { apr_hash_t *response_attrs; error_item_t *item; response_attrs = svn_ra_serf__xml_gather_since(xes, MS_RESPONSE); item = apr_pcalloc(server_error->pool, sizeof(*item)); item->path = apr_pstrdup(server_error->pool, svn_hash_gets(response_attrs, "path")); item->propname = apr_pstrdup(server_error->pool, svn_hash_gets(attrs, "propname")); SVN_ERR(parse_status_line(&item->http_status, &item->http_reason, status, server_error->pool, scratch_pool)); /* Do we have a mod_dav specific message? */ item->message = svn_hash_gets(attrs, "human-readable"); if (item->message) { if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL) { apr_int64_t val; SVN_ERR(svn_cstring_atoi64(&val, errcode)); item->apr_err = (apr_status_t)val; } item->message = apr_pstrdup(server_error->pool, item->message); } else item->message = apr_pstrdup(server_error->pool, svn_hash_gets(attrs, "description")); APR_ARRAY_PUSH(server_error->items, error_item_t *) = item; }
static int read_reply(int s, cachemgr_request * req) { char buf[4 * 1024]; FILE *fp = fdopen(s, "r"); /* interpretation states */ enum { isStatusLine, isHeaders, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError } istate = isStatusLine; int parse_menu = 0; const char *action = req->action; const char *statusStr = NULL; int status = -1; if (0 == strlen(req->action)) parse_menu = 1; else if (0 == strcasecmp(req->action, "menu")) parse_menu = 1; if (fp == NULL) { perror("fdopen"); return 1; } if (parse_menu) action = "menu"; /* read reply interpreting one line at a time depending on state */ while (istate < isEof) { if (!fgets(buf, sizeof(buf), fp)) istate = istate == isForward ? isForwardEof : isEof; switch (istate) { case isStatusLine: /* get HTTP status */ /* uncomment the following if you want to debug headers */ /* fputs("\r\n\r\n", stdout); */ status = parse_status_line(buf, &statusStr); istate = status == 200 ? isHeaders : isForward; /* if cache asks for authentication, we have to reset our info */ if (status == 401 || status == 407) { reset_auth(req); status = 403; /* Forbiden, see comments in case isForward: */ } /* this is a way to pass HTTP status to the Web server */ if (statusStr) printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */ break; case isHeaders: /* forward header field */ if (!strcmp(buf, "\r\n")) { /* end of headers */ fputs("Content-Type: text/html\r\n", stdout); /* add our type */ istate = isBodyStart; } if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */ fputs(buf, stdout); break; case isBodyStart: printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE></HEAD><BODY>\n", req->hostname, action); if (parse_menu) { printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>", menu_url(req, "authenticate"), req->hostname); printf("<UL>\n"); } else { printf("<P><A HREF=\"%s\">%s</A>\n<HR>\n", menu_url(req, "menu"), "Cache Manager menu"); printf("<PRE>\n"); } istate = isBody; /* yes, fall through, we do not want to loose the first line */ case isBody: /* interpret [and reformat] cache response */ if (parse_menu) fputs(munge_menu_line(buf, req), stdout); else fputs(munge_other_line(buf, req), stdout); break; case isForward: /* forward: no modifications allowed */ /* * Note: we currently do not know any way to get browser.reply to * 401 to .cgi because web server filters out all auth info. Thus we * disable authentication headers for now. */ if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19)); /* skip */ else fputs(buf, stdout); break; case isEof: /* print trailers */ if (parse_menu) printf("</UL>\n"); else printf("</table></PRE>\n"); print_trailer(); istate = isSuccess; break; case isForwardEof: /* indicate that we finished processing an "error" sequence */ istate = isError; break; default: printf("%s: internal bug: invalid state reached: %d", script_name, istate); istate = isError; } } close(s); return 0; }
// start-line = Request-Line | Status-Line const char* parse_start_line(unsigned char** p) { if (!parse_request_line(p)) return NULL; return parse_status_line(p); }
static int read_reply(int s, cachemgr_request * req) { char buf[4 * 1024]; #ifdef _SQUID_MSWIN_ int reply; char *tmpfile = tempnam(NULL, "tmp0000"); FILE *fp = fopen(tmpfile, "w+"); #else FILE *fp = fdopen(s, "r"); #endif /* interpretation states */ enum { isStatusLine, isHeaders, isActions, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError } istate = isStatusLine; int parse_menu = 0; const char *action = req->action; const char *statusStr = NULL; int status = -1; if (0 == strlen(req->action)) parse_menu = 1; else if (0 == strcasecmp(req->action, "menu")) parse_menu = 1; if (fp == NULL) { #ifdef _SQUID_MSWIN_ perror(tmpfile); xfree(tmpfile); closesocket(s); #else perror("fdopen"); close(s); #endif return 1; } #ifdef _SQUID_MSWIN_ while ((reply = recv(s, buf, sizeof(buf), 0)) > 0) fwrite(buf, 1, reply, fp); rewind(fp); #endif if (parse_menu) action = "menu"; /* read reply interpreting one line at a time depending on state */ while (istate < isEof) { if (!fgets(buf, sizeof(buf), fp)) istate = istate == isForward ? isForwardEof : isEof; switch (istate) { case isStatusLine: /* get HTTP status */ /* uncomment the following if you want to debug headers */ /* fputs("\r\n\r\n", stdout); */ status = parse_status_line(buf, &statusStr); istate = status == 200 ? isHeaders : isForward; /* if cache asks for authentication, we have to reset our info */ if (status == 401 || status == 407) { reset_auth(req); status = 403; /* Forbiden, see comments in case isForward: */ } /* this is a way to pass HTTP status to the Web server */ if (statusStr) printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */ break; case isHeaders: /* forward header field */ if (!strcmp(buf, "\r\n")) { /* end of headers */ fputs("Content-Type: text/html\r\n", stdout); /* add our type */ istate = isBodyStart; } if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */ fputs(buf, stdout); break; case isBodyStart: printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"); printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n", req->hostname, action); printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}TABLE{background-color:#333333;border:0pt;padding:0pt}TH,TD{background-color:#ffffff;white-space:nowrap}--></STYLE>\n"); printf("</HEAD><BODY>\n"); if (parse_menu) { printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>", menu_url(req, "authenticate"), req->hostname); } else { printf("<P><A HREF=\"%s\">%s</A>\n", menu_url(req, "menu"), "Cache Manager menu"); } istate = isActions; /* yes, fall through, we do not want to loose the first line */ case isActions: if (strncmp(buf, "action:", 7) == 0) { fputs(" ", stdout); fputs(munge_action_line(buf + 7, req), stdout); break; } if (parse_menu) { printf("<UL>\n"); } else { printf("<HR noshade size=\"1px\">\n"); printf("<PRE>\n"); } istate = isBody; /* yes, fall through, we do not want to loose the first line */ case isBody: /* interpret [and reformat] cache response */ if (parse_menu) fputs(munge_menu_line(buf, req), stdout); else fputs(munge_other_line(buf, req), stdout); break; case isForward: /* forward: no modifications allowed */ /* * Note: we currently do not know any way to get browser.reply to * 401 to .cgi because web server filters out all auth info. Thus we * disable authentication headers for now. */ if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19)); /* skip */ else fputs(buf, stdout); break; case isEof: /* print trailers */ if (parse_menu) printf("</UL>\n"); else printf("</table></PRE>\n"); print_trailer(); istate = isSuccess; break; case isForwardEof: /* indicate that we finished processing an "error" sequence */ istate = isError; break; default: printf("%s: internal bug: invalid state reached: %d", script_name, istate); istate = isError; } } #ifdef _SQUID_MSWIN_ fclose(fp); remove(tmpfile); xfree(tmpfile); closesocket(s); #else close(s); #endif return 0; }
int handle_response(char *ip, char *filename) { int i; char buffer[RESPONSE_BUFFER_SIZE]; int buffer_size = 0; int pointer = 0; char status_line[HEADER_LINE_LENGTH]; char header[HEADER_LINE_LENGTH]; char value[HEADER_LINE_LENGTH]; int status_ok; char header_content_length[HEADER_LINE_LENGTH]; char header_content_type[HEADER_LINE_LENGTH]; char header_last_modified[HEADER_LINE_LENGTH]; time_t curtime; char current_time[HEADER_LINE_LENGTH]; FILE *fp; /* fill buffer with first part of response */ buffer_size = get_response_header(buffer, RESPONSE_BUFFER_SIZE); if (buffer_size < 0) { printf("Request failed: could not retrieve response from server\n"); return 0; } /* read status line */ pointer = parse_status_line(buffer, buffer_size, status_line, HEADER_LINE_LENGTH, &status_ok); if (pointer < 0) { printf("Request failed: invalid response status code sent by server\n"); return 0; } /* default value for headers */ snprintf(header_content_length, HEADER_LINE_LENGTH, "Unknown"); snprintf(header_content_type, HEADER_LINE_LENGTH, "Unknown"); snprintf(header_last_modified, HEADER_LINE_LENGTH, "Unknown"); /* read headers */ while ((i = parse_header(buffer + pointer, buffer_size - pointer, header, HEADER_LINE_LENGTH, value, HEADER_LINE_LENGTH)) > 0) { pointer += i; if (strcmp(header, "Content-Length") == 0) { memcpy(header_content_length, value, HEADER_LINE_LENGTH); } else if (strcmp(header, "Content-Type") == 0) { memcpy(header_content_type, value, HEADER_LINE_LENGTH); } else if (strcmp(header, "Last-Modified") == 0) { memcpy(header_last_modified, value, HEADER_LINE_LENGTH); } } i = read_separator(buffer + pointer, buffer_size - pointer); if (i < 0) { printf("Request failed: invalid response sent by server\n"); return 0; } pointer += i; /* get current time */ time(&curtime); strftime(current_time, HEADER_LINE_LENGTH, DATE_TIME_FORMAT, gmtime(&curtime)); /* print some output */ printf("Request sent to http server at %s. Received response:\n", ip); printf(" The return code was: %s\n", status_line); printf(" Date of retrieval: %s\n", current_time); printf(" Document last modified at: %s\n", header_last_modified); printf(" Document size: %s bytes\n", header_content_length); printf(" Document's mime type: %s\n", header_content_type); if (!status_ok) { printf("Since the status code was not '200 OK', no data has been written\nto %s\n", filename); return 1; } /* open file for writing */ if ((fp = fopen(filename, "w")) == (FILE *)0) { switch (errno) { case EACCES: printf("No permissions to open file for writing: %s\n", filename); return 0; break; default: printf("Could not open file for writing: %s\n", filename); return 0; } } /* read until end of stream */ do { /* write buffer contents to file */ while (pointer < buffer_size) { putc(buffer[pointer], fp); pointer++; } signal(SIGALRM, alarm_handler); alarm(TIME_OUT); buffer_size = tcp_read(buffer, RESPONSE_BUFFER_SIZE); alarm(0); /* failed reading */ if (buffer_size < 0 || alarm_went_off) { /* Actually, we should write to a temp file and delete it here. Only if all data is received, copy temp file to real file. But this is a problem if we have write rights on filename, but not to create temp file. So we leave this for the time being. */ fclose(fp); printf("Request failed: could not retrieve message body\n"); printf("Wrote partial message body to file: %s\n", filename); return 0; } pointer = 0; } while (buffer_size); fclose(fp); printf("Wrote message body to file: %s\n", filename); return 1; }
/* ========================================================================= */ struct query *read_request(struct query *q, int s, struct iobuf *b) { size_t offset; ssize_t got; int recvmore; char uri[MAX_URI_LENGTH]; int method; offset = 0; recvmore = 1; while ( recvmore ) { got = recv(s, b->buf + offset, b->size - offset, 0); if ( got < 0 ) { if ( errno != EINTR ) { if ( q ) q->parse_failure |= UP_DEAD_SOCKET; else { /* NOTE: ParseURLString() will allocate and initialize q if it is NULL. So... it is possible that q might be NULL here. If it is, then allocate it so we can pass the specific error. */ if ( NULL == (q = (struct query *)malloc(sizeof(struct query))) ) { error_msg("ERROR: Failed to allocate memory for a query."); pthread_exit((void *)1); } q->parse_failure = UP_DEAD_SOCKET; } error_msg("ERROR: Dropped connection on recv() call.\n"); return(q); } } else { offset += got; b->eod += got; if ( got == 0 ) { /* This should never happen */ if ( offset > 10 ) /* Arbitrary number *somewhat* large enough to hold a request */ { if (( b->buf[offset - 4] == '\r' ) && ( b->buf[offset - 3] == '\n' ) && ( b->buf[offset - 2] == '\r' ) && ( b->buf[offset - 1] == '\n' )) { recvmore = 0; parse_status_line(&method, uri, b->buf); } } /* If some data ( > 10 ) */ } else { if ( offset > 10 ) /* Arbitrary number *somewhat* large enough to hold a request */ { if (( b->buf[offset - 4] == '\r' ) && ( b->buf[offset - 3] == '\n' ) && ( b->buf[offset - 2] == '\r' ) && ( b->buf[offset - 1] == '\n' )) { recvmore = 0; parse_status_line(&method, uri, b->buf); } } /* If some data ( > 10 ) */ } /* if ( got == 0 ) else */ } /* if ( got < 0 ) else */ } /* while recvmore */ b->buf[b->eod] = 0; /* Terminate for safety */ /* STUB: Enable this to see the http request header DEBUG_STUB("%s", b->buf); */ switch(method) { case METHOD_GET: q = ParseURLString(q, uri); q->method = METHOD_GET; break; case METHOD_HEAD: q = ParseURLString(q, uri); q->method = METHOD_HEAD; break; case METHOD_OPTIONS: q = ParseURLString(q, NULL); /* This insures that q has been allocated */ q->method = METHOD_OPTIONS; break; case METHOD_POST: q = ParseURLString(q, uri); q->method = METHOD_POST; break; default: error_msg("ERROR: Unsupported request method.\n"); q = ParseURLString(q, NULL); /* This insures that q has been allocated */ q->parse_failure |= UP_BAD_METHOD; break; } return(q); }
bool net::internet::http::analyzer::process(time_t t, connection* conn, const packet& pkt) { if (conn->state == kIgnoringConnection) { return true; } const unsigned char* begin; size_t count; if (pkt.direction == kOutgoingPacket) { if (conn->state >= kParsingStatusLine) { return true; } count = conn->out ? conn->out->count() : 0; if (!conn->append_out(reinterpret_cast<const char*>(pkt.payload), pkt.len)) { return false; } begin = reinterpret_cast<const unsigned char*>(conn->out->data()) + count; } else { if (conn->state < kParsingStatusLine) { return true; } count = conn->in ? conn->in->count() : 0; if (!conn->append_in(reinterpret_cast<const char*>(pkt.payload), pkt.len)) { return false; } begin = reinterpret_cast<const unsigned char*>(conn->in->data()) + count; } count += pkt.len; const unsigned char* ptr = begin; const unsigned char* end = ptr + pkt.len; size_t methodlen = conn->protocol.http.methodlen; size_t pathlen = conn->protocol.http.pathlen; size_t hostlen = conn->protocol.http.hostlen; int state = conn->state; do { unsigned char c = *ptr; switch (state) { case kBeforeMethod: // Before method. if ((c >= 'A') && (c <= 'Z')) { conn->protocol.http.method = count - (end - ptr); methodlen = 1; state = kMethod; // Method. } else if ((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n')) { // Ignore connection. conn->state = kIgnoringConnection; return true; } break; case kMethod: // Method. if ((c >= 'A') && (c <= 'Z')) { if (++methodlen > kMethodMaxLen) { // Ignore connection. conn->state = kIgnoringConnection; return true; } } else if ((c == ' ') || (c == '\t')) { conn->protocol.http.methodlen = methodlen; state = kBeforePath; // Before path. } else { // Ignore connection. conn->state = kIgnoringConnection; return true; } break; case kBeforePath: // Before path. if (c > ' ') { conn->protocol.http.path = count - (end - ptr); pathlen = 1; state = kPath; // Path. } else if ((c != ' ') && (c != '\t')) { // Ignore connection. conn->state = kIgnoringConnection; return true; } break; case kPath: // Path. switch (c) { case ' ': case '\t': case '\r': case '\n': conn->protocol.http.pathlen = pathlen; if ((pathlen > 7) && (strncasecmp(conn->out->data() + conn->protocol.http.path, "http://", 7) == 0)) { conn->protocol.http.host = 0; conn->protocol.http.offset = 0; conn->state = kParsingStatusLine; conn->protocol.http.substate = 0; return true; } state = kSearchingHost; // Searching host. break; default: if (c > ' ') { if (++pathlen > kPathMaxLen) { // Ignore connection. conn->state = kIgnoringConnection; return true; } } else { // Ignore connection. conn->state = kIgnoringConnection; return true; } } break; case kSearchingHost: // Searching host. ptr = reinterpret_cast<const unsigned char*>(conn->out->data()) + conn->protocol.http.path + conn->protocol.http.pathlen; if ((ptr = reinterpret_cast<const unsigned char*>(memmem(ptr, end - ptr, "Host:", 5))) == NULL) { // Host header might come in the next packet. ptr = end; continue; } else { ptr += 5; state = kBeforeHostValue; // Before host value. continue; } break; case kBeforeHostValue: // Before host value. if (c > ' ') { conn->protocol.http.host = count - (end - ptr); hostlen = 1; state = kHost; // Host. } else if ((c != ' ') && (c != '\t')) { // Ignore connection. conn->state = kIgnoringConnection; return true; } break; case kHost: // Host. switch (c) { case ' ': case '\t': case '\r': case '\n': conn->protocol.http.hostlen = hostlen; conn->protocol.http.offset = 0; conn->state = kParsingStatusLine; conn->protocol.http.substate = 0; return true; default: if (c > ' ') { if (++hostlen > kHostMaxLen) { // Ignore connection. conn->state = kIgnoringConnection; return true; } } else { // Ignore connection. conn->state = kIgnoringConnection; return true; } } break; case kParsingStatusLine: // Parsing status-line. switch (parse_status_line(conn)) { case kParseError: // Ignore connection. conn->state = kIgnoringConnection; return true; case kParsingNotCompleted: return true; case kParsingCompleted: ptr = reinterpret_cast<const unsigned char*>(conn->in->data()) + conn->protocol.http.offset; state = kParsingServerHeaders; continue; } break; case kParsingServerHeaders: // Parsing server headers. switch (conn->protocol.http.parse_server_headers(conn->in->data() + conn->protocol.http.offset, count - conn->protocol.http.offset)) { case headers::PARSE_NO_MEMORY: case headers::PARSE_INVALID_HEADER: case headers::PARSE_HEADERS_TOO_LARGE: // Ignore connection. conn->state = kIgnoringConnection; return true; case headers::PARSE_NOT_END_OF_HEADER: return true; case headers::PARSE_END_OF_HEADER: if (!gsniffer.get_http_logger()->log(t, conn)) { return false; } conn->state = kIgnoringConnection; return true; } break; } ptr++; } while (ptr < end); if (pkt.direction == kOutgoingPacket) { // Request too large? if (count > kRequestMaxLen) { // Ignore connection. conn->state = kIgnoringConnection; return true; } } conn->state = state; conn->protocol.http.methodlen = methodlen; conn->protocol.http.pathlen = pathlen; conn->protocol.http.hostlen = hostlen; return true; }
/* Perform one iteration of the state machine. * * Will return when one the following conditions occurred: * 1) a state change * 2) an error * 3) the stream is not ready or at EOF * 4) APR_SUCCESS, meaning the machine can be run again immediately */ static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx) { apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */ switch (ctx->state) { case STATE_STATUS_LINE: /* RFC 2616 says that CRLF is the only line ending, but we can easily * accept any kind of line ending. */ status = fetch_line(ctx, SERF_NEWLINE_ANY); if (SERF_BUCKET_READ_ERROR(status)) return status; if (ctx->linebuf.state == SERF_LINEBUF_READY) { /* The Status-Line is in the line buffer. Process it. */ status = parse_status_line(ctx, bkt->allocator); if (status) return status; /* Good times ahead: we're switching protocols! */ if (ctx->sl.code == 101) { ctx->body = serf_bucket_barrier_create(ctx->stream, bkt->allocator); ctx->state = STATE_DONE; break; } /* Okay... move on to reading the headers. */ ctx->state = STATE_HEADERS; } else { /* The connection closed before we could get the next * response. Treat the request as lost so that our upper * end knows the server never tried to give us a response. */ if (APR_STATUS_IS_EOF(status)) { return SERF_ERROR_REQUEST_LOST; } } break; case STATE_HEADERS: status = fetch_headers(bkt, ctx); if (SERF_BUCKET_READ_ERROR(status)) return status; /* If an empty line was read, then we hit the end of the headers. * Move on to the body. */ if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) { const void *v; /* Advance the state. */ ctx->state = STATE_BODY; ctx->body = serf_bucket_barrier_create(ctx->stream, bkt->allocator); /* * Instaweb/mod_pagespeed change: This section is * re-ordered from the original code from serf to Follow * HTTP spec by checking "Transfer-Encoding: chunked", * before "Content-Length". */ /* Are we C-L, chunked, or conn close? */ v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding"); /* Need to handle multiple transfer-encoding. */ if (v && strcasecmp("chunked", v) == 0) { ctx->chunked = 1; ctx->body = serf_bucket_dechunk_create(ctx->body, bkt->allocator); } else { v = serf_bucket_headers_get(ctx->headers, "Content-Length"); if (v) { apr_uint64_t length; length = apr_strtoi64(v, NULL, 10); if (errno == ERANGE) { return APR_FROM_OS_ERROR(ERANGE); } ctx->body = serf_bucket_limit_create(ctx->body, length, bkt->allocator); } else if ((ctx->sl.code == 204 || ctx->sl.code == 304)) { ctx->state = STATE_DONE; } } /* * Instaweb would prefer to receive gzipped output if that's what * was asked for. * * v = serf_bucket_headers_get(ctx->headers, "Content-Encoding"); * if (v) { * * Need to handle multiple content-encoding. * * if (v && strcasecmp("gzip", v) == 0) { * ctx->body = * serf_bucket_deflate_create(ctx->body, bkt->allocator, * SERF_DEFLATE_GZIP); * } * else if (v && strcasecmp("deflate", v) == 0) { * ctx->body = * serf_bucket_deflate_create(ctx->body, bkt->allocator, * SERF_DEFLATE_DEFLATE); * } * } */ /* If we're a HEAD request, we don't receive a body. */ if (ctx->head_req) { ctx->state = STATE_DONE; } } break; case STATE_BODY: /* Don't do anything. */ break; case STATE_TRAILERS: status = fetch_headers(bkt, ctx); if (SERF_BUCKET_READ_ERROR(status)) return status; /* If an empty line was read, then we're done. */ if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) { ctx->state = STATE_DONE; return APR_EOF; } break; case STATE_DONE: return APR_EOF; default: /* Not reachable */ return APR_EGENERAL; } return status; }