@@ -232,10 +232,9 @@ bool CurlClient::SetupCurlOptions(CURL* curl,
232232 headers = curl_slist_append (headers, header.c_str ());
233233 }
234234
235- // Add Last-Event-ID if we have one
235+ // Add Last-Event-ID if we have one from previous connection
236236 if (context.last_event_id && !context.last_event_id ->empty ()) {
237- std::string last_event_header =
238- " Last-Event-ID: " + *context.last_event_id ;
237+ std::string last_event_header = " Last-Event-ID: " + *context.last_event_id ;
239238 headers = curl_slist_append (headers, last_event_header.c_str ());
240239 }
241240
@@ -322,7 +321,7 @@ int CurlClient::ProgressCallback(void* clientp,
322321 context->last_progress_time = now;
323322 } else {
324323 // No new data - check if we've exceeded the timeout
325- auto elapsed = std::chrono::duration_cast<
324+ const auto elapsed = std::chrono::duration_cast<
326325 std::chrono::milliseconds>(
327326 now - context->last_progress_time );
328327
@@ -369,128 +368,17 @@ size_t CurlClient::WriteCallback(const char* data,
369368 return 0 ; // Abort the transfer
370369 }
371370
372- // Parse SSE data
373- std::string_view body (data, total_size);
374-
375- // Parse stream into lines
376- size_t i = 0 ;
377- while (i < body.size ()) {
378- // Find next line delimiter
379- const size_t delimiter_pos = body.find_first_of (" \r\n " , i);
380- const size_t append_size = (delimiter_pos == std::string::npos)
381- ? (body.size () - i)
382- : (delimiter_pos - i);
383-
384- // Append to buffered line
385- if (context->buffered_line .has_value ()) {
386- context->buffered_line ->append (body.substr (i, append_size));
387- } else {
388- context->buffered_line = std::string (body.substr (i, append_size));
389- }
390-
391- i += append_size;
392-
393- if (i >= body.size ()) {
394- break ;
395- }
396-
397- // Handle line delimiters
398- if (body[i] == ' \r ' ) {
399- context->complete_lines .push_back (*context->buffered_line );
400- context->buffered_line .reset ();
401- context->begin_CR = true ;
402- i++;
403- } else if (body[i] == ' \n ' ) {
404- if (context->begin_CR ) {
405- context->begin_CR = false ;
406- } else {
407- context->complete_lines .push_back (*context->buffered_line );
408- context->buffered_line .reset ();
409- }
410- i++;
411- }
412- }
413-
414- // Parse completed lines into events
415- while (!context->complete_lines .empty ()) {
416- std::string line = std::move (context->complete_lines .front ());
417- context->complete_lines .pop_front ();
418-
419- if (line.empty ()) {
420- // Empty line indicates end of event
421- if (context->current_event ) {
422- // Trim trailing newline from data
423- if (!context->current_event ->data .empty () &&
424- context->current_event ->data .back () == ' \n ' ) {
425- context->current_event ->data .pop_back ();
426- }
427-
428- // Update last_event_id_ only when dispatching a completed event
429- if (context->current_event ->id ) {
430- context->last_event_id = context->current_event ->id ;
431- }
432-
433- // Dispatch event on executor thread
434- auto event_data = context->current_event ->data ;
435- auto event_type = context->current_event ->type .empty ()
436- ? " message"
437- : context->current_event ->type ;
438- auto event_id = context->current_event ->id ;
439- context->receive (Event (
440- std::move (event_type),
441- std::move (event_data),
442- std::move (event_id)));
443-
444- context->current_event .reset ();
445- }
446- continue ;
447- }
448-
449- // Parse field
450- const size_t colon_pos = line.find (' :' );
451- if (colon_pos == 0 ) {
452- // Comment line, dispatch it
453- std::string comment = line.substr (1 );
454-
455- context->receive (Event (" comment" , comment));
456- continue ;
457- }
458-
459- std::string field_name;
460- std::string field_value;
461-
462- if (colon_pos == std::string::npos) {
463- field_name = line;
464- field_value = " " ;
465- } else {
466- field_name = line.substr (0 , colon_pos);
467- field_value = line.substr (colon_pos + 1 );
468-
469- // Remove leading space from value if present
470- if (!field_value.empty () && field_value[0 ] == ' ' ) {
471- field_value = field_value.substr (1 );
472- }
473- }
474-
475- // Initialize event if needed
476- if (!context->current_event ) {
477- context->current_event .emplace (detail::Event{});
478- context->current_event ->id = context->last_event_id ;
371+ // Set up the event receiver callback for the parser
372+ context->parser_body ->on_event ([context](Event event) {
373+ // Track last event ID for reconnection
374+ if (event.id ()) {
375+ context->last_event_id = event.id ();
479376 }
377+ context->receive (std::move (event));
378+ });
480379
481- // Handle field
482- if (field_name == " event" ) {
483- context->current_event ->type = field_value;
484- } else if (field_name == " data" ) {
485- context->current_event ->data += field_value;
486- context->current_event ->data += ' \n ' ;
487- } else if (field_name == " id" ) {
488- if (field_value.find (' \0 ' ) == std::string::npos) {
489- context->current_event ->id = field_value;
490- }
491- }
492- // retry field is ignored for now
493- }
380+ const std::string_view data_view (data, total_size);
381+ context->parser_reader ->put (data_view);
494382
495383 return total_size;
496384}
@@ -525,11 +413,8 @@ void CurlClient::PerformRequestWithMulti(
525413 return ;
526414 }
527415
528- // Clear parser state for new connection
529- context->buffered_line .reset ();
530- context->complete_lines .clear ();
531- context->current_event .reset ();
532- context->begin_CR = false ;
416+ // Initialize parser for new connection (last_event_id is tracked separately)
417+ context->init_parser ();
533418
534419 CURL * curl = curl_easy_init ();
535420 if (!curl) {
0 commit comments